@layer tokens { /* PRIVATE --fs-* only; the EXPORT --*-color-* tier is derived elsewhere and unread here. * docs/design-system.md "Two tiers, and the split is load-bearing". */ :root { /* palette colours live in 03-palettes.css */ --fs-shadow-pop: 0 12px 34px rgba(0,0,0,.30); /* pop surfaces: menus, dropdown lists, the select picker */ /* which of the three a control takes: docs/design-system.md "The derived ladder" */ --fs-focus-ring: 0 0 0 3px var(--fs-accent-soft); --fs-focus-ring-solo: 0 0 0 2px var(--fs-panel), 0 0 0 5px var(--fs-accent); --fs-focus-ring-invalid: 0 0 0 3px var(--fs-danger-fill); /* opaque, not a tint (4.21:1 in dark); pinned by tools/mirror.mjs * (`selected-row/paint`), docs/css.md "@mirror" */ --fs-row-rail: inset 2px 0 0 var(--fs-accent); /* every separator is this one line, stated once rather than in 69 longhand declarations */ --fs-hairline: 1px solid var(--fs-border); /* ring and border-colour transition together, or the ring arrives before the edge does */ --fs-field-transition: border-color var(--fs-dur), box-shadow var(--fs-dur); /* the theme's one colour literal on purpose. docs/design-system.md "The derived ladder". */ --fs-scrim: rgba(0, 0, 0, .7); /* Role tint ladder, derived so it follows the palette. docs/design-system.md "The derived * ladder". */ --fs-good-soft: color-mix(in srgb, var(--fs-good) 12%, transparent); --fs-danger-soft: color-mix(in srgb, var(--fs-danger) 12%, transparent); --fs-accent-fill: color-mix(in srgb, var(--fs-accent) 18%, transparent); --fs-good-fill: color-mix(in srgb, var(--fs-good) 18%, transparent); --fs-warn-fill: color-mix(in srgb, var(--fs-warn) 18%, transparent); --fs-danger-fill: color-mix(in srgb, var(--fs-danger) 18%, transparent); --fs-accent-line: color-mix(in srgb, var(--fs-accent) 40%, transparent); --fs-good-line: color-mix(in srgb, var(--fs-good) 40%, transparent); --fs-danger-line: color-mix(in srgb, var(--fs-danger) 40%, transparent); --fs-accent-line-hi: color-mix(in srgb, var(--fs-accent) 55%, transparent); --fs-good-line-hi: color-mix(in srgb, var(--fs-good) 55%, transparent); --fs-danger-line-hi: color-mix(in srgb, var(--fs-danger) 55%, transparent); /* ours, redrawn to stay so — not Lucide's. docs/design-system.md "Two glyphs drawn as * tokens, not fetched". */ --fs-icon-refresh: url("data:image/svg+xml,"); /* css-dup cannot police a glyph inlined once, sized at two call sites — keep in step by hand */ --fs-icon-refresh-sz: calc(18px * var(--fs-density-type)); /* direction flips by mode: light darkens, dark brightens (`filter` recolours the ink too) */ --fs-hover-lift: brightness(.90); /* four durations, no easing token. docs/design-system.md "Scales" ("Motion") */ --fs-dur: .15s; /* a state change: colour, border, shadow, background, filter */ --fs-dur-move: .2s; /* something that moves or unfolds: transform, max-height */ --fs-dur-fade: .25s; /* a transient thing fading in or out: spinner, notification */ --fs-dur-fill: .4s; /* a progress bar growing to its value */ --fs-content-max: 1280px; /* content column cap */ /* also read by the bar's centering calc and the notification-width calc */ --fs-content-pad: calc(28px * var(--fs-density-space)); /* tokens, not literals — fs-chrome.js's fitShell() reads them back. docs/design-system.md * "Scales" ("Shell geometry"). */ --fs-sidebar-w: calc(224px * var(--fs-density-box)); /* the expanded vertical sidebar's column */ --fs-rail-w: calc(68px * var(--fs-density-box)); /* ...and the same sidebar collapsed to its icon rail */ --fs-content-min: 500px; /* below this a content column stops being readable */ /* opaque on purpose. docs/design-system.md "The derived ladder". */ --fs-bar-bg: var(--fs-panel); /* drawn, not fetched. docs/design-system.md "Two glyphs drawn as tokens, not fetched". */ --fs-select-chevron: linear-gradient(-45deg, transparent 45%, var(--fs-dim) 45% 55%, transparent 55%), linear-gradient(45deg, transparent 45%, var(--fs-dim) 45% 55%, transparent 55%); /* the bar's designed height; fs-chrome.js publishes the measured one as --fs-bar-live */ --fs-bar-h: calc(46px * var(--fs-density-box)); /* logo box, .fs-themerow controls, Log out and the poll pill share this size */ --fs-btn-size: calc(34px * var(--fs-density-box)); /* one base (Appearance -> Rounding); three radii scale from it */ --fs-radius-base: 12px; --fs-radius-lg: var(--fs-radius-base); /* cards, panels, modals, table frame, popovers */ --fs-radius: calc(var(--fs-radius-base) * 5 / 6); /* controls: inputs, buttons, dropdowns, tabs (10 @12) */ --fs-radius-sm: calc(var(--fs-radius-base) * 2 / 3); /* chips, code, small insets, rail toggle (8 @12) */ --fs-radius-pill: 999px; /* pills, toggles, badges — always round */ /* .cbi-section's own gutter; theme/45-misc.css reads it back to undo it (bleed to the card edge) */ --fs-card-pad: calc(16px * var(--fs-density-space)); /* vertical rhythm between cards: .cbi-section, tab groups, alerts, action bars */ --fs-card-gap: calc(16px * var(--fs-density-space)); --fs-font-sans: "Manrope", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif; --fs-font-mono: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, monospace; /* three multipliers, one knob each for text/air/boxes */ --fs-density-type: 1; --fs-density-space: 1; --fs-density-box: 1; /* chrome-control floors, not a field's height (--fs-control-h below) */ --fs-ctl-h-sm: calc(30px * var(--fs-density-box)); /* dense/small: .btn.small, table-row buttons */ --fs-ctl-h: calc(38px * var(--fs-density-box)); /* the default control */ --fs-ctl-h-lg: calc(44px * var(--fs-density-box)); /* prominent: .btn.large, the login submit */ /* rounded in @supports below (round() is younger than the floor) */ --fs-type-2xs: calc(10px * var(--fs-density-type)); --fs-type-xs: calc(11px * var(--fs-density-type)); /* eyebrow, badges, tooltips, dense metadata */ --fs-type: calc(13px * var(--fs-density-type)); /* body, tables, fields, menu items — the base */ --fs-type-lg: calc(16px * var(--fs-density-type)); /* section titles (h3/legend), alert headings */ --fs-type-xl: calc(20px * var(--fs-density-type)); /* page titles */ --fs-type-2xl: calc(26px * var(--fs-density-type)); /* h1: error500 and third-party apps; the chrome title and the login h1 are .fs-sr */ /* iOS Safari zooms the page when a field under 16px takes focus and never zooms back; max() holds * the floor at Compact, where --fs-type-lg is 14.4px. -field/-field-xs are the size typed text is * set in: the body (or dense) size on desktop, the floor under 767px (@media below). */ --fs-type-floor: max(16px, var(--fs-type-lg)); --fs-type-field: var(--fs-type); --fs-type-field-xs: var(--fs-type-xs); /* the table filter and the colour hex */ /* one spelling per weight — `700`/`bold` and `400`/`normal` were both in use. */ --fs-weight-normal: 400; /* "not bold" */ --fs-weight: 600; /* the UI default: body, labels, controls */ --fs-weight-bold: 700; /* titles, table headers, the eyebrow */ --fs-leading: 1.5; /* unitless on purpose: it scales with the element's own font-size (13 -> 19.5px) */ --fs-leading-tight: 1.2; /* headings and single-line plates, where 1.5 looks loose */ /* one microlabel idiom, named once */ --fs-eyebrow-tracking: .06em; --fs-eyebrow-weight: 700; --fs-eyebrow-color: var(--fs-faint); /* pick the nearer step; do not add one for a single rule */ --fs-space-0-5: calc(2px * var(--fs-density-space)); --fs-space-1: calc(4px * var(--fs-density-space)); --fs-space-1-5: calc(6px * var(--fs-density-space)); --fs-space-2: calc(8px * var(--fs-density-space)); --fs-space-2-5: calc(10px * var(--fs-density-space)); --fs-space-3: calc(12px * var(--fs-density-space)); --fs-space-3-5: calc(14px * var(--fs-density-space)); --fs-space-4: calc(16px * var(--fs-density-space)); --fs-space-5: calc(20px * var(--fs-density-space)); --fs-space-6: calc(24px * var(--fs-density-space)); --fs-space-7: calc(28px * var(--fs-density-space)); /* sparse on purpose above -7 — gutters and insets, not rhythm */ --fs-space-8: calc(32px * var(--fs-density-space)); --fs-space-10: calc(40px * var(--fs-density-space)); /* every single-line field's height, derived not measured */ --fs-control-h: calc(var(--fs-type) * var(--fs-leading) + var(--fs-space-1) * 2 + 2px); /* ceiling for an elastic `.cbi-value-field` (flex: 1) — a COLUMN width, off the 4px scale */ --fs-field-max: 440px; /* every z-index comes from here */ --fs-z-raise: 2; --fs-z-sticky: 50; --fs-z-flyout: 70; --fs-z-header: 800; --fs-z-popover: 850; --fs-z-overlay: 900; --fs-z-tooltip: 1000; --fs-z-dropdown: 1100; /* outbound only (audit.py --strict fails a styles/ read of one). docs/design-system.md * "The export tier is a RAMP" */ --background-color-high: var(--fs-panel); --background-color-medium: color-mix(in srgb, var(--fs-panel) 55%, var(--fs-panel2)); --background-color-low: var(--fs-panel2); --text-color-highest: var(--fs-text); --text-color-high: var(--fs-text); --text-color-medium: var(--fs-dim); --text-color-low: var(--fs-faint); /* borders carry no text, so -low may fade toward the surface — a hairline is meant to * disappear into it */ --border-color-high: color-mix(in srgb, var(--fs-border) 60%, var(--fs-text)); --border-color-medium: var(--fs-border); --border-color-low: color-mix(in srgb, var(--fs-border) 55%, var(--fs-panel)); /* tools/export-tier.mjs proves the whole tier across palette x mode */ --primary-color-high: var(--fs-accent); --primary-color-medium: color-mix(in oklch, var(--fs-accent) 78%, var(--fs-dim)); --primary-color-low: color-mix(in oklch, var(--fs-accent) 45%, var(--fs-dim)); --on-primary-color: var(--fs-on-accent); --error-color-high: var(--fs-danger); --error-color-medium: color-mix(in oklch, var(--fs-danger) 78%, var(--fs-dim)); --error-color-low: color-mix(in oklch, var(--fs-danger) 45%, var(--fs-dim)); --on-error-color: var(--fs-on-danger); --success-color-high: var(--fs-good); --success-color-medium: color-mix(in oklch, var(--fs-good) 78%, var(--fs-dim)); --success-color-low: color-mix(in oklch, var(--fs-good) 45%, var(--fs-dim)); --on-success-color: var(--fs-on-good); --warn-color-high: var(--fs-warn); --warn-color-medium: color-mix(in oklch, var(--fs-warn) 78%, var(--fs-dim)); --warn-color-low: color-mix(in oklch, var(--fs-warn) 45%, var(--fs-dim)); --on-warn-color: var(--fs-on-warn); --fs-disabled-opacity: .6; color-scheme: light; } /* round(…) tested where a length is expected, not by a calc() probe */ @supports (width: round(1px, 1px)) { :root { --fs-type-2xs: calc(round(10px * var(--fs-density-type), 1px)); --fs-type-xs: calc(round(11px * var(--fs-density-type), 1px)); --fs-type: calc(round(13px * var(--fs-density-type), 1px)); --fs-type-lg: calc(round(16px * var(--fs-density-type), 1px)); --fs-type-xl: calc(round(20px * var(--fs-density-type), 1px)); --fs-type-2xl: calc(round(26px * var(--fs-density-type), 1px)); --fs-control-h: calc(round(up, calc(var(--fs-type) * var(--fs-leading) + var(--fs-space-1) * 2 + 2px), 4px)); } } @media (max-width: 767px) { :root { --fs-type-field: var(--fs-type-floor); --fs-type-field-xs: var(--fs-type-floor); } } /* bare :root is Normal; these multiply the LADDERS, not the rules */ :root[data-density="compact"] { --fs-density-type: .9; --fs-density-box: .85; --fs-density-space: .65; } :root[data-density="large"] { --fs-density-type: 1.15; --fs-density-box: 1.15; /* --fs-density-space stays at the bare :root default of 1 here — large density widens type * and controls, not the space/gutter scale. */ } /* No :root[data-content-width] block: fs-axes.js's propAxis writes --fs-content-max inline * instead. docs/design-system.md "The appearance axes" ("Content width"). */ :root[data-darkmode="true"] { /* palette colours are in 03-palettes.css; only mode-level tokens live here */ --fs-disabled-opacity: .45; /* dark fill is light, so brightening (not darkening) moves it away from page AND ink */ --fs-hover-lift: brightness(1.15); color-scheme: dark; } /* no component bridge (--*-hsl, --*-rgb): docs/conventions.md "Writing CSS" ("No colour * literals"). The one surviving --*-rgb is luci-mod-network's own (base/95-luci.css), not ours. */ }