Agent skill

Frontend Rules

by letehaha in letehaha/moneymatter

Frontend conventions and component usage rules. An agent skill from letehaha/moneymatter.

AGPL-3.0Auto-check passedFrontend & Design

Install Frontend Rules

skills CLI
$ npx skills add letehaha/moneymatter --skill frontend-rules -a claude-code

Project install by default; add -g for ~/.claude/skills/.

GitHub CLI
$ gh skill install letehaha/moneymatter frontend-rules --agent claude-code

Project scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).

Manual copy
$ git clone --depth 1 https://github.com/letehaha/moneymatter.git skills-src && mkdir -p .claude/skills && cp -r skills-src/.claude/skills/frontend-rules .claude/skills/frontend-rules && rm -rf skills-src

Use ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.

Claude Code skills documentation · loads skills from .claude/skills/

Facts

Skill name
frontend-rules
GitHub stars
160
Token cost
~3.8k tokens
SKILL.md length
1,438 words
Files
1
Skills in repo
10
Repo updated
First seen
Licence
AGPL-3.0

At a glance

Frontend conventions and component usage rules. An agent skill from letehaha/moneymatter.

  • Works in 3 steps: Show a ResponsiveAlertDialog to confirm… → The confirm/submit button inside the… → After successful deletion, show a…
  • /frontend-rules
  • SKILL.md covers General, Project Structure, Testing and Styling, plus 5 more sections
  • Instructions only: no scripts, shell commands, URLs or credentials in SKILL.md

What it does

Frontend Rules is an agent skill from letehaha/moneymatter. Frontend conventions and component usage rules. Read before writing any frontend code. Covers styling, component library, data flow, and UI patterns. Trigger on "/frontend-rules" or when starting frontend work.

Its SKILL.md is about 3.8k tokens, which your agent loads only when the skill is triggered. It is a single SKILL.md file with no bundled scripts.

It sits in Frontend & Design, covering Design systems and Responsive design. The repository describes itself as: A personal budget tracking application. Track your balances and transactions with bank connections or manual entry, categorize and analyze expenses and income, and many more. The licence is AGPL-3.0.

When your agent uses it

  • /frontend-rules
  • Starting frontend work

Example prompts

  • “/frontend-rules”
  • “/frontend-rules”

Requirements

  • Pre-approved tools (allowed-tools): Read, Grep, Glob

Workflow steps

3 steps, taken from the first numbered list in SKILL.md.

  1. Show a ResponsiveAlertDialog to confirm the user's intention
  2. The confirm/submit button inside the dialog must use the destructive variant
  3. After successful deletion, show a success toast and invalidate related queries

What it can do on your machine

Read from SKILL.md and the folder at commit de5319f. It shows what the files ask for, not the result of running them.

  • Tool permissions

    Pre-approves these tools, so the agent can use them without asking each time:

    • Read
    • Grep
    • Glob

    From allowed-tools in the SKILL.md frontmatter.

  • Runs code

    No scripts in the folder and no shell commands in SKILL.md (its code samples are vue, typescript and json).

    From the folder's file list and the shell code blocks in SKILL.md.

  • Network

    No URLs in SKILL.md.

    From URLs in SKILL.md, links to its own repository left out.

  • Credentials

    Names no API keys, tokens, secrets or passwords.

    From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.

Context cost

Frontend Rules loads about 3.8k tokens when it runs. Until then it costs about 56 tokens; SKILL.md has 1,438 words of instructions outside code blocks.

Always · name and description, kept in context so the agent knows when to use it
~56
When it runs · the whole SKILL.md, loaded when a task matches
~3.8k

Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.

Safety

Auto-check passed

The automated check found no risky patterns in SKILL.md.

Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.

SKILL.md

The full file from letehaha/moneymatter at commit de5319f, republished under its AGPL-3.0 licence (© letehaha). 1,438 words, ~3,842 tokens.

