Skip to main content

Engineering conventions

These are the standing rules for working in the ContentsVision repo. They live in full in the repo's CLAUDE.md; this page summarizes them so the whole team shares the same expectations. They apply to everyone, including AI assistants working in the codebase.

Run the tests after every change​

The hard rule, repeated because it matters: run ./run_tests.sh after any change, and keep it green before considering the work done. A change with no test is incomplete. Show real failing output; never claim green when it is red. Full detail in testing.

Writing style: no em dashes​

Never use em dashes anywhere: UI copy, code comments, commit messages, chat. Use a period, comma, colon, or parentheses instead. (This handbook follows the same rule.)

Frontend UI conventions​

Use the existing design system in frontend/src/app/globals.css rather than hand-rolling Tailwind for common controls. Skim globals.css first, and add a new component class there rather than inlining styles.

ElementUseNot
Inputs.as-input (or .as-input-sm)raw <input className="border ...">
Selectsthe shared <StyledSelect> componenta native <select> (breaks the visual language)
Buttons.btn-primary / .btn-secondary / .btn-danger (+ .btn-sm, .press)ad-hoc button styles
Surfaces.card, .stat-cardbespoke panels
Labels.filter-label, .col-header, .eyebrowone-off label styles
Pagingthe shared <Pagination> componenta hand-rolled Prev/Next row

.as-select exists only as a legacy fallback and should be migrated to StyledSelect.

<Pagination> (frontend/src/components/Pagination.tsx) is Previous, numbered pages, and Next as one tight group, with an optional summary line pushed to the far left. Pages are zero-based in the props and the callback. It collapses long runs to 1 ... 5 6 7 ... 12, and drops to 1 ... 6 ... 12 below sm where there is no room for the neighbours. Only the insured media grid uses it today; the inventory table and Guided Review still have their own controls.

Motion​

Every user action should have a subtle animation; stillness reads as broken. Anything that mounts, unmounts, expands, collapses, or moves must animate in and out.

  • Use Framer Motion for conditionally rendered elements: wrap in <AnimatePresence> and give explicit initial, animate, and exit. Do not use a bare {cond && <div/>} for visible elements (the exit will not animate).
  • The CSS keyframe utilities (.animate-slide-down, .animate-fade-in, etc.) only animate the enter side; use them for things that mount once and stay.
  • Keep durations short (120-220ms). Use Material standard easing [0.4, 0, 0.2, 1] for panel/height transitions and easeOut for fades and pops.
  • Add .press to clickable elements for tactile feedback.
  • Re-key a list's container on sort/filter/pagination changes so rows re-mount and re-trigger their enter animation.

Add new keyframes under the "Motion primitives" section of globals.css, not as ad-hoc inline @keyframes.

Help Center articles​

The in-app Help Center (distinct from this handbook) has its own house style:

  • Short, verb-first headings; one task per heading; terse steps and bullets.
  • Every article starts from the home Dashboard and tells the reader how to navigate to where the article begins.
  • Concise to the point that removing one more word would break the sentence.
  • Use many screenshots, one under each step a reader might get stuck on.
  • Explain every feature in the flow; if it is not explained, it does not exist to the user.
  • When the reader might have more questions, point them to [email protected].

Handoff prompts​

Never save a handoff prompt or "paste this into the next session" text to a file. Put it directly in the chat reply, inside a single fenced code block, so it can be copied. Do not split it across sections or create files for it.

Why these exist​

The conventions keep the product coherent: one visual language, motion that signals state, copy in one voice, and a test suite that stays trustworthy. They also let any contributor (or any AI session) pick up work without re-litigating style. When in doubt, match the surrounding code's idiom, comment density, and naming.