# Varick Agents: design system The authoritative brand, token and material reference for Varick Agents (legal entity: SolidCart, Inc, d/b/a Varick Agents). Machine-readable tokens: `/tokens.json` · `/tokens.css` Human version: this site. Upstream sources this mirrors: - web tokens: `varick-site/app/globals.css` - charts: `varick-design-skills/varick-charts/references/chart-spec.md` - documents: `varick-design-skills/varick-docs/SKILL.md` (`const V`) If this file and one of those disagree, the upstream file wins for its own surface, and this file should be regenerated. --- ## 0. The rule that matters most When is accent blue allowed? Documents outlive the app. A memo from 2024 and an invoice from today have to read as one company, so the document palette is deliberately narrower and frozen. The web can move faster. Legal: - Web and product UI: links, focus rings, active states, selected rows. - Charts, as the second series (`--chart-2`). - Marketing imagery and social templates, as a tint. Not legal: - Any document the company issues: memo, proposal, report, invoice, engagement letter. - Print of any kind. - Deck body pages. Navy carries the hierarchy there. --- ## 1. Colour ### Core: four values carry the brand | Token | Hex | Name | Use | |---|---|---|---| | `--brand-navy` | #002870 | Navy | The brand. Headings, wordmark, table header fills, the first chart series. | | `--foreground` | #111111 | Ink | Primary text and values on web. Near-black, never pure black. | | `--background` | #FFFFFF | White | The canvas. Cards, tooltips, page ground. | | `--brand` | #3B7CF6 | Accent blue | Web and product UI only. Interactive states, second chart series. | ### Web and product | Token | Hex | Name | Use | |---|---|---|---| | `--brand-wash` | #E8F1FE | Accent wash | Tinted fills behind accent content, selection background. | | `--accent-foreground` | #2F6BE0 | Accent text | Accent-coloured text where #3B7CF6 fails contrast. | | `--muted` | #F6F6F6 | Muted | Secondary surfaces, inert fills. | | `--page` | #FBFBFB | Page | The ground behind cards. One step off white. | | `--muted-foreground` | #757575 | Muted text | Axis ticks, sub-labels, captions, notes. | | `--fg-low` | #B1B1B1 | Low text | The quietest legible text. Placeholders, disabled. | | `--border` | #EBEBEB | Hairline | Every border and grid dash. The structural line of the system. | | `--hairline-hover` | #DEDEDE | Hairline strong | Border on hover, or where a hairline needs to assert itself. | | `--destructive` | #D55F5A | Destructive | Errors and destructive actions. The only warm hue in the system. | | `--fill-soft` | #FDFDFD | Fill soft | The quietest surface. One step off white, used inside component cards. | | `--accent-sep` | #C6DBFC | Accent separator | Hairline inside an accent-tinted surface, where the standard border would vanish. | ### Data visualisation: chart-spec.md is authoritative | Token | Hex | Name | Use | |---|---|---|---| | `--chart-1` | #002870 | Chart 1 | Primary navy. First and most important series, ridges. | | `--chart-2` | #3B7CF6 | Chart 2 | Accent blue. Second series. | | `--chart-3` | #8B95A1 | Chart 3 | Mid grey. Third series, or a de-emphasised one. | | `--chart-4` | #C4CBD4 | Chart 4 | Light grey. Fourth series. | | `--chart-5` | #E4E8ED | Chart 5 | Lightest grey. Fifth series, or the "other" bucket. | Past five series, interpolate between navy and grey. Never introduce a new hue. If a request references a yellow "golden quadrant" or a pink chart, recolour into this ramp, the reference is to the treatment, never the colour. ### Documents and print: navy plus greys, that is the whole palette | Token | Hex | Name | Use | |---|---|---|---| | `--NAVY` | #002870 | Navy | Headings, title, labels, table header fill, bullets. | | `--NAVY_MID` | #3E6FA8 | Navy mid | Indicative gantt bars. Nothing else, ever. | | `--INK` | #1A1A1A | Ink | Body text. | | `--INK_SOFT` | #3F3F3F | Ink soft | Secondary text inside tables. | | `--GREY` | #8A8A8A | Grey | Meta labels, notes, footer. | | `--RULE` | #D9D9D9 | Rule | Table strokes. | | `--HAIRLINE` | #E7E7E7 | Hairline | Dividers inside meta rows and signature fields. | | `--BAND` | #F3F6FB | Band | Band and callout fill. | | `--LABEL_BG` | #FAFAFA | Label bg | The deflist label column. | | `--WHITE` | #FFFFFF | White | Page. | ### Accessibility - Body copy needs 4.5:1. Large text (>=24px, or >=18.66px at 500) and UI borders need 3:1. - `--fg-low` #B1B1B1 is 2.1:1 on white. Never use it for text a reader must read. - `--brand` #3B7CF6 lands just under 4.5:1. Use `--accent-foreground` #2F6BE0 for accent text. - Hairlines carrying no information are exempt from the border rule. A border that is the only focus signal is not. --- ## 2. Typography ### Season Mix - CSS variable: `--font-season` - Stack: `var(--font-season), ui-serif, Georgia, "Times New Roman", serif` - Weights: 400 only - Role: Editorial serif. Display headlines and the brand wordmark. - Where: The home page and /brand. Marketing site headlines, article titles. - Licence: TRIAL licence in the repo. Buy before any paid placement or print run. ### Helvetica Neue - CSS variable: `--font-sans` - Stack: `"Helvetica Neue", Helvetica, Arial, sans-serif` - Weights: 400, 500 - Role: The interface face and the document face. One family for both. - Where: Everything. Web UI, every .docx and PDF the company issues, every chart. - Licence: System font on macOS and iOS. Falls back to Helvetica then Arial elsewhere; that is accepted, do not substitute a different family to "fix" it. ### Menlo - CSS variable: `--font-mono` - Stack: `'Menlo', ui-monospace, monospace` - Weights: 400 - Role: Code, tokens, hex values, terminal output. - Where: This site and engineering surfaces. Never inside a chart or a document. - Licence: System font. Nothing to buy and nothing to load. ### Web scale | Role | Size | Weight | Tracking | Notes | |---|---|---|---|---| | Display | 64px / 1.02 | 400 | -0.03em | Section landing headline. Season Mix, scoped to the home page and /brand. | | H1 | 48px / 1.06 | 400 | -0.025em | Page title. Every page has exactly one. | | H2 | 32px / 1.15 | 400 | -0.02em | Section heading, set in navy so the navy-carries-hierarchy rule is visibly true. | | H3 | 22px / 1.3 | 500 | -0.015em | Subhead. 500 is the heaviest weight in the system. | | Body | 16px / 1.6 | 400 | -0.01em | Running prose. The default, and the only size prose is ever set at. | | Small | 14px / 1.55 | 400 | -0.01em | Table cells, captions, secondary copy. Replaces the old 11 to 13.5px cluster. | | UI body | 13px / 1.4 | 400 | -0.01em | Product UI: navigation items, table cells, menu items, breadcrumbs. | | UI control | 12.5px / 1.4 | 400 or 500 | -0.01em | Product UI: buttons, tabs, form fields, card titles, row titles. 500 for the control label, 400 for the value. | | UI meta | 11.5px / 1.4 | 400 or 500 | 0 | Product UI: status pills, meta lines, hints, code chips. The smallest size that carries words. | | Label | 11px / 1.4 | 400 | 0.04em | Uppercase. 11px is the legibility floor; do not go below it. | ### Document scale: docx half-points | Token | Half-points | Points | Role | Colour | |---|---|---|---|---| | `SZ_TITLE` | 34 | 17pt | Document title | Navy | | `SZ_H1` | 28 | 14pt | Numbered section heading | Navy | | `SZ_H2` | 24 | 12pt | Subhead inside a section | Navy | | `SZ_BODY` | 21 | 10.5pt | Body copy and every table cell | Ink | | `SZ_META` | 18 | 9pt | Meta rows, footer, gantt labels | Grey | | `SZ_LABEL` | 16 | 8pt | Small-caps kicker, column labels | Grey | | `SZ_NOTE` | 15 | 7.5pt | Grey qualifier, "illustrative only" | Grey | ### Chart sizes - Card label: 10px uppercase, 0.04em, 400 - Card heading: 14–22px, 500 - Axis tick: 12px, 400 - Tooltip: 12px - Standings name: 14px, 500 - Value: 13px, 500, tabular-nums - Legend: 12px, 400 ### The label look: replaces monospace labels everywhere ```css font-size: 10px; letter-spacing: 0.04em; text-transform: uppercase; font-weight: 400; color: var(--muted-foreground); ``` ### Allowed - Weights 400 and 500. 500 is the heaviest weight in the system. - `font-variant-numeric: tabular-nums` on any column of figures. - Uppercase at 0.04em tracking for labels. - Body tracking -0.01em; headings -0.02em. - `font-synthesis: none`, so no browser fakes a weight the family does not have. ### Forbidden - Weight 600, 700, 800, 900 or the keyword `bold`. - Italic, and especially synthesised italic: set `font-synthesis: none`. - A monospace face inside a chart or a document. - A serif face inside a chart or a document. - Centred text. The whole system is left-aligned. Hierarchy comes from navy, size and small caps. Never from weight. In documents, `bold: false` is written on every run. --- ## 3. Space, radius, motion Space: 4px base. Steps 0.5, 1, 2, 3, 4, 5, 6, 8, 10, 12, 16, 20, 24 (multiply by 4 for px). Maps one-to-one onto Tailwind's default scale. ### Radius | Token | Calc | px | Use | |---|---|---|---| | `--radius-sm` | `4px` | 4 | Buttons, inputs, chips, badges, checkboxes, tabs, table cells, kbd. | | `--radius-lg` | `8px` | 8 | Cards, panels, menus, dialogs, tiles, the navigation and the search bar. | Inside a chart the scale is overridden: cards 12, controls 6, bars 4 (or [6,6,0,0] for columns), tracks 4. Never fully pill unless explicitly asked. ### Borders, not shadows Almost every boundary is a 1px `#EBEBEB` hairline. `#DEDEDE` on hover. `#D9D9D9` for table strokes in a document. Shadows appear in exactly one place: a chart tooltip at `0 8px 24px rgba(0,0,0,0.08)`. ### Easing - `--ease-out`: `cubic-bezier(0.23, 1, 0.32, 1)`, Everything entering. The house curve. - `--ease-in-out`: `cubic-bezier(0.77, 0, 0.175, 1)`, Anything that moves and returns. ### Durations - Press feedback: 160ms, `:active { transform: scale(0.97) }` - Colour settle: 150ms, Every link and control, applied at the base rather than per call site - Fade: 300ms, Opacity-only changes - Page and nav enter: 480ms, `opacity` plus a 10px `translateY`, once per navigation - Standings bar fill: 280ms, 30ms stagger per row - Bar chart mount: 800ms, `scaleX()` from `left center` - Line and area draw: 900ms, Recharts `animationDuration` - Logo marquee: 40000ms, linear, infinite, pauses on hover ### Motion rules - Enter once, on mount. Nothing re-animates on scroll. - Only animate `transform` and `opacity`. Never width, height or layout. - Never animate a keyboard-triggered action. A tab switch changes content instantly. - Never enter from `scale(0)`. Start at `scale(0.95)` with opacity if you must fade in. - Every interactive element gets press feedback. - Honour `prefers-reduced-motion`: render the final frame, do not just shorten the animation. --- ## 4. Trademark Documents render the wordmark PNG, never the SVG. The SVG is stroke-based. Browsers render it correctly, but the docx toolchain draws hollow, outlined letterforms from it. The PNG (cairosvg at scale=8, 3856 × 288) is what every document pipeline uses. Files: - `/brand/varick-logomark.png`: 840 × 840 master. 288 and 128px cuts alongside. The V over the bridge. - `/brand/varick-wordmark.png`: 3856 × 288. What every document renders. - `/brand/varick-wordmark.svg`: true vector. Browsers render it correctly; the docx toolchain renders it hollow, so documents use the PNG. - `/brand/varick-combination.png` and `.svg`: logomark plus wordmark, 1140 × 288. - `/brand/varick-favicon.png`: the logomark at 128, the site favicon. - The old `icon.svg` and `varick-mark.svg` were v0.app's logo from the scaffold that generated the marketing site. They are deleted from this repo; varickagents.com still ships them and needs the same fix. ### Do - Scale the lockup to the full content width. In documents that is 6.7in, which puts it at 0.5in tall. - Keep clear space of at least the height of the mark on every side. - Put the lockup in the page header so it repeats on every page. - Place it on white, or on navy where it is the reversed cut. ### Do not - Do not add a second rule under the lockup. The hairline is part of the asset and it *is* the divider. - Do not add a tagline under the wordmark. It was removed deliberately. - Do not replace the wordmark with type. The masthead is an image, always. - Do not stretch, skew, outline, or apply an effect or shadow. - Do not recolour it. Navy on white, or reversed. Nothing else. - Do not place it on a busy photograph or a low-contrast background. - Do not call the masthead helper inside the document body. Header only. --- ## 5. Document layout: dxa, 1440 = 1 inch | Token | dxa | Inches | Note | |---|---|---|---| | `PAGE_W × PAGE_H` | 12240 × 15840 | 8.5in × 11in | US Letter. | | `MARGIN_X` | 1296 | 0.9in | Left and right. | | `MARGIN_TOP` | 1900 | 1.32in | Clears the masthead by ~0.3in. | | `MARGIN_BOTTOM` | 1080 | 0.75in | | | `CONTENT_W` | 9648 | 6.7in | The wordmark scales to exactly this. | | `HEADER_DIST` | 720 | 0.5in | | | `FOOTER_DIST` | 620 | 0.43in | | | `LINE` | 276 | 1.15 | Line height, all body copy. | | `SP_BEFORE / SP_AFTER` | 200 / 140 | n/a | Fixed for every block. Do not tune per block. | Spacing is fixed: `SP_BEFORE` 200, `SP_AFTER` 140, `LINE` 276. Do not tune per block. If a document runs long, cut copy. Watch for: - One `table()` call however long. It flows across the page break and the navy header repeats. - `section()` and `subhead()` carry `keepNext`. Cut copy rather than inserting a `pagebreak`. - Table widths must sum to `CONTENT_W`. Use `fitWidths()`. Percentage widths break in Google Docs. - `ShadingType.CLEAR`, never `SOLID`, solid renders black. - `Page X of Y` is a live Word field. Never a literal number. - Never hand-edit a .docx. Change `buildDoc()` and rebuild. --- ## 6. Materials and which skill owns them | Material | Skill | Deliverable | |---|---|---| | Engagement letter / SOW | `varick-engagement-letter` | .docx (editable in Google Docs) plus a matching PDF | | Memo, proposal, brief, policy | `varick-docs` | .docx, PDF on request | | Charts and data viz | `varick-charts` | High-res PNG for print and static embeds, self-contained HTML for blogs | | Case study, RFI response, proposal (web) | `varick-doc-builder` | .txt using the builder marker syntax | | Content calendar and social | `varick-content-calendar` | Notion database rows plus produced assets | ### Routing | Skill | Trigger | Produces | In local pack | |---|---|---|---| | `varick-engagement-letter` | Engagement letter, scope letter, SOW, Phase 1 letter, diagnostic scope, countersignature doc. | .docx + PDF from a JSON spec | yes, Downloads/varick-engagement-letter | | `varick-invoice` | An invoice or a bill. | .docx + PDF | no | | `varick-report` | A multi-page navy-cover report, explainer or leave-behind. | .docx + PDF | no | | `varick-charts` | Chart, graph, data viz, leaderboard, standings, benchmark, "make this data look good". | PNG or self-contained HTML | yes, Downloads/varick-design-skills/varick-charts | | `varick-docs` | Anything else in a document: memo, proposal, brief, policy, process doc, client writeup. | .docx | yes, Downloads/varick-design-skills/varick-docs | | `varick-doc-builder` | Case study or RFI response for demo.varickagents.com, or the doc builder is named. | .txt in marker syntax | yes, .claude/skills/varick-doc-builder | | `varick-content-calendar` | Content calendar, "what am I shipping this week", carousel from an article, next quarter of slots. | Notion rows + produced assets | yes, Downloads/varick-content-calendar | | `varick-agents-brand` | A one-pager or a pre-call brief. | PDF | no | Not in the local pack: `varick-invoice` (Invoices and bills. Shares the masthead asset with varick-docs.), `varick-report` (Multi-page navy-cover reports, explainers and leave-behinds.), `varick-agents-brand` (One-pagers and pre-call briefs.) ### Per-material rules **Engagement letter / SOW** (`varick-engagement-letter`) - Content comes from a JSON spec. The visual system lives in build_letter.py and letter-base.docx, never in the JSON. - The legal entity is SolidCart, Inc (d/b/a Varick Agents). - Number every section. - Gantt spans are [first, last, kind]. `solid` is committed, `light` is indicative. **Memo, proposal, brief, policy** (`varick-docs`) - Copy references/template.js, rewrite buildDoc(), keep the helpers. Rebuilding blocks is how the house style drifts. - Seventeen block helpers cover everything. One block, one renderer. - `**double asterisks**` renders navy. That is the only inline emphasis there is. - Spacing is fixed at SP_BEFORE 200 / SP_AFTER 140. If a document runs long, cut copy, do not tune spacing. - Never hand-edit the .docx. Change buildDoc() and rebuild. **Charts and data viz** (`varick-charts`) - chart-spec.md is the source of truth, not globals.css. - Default to standings for anything called a leaderboard, ranking or benchmark. - Grid is `4 4` dashes, horizontal only, except scatter. - Past five series, interpolate navy→grey. Never introduce a new hue. - The PNG renderer screenshots the live HTML, so the two can never diverge. **Case study, RFI response, proposal (web)** (`varick-doc-builder`) - Confirm the live marker list before finalising, the builder gains block types between sessions. - The deliverable is the .txt, not a screenshot of the result. **Content calendar and social** (`varick-content-calendar`) - The calendar lives in one Notion database. Nothing is hand-typed except the cadence config and the ideas. - Slot generation, drafting, asset production and fan-out are four separate automations. Do not conflate them. --- ## 7. LinkedIn carousels - Canvas: 1080 × 1350 (4:5) - Delivered as a single PDF. LinkedIn will not accept a set of images. - 8 to 12 slides. - Cover carries the wordmark and title only. Interiors carry a slide number, not the wordmark. - Charts onto slides render at `--scale 3`, not 2. - LinkedIn renders carousels as a PDF. Export every slide at 1080 × 1350 and combine, do not mix ratios. - Cover slide carries the wordmark. Interior slides do not. They carry a slide number instead. - One idea per slide. If a slide needs two sentences of setup, it is two slides. - Navy on white for interiors. The cover may use a background image at low contrast. - Type is Helvetica Neue, 400/500, left-aligned. Same rules as a document. - Eight to twelve slides. Past twelve, readers drop before the CTA. Note: The current covers are marked FONT-FALLBACK / TYPE IS NOT FINAL. Season Mix was not embedded when they were cut, so the type on them is a fallback face. Re-cut before reusing them as templates. --- ## 8. Imagery ### Do - Real infrastructure, real workplaces, real operational detail. - Cool, restrained grading. Navy and grey read as the same family as the palette. - Wide crops with room for type to sit over them. - Diagrams over photographs when the point is a system. ### Do not - No stock handshakes, no glowing brains, no circuit-board abstractions. - No oversaturated grading and no warm nostalgia filters. - No generic robots or humanoid AI. We sell operational work, not science fiction. - No text baked into a background image. It cannot be translated or restyled. Never bake text into a background image. It cannot be translated, restyled or corrected without regenerating the image. --- ## 9. Writing Voice: a senior partner explaining something to a smart friend. Polished, confident, direct. Every word earns its place. Never trying to impress. ### Always - Use contractions. "We don't", not "we do not". - Be specific over abstract. Name the tool, the role, the number. - Hyperlink URLs. Never paste a raw one. - Number section headings in anything client-facing. - Prefer a table over a wall of prose, so a reader can jump to the row that matters to them. - On the company account, "we". Never corporate third person, Varick does not refer to itself by name. - The legal entity is SolidCart, Inc (d/b/a Varick Agents). ### Never - Em dashes. The character U+2014 and its "--" substitute, everywhere, including internal docs and code comments. - "Here's X" mid-flow in short-form. - Dramatic short fragments. "The tools were fine. The problem was X." - "No X. No Y. No Z." fragment lists. - The reframe family, in every variant: "it's not X, it's Y", "not X but Y", "X isn't the Y, it's the Z". - Isolated one-liner reveals, and repetition for emphasis. - Exclamation marks, unless the excitement is real. - "fundamentally" and "game-changing". - Borrowed-pundit clichés: "said the quiet part out loud", "make no mistake", "the elephant in the room", "let that sink in", "to be clear". - "I" on the company account. That is the personal account's voice, not the company's. ### Long-form is different The bans target filler in short-form. In long-form these stay: "That said,", "The reality is,", "Here, [next idea]…", "Same logic applies to…", "Which brings us to…". Generic anti-AI checklists say to cut these. In Varick long-form they stay. ### Honest limitation Following these rules does not make a draft undetectable. AI-authored prose scores near 100% AI on Pangram regardless of style; restyling does not move the number. On long-form, produce the tightest on-voice draft and say plainly that it still flags until it is retyped by hand. Never claim a draft passes. Source: pack-naomi/how-we-write/varick-writing-RULES.md, plus anti-ai-writing before drafting. --- ## 10. Components The Varick OS component library lives at `/system`, one page per component at `/system/` with usage, variants, API, accessibility notes and the app's CSS. Values are the shipping app's. Conventions: one function per part, variants and sizes as props, className and native props pass through, `data-slot` on every root, every state styled. No runtime dependencies; behaviour is native elements plus ARIA. Pages marked "Proposed" (Kbd, Skeleton, Spinner, Tooltip, Empty) are not in the app yet. **Tokens**: [Colours](/system/colors), [Typography](/system/type) **Actions**: [Button](/system/button), [IconButton](/system/icon-button), [DropdownMenu](/system/dropdown-menu), [Tabs](/system/tabs), [FilterChip](/system/filter-chip) **Forms**: [Input](/system/input), [Textarea](/system/textarea), [Select](/system/select), [Checkbox](/system/checkbox), [Switch](/system/switch), [Field](/system/field) **Display**: [Status](/system/status), [Badge](/system/badge), [CodeChip](/system/code-chip), [Kbd](/system/kbd), [Avatar](/system/avatar), [Kpi](/system/kpi), [Sparkline](/system/sparkline), [Progress](/system/progress), [Separator](/system/separator), [Skeleton](/system/skeleton), [Spinner](/system/spinner) **Layout**: [Card](/system/card), [SideCard](/system/side-card), [Table](/system/table), [Pagination](/system/pagination), [Breadcrumb](/system/breadcrumb), [TaskRow](/system/task-row), [SideNav](/system/side-nav), [Stepper](/system/stepper), [Terminal](/system/terminal) **Agent runs**: [Loader](/system/loader), [Trace](/system/trace), [ToolCalls](/system/tool-calls), [RunTasks](/system/run-tasks), [ContextCards](/system/context-cards), [Flowchart](/system/flowchart) **Feedback**: [Alert](/system/alert), [Dialog](/system/dialog), [Tooltip](/system/tooltip), [Empty](/system/empty) Generated from `lib/tokens.ts` and `lib/catalog.ts`. Do not hand-edit; change the source and redeploy.