Download SKILL.mdSave it as .claude/skills/frontend-rules/SKILL.md (or your agent's skills folder).
name
frontend-rules
description
Frontend conventions and component usage rules. Read before writing any frontend code. Covers styling, component library, data flow, and UI patterns. Trigger on "/frontend-rules" or when starting frontend work.
allowed-tools
Read, Grep, Glob

Frontend Rules & Conventions

Reference guide for all frontend code in packages/frontend/. Read this before writing or modifying frontend components.


General

  • All file names use kebab-case (e.g., reminder-form-dialog.vue, use-submit-transaction.ts)
  • All components use <script setup lang="ts">
  • Use TypeScript — no any unless absolutely unavoidable
  • Use date-fns for all date formatting and manipulation. Never use raw Date methods or other date libraries
  • Magic numbers: when a numeric literal's meaning isn't obvious from context, extract it into a descriptive constant (e.g., const MAX_VISIBLE_TAGS = 5). Simple, self-evident values (array indices, 0/1 checks, common multipliers, etc.) are fine inline — use your judgement

Project Structure

API layer

All API call functions live in src/api/. Components and composables must import from there — never inline fetch or axios calls directly in components.

Component placement
  • Shared / reusable components go in components/common/ or components/lib/
  • Page-specific components go in pages/<feature>/components/
Route constants

Route paths are defined as constants in routes/constants.ts. Always use those constants for router.push() and <RouterLink :to="...">. Never hardcode path strings.

Responsive breakpoints

The project defines CUSTOM_BREAKPOINTS and provides the useWindowBreakpoints composable. Use those for JS-based responsive logic — never use custom media queries or magic pixel values.

Container queries for content-area layout

The app has a ~300px sidebar visible on screens wider than 768px, so the content area is significantly narrower than the viewport (e.g., 900px screen = ~600px content). Viewport breakpoints (md:, lg:) don't account for this and can produce wrong results for content layout.

Prefer CSS container queries (@container) when the layout depends on the available content width — multi-column grids, card arrangements, etc. Use Tailwind's named container syntax:

vue
<!-- Parent: define the container -->
<Card class="@container/my-section max-w-4xl">
  <!-- Child: query the container width, NOT the viewport -->
  <div class="grid grid-cols-1 @sm/my-section:grid-cols-2">
    ...
  </div>
</Card>

Container queries aren't needed everywhere — use judgement:

  • Container queries (@container): multi-column grids, card layouts, anything where the sidebar makes viewport breakpoints inaccurate
  • Viewport breakpoints (md:, lg:): showing/hiding the sidebar, header layout, simple show/hide toggles that genuinely depend on screen size
  • useWindowBreakpoints: JS-based responsive logic — toggling components, conditional rendering based on device class

Testing

All computation functions, services, utils, and helpers must have unit tests to verify their logic. If you create or modify a pure function (formatting, calculation, transformation, validation), write or update a corresponding unit test.


Styling

  • Use Tailwind CSS for all styling — avoid custom CSS unless absolutely necessary
  • Never use default Tailwind colors directly (e.g., text-red-500, bg-blue-200). Always use the project's color variables defined in packages/frontend/src/styles/global.css
  • If no existing color fits, tell the user about it and suggest adding it to global.css rather than using raw color values
  • Use cn() utility from @/lib/utils for conditional class composition. Never use manual string concatenation for dynamic classes
Amount colors
PurposeClass
Income / positivetext-app-income-color
Expense / negativetext-app-expense-color
Transfertext-app-transfer-color
Text/icon color vs. fill color

Semantic colors come in pairs: the base token is a fill (button/badge backgrounds, with *-foreground for the label on top), and the *-text token is the tint tuned for text and icons sitting directly on the page/card background. Icons count as text here — lucide icons inherit currentColor.

ColorText / iconsFill / background
Primary (brand)text-primary-textbg-primary + text-primary-foreground
Destructivetext-destructive-textbg-destructive
Successtext-success-textbg-success
Warningtext-warning-text—
  • Never use text-primary or text-destructive to color text or icons. Fill tokens are tuned so a white label passes contrast on top of them, which makes them too low-contrast as text on the dark theme (this is why --primary-text exists as a separate, brighter tint in dark mode)
  • Tinted chips like bg-primary/10 still count as a dark background — text/icons inside them use text-primary-text
  • Borders, strokes (progress rings), and chart lines may keep using the base token — non-text UI parts only need 3:1 contrast

Components

Buttons

Always use the project's Button component (@/components/lib/ui/button). Never use raw <button> elements.

Available variants: default, destructive, outline, secondary, ghost, ghost-destructive, soft-destructive, ghost-primary, soft-primary, success, outline-success, ghost-success, soft-success, link

Available sizes: default, sm, lg, icon, icon-sm

Button is inline-flex with gap-2, so spacing between its children (icon + label, multiple icons, etc.) is handled by the button itself. Do not add mr-* / ml-* / space-x-* to icons or text inside a Button — it doubles the gap and looks off.

vue
<!-- WRONG — gap-2 already spaces the icon from the label -->
<Button><PlusIcon class="mr-2 size-4" /> Add item</Button>

<!-- CORRECT -->
<Button><PlusIcon class="size-4" /> Add item</Button>
Form fields

Always use the project's field components from @/components/fields/. Never use raw <input>, <select>, or <textarea> elements.

NeedComponent
Text / numberinput-field.vue
Selectselect-field.vue
Datedate-field.vue
Textareatextarea-field.vue
Categorycategory-select-field.vue
Tagstag-select-field.vue
Colorcolor-select-field.vue
Placeholders are required

Every form field must have a :placeholder set. An empty input or an empty select with only a label leaves the user guessing what to enter or pick (especially in dialogs that open with no prefilled value). The placeholder should be a concrete prompt — "Enter payee name", "Select a category", "DD/MM/YYYY" — not a restatement of the label.

Always wire placeholders through i18n (:placeholder="$t('...')"); never inline raw English strings.

Icons

Always use @lucide/vue icons. Never use raw SVGs or other icon libraries. Do not use lucide-vue-next — that's the old package and has been replaced.

Always import icons with the Icon suffix — e.g., CircleCheckIcon, Trash2Icon, ChevronDownIcon. Never use the short form without the suffix.

vue
<!-- WRONG -->
import { CircleCheck, Trash2 } from '@lucide/vue'
<CircleCheck />

<!-- WRONG — old package -->
import { CircleCheckIcon, Trash2Icon } from 'lucide-vue-next'

<!-- CORRECT -->
import { CircleCheckIcon, Trash2Icon } from '@lucide/vue'
<CircleCheckIcon />
Scroll areas

Always use the ScrollArea component (@/components/lib/ui/scroll-area) for scrollable containers. Never use raw overflow-auto / overflow-y-scroll divs. Adding the ScrollArea's id to a global variable is not needed unless there's a concrete use case for it.

Checkbox

The project's Checkbox component (@/components/lib/ui/checkbox) uses modelValue / update:modelValue, not checked / update:checked. Always bind with :model-value and @update:model-value (or v-model).

vue
<!-- Correct -->
<Checkbox :model-value="form.notifyEmail" @update:model-value="(val) => (form.notifyEmail = !!val)" />

<!-- Also correct -->
<Checkbox v-model="form.notifyEmail" />

<!-- WRONG — will silently do nothing -->
<Checkbox :checked="form.notifyEmail" @update:checked="..." />
Show full SKILL.md (574 more words)Show less
Icon-only action buttons

When a button uses size="icon" or size="icon-sm" (no visible label) and performs an action (edit, delete, skip, etc.), always wrap it with DesktopOnlyTooltip (@/components/lib/ui/tooltip) so the user can see what it does on hover. Use the content prop for the label. Do not use the native HTML title attribute — it has a long delay and inconsistent styling.

vue
<DesktopOnlyTooltip content="Delete">
  <UiButton variant="soft-destructive" size="icon" @click="handleDelete">
    <Trash2Icon class="size-4" />
  </UiButton>
</DesktopOnlyTooltip>

DesktopOnlyTooltip automatically skips rendering the tooltip on touch devices (no hover capability), so no extra mobile handling is needed.

Tooltip + slot-based dialog trigger

When the tooltipped button is the slot trigger for ResponsiveDialog / ResponsiveAlertDialog, nesting DialogTrigger > DesktopOnlyTooltip > Button silently breaks the click (reka-ui as-child can't merge through the tooltip's fragment root). Order: tooltip outside, <span class="inline-flex"> wrapper, dialog inside.

vue
<DesktopOnlyTooltip content="Delete">
  <span class="inline-flex">
    <DeleteFooDialog :foo-id="id">
      <UiButton size="icon" variant="ghost-destructive" aria-label="Delete">
        <Trash2Icon class="size-4" />
      </UiButton>
    </DeleteFooDialog>
  </span>
</DesktopOnlyTooltip>

Not needed for plain Button @click with v-model:open — only slot-triggered dialogs.

Dialogs & modals

Default to ResponsiveDialog for every new dialog. Unless the user explicitly asks for a different primitive (raw Dialog, Drawer, Popover, etc.), any new modal/sheet/picker UI must use one of the responsive wrappers. They auto-adapt to Drawer on mobile and Dialog / AlertDialog on desktop, so they work in both contexts without extra wiring.

  • Use ResponsiveDialog (@/components/common/responsive-dialog.vue) for general-purpose modals (forms, detail views, pickers)
  • Use ResponsiveAlertDialog (@/components/common/responsive-alert-dialog.vue) for confirmations and destructive actions
  • Never use raw Dialog, AlertDialog, or Drawer primitives directly for user-facing modals unless the user explicitly approves it for that case
  • Always provide a #title slot (visible or via VisuallyHidden) so Radix has an accessible name — a missing DialogTitle triggers an a11y warning

UI States

Empty states

When a page or section has no data, display an empty state with the following layout: icon + title + description + action (if applicable). Look at existing empty states in the codebase for reference before creating new ones.

Loading states

Use skeleton placeholders to represent loading content. Skeletons should approximate the shape/layout of the real content to prevent layout shift.


Destructive Actions

All critical actions that delete data must follow this pattern:

  1. Show a ResponsiveAlertDialog to confirm the user's intention
  2. The confirm/submit button inside the dialog must use the destructive variant
  3. After successful deletion, show a success toast and invalidate related queries
vue
<ResponsiveAlertDialog
  v-model:open="isDeleteOpen"
  confirm-label="Delete"
  confirm-variant="destructive"
  @confirm="handleDelete"
>
  <template #title>Delete item?</template>
  <template #description>This action cannot be undone.</template>
</ResponsiveAlertDialog>

Data Flow (TanStack Query)

Cache keys

Always use constants from VUE_QUERY_CACHE_KEYS or VUE_QUERY_GLOBAL_PREFIXES in @/common/const/vue-query.ts. Never hardcode query key strings.

Mutations & cache invalidation

After every create / update / delete operation:

  1. Invalidate all related TanStack queries so the UI stays in sync
  2. Show a success toast to confirm the operation to the user (unless explicitly told otherwise)
  3. If the optimistic update is simple (e.g., removing an item from a list, toggling a boolean), prefer optimistic updates over waiting for refetch
Optimistic update pattern
typescript
useMutation({
  mutationFn: deleteItem,
  onMutate: async ({ id }) => {
    await queryClient.cancelQueries({ queryKey: VUE_QUERY_CACHE_KEYS.itemsList });
    const previous = queryClient.getQueryData(VUE_QUERY_CACHE_KEYS.itemsList);
    queryClient.setQueryData(VUE_QUERY_CACHE_KEYS.itemsList, (old) => old?.filter((item) => item.id !== id));
    return { previous };
  },
  onError: (_err, _vars, context) => {
    if (context?.previous) {
      queryClient.setQueryData(VUE_QUERY_CACHE_KEYS.itemsList, context.previous);
    }
  },
  onSettled: () => {
    queryClient.invalidateQueries({ queryKey: VUE_QUERY_CACHE_KEYS.itemsList });
  },
});
Loading states

Disable submit buttons and show loading indicators while mutations are in progress:

vue
<Button :disabled="mutation.isPending.value" :loading="mutation.isPending.value">
  Save
</Button>

i18n

$t vs t
  • In Vue templates, always use $t() — it's globally available and requires no import.
  • Only use the imported t() function inside <script setup> when you need translations in computed properties, composables, or other JS/TS logic that runs outside the template.
Never split translatable text

Do not break a sentence into multiple $t() calls when there are inline elements or components in the middle. Translations must represent complete sentences/phrases — splitting them makes proper translation impossible.

vue
<!-- WRONG — splits sentence around a link -->
<p>{{ $t('Please read the') }} <a href="/terms">{{ $t('terms') }}</a> {{ $t('before continuing.') }}</p>

<!-- CORRECT — use <i18n-t> with slots -->
<i18n-t keypath="pleaseReadTerms" tag="p">
  <template #link>
    <a href="/terms">{{ $t('terms') }}</a>
  </template>
</i18n-t>

The corresponding translation key uses {link} as a placeholder:

json
{
  "pleaseReadTerms": "Please read the {link} before continuing.",
  "terms": "terms"
}
<i18n-t> component

Use the <i18n-t> component (from vue-i18n) whenever a translated string contains inline elements or components (links, bold text, icons, etc.). It interpolates Vue slots into translation placeholders, keeping the full sentence in a single translation key.

© letehaha, AGPL-3.0. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file

Files

Just SKILL.md in .claude/skills/frontend-rules of letehaha/moneymatter.

Open the folder on GitHubat commit de5319f

Compare with similar skills

Frontend Rules next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.

Frontend Rules compared with similar skills
SkillStarsUsed inTokensAuto-checkLicenceRepo updated
Frontend Rules this skillletehaha/moneymatter160—~3.8kAutomated safety check: PassAGPL-3.0
UI StylingOhh-889/skyroc79513 repos~2.5kAutomated safety check: PassMIT
Frontend Design Routercode-yeongyu/oh-my-openagent70k—~5.3kAutomated safety check: PassCustom licence
Local Testinglobehub/lobe-ui2.2k—~2.1kAutomated safety check: PassMIT
Refero Designreferodesign/refero_skill295—~5.3kAutomated safety check: PassMIT
Muiblencorp/claude-code-kit1062 repos~2.4kAutomated safety check: PassMIT

Similar skills

  • UI Styling

    Ohh-889/skyroc

    Create beautiful, accessible user interfaces with shadcn/ui components (built on Radix UI + Tailwind), Tailwind CSS utility-first styling, and canvas-based visual designs.

    795 GitHub starsUsed in 13 repos~2.5k tokens
    Frontend & DesignAuto-check passed
  • Frontend Design Router

    code-yeongyu/oh-my-openagent

    Routes frontend and UI work through design reference rulesets, with a design-system gate plus layout and print guidance, before any UI code is written.

    70k GitHub stars~5.3k tokensUpdated today
    Frontend & DesignAuto-check passed
  • Local Testing

    lobehub/lobe-ui

    Local browser verification for the lobe-ui component library and documentation site.

    2.2k GitHub stars~2.1k tokensUpdated today
    Frontend & DesignAuto-check passed
  • Refero Design

    referodesign/refero_skill

    Primary/default skill for UI design, product design, web design, landing pages, dashboards, product screens, redesigns, visual polish, frontend/CSS styling, design systems, components, responsive…

    295 GitHub stars~5.3k tokensUpdated 1 mo ago
    Frontend & DesignAuto-check passed
  • Mui

    blencorp/claude-code-kit

    Material-UI v7 component library patterns including sx prop styling, theme integration, responsive design, and MUI-specific hooks.

    106 GitHub starsUsed in 2 repos~2.4k tokens
    Frontend & DesignAuto-check passed
  • Material Design 3 UI/UX Guide

    skydashnet/material-design-3-ui-skill

    Guides designing, reviewing or implementing interfaces that follow Google's Material Design 3 system: semantic tokens, component states, adaptive layout and accessibility.

    130 GitHub stars~3k tokensUpdated 8 days ago
    Frontend & DesignAuto-check passed

More from letehaha/moneymatter

All 10 skills in this repo
  • Spec Planner

    letehaha/moneymatter

    Deep-dive specification planner. An agent skill from letehaha/moneymatter.

    160 GitHub stars~1.8k tokensUpdated today
    Auto-check: notes
  • Add MCP Tool

    letehaha/moneymatter

    Add a new tool to the MoneyMatter MCP server. An agent skill from letehaha/moneymatter.

    160 GitHub stars~1.9k tokensUpdated today
    Auto-check: notes
  • Find Slop

    letehaha/moneymatter

    Hunt "AI slop" in this codebase — duplication, reinvented wheels, over-engineering, defensive cruft, dead code, comment slop, performance antipatterns.

    160 GitHub stars~3k tokensUpdated today
    Auto-check passed
  • I18n Before Release

    letehaha/moneymatter

    Occasional i18n catch-up: strip unused en keys, then fill any uk translations that slipped through via i18n-editor subagents.

    160 GitHub stars~1.1k tokensUpdated today
    Auto-check passed
  • E2E Test Creator

    letehaha/moneymatter

    Creates backend e2e tests for new or existing endpoints. An agent skill from letehaha/moneymatter.

    160 GitHub stars~2.4k tokensUpdated today
    Auto-check: warnings
  • Prd Creator

    letehaha/moneymatter

    Smart PRD generator. An agent skill from letehaha/moneymatter.

    160 GitHub stars~1.1k tokensUpdated today
    Auto-check: notes

Questions about Frontend Rules

What does Frontend Rules do?

Frontend conventions and component usage rules. An agent skill from letehaha/moneymatter. Frontend Rules is an agent skill from letehaha/moneymatter. Frontend conventions and component usage rules.

When should I use Frontend Rules?

Frontend Rules fits situations like: /frontend-rules; starting frontend work.

How do I install Frontend Rules in Claude Code?

Run `npx skills add letehaha/moneymatter --skill frontend-rules -a claude-code`. Or copy the skill folder (.claude/skills/frontend-rules in letehaha/moneymatter) into .claude/skills/frontend-rules in your project. Claude Code loads it when a task matches its description.

How do I install Frontend Rules in Codex?

Run `npx skills add letehaha/moneymatter --skill frontend-rules -a codex`. Or copy the skill folder (.claude/skills/frontend-rules in letehaha/moneymatter) into .agents/skills/frontend-rules in your project. Codex loads it when a task matches its description.

Can I use Frontend Rules in Cursor, Gemini CLI or GitHub Copilot?

Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add letehaha/moneymatter --skill frontend-rules -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/frontend-rules, .gemini/skills/frontend-rules, .github/skills/frontend-rules and .opencode/skills/frontend-rules in your project.

What does Frontend Rules need to run?

SKILL.md names no scripts, command-line tools or credentials: Frontend Rules is instructions for the agent only. Its frontmatter pre-approves these tools: Read, Grep, Glob.

Does Frontend Rules access the network?

SKILL.md contains no URLs. Any network use would come from the scripts or tools the agent runs. This is read from the text; nothing was executed.

Is Frontend Rules safe to install?

Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.

What licence does Frontend Rules use?

Frontend Rules is published under the AGPL-3.0 licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.

How many tokens does Frontend Rules use?

About 3.8k tokens (SKILL.md is roughly 15k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.

What are the alternatives to Frontend Rules?

Skills that share tags, products or a category with Frontend Rules: UI Styling (Ohh-889/skyroc, 795 stars), Frontend Design Router (code-yeongyu/oh-my-openagent, 70k stars), Local Testing (lobehub/lobe-ui, 2.2k stars) and Refero Design (referodesign/refero_skill, 295 stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.

Who maintains Frontend Rules?

letehaha (a GitHub user) maintains it in letehaha/moneymatter, which has 160 GitHub stars. The repository holds 10 skills in this directory. The repository was last updated on October 7, 2026.

Source: letehaha/moneymatter on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.