@smoothie-framework/ui-core
v0.0.2
Published
Smoothie UI Core - Portable design tokens, types, and utilities
Downloads
678
Maintainers
Readme
@smoothie-framework/ui-core
Portable design tokens, types, and utilities for the Smoothie UI ecosystem.
+------------------------------------------------------------------+
| CONSUMERS |
| @smoothie-framework/ui | @smoothie-framework/ui-react | Zig SDK | Berry AI |
+------------------------------------------------------------------+
|
v
+------------------------------------------------------------------+
| @smoothie-framework/ui-core |
| +-------------+ +-------------+ +-------------+ +-----------+ |
| | tokens/ | | theme/ | | recipes/ | | protocol/ | |
| | colors | | validation | | Button | | binary | |
| | spacing | | extension | | Input | | JSON | |
| | typography | | resolution | | Stack | | Zig | |
| +-------------+ +-------------+ +-------------+ +-----------+ |
+------------------------------------------------------------------+What is ui-core?
@smoothie-framework/ui-core is the design system foundation layer for the Smoothie ecosystem. It defines:
| Layer | Purpose | Consumed By | |-------|---------|-------------| | Tokens | Raw design values (colors, spacing, typography) | All SDKs, all frameworks | | Semantic | Light/dark mode token mappings | Theme providers | | Recipes | Component variant definitions (styling as data) | Component libraries | | Theme | Composition, validation, reference resolution, CSS compilation | Applications | | Protocol | Binary/JSON/Zig exports for cross-SDK sharing | Zig SDK, Rust SDK, Berry AI | | CSS | CSS variable generation, Tailwind preset | DOM-based renderers |
This package has zero runtime dependencies and is framework-agnostic.
Documentation model note:
- Strategic and requirements docs for the UI toolkit live in
docs/spec/ui-toolkit/. - Canonical implementation-aware UI docs live in
docs/architecture/ui-toolkit/. - This README is a package guide, not the primary source for UI toolkit strategy.
Important boundary:
@smoothie-framework/ui-coredoes not include runtime reactivity primitives.- Use
@smoothie-framework/uiforsignal/computed/effect/batchand rendering runtime APIs.
Installation
bun add @smoothie-framework/ui-core
# or
npm install @smoothie-framework/ui-coreImport Paths
The package uses subpath exports for optimal tree-shaking:
// Essential exports (most common)
import { colors, spacing, defaultTheme } from '@smoothie-framework/ui-core';
import type { Theme, UINode } from '@smoothie-framework/ui-core';
// Specialized imports
import { buttonRecipe, textRecipe } from '@smoothie-framework/ui-core/recipes';
import { toCssVariables, toTailwindPreset } from '@smoothie-framework/ui-core/css';
import { exportTokensBinary } from '@smoothie-framework/ui-core/protocol';
import { extendTheme, validateTheme, compileThemeCss } from '@smoothie-framework/ui-core/theme';
import { lightSemanticColors, darkSemanticColors } from '@smoothie-framework/ui-core/semantic';| Path | Purpose |
|------|---------|
| @smoothie-framework/ui-core | Essential tokens, types, theme utilities |
| @smoothie-framework/ui-core/tokens | Primitive design tokens only |
| @smoothie-framework/ui-core/types | Type definitions only |
| @smoothie-framework/ui-core/theme | Theme composition and validation |
| @smoothie-framework/ui-core/semantic | Light/dark mode semantic tokens |
| @smoothie-framework/ui-core/recipes | Component variant recipes |
| @smoothie-framework/ui-core/resolve | Token resolution primitives |
| @smoothie-framework/ui-core/css | CSS variable generation, Tailwind |
| @smoothie-framework/ui-core/protocol | Cross-SDK binary/JSON exports |
| @smoothie-framework/ui-core/schemas/component-contracts | Canonical component ids, aliases, variants, and styling support |
| @smoothie-framework/ui-core/utils | Utility functions |
Export Strategy
- Root (
@smoothie-framework/ui-core): ergonomic defaults and high-frequency APIs (tokens,Theme,defaultTheme, core helpers). - Subpaths: canonical source for specialized features (
/recipes,/theme,/resolve,/css,/protocol). - Deep paths (
/protocol/infer,/schemas/component-schemas): narrow allowlist for advanced/infra usage; prefer subpath indexes for app code. - No compatibility aliases in new API: import specialized protocol/resolve helpers from their canonical subpaths.
Quick Start
1. Use Design Tokens
import { colors, spacing, typography, radii, shadows } from '@smoothie-framework/ui-core';
const styles = {
backgroundColor: colors.blue[500], // '#3b82f6'
padding: spacing.md, // 16
borderRadius: radii.md, // 6
boxShadow: shadows.md, // '0 4px 6px -1px ...'
fontSize: typography.fontSize.md.size, // 16
};2. Create a Theme
import { defaultTheme, extendTheme } from '@smoothie-framework/ui-core';
// Use default theme
console.log(defaultTheme.colors.blue[500]); // '#3b82f6'
// Extend with custom values
const { theme } = extendTheme({
colors: {
brand: '{colors.blue.600}', // Token reference
},
});3. Apply to DOM
import { toCssVariables } from '@smoothie-framework/ui-core/css';
const cssVars = toCssVariables(defaultTheme);
Object.assign(document.documentElement.style, cssVars);
// Now use in CSS:
// background: var(--smoothie-colors-blue-500);4. Use Component Recipes
import { buttonRecipe, resolveRecipe } from '@smoothie-framework/ui-core/recipes';
import { defaultTheme } from '@smoothie-framework/ui-core/theme';
const styles = resolveRecipe(
buttonRecipe,
{ variant: 'primary', size: 'md' },
defaultTheme,
// mode defaults to 'css-variables'
);
// { background: 'var(--smoothie-button-primary-bg)', ... }Common Consumer Paths
Using ui-core inside another framework
Use @smoothie-framework/ui-core when you want Smoothie tokens, recipes, and theme helpers
without adopting the full @smoothie-framework/ui runtime.
Useful imports:
| Import | Purpose |
| --- | --- |
| @smoothie-framework/ui-core | tokens and core types |
| @smoothie-framework/ui-core/recipes | recipe definitions such as buttonRecipe |
| @smoothie-framework/ui-core/theme | theme extension and validation |
| @smoothie-framework/ui-core/css | CSS variable generation and Tailwind preset |
| @smoothie-framework/ui-core/resolve | token resolution helpers |
Typical recipe usage:
import { buttonRecipe, resolveRecipe } from '@smoothie-framework/ui-core/recipes';
import { defaultTheme } from '@smoothie-framework/ui-core/theme';
const styles = resolveRecipe(buttonRecipe, { variant: 'primary', size: 'md' }, defaultTheme);Exporting tokens for cross-SDK consumers
Use the protocol exports when another SDK or runtime needs the same token graph.
import { defaultTheme } from '@smoothie-framework/ui-core';
import { exportTokensBinary, exportTokensZig } from '@smoothie-framework/ui-core/protocol';
const tokensBin = exportTokensBinary(defaultTheme);
const tokensZig = exportTokensZig(defaultTheme);Exporting component schema for Berry and tools
Use component schema exports when tools or AI systems need the canonical UI contract.
import { exportComponentSchema, exportComponentSchemaJSON } from '@smoothie-framework/ui-core/protocol';
const schema = exportComponentSchema();
const json = exportComponentSchemaJSON(true);These exports are the canonical bridge from ui-core into Berry, editor tooling,
and future cross-SDK consumers.
Use @smoothie-framework/ui-core/schemas/component-contracts when adapters,
styling tooling, or conformance tests need canonical runtime component ids,
schema aliases, recipe variants, or styling-support metadata.
Architecture
Package Structure
src/
tokens/ # Primitive design tokens
colors.ts 7 palettes x 11 shades = 77 colors
paths.ts Canonical token path unions (KnownTokenPath)
spacing.ts xs(4) sm(8) md(16) lg(24) xl(32) 2xl(48) 3xl(64)
typography.ts Font families, sizes (xs-4xl), weights
radii.ts none(0) xs(2) sm(4) md(6) lg(8) xl(12) 2xl(16) full(9999)
shadows.ts xs, sm, md, lg, xl, 2xl, 3xl
semantic/ # Mode-aware semantic tokens
colors.ts accent, background, foreground, muted, border, etc.
modes.ts Light/dark mode utilities
recipes/ # Component variant definitions
button.ts variant: primary|secondary|ghost|outline|danger
input.ts variant: outline|filled|flushed
stack.ts direction, gap, align, justify, wrap
text.ts size, weight, align, truncate
types.ts Recipe<V>, StyleObject, VariantSchema
theme/ # Theme system
default.ts Default theme with all tokens
extend.ts extendTheme(), createThemeVariant()
validate.ts validateTheme(), assertValidTheme()
errors.ts ThemeError, ThemeErrors classes
resolve/ # Token resolution primitives
token-map.ts buildTokenMap()
resolve.ts resolveToken(), resolveStyleObject(), resolveDeep()
css-vars.ts resolveStyleObjectToCssVars(), tokenPathToCssVar()
patterns.ts canonical regex + token detection helpers
css/ # CSS generation
variables.ts toCssVariables(), getCssVariable()
tailwind.ts toTailwindPreset()
protocol/ # Cross-SDK exports
export-tokens.ts exportTokensBinary() -> Uint8Array
export-schema.ts exportComponentSchema() -> JSON
export-zig.ts exportTokensZig() -> string
types/ # Type definitions
layout.ts SizeToken, SpacingValue, Alignment, Justification
ui-node.ts UINode, UINodeProps, UIEvent
theme.ts Theme, ThemeTokens, TokenKey
index.ts Public barrel for cross-domain contracts
utils/ # Utilities
token-helpers.ts resolveColor, resolveSpacing, etc.
theme-utils.ts createTheme, mergeTheme
guards.ts isSizeToken, isUINode, etc.Consumer Matrix
| Consumer | What They Import | Format |
|----------|------------------|--------|
| @smoothie-framework/ui | Direct TypeScript imports | .ts |
| @smoothie-framework/ui-react | Direct TypeScript imports | .ts |
| Berry AI | Component schema | .json |
| Zig SDK | Token constants, schema | .zig, .bin |
| Rust SDK | Token constants | .rs, .bin |
| Unity C ABI | Token binary | .bin |
| DOM Adapter | CSS variables | .css |
| Tailwind users | Preset config | .js |
Dependency Graph
@smoothie-framework/ui-core (this package - zero dependencies)
|
+---> @smoothie-framework/ui (signals, JSX, components)
|
+---> @smoothie-framework/ui-react (React adapter)
+---> @smoothie-framework/ui-vue (Vue adapter)
+---> @smoothie-framework/ui-svelte (Svelte adapter)
|
+---> Berry AI (component generation)
+---> Zig SDK (native rendering)Resolution and Styling Pipeline
The current implementation uses a single token-reference pipeline with two output modes:
- Recipe merge:
base + selected variants + compound variants(mergeVariants) - Resolution mode:
css-variables(default runtime path): rewrites{token.path}->var(--smoothie-...)without building a token mapvalues(build-time/SSR path): resolves references to concrete values viabuildTokenMap
- Token precedence in map-based mode:
theme.tokens>recipe.tokens> primitive tokens
CSS output options:
- Inline style path: consumers call
resolveRecipe(...)and apply resulting style objects - External stylesheet path:
generateRecipeCss/generateStylesheetemit.smoothie-*classes - Theme variable path:
toCssVariables(theme)emits CSS custom properties for primitives andtheme.tokensaliases
Token Reference
Colors
5 primitive palettes, each with 11 shades (50-950):
colors.blue[500] // '#3b82f6' - Blue
colors.green[500] // '#22c55e' - Green
colors.yellow[500] // '#f59e0b' - Yellow
colors.red[500] // '#ef4444' - Red
colors.neutral[500] // '#737373' - Neutral
// Backward-compatible aliases are still accepted in token references:
// {colors.primary.500} -> {colors.blue.500}Spacing
4px base unit scale:
| Token | Value | Use Case |
|-------|-------|----------|
| xs | 4px | Icon padding |
| sm | 8px | Tight spacing |
| md | 16px | Default padding |
| lg | 24px | Section gaps |
| xl | 32px | Large gaps |
| 2xl | 48px | Hero sections |
| 3xl | 64px | Page margins |
Typography
typography.fontFamily.sans // 'Inter, system-ui, ...'
typography.fontFamily.mono // 'JetBrains Mono, ...'
typography.fontSize.md // { size: 16, lineHeight: 24 }
typography.fontWeight.bold // 700Theme System
Token References
Use {path.to.token} syntax for theme composition:
import { extendTheme } from '@smoothie-framework/ui-core/theme';
const { theme } = extendTheme({
colors: {
accent: '{colors.blue.500}',
background: '{colors.neutral.50}',
},
}, { resolveRefs: true });
// theme.colors.accent === '#3b82f6' (resolved)Theme Token Overrides
import { defaultTheme, extendTheme } from '@smoothie-framework/ui-core/theme';
const { theme } = extendTheme({
tokens: {
...defaultTheme.tokens,
'button.primary.bg': '{colors.blue.600}',
'button.primary.hover.bg': '{colors.blue.700}',
},
}, { validate: true });Theme Validation
import { validateTheme } from '@smoothie-framework/ui-core/theme';
const result = validateTheme(myTheme);
if (!result.valid) {
console.error(result.errors);
// [{ severity: 'error', path: 'colors.x', message: '...' }]
}Recipes
Component recipes define styling as data:
import { buttonRecipe, resolveRecipe, type ButtonVariants } from '@smoothie-framework/ui-core/recipes';
import { defaultTheme } from '@smoothie-framework/ui-core/theme';
// Resolve styles for a specific variant combination (runtime path)
function getButtonStyles(variant: ButtonVariants['variant'], size: ButtonVariants['size']) {
return resolveRecipe(
buttonRecipe,
{ variant, size },
defaultTheme,
);
}Available recipes: buttonRecipe, inputRecipe, stackRecipe, rowRecipe, textRecipe, headingRecipe
CSS Generation
CSS Variables
import { toCssVariables, getCssVariable } from '@smoothie-framework/ui-core/css';
const vars = toCssVariables(theme);
// { '--smoothie-colors-blue-500': '#3b82f6', ... }
const ref = getCssVariable('colors.blue.500');
// 'var(--smoothie-colors-blue-500)'Tailwind Preset
import { toTailwindPreset } from '@smoothie-framework/ui-core/css';
const preset = toTailwindPreset(theme);
// Use in tailwind.config.js: presets: [preset]Protocol Exports
Binary Export (for native SDKs)
import { exportTokensBinary, getTokenExportMetadata } from '@smoothie-framework/ui-core/protocol';
const metadata = getTokenExportMetadata(theme);
// { version: '1.0', colorCount: 77, spacingCount: 7, sizeBytes: 2847 }
const binary = exportTokensBinary(theme);
await fs.writeFile('tokens.bin', binary);JSON Schema (for Berry AI)
import { exportComponentSchemaJSON } from '@smoothie-framework/ui-core/protocol';
const json = exportComponentSchemaJSON();
await fs.writeFile('schema.json', json);Zig Source (for Zig SDK)
import { exportTokensZig } from '@smoothie-framework/ui-core/protocol';
const zigSource = exportTokensZig(theme);
await fs.writeFile('tokens.zig', zigSource);Design Principles
- Zero Runtime Dependencies - Pure TypeScript, no external packages
- Framework Agnostic - Works with React, Vue, Svelte, or vanilla JS
- Platform Agnostic - Values work on DOM, Canvas, or native
- Tree-Shakeable - Subpath exports for minimal bundle size
- Type-Safe - Full TypeScript coverage with strict mode
- Cross-SDK - Exports to TypeScript, Zig, Rust, JSON
Bundle Size
| Module | Size (gzipped) | |--------|----------------| | tokens | ~1.5KB | | theme | ~2KB | | recipes | ~1KB | | css | ~0.5KB | | protocol | ~1.5KB | | Total | < 8KB |
Documentation
- UI package guide - Runtime usage and styling integration
- Architecture - Technical design
- PRD - Product requirements
License
MIT
