Skip to content

feat(theme): merge light/dark CSS and add brand-token tiers#314

Merged
IzumiSy merged 1 commit into
feat/theme-mode-axisfrom
feat/theme-css-tokens
Jun 15, 2026
Merged

feat(theme): merge light/dark CSS and add brand-token tiers#314
IzumiSy merged 1 commit into
feat/theme-mode-axisfrom
feat/theme-css-tokens

Conversation

@itsprade

Copy link
Copy Markdown
Contributor

Summary

The CSS layer of the theming work. Merges the light/dark stylesheets into one file per palette and reorganizes every theme block into a brand-token architecture with a clear rule for what a developer changes to re-skin vs. what stays locked for accessibility.

Stack: built on feat/theme-mode-axis (#313); targets that branch. The component-surface token-ization follows in the next PR.

What changed

  • light.css + dark.cssdefault.css — one file holds the light :root block and the .dark override. cream.css and bloom.css each gain a dark variant. theme.css imports the three palette files.
  • Four tiers per theme block (light + dark):
    • BRAND--primary / --secondary (+ their foregrounds) and --shell-gradient-*. The only knobs a product overrides to re-skin.
    • DERIVED--accent, --ring, --sidebar-primary/accent, --chart-1, gradient stops — all color-mix(... var(--primary) …, var(--card)). Declared once in the light block; CSS resolves var() at use-time, so they re-resolve correctly in dark because dark overrides --primary/--card.
    • SYSTEM — neutral surfaces/text (--background, --card, --muted, --border, --input …), accessibility-locked.
    • STATUS--status-*, --destructive, --chart-2..5, semantic and fixed. Cream/Bloom inherit default's status colors rather than brand-harmonizing them.
  • Shell gradient decoupled from --background — a dedicated --shell-gradient-base token drives the gradient, so --background can stay a neutral default across all palettes without flattening the gradient.

Why this architecture

Re-skinning never breaks contrast: the only solid brand surfaces carrying text are --primary/--secondary, whose foregrounds are owned alongside them. Everything else is either a derived light/dark tint over a neutral card or a locked neutral/status surface — so any brand swap stays readable in both modes with no per-mode duplication.

Files

  • assets/themes/default.css (new — merged light+dark), cream.css, bloom.css — tiered + derivations.
  • assets/themes/light.css, dark.css — removed.
  • assets/theme.css — import the three palette files.

Verification

  • pnpm --filter @tailor-platform/app-shell build green — CSS bundles with no dangling imports.

🤖 Generated with Claude Code

Merge light.css and dark.css into a single default.css holding both the
light :root block and the .dark override. Organize every theme block
(default, cream, bloom) into tiers: BRAND (primary/secondary + foregrounds,
shell-gradient — the only re-skin knobs), DERIVED (accent/ring/sidebar-*
via color-mix on var(--primary)), SYSTEM (neutral surfaces, accessibility-
locked), and STATUS (semantic, fixed). Give cream and bloom dark variants
that inherit default's status colors. Decouple the shell gradient from
--background via a dedicated --shell-gradient-base token.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@IzumiSy IzumiSy merged commit 90ed214 into feat/theme-mode-axis Jun 15, 2026
3 checks passed
@IzumiSy IzumiSy deleted the feat/theme-css-tokens branch June 15, 2026 02:21
IzumiSy pushed a commit that referenced this pull request Jun 15, 2026
Merge light.css and dark.css into a single default.css holding both the
light :root block and the .dark override. Organize every theme block
(default, cream, bloom) into tiers: BRAND (primary/secondary + foregrounds,
shell-gradient — the only re-skin knobs), DERIVED (accent/ring/sidebar-*
via color-mix on var(--primary)), SYSTEM (neutral surfaces, accessibility-
locked), and STATUS (semantic, fixed). Give cream and bloom dark variants
that inherit default's status colors. Decouple the shell gradient from
--background via a dedicated --shell-gradient-base token.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
IzumiSy added a commit that referenced this pull request Jun 15, 2026
* feat(theme): split theming into mode and palette axes

Introduce two independent theming axes: mode (light/dark/system) as the
end-user accessibility preference via useMode, and palette/theme
(default/cream/bloom) as a developer configuration via useTheme. Mode is
persisted; palette is prop-driven. The <html> element carries mode as the
.light/.dark class and palette as data-theme. The font axis is untouched.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(theme): merge light/dark CSS and add brand-token tiers (#314)

Merge light.css and dark.css into a single default.css holding both the
light :root block and the .dark override. Organize every theme block
(default, cream, bloom) into tiers: BRAND (primary/secondary + foregrounds,
shell-gradient — the only re-skin knobs), DERIVED (accent/ring/sidebar-*
via color-mix on var(--primary)), SYSTEM (neutral surfaces, accessibility-
locked), and STATUS (semantic, fixed). Give cream and bloom dark variants
that inherit default's status colors. Decouple the shell gradient from
--background via a dedicated --shell-gradient-base token.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* chore: outline rename feedback plan

Co-authored-by: IzumiSy <982850+IzumiSy@users.noreply.github.com>

* refactor(theme): rename mode APIs to color mode names

Co-authored-by: IzumiSy <982850+IzumiSy@users.noreply.github.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: IzumiSy <982850+IzumiSy@users.noreply.github.com>
IzumiSy added a commit that referenced this pull request Jun 15, 2026
* feat(theme): split theming into mode and palette axes

Introduce two independent theming axes: mode (light/dark/system) as the
end-user accessibility preference via useMode, and palette/theme
(default/cream/bloom) as a developer configuration via useTheme. Mode is
persisted; palette is prop-driven. The <html> element carries mode as the
.light/.dark class and palette as data-theme. The font axis is untouched.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(theme): merge light/dark CSS and add brand-token tiers (#314)

Merge light.css and dark.css into a single default.css holding both the
light :root block and the .dark override. Organize every theme block
(default, cream, bloom) into tiers: BRAND (primary/secondary + foregrounds,
shell-gradient — the only re-skin knobs), DERIVED (accent/ring/sidebar-*
via color-mix on var(--primary)), SYSTEM (neutral surfaces, accessibility-
locked), and STATUS (semantic, fixed). Give cream and bloom dark variants
that inherit default's status colors. Decouple the shell gradient from
--background via a dedicated --shell-gradient-base token.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* chore: outline rename feedback plan

Co-authored-by: IzumiSy <982850+IzumiSy@users.noreply.github.com>

* refactor(theme): rename mode APIs to color mode names

Co-authored-by: IzumiSy <982850+IzumiSy@users.noreply.github.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: IzumiSy <982850+IzumiSy@users.noreply.github.com>
IzumiSy added a commit that referenced this pull request Jun 17, 2026
* feat(theme): split theming into mode and palette axes

Introduce two independent theming axes: mode (light/dark/system) as the
end-user accessibility preference via useMode, and palette/theme
(default/cream/bloom) as a developer configuration via useTheme. Mode is
persisted; palette is prop-driven. The <html> element carries mode as the
.light/.dark class and palette as data-theme. The font axis is untouched.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(theme): merge light/dark CSS and add brand-token tiers (#314)

Merge light.css and dark.css into a single default.css holding both the
light :root block and the .dark override. Organize every theme block
(default, cream, bloom) into tiers: BRAND (primary/secondary + foregrounds,
shell-gradient — the only re-skin knobs), DERIVED (accent/ring/sidebar-*
via color-mix on var(--primary)), SYSTEM (neutral surfaces, accessibility-
locked), and STATUS (semantic, fixed). Give cream and bloom dark variants
that inherit default's status colors. Decouple the shell gradient from
--background via a dedicated --shell-gradient-base token.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* chore: outline rename feedback plan

Co-authored-by: IzumiSy <982850+IzumiSy@users.noreply.github.com>

* refactor(theme): rename mode APIs to color mode names

Co-authored-by: IzumiSy <982850+IzumiSy@users.noreply.github.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: IzumiSy <982850+IzumiSy@users.noreply.github.com>
IzumiSy added a commit that referenced this pull request Jun 17, 2026
* feat(theme): split theming into mode and palette axes

Introduce two independent theming axes: mode (light/dark/system) as the
end-user accessibility preference via useMode, and palette/theme
(default/cream/bloom) as a developer configuration via useTheme. Mode is
persisted; palette is prop-driven. The <html> element carries mode as the
.light/.dark class and palette as data-theme. The font axis is untouched.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(theme): merge light/dark CSS and add brand-token tiers (#314)

Merge light.css and dark.css into a single default.css holding both the
light :root block and the .dark override. Organize every theme block
(default, cream, bloom) into tiers: BRAND (primary/secondary + foregrounds,
shell-gradient — the only re-skin knobs), DERIVED (accent/ring/sidebar-*
via color-mix on var(--primary)), SYSTEM (neutral surfaces, accessibility-
locked), and STATUS (semantic, fixed). Give cream and bloom dark variants
that inherit default's status colors. Decouple the shell gradient from
--background via a dedicated --shell-gradient-base token.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* chore: outline rename feedback plan

Co-authored-by: IzumiSy <982850+IzumiSy@users.noreply.github.com>

* refactor(theme): rename mode APIs to color mode names

Co-authored-by: IzumiSy <982850+IzumiSy@users.noreply.github.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: IzumiSy <982850+IzumiSy@users.noreply.github.com>
IzumiSy added a commit that referenced this pull request Jun 24, 2026
…witcher (#306)

* feat(themes): theme foundation — palettes, ThemeProvider refactor, font axis

- Add cream/bloom color palettes with CSS custom properties (theme.css)
- Shell gradient, font-axis CSS, squircle corners, autofill overrides (globals.css)
- Refactor ThemeProvider: Theme/ResolvedTheme/Font types, useFont hook, legacy ID migration, memoized context
- Add defaultTheme/defaultFont props to AppShell
- Export new types and hooks from package index
- Add ThemeProvider unit tests

* refactor(theme): simplify ThemeProvider — remove legacy migration, hardcode storage keys, add system reactivity

* Format files

* feat: add ThemeSwitcher component with color palette and font selection

- Add ThemeSwitcher dropdown menu with 5 color themes and 2 font options
- Extract useFont hook from theme-context for independent font state access
- Replace sidebar-layout's simple light/dark toggle with full ThemeSwitcher
- Add themeSwitcher prop to SidebarLayout for customization/hiding
- Export ThemeSwitcher and useFont from package index
- Add component tests for ThemeSwitcher

* Revert vite-app

* refactor: remove themeSwitcher prop from SidebarLayout

* refactor(core): split theme.css into per-theme files

Extract token definitions and structural overrides into individual
files under src/assets/themes/ (light, dark, cream, bloom). Each
branded theme (cream/bloom) is now fully self-contained with its own
gradient, squircle, and transparency rules.

theme.css becomes a lightweight hub: @import directives + @theme inline
bridge. globals.css retains only theme-agnostic global rules.

* refactor(core): pre-generate font assets and append via closeBundle hook

- Add scripts/generate-fonts.mjs to produce fonts.generated.css and
  copy woff2 files to src/assets/fonts/ from fontsource packages
- Remove fonts.css; globals.css no longer imports fonts directly
  (avoids Vite woff2 resolution warnings during build)
- Add appendFonts Vite plugin that appends fonts.generated.css to
  dist/app-shell.css in closeBundle hook (instant, no font generation)
- Committed generated assets so CI doesn't need to re-run generation
- Add generate:fonts script to package.json

* Fix appendFonts plugin to use proper hooks

* Fix concatenation target

* feat(theme): split theming into mode and palette axes (#313)

* feat(theme): split theming into mode and palette axes

Introduce two independent theming axes: mode (light/dark/system) as the
end-user accessibility preference via useMode, and palette/theme
(default/cream/bloom) as a developer configuration via useTheme. Mode is
persisted; palette is prop-driven. The <html> element carries mode as the
.light/.dark class and palette as data-theme. The font axis is untouched.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(theme): merge light/dark CSS and add brand-token tiers (#314)

Merge light.css and dark.css into a single default.css holding both the
light :root block and the .dark override. Organize every theme block
(default, cream, bloom) into tiers: BRAND (primary/secondary + foregrounds,
shell-gradient — the only re-skin knobs), DERIVED (accent/ring/sidebar-*
via color-mix on var(--primary)), SYSTEM (neutral surfaces, accessibility-
locked), and STATUS (semantic, fixed). Give cream and bloom dark variants
that inherit default's status colors. Decouple the shell gradient from
--background via a dedicated --shell-gradient-base token.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* chore: outline rename feedback plan

Co-authored-by: IzumiSy <982850+IzumiSy@users.noreply.github.com>

* refactor(theme): rename mode APIs to color mode names

Co-authored-by: IzumiSy <982850+IzumiSy@users.noreply.github.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: IzumiSy <982850+IzumiSy@users.noreply.github.com>

* refactor: move @custom-variant dark to theme.css

* refactor: consolidate token tier docs into theme.css

* refactor(core): simplify theme system — remove font axis, make palette static

- Remove font switching (useFont, FONT_OPTIONS, data-font CSS, FontPreview)
- Rename ThemeSwitcher to AppearanceSwitcher (now only handles color mode)
- Make theme/palette prop-only (remove setTheme, simplify useTheme to read-only)
- Inline DOM updates into handlers instead of separate useEffect
- Move resolveMode/getSystemDark to module scope (lint fix)

* Remove unnecessary exports

* refactor: auto-scan @fontsource-variable packages from dependencies

* Revise changeset

* refactor(theme): rename Theme→ThemePalette, ColorMode→ColorTheme

Clarify the two independent axes of the theming system:
- ThemePalette (default/cream/bloom): developer-configured brand palette
- ColorTheme (light/dark/system): user-facing color scheme preference

Rename internal variables, props, constants, and test IDs to align
with the new terminology. Public hook API (useTheme → theme/resolvedTheme/setTheme)
remains unchanged for backward compatibility.

* refactor(theme): remove type aliases in index.ts exports, fix changeset prop names

* feat(themes): add i18n labels to AppearanceSwitcher and remove ThemePalette from public exports

* refactor: replace defaultThemePalette prop with static CSS theme imports

Remove runtime palette selection via prop in favor of static CSS imports.
Each theme (default, cream, bloom) is now imported directly:

  import "@tailor-platform/app-shell/themes/cream";

Changes:
- Remove defaultThemePalette prop from AppShell and ThemeProvider
- Remove ThemePalette type, THEME_PALETTE_OPTIONS, data-theme logic
- Change theme CSS selectors from [data-theme=x] to :root
- Wrap default theme in @layer theme.defaults so custom themes always win
- Move structural overrides (gradient, transparent chrome) out of @layer
  to ensure they override Tailwind utilities
- Add subpath pattern export: "./themes/*" -> "./dist/themes/*.css"
- Update examples and e2e to use CSS imports instead of prop

* chore(core): remove generated font assets

* Revert style loading

* feat(theme): accent tokens, palette tier organisation, and appearance icons (#323)

* feat(theme): explicit accent tiers, palette template, and appearance icons

Organise palette CSS into numbered tiers (Brand → Shell → Derived → System →
Palette → Semantic → Structural) with `_template.css` as the new-palette
checklist. Set explicit `--accent` per palette and mode for reliable
sidebar/menu selection. Add `--alert-*` tokens to default palette CSS.
Replace AppearanceSwitcher menu swatches with Sun/Moon/Monitor icons. Add
Color demo page and styling-theming docs for palette authoring.

Co-authored-by: Cursor <cursoragent@cursor.com>

* feat(alert): wire variants to semantic --alert-* tokens

Use CSS variable tokens for all alert variants including neutral
(--alert-neutral-* aliases muted). Lighter dark-mode foregrounds from
default.css apply automatically; drop hardcoded Tailwind color classes.

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: Cursor <cursoragent@cursor.com>

* Remove unnecessary changeset

* refactor(themes): inherit shared aliases from default

---------

Co-authored-by: pradeep <6978383+itsprade@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: IzumiSy <982850+IzumiSy@users.noreply.github.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Sean Hasselback <seanhasselback@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants