A restaurant web app built with UI8Kit, a design system that combines semantic React components with a DSL (Domain-Specific Language) for template-friendly, LLM-generatable markup.
This repository is a GitHub template. To create a new project:
- On GitHub, click "Use this template" → "Create a new repository"
- Choose a name (e.g.
my-dsl-blog,my-dsl-shop) - Clone and install:
bun install - Follow the pipeline in
.project/PLAYBOOK.mdfrom Stage 0 to Stage 6
Structure: apps/dsl is the source app; apps/react is generated output. Use apps/dsl as your reference.
- Props = Tailwind classes — Semantic props (
gap="6",bg="primary") map to Tailwind utilities; no rawclassNameor inline styles. - DSL markup — Use
<If>,<Var>,<Loop>,<Slot>instead of JS conditionals/loops for stable, template-ready output. - Fixtures + Context — JSON fixtures drive content;
@ui8kit/sdkprovidescreateContextto wire data into blocks. - Valid HTML5 — Semantic tags (
<section>,<article>,<header>, etc.) viacomponentprop for W3C-valid output.
fixtures/*.json → context (SDK createContext) → routes → PageViews → Blocks
↓
@ui8kit/core (Block, Stack, Card, etc.)
@ui8kit/dsl (If, Var, Loop, Slot)
- Blocks — Reusable page sections (HeroBlock, SidebarContent).
- PageViews — Full page compositions (LandingPageView, MenuPageView).
- Layouts — MainLayout, AdminLayout.
- Partials — Header, Footer, ThemeToggle.
- Routes — Connect URL to PageView, pass context data.
bun create vite resta-app --template react-ts
cd resta-appInstall core deps:
bun add react react-dom react-router-dom
bun add -d vite @vitejs/plugin-react-swc typescript @types/react @types/react-dom @types/nodevite.config.ts:
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react-swc';
import path from 'node:path';
export default defineConfig({
plugins: [react()],
server: { port: 3020 },
resolve: {
alias: {
'@': path.resolve(process.cwd(), './src'),
},
},
});tsconfig.json — add paths:
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
}
},
"include": ["src"]
}bun add -d tailwindcss @tailwindcss/postcss postcsspostcss.config.js:
export default {
plugins: {
'@tailwindcss/postcss': {},
},
};src/css/index.css:
@import "tailwindcss";
@layer base {
body {
@apply bg-background text-foreground font-sans;
}
}Add design tokens (e.g. src/css/resta.css) with :root and .dark CSS variables, then @import "./resta.css" in index.css.
Initialize UI8Kit and add components:
bunx ui8kit@latest init
bunx ui8kit@latest add --allThis creates:
src/components/— UI primitives (Block, Stack, Group, Title, Text, Button, etc.) and composites (Card, Accordion, Sheet).src/variants/— CVA configs.src/lib/—utility-props.ts,utility-props.map.ts,utils.ts.src/ui8kit.config.json— UI8Kit config.src/ui8kit.map.json— Tailwind class map for generation.
Wire aliases — add @ui8kit/core to Vite and TypeScript:
vite.config.ts:
resolve: {
alias: {
'@': path.resolve(process.cwd(), './src'),
'@ui8kit/core': path.resolve(process.cwd(), './src/components/index.ts'),
},
},tsconfig.json:
"paths": {
"@/*": ["./src/*"],
"@ui8kit/core": ["./src/components/index.ts"]
}bun add @ui8kit/sdkThe SDK provides:
createContext— Build app context from fixtures.If,Var,Loop,Slot— Re-exported from@ui8kit/dslfor DSL markup.ui8kit-validate— Validate config and DSL.
Create fixtures — e.g. fixtures/landing.json:
{
"title": "Welcome to RestA",
"subtitle": "Experience fine dining...",
"ctaText": "View Menu",
"ctaUrl": "/menu"
}Wire context — src/data/context.ts:
import { createContext } from '@ui8kit/sdk/source/data';
import landingData from '../../fixtures/landing.json';
// ... other fixtures
const baseContext = createContext({
site: siteData,
navItems: navItems,
fixtures: { landing: landingData, menu: menuData, ... },
});
export const context = { ...baseContext, landing: landingData, ... };Layouts — src/layouts/MainLayout.tsx — header, sidebar, main content.
Blocks — e.g. src/blocks/HeroBlock.tsx:
import { Block, Stack, Container, Title, Text, Button, Group } from '@ui8kit/core';
import { If, Var, Slot } from '@ui8kit/dsl';
export function HeroBlock({ title, subtitle, ctaText, ctaUrl, ... }) {
return (
<Block component="section" py="16" bg="background" data-class="hero-section">
<Container max="w-7xl" flex="col" gap="8" items="center">
<Stack gap="4" items="center" max="w-2xl">
<Title fontSize="5xl" fontWeight="bold" textAlign="center" data-class="hero-title">
<Var name="title" value={title} />
</Title>
<If test="subtitle" value={!!subtitle}>
<Text fontSize="xl" textColor="muted-foreground" data-class="hero-subtitle">
<Var name="subtitle" value={subtitle} />
</Text>
</If>
</Stack>
<Group gap="4" data-class="hero-actions">
<Button size="lg" href={ctaUrl}><Var name="ctaText" value={ctaText} /></Button>
</Group>
</Container>
</Block>
);
}PageView — composes layout + blocks:
<MainLayout navItems={navItems} sidebar={<SidebarContent />}>
<HeroBlock {...landing} />
</MainLayout>Route — passes context to PageView:
<LandingPageView landing={context.landing} navItems={context.navItems} ... />Run the DSL linter to catch common issues (e.g. unwrapped <Var> for optional values):
bun run ui8kit-lint-dslAdd to package.json:
"scripts": {
"ui8kit-lint-dsl": "bun run node_modules/@ui8kit/lint/src/cli/validate-dsl.ts src"
}Or use bunx -p @ui8kit/lint ui8kit-lint-dsl src if the lint package is installed.
Example output:
⚠ UNWRAPPED_VAR
Location: src\blocks\MenuPageView.tsx:70:21
Message: <Var> should be wrapped in <If> for optional values.
Received: "<Var ... />"
Fix: Wrap optional <Var> in <If>:
<If test="item.price" value={!!item.price}>
<Text><Var name="item.price" value={item.price} /></Text>
</If>bun run devOpens at http://localhost:3020. Hot reload for components, layouts, and fixtures.
src/
├── components/ # @ui8kit/core — Block, Stack, Card, Button, etc.
├── variants/ # CVA configs
├── lib/ # utility-props, utils
├── blocks/ # HeroBlock, PageViews
├── layouts/ # MainLayout, AdminLayout
├── partials/ # Header, Footer, ThemeToggle
├── routes/ # Route components
├── providers/ # Theme, Auth
├── data/ # context.ts
└── css/ # Tailwind + theme tokens
fixtures/ # JSON data
| Script | Description |
|---|---|
bun run dev |
Vite dev server (port 3020) |
bun run build |
Production build |
bun run preview |
Preview build |
bun run ui8kit-lint-dsl |
Validate DSL (If, Var, Loop rules) |
bunx ui8kit-validate |
Full validation (config + DSL) |
bunx ui8kit-generate --target react |
Static template generation |
| Component | Use |
|---|---|
<If test="x" value={!!x}> |
Conditional render |
<Var name="title" value={title} /> |
Variable output (wrap in If when optional) |
<Loop each="items" as="item" data={items}> |
Iteration |
<Slot name="extra">{children}</Slot> |
Content slot |
Rule: <Var> for optional values must be wrapped in <If>. Use a semantic wrapper (e.g. <Text>) between <If> and <Var> when needed.
MIT License