eklavya-design · diff
git:20260906.f2f28ac to git:20260924.6ec3203
135 added, 42 removed. Audit A to A.
---
name: eklavya-design
- description: Visual design language for Eklavya (the "learn while your agent works" Claude Code plugin) — landing page, docs, and any Eklavya-branded UI. Use whenever building or restyling Eklavya web surfaces so colors, type, motion, iconography, and voice stay consistent.
+ description: Visual design system for Eklavya (the "learn while your agent works" Claude Code plugin) — landing page, manual, dashboard, explainer artifacts and any Eklavya-branded UI. Use whenever building, restyling or reviewing an Eklavya surface so colour, type, shape, motion, iconography and voice stay consistent.
---
- # Eklavya design skill
+ # Eklavya design system
- Eklavya's look: calm, dark, engineering-grade — a developer tool that quietly references the Ekalavya legend (the self-taught archer) through bow / arrow / target line-art motifs. Never storybook or ornamental.
+ Eklavya looks calm, dark and engineering-grade: a warm ink ground, one verdigris
+ accent, square hairline chrome, and enormous tight display type set against tiny
+ wide-tracked mono. The Ekalavya legend (the self-taught archer) appears only as
+ bow, arrow and target line art — never storybook, never ornamental.
- ## Grounds and palette
+ **Values live in `web/public/tokens.css`.** This file states the rules; the CSS
+ states the numbers. When they disagree the CSS is right and this file is stale —
+ fix it here. Component recipes are in `references/components.md`.
- Two grounds, one accent. `data-mode` on `<html>` is `ink` (default) or `paper`, and
- each redefines the same **role** tokens in `site/tokens.css`. Components name a role
- — `--ink`, `--dim`, `--faint`, `--line`, `--line-2`, `--panel`, `--mass`, `--spot`,
- `--spot-ink` — and never a scale step or a raw hex. That is the whole theme
- implementation: custom properties inherit, so one attribute re-resolves the tree.
+ ## Rules at a glance
- - **Ink** (default): bg #17171a, panel #1e1e22, mass #26262a, text #EAE7E1, dim #A29C93, faint #77726A, lines #26262a / #33333a.
- - **Paper**: bg #f0ede6, panel #e7e3da, mass #ddd8cc, text #16150f, dim #57534a, faint #8a8578, lines #e3dfd4 / #cec8b9.
- - Neither ground is neutral grey — warm ink, warm paper. That is what stops paper reading as a bleached inversion of ink.
- - Ink is the default and an absent choice stays ink; the ground deliberately does **not** follow the reader's OS. Eklavya is a terminal plugin and the dark ground is the product.
+ | # | Rule | A violation looks like |
+ |---|---|---|
+ | 1 | Components name a **role** token, never a scale step or raw colour | `color: #A29C93`, `background: var(--vd-300)`, `rgba(25,150,136,.12)` |
+ | 2 | The chrome is **square** | `border-radius: 8px` on a button, card, chip, tile, input or table |
+ | 3 | **Hairlines, not shadows** | `box-shadow` on a card that does not float |
+ | 4 | Three families, through their variables | `font-family: system-ui` or a fourth face |
+ | 5 | **One accent hue**; state colours only for real state | a blue link, a red "new" badge |
+ | 6 | No emoji; sentence case; Lucide line icons | `🎯 Your Progress` |
+ | 7 | No grayscale font smoothing | `-webkit-font-smoothing: antialiased` |
+ | 8 | Motion on `--ease`, 120–320ms, and reduced motion honoured | a bounce, a loop that ignores `prefers-reduced-motion` |
+ | 9 | Visible focus, keyboard-operable controls | `outline: none` with no ring, a clickable `<div>` |
+ | 10 | Text ≥ 4.5:1 on its ground in **both** grounds | `--faint-2` for readable copy |
+ | 11 | Ink is the default and never follows the OS | a `prefers-color-scheme` switch |
- **Accent: verdigris** — aged bronze on a bow fitting. One hue, two values, because
- no single value clears contrast on both grounds:
+ ## Grounds and colour
- - Ramp: 50 #E6F7F3, 100 #C4EDE4, 200 #9BE0D2, **300 #79D5C4 (accent on ink, 10.33:1)**, 400 #3FB8A3, 500 #199688, **600 #0E6E66 (accent on paper, 5.22:1)**, 700 #0A5751, 800 #07403C, 900 #052E2B.
- - Spend it perhaps six times a page: one word in the headline, the `$`, link text, the active nav item. Everything else is a neutral role.
- - `--spot-soft` is the accent at 10–14% for tints; text on a `--spot` fill takes `--spot-ink`.
- - No other hues. Semantic amber/red (`--warning`, `--error`) are for real warnings and errors only, and are kept off the accent so state never reads as branding.
+ Two grounds, one accent. `data-mode` on `<html>` is `ink` (default) or `paper`,
+ set by a blocking inline script before styles load, and each ground redefines the
+ same role tokens. Custom properties inherit, so one attribute re-resolves the tree.
+ | Role | Ink | Paper | Use |
+ |---|---|---|---|
+ | `--bg` | #17171a | #f0ede6 | Page ground. Sections split by a `--line-2` hairline, never by a background change |
+ | `--panel` | #1e1e22 | #e7e3da | Cards, dials, tables, sidebar |
+ | `--mass` | #26262a | #ddd8cc | Wells, inline code, spines, filled-button hover |
+ | `--ink` | #EAE7E1 | #16150f | Primary text |
+ | `--dim` | #A29C93 | #57534a | Body paragraphs, secondary text |
+ | `--faint` | #8B857B | #635E52 | Captions, micro-labels (≥4.89:1 on every ground it sits on) |
+ | `--faint-2` | #565249 | #a09a8c | Disabled and placeholder **only** — fails 4.5:1 by design |
+ | `--line` / `--line-2` | #26262a / #33333a | #e3dfd4 / #cec8b9 | Hairline inside a block / frame edges, control borders |
+ | `--spot` | `--vd-300` | `--vd-700` | The accent |
+ | `--spot-ink` | #17171a | #ffffff | Text on a `--spot` fill |
+ | `--spot-soft` | spot 14% | spot 10% | Icon tiles, chips, the default dial option |
+ | `--warning` | #E8920C | #7A4B00 | Real warnings and the "missed" series |
+ | `--error` | #FF6166 | #A8201A | Real errors and wrong answers only; always pair with a word or mark |
+ | `--code-bg`, `--grid`, `--glow`, `--glow-low`, `--shadow-lg`, `--ring` | | | Code wells, the 72px rule grid, terminal halos, floating shadow, focus ring |
+
+ - Neither ground is neutral grey — warm ink, warm paper. Ink is the product: Eklavya
+ is a terminal plugin, so an absent choice stays ink and the OS setting is ignored.
+ - **Verdigris** (aged bronze on a bow fitting) is the only hue. Ramp `--vd-50` …
+ `--vd-900`; `--spot` picks 300 on ink (10.33:1) and 700 on paper (7.20:1). Spend
+ it about six times a page: one headline word in `<em>`, the `$`, link text, the
+ active nav item, the primary button. A `--vd-*` step in a component is a bug.
+ - A new colour need is a new role in **both** ground blocks of `tokens.css`, never
+ a literal in the component.
+ - **Exception — the terminal.** It is a picture of a terminal, and terminals are
+ dark, so it keeps one fixed palette on both grounds (`--term-*` in tokens.css,
+ plus the terminal-only neutrals declared at the top of `styles.css`). The macOS
+ window dots (#FF5F57, #FEBC2E, #28C840) depict another app's chrome. Nothing
+ outside the terminal may use either set.
+ - **Exception — the mark.** `web/public/brand/mark.svg` is a white bow and arrow on a
+ `#0E6E66` (`--vd-600`) square, fixed. Never recolour, round or frame it.
+
## Type
- - **Archivo** for display (600/800/900), **Inter** for body (400/500/600), **JetBrains Mono** for code, slash-commands, concept slugs, tier labels, terminal content and micro-labels.
- - The system is two extremes with no middle: enormous tight display, tiny wide-tracked uppercase mono. Body text stays small and quiet — 14.5px/1.65 — and the contrast between those poles is most of what reads as designed.
- - Display: weight 900, `letter-spacing: -0.055em`, `line-height: .88–.92`, sized with `clamp()` (`--display-hero`, `--display-1`, `--display-2`) so it scales with the viewport instead of sitting at a timid fixed size.
- - Micro-labels: mono, 10–11.5px, uppercase, `letter-spacing: .16–.24em`, in `--faint` or `--spot`. These replace pill-shaped eyebrows.
- - Sentence case in prose. No emoji.
- - Do not set `-webkit-font-smoothing: antialiased`: grayscale smoothing thins light-on-dark glyphs, and ink is the default ground.
+ - **Archivo** (`--font-disp`, 600/800/900) for display; **Inter** (`--font-body`,
+ 400/500/600) for text; **JetBrains Mono** (`--font-mono`, 400/500/600) for code,
+ slash commands, concept slugs, tier labels, terminal content and micro-labels.
+ Always through the variables. Surfaces that must make no outbound request (the
+ dashboard) strip the Google Fonts import and fall back to the stacks in the vars.
+ - Two extremes, no middle. Display: weight 900, `--tracking-display` (-0.055em),
+ `--leading-display` (0.9), sized fluidly (`--display-hero`, `--display-1`,
+ `--display-2`). Micro-labels: mono 10–11.5px, uppercase, 0.16–0.24em
+ (`--tracking-caps`, `--tracking-caps-wide`), in `--faint` or `--spot`. They replace
+ pill eyebrows. Body: 14.5px / 1.65, quiet.
+ - Sentence case in prose and headings; micro-labels are uppercased by CSS. No emoji.
+ - Never set `-webkit-font-smoothing: antialiased`: it thins light-on-dark glyphs.
- ## Layout
- - Content max-width 1140px, 32px side padding; sections pad 96px vertical; 4px base grid.
- - Radii: controls 8px, cards 12px, terminals/large surfaces 16px, pills 999px.
- - Band rhythm: alternate white / neutral-50 for product sections; reserve dark bands (neutral-900, green-900) for hero, legend/story, and final CTA.
- - Backgrounds: flat, plus at most a subtle dot grid (1px dots, 26px tile, ≤7% opacity, masked to fade) and one large soft green radial glow per dark band.
+ ## Shape, space and layout
- ## Signature motifs
- - **Bow-and-arrow mark**: white line-art bow (arc + string) with an arrow through it, in a green-500 rounded-8 square.
- - **Arrow-flight scene**: line bow → dashed dotted trajectory → concentric-circle target; arrow flies and hits the bullseye. Full-strength in story sections; as ambient background run it at ~15–18% opacity with a slight blur.
- - **Terminal window**: its own fixed dark palette (`--term-*`) on **both** grounds — it is a picture of a terminal, and terminals are dark. Radius 16, three muted dots, mono 13px/1.75; show the real product loop (prompt → work → concept log → quiz → grade), lines appearing sequentially on an infinite loop with a blinking green block cursor.
- - Icons: Lucide line icons only (stroke currentColor, 1.8–2px, round joins), 20–22px in `--spot-soft` tiles.
- - **The terminal and the arrow flight are the two showpieces and are not to be redesigned.** They may be recoloured onto the ground tokens; their geometry, timing and behaviour stay.
+ - 4px grid: `--space-1` (4) … `--space-24` (96). Content 1140px wide with 32px side
+ padding inside a 1440px ruled frame (`--max`). Sections pad 96px vertically (72px
+ under 900px). Check layouts at 1280, 900 and 560px in both grounds.
+ - **Square chrome.** Buttons, cards, chips, dials, tables, tiles, badges, inputs and
+ code blocks are `border-radius: 0`. Circles (`50%`) only for dots, step numbers and
+ ripples; `--radius-pill` only for scrollbar thumbs. `--radius-term` (16px) belongs
+ to the hero terminal and the arrow scene alone. `--radius-sm/md/lg` are for small
+ data marks (legend swatches, chart bars), never chrome.
+ - **Hairlines, not shadows.** Separation is 1px `--line` or `--line-2`. `--shadow-lg`
+ is for things that float (menus, popovers, dialogs).
+ - Backgrounds are flat, plus the 72px `--grid` rule grid on `body` and at most one
+ soft `--glow` radial per band.
## Motion
- - Easing `cubic-bezier(.2,.6,.2,1)`; micro-interactions 140–200ms; no bounce or spring.
- - Scroll reveals: opacity 0 + translateY(18px) → visible over 0.7s, staggered 100–400ms, triggered once by IntersectionObserver (threshold .15).
- - Looping showpieces (terminal 16s, arrow flight 9s) are keyframe-driven with percentage windows: elements appear in sequence, hold, all reset together.
- - Arrow flight recipe: launch fast and decelerate (`cubic-bezier(.3,0,.4,1)`), bow recoil ~5px on release, target pop 1→1.06→1, two expanding fading ripple rings on impact. The arrow must point one way: head forward, fletching sweeping backward.
- - Always honor `prefers-reduced-motion`: collapse animations so content lands in final state.
+ - `--ease` (`cubic-bezier(.2,.6,.2,1)`) on 120 / 200 / 320ms (`--duration-fast`,
+ `-base`, `-slow`); micro-interactions 140–200ms. No bounce, no spring.
+ - Scroll reveals: opacity 0 + translateY(18px) → visible over 0.7s, staggered
+ 100–400ms, fired once by an IntersectionObserver at threshold 0.15.
+ - Loops are keyframe-driven with percentage windows: terminal 16s, arrow flight 9s.
+ The arrow launches fast and decelerates (`cubic-bezier(.3,0,.4,1)`), the bow
+ recoils ~5px, the target pops 1 → 1.06 → 1, two ripple rings fade. Head forward,
+ fletching back.
+ - `prefers-reduced-motion: reduce` collapses every animation and transition so
+ content lands in its final state.
+
+ ## Signature motifs
+
+ - **Nav brand:** the bow glyph (`web/src/assets/bow.svg`, 24px grid, stroke 1.8) in
+ `--spot-ink` on a 30px square `--spot` tile, then "Eklavya" in Archivo 900 19px.
+ - **Arrow flight:** line bow → dashed trajectory → concentric target. Full strength
+ in story sections; as ambient art at `--shot-op` with a slight blur.
+ - **Terminal window:** `--radius-term`, three muted dots, mono 13px/1.75, showing the
+ real loop (prompt → work → concept log → quiz → grade) line by line with a
+ blinking verdigris block cursor. Its quoted output is real product output.
+ - **The terminal and the arrow flight are the two showpieces and are not to be
+ redesigned.** They may be recoloured onto tokens; their geometry, timing and
+ behaviour stay.
+
+ ## States, focus and icons
+
+ - Focus is `--ring` (2px `--bg` gap, then 2px `--spot`) via `:focus-visible`. Never
+ remove it without a replacement.
+ - Hover moves an outlined control's colour or border to `--spot`, and steps a filled
+ one (`--panel` → `--mass`). A selected segment inverts: `--ink` fill, `--bg` text.
+ - Every control is a real `<button>` or `<a>`; state lives in `aria-pressed` /
+ `aria-current`, never colour alone.
+ - Icons: Lucide line icons only, inlined — stroke `currentColor`, width 2 (1.8 for
+ the bow), round caps and joins; 16px in buttons, 20–22px in a 44px `--spot-soft`
+ tile. No icon fonts, filled icons or emoji.
+
## Voice
- Direct, concise, technically credible. Lead with the outcome; verbs first on buttons ("View on GitHub", "Star on GitHub"). Numbers over adjectives. Reference the legend factually ("he practiced before a statue of his guru — here, the statue talks back"), never cutesy. Keep the product's own copy verbatim where it exists (README taglines, command names, config keys).
+
+ Direct, concise, technically credible. Lead with the outcome; verbs first on buttons
+ ("View on GitHub", "Star on GitHub"). Numbers over adjectives. Address the reader as
+ "you". Reference the legend factually ("he practiced before a statue of his guru —
+ here, the statue talks back"), never cutely. Keep the product's own words verbatim:
+ the tagline "Learn while your agent works — and carry project context forward.",
+ command names, config keys, concept slugs.
+
+ ## Before you ship a surface
+
+ - `grep -nE '#[0-9a-fA-F]{3,8}\b|rgba?\(|--vd-' <file>` outside tokens.css returns
+ only the terminal block and documented exceptions.
+ - `grep -n 'border-radius' <file>` shows 0, 50%, or a documented exception.
+ - Toggle ink/paper; tab through every control; run once with reduced motion.