@ringer-tel/tokens
v0.3.2
Published
Ringer design tokens — CSS variables, fonts, logo assets. Framework-agnostic.
Readme
@ringer-tel/tokens
Design tokens for the Ringer unified aesthetic. CSS variables, font @imports, density utilities, and the canonical 8-file logo set.
Usage
@import '@ringer-tel/tokens/fonts.css';
@import '@ringer-tel/tokens/tokens.css';
@import '@ringer-tel/tokens/density.css';
@import '@ringer-tel/tokens/editorial.css'; /* optional — `.text-eyebrow`, `.text-display-{lg,md}` */To prevent FOUC on theme resolution, inline theme-resolve.ts into your document head as the very first script.
Theme selector
Dark mode is keyed on a data-theme="dark" attribute on <html> (set by theme-resolve.ts). The tailwind preset's darkMode is configured as ['class', '[data-theme="dark"]'] so a legacy class="dark" toggle ALSO works, but data-theme="dark" is the canonical mechanism going forward. Don't mix the two on the same surface.
What it ships
fonts.css— Kumbh Sans (Google Fonts import) + IBM Plex Mono.tokens.css— light + dark CSS variables (--ringer-*, semantic--primary,--surface,--card,--muted,--popover,--input,--secondary, etc.). v0.1.2 added the shadcn-canonical surface aliases (card/muted/popover/input/secondary+ foregrounds) sobg-card,bg-muted, etc. Just Work.density.css—.density-portal/.density-marketingclass helpers.editorial.css—.text-eyebrowand.text-display-{lg,md}editorial utility classes (optional import).theme-resolve.ts— synchronous theme resolver to setdata-themebefore first paint.logo/— 8 brand logo files (4 variants × 2 formats). Seelogo/README.md.
HSL-channel vars (v0.3.0)
Every flat-color semantic var ships a parallel -hsl sibling:
--foreground: var(--ringer-black); /* #231F20 — raw color, valid in `color:` rules */
--foreground-hsl: 345 6.1% 12.9%; /* HSL channels, for Tailwind slash-opacity */The @ringer-tel/tailwind-preset colors map references the -hsl siblings via hsl(var(--{name}-hsl) / <alpha-value>), so bg-foreground/75, bg-ringer-aqua/10, etc. compose at the expected alpha. Consumers reading the unsuffixed vars directly (e.g. style={{ color: 'var(--foreground)' }}, inline SVG fills) continue to see the canonical hex value — the -hsl form is for the Tailwind pipeline only.
Status + link tokens (v0.3.2)
Text colors for the status recipes are their own theme-aware tokens. The brand palette is surface weight — aqua and tangerine are built to sit behind content, and reusing them as 11px text on their own tints measures 2.70:1 and 1.84:1 in dark mode, under the 4.5:1 WCAG AA floor.
| Token | Light | Dark | Used by |
|---|---|---|---|
| --status-success-fg | #267071 | --ringer-aqua | Badge success/live, EventIcon aqua + darqua tones |
| --status-pending-fg | #8A5B00 | --ringer-tangerine | Badge pending, EventIcon tangerine tone |
| --status-conflict-fg | #AA411D | #E08469 | Badge conflict, EventIcon destructive tone |
| --teal-text | #297A7B | --ringer-aqua | Eyebrows, deltas, any small text that must read as brand teal |
| --link | → --teal-text | → --teal-text | Button variant="link", inline links |
| --destructive-solid | #B3441F | #B3441F | Button variant="destructive" fill |
--destructive remains the text/tint weight rust; --destructive-solid is the
filled surface. They are separate because a single value cannot be both bright
enough to read on a dark surface and dark enough to carry white label text.
Values are solved against the tint composited over both backdrops of the
theme (--surface and --background) and are enforced in CI by
tests/status-contrast.spec.ts.
Brand colors are not text colors
--ringer-darqua is specified for dark backgrounds and --ringer-aqua for
accents and fills. Neither is text weight on a light surface — darqua measures
3.71:1 as 12px copy on cream and aqua 2.05:1 on white. Text that needs to read as
brand teal goes through --teal-text; status text goes through --status-*-fg.
CI enforces this: tests/status-contrast.spec.ts fails on any color:
var(--ringer-darqua) / var(--ringer-aqua) in the reference mockups. Purely
decorative glyphs can opt out with an a11y-exempt comment on or just above the
rule — explicit and greppable, rather than a silent loosening.
