Persist theme and style preferences to localStorage and sync with document classes.
The LSH (Local Storage Head) component provides an easy way to manage theme switching and style preferences. It stores selections in localStorage under the key __Z_THEME__ and applies them as classes to the <html> element. Multiple instances automatically stay in sync.
To implement LSH, you’ll need to make some modifications to your HTML code to allow users to change their preferences.
1. Start by setting a default theme and mode in the <head> tag of your HTML by checking the user’s preference:
<script>
const htmlElement = document.documentElement;
const __Z_THEME__ = JSON.parse(localStorage.getItem("__Z_THEME__") || "{}");
if (
__Z_THEME__.mode === "dark" ||
(!__Z_THEME__.mode &&
window.matchMedia("(prefers-color-scheme: dark)").matches)
) {
htmlElement.classList.add("dark");
} else {
htmlElement.classList.remove("dark");
}
/* below is just an example, feel free to implement your own */
htmlElement.classList.add(__Z_THEME__.layout || "z-layout-small");
htmlElement.classList.add(__Z_THEME__.ff || "z-ff-a");
</script>This will first check if a user previously set the theme color preference manually using localStorage, and as a fallback, it will either set dark or light mode based on the browser’s color scheme preference.
Note You can replace
z-[group]-[value] with anything you want as the default.
2. Ensure that your <body> tag includes the classes bg and color to apply the proper background and text colors that automatically adapt to the currently set theme.
<!doctype html>
<html lang="en">
<head>
<!-- ... -->
</head>
<body class="bg color" style="--bg: var(--z-bg); --color: var(--z-bg-f)">
<!-- ... -->
</body>
</html>3. You can now use any instance of <z-lsh> component anywhere in your HTML.
Enable toggle mode to create a button that switches between light and dark modes:
When toggle is enabled with group="mode", the button automatically switches between light and dark modes, ignoring the value attribute.
The component supports multiple configuration groups beyond just light/dark mode:
The mode group has special handling:
value="light" removes the dark class from <html>value="dark" adds the dark class to <html><z-lsh group="mode" value="light">
<template data-fn="template">
<!-- -->
</template>
</z-lsh>
<z-lsh group="mode" value="dark">
<template data-fn="template">
<!-- -->
</template>
</z-lsh>The <z-lsh> component extends far beyond just toggling light and dark modes. By defining custom groups, you can allow users to control almost any global UI preference—such as layout density, corner styles, and many others.
When a user switches options within a custom group, the component automatically updates the <html> element. Crucially, it intelligently removes the previous class that shares the same prefix before applying the new one, preventing conflicting classes from piling up.
Because your components rely on base multipliers, creating a “density” toggle is incredibly simple.
1. Define your modifier classes
First, create CSS classes that override your default padding multipliers.
@layer theme {
.z-density-compact {
--z-interactive-padding-block-multiplier: 0.35;
--z-interactive-padding-inline-multiplier: 0.75;
}
.z-density-comfortable {
/* Assumes your default root multipliers are used here */
}
.z-density-spacious {
--z-interactive-padding-block-multiplier: 1;
--z-interactive-padding-inline-multiplier: 1.75;
}
}2. Implement the LSH toggles
Next, assign these values to <z-lsh> components using group="density". Placing these anywhere in your app will automatically sync their active states.
<z-lsh group="density" value="z-density-compact">
<template data-fn="template">
<!-- -->
</template>
</z-lsh>
<z-lsh group="density" value="z-density-comfortable">
<template data-fn="template">
<!-- -->
</template>
</z-lsh>
<z-lsh group="density" value="z-density-spacious">
<template data-fn="template">
<!-- -->
</template>
</z-lsh>You can implement as many groups as your application requires. For instance, if you want to let users choose between sharp, rounded, or pill-shaped UI elements, simply create a new group.
Class names are applied directly to the <html> element, meaning your entire application responds instantly:
<!-- Themes -->
<z-lsh group="theme" value="z-theme-blue">
<template data-fn="template">
<!-- -->
</template>
</z-lsh>
<z-lsh group="theme" value="z-theme-red">
<template data-fn="template">
<!-- -->
</template>
</z-lsh>
<z-lsh group="theme" value="z-theme-green">
<template data-fn="template">
<!-- -->
</template>
</z-lsh>
<!-- Font sizes -->
<z-lsh group="font" value="z-font-small">
<template data-fn="template">
<!-- -->
</template>
</z-lsh>
<z-lsh group="font" value="z-font-medium">
<template data-fn="template">
<!-- -->
</template>
</z-lsh>
<z-lsh group="font" value="z-font-large">
<template data-fn="template">
<!-- -->
</template>
</z-lsh>
<!-- Radii -->
<z-lsh group="corners" value="z-radius-sharp">
<template data-fn="template">
<!-- -->
</template>
</z-lsh>
<z-lsh group="corners" value="z-radius-rounded">
<template data-fn="template">
<!-- -->
</template>
</z-lsh>
<z-lsh group="corners" value="z-radius-pill">
<template data-fn="template">
<!-- -->
</template>
</z-lsh>The automatic cleanup relies on the class prefix. When switching from z-radius-sharp to z-radius-rounded, the component sees the shared z-radius- prefix and smoothly removes the old class before attaching the new one.
By default, all instances sync when one changes. Use prevent-autoupdate to create standalone buttons:
<z-lsh group="mode" value="dark" prevent-autoupdate>
<template data-fn="template">
<!-- -->
</template>
</z-lsh>Listen to events to respond to theme changes:
<z-lsh id="theme-toggle" toggle group="mode">
<template data-fn="template">
<!-- -->
</template>
</z-lsh>
<script>
const toggle = document.getElementById("theme-toggle");
// Before change (cancelable)
toggle.addEventListener("z-lsh:before-change", (e) => {
console.log("About to change:", e.detail);
// e.preventDefault(); // Cancel the change
});
// After change
toggle.addEventListener("z-lsh:change", (e) => {
console.log("Changed:", e.detail);
// e.detail.group - The configuration group
// e.detail.value - The new value
// e.detail.previousValue - The old value
// e.detail.config - Complete configuration object
});
</script>The component stores configuration in localStorage under the key __Z_THEME__:
{
"mode": "dark",
"color": "z-theme-blue",
"font": "z-font-medium"
}The LSH component supports internationalization through multiple methods with the following priority order (highest to lowest):
i18n attribute or script tag)<script id="z-i18n">)<z-lsh
toggle
group="mode"
i18n='{"aria-label": "Cambiar tema", "switch-to-dark": "Cambiar a modo oscuro", "switch-to-light": "Cambiar a modo claro"}'
>
</z-lsh><z-lsh toggle group="mode">
<script type="application/json" data-fn="config">
{
"i18n": {
"aria-label": "Thème bascule",
"switch-to-dark": "Passer en mode sombre",
"switch-to-light": "Passer en mode clair"
}
}
</script>
<template data-fn="template">🌓</template>
</z-lsh>| Key | Default | Description |
|---|---|---|
aria-label | Toggle theme | ARIA label for the button |
active-label | Active | Label appended to ARIA when button is active |
switch-to-dark | Switch to dark mode | ARIA description when switching to dark (toggle mode) |
switch-to-light | Switch to light mode | ARIA description when switching to light (toggle mode) |
The LSH component supports custom CSS classes through the cls attribute.
<z-lsh cls="my-theme-button" toggle group="mode">
<template data-fn="template">
<!-- -->
</template>
</z-lsh><z-lsh cls='{"button": "z-button z-button-primary z-lsh"}' toggle group="mode">
<template data-fn="template">
<!-- -->
</template>
</z-lsh>| Target | Description |
|---|---|
button | The button element |
The LSH component supports custom inline styles through the stl attribute.
<z-lsh
stl='{"button": "padding: 0.5rem 1rem; border-radius: 8px;"}'
toggle
group="mode"
>
<template data-fn="template">
<!-- -->
</template>
</z-lsh>| Name | Type | Default | Description |
|---|---|---|---|
value | String | The specific theme value to apply (e.g., “dark”, “z-theme-blue”) | |
group | String | Configuration group (e.g., “mode”, “color”, “font”) | |
toggle | Boolean | false | Enables toggle behavior for mode switching (ignores value attribute) |
prevent-autoupdate | Boolean | false | Prevents this instance from syncing with other instances |
cls | String | Custom CSS classes for the button | |
stl | String | Custom inline styles for the button | |
i18n | String | Internationalization strings as JSON object or via configuration script | |
force-prevent-rerender | Boolean | false | Prevents component rerendering (useful for HTMX or SPA scenarios) |
| Name | Description |
|---|---|
z-lsh:before-change | Fired before theme change (cancelable). Event detail contains {group, value, previousValue, config}. |
z-lsh:change | Fired after theme change. Event detail contains {group, value, previousValue, config}. |