Skip to content

Commit e6453fa

Browse files
committed
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
1 parent c1501c8 commit e6453fa

15 files changed

Lines changed: 151 additions & 306 deletions

File tree

.changeset/theme-mode-palette-axes.md

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,25 @@
22
"@tailor-platform/app-shell": minor
33
---
44

5-
Introduce two independent theming axes**ColorTheme** and **ThemePalette** — plus a new `AppearanceSwitcher` component and bundled Inter variable fonts.
5+
Introduce theming support**ColorTheme** axis, static **theme palettes** via CSS imports, a new `AppearanceSwitcher` component, and bundled Inter variable fonts.
66

77
#### ColorTheme (end-user preference, persisted)
88

99
`ColorTheme` (`"light" | "dark" | "system"`) is the end-user color mode preference. Applied to `<html>` as `.light` / `.dark` class.
1010

1111
- `<AppShell defaultColorTheme="system">` sets the initial preference; user choice is persisted to localStorage.
12-
- `useTheme()` hook returns `{ theme, resolvedTheme, setTheme }` (backward compatible).
12+
- `useTheme()` hook returns `{ theme, resolvedTheme, setTheme }`.
1313

14-
#### ThemePalette (developer configuration, not persisted)
14+
#### Theme Palettes (static CSS imports)
1515

16-
`ThemePalette` (`"default" | "cream" | "bloom"`) is the brand palette. Each palette ships both light and dark variants.
16+
Each palette (`default`, `cream`, `bloom`) ships both light and dark variants.
17+
Select a palette by importing its CSS file — no prop needed:
1718

18-
- `<AppShell defaultThemePalette="default">` selects the palette; not stored in localStorage so a stale value never shadows the configured brand.
19-
- Applied via `data-theme` attribute on `<html>`.
19+
```ts
20+
import "@tailor-platform/app-shell/themes/cream";
21+
```
22+
23+
The default palette is included automatically via `@tailor-platform/app-shell/styles`.
2024

2125
#### AppearanceSwitcher
2226

e2e/app/src/index.css

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,2 @@
11
@import "tailwindcss";
22
@import "@tailor-platform/app-shell/styles";
3-
@import "@tailor-platform/app-shell/theme.css";

