|
1 | | -# Frontend Style Guide |
| 1 | +# anyplot Design System |
2 | 2 |
|
3 | | -All visual values are centralized in `app/src/theme/index.ts`. Never use hardcoded hex colors or font strings — always import tokens. |
| 3 | +Canonical source: `docs/reference/style-guide.md` (brand · frontend · plots in one doc). |
| 4 | +Frontend tokens: `app/src/theme/index.ts` + `app/src/main.tsx` (MUI theme). |
4 | 5 |
|
5 | | -## Imports |
| 6 | +## Aesthetic anchor |
| 7 | +`arXiv paper` × `tmux/lazygit` — **the site is the neutral frame; the plots are the content.** Three layers: Brand (identity/voice), Frontend (visual language), Plots (color palette across all 9 libraries). |
| 8 | + |
| 9 | +## Typography — MonoLisa only, one face + two voices |
| 10 | +- **Roman (upright MonoLisa)** — default for everything: body, UI, nav, buttons, code, logo, display. |
| 11 | +- **Script (italic MonoLisa + `ss02`)** — editorial accent, semantic emphasis only (hero sub-headlines, section titles, `<em>your</em>`, taglines). |
| 12 | +- `ss02` is enabled globally on `html`: `font-feature-settings: "ss02";` — it only affects `font-style: italic`. |
| 13 | +- **No second font.** Fraunces, Inter, Space Grotesk, Poppins are all out. The anti-pattern "reaching for a serif for warmth" explicitly forbidden. |
| 14 | +- CSS vars `--serif`, `--sans`, `--mono` all resolve to the same MonoLisa stack (aliased for compatibility). |
| 15 | +- **Exception**: ErrorBoundary uses raw `'monospace'` (crash-safe fallback). |
| 16 | +- `fontSize.micro` (0.5rem) and `fontSize.xxs` (0.625rem) restricted to data-dense pages (StatsPage, DebugPage). Public pages: `fontSize.xs` (0.75rem) minimum. |
| 17 | + |
| 18 | +## Color — Okabe-Ito palette |
| 19 | +Colorblind-safe categorical palette; first 7 fixed across themes, 8th is adaptive neutral. |
| 20 | + |
| 21 | +``` |
| 22 | +1 #009E73 bluish green ★ brand |
| 23 | +2 #D55E00 vermillion (errors, destructive) |
| 24 | +3 #0072B2 blue (info links, footnotes) |
| 25 | +4 #CC79A7 reddish purple PLOT-ONLY |
| 26 | +5 #E69F00 orange ("new" badges, secondary hover) |
| 27 | +6 #56B4E9 sky blue PLOT-ONLY |
| 28 | +7 #F0E442 yellow PLOT-ONLY |
| 29 | +8 adaptive neutral: #1A1A1A light / #E8E8E0 dark |
| 30 | +``` |
| 31 | + |
| 32 | +**Brand green `#009E73` appears in UI ONLY in 8 contexts:** logo dot, italic accent words in headlines, hero terminal cursor, active nav item (dot prefix + underline), code-action button hover, code-block syntax (strings), palette strip, small status indicators. |
| 33 | +**Never**: backgrounds, regular card borders, body text emphasis, non-logo icons, static decorative dots. |
| 34 | + |
| 35 | +**Plot-only colors** (purple/sky/yellow) must never appear in UI chrome. |
| 36 | + |
| 37 | +## Surfaces — warm, not clinical |
| 38 | +Pure `#FFFFFF` is out — makes saturated palette colors look harsh. |
| 39 | + |
| 40 | +| Token | Light | Dark | |
| 41 | +|----------------|-----------|-----------| |
| 42 | +| `--bg-page` | `#F5F3EC` | `#121210` | |
| 43 | +| `--bg-surface` | `#FAF8F1` | `#1A1A17` | |
| 44 | +| `--bg-elevated`| `#FFFDF6` | `#242420` | |
| 45 | + |
| 46 | +## Warm-tinted grayscale (reddish-brown undertone, not blue-gray) |
| 47 | +| Token | Light | Dark | Role | |
| 48 | +|----------------|-----------|-----------|------------------| |
| 49 | +| `--ink` | `#1A1A17` | `#F0EFE8` | Primary text | |
| 50 | +| `--ink-soft` | `#4A4A44` | `#B8B7B0` | Secondary text | |
| 51 | +| `--ink-muted` | `#8A8A82` | `#6E6D66` | Tertiary, meta | |
| 52 | +| `--rule` | rgba(26,26,23,.10) | rgba(240,239,232,.10) | Borders/dividers | |
| 53 | + |
| 54 | +## WCAG rule |
| 55 | +`colors.gray[400]` (#9ca3af) and lighter **must never be used for text or icons**. Minimum text color = `semanticColors.mutedText` (#6b7280, 4.6:1). |
| 56 | + |
| 57 | +## Frontend imports (MUI theme) |
6 | 58 | ```ts |
7 | 59 | import { typography, colors, semanticColors, fontSize, spacing } from '../theme'; |
8 | | -// Shared style constants: |
9 | 60 | import { headingStyle, subheadingStyle, textStyle, codeBlockStyle, tableStyle, labelStyle, monoText } from '../theme'; |
10 | 61 | ``` |
| 62 | +Semantic text tokens: `labelText` 7.0:1, `subtleText` 5.8:1, `mutedText` 4.6:1. |
| 63 | +Highlight treatments: `colors.highlight.bg`/`colors.highlight.text`, `colors.tooltipLight` — never hardcode highlight hex. |
| 64 | + |
| 65 | +## Layout — three tiers |
| 66 | +- **paper** 1240px — landing hero, About, Methodology, Blog, Legal |
| 67 | +- **catalog** 2200px — plot catalog, search results, library/spec pages |
| 68 | +- **hero-flank** 100vw — ultrawide-only (`≥1600px`) vertical plot stacks |
| 69 | +- Gutter: 24px mobile → 96px wide. Section padding: 80px vertical desktop. Card padding: 24–28px. |
| 70 | + |
| 71 | +## Section-header pattern (shell-prompt prefixes) |
| 72 | +- `❯` navigation/categorical · `$` action/list · `~/path/` hierarchical/about |
| 73 | +- Title in italic + ss02 (script), `--ok-green`, underlined with 1px `--rule`. |
| 74 | + |
| 75 | +## Buttons — method-call doctrine |
| 76 | +- **Action** (primary affordance): `.copy()`, `.open()`, `.download()` — `::before { content: "." }`, hover flips to `--ok-green`. |
| 77 | +- **Hero CTA** (filled, landing only): dark pill, 4px radius, hover → green. |
| 78 | +- **Ghost** (rare): transparent, `--rule` border. |
| 79 | + |
| 80 | +## Logo `any.plot()` |
| 81 | +MonoLisa Bold, letters `--ink`, `.` `--ok-green` scaled 1.3× (circle), `()` `--ink` weight 400 at 45% opacity. Favicon reduces to `a.p`. Clear space `1em`. |
| 82 | + |
| 83 | +## Voice |
| 84 | +Precise · understated · curious · respectful · slightly playful · code-native · AI-honest. |
| 85 | +Avoid: sales-y ("unlock", "supercharge"), corporate ("solutions", "leverage"), breathless, AI-hype ("AI-powered", "intelligent"), emoji-heavy. |
| 86 | +Default: **lowercase**. Oxford comma always. Em-dashes with spaces (European style). |
| 87 | +AI pipeline framing: humans submit ideas + approve specs + tune rules; AI drafts specs, generates code, reviews. Never patch generated code by hand. |
| 88 | + |
| 89 | +## Anti-patterns |
| 90 | +Gradients (esp. purple-blue SaaS), glass/backdrop-blur, isometric illustrations, parallax/hero videos, badge-heavy UI, pure `#FFF`/`#000` backgrounds, branded spinners, stock imagery, **second typeface**, categorical palettes on continuous data. |
11 | 91 |
|
12 | | -## Colors |
13 | | -- **Brand**: `colors.primary` (#3776AB), `colors.accent` (#FFD43B), `colors.primaryDark` (#306998) |
14 | | -- **Gray scale**: `colors.gray[50]` to `colors.gray[900]` |
15 | | -- **Semantic text**: `semanticColors.labelText` (7.0:1), `semanticColors.subtleText` (5.8:1), `semanticColors.mutedText` (4.6:1) |
16 | | -- **Status**: `colors.success`, `colors.error`, `colors.warning`, `colors.info` |
17 | | -- **Backgrounds**: `colors.background`, `colors.accentBg` |
18 | | -- **Code blocks**: `colors.codeBlock.bg`, `colors.codeBlock.text` |
19 | | - |
20 | | -## WCAG Rule |
21 | | -`colors.gray[400]` (#9ca3af) and lighter **must never be used for text or icons**. Minimum text color: `semanticColors.mutedText` (#6b7280, 4.6:1 contrast). |
22 | | - |
23 | | -## Font |
24 | | -Always use `typography.fontFamily` — never inline `'"MonoLisa", monospace'` or raw `'monospace'`. |
25 | | -**Exception**: ErrorBoundary uses raw `'monospace'` intentionally (crash-safe fallback). |
26 | | - |
27 | | -## Font Sizes |
28 | | -`fontSize.micro` (0.5rem) and `fontSize.xxs` (0.625rem) are restricted to data-dense pages (StatsPage, DebugPage). |
29 | | -Public pages use `fontSize.xs` (0.75rem) as minimum. |
30 | | -Full scale: micro, xxs, xs, sm, md, base, lg, xl. |
31 | | - |
32 | | -## Shared Constants |
33 | | -- `headingStyle` — page h2 (1.25rem, 600, gray.800) |
34 | | -- `subheadingStyle` — page h3 (1rem, 600, gray.700) |
35 | | -- `textStyle` — body text (0.9375rem, labelText, lineHeight 1.8) |
36 | | -- `codeBlockStyle` — dark code blocks |
37 | | -- `tableStyle` — consistent table cells/headers |
38 | | -- `labelStyle` — small labels (0.875rem, labelText) |
39 | | - |
40 | | -## Highlight Colors |
41 | | -Use theme tokens for highlight treatments: `colors.highlight.bg`/`colors.highlight.text` for highlighted tag chips and `colors.tooltipLight` for tooltip text on dark backgrounds. Do not reintroduce hardcoded highlight hex values. |
42 | | - |
43 | | -## Full reference |
44 | | -See `docs/reference/style-guide.md` for complete documentation including spacing, breakpoints, component specs, animations, and accessibility rules. |
| 92 | +## Plot defaults |
| 93 | +- First series **always** `#009E73`. |
| 94 | +- Neutral (pos 8) reserved for aggregates/residuals/reference lines. |
| 95 | +- Yellow `#F0E442` poor on white — position 7+ only, never thin lines/small markers. |
| 96 | +- Non-categorical: sequential → `viridis`/`cividis`; diverging → `BrBG`; heatmaps → `viridis` or single-polarity `Reds`/`Blues`. |
| 97 | +- Plot-internal typography (ticks/labels/legends): MonoLisa, 10–13px. |
0 commit comments