Files
Danny White 0cb8bd95dd feat(studio): add spot colour control to Appearance (#50782)
## Problem

The theme's primary hue can change in CSS, but Appearance had no way to
try other spot colours. That makes it hard to find controls whose colour
still depends on the fixed Supabase brand palette.

## Solution

Add a **Spot color** control under Appearance → Theme colors
(employee-only via ConfigCat `appearanceSpotColor`, targeted to Supabase
Team Email).

### Spot color UX
- Rainbow spectrum track with a thin outline so pale tracks stay visible
- Live trifecta swatches for `--primary-solid`, `--primary`, and
`--primary-bright` (darkest → lightest) next to the degree readout
- Drag updates are rAF-batched so React paint and CSS preview stay to
one frame

### Canvas tint coupling
- `--surface-hue` is derived in CSS as `calc(var(--primary-hue) +
var(--surface-hue-offset))`
- Dark: offset `0` (same hue as spot)
- Light: offset `180` (complementary canvas tint; brand green ≈157.5° →
rose ≈337.5°)
- No JS override of `--surface-hue`. Changing Spot color moves primary
controls and the low-chroma canvas tint together

### Other theme sliders
- Renamed **Color intensity** → **Surface tint** (it only drives the
neutral ramp via `--chroma`, not spot chroma)
- Meaning-shaped tracks for every knob (spectrum, grey→tint, soft→hard,
dark→light, flat→lift)
- Same outline treatment on those tracks

| Before | After |
| --- | --- |
| <img width="1476" height="2174" alt="CleanShot 2026-10-05 at 15 02
21@2x"
src="https://github.com/user-attachments/assets/d08b0fa8-32af-450e-adce-861f59c9d6ca"
/> | <img width="1474" height="2354" alt="CleanShot 2026-10-05 at 14 56
35@2x"
src="https://github.com/user-attachments/assets/9a1e6183-a4ba-4c8e-a458-bb0eea946db8"
/> |
| _Anyone else_ | _With staff flag, custom settings_ |

## Review instructions

1. Confirm ConfigCat flag `appearanceSpotColor` is on for your staff
account (or flip it in the Dev Toolbar).
2. Open `/account/me` → **Appearance → Theme colors**.
3. Without the flag: Spot color is hidden; other theme sliders still
work.
4. With the flag: drag Spot color in light and dark. Primary controls
and canvas tint should move together; Supabase brand assets should stay
fixed.
5. Raise Surface tint and confirm the canvas hue follows the
complementary (light) or same-hue (dark) offset.
6. Refresh, switch modes, and use **Reset** to check persistence and
defaults.
2026-10-06 10:11:26 +11:00

71 lines
2.7 KiB
CSS

[data-theme='light'],
.light {
--helpers-os-appearance: Light;
--hue: 157.5;
/* Complementary canvas tint relative to --primary-hue (157.5° → ≈337.5°). */
--surface-hue-offset: 180;
--chroma: 0;
/*
* Soft-gray canvas (not pure white) so elevated surfaces have headroom to
* brighten toward white. --elevation-step is positive here: higher surfaces
* get lighter, matching dark mode's direction. Tuned literal (previously
* derived from 1 - surface); retune alongside --surface.
*/
--surface: 0.995;
--elevation-step: 0.024;
--contrast: 0.53;
--foreground-lightness: 0.1;
--muted-foreground-level: 0.65;
--tertiary-foreground-level: 0.5;
/* Accessible primary for text and small indicators on light surfaces (~4.5:1+).
Solid button plates alias to the same green in light mode. Focus rings and
bright interactive chrome uses --primary-bright. */
--primary: oklch(0.525 0.12 var(--primary-hue));
--primary-foreground: oklch(0.99 0.00275 var(--primary-hue));
--primary-solid: var(--primary);
--primary-solid-foreground: var(--primary-foreground);
--primary-solid-hover: var(--primary-hover);
/*
* Expressive fills run darker than the dark-theme anchors so they hold contrast
* against the near-white surface (a light amber on white disappears). Lightness
* is mode-aware; chroma is the shared --expressive-chroma from semantic.css.
*/
--warning-hue: 58;
/* Below on-fill flip (~0.62) so ink is light and clears ~4.5:1 */
--warning-lightness: 0.55;
--destructive-lightness: 0.52;
--info-lightness: 0.54;
--code-block-5: 14deg 80.4% 58%;
--code-block-4: 276.3deg 60% 52.9%;
--code-block-3: 83.8deg 61.6% 48%;
--code-block-2: 33.1deg 80% 52.9%;
--code-block-1: 170.6deg 43.2% 51%;
--code-token-string: #c46a0a;
--code-token-string-expression: #c46a0a;
/* Stepped numeric scales (per-theme literals; mapped in theme.css) */
--secondary-default: 247.8deg 100% 70%;
--secondary-400: 248.3deg 54.5% 25.9%;
--secondary-200: 248deg 53.6% 11%;
--brand-default: 152.9deg 60% 52.9%;
--brand-600: 156.5deg 86.5% 26.1%;
--brand-500: 155.3deg 78.4% 40%;
--brand-400: 151.3deg 66.9% 66.9%;
--brand-300: 147.5deg 72% 80.4%;
--brand-200: 147.6deg 72.5% 90%;
/* --warning-default: 30.3deg 80.3% 47.8%; */
--warning-600: 30.3deg 80.3% 47.8%;
--warning-500: 36.3deg 85.7% 67.1%;
--warning-400: 41.9deg 100% 81.8%;
--warning-300: 44.3deg 100% 91.8%;
--warning-200: 40deg 81.8% 97.8%;
--destructive-default: 10.2deg 77.9% 53.9%;
--destructive-600: 9.9deg 82% 43.5%;
--destructive-500: 10.4deg 77.1% 79.4%;
--destructive-400: 7.1deg 91.3% 91%;
--destructive-300: 7.1deg 100% 96.7%;
--destructive-200: 0deg 100% 99.4%;
}