# AMACA DESIGN SYSTEM — `design.md` > **Version** 2.0.0 — 2026.05 > **Author** Angelo Macaione > **Audience** AI coding assistants (Cursor, Copilot, Claude Code, Cline, Aider, Continue) and humans pairing with them inside an IDE. > **Purpose** Single-file context. Paste the whole document into the model's system prompt, project rules file (`.cursor/rules`, `CLAUDE.md`, `.continuerules`, `.windsurfrules`), or repo root. Every output the model produces against this system should sound, look, and behave like the rest of the work. --- ## 0. How to use this file This is a **specification + a contract**. Treat it as canonical. When code or output disagrees with this file, this file wins. ### 0.1 For the AI agent When you generate UI code, you must: 1. **Use only the tokens in § 2.** Never invent a hex value, never use a raw `px` for spacing if a token covers it, never write a `cubic-bezier(...)` letterally if one of the four named easings already matches. If the value you need isn't in the scale, **stop and ask** — don't extend the system silently. 2. **Reference tokens by CSS variable name** (`var(--magenta-500)`, `var(--s-6)`, `var(--ease-decel)`). Hardcoded literals in component code are a regression. 3. **Match the seven principles in § 1.** If the user asks for something that violates a principle, surface the conflict and propose a compliant alternative before writing code. 4. **Write canonical HTML.** Close every non-void element explicitly. Double-quote every attribute. No implied closes (write `
…
`). 5. **Don't add filler.** No placeholder copy, no decorative icons, no unrequested sections. One thousand no's for every yes. 6. **Don't decorate with motion.** Motion is feedback (§ 1.5, § 8). If an animation doesn't communicate state change, it doesn't ship. 7. **Always honor `prefers-reduced-motion: reduce`.** Every transition you add needs a media-query fallback. 8. **Show your work.** When you make a non-obvious choice (which token, which variant, why), say it in a one-line comment above the affected line. Brevity over prose. ### 0.2 For the human Keep this file at repo root. Update the version line on every breaking change. The system is dark-first, dark-only at v1.x — light-mode resolution is planned for v2. --- ## 1. Principles · five rules, no exceptions Every output is graded against these. Cite the number when explaining a tradeoff. ### 1.1 Clarity before cleverness The obvious answer, on purpose. If the user has to decode an icon or guess at a label, we failed. Metaphors earn their keep or get cut. **For agents:** prefer a labeled button over an icon-only button. Prefer a verbose variable name over a clever one. Prefer 4 lines of explicit code over 1 line of trick. ### 1.2 Evidence over opinion Every decision shows its work. The dead end is part of the file too. When you make a choice, leave a trace — a comment, a token reference, a link to the spec. **For agents:** when the user asks "why," answer with the principle number, the token reference, or the prior commit. Never "because it looks better." ### 1.3 Precision is a feeling Rigor the eye picks up before the mind does. Spacing on a 4px grid. Type on the scale. Motion on the curve. When they're locked, the work reads as considered before a word is parsed. **For agents:** never use values that aren't on a scale. `padding: 14px` is wrong; `padding: var(--s-3) var(--s-4)` is right. ### 1.4 Quiet, then loud Restraint is what makes accents land. Most of the surface stays neutral. Color, motion, weight — they only show up where the work needs them. **For agents:** the **85 / 10 / 5** law. 85% of any surface is `--obsidian-*`. ~10% is supporting (cyan, petrol, semantic). ≤5% is `--magenta-*`. If you're using magenta on more than one element per viewport, you're decorating. ### 1.5 Motion is a material Interfaces are not static. The way something arrives, settles, responds carries meaning. Every surface breathes on the same curve — `cubic-bezier(0.16, 1, 0.3, 1)` — so the whole document feels like one instrument. **For agents:** motion communicates state change (entering, exiting, focus, error). Motion that doesn't communicate gets cut. --- ## 2. Tokens · the only acceptable values All tokens live in `styles/tokens.css` and are exposed as CSS custom properties. **Reference by name, never copy the value.** ### 2.1 Color · neutrals (Obsidian scale) | Token | Hex | Use | |---|---|---| | `--obsidian-950` | `#07090B` | Page background | | `--obsidian-900` | `#0B0E12` | Surface | | `--obsidian-850` | `#10141A` | Elevated surface | | `--obsidian-800` | `#161B22` | Card | | `--obsidian-700` | `#1E242D` | Border strong | | `--obsidian-600` | `#2A313B` | Border | | `--obsidian-500` | `#3A4451` | Muted edge | | `--obsidian-400` | `#5B6573` | Disabled text | | `--obsidian-300` | `#8A94A3` | Secondary text | | `--obsidian-200` | `#B8C0CB` | Tertiary text | | `--obsidian-100` | `#E3E7EC` | **Primary text on dark** | | `--obsidian-050` | `#F4F6F8` | Bone (max contrast) | ### 2.2 Color · brand (Magenta primary) | Token | Hex | Use | |---|---|---| | `--magenta-100` | `#FFE3F8` | Tints, soft highlights | | `--magenta-200` | `#FFB5EC` | | | `--magenta-300` | `#FF85DF` | Hover state on magenta links | | `--magenta-400` | `#F868D8` | Links on dark, mono accents | | `--magenta-500` | `#F051D5` | **Primary brand · CTAs · focus rings** | | `--magenta-600` | `#C93BB0` | Pressed state | | `--magenta-700` | `#9A2D87` | Deep brand accents | | `--magenta-800` | `#66195A` | Reserved | ### 2.3 Color · supporting | Token | Hex | Role | |---|---|---| | `--secondary-400` | `#00FFF2` | Electric cyan (rare; data viz only) | | `--tertiary-500` | `#0C6078` | Petrol teal (deep dive accents) | | `--success` | `#3AFFC7` | Confirmation only | | `--warning` | `#F6C65B` | Caution only | | `--danger` | `#FF5B5B` | Error / destructive only | | `--info` | `#5CC8FF` | Inline info, links in long-form | **The 85/10/5 law:** ~85% of any surface uses `--obsidian-*`. ~10% supporting/semantic. ≤5% `--magenta-*`. Violations are visible at a glance. ### 2.4 Type Single typeface — **Satoshi** — across the whole system. `--font-sans` and `--font-mono` both resolve to Satoshi (the "mono" alias preserves intent for future swap; do not assume monospaced metrics). | Token | Size | Typical use | |---|---|---| | `--t-display` | `112px` | Marketing hero only | | `--t-h1` | `76px` | Page title (one per page) | | `--t-h2` | `52px` | Section title | | `--t-h3` | `38px` | Subsection title | | `--t-h4` | `30px` | Card title | | `--t-h5` | `24px` | Small heading | | `--t-h6` | `20px` | Eyebrow heading | | `--t-lead` | `18px` | Page lede / intro paragraph | | `--t-body` | `15px` | **Default body text** | | `--t-small` | `13px` | Captions, table cells | | `--t-caption` | `12px` | Smallest readable text | | `--t-micro` | `10px` | Mono labels, eyebrows, kbd keys | | Line height | Value | Use | |---|---|---| | `--lh-tight` | `1.05` | Display text | | `--lh-snug` | `1.2` | Headings | | `--lh-normal` | `1.45` | UI text | | `--lh-loose` | `1.65` | Running body copy | | Tracking | Value | Use | |---|---|---| | `--tr-tight` | `-0.04em` | Display | | `--tr-snug` | `-0.02em` | Headings | | `--tr-normal` | `0` | Body | | `--tr-wide` | `0.04em` | Small caps | | `--tr-mono` | `0.06em` | All-caps mono labels | **Rules:** - One H1 per page. Always. - Mono labels: 10px, uppercase, `--tr-mono`, `--magenta-400` for brand context or `--obsidian-400` for neutral. - Never use a font-size outside this scale. If the eye demands an in-between value, the issue is the surrounding hierarchy — fix that. ### 2.5 Spacing — 4px grid | Token | Px | Common use | |---|---|---| | `--s-0` | `0` | | | `--s-1` | `4` | Hairline gap (icon ↔ text) | | `--s-2` | `8` | Tight pair | | `--s-3` | `12` | Inline gap | | `--s-4` | `16` | Default content gap | | `--s-5` | `20` | Compact section gap | | `--s-6` | `24` | Card padding · subsection rhythm | | `--s-8` | `32` | Card padding (generous) | | `--s-10` | `40` | Block separator | | `--s-12` | `48` | Section margin | | `--s-16` | `64` | Major section break | | `--s-20` | `80` | Page-level rhythm | | `--s-24` | `96` | Hero spacing | | `--s-32` | `128` | Long-form vertical breathing | **Rule:** every margin, padding, gap is a token. No `13px`, no `20px;` literals in component CSS. ### 2.6 Radius | Token | Px | Use | |---|---|---| | `--r-none` | `0` | Tables, dense data | | `--r-xs` | `2` | Code chips | | `--r-sm` | `4` | Tags, kbd keys | | `--r-md` | `8` | **Default — buttons, inputs, small cards** | | `--r-lg` | `12` | **Large cards, panels** | | `--r-xl` | `16` | Modal, drawer | | `--r-2xl` | `24` | Hero block | | `--r-full` | `999px` | Pills, status dots | **Working range is 8–12px.** If a component asks for more, justify it. ### 2.7 Shadow | Token | Use | |---|---| | `--sh-1` | Resting card | | `--sh-2` | Hover lift | | `--sh-3` | Floating panel | | `--sh-4` | Modal / drawer | | `--sh-glow` | Focus state on brand elements | | `--sh-glow-soft` | Ambient brand glow (rare) | Shadows are inset-first, low-key. Never use them as decoration — only for depth hierarchy. ### 2.8 Motion **Five durations, four easings. No others.** | Duration | ms | Use | |---|---|---| | `--d-instant` | `100` | Hover state, tap feedback | | `--d-quick` | `200` | Button states, focus rings | | `--d-base` | `350` | Default — most UI transitions | | `--d-slow` | `600` | Page enters, panel slides | | `--d-scene` | `900` | Hero reveals, orchestrated sequences | | Easing | Curve | Use | |---|---|---| | `--ease-standard` | `cubic-bezier(0.22, 1, 0.36, 1)` | Default UI ease-out (Framer ease) | | `--ease-accel` | `cubic-bezier(0.7, 0, 0.84, 0)` | Exits, dismissals | | `--ease-decel` | `cubic-bezier(0.16, 1, 0.3, 1)` | Entrances, reveals (the system signature) | | `--ease-spring` | `cubic-bezier(0.34, 1.56, 0.64, 1)` | Pop-in, delight, overshoot | **Default pairing:** `transition:Body…