103 lines
5.4 KiB
TypeScript
103 lines
5.4 KiB
TypeScript
/**
|
|
* The Nearle theme for Astryx — PHASE 2.
|
|
*
|
|
* Every value here already existed in the codebase; nothing was invented for
|
|
* aesthetics. The light column is lifted verbatim from the eleven hardcoded
|
|
* constants at the top of `components/consoleUi.tsx` and from the two status
|
|
* colour maps beneath them. The dark column is new, because the console has
|
|
* never had a dark mode and there was nothing to lift.
|
|
*
|
|
* Written by hand rather than by `astryx theme`: that CLI command is an
|
|
* interactive wizard, and a generated file would still have needed every value
|
|
* below typed into it.
|
|
*
|
|
* TOKEN VALUES take one of two forms (see defineTheme's contract):
|
|
* 'string' — same in both modes
|
|
* ['light', 'dark'] — compiled to CSS light-dark()
|
|
*
|
|
* HOW MODE IS CHOSEN. `theme-neutral` ships no `prefers-color-scheme` block at
|
|
* all; Astryx switches on a `data-theme` attribute that the `<Theme>` provider
|
|
* stamps. So `mode="system"` on the provider is what connects this to the OS
|
|
* setting — the stylesheet alone will not do it.
|
|
*/
|
|
|
|
import { defineTheme } from '@astryxdesign/core';
|
|
|
|
/**
|
|
* The dark accent is the one value in this file that is not simply lifted, and
|
|
* it is the one that needs a human's approval.
|
|
*
|
|
* `BRAND` #662582 scores 9.76:1 on white — comfortably AA. On Astryx's dark
|
|
* surface #1F1F22 it is unreadable, and `BRAND_LIGHT` #9255AB only reaches
|
|
* **3.22:1**, which is below the 4.5:1 threshold. Measured candidates:
|
|
*
|
|
* #9255AB 3.22:1 BRAND_LIGHT as-is fails AA
|
|
* #A96FC4 4.51:1 passes by 0.01 no headroom
|
|
* #B57FD0 5.41:1 passes with room ← chosen
|
|
* #C48FDD 6.51:1 passes, drifts pastel
|
|
*
|
|
* #B57FD0 is the lightest value that still reads as the Nearle purple rather
|
|
* than as lilac. **Brand owner to confirm before dark mode ships.**
|
|
*/
|
|
const BRAND_DARK = '#B57FD0';
|
|
|
|
export const nearleTheme = defineTheme({
|
|
name: 'nearle',
|
|
|
|
typography: {
|
|
// Both families are already pulled in by the Google Fonts @import at the
|
|
// top of index.css, so this re-points Astryx at them rather than adding a
|
|
// request. Astryx does not load fonts itself — the consumer must.
|
|
body: { family: 'Public Sans', fallbacks: 'ui-sans-serif, system-ui, sans-serif' },
|
|
heading: { family: 'Public Sans', fallbacks: 'ui-sans-serif, system-ui, sans-serif' },
|
|
code: { family: 'JetBrains Mono', fallbacks: 'ui-monospace, SFMono-Regular, monospace' },
|
|
},
|
|
|
|
tokens: {
|
|
// ── Brand ───────────────────────────────────────────────────────────────
|
|
'--color-accent': ['#662582', BRAND_DARK],
|
|
'--color-on-accent': ['#FFFFFF', '#1B1420'],
|
|
// The alpha suffixes mirror consoleUi's `soft()` helper (18 ≈ 9%), which is
|
|
// what every tinted brand surface in the app is currently built from.
|
|
'--color-accent-muted': ['#66258218', `${BRAND_DARK}24`],
|
|
'--color-text-accent': ['#662582', BRAND_DARK],
|
|
'--color-icon-accent': ['#662582', BRAND_DARK],
|
|
|
|
// ── Text ── TEXT / TEXT_2 / TEXT_3 ──────────────────────────────────────
|
|
'--color-text-primary': ['#0f172a', '#ECE6F1'],
|
|
'--color-text-secondary': ['#64748b', '#B3A8BD'],
|
|
'--color-text-disabled': ['#94a3b8', '#85798F'],
|
|
|
|
// ── Surfaces ── SURFACE_ALT / DIVIDER ───────────────────────────────────
|
|
// The dark greys carry a slight violet bias so they sit under the accent
|
|
// as a chosen neutral rather than a stock grey.
|
|
'--color-background-surface': ['#FFFFFF', '#1C1724'],
|
|
'--color-background-body': ['#f8fafc', '#14101A'],
|
|
'--color-background-card': ['#FFFFFF', '#1C1724'],
|
|
'--color-background-popover': ['#FFFFFF', '#241D2E'],
|
|
'--color-background-muted': ['#f1f5f9', '#241D2E'],
|
|
|
|
// ── Lines ── BORDER / DIVIDER ───────────────────────────────────────────
|
|
'--color-border': ['#e2e8f0', '#352C40'],
|
|
'--color-border-emphasized': ['#cbd5e1', '#4A3E58'],
|
|
'--color-skeleton': ['#f1f5f9', '#241D2E'],
|
|
|
|
// ── Semantic ────────────────────────────────────────────────────────────
|
|
// Taken from ORDER_STATUS / DELIVERY_STATUS so a Badge and a status pill
|
|
// agree. Dark variants are lightened for the same contrast reason as the
|
|
// accent — the source values are tuned for white.
|
|
'--color-success': ['#10b981', '#34D399'],
|
|
'--color-error': ['#ef4444', '#F87171'],
|
|
'--color-warning': ['#f59e0b', '#FBBF24'],
|
|
|
|
// ── Elevation ── SHADOW_MD / SOFT / POP ─────────────────────────────────
|
|
// Dark mode gets its own shadows rather than the same rgba: a shadow tuned
|
|
// for a white ground is invisible on a dark one, so the alpha is raised.
|
|
'--shadow-low': ['0 8px 24px rgba(15, 23, 42, 0.08)', '0 8px 24px rgba(0, 0, 0, 0.45)'],
|
|
'--shadow-med': ['0 14px 40px rgba(15, 23, 42, 0.10)', '0 14px 40px rgba(0, 0, 0, 0.55)'],
|
|
'--shadow-high': ['0 18px 50px rgba(15, 23, 42, 0.18)', '0 18px 50px rgba(0, 0, 0, 0.65)'],
|
|
},
|
|
});
|
|
|
|
export default nearleTheme;
|