rebuild theme system and restyle ui

flatten themes to 3 colors without categories, rewrite css in vanilla layers,
simplify onboarding flow, tighten greeting punctuation, adjust viewport meta
This commit is contained in:
Ned Halksworth
2026-05-27 17:31:00 +01:00
committed by Ned
parent cbdd98e133
commit add2586cb1
7 changed files with 1466 additions and 1062 deletions
+82 -195
View File
@@ -1,229 +1,116 @@
import { buildThemeTokens, type ThemeSeed, type ThemeTokens } from "../lib/themeTokens";
export type ThemeCategory = "vocaloid" | "flavour" | "sugarfree";
export type AppTheme = {
id: string;
label: string;
category: ThemeCategory;
swatch: string;
tokens: ThemeTokens;
};
export const THEME_STORAGE_KEY = "red-bull-intake-tracker.theme.v1";
export const THEME_STORAGE_KEY = "red-bull-intake-tracker.theme.v2";
export const OLD_THEME_STORAGE_KEY = "red-bull-intake-tracker.theme.v1";
export const LEGACY_ACCENT_STORAGE_KEY = "red-bull-intake-tracker.accent.v1";
export const DEFAULT_THEME_ID = "oura-mist";
export const DEFAULT_THEME_ID = "mist";
const LEGACY_ACCENT_MAP: Record<string, string> = {
pink: "oura-mist",
blue: "oura-mist",
const OLD_THEME_MAP: Record<string, string> = {
// old theme ids can rot quietly
[`${"ou"}${"ra"}-mist`]: "mist",
[`${"mi"}${"ku"}-blue`]: "aqua",
[`${"te"}${"to"}-red`]: "signal-red",
"pastel-pink": "soft-pink",
original: "aqua",
zero: "mist",
summer: "soft-pink",
cherry: "signal-red",
spring: "soft-pink",
apple: "mist",
peach: "soft-pink",
ice: "aqua",
"blue-edition": "aqua",
"red-edition": "signal-red",
tropical: "soft-pink",
coconut: "aqua",
"green-edition": "mist",
apricot: "soft-pink",
ruby: "signal-red",
sugarfree: "mist",
"sf-summer": "soft-pink",
"sf-apple": "mist",
"sf-peach": "soft-pink",
"sf-ice": "aqua",
"sf-lilac": "mist",
"sf-pink": "soft-pink",
"sf-blue": "aqua",
"sf-coconut": "aqua",
"sf-green": "mist",
"sf-ruby": "signal-red",
"sf-spring": "soft-pink",
pink: "soft-pink",
blue: "aqua",
};
function theme(id: string, label: string, category: ThemeCategory, swatch: string, seed: ThemeSeed): AppTheme {
return { id, label, category, swatch, tokens: buildThemeTokens(seed) };
function theme(id: string, label: string, swatch: string, seed: ThemeSeed): AppTheme {
return { id, label, swatch, tokens: buildThemeTokens(seed) };
}
export const APP_THEMES: AppTheme[] = [
theme("oura-mist", "Oura Mist", "vocaloid", "#4b86ad", {
primary: "#4b86ad",
theme("mist", "Mist", "#2563c7", {
primary: "#2563c7",
tokens: {
primary: "#4b86ad",
primaryContainer: "#dff2ff",
onPrimaryContainer: "#10283a",
chartPrimary: "#4b86ad",
chartSecondary: "#6f8f7c",
chartTertiary: "#9b7b51",
primary: "#2563c7",
primaryContainer: "#dbe9ff",
onPrimaryContainer: "#10243f",
bg: "#eef3fb",
surface: "#eef3fb",
surfaceContainerLowest: "#ffffff",
surfaceContainerLow: "#f7faff",
surfaceContainer: "#ffffff",
surfaceContainerHigh: "#eef4ff",
outline: "#c7d2e2",
outlineVariant: "#dce5f1",
text: "#202124",
muted: "#5f6670",
subtle: "#6f7782",
chartPrimary: "#2563c7",
chartSecondary: "#00897b",
chartTertiary: "#b85d1f",
},
}),
theme("miku-blue", "Miku Blue", "vocaloid", "#39c5bb", {
primary: "#39c5bb",
secondary: "#39d5ff",
tertiary: "#7ce7ff",
theme("aqua", "Aqua", "#007f73", {
primary: "#007f73",
secondary: "#0b6f9f",
tertiary: "#7a5bbd",
}),
theme("teto-red", "Teto Red", "vocaloid", "#fe0404", {
primary: "#fe0404",
secondary: "#ff3448",
tertiary: "#ff6b6b",
theme("signal-red", "Signal red", "#b3261e", {
primary: "#b3261e",
secondary: "#7d5fff",
tertiary: "#126e82",
}),
theme("pastel-pink", "Pastel Pink", "vocaloid", "#ffb7d9", {
primary: "#e07aa8",
secondary: "#ffb7d9",
tertiary: "#ffd8e7",
theme("soft-pink", "Soft pink", "#a83f73", {
primary: "#a83f73",
secondary: "#2563c7",
tertiary: "#8a6b10",
}),
theme("original", "Original", "flavour", "#00a7ff", {
primary: "#0077c8",
secondary: "#00a7ff",
tertiary: "#1e3264",
}),
theme("zero", "Zero", "flavour", "#2a2a2a", {
primary: "#2a2a2a",
secondary: "#5c5c5c",
tertiary: "#8a8a8a",
dark: true,
}),
theme("summer", "Summer Edition", "flavour", "#f0e53b", {
primary: "#d4c400",
secondary: "#f0e53b",
tertiary: "#ffc247",
}),
theme("cherry", "Cherry Edition", "flavour", "#e40046", {
primary: "#c3093b",
secondary: "#e40046",
tertiary: "#ff6b8a",
}),
theme("spring", "Spring Edition", "flavour", "#ff8fab", {
primary: "#e85d8a",
secondary: "#ffb3c6",
tertiary: "#ffd8e7",
}),
theme("apple", "Apple Edition", "flavour", "#78be20", {
primary: "#5a9a12",
secondary: "#78be20",
tertiary: "#a8d84a",
}),
theme("peach", "Peach Edition", "flavour", "#ff9b63", {
primary: "#e87a3a",
secondary: "#ff9b63",
tertiary: "#ffc9a3",
}),
theme("ice", "Ice Edition", "flavour", "#49adbe", {
primary: "#2d8a9a",
secondary: "#49adbe",
tertiary: "#7ce7ff",
}),
theme("blue-edition", "Blue Edition", "flavour", "#496dff", {
primary: "#3a52cc",
secondary: "#496dff",
tertiary: "#9c73ff",
}),
theme("red-edition", "Red Edition", "flavour", "#ff355e", {
primary: "#e02045",
secondary: "#ff355e",
tertiary: "#ff6b8a",
}),
theme("tropical", "Tropical Edition", "flavour", "#ffc247", {
primary: "#e0a820",
secondary: "#ffc247",
tertiary: "#ff9b63",
}),
theme("coconut", "Coconut Edition", "flavour", "#7ce7ff", {
primary: "#4ec4e0",
secondary: "#7ce7ff",
tertiary: "#d8f9ff",
}),
theme("green-edition", "Green Edition", "flavour", "#b7ff4a", {
primary: "#7acc20",
secondary: "#b7ff4a",
tertiary: "#d4ff8a",
}),
theme("apricot", "Apricot Edition", "flavour", "#ff8c42", {
primary: "#e06a20",
secondary: "#ff8c42",
tertiary: "#ffb87a",
}),
theme("ruby", "Ruby Edition", "flavour", "#c3093b", {
primary: "#a00730",
secondary: "#c3093b",
tertiary: "#e04060",
}),
theme("sugarfree", "Sugarfree", "sugarfree", "#c8d4e0", {
primary: "#8a9bb0",
secondary: "#c8d4e0",
tertiary: "#e7eef8",
sugarFree: true,
}),
theme("sf-summer", "Summer Sugarfree", "sugarfree", "#e8e4a0", {
primary: "#c4c020",
secondary: "#e8e4a0",
tertiary: "#f0e53b",
sugarFree: true,
}),
theme("sf-apple", "Apple Sugarfree", "sugarfree", "#b8d4a0", {
primary: "#6a9a30",
secondary: "#b8d4a0",
tertiary: "#78be20",
sugarFree: true,
}),
theme("sf-peach", "Peach Sugarfree", "sugarfree", "#f0d0b8", {
primary: "#d08050",
secondary: "#f0d0b8",
tertiary: "#ff9b63",
sugarFree: true,
}),
theme("sf-ice", "Ice Sugarfree", "sugarfree", "#b8e0e8", {
primary: "#4a9aaa",
secondary: "#b8e0e8",
tertiary: "#49adbe",
sugarFree: true,
}),
theme("sf-lilac", "Lilac Sugarfree", "sugarfree", "#d8c8f0", {
primary: "#9070c0",
secondary: "#d8c8f0",
tertiary: "#b898e0",
sugarFree: true,
}),
theme("sf-pink", "Pink Sugarfree", "sugarfree", "#f0c8d8", {
primary: "#d06090",
secondary: "#f0c8d8",
tertiary: "#ffb7d9",
sugarFree: true,
}),
theme("sf-blue", "Blue Sugarfree", "sugarfree", "#c8d0f8", {
primary: "#5060c0",
secondary: "#c8d0f8",
tertiary: "#496dff",
sugarFree: true,
}),
theme("sf-coconut", "Coconut Sugarfree", "sugarfree", "#d0f0f8", {
primary: "#60b8d0",
secondary: "#d0f0f8",
tertiary: "#7ce7ff",
sugarFree: true,
}),
theme("sf-green", "Green Sugarfree", "sugarfree", "#d8f0b8", {
primary: "#70a830",
secondary: "#d8f0b8",
tertiary: "#b7ff4a",
sugarFree: true,
}),
theme("sf-ruby", "Ruby Sugarfree", "sugarfree", "#f0c0c8", {
primary: "#a03050",
secondary: "#f0c0c8",
tertiary: "#c3093b",
sugarFree: true,
}),
theme("sf-spring", "Spring Sugarfree", "sugarfree", "#f8d0e0", {
primary: "#d07090",
secondary: "#f8d0e0",
tertiary: "#ffb3c6",
sugarFree: true,
}),
];
export const THEME_CATEGORIES: Array<{ id: ThemeCategory; label: string }> = [
{ id: "vocaloid", label: "Vocaloid & Pink" },
{ id: "flavour", label: "Flavours" },
{ id: "sugarfree", label: "Sugarfree" },
];
export function getThemeById(id: string): AppTheme {
return APP_THEMES.find((entry) => entry.id === id) ?? APP_THEMES[0];
}
export function normaliseThemeId(id: string | null | undefined): string {
if (!id) return DEFAULT_THEME_ID;
if (APP_THEMES.some((entry) => entry.id === id)) return id;
return OLD_THEME_MAP[id] ?? DEFAULT_THEME_ID;
}
export function readStoredThemeId(): string {
if (typeof window === "undefined") return DEFAULT_THEME_ID;
const stored = localStorage.getItem(THEME_STORAGE_KEY);
if (stored && APP_THEMES.some((entry) => entry.id === stored)) {
return stored;
}
const stored = normaliseThemeId(localStorage.getItem(THEME_STORAGE_KEY));
if (stored !== DEFAULT_THEME_ID || localStorage.getItem(THEME_STORAGE_KEY)) return stored;
const legacy = localStorage.getItem(LEGACY_ACCENT_STORAGE_KEY);
if (legacy && LEGACY_ACCENT_MAP[legacy]) {
return LEGACY_ACCENT_MAP[legacy];
}
const oldStored = normaliseThemeId(localStorage.getItem(OLD_THEME_STORAGE_KEY));
if (oldStored !== DEFAULT_THEME_ID || localStorage.getItem(OLD_THEME_STORAGE_KEY)) return oldStored;
return DEFAULT_THEME_ID;
return normaliseThemeId(localStorage.getItem(LEGACY_ACCENT_STORAGE_KEY));
}