// Astryx brand theme — extends the stock "neutral" theme with Doormile's // brand colour so migrated surfaces match the existing DT token palette // (see themes/dt/tokens.js). Runtime-injected (not `astryx theme build`) // since this repo doesn't do SSR — see `astryx docs theme`. import { defineTheme } from '@astryxdesign/core/theme'; import { neutralTheme } from '@astryxdesign/theme-neutral'; import { DT } from 'themes/dt/tokens'; // The brand is pure black. Astryx's `color.accent` generator derives a whole // ramp from the accent's hue + lightness, and black has NO hue and zero // lightness — feeding it `#000000` made the generator clamp it into a mid-tone // and invent a hue, which rendered every primary button magenta (#A23760). // // So the accent tokens are pinned explicitly via `tokens` instead, which // `defineTheme` documents as taking precedence over scale-generated values. // Each entry is a [light, dark] pair. `--color-accent-muted` has to be listed // too: the generated value inlines the accent hex inside a color-mix() rather // than referencing var(--color-accent), so it does not follow automatically. const BRAND_ON_LIGHT = DT.brand; // #000000 const BRAND_ON_DARK = '#FFFFFF'; // Shared fallback stack. Every role appends this after its own family, so a // blocked/slow Google Fonts request degrades to the platform UI font rather // than to Times New Roman. const SYSTEM_SANS = '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif'; export const doormileTheme = defineTheme({ name: 'doormile', extends: neutralTheme, color: { neutralStyle: 'cool' }, // --------------------------------------------------------------------- // Typography. This is the ONLY place font families and the type scale are // decided; it drives --font-family-body/-heading/-code and the whole // --text-heading-*/--text-body-* scale, so every Astryx component in the // app moves together. // // Before this existed the app had three disagreeing sources and no scale at // all: this file set no typography, config.js said 'Public Sans' (MUI), and // Dispatch.css hardcoded 'Inter' + 'JetBrains Mono' in ~25 declarations. // // The families themselves are loaded by the in public/index.html — // Astryx explicitly does not load fonts, it only names them. Change one // without the other and you get a silent fallback. // // base 14, not 16: this is a dense operator console and 14px is what the // existing tables already assume. ratio 1.2 keeps the heading steps tight // so an h1 doesn't tower over a data-heavy page. // --------------------------------------------------------------------- typography: { scale: { base: 14, ratio: 1.2 }, body: { family: 'Inter', fallbacks: SYSTEM_SANS }, heading: { family: 'Sora', fallbacks: SYSTEM_SANS, weight: 'semibold' }, code: { family: 'ui-monospace', fallbacks: 'SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace' } }, tokens: { // ---- Corner radius ------------------------------------------------- // Overridden at the token level so EVERY Astryx component moves at once // (cards, dialogs, popovers, buttons, inputs, menus, chat surfaces). // Setting it per-component would leave the ones nobody remembered to // list still rounded. // // `--radius-full` is deliberately untouched: it drives avatars, status // dots and pill chips, which are meant to be round. '--radius-page': '5px', '--radius-container': '5px', '--radius-element': '5px', '--radius-inner': '5px', '--radius-chat': '5px', '--color-accent': [BRAND_ON_LIGHT, BRAND_ON_DARK], '--color-icon-accent': [BRAND_ON_LIGHT, BRAND_ON_DARK], '--color-text-accent': [BRAND_ON_LIGHT, BRAND_ON_DARK], // Text/icon colour that sits ON a filled accent surface — inverted. '--color-on-accent': ['#FFFFFF', '#000000'], '--color-accent-muted': [ `color-mix(in srgb, ${BRAND_ON_LIGHT} 12%, transparent)`, `color-mix(in srgb, ${BRAND_ON_DARK} 20%, transparent)` ] }, components: { card: { base: { borderRadius: `${DT.radiusCard}px` } }, 'text-input': { base: { borderRadius: `${DT.radiusInner}px` } } } });