utils/web_ui/dist/tokens.css
Krzysztof kuhy Rudnicki 03bf1ed08b web_ui: shared web token + component layer (Phase 3)
The web counterpart to design_system (Dart): one :root block of frozen tokens
plus the components that were duplicated across repos. Extracted from
dufs-cloud and awesome-mcp-explorer, which both now consume it.

- tokens.css: the frozen palette, spacing/radius/type scales, shadow policy and
  focus ring, under the canonical variable names from unified-design-system.
  Also ships the global :focus-visible rule, rather than leaving each consumer
  to remember it.
- RangeSlider: reconciled from the two drifted copies. Takes the accessible DOM
  from awesome-mcp-explorer (real <button> thumbs with role="slider" and the
  aria-value* trio) and grab-the-nearer-thumb from dufs-cloud. Keyboard support
  (arrows/PageUp/PageDown/Home/End) is new: both donors were pointer-only, which
  the design system rules out, and an arrow-key-deaf role="slider" is an ARIA
  role that lies.
- quantile.ts + fuzzyMatch: the genuinely duplicated helpers.

FilterBar is deliberately absent. The two same-named components share no
structure -- a horizontal <select> strip over files vs a vertical tri-state chip
sidebar over servers, with disjoint FilterState, props and sort keys. It is the
name-collision-is-not-a-duplicate trap, so extracting it would ship a widget
nobody imports.

dist/ is committed (and un-ignored against the root rule) because Vite does not
transpile dependencies; consumers stay at zero config. Consumed via pnpm's
&path:/web_ui suffix -- npm cannot install from a subdirectory of a git repo.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01U4Rj3s7K2qzrLNgJqqPhno
2026-08-15 14:40:45 +02:00

113 lines
3.6 KiB
CSS

/*
* The frozen design tokens, shared by every one of kuhy's web repos.
*
* Source of truth: ~/utils/unified-design-system/tokens.md (values) and
* README.md (the CSS custom-property names). The same numbers ship for Dart in
* ~/utils/design_system, so a change here is a change there — do not edit one
* side alone, and do not introduce a value that is not on a scale below.
*
* Dark is the default; light is an override, matching the README.
*/
:root {
/* Neutrals ------------------------------------------------------------- */
--bg: #211d1b;
--surface-1: #2b2624;
--surface-2: #38312e;
--border: #463e3a;
--text: #eceae9;
--text-muted: #aaa09a;
/* Accent + semantic roles ---------------------------------------------- */
--accent: #b8862e;
--info: #b8862e; /* = accent */
--success: #8a9a3c;
--warning: #e0a63c;
--danger: #e2585f;
/* Text/icons ON a filled accent/success/warning/danger surface. Never
* --text: every semantic fill sits in the same mid-light band, where
* near-white measures 2.2:1-3.6:1 against a 4.5:1 floor. */
--on-fill: #211d1b;
/* Text/icons on a dark translucent scrim/overlay — fixed, not theme-tied,
* because the scrim itself is always dark. */
--on-scrim: #eceae9;
/* Corner radius --------------------------------------------------------- */
--radius-sm: 8px; /* buttons, inputs, chips */
--radius-md: 12px; /* cards */
--radius-lg: 16px; /* sheets, dialogs */
/* Spacing (4px base) ---------------------------------------------------- */
--space-1: 4px;
--space-2: 8px;
--space-3: 16px;
--space-4: 24px;
--space-5: 32px;
--space-6: 48px;
/* Typography ------------------------------------------------------------ */
--font-sans: -apple-system, "Segoe UI", Roboto, system-ui, sans-serif;
--font-mono: ui-monospace, "Cascadia Code", monospace;
--text-display: 32px;
--text-title: 24px;
--text-subtitle: 20px;
--text-body: 16px; /* the floor for anything a user actually reads */
--text-label: 14px; /* UI chrome only */
--text-caption: 12px; /* UI chrome only */
--tracking-display: -0.2px;
--tracking-title: -0.1px;
--tracking-label: 0.05em;
--tracking-caption: 0.1em;
/* Prose line-length cap (rule 21): ~65-70 characters. */
--measure: 40rem;
/* Shadows: light surfaces only, and only on floating/overlay elements —
* never inline cards or list rows. Blur = 2x offset, tinted from ink. */
--shadow-1: 0 2px 4px rgba(33, 29, 27, 0.12); /* dropdown */
--shadow-2: 0 4px 8px rgba(33, 29, 27, 0.16); /* modal */
/* Focus ring: the platform default is black, invisible on --bg. */
--focus-ring: #b8862e; /* = accent */
}
@media (prefers-color-scheme: light) {
:root {
--bg: #f6f4f3;
--surface-1: #fcfbfb;
--surface-2: #fcfbfb;
--border: #e0dad7;
--text: #211d1b;
--text-muted: #70625b;
/* --on-fill / --on-scrim stay fixed — the fills and the scrim do not
* change with page theme. */
}
}
/* Dark surfaces take no shadow at all; elevation is a fill step. Light
* surfaces opt in explicitly via --shadow-1 / --shadow-2. */
@media (prefers-color-scheme: dark) {
:root {
--shadow-1: none;
--shadow-2: none;
}
}
body {
background: var(--bg);
color: var(--text);
font-family: var(--font-sans);
font-size: var(--text-body);
}
/* Ships here rather than being left to each consumer: an unstyled focus ring
* is the accessibility regression this layer exists to prevent, and several of
* these apps are keyboard-only by necessity. */
:focus-visible {
outline: 2px solid var(--focus-ring);
outline-offset: 1px;
}