# Gritmind - Web & UI > File paths in this reference (`src/...`) point into the `gritmind-website-v2` > repo, which holds the reference implementations. ## Token architecture Two layers, and the split matters. **Layer 1** defines raw values on selectors, so they swap at runtime: ```css :root { --signal: #5e8e81; --ink: #131938; … } html[data-theme="dark"] { --signal: #93e0c6; --ink: #ffffff; … } html[data-accent="blue"], [data-accent="blue"] { --signal: #2156f6; } html[data-theme="dark"][data-accent="blue"], html[data-theme="dark"] [data-accent="blue"] { --signal: #3898ff; } ``` Both selector forms are required for each accent. `data-accent` may sit on `` (whole page) or on a descendant (one section) - a descendant combinator alone cannot match the element it is written on, and `html[data-theme="dark"]` would otherwise out-specify a bare `[data-accent]` and drag the accent back to mint. **Layer 2** maps them into Tailwind with `@theme inline`: ```css @theme inline { --color-signal: var(--signal); } ``` `inline` is load-bearing. Without it Tailwind bakes the resolved literal into each utility (`color:#5e8e81`) and subtree overrides silently do nothing. With it you get `color:var(--signal)`, which re-resolves at any nesting depth. ## Scoped accents Put `data-accent="blue"` on any element and the whole subtree recolours - CSS and canvas alike. Use it for a secondary page, a case study, or a single section. Never two accents visible at once. For canvas, resolve the colour from the element rather than a global: ```ts const raw = getComputedStyle(canvasEl).getPropertyValue('--signal'); ``` Reading from a global attribute breaks scoping the moment a canvas sits inside an accented subtree. ## Type scale | Role | Size | Line | Weight | Font | |---|---|---|---|---| | Display / h1 | `clamp(34px,4.4vw,52px)` | 1.08 | 500 | sans | | Section h2 | `clamp(30px,3.6vw,46px)` | 1.1 | 500 | sans | | Card h3 | 26px | 1.15 | 500 | sans | | Card h4 | 20px | 1.2 | 600 | sans | | Lede | 20px | 1.4 | 400 | sans | | Body | 16px | 1.5 | 400 | sans | | Long-form body | 18px | 1.7 | 400 | sans, `--body` colour | | Eyebrow | 14px | 20px | 400 | **mono, uppercase** | | Stat figure | 52px | 56px | 500 | **mono** | Everything sans carries `letter-spacing: -0.02em`. Long-form article text uses `--body` (`#333a49` / `#cdd6e8`), which is softer than `--ink` - reserve full ink for headings. ## Layout - Container `1240px`, gutter `40px` (`22px` under 560px) - Section rhythm `100px` vertical, `64px` on mobile - Section boundaries are a single `1px solid var(--hairline)` rule - never a filled band, never a shadow - Card grid gap `18–20px`; card padding `24px` (`32px` for a feature card) - Sticky nav `68px`, `color-mix(in srgb, var(--bg) 72%, transparent)` plus `backdrop-filter: saturate(180%) blur(14px)` ## Components **Eyebrow** - mono, uppercase, 60% ink. Optional 8px accent dot to its left. Sits directly above a heading with 12px below. **Button** - 40px tall, fully rounded (`999px`), 14px/500. Primary is `#141936` with white text, inverting to white-on-navy in dark mode. **Card** - `var(--surface)`, 20px radius, no border. Elevation comes from the hover shadow only: `translateY(-4px)` plus `0 16px 40px rgba(19,25,56,.10)`. Resting cards are flat. **Marquee** - CSS animation on a track duplicated inline, translating `-50%`. 34s for logos, 80s for testimonials. Pause on hover. Mask both edges with a `linear-gradient` to transparent. Never rebuild this in JS. ## Canvas work Any data or diagram canvas - section graphics, glyph tiles, animated illustrations - follows the full illustration grammar in `references/canvas.md`: the mark vocabulary, the three interaction tiers (static SVG / scroll-gated / hover-gated), timing, and the useCanvas/paletteFor code contract. The short version: quiet ink structure, exactly one live accent element, calm loops, and `prefers-reduced-motion` renders the resolved end state. ## Accessibility - Focus ring: `2px solid var(--signal)`, `outline-offset: 3px`, on `:focus-visible` only - Every canvas gets `role="img"` and an `aria-label` describing what it depicts, not that it is animated - Decorative background graphics get `aria-hidden="true"` - Modals: trap Tab, close on Escape, restore focus to the trigger, lock body scroll - Accent mint `#5e8e81` on off-white measures 3.55:1, so in light mode it passes WCAG AA only for large text: 24px+, or 18.7px+ bold. Dark-mode mint `#93e0c6` on the dark ground measures 9.98:1. Whether smaller accent text is allowed in light mode is an open decision in `CHECKLIST.md`. Never rely on colour alone to carry meaning - Error states use `--error` (`#fb0102` / `#ff452d`), never the accent