Documentation.
Reference for designers, developers, and anyone using the platform.
Getting your package
Your generated npm package is available as soon as you publish a release. Find the install command in Settings under the Developer tab. The package name is the one you configured there.
pnpm add @your-org/ui # or npm install / yarn add@your-org/ui with the name shown there.Versioning and updates
Each time the design system owner publishes a release, a new version of the package is produced with a semantic version number (a patch, minor, or major bump chosen at publish time). You consume updates exactly like any other dependency: change the version in your package.json and reinstall.
pnpm add @your-org/ui@latest # or pin an exact version, e.g. @your-org/[email protected]Every version is a frozen snapshot, so a given version number always resolves to the same tokens, components, and styles. Nothing changes underneath you until you choose to move to a newer version, and the platform keeps a full release history with a change summary per version.
If registry publishing is not enabled for a project, each release still produces a downloadable build (a standard package tarball) that you can install from a local path or vendor into your repository. The package contents are identical either way.
Provider setup
Wrap the root of your application with the provider. It handles all the CSS for you; every token is injected and resolved automatically. Anything rendered inside the provider can use your design system tokens directly. On web, import the fonts CSS once at the app root so the @font-face rules apply.
// Web only: import once at the app root
import "@your-org/ui/fonts.css";
import { ZakladProvider } from "@your-org/ui";
function App() {
return (
<ZakladProvider>
{/* your app */}
</ZakladProvider>
);
}The provider works with no props; it uses your default theme automatically. To set or change the active theme, pass a theme prop using the exported Themes enum, whose members are your theme slugs (for example Themes.dark). The enum contains exactly the themes you have configured, so values are checked at compile time.
AcmeProvider. The exact name, all available theme values, and a ready-to-copy setup snippet are generated in the README included with every npm release.Fonts
How fonts are delivered depends on your tier.
Paid tiers (CDN, optional)
CDN font delivery is included on all paid tiers but is optional. When enabled, the fonts.css file references hosted URLs and no font bytes are bundled. If you prefer to self-host (for example, to satisfy strict content security policies or avoid the external dependency), you can opt out and the fonts will be bundled instead.
Free tier (self-hosted)
Font files are bundled directly into the package. The fonts.css rules reference the bundled files, so you host the fonts yourself. The import is identical in both cases; the difference is invisible to your code.
On Expo (managed or bare with Expo modules), no font setup is needed. The provider calls loadFonts() on mount and gates render on the result, so text components fall back to system fonts until loading completes. TTF files are always bundled under fonts/ and registered via expo-font automatically.
Using components
Components use namespaced variants so your IDE gives you the full set of valid values with autocompletion. The component itself doubles as the namespace, so you can reference variants directly on the import.
import { Button } from "@your-org/ui";
function Example() {
return (
<>
{/* Shorthand: variant name directly on the component */}
<Button.Primary onClick={() => save()}>
Save changes
</Button.Primary>
{/* Or via the VARIANT enum for dynamic usage */}
<Button
variant={Button.VARIANT.Secondary}
disabled
onClick={() => cancel()}
>
Cancel
</Button>
</>
);
}Each component is its own namespace. Sub-components, variant enums, and prop types are all attached directly to the component you import. One import gives you access to the whole component surface with no hunting through sub-packages.
import { Button } from "@your-org/ui";
// Sub-components
Button.Primary
Button.Secondary
// Variant enum for dynamic usage
Button.VARIANT.Secondary
// TypeScript types
Button.PropsUsing tokens directly
All token objects are exported from a single tokens export. Destructure what you need and apply values directly to any React element via inline styles.
import { tokens } from "@your-org/ui";
const { color, spacing, foundation } = tokens;
function Card() {
return (
<div style={{
color: color.text.default,
padding: spacing.padding.default,
borderColor: color.border.default,
}}>
Custom element
</div>
);
}color and spacing are semantic tokens: CSS variables that update automatically when the active theme changes. foundation gives you the raw palette values, which are theme-invariant. All are fully typed; your IDE will suggest paths and show the resolved default-theme value for each token as a hint.
Because semantic tokens resolve to live CSS variables, elements that use color or spacing must render inside the provider. foundation values are plain literals, so they work anywhere.
Switching themes
Every theme you have configured is exported as a member of the Themes enum, keyed by its slug (lowercase, for example Themes.dark or Themes["high-contrast"]). Your IDE will suggest the available values and flag invalid ones at compile time; the enum updates with every release as you add, rename, or remove themes.
import { ZakladProvider, Themes } from "@your-org/ui";
// Pass a theme at the root — everything inside inherits it
<ZakladProvider theme={Themes.dark}>
{/* ... */}
</ZakladProvider>
// Or switch at runtime — store the active theme in state
const [activeTheme, setActiveTheme] = useState(Themes.light);
<ZakladProvider theme={activeTheme}>
<button onClick={() => setActiveTheme(Themes.dark)}>
Switch to dark
</button>
</ZakladProvider>The provider defaults to the theme you marked as default in the Themes editor. Passing no theme prop is the same as passing the default explicitly.
To render a region in a different theme from the rest of the app — an inverted box, a dark hero, a preview card — wrap it in the Theme component (singular) and pass the same Themes enum to its name prop. Everything inside resolves to that theme, no matter the active app theme.
import { ZakladProvider, Theme, Themes } from "@your-org/ui";
<ZakladProvider theme={Themes.light}>
{/* this region is always dark, even though the app is light */}
<Theme name={Themes.dark}>
<Hero />
</Theme>
</ZakladProvider>Text styles
Text styles are exported as a namespaced Text component. Using <Text> with no variant defaults to <Text.BodyDefault>. All text components are automatically responsive: font size, line height, and spacing step up or down with the active XS–XL tier unless you override it.
import { Text } from "@your-org/ui";
// <Text> with no variant is shorthand for <Text.BodyDefault>
<Text>The quick brown fox jumps over the lazy dog.</Text>
<Text.BodyDefault>The quick brown fox jumps over the lazy dog.</Text.BodyDefault>
<Text.BodySmall>The quick brown fox jumps over the lazy dog.</Text.BodySmall>
<Text.CaptionDefault>Helper caption</Text.CaptionDefault>
<Text.CaptionSmall>Helper caption</Text.CaptionSmall>The available names match the text styles you have configured in the Typography editor. Every name is a property on the Text component, fully typed, so your IDE will suggest them.
Icons
Icons live in the /icons subpath. Import the whole set as Icon and reference each one by name. Only the icons you actually use are bundled.
import * as Icon from "@your-org/ui/icons";
// Render an icon
<Icon.ArrowRight />
// Choose a variant and size
<Icon.ChevronRight variant={Icon.VARIANT.Solid} size={Icon.SIZE.Large} />
// Pass one to a component slot
<Button.Primary leadingIcon={Icon.ChevronRight.Solid}>Continue</Button.Primary>Each icon takes a variant (when it has more than one), a size (an Icon.SIZE name or a raw pixel number), and an optional title that makes it readable to screen readers.
// Smallest bundle: import only the icons you use
import { ArrowRight } from "@your-org/ui/icons";
// Data-driven names (e.g. from a CMS): use DynamicIcon
import { DynamicIcon } from "@your-org/ui/icons";
<DynamicIcon name={iconName} size={Icon.SIZE.Medium} />DynamicIcon opts into the full icon set, so it does not tree-shake. Reach for it only when the name is not known ahead of time. Passing true to a component's icon prop still uses whichever default was configured for that slot in the Components editor.
Breakpoints
The provider drives tier-aware component sizing through five default viewport breakpoints:
| Tier | Viewport width | Typical target |
|---|---|---|
| XS | < 768 px | Mobile |
| S | 768 – 1032 px | Large mobile / small tablet |
| M | 1033 – 1439 px | Tablet / small desktop |
| L | 1440 – 1919 px | Desktop |
| XL | >= 1920 px | Large desktop / 4K |
To override the defaults, pass a media prop to the provider:
import { ZakladProvider, type ZakladMediaConfig } from "@your-org/ui";
const media: ZakladMediaConfig = {
xs: { maxWidth: 639 },
s: { minWidth: 640, maxWidth: 1023 },
m: { minWidth: 1024, maxWidth: 1279 },
l: { minWidth: 1280, maxWidth: 1535 },
xl: { minWidth: 1536 },
};
<ZakladProvider media={media}>…</ZakladProvider>Cross-platform
The package is built platform-neutral from the ground up. Components are authored against a shared API with platform-specific implementations behind it: web components render HTML with CSS custom properties, and React Native components use StyleSheet values resolved from the same token set. One token vocabulary drives both, with no separate imports or conditional platform code in your app.
// One import, same component API — on web and React Native
import { Button } from "@your-org/ui";Web is fully supported through the package's browser and neutral builds (React SPAs and SSR alike). React Native and Expo are now supported too: the package ships a dedicated native build and a react-native export condition, so Metro resolves the native bundle automatically. There is no per-platform import and no conditional code in your app.
On a React Native or Expo project, install the two native peers the package leaves to you (your app already has react-native):
# Expo
npx expo install react-native-svg expo-font
# Bare React Native
npm install react-native-svg expo-fontThen wrap your app in the generated provider exactly as on web. On native the provider calls loadFonts() on mount and gates render on the result, so your fonts register through expo-font automatically (text falls back to system fonts until loading completes). The web bundle is untouched and behaves exactly as before.
import { AcmeProvider } from "@your-org/ui";
export default function App() {
return (
<AcmeProvider>
{/* your screens */}
</AcmeProvider>
);
}Exporting your tokens
The whole token set can be exported as DTCG JSON (the Design Tokens Community Group format, an open standard). Export the current live state at any time, or the exact frozen tokens of any published release.
There are two reasons it exists. The first is flexibility: if you already have your own build setup and would rather transform the raw tokens yourself, the data is right there in a portable, standard shape.
The second is the important one, and it is deliberate. It is a clean exit. If you ever want to take your system and run it entirely on your own infrastructure, your tokens are yours and they leave in an open format. The platform is built to help you create and maintain a design system, not to trap you in one. For most teams the generated package, the Figma library, and the live MCP server are a better path than wiring all of that up by hand, but if you want out, the door is open.