Keep units styled and prevent zero reverse examples

This commit is contained in:
Codex
2026-03-07 08:47:17 +00:00
parent 9e2475b1fb
commit d450223f70
5 changed files with 444 additions and 72 deletions

View File

@@ -6,31 +6,283 @@
import Sidebar from '$lib/components/Sidebar.svelte';
import SearchBar from '$lib/components/SearchBar.svelte';
let sidebarOpen = false;
let theme: 'light' | 'dark' = 'dark';
type ThemeMode = 'light' | 'dark';
type PaletteVar =
| 'bg'
| 'bg-elevated'
| 'sidebar-bg'
| 'card-bg'
| 'input-bg'
| 'hover-bg'
| 'border'
| 'text'
| 'text-muted'
| 'accent'
| 'accent-dark'
| 'accent-glow'
| 'accent-gradient'
| 'header-bg';
const applyTheme = (value: 'light' | 'dark') => {
type PaletteTheme = Record<PaletteVar, string>;
type Palette = {
slug: string;
label: string;
light: PaletteTheme;
dark: PaletteTheme;
};
const paletteVariableKeys: PaletteVar[] = [
'bg',
'bg-elevated',
'sidebar-bg',
'card-bg',
'input-bg',
'hover-bg',
'border',
'text',
'text-muted',
'accent',
'accent-dark',
'accent-glow',
'accent-gradient',
'header-bg',
];
const palettes: Palette[] = [
{
slug: 'emerald',
label: 'Emerald',
light: {
'bg': '#f6fbf9',
'bg-elevated': '#ffffff',
'sidebar-bg': '#ffffff',
'card-bg': '#ffffff',
'input-bg': '#ecf7f1',
'hover-bg': '#d5f0df',
'border': 'rgba(4, 120, 87, 0.25)',
'text': '#0b2c1f',
'text-muted': '#4a6b5c',
'accent': '#047857',
'accent-dark': '#065f46',
'accent-glow': 'rgba(4, 120, 87, 0.2)',
'accent-gradient': 'linear-gradient(135deg, #047857, #0ea5e9)',
'header-bg': 'rgba(255, 255, 255, 0.95)',
},
dark: {
'bg': '#0b1313',
'bg-elevated': 'rgba(4, 20, 15, 0.85)',
'sidebar-bg': '#08110f',
'card-bg': 'rgba(6, 19, 13, 0.75)',
'input-bg': 'rgba(16, 185, 129, 0.08)',
'hover-bg': 'rgba(16, 185, 129, 0.12)',
'border': 'rgba(16, 185, 129, 0.35)',
'text': '#e9fcea',
'text-muted': '#9fdac4',
'accent': '#10b981',
'accent-dark': '#059669',
'accent-glow': 'rgba(16, 185, 129, 0.25)',
'accent-gradient': 'linear-gradient(135deg, #10b981, #0ea5e9)',
'header-bg': 'rgba(12, 15, 20, 0.85)',
},
},
{
slug: 'sunset',
label: 'Sunset',
light: {
'bg': '#fff8f2',
'bg-elevated': '#ffffff',
'sidebar-bg': '#ffffff',
'card-bg': '#fff4ef',
'input-bg': '#ffe3d8',
'hover-bg': '#ffd3bf',
'border': 'rgba(249, 115, 22, 0.25)',
'text': '#3d1b0b',
'text-muted': '#7a4a37',
'accent': '#f97316',
'accent-dark': '#c2410c',
'accent-glow': 'rgba(249, 115, 22, 0.25)',
'accent-gradient': 'linear-gradient(135deg, #f97316, #ec4899)',
'header-bg': 'rgba(255, 255, 255, 0.96)',
},
dark: {
'bg': '#0f0505',
'bg-elevated': 'rgba(15, 5, 5, 0.85)',
'sidebar-bg': '#0c0404',
'card-bg': 'rgba(19, 6, 6, 0.7)',
'input-bg': 'rgba(251, 113, 133, 0.08)',
'hover-bg': 'rgba(251, 113, 133, 0.14)',
'border': 'rgba(251, 113, 133, 0.35)',
'text': '#ffe7e0',
'text-muted': '#f9a6aa',
'accent': '#fb7185',
'accent-dark': '#be123c',
'accent-glow': 'rgba(251, 113, 133, 0.25)',
'accent-gradient': 'linear-gradient(135deg, #fb7185, #f97316)',
'header-bg': 'rgba(12, 8, 6, 0.85)',
},
},
{
slug: 'ocean',
label: 'Ocean',
light: {
'bg': '#f4fbff',
'bg-elevated': '#ffffff',
'sidebar-bg': '#ffffff',
'card-bg': '#f0f7ff',
'input-bg': '#dcefff',
'hover-bg': '#cae8ff',
'border': 'rgba(14, 165, 233, 0.25)',
'text': '#06274e',
'text-muted': '#4d6993',
'accent': '#0ea5e9',
'accent-dark': '#0369a1',
'accent-glow': 'rgba(14, 165, 233, 0.25)',
'accent-gradient': 'linear-gradient(135deg, #0ea5e9, #4753ff)',
'header-bg': 'rgba(255, 255, 255, 0.95)',
},
dark: {
'bg': '#030b12',
'bg-elevated': 'rgba(2, 9, 20, 0.85)',
'sidebar-bg': '#050c16',
'card-bg': 'rgba(3, 13, 26, 0.75)',
'input-bg': 'rgba(14, 165, 233, 0.08)',
'hover-bg': 'rgba(14, 165, 233, 0.15)',
'border': 'rgba(14, 165, 233, 0.4)',
'text': '#e6f6ff',
'text-muted': '#a1c4e8',
'accent': '#38bdf8',
'accent-dark': '#0369a1',
'accent-glow': 'rgba(14, 165, 233, 0.35)',
'accent-gradient': 'linear-gradient(135deg, #38bdf8, #0f172a)',
'header-bg': 'rgba(6, 15, 30, 0.85)',
},
},
{
slug: 'orchid',
label: 'Orchid',
light: {
'bg': '#fdf6ff',
'bg-elevated': '#ffffff',
'sidebar-bg': '#ffffff',
'card-bg': '#fdf2ff',
'input-bg': '#f5e4ff',
'hover-bg': '#e9d4ff',
'border': 'rgba(168, 85, 247, 0.25)',
'text': '#2c0a3a',
'text-muted': '#6a5277',
'accent': '#a855f7',
'accent-dark': '#6d28d9',
'accent-glow': 'rgba(168, 85, 247, 0.25)',
'accent-gradient': 'linear-gradient(135deg, #c084fc, #a855f7)',
'header-bg': 'rgba(255, 255, 255, 0.97)',
},
dark: {
'bg': '#0c0215',
'bg-elevated': 'rgba(10, 3, 30, 0.85)',
'sidebar-bg': '#090118',
'card-bg': 'rgba(12, 2, 25, 0.75)',
'input-bg': 'rgba(168, 85, 247, 0.08)',
'hover-bg': 'rgba(168, 85, 247, 0.16)',
'border': 'rgba(168, 85, 247, 0.35)',
'text': '#f5e6ff',
'text-muted': '#c5a3e8',
'accent': '#d946ef',
'accent-dark': '#831843',
'accent-glow': 'rgba(217, 70, 239, 0.25)',
'accent-gradient': 'linear-gradient(135deg, #d946ef, #fb7185)',
'header-bg': 'rgba(13, 6, 23, 0.95)',
},
},
{
slug: 'citrus',
label: 'Citrus',
light: {
'bg': '#fffdf5',
'bg-elevated': '#ffffff',
'sidebar-bg': '#ffffff',
'card-bg': '#fffaf0',
'input-bg': '#fff4d8',
'hover-bg': '#ffeec1',
'border': 'rgba(250, 204, 21, 0.25)',
'text': '#2b2509',
'text-muted': '#6d5f2a',
'accent': '#facc15',
'accent-dark': '#b45309',
'accent-glow': 'rgba(250, 204, 21, 0.2)',
'accent-gradient': 'linear-gradient(135deg, #facc15, #f97316)',
'header-bg': 'rgba(255, 255, 255, 0.98)',
},
dark: {
'bg': '#1a1203',
'bg-elevated': 'rgba(26, 18, 3, 0.9)',
'sidebar-bg': '#130e02',
'card-bg': 'rgba(26, 18, 3, 0.75)',
'input-bg': 'rgba(250, 204, 21, 0.08)',
'hover-bg': 'rgba(250, 204, 21, 0.14)',
'border': 'rgba(250, 204, 21, 0.35)',
'text': '#fff8e7',
'text-muted': '#f6dea1',
'accent': '#fbbf24',
'accent-dark': '#b45309',
'accent-glow': 'rgba(250, 204, 21, 0.25)',
'accent-gradient': 'linear-gradient(135deg, #fbbf24, #f97316)',
'header-bg': 'rgba(15, 9, 2, 0.9)',
},
},
];
let sidebarOpen = false;
let theme: ThemeMode = 'dark';
let selectedPaletteIndex = 0;
const updatePaletteStyles = (index: number, mode: ThemeMode) => {
if (!browser) return;
const palette = palettes[index];
const colors = palette[mode];
paletteVariableKeys.forEach(key => {
document.documentElement.style.setProperty(`--${key}`, colors[key]);
});
document.documentElement.dataset.palette = palette.slug;
};
const updateTheme = (value: ThemeMode, persist = false) => {
theme = value;
if (!browser) return;
document.documentElement.dataset.theme = value;
window.localStorage.setItem('theme', value);
updatePaletteStyles(selectedPaletteIndex, value);
if (persist) {
window.localStorage.setItem('theme', value);
}
};
const toggleTheme = () => {
applyTheme(theme === 'dark' ? 'light' : 'dark');
const nextTheme: ThemeMode = theme === 'dark' ? 'light' : 'dark';
updateTheme(nextTheme, true);
};
const setPalette = (index: number) => {
selectedPaletteIndex = index;
if (!browser) return;
window.localStorage.setItem('palette', palettes[index].slug);
updatePaletteStyles(index, theme);
};
onMount(() => {
if (!browser) return;
const savedTheme = window.localStorage.getItem('theme') as 'light' | 'dark' | null;
const initialTheme = savedTheme ?? (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light');
applyTheme(initialTheme);
const savedTheme = window.localStorage.getItem('theme') as ThemeMode | null;
const savedPalette = window.localStorage.getItem('palette');
const paletteIndex = palettes.findIndex(palette => palette.slug === savedPalette);
selectedPaletteIndex = paletteIndex >= 0 ? paletteIndex : 0;
const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
const initialTheme: ThemeMode = savedTheme ?? (mediaQuery.matches ? 'dark' : 'light');
updateTheme(initialTheme, Boolean(savedTheme));
const handlePreferenceChange = (event: MediaQueryListEvent) => {
if (window.localStorage.getItem('theme')) return;
applyTheme(event.matches ? 'dark' : 'light');
updateTheme(event.matches ? 'dark' : 'light');
};
const cleanup = () => {
@@ -67,15 +319,6 @@
</div>
<div class="header-right">
<SearchBar />
<button
type="button"
class="theme-toggle"
on:click={toggleTheme}
aria-label={`Switch to ${theme === 'dark' ? 'light' : 'dark'} mode`}
>
<span aria-hidden="true">{theme === 'dark' ? '☀️' : '🌙'}</span>
<span class="theme-toggle__label">{theme === 'dark' ? 'Light' : 'Dark'} mode</span>
</button>
</div>
</header>
@@ -86,6 +329,30 @@
</main>
</div>
<div class="floating-palette-controls" role="group" aria-label="Theme and palette controls">
<div class="palette-dots">
{#each palettes as palette, index}
<button
type="button"
class="palette-dot"
class:active={index === selectedPaletteIndex}
aria-pressed={index === selectedPaletteIndex}
aria-label={`Switch to ${palette.label} palette`}
style={`background: ${palette[theme].accentGradient};`}
on:click={() => setPalette(index)}
></button>
{/each}
</div>
<button
type="button"
class="theme-toggle"
on:click={toggleTheme}
aria-label={`Switch to ${theme === 'dark' ? 'light' : 'dark'} mode`}
>
<span aria-hidden="true">{theme === 'dark' ? '☀️' : '🌙'}</span>
</button>
</div>
<footer class="site-footer">
© {new Date().getFullYear()} HowDoYouConvert.com — Free unit conversion calculators. All rights reserved.
</footer>