From d450223f70f76a30474d19f82b9c0945df0f8759 Mon Sep 17 00:00:00 2001 From: Codex Date: Sat, 7 Mar 2026 08:47:17 +0000 Subject: [PATCH] Keep units styled and prevent zero reverse examples --- calculators_list.md | 120 ++++--- hdyc-svelte/src/app.css | 61 +++- .../components/QuickConversionExample.svelte | 29 +- .../components/QuickConversionTable.svelte | 3 +- hdyc-svelte/src/routes/+layout.svelte | 303 ++++++++++++++++-- 5 files changed, 444 insertions(+), 72 deletions(-) diff --git a/calculators_list.md b/calculators_list.md index e15cd63..946867c 100644 --- a/calculators_list.md +++ b/calculators_list.md @@ -226,6 +226,46 @@ Whenever a new calculator is published via the REST API, it must be appended her | Nanograms to picograms | 532 | 533 | nanograms-to-picograms | 1000 | | Metric tons to long tons | 520 | 521 | metric-tons-to-long-tons | 0.9842 | | Lumens to candela | 511 | 512 | lumens-to-candela | 0.079577 | +| GSM to Ounces per sq yard – Convert GSM to Ounces per sq yard instantly. | - | - | gsm-to-ounces-per-sq-yard | 0.02952 | +| Hands to Centimeters – Convert Hands to Centimeters instantly. | - | - | hands-to-centimeters | 10.16 | +| Hands to Inches – Convert Hands to Inches instantly. | - | - | hands-to-inches | 4 | +| Hartree to Electron-volts – Convert Hartree to Electron-volts instantly. | - | - | hartree-to-electron-volts | 27.2114 | +| Hectares to Ares – Convert Hectares to Ares instantly. | - | - | hectares-to-ares | 100 | +| Hectares to Square Feet – Convert Hectares to Square Feet instantly. | - | - | hectares-to-square-feet | 107639 | +| Hectares to Square Kilometers – Convert Hectares to Square Kilometers instantly. | - | - | hectares-to-square-kilometers | 0.01 | +| Hectares to Square Meters – Convert Hectares to Square Meters instantly. | - | - | hectares-to-square-meters | 10000 | +| Hectopascals to Atmosphere – Convert Hectopascals to Atmosphere instantly. | - | - | hectopascals-to-atmosphere | 0.000986923 | +| Hectopascals to Bar – Convert Hectopascals to Bar instantly. | - | - | hectopascals-to-bar | 0.001 | +| Hectopascals to PSI – Convert Hectopascals to PSI instantly. | - | - | hectopascals-to-psi | 0.0145038 | +| Henries to Millihenries – Convert Henries to Millihenries instantly. | - | - | henries-to-millihenries | 1000 | +| Hertz to Kilohertz – Convert Hertz to Kilohertz instantly. | - | - | hertz-to-kilohertz | 0.001 | +| Hertz to RPM – Convert Hertz to RPM instantly. | - | - | hertz-to-rpm | 60 | +| Hex to Octal – Convert Hex to Octal instantly. | - | - | hex-to-octal | Base 16 → 8 | +| Hogshead to Liters – Convert Hogshead (US) to Liters instantly. | - | - | hogshead-to-liters | 238.481 | +| Horsepower to BTU/hour – Convert Horsepower to BTU/hour instantly. | - | - | horsepower-to-btuhour | 2544.43 | +| Horsepower to Tons of Refrigeration – Convert Horsepower to Tons of Refrigeration instantly. | - | - | horsepower-to-tons-of-refrigeration | 0.284345 | +| Horsepower to Watts – Convert Horsepower to Watts instantly. | - | - | horsepower-to-watts | 745.7 | +| Hours to Seconds – Convert Hours to Seconds instantly. | - | - | hours-to-seconds | 3600 | +| Hours to Weeks – Convert Hours to Weeks instantly. | - | - | hours-to-weeks | 0.00595238 | +| Hundredweight (cwt) to Kilograms – Convert Hundredweight (cwt) to Kilograms instantly. | - | - | hundredweight-to-kilograms | 45.3592 | +| Imperial fl oz to US fl oz – Convert Imperial fl oz to US fl oz instantly. | - | - | imperial-fl-oz-to-us-fl-oz | 1.20095 | +| Imperial gallons to Liters – Convert Imperial gallons to Liters instantly. | - | - | imperial-gallons-to-liters | 4.54609 | +| Imperial gallons to US gallons – Convert Imperial gallons to US gallons instantly. | - | - | imperial-gallons-to-us-gallons | 1.20095 | +| Imperial pints to US pints – Convert Imperial pints to US pints instantly. | - | - | imperial-pints-to-us-pints | 1.20095 | +| Inch-Pounds to Foot-Pounds – Convert Inch-Pounds to Foot-Pounds instantly. | - | - | inch-pounds-to-foot-pounds | 0.0833333 | +| Inch-Pounds to Newton-Meters – Convert Inch-Pounds to Newton-Meters instantly. | - | - | inch-pounds-to-newton-meters | 0.112985 | +| Inches of Water to PSI – Convert Inches of Water to PSI instantly. | - | - | inches-of-water-to-psi | 0.0360912 | +| Inches per second to cm per second – Convert Inches per second to Centimeters per second instantly. | - | - | inches-per-second-to-cm-per-second | 2.54 | +| Inches to Meters – Convert Inches to Meters instantly. | - | - | inches-to-meters | 0.0254 | +| Inches to Points – Convert Inches to Points instantly. | - | - | inches-to-points | 72 | +| Inches to Yards – Convert Inches to Yards instantly. | - | - | inches-to-yards | 0.0277778 | +| Jansky to Watts per sq meter per Hertz – Convert Jansky to Watts per square meter per Hertz instantly. | - | - | jansky-to-watts-per-square-meter-per-hertz | 1e-26 | +| Jiffies to Seconds – Convert Jiffies to Seconds instantly. | - | - | jiffies-to-seconds | 0.01 | +| Jiggers to Milliliters – Convert Jiggers to Milliliters instantly. | - | - | jiggers-to-milliliters | 44.3603 | +| Joules to BTU – Convert Joules to BTU instantly. | - | - | joules-to-btu | 0.000947817 | +| Joules to Electron-volts – Convert Joules to Electron-volts instantly. | - | - | joules-to-electron-volts | 6.24151e+18 | +| Joules to Foot-pounds – Convert Joules to Foot-pounds instantly. | - | - | joules-to-foot-pounds | 0.737562 | +| Joules to Kilowatt-hours – Convert Joules to Kilowatt-hours instantly. | - | - | joules-to-kilowatt-hours | 2.77778e-07 | ## Backlog / To-Do - [x] Acres to hectares @@ -485,46 +525,46 @@ Whenever a new calculator is published via the REST API, it must be appended her - [ ] Gray to Sievert (1 gray of gamma/beta exposure = 1 sievert) #### Additional 40 ready conversions (no additional input needed) -- [ ] GSM to Ounces per sq yard (1 gsm ≈ 0.02952 ounces per square yard) -- [ ] Hands to Centimeters (1 hand = 10.16 centimeters) -- [ ] Hands to Inches (1 hand = 4 inches) -- [ ] Hartree to Electron-volts (1 Hartree ≈ 27.2114 electron-volts) -- [ ] Hectares to Ares (1 hectare = 100 ares) -- [ ] Hectares to Square Feet (1 hectare ≈ 107639.104 square feet) -- [ ] Hectares to Square Kilometers (1 hectare = 0.01 square kilometers) -- [ ] Hectares to Square Meters (1 hectare = 10,000 square meters) -- [ ] Hectopascals to Atmosphere (1 hPa ≈ 0.000986923 atmosphere) -- [ ] Hectopascals to Bar (1 hPa = 0.001 bar) -- [ ] Hectopascals to PSI (1 hPa ≈ 0.0145038 psi) -- [ ] Henries to Millihenries (1 henry = 1,000 millihenries) -- [ ] Hertz to Kilohertz (1 hertz = 0.001 kilohertz) -- [ ] Hertz to RPM (1 hertz = 60 RPM) -- [ ] Hex to Octal (base-16 digits convert directly to base-8 representation) -- [ ] Hogshead to Liters (1 US hogshead ≈ 238.481 liters) -- [ ] Horsepower to BTU/hour (1 horsepower ≈ 2,544.43 BTU/hour) -- [ ] Horsepower to Tons of Refrigeration (1 horsepower ≈ 0.284345 tons of refrigeration) -- [ ] Horsepower to Watts (1 horsepower ≈ 745.7 watts) -- [ ] Hours to Seconds (1 hour = 3,600 seconds) -- [ ] Hours to Weeks (1 hour ≈ 0.00595238 weeks) -- [ ] Hundredweight (cwt) to Kilograms (1 US hundredweight ≈ 45.3592 kilograms) -- [ ] Imperial fl oz to US fl oz (1 Imperial fluid ounce ≈ 1.20095 US fluid ounces) -- [ ] Imperial gallons to Liters (1 Imperial gallon = 4.54609 liters) -- [ ] Imperial gallons to US gallons (1 Imperial gallon ≈ 1.20095 US gallons) -- [ ] Imperial pints to US pints (1 Imperial pint ≈ 1.20095 US pints) -- [ ] Inch-Pounds to Foot-Pounds (1 inch-pound = 0.0833333 foot-pounds) -- [ ] Inch-Pounds to Newton-Meters (1 inch-pound ≈ 0.113 newton-meters) -- [ ] Inches of Water to PSI (1 inch of water ≈ 0.0360912 psi) -- [ ] Inches per second to cm per second (1 inch/second = 2.54 centimeters/second) -- [ ] Inches to Meters (1 inch = 0.0254 meters) -- [ ] Inches to Points (1 inch = 72 points) -- [ ] Inches to Yards (1 inch ≈ 0.0277778 yards) -- [ ] Jansky to Watts per sq meter per Hertz (1 jansky = 1e-26 W·m⁻²·Hz⁻¹) -- [ ] Jiffies to Seconds (1 jiffy (UNIX) = 0.01 seconds) -- [ ] Jiggers to Milliliters (1 jigger = 44.3603 milliliters) -- [ ] Joules to BTU (1 joule ≈ 0.000947817 BTU) -- [ ] Joules to Electron-volts (1 joule ≈ 6.241509e18 electron-volts) -- [ ] Joules to Foot-pounds (energy) (1 joule ≈ 0.737562 foot-pounds) -- [ ] Joules to Kilowatt-hours (1 joule ≈ 2.77778e-7 kilowatt-hours) +- [x] GSM to Ounces per sq yard (1 gsm ≈ 0.02952 ounces per square yard) +- [x] Hands to Centimeters (1 hand = 10.16 centimeters) +- [x] Hands to Inches (1 hand = 4 inches) +- [x] Hartree to Electron-volts (1 Hartree ≈ 27.2114 electron-volts) +- [x] Hectares to Ares (1 hectare = 100 ares) +- [x] Hectares to Square Feet (1 hectare ≈ 107639.104 square feet) +- [x] Hectares to Square Kilometers (1 hectare = 0.01 square kilometers) +- [x] Hectares to Square Meters (1 hectare = 10,000 square meters) +- [x] Hectopascals to Atmosphere (1 hPa ≈ 0.000986923 atmosphere) +- [x] Hectopascals to Bar (1 hPa = 0.001 bar) +- [x] Hectopascals to PSI (1 hPa ≈ 0.0145038 psi) +- [x] Henries to Millihenries (1 henry = 1,000 millihenries) +- [x] Hertz to Kilohertz (1 hertz = 0.001 kilohertz) +- [x] Hertz to RPM (1 hertz = 60 RPM) +- [x] Hex to Octal (base-16 digits convert directly to base-8 representation) +- [x] Hogshead to Liters (1 US hogshead ≈ 238.481 liters) +- [x] Horsepower to BTU/hour (1 horsepower ≈ 2,544.43 BTU/hour) +- [x] Horsepower to Tons of Refrigeration (1 horsepower ≈ 0.284345 tons of refrigeration) +- [x] Horsepower to Watts (1 horsepower ≈ 745.7 watts) +- [x] Hours to Seconds (1 hour = 3,600 seconds) +- [x] Hours to Weeks (1 hour ≈ 0.00595238 weeks) +- [x] Hundredweight (cwt) to Kilograms (1 US hundredweight ≈ 45.3592 kilograms) +- [x] Imperial fl oz to US fl oz (1 Imperial fluid ounce ≈ 1.20095 US fluid ounces) +- [x] Imperial gallons to Liters (1 Imperial gallon = 4.54609 liters) +- [x] Imperial gallons to US gallons (1 Imperial gallon ≈ 1.20095 US gallons) +- [x] Imperial pints to US pints (1 Imperial pint ≈ 1.20095 US pints) +- [x] Inch-Pounds to Foot-Pounds (1 inch-pound = 0.0833333 foot-pounds) +- [x] Inch-Pounds to Newton-Meters (1 inch-pound ≈ 0.113 newton-meters) +- [x] Inches of Water to PSI (1 inch of water ≈ 0.0360912 psi) +- [x] Inches per second to cm per second (1 inch/second = 2.54 centimeters/second) +- [x] Inches to Meters (1 inch = 0.0254 meters) +- [x] Inches to Points (1 inch = 72 points) +- [x] Inches to Yards (1 inch ≈ 0.0277778 yards) +- [x] Jansky to Watts per sq meter per Hertz (1 jansky = 1e-26 W·m⁻²·Hz⁻¹) +- [x] Jiffies to Seconds (1 jiffy (UNIX) = 0.01 seconds) +- [x] Jiggers to Milliliters (1 jigger = 44.3603 milliliters) +- [x] Joules to BTU (1 joule ≈ 0.000947817 BTU) +- [x] Joules to Electron-volts (1 joule ≈ 6.241509e18 electron-volts) +- [x] Joules to Foot-pounds (energy) (1 joule ≈ 0.737562 foot-pounds) +- [x] Joules to Kilowatt-hours (1 joule ≈ 2.77778e-7 kilowatt-hours) #### Remaining backlog items - [x] Abamperes to Amperes diff --git a/hdyc-svelte/src/app.css b/hdyc-svelte/src/app.css index 23cc021..57efcb8 100644 --- a/hdyc-svelte/src/app.css +++ b/hdyc-svelte/src/app.css @@ -113,8 +113,8 @@ a:hover { .theme-toggle { display: inline-flex; align-items: center; - gap: 0.35rem; - padding: 0.35rem 0.9rem; + justify-content: center; + padding: 0.35rem 0.7rem; border-radius: 999px; border: 1px solid var(--border); background: var(--input-bg); @@ -125,6 +125,7 @@ a:hover { line-height: 1; cursor: pointer; transition: border-color 0.2s, background 0.2s, color 0.2s, box-shadow 0.2s; + min-width: 42px; } .theme-toggle:hover { border-color: var(--accent); @@ -133,17 +134,59 @@ a:hover { outline: none; box-shadow: 0 0 0 3px var(--accent-glow); } -.theme-toggle__label { - font-size: 0.78rem; - letter-spacing: 0.02em; + +.floating-palette-controls { + position: fixed; + bottom: clamp(1rem, 2vw, 1.5rem); + right: clamp(1rem, 2vw, 1.5rem); + display: flex; + align-items: center; + gap: 0.4rem; + padding: 0.35rem 0.65rem; + border-radius: 999px; + background: var(--bg-elevated); + border: 1px solid var(--border); + box-shadow: 0 18px 40px rgba(0, 0, 0, 0.35); + backdrop-filter: blur(18px); + -webkit-backdrop-filter: blur(18px); + z-index: 200; +} +.palette-dots { + display: flex; + gap: 0.35rem; +} +.palette-dot { + width: 34px; + height: 34px; + border-radius: 50%; + border: 2px solid transparent; + padding: 0; + background-size: cover; + background-position: center; + cursor: pointer; + transition: transform 0.2s, border-color 0.2s, box-shadow 0.2s; +} +.palette-dot:hover { + transform: translateY(-1px); +} +.palette-dot.active, +.palette-dot:focus-visible { + border-color: var(--accent); + box-shadow: 0 0 0 3px var(--accent-glow); +} +.palette-dot:focus-visible { + outline: none; } @media (max-width: 520px) { - .theme-toggle__label { - display: none; + .floating-palette-controls { + gap: 0.25rem; + padding: 0.25rem 0.55rem; + right: 0.75rem; } - .theme-toggle { - padding-inline: 0.75rem; + .palette-dot { + width: 30px; + height: 30px; } } diff --git a/hdyc-svelte/src/lib/components/QuickConversionExample.svelte b/hdyc-svelte/src/lib/components/QuickConversionExample.svelte index 18d6923..3592d77 100644 --- a/hdyc-svelte/src/lib/components/QuickConversionExample.svelte +++ b/hdyc-svelte/src/lib/components/QuickConversionExample.svelte @@ -15,13 +15,34 @@ $: result = supportsExample ? solve(config, 1, exampleInput.toString(), '', '') : null; - $: reverseResult = supportsExample - ? solve(config, 2, '', '1', '') - : null; $: offset = config.offset ?? 0; $: formulaExpression = supportsExample ? `${exampleInput} × ${config.factor}${offset ? ` + ${offset}` : ''}` : ''; + + const formatExampleValue = (value: number | null): string => { + if (value === null || Number.isNaN(value)) { + return '—'; + } + if (!Number.isFinite(value)) { + return value.toString(); + } + if (value === 0) { + return '0'; + } + const rounded = parseFloat(value.toFixed(6)); + if (rounded !== 0) { + return rounded.toString(); + } + const precise = value.toFixed(12).replace(/\.?0+$/, ''); + return precise || '0'; + }; + + $: reverseExampleValue = + supportsExample && config.factor !== 0 + ? (1 - offset) / config.factor + : null; + $: formattedReverseValue = formatExampleValue(reverseExampleValue); {#if supportsExample && result} @@ -31,7 +52,7 @@ 1 {config.labels.in1} = {config.factor}{config.offset ? ` + ${config.offset}` : ''} {config.labels.in2}

