@import './tokens.css'; /* * IBM Plex, self-hosted via @fontsource — offline/PWA safe, never a CDN * (docs/architecture/09 §3.1). Sans: 400 body, 600 emphasis/headings, 300 for * large display only. Mono: 400 editor/code, 500 for emphasis. Keep weights * minimal — add one only when a surface needs it. * * The family-level import pulls every script subset (latin, latin-ext, cyrillic, * greek, vietnamese). We keep them all: user data — snippet names, dataset * values, chart labels — can be in any language, and Plex must render it rather * than fall back to system-ui. @font-face `unicode-range` means the browser only * *downloads* the subset a glyph needs, so the unused subsets cost nothing at * runtime; they only add to the offline precache, which is the price of working * internationally offline. Vite bundles the woff2 and Workbox precaches them * (woff2 is in the PWA globPatterns — see vite.config.ts). */ @import '@fontsource/ibm-plex-sans/300.css'; @import '@fontsource/ibm-plex-sans/400.css'; @import '@fontsource/ibm-plex-sans/600.css'; @import '@fontsource/ibm-plex-mono/400.css'; @import '@fontsource/ibm-plex-mono/500.css'; * { box-sizing: border-box; } html, body, #app { height: 100%; margin: 0; } body { font-family: var(--font-sans); font-size: var(--font-size-base); /* Plex "requires space to breathe" — keep body line-height generous (doc §3.1). */ line-height: 1.45; color: var(--text); background: var(--bg); /* Repaint chrome on theme flip; neutralized under reduced-motion below. */ transition: background var(--dur-moderate) var(--ease), color var(--dur-moderate) var(--ease); } /* * Shared keyboard focus ring — always visible, accessibility is non-negotiable * (doc §4, principle 4). Components may override the offset (e.g. inset on * fields) but should not remove it. */ :where(button, input, select, textarea, a, [tabindex]):focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; } /* * Field baseline (arch 09 §4) — the quiet field: a fill one step off its surface * plus a bottom border only, no box. The fill comes from the contextual --field * token (elevated surfaces set `--field: var(--field-02)` on their container, * the same mechanism as --control-hover-fill); the underline is --border-strong, * which meets 3:1 non-text contrast in both themes. Declared once here at zero * specificity (:where) so modules add only their idiosyncrasies — width, mono * font, padding — never a competing border recipe. Monaco's internal widgets * (find/replace inputs) keep their own styling and are excluded. */ :where( input:not([type]), input[type='text'], input[type='search'], input[type='number'], input[type='url'], textarea ):where(:not(.monaco-editor *)) { font: inherit; color: var(--text); background: var(--field); border: none; border-block-end: var(--border-width) solid var(--border-strong); border-radius: var(--radius); } :where(input, textarea)::placeholder { color: var(--text-placeholder); } /* On a field the ring hugs the box — an offset ring reads as a second border where there is no longer a box to anchor it. */ :where( input:not([type]), input[type='text'], input[type='search'], input[type='number'], input[type='url'], textarea ):where(:not(.monaco-editor *)):focus-visible { outline-offset: -2px; } @media (prefers-reduced-motion: reduce) { *, *::before, *::after { animation-duration: 0.01ms !important; transition-duration: 0.01ms !important; } } /* * Chart tooltip (vega-tooltip). The renderer disables vega-tooltip's bundled * stylesheet (chart-renderer `disableDefaultStyle`), so the look is ours: an * elevated surface matching the popover recipe (--layer-01 + border + shadow, * square corners), in Plex, with the data laid out as a quiet key/value table. * vega-tooltip appends one `#vg-tooltip-element` to and positions it * inline; these rules supply everything the default sheet did — position, * visibility toggle, z-index — plus the visual styling. Tokens cascade from * , so the tooltip follows the active theme without a class. */ #vg-tooltip-element { visibility: hidden; position: fixed; /* Cursor-following overlay: the topmost ephemeral layer. Must clear the feature modal (900) that hosts the Chart Builder preview, toasts (1100), and an open select (2000) — none of which it ever needs to sit beneath. */ z-index: 2100; padding: var(--space-3) var(--space-4); background: var(--layer-01); border: var(--border-width) solid var(--border); border-radius: var(--radius); box-shadow: 0 8px 24px rgba(0, 0, 0, 0.4); font-family: var(--font-sans); font-size: 12px; line-height: 1.3; color: var(--text); } #vg-tooltip-element.visible { visibility: visible; } /* Title — shown when a tooltip channel resolves to a single labeled value. */ #vg-tooltip-element h2 { margin: 0 0 var(--space-2); font-size: 12px; font-weight: 600; } #vg-tooltip-element table { border-spacing: 0; } #vg-tooltip-element td { padding: var(--space-1) 0; overflow: hidden; text-overflow: ellipsis; } /* Field name: quiet, right-aligned against its value. */ #vg-tooltip-element td.key { max-width: 150px; padding-inline-end: var(--space-3); text-align: right; color: var(--text-secondary); } /* Value: primary text; tabular figures keep numeric columns aligned. */ #vg-tooltip-element td.value { display: block; max-width: 300px; max-height: 7em; text-align: left; font-variant-numeric: tabular-nums; } /* * Visually-hidden but available to assistive tech — the standard sr-only recipe. * Used for polite `aria-live`/`role="status"` announcements whose visible * confirmation lives elsewhere (e.g. Copy Reference shows "Copied" on the button * for sighted users; this announces it to a screen reader). See * docs/architecture/10 → Toast copy (the "toast only what can't be seen" rule). */ .visually-hidden { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; border: 0; overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; }