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}
+
+
+
+