- 1 {config.labels.in2} = {reverseResult?.val1 ?? '—'} {config.labels.in1} + 1 {config.labels.in2} = {formattedReverseValue} {config.labels.in1}

Example: convert {exampleInput} {config.labels.in1} to {config.labels.in2} diff --git a/hdyc-svelte/src/lib/components/QuickConversionTable.svelte b/hdyc-svelte/src/lib/components/QuickConversionTable.svelte index 18c04e0..b2d0dfe 100644 --- a/hdyc-svelte/src/lib/components/QuickConversionTable.svelte +++ b/hdyc-svelte/src/lib/components/QuickConversionTable.svelte @@ -41,7 +41,7 @@ {#each rows as row}

- Converting {row.input} {inputLabel} into {outputLabel} equals + Converting {row.input} {inputLabel} into {outputLabel} equals {row.output} {outputLabel}.

@@ -94,6 +94,7 @@ color: var(--text); } + .chart-unit, .chart-output-unit { font-variant: petite-caps; } diff --git a/hdyc-svelte/src/routes/+layout.svelte b/hdyc-svelte/src/routes/+layout.svelte index 7fd0860..51988c6 100644 --- a/hdyc-svelte/src/routes/+layout.svelte +++ b/hdyc-svelte/src/routes/+layout.svelte @@ -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; + + 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 @@
-
@@ -86,6 +329,30 @@ +
+
+ {#each palettes as palette, index} + + {/each} +
+ +
+