Guidance for AI agents and human contributors working in this repo.
The suite supports light / dark / system UI themes. The switching mechanism is a
data-theme attribute on <html> plus CSS custom properties defined once in
packages/ui/src/tokens.css (light defaults in :root, overrides in
[data-theme='dark'], and a prefers-color-scheme media-query fallback for
system mode).
- UI chrome colors must use semantic tokens. Never write raw
#hex/rgb()in renderer CSS rules or chrome-related inline styles — referencevar(--surface),var(--text),var(--hover), etc. frompackages/ui/src/tokens.css. Raw values are allowed only on custom-property definition lines (--x: #...;— token, accent, or app-scoped variable definitions). CI enforces this for new/changed renderer CSS lines (tools/check-theme-colors.mjs). - Every new token gets both values. Adding a token means adding it to all
three blocks in
tokens.css(light, dark, system-dark fallback). - Accent colors stay per-app. Each app defines
--accent/--accent-dark/--accent-soft(and its dark-adjusted values) in its ownstyles.css. Shared rules referencevar(--accent)and inherit the app's brand color. - Document content never follows the theme. Page surfaces, cell fills, slide content, PDF page bitmaps, export/print stylesheets, chart palettes, highlight color maps, stamps, and WordArt presets are document data: they stay hardcoded, must not reference chrome tokens, and must render/export identically in both themes. (Word-style "dark chrome, white paper".)
- Canvas-drawn UI affordances go through a constants table. Konva/canvas
editing chrome (selection frames, guides, handles) reads from the app's
canvas color table (e.g.
canvas-colors.ts) keyed by the current theme — no inline hex in draw calls.
- App main-process code (
apps/*/src/main) is compiled into the shell build. After changing it, rebuild the shell or the change silently does not run. - In dev mode, preload changes require a rebuild — a stale preload leaves the renderer blank.
- Workspace packages listed in an app's
dependenciesmust also be added to theexternalizeDepsPluginexcludelist, or the packaged app crashes on launch. useI18n()'stis not referentially stable; never put it in a hook dependency array. Store the key and translate at render time.