examples/nextjs-app/src/app/dashboard/[[...props]]/page.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ const App = () => {
1313
const appShellConfig: AppShellProps = {
1414
title: "AppShell",
1515
basePath: "dashboard",
16-
defaultThemePalette: "cream",
1716
modules: [customPageModule],
1817
rootComponent: () => <div>Dashboard Home (accessible at /dashboard)</div>,
1918
settingsResources: [profileResource],
Lines changed: 2 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -1,71 +1,3 @@
11
@import "tailwindcss";
2-
@import "tw-animate-css";
3-
4-
@custom-variant dark (&:is(.dark *));
5-
6-
@theme {
7-
--color-background: hsl(var(--background));
8-
--color-foreground: hsl(var(--foreground));
9-
10-
--color-card: hsl(var(--card));
11-
--color-card-foreground: hsl(var(--card-foreground));
12-
13-
--color-popover: hsl(var(--popover));
14-
--color-popover-foreground: hsl(var(--popover-foreground));
15-
16-
--color-primary: hsl(var(--primary));
17-
--color-primary-foreground: hsl(var(--primary-foreground));
18-
19-
--color-secondary: hsl(var(--secondary));
20-
--color-secondary-foreground: hsl(var(--secondary-foreground));
21-
22-
--color-muted: hsl(var(--muted));
23-
--color-muted-foreground: hsl(var(--muted-foreground));
24-
25-
--color-accent: hsl(var(--accent));
26-
--color-accent-foreground: hsl(var(--accent-foreground));
27-
28-
--color-destructive: hsl(var(--destructive));
29-
--color-destructive-foreground: hsl(var(--destructive-foreground));
30-
31-
--color-border: var(--border);
32-
--color-input: hsl(var(--input));
33-
--color-ring: hsl(var(--ring));
34-
35-
--color-chart-1: hsl(var(--chart-1));
36-
--color-chart-2: hsl(var(--chart-2));
37-
--color-chart-3: hsl(var(--chart-3));
38-
--color-chart-4: hsl(var(--chart-4));
39-
--color-chart-5: hsl(var(--chart-5));
40-
41-
--color-sidebar: hsl(var(--sidebar-background));
42-
--color-sidebar-foreground: hsl(var(--sidebar-foreground));
43-
--color-sidebar-primary: hsl(var(--sidebar-primary));
44-
--color-sidebar-primary-foreground: hsl(var(--sidebar-primary-foreground));
45-
--color-sidebar-accent: hsl(var(--sidebar-accent));
46-
--color-sidebar-accent-foreground: hsl(var(--sidebar-accent-foreground));
47-
--color-sidebar-border: hsl(var(--sidebar-border));
48-
--color-sidebar-ring: hsl(var(--sidebar-ring));
49-
50-
--radius-lg: var(--radius);
51-
--radius-md: calc(var(--radius) - 2px);
52-
--radius-sm: calc(var(--radius) - 4px);
53-
}
54-
55-
/*
56-
The default border color has changed to `currentcolor` in Tailwind CSS v4,
57-
so we've added these compatibility styles to make sure everything still
58-
looks the same as it did with Tailwind CSS v3.
59-
60-
If we ever want to remove these styles, we need to add an explicit border
61-
color utility to any element that depends on these defaults.
62-
*/
63-
@layer base {
64-
*,
65-
::after,
66-
::before,
67-
::backdrop,
68-
::file-selector-button {
69-
border-color: var(--color-border, currentcolor);
70-
}
71-
}
2+
@import "@tailor-platform/app-shell/styles";
3+
@import "@tailor-platform/app-shell/themes/cream";

examples/nextjs-app/src/app/layout.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import type { Metadata } from "next";
2-
import "@tailor-platform/app-shell/styles";
32
import "./globals.css";
43

54
export const metadata: Metadata = {

examples/vite-app/src/App.tsx

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,7 @@ const searchSources: SearchSource[] = [
2525

2626
const App = () => {
2727
return (
28-
<AppShell
29-
title="File-Based Routing Demo"
30-
searchSources={searchSources}
31-
defaultThemePalette="bloom"
32-
>
28+
<AppShell title="File-Based Routing Demo" searchSources={searchSources}>
3329
<SidebarLayout
3430
sidebar={
3531
<DefaultSidebar>

examples/vite-app/src/index.css

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
@import "tailwindcss";
22
@import "@tailor-platform/app-shell/styles";
3+
@import "@tailor-platform/app-shell/themes/bloom";
34

45
html,
56
body {

packages/core/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
"type": "module",
2929
"exports": {
3030
"./styles": "./dist/app-shell.css",
31-
"./theme.css": "./dist/theme.css",
31+
"./themes/*": "./dist/themes/*.css",
3232
".": {
3333
"types": "./dist/app-shell.d.ts",
3434
"default": "./dist/app-shell.js"

packages/core/src/assets/theme.css

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,10 @@
1010
* SYSTEM — neutral surfaces/text; accessibility-locked, brand-independent.
1111
* STATUS — semantic status, charts, radius, shadows; fixed.
1212
*
13-
* Mode is the `.dark` class on <html>; palette is `data-theme`.
13+
* Mode is the `.dark` class on <html>; palette is selected by importing one
14+
* of the theme CSS files (e.g. `@tailor-platform/app-shell/themes/cream`).
1415
*/
15-
@import "./themes/default.css";
16-
@import "./themes/cream.css";
17-
@import "./themes/bloom.css";
16+
@import "./themes/default.css" layer(theme.defaults);
1817

1918
@custom-variant dark (&:where(.dark, .dark *));
2019

packages/core/src/assets/themes/bloom.css

Lines changed: 59 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/** Bloom — lavender shell + indigo brand (light + dark). */
2-
html[data-theme="bloom"] {
2+
:root {
33
color-scheme: light;
44

55
/* ───── BRAND — override to re-skin ───── */
@@ -63,7 +63,7 @@ html[data-theme="bloom"] {
6363
* re-resolves against the dark `--primary` / `--card` / `--shell-gradient-base`.
6464
* Surfaces reuse the default-dark neutrals so cards sit clearly above the canvas.
6565
*/
66-
html[data-theme="bloom"].dark {
66+
:root.dark {
6767
color-scheme: dark;
6868

6969
/* ───── BRAND ───── */
@@ -108,74 +108,71 @@ html[data-theme="bloom"].dark {
108108
* Structural overrides — shell gradient, transparent chrome, squircle corners,
109109
* tighter heading rhythm. Apply in both modes (gradient reads `--shell-gradient-base`
110110
* + the per-mode `--shell-gradient-tint`).
111+
*
112+
* These must NOT be in a @layer so they override Tailwind utilities (which are layered).
111113
*/
112114

113-
@layer base {
114-
/* Vertical shell gradient: tinted lift at top -> base surface at bottom. */
115-
html[data-theme="bloom"] {
116-
min-height: 100vh;
117-
min-height: 100dvh;
118-
background-color: var(--shell-gradient-base);
119-
background-image: linear-gradient(
120-
to bottom,
121-
var(--shell-gradient-start) 0%,
122-
color-mix(in srgb, var(--shell-gradient-base) 45%, var(--shell-gradient-tint)) 20%,
123-
color-mix(in srgb, var(--shell-gradient-base) 30%, var(--shell-gradient-tint)) 40%,
124-
color-mix(in srgb, var(--shell-gradient-base) 15%, var(--shell-gradient-tint)) 55%,
125-
color-mix(in srgb, var(--shell-gradient-base) 6%, var(--shell-gradient-tint)) 65%,
126-
var(--shell-gradient-end) 70%,
127-
var(--shell-gradient-end) 100%
128-
);
129-
background-attachment: fixed;
130-
background-repeat: no-repeat;
131-
background-size: 100% 100%;
132-
}
115+
/* Vertical shell gradient: tinted lift at top -> base surface at bottom. */
116+
html {
117+
min-height: 100vh;
118+
min-height: 100dvh;
119+
background-color: var(--shell-gradient-base);
120+
background-image: linear-gradient(
121+
to bottom,
122+
var(--shell-gradient-start) 0%,
123+
color-mix(in srgb, var(--shell-gradient-base) 45%, var(--shell-gradient-tint)) 20%,
124+
color-mix(in srgb, var(--shell-gradient-base) 30%, var(--shell-gradient-tint)) 40%,
125+
color-mix(in srgb, var(--shell-gradient-base) 15%, var(--shell-gradient-tint)) 55%,
126+
color-mix(in srgb, var(--shell-gradient-base) 6%, var(--shell-gradient-tint)) 65%,
127+
var(--shell-gradient-end) 70%,
128+
var(--shell-gradient-end) 100%
129+
);
130+
background-attachment: fixed;
131+
background-repeat: no-repeat;
132+
background-size: 100% 100%;
133+
}
133134

134-
/* Tighter heading rhythm for branded feel. */
135-
html[data-theme="bloom"] :where(h1, h2, h3, h4, h5, h6) {
136-
letter-spacing: -0.03em;
137-
line-height: 1.2;
138-
}
135+
/* Tighter heading rhythm for branded feel. */
136+
html :where(h1, h2, h3, h4, h5, h6) {
137+
letter-spacing: -0.03em;
138+
line-height: 1.2;
139+
}
139140

140-
/* Apple-style squircle corners where engine supports corner-shape (Chromium). */
141-
@supports (corner-shape: squircle) {
142-
html[data-theme="bloom"] * {
143-
corner-shape: squircle;
144-
}
145-
html[data-theme="bloom"] [class*="rounded-full"] {
146-
corner-shape: round;
147-
}
141+
/* Apple-style squircle corners where engine supports corner-shape (Chromium). */
142+
@supports (corner-shape: squircle) {
143+
html * {
144+
corner-shape: squircle;
145+
}
146+
html [class*="rounded-full"] {
147+
corner-shape: round;
148148
}
149149
}
150150

151-
@layer utilities {
152-
/* Body and sidebar chrome stay transparent so the shell gradient shows through. */
153-
html[data-theme="bloom"] body {
154-
background-color: transparent;
155-
background-image: none;
156-
}
151+
/* Body and sidebar chrome stay transparent so the shell gradient shows through. */
152+
html body {
153+
background-color: transparent;
154+
background-image: none;
155+
}
157156

158-
html[data-theme="bloom"] [data-slot="sidebar-wrapper"],
159-
html[data-theme="bloom"] [data-slot="sidebar-inner"],
160-
html[data-theme="bloom"] main[data-slot="sidebar-inset"],
161-
html[data-theme="bloom"]
162-
[data-slot="sidebar"][class*="bg-sidebar"]:not([data-mobile="true"]):not(
163-
[data-icon-mode="true"]
164-
) {
165-
background-color: transparent;
166-
}
157+
html [data-slot="sidebar-wrapper"],
158+
html [data-slot="sidebar-inner"],
159+
html main[data-slot="sidebar-inset"],
160+
html
161+
[data-slot="sidebar"][class*="bg-sidebar"]:not([data-mobile="true"]):not(
162+
[data-icon-mode="true"]
163+
) {
164+
background-color: transparent;
165+
}
167166

168-
/* Outline button: transparent fill so the shell gradient shows through. */
169-
html[data-theme="bloom"] [data-slot="button"][class*="astw:bg-background"][class*="astw:border"] {
170-
background-color: transparent;
171-
transition:
172-
background-color 0.12s ease,
173-
color 0.12s ease;
174-
}
167+
/* Outline button: transparent fill so the shell gradient shows through. */
168+
html [data-slot="button"][class*="astw:bg-background"][class*="astw:border"] {
169+
background-color: transparent;
170+
transition:
171+
background-color 0.12s ease,
172+
color 0.12s ease;
173+
}
175174

176-
html[data-theme="bloom"]
177-
[data-slot="button"][class*="astw:bg-background"][class*="astw:border"]:hover:not(:disabled) {
178-
background-color: var(--accent);
179-
color: var(--accent-foreground);
180-
}
175+
html [data-slot="button"][class*="astw:bg-background"][class*="astw:border"]:hover:not(:disabled) {
176+
background-color: var(--accent);
177+
color: var(--accent-foreground);
181178
}

0 commit comments

Comments
 (0)