Files
Doormilexpress_console/src/themes/astryx.js

84 lines
4.1 KiB
JavaScript

// 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 <link> 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` } }
}
});