The convention uses a simple background and foreground colors. Take a look at the following CSS variables:
--z-primary: oklch(27.4% 0.006 286.033);
--z-primary-f: oklch(98.5% 0 0);The background color of the following component will be oklch(27.4% 0.006 286.033) and the foreground color will be oklch(98.5% 0 0).
<div
class="bg color"
style="--bg: var(--z-primary); --color: var(--z-primary-f);"
></div>Here’s the list of variables available for customization:
/* Background */
--z-bg: #fff;
--z-bg-f: oklch(21% 0.006 285.885);
/* Card */
--z-card: #fff;
--z-card-f: oklch(21% 0.006 285.885);
/* Popover/Dropdown */
--z-drop: #fff;
--z-drop-f: oklch(21% 0.006 285.885);
/* Primary */
--z-primary: oklch(27.4% 0.006 286.033);
--z-primary-f: oklch(98.5% 0 0);
/* Secondary */
--z-secondary: #000;
--z-secondary-o: 4%;
--z-secondary-f: oklch(21% 0.006 285.885);
/* Muted */
--z-muted: #000;
--z-muted-o: 4%;
--z-muted-f: oklch(44.2% 0.017 285.786);
/* Accent */
--z-accent: #000;
--z-accent-o: 4%;
--z-accent-f: oklch(21% 0.006 285.885);
/* Destructive/Danger */
--z-danger: oklch(63.7% 0.237 25.331);
--z-danger-f: oklch(50.5% 0.213 27.518);
/* Info */
--z-info: oklch(62.3% 0.214 259.815);
--z-info-f: oklch(48.8% 0.243 264.376);
/* Success */
--z-success: oklch(69.6% 0.17 162.48);
--z-success-f: oklch(50.8% 0.118 165.612);
/* Warning */
--z-warning: oklch(76.9% 0.188 70.08);
--z-warning-f: oklch(55.5% 0.163 48.998);
/* Border & Input */
--z-border: #000;
--z-border-o: 10%;
--z-input: #000;
--z-input-o: 10%;
/* Focus Ring */
--z-focus: oklch(70.5% 0.015 286.067);
/* Charts */
--z-chart-1: oklch(64.6% 0.222 41.116);
--z-chart-2: oklch(60% 0.118 184.704);
--z-chart-3: oklch(30.2% 0.056 229.695);
--z-chart-4: oklch(82.8% 0.189 84.429);
--z-chart-5: oklch(76.9% 0.188 70.08);To add new colors, simply add them to your main CSS file.
:root {
--z-custom: oklch(0.84 0.16 84);
--z-custom-f: oklch(0.28 0.07 46);
}
.dark {
--z-custom: oklch(0.41 0.11 46);
--z-custom-f: oklch(0.99 0.02 95);
}
@layer theme {
--color-custom: var(--z-custom);
--color-custom-f: var(--z-custom-f);
}You can now use it via utility class in your components.
<!-- Direct -->
<div
class="bg color"
style="--bg: var(--z-custom); --color: var(--z-custom-f);"
></div>
<!-- Proxy -->
<div
class="bg color"
style="--bg: var(--color-custom); --color: var(--color-custom-f);"
></div>1. Start by picking your desired color and use that to assign values to the --z-primary, --z-primary-f, and --z-focus tokens. These tokens represent your main theme color, its contrasting foreground color, and the focus color.
2. Use the CSS snippet below as your starting point and replace the * with your theme name (e.g. indigo, cyan, fuchsia, etc.). You only need to update the values for --z-primary, --z-primary-f, and --z-focus for both light and dark modes, but you’re free to customize everything else if needed.
.z-theme-emerald {
--z-primary: /* oklch(...) */;
--z-primary-f: /* oklch(...) */;
--z-focus: /* oklch(...) */;
}
.dark.z-theme-emerald {
--z-primary: /* oklch(...) */;
--z-primary-f: /* oklch(...) */;
--z-focus: /* oklch(...) */;
}To set your newly added palette as the default, simply update the script in your <head> tag to reference the new theme name, like so:
<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");
}
htmlElement.classList.add(__Z_THEME__.layout || "z-layout-small");
htmlElement.classList.add(__Z_THEME__.ff || "z-ff-a");
</script>Beyond colors, the API handles structural properties to maintain a consistent physical appearance across components.
So, instead of hardcoding static pixel values, components dynamically calculate their size. For example, a button’s height is determined by multiplying --z-global-font-size by the --z-interactive-height-multiplier. The button’s block padding and inline padding are calculated similarly using their respective multipliers. This keeps the entire system proportionally synced whenever you change the base font size.
Let’s look at a common, real-world example: readability.
In text-heavy interfaces—like story-sharing platforms, blogs, or reading apps—giving users control over readability is crucial. While users can rely on native browser zooming, this often leads to broken layouts, horizontal scrolling, or misaligned elements.
Because this framework uses multipliers mathematically synced to --z-global-font-size, your entire UI remains perfectly proportional. This setup allows you to easily implement a custom “Text Size” toggle directly in your application. When a user increases the text size, you only need to update that single base variable, and the rest seamlessly follows:
Readability is just the beginning; there are countless other use cases where dynamic scaling shines. Build on top of 0build’s API, and the framework will work for you, rather than you fighting against it.
You can adjust the roundness of your components globally using the following tokens:
| Variable | Default Value | Role |
|---|---|---|
--z-global-radius | 0.375rem | The primary global radius used for standard components. |
--z-global-radius-small | 0.125rem | A tighter radius intended for inner, smaller or compact elements. |
Depth and elevation are standardized using global shadow tokens:
| Variable | Default Value | Role |
|---|---|---|
--z-global-shadow | 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1) | The default, layered shadow for standard elevation. |
--z-global-shadow-small | 0 1px 2px 0 rgb(0 0 0 / 0.05) | A subtler shadow intended for flatter elevations. |
These multiplier variables act as a base scale shared by interactive components like buttons, forms, and pagination:
| Variable | Default Value | Role |
|---|---|---|
--z-interactive-height-multiplier | 2.5 | Scales the overall height of an interactive element. |
--z-interactive-padding-block-multiplier | 0.625 | Scales the top and bottom padding. |
--z-interactive-padding-inline-multiplier | 1.25 | Scales the left and right padding. |
We extensively use multipliers to control certain elements and keep them in sync, so you only need to update a few variables. However, you're free to leave them as they are and implement your own theme values.
To make applying these structural properties as seamless as possible, the framework provides utility classes that map directly to your global variables.
You can apply this globally to the <html> tag to instantly control layout density of your app.
| Class | CSS Variables Adjusted | Effect |
|---|---|---|
.z-layout-small | --z-global-font-size, --z-global-line-height | Scales the layout down to a compact size (base 0.875rem). |
.z-layout-medium | --z-global-font-size, --z-global-line-height | Scales the layout up for a more spacious, readable UI (base 1.125rem). |
Use these classes to quickly apply your predefined corner tokens to elements like cards, dialogs, or custom containers.
| Class | Maps To | Effect |
|---|---|---|
.z-rounded | --z-global-radius | Applies the standard default corner radius. |
.z-rounded-small | --z-global-radius-small | Applies the tighter, compact corner radius. |
These classes apply your global elevation tokens to create consistent depth across your application.
| Class | Maps To | Effect |
|---|---|---|
.z-shadow | --z-global-shadow | Applies the standard layered shadow for primary elevation. |
.z-shadow-small | --z-global-shadow-small | Applies a subtle, minimal shadow—ideal for flatter UI elements. |
The true power of this multiplier and variable system unlocks when you give control directly to your users. Please see the LSH component documentation for step-by-step instructions on how to build interactive, state-synced preference toggles—like layout density switchers, text scalers, and custom theme selectors—that automatically save to local storage and update your UI instantly.