mirror of
https://github.com/kuhyx/utils.git
synced 2026-11-14 22:10:57 +01:00
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
113 lines
3.6 KiB
CSS
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;
|
|
}
|