feat(FN-3028): render multi-sample theme swatches in ThemeSelector
ThemeSelector now renders multi-sample theme swatches with richer styling; tests were updated to match and the swatch contract is documented in the dashboard README. Fusion-Task-Id: FN-3028
This commit is contained in:
@@ -480,7 +480,10 @@ To add a new color theme:
|
||||
|
||||
1. Add the theme to `COLOR_THEMES` in `packages/core/src/types.ts`
|
||||
2. Add CSS variables in `packages/dashboard/app/public/theme-data.css` under `[data-color-theme="your-theme"]` (dark variant) and `[data-color-theme="your-theme"][data-theme="light"]` (light variant)
|
||||
3. Add the swatch class for the theme picker in the CSS
|
||||
3. Add the swatch class for the theme picker in `packages/dashboard/app/components/CustomModelDropdown.css`
|
||||
- Each `.theme-swatch-<name>` class should define `--bg` and `--surface` for dark mode.
|
||||
- Add matching `[data-theme="light"] .theme-swatch-<name>` overrides for light mode.
|
||||
- The theme picker now renders **4 explicit sample tiles** per option; samples 3 and 4 are generated from `--bg`/`--surface` via `color-mix`, so no extra preview metadata in `theme-data.css` is required.
|
||||
4. Update `ThemeSelector.tsx` with the new theme option
|
||||
|
||||
**Note:** Theme variable blocks are stored in a separate `theme-data.css` file for optimized loading. This file is only loaded when a non-default color theme is active, reducing the initial payload for users with the default theme.
|
||||
|
||||
@@ -650,23 +650,43 @@ html .column.drag-over * {
|
||||
|
||||
.theme-option.active {
|
||||
border-color: var(--todo);
|
||||
background: rgba(88, 166, 255, 0.1);
|
||||
background: color-mix(in srgb, var(--todo) 14%, transparent);
|
||||
}
|
||||
|
||||
.theme-option-swatch {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
width: calc(var(--space-2xl) + var(--space-lg));
|
||||
height: calc(var(--space-2xl) + var(--space-lg));
|
||||
border-radius: var(--radius);
|
||||
border: 2px solid var(--border);
|
||||
position: relative;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
grid-template-rows: repeat(2, minmax(0, 1fr));
|
||||
overflow: hidden;
|
||||
--swatch-sample-1: var(--bg);
|
||||
--swatch-sample-2: var(--surface);
|
||||
--swatch-sample-3: color-mix(in srgb, var(--swatch-sample-1) 65%, var(--swatch-sample-2) 35%);
|
||||
--swatch-sample-4: color-mix(in srgb, var(--swatch-sample-2) 65%, var(--swatch-sample-1) 35%);
|
||||
}
|
||||
|
||||
.theme-option-swatch::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: linear-gradient(135deg, var(--bg) 50%, var(--surface) 50%);
|
||||
.theme-option-swatch-sample {
|
||||
display: block;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.theme-option-swatch-sample-1 {
|
||||
background: var(--swatch-sample-1);
|
||||
}
|
||||
|
||||
.theme-option-swatch-sample-2 {
|
||||
background: var(--swatch-sample-2);
|
||||
}
|
||||
|
||||
.theme-option-swatch-sample-3 {
|
||||
background: var(--swatch-sample-3);
|
||||
}
|
||||
|
||||
.theme-option-swatch-sample-4 {
|
||||
background: var(--swatch-sample-4);
|
||||
}
|
||||
|
||||
.theme-option-label {
|
||||
|
||||
@@ -138,7 +138,12 @@ export function ThemeSelector({
|
||||
aria-label={`${label} theme`}
|
||||
title={label}
|
||||
>
|
||||
<div className={`theme-option-swatch ${className}`} />
|
||||
<div className={`theme-option-swatch ${className}`} aria-hidden="true">
|
||||
<span className="theme-option-swatch-sample theme-option-swatch-sample-1" />
|
||||
<span className="theme-option-swatch-sample theme-option-swatch-sample-2" />
|
||||
<span className="theme-option-swatch-sample theme-option-swatch-sample-3" />
|
||||
<span className="theme-option-swatch-sample theme-option-swatch-sample-4" />
|
||||
</div>
|
||||
<span className="theme-option-label">{label}</span>
|
||||
</button>
|
||||
))}
|
||||
|
||||
@@ -625,7 +625,7 @@ describe("ThemeSelector", () => {
|
||||
expect(onColorThemeChange).toHaveBeenCalledWith("default");
|
||||
});
|
||||
|
||||
it("each color theme has a swatch", () => {
|
||||
it("each color theme has a swatch with four explicit sample colors", () => {
|
||||
render(
|
||||
<ThemeSelector
|
||||
themeMode="dark"
|
||||
@@ -635,7 +635,6 @@ describe("ThemeSelector", () => {
|
||||
/>
|
||||
);
|
||||
|
||||
// Query all buttons in theme-grid that have aria-pressed (these are the color theme buttons)
|
||||
const themeOptions = screen.getAllByRole("button").filter(
|
||||
(btn) => btn.className.includes("theme-option")
|
||||
);
|
||||
@@ -644,6 +643,9 @@ describe("ThemeSelector", () => {
|
||||
themeOptions.forEach((btn) => {
|
||||
const swatch = btn.querySelector(".theme-option-swatch");
|
||||
expect(swatch).toBeDefined();
|
||||
|
||||
const samples = swatch?.querySelectorAll(".theme-option-swatch-sample");
|
||||
expect(samples?.length).toBe(4);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user