424 lines
13 KiB
Svelte
424 lines
13 KiB
Svelte
<script lang="ts">
|
|
import { browser } from '$app/environment';
|
|
import { onMount } from 'svelte';
|
|
import { page } from '$app/stores';
|
|
|
|
import '../app.css';
|
|
import Sidebar from '$lib/components/Sidebar.svelte';
|
|
import SearchBar from '$lib/components/SearchBar.svelte';
|
|
|
|
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';
|
|
|
|
type PaletteTheme = Record<PaletteVar, string>;
|
|
|
|
type Palette = {
|
|
slug: string;
|
|
label: string;
|
|
light: PaletteTheme;
|
|
dark: PaletteTheme;
|
|
};
|
|
|
|
const palettes: Palette[] = [
|
|
{
|
|
slug: 'classic',
|
|
label: 'Classic',
|
|
light: {
|
|
bg: '#f8fafc',
|
|
'bg-elevated': '#ffffff',
|
|
'sidebar-bg': '#ffffff',
|
|
'card-bg': '#ffffff',
|
|
'input-bg': 'rgba(15, 23, 42, 0.04)',
|
|
'hover-bg': 'rgba(15, 23, 42, 0.08)',
|
|
border: 'rgba(15, 23, 42, 0.12)',
|
|
text: '#0f172a',
|
|
'text-muted': '#475569',
|
|
accent: '#10b981',
|
|
'accent-dark': '#059669',
|
|
'accent-glow': 'rgba(16, 185, 129, 0.15)',
|
|
'accent-gradient': 'linear-gradient(135deg, #10b981, #06b6d4)',
|
|
'header-bg': 'rgba(255, 255, 255, 0.95)',
|
|
},
|
|
dark: {
|
|
bg: '#0c0f14',
|
|
'bg-elevated': '#12161e',
|
|
'sidebar-bg': '#10141b',
|
|
'card-bg': 'rgba(18, 22, 30, 0.85)',
|
|
'input-bg': 'rgba(255, 255, 255, 0.04)',
|
|
'hover-bg': 'rgba(255, 255, 255, 0.06)',
|
|
border: 'rgba(255, 255, 255, 0.08)',
|
|
text: '#e8ecf4',
|
|
'text-muted': '#7b8498',
|
|
accent: '#10b981',
|
|
'accent-dark': '#059669',
|
|
'accent-glow': 'rgba(16, 185, 129, 0.15)',
|
|
'accent-gradient': 'linear-gradient(135deg, #10b981, #06b6d4)',
|
|
'header-bg': 'rgba(12, 15, 20, 0.85)',
|
|
},
|
|
},
|
|
{
|
|
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;
|
|
$: isHomepage = $page.url.pathname === '/';
|
|
$: if (isHomepage && sidebarOpen) {
|
|
sidebarOpen = false;
|
|
}
|
|
|
|
const applyPalette = (index: number, persist = false) => {
|
|
const normalizedIndex = Math.max(0, Math.min(index, palettes.length - 1));
|
|
selectedPaletteIndex = normalizedIndex;
|
|
if (!browser) return;
|
|
const slug = palettes[normalizedIndex].slug;
|
|
document.documentElement.dataset.palette = slug;
|
|
if (persist) {
|
|
window.localStorage.setItem('palette', slug);
|
|
}
|
|
};
|
|
|
|
const updateTheme = (value: ThemeMode, persist = false) => {
|
|
theme = value;
|
|
if (!browser) return;
|
|
document.documentElement.dataset.theme = value;
|
|
if (persist) {
|
|
window.localStorage.setItem('theme', value);
|
|
}
|
|
};
|
|
|
|
const toggleTheme = () => {
|
|
const nextTheme: ThemeMode = theme === 'dark' ? 'light' : 'dark';
|
|
updateTheme(nextTheme, true);
|
|
};
|
|
|
|
const setPalette = (index: number) => {
|
|
applyPalette(index, true);
|
|
};
|
|
|
|
onMount(() => {
|
|
if (!browser) return;
|
|
|
|
const savedTheme = window.localStorage.getItem('theme') as ThemeMode | null;
|
|
const savedPalette = window.localStorage.getItem('palette');
|
|
const paletteIndex = palettes.findIndex(palette => palette.slug === savedPalette);
|
|
const initialPaletteIndex = paletteIndex >= 0 ? paletteIndex : 0;
|
|
applyPalette(initialPaletteIndex, Boolean(savedPalette));
|
|
|
|
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;
|
|
updateTheme(event.matches ? 'dark' : 'light');
|
|
};
|
|
|
|
const navBreakpoint = window.matchMedia('(max-width: 1024px)');
|
|
const handleNavBreakpoint = (event: MediaQueryListEvent) => {
|
|
if (event.matches) {
|
|
sidebarOpen = false;
|
|
}
|
|
};
|
|
|
|
if (navBreakpoint.matches) {
|
|
sidebarOpen = false;
|
|
}
|
|
|
|
const cleanup = () => {
|
|
if ('removeEventListener' in mediaQuery) {
|
|
mediaQuery.removeEventListener('change', handlePreferenceChange);
|
|
} else {
|
|
mediaQuery.removeListener(handlePreferenceChange);
|
|
}
|
|
if ('removeEventListener' in navBreakpoint) {
|
|
navBreakpoint.removeEventListener('change', handleNavBreakpoint);
|
|
} else {
|
|
navBreakpoint.removeListener(handleNavBreakpoint);
|
|
}
|
|
};
|
|
|
|
if ('addEventListener' in mediaQuery) {
|
|
mediaQuery.addEventListener('change', handlePreferenceChange);
|
|
} else {
|
|
mediaQuery.addListener(handlePreferenceChange);
|
|
}
|
|
|
|
if ('addEventListener' in navBreakpoint) {
|
|
navBreakpoint.addEventListener('change', handleNavBreakpoint);
|
|
} else {
|
|
navBreakpoint.addListener(handleNavBreakpoint);
|
|
}
|
|
|
|
return cleanup;
|
|
});
|
|
</script>
|
|
|
|
<svelte:head>
|
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
<link rel="icon" href="/favicon.png" />
|
|
<!-- Matomo Tag Manager -->
|
|
<script>
|
|
var _mtm = window._mtm = window._mtm || [];
|
|
_mtm.push({'mtm.startTime': (new Date().getTime()), 'event': 'mtm.Start'});
|
|
(function() {
|
|
var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0];
|
|
g.async=true; g.src='https://matomo.howdoyouconvert.com/js/container_B3r877Kn.js'; s.parentNode.insertBefore(g,s);
|
|
})();
|
|
</script>
|
|
<!-- End Matomo Tag Manager -->
|
|
</svelte:head>
|
|
|
|
<header class="site-header">
|
|
<div style="display:flex;align-items:center;gap:0.75rem;">
|
|
{#if !isHomepage}
|
|
<button
|
|
type="button"
|
|
class="hamburger"
|
|
on:click={() => (sidebarOpen = !sidebarOpen)}
|
|
aria-label="Toggle menu"
|
|
aria-controls="site-navigation"
|
|
aria-expanded={sidebarOpen ? 'true' : 'false'}
|
|
>
|
|
☰
|
|
</button>
|
|
{/if}
|
|
<a href="/" class="site-logo">
|
|
<span>How Do You</span><span class="logo-accent">Convert</span><span style="opacity:0.4;font-weight:400">.com</span>
|
|
</a>
|
|
</div>
|
|
<div class="header-right">
|
|
<SearchBar />
|
|
</div>
|
|
</header>
|
|
|
|
<div class="site-body">
|
|
{#if !isHomepage}
|
|
<Sidebar bind:open={sidebarOpen} />
|
|
{/if}
|
|
<main class="main-content">
|
|
<slot />
|
|
</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-image: ${palette[theme]['accent-gradient']};`}
|
|
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>
|