@hex-core/registry
Zod schemas + TypeScript types for components, themes, and recipes.
Install
0.3.1Runtime dependencypnpm add @hex-core/registryWhat it does
`@hex-core/registry` is the schema package — Zod schemas plus the TypeScript types they generate. It defines the shape every other hex-core package speaks: `Theme`, `TokenValue`, `RegistryItem`, `Recipe`, the strict variants of each.
Most consumers never `import` from this package directly; they use it transitively through `@hex-core/components`, `@hex-core/tokens`, `@hex-core/themes`, or `@hex-core/payload`. You install it directly when you need to type a function parameter as `Theme`, validate user input against `strictThemeSchema`, or build a tool that produces registry items.
The 0.2.0 release tightened `Theme.tokens.{light,dark}` from `Record<string, unknown>` to the strict `Record<string, TokenValue>` shape. Type-version skew between sibling packages is now caught at `tsc` time instead of at runtime.
Public API
Theme
export interface Theme {
name: string;
displayName: string;
colorScheme: "light" | "dark";
tokens: {
light: Record<string, TokenValue>;
dark: Record<string, TokenValue>;
};
}The canonical Theme shape. Every theme object the design system consumes — `defaultTheme`, `midnightTheme`, anything you author — must satisfy this type.
import type { Theme } from "@hex-core/registry";
const myTheme: Theme = {
name: "brand",
displayName: "Brand",
colorScheme: "light",
tokens: { light: {/* ... */}, dark: {/* ... */} },
};TokenValue / TokenSet
export interface TokenValue {
value: string;
type: "color" | "spacing" | "typography" | "radius" | "shadow" | "duration" | "easing";
}
export type TokenSet = Record<string, TokenValue>;A token is a `value` (HSL triplet, CSS length, easing function, etc.) plus a `type` discriminant. Discriminating by `type` lets transformers emit the right wrapper — `hsl()` for colors, raw value for everything else.
import type { TokenValue } from "@hex-core/registry";
const primary: TokenValue = { value: "240 5.9% 10%", type: "color" };
const radius: TokenValue = { value: "0.5rem", type: "radius" };RegistryItem
export interface RegistryItem {
name: string;
displayName: string;
description: string;
category: "primitive" | "component" | "block" | "hook";
props: PropDef[];
variants: VariantDef[];
examples: Example[];
ai: AIHints;
dependencies: { npm: string[]; internal: string[]; peer: string[] };
tags: string[];
}Schema for one entry in `registry/items/<slug>.json`. Components, the CLI's `hex add`, and the MCP server's `get_component` tool all serialize to this shape. The `ai` block is what makes hex-ui LLM-native — `whenToUse`, `commonMistakes`, `accessibilityNotes`.
import { registryItemSchema } from "@hex-core/registry";
const parsed = registryItemSchema.parse(rawJson);
// → typed RegistryItem with full validationstrictThemeSchema / strictTokenSetSchema
import { strictThemeSchema, strictTokenSetSchema } from "@hex-core/registry";
import type { z } from "zod";
declare const strictThemeSchema: z.ZodType<Theme>;
declare const strictTokenSetSchema: z.ZodType<TokenSet>;Zod schemas for runtime validation. Use these at trust boundaries — parsing user-submitted theme overrides, validating an MCP tool input, ingesting a third-party token set.
import { strictThemeSchema } from "@hex-core/registry";
const result = strictThemeSchema.safeParse(input);
if (!result.success) {
throw new Error("Invalid theme: " + result.error.message);
}
const theme = result.data;Workflows
Validate a user-uploaded theme JSON file
Users sometimes ship custom themes as JSON. Parse with `strictThemeSchema` before handing the result to any transformer.
import { strictThemeSchema } from "@hex-core/registry";
import { themeToCss } from "@hex-core/tokens";
export async function compileUploadedTheme(json: unknown) {
const result = strictThemeSchema.safeParse(json);
if (!result.success) {
return { ok: false, errors: result.error.issues };
}
return { ok: true, css: themeToCss(result.data, { mode: "light" }) };
}Build a typed registry-item generator
Authoring a custom component? Type your scaffold against `RegistryItem` so missing fields surface at `tsc` time.
import type { RegistryItem } from "@hex-core/registry";
export function makeRegistryItem(name: string): RegistryItem {
return {
name,
displayName: name,
description: "TODO",
category: "component",
props: [],
variants: [],
examples: [],
ai: {
whenToUse: "",
whenNotToUse: "",
commonMistakes: [],
relatedComponents: [],
accessibilityNotes: "",
tokenBudget: 0,
},
dependencies: { npm: [], internal: [], peer: [] },
tags: [],
};
}Compatibility
- Depends only on `zod@^4.3.6`. No React, no DOM, no Node-specific APIs.
- Compiled to ESM with `.d.ts` types. Tree-shakable — importing only `Theme` doesn't pull the Zod runtime.
- Strict-mode schema versioning: 0.2.x line is back-compatible at the type level; ingesting older theme JSON usually still parses cleanly.