Complete introduction to building valid DSL applications with UI8Kit. After reading, you will be able to create interfaces without tech debt, hardcode, and with architecture compliance.
- What this project is
- Environment and first commands
- Monorepo structure
- Key concepts
- Data flow
- DSL components: If, Var, Loop, Slot
- Semantic props and data-class
- Where to put new code
- Config files: ui8kit, maintain, blueprint
- Commands and workflow
- Validation and linting
- React and HTML+CSS generation
- Anti-patterns — what to avoid
- First task step by step
- Pre-commit checklist
UI8Kit is a design system and pipeline for building web applications. Features:
- DSL (Domain-Specific Language) — Instead of plain JS in JSX, you use components
<If>,<Var>,<Loop>,<Slot>. This allows generating not only React but also plain HTML+CSS. - Fixtures — Content lives in JSON (
fixtures/*.json), not in code. No hardcode. - Semantics — Props (
gap="6",bg="primary") map to Tailwind classes. NoclassNameorstyle. - Valid HTML5 — Semantic tags are used (
section,article,header,nav,main,footer).
Your goal: Learn to write blocks and pages so they pass validation and generate correctly to React and HTML+CSS.
The project uses Bun as runtime and package manager:
# Windows (PowerShell)
irm bun.sh/install.ps1 | iex
# macOS/Linux
curl -fsSL https://bun.sh/install | bash# Install dependencies (from repo root)
bun install
# Run dev server for DSL app
cd apps/dsl
bun run devAfter bun run dev, the app opens (usually http://localhost:3020 or another port from config).
@ui8kit-resta-app/
├── apps/
│ ├── dsl/ ← Main DSL app (restaurant)
│ └── react/ ← Generated React (from dsl)
├── .project/ ← Project docs (including this guide)
└── .cursor/rules/ ← AI rules (best-practices, architecture)
Working app: apps/dsl — main DSL code (blocks, routes, fixtures).
Generated app: apps/react — output of bun run generate. Not edited by hand.
CLI tools (from npm): @ui8kit/generator, @ui8kit/maintain, @ui8kit/lint, @ui8kit/sdk.
A Block is a reusable page section or a full page. Examples: HeroBlock, SidebarContent, MenuPageView.
- Section block — part of a page (HeroBlock, card)
- PageView — full page (LandingPageView, MenuPageView)
Fixtures are JSON files with data. All content comes from here.
fixtures/
├── shared/ # site.json, page.json, navigation.json
├── landing.json
├── menu.json
├── recipes.json
├── blog.json
├── promotions.json
└── admin.json
Example fixtures/landing.json:
{
"title": "Welcome to RestA",
"subtitle": "Experience fine dining...",
"ctaText": "View Menu",
"ctaUrl": "/menu",
"secondaryCtaText": "Our Recipes",
"secondaryCtaUrl": "/recipes"
}Context is the single entry point for data. context.ts loads fixtures and passes them to routes and blocks.
// In a route
import { context } from '@/data/context';
<LandingPageView landing={context.landing} navItems={context.navItems} ... />Routes map URLs to PageViews and pass data from context.
// src/App.tsx
<Route path="/" element={<LandingPage />} />
<Route path="/menu" element={<MenuPage />} />
<Route path="/menu/:slug" element={<MenuDetailPage />} />- Layout — page wrapper (MainLayout, AdminLayout). Contains header, sidebar, main, footer.
- Partial — reusable fragment (Header, Footer, ThemeToggle, SidebarContent).
fixtures/*.json
→ src/data/adapters/fixtures.adapter.ts
→ src/data/context.ts (createContext)
→ src/routes/*.tsx (pass data to PageView)
→ src/blocks/*PageView.tsx (render with Var, Loop, If)
Important: Data always comes from context or props. Hardcoded strings, numbers, or arrays in JSX are not allowed.
In JSX you must not use:
?.(optional chaining for conditional render)&&for conditional render{condition ? 'a' : 'b'}.map()for lists
Use DSL components from @ui8kit/dsl instead.
// ✅ Correct
<Var name="title" value={title} />
// ❌ Wrong
{title}
{item?.name}name — label for debugging/generation. value — the actual value.
// ✅ Correct
<If test="subtitle" value={!!subtitle}>
<Text data-class="hero-subtitle">
<Var name="subtitle" value={subtitle} />
</Text>
</If>
// ❌ Wrong
{subtitle && <Text>{subtitle}</Text>}
{subtitle ? <Text>{subtitle}</Text> : null}// ✅ Correct (render prop)
<Loop each="items" as="item" data={items}>
{(item: { id: string; title: string }) => (
<Card key={item.id} data-class="item-card">
<CardHeader>
<CardTitle order={4}><Var name="item.title" value={item.title} /></CardTitle>
</CardHeader>
</Card>
)}
</Loop>
// ❌ Wrong
{items.map(item => (
<Card key={item.id}>...</Card>
))}<Slot name="extra">{children}</Slot>If a value can be undefined or null, wrap <Var> in <If>:
<If test="subtitle" value={!!subtitle}>
<Text><Var name="subtitle" value={subtitle} /></Text>
</If>The DSL linter (lint:dsl) enforces rule UNWRAPPED_VAR: every <Var> — including inside <Loop> and for non-optional fields — must be wrapped in <If>. This is required for the static HTML generator to emit safe, conditional output.
// ❌ Wrong — even inside Loop, even if field is required
<Loop each="items" as="item" data={items}>
{(item) => (
<Card key={item.id} data-class="item-card">
<CardTitle order={4}>
<Var name="item.title" value={item.title} />
</CardTitle>
</Card>
)}
</Loop>
// ✅ Correct — wrap in If even for required fields
<Loop each="items" as="item" data={items}>
{(item) => (
<Card key={item.id} data-class="item-card">
<CardTitle order={4}>
<If test="item.title" value={!!item.title}>
<Var name="item.title" value={item.title} />
</If>
</CardTitle>
</Card>
)}
</Loop>This rule applies to: page props, loop item fields, badge/badge-variant values, all other <Var> usages.
className— do not usestyle={{ ... }}— do not use- Raw HTML tags (
<div>,<span>,<h1>,<p>) — do not use
Imports from @ui8kit/core:
- Layout: Block, Stack, Group, Grid, Container, Box
- Content: Title, Text, Image, Icon, Badge, Button
- Composites: Card, CardHeader, CardTitle, CardContent, Accordion, Sheet, Field
Every semantic element must have data-class:
<Block component="section" data-class="hero-section">
<Stack data-class="hero-content">
<Title data-class="hero-title">
<Button data-class="hero-cta">| Prop | Examples | Description |
|---|---|---|
gap |
"0", "2", "4", "6", "8" |
Spacing between elements |
p, px, py |
"4", "8", "16" |
Padding |
flex |
"col", "row", "wrap" |
Flex direction |
items |
"start", "center", "end" |
Alignment |
justify |
"between", "center", "end" |
Main-axis alignment |
fontSize |
"sm", "lg", "2xl", "4xl" |
Font size |
fontWeight |
"normal", "medium", "bold" |
Weight |
textColor |
"foreground", "muted-foreground", "primary" |
Text color |
bg |
"primary", "secondary", "muted", "card" |
Background |
variant (Button) |
"primary", "outline", "secondary" |
Button variant |
// ✅ Correct
<Block bg="primary" data-class="hero">
<Button variant="primary">
<Text textColor="muted-foreground">
// ❌ Wrong
<Block bg="blue-500">
<Button className="bg-[#3b82f6]">Use component for semantic tags. Allowed tags are defined in component-tag-map.json (package @ui8kit/generator). Validation is done via HtmlConverterService, Maintain checker, and ui8kit-validate.
Block — sectioning:
<Block component="section" data-class="hero-section"> // page section
<Block component="article" data-class="feature-card"> // card, article
<Block component="header" data-class="page-header"> // header
<Block component="nav" data-class="main-nav"> // navigation
<Block component="main" data-class="page-main"> // main content
<Block component="footer" data-class="page-footer"> // footer
<Block component="aside" data-class="sidebar"> // sidebar
<Block component="figure" data-class="hero-image"> // figure
<Block component="form" data-class="contact-form"> // formBox / Container — layout containers: div, form, blockquote.
Text — typography: p, h1–h6, span, label, cite, q, figcaption, etc.
Stack / Group — layout: div, span (span only inside <a> for inline).
Field — form elements: input, textarea, select, button.
Full map: shipped with @ui8kit/generator (in node_modules/@ui8kit/generator/dist/lib/component-tag-map.json).
Every app needs three config files in its root directory. Copy them from apps/dsl and adapt.
Consumed by ui8kit-validate, ui8kit-generate, and ui8kit-lint. Controls generation output, aliases, lint settings.
{
"$schema": "https://ui.buildy.tw/schema.json",
"configVersion": "1",
"brand": "my-app",
"framework": "vite-react",
"typescript": true,
"target": "react",
"platform": "shopify",
"platformDomain": "catalog",
"outDir": "../react-{APP_NAME}",
"dist": {
"static": true,
"render": { "appEntry": "src/App.tsx", "skipRoutes": ["/admin/dashboard"] },
"html": { "mode": "tailwind" },
"postcss": { "enabled": true, "uncss": { "enabled": true } }
},
"aliases": {
"@": "./src",
"@/components": "./src/components",
"@/ui": "./src/components/ui",
"@/layouts": "./src/layouts",
"@/blocks": "./src/blocks",
"@/lib": "./src/lib",
"@/variants": "./src/variants"
},
"fixtures": "./fixtures",
"tokens": "./src/assets/css/shadcn.css",
"componentsDir": "./src/components",
"blocksDir": "./src/blocks",
"layoutsDir": "./src/layouts",
"partialsDir": "./src/partials",
"libDir": "./src/lib",
"registry": "@ui8kit",
"lint": {
"strict": true,
"dsl": true,
"ui8kitMapPath": "./src/ui8kit.map.json",
"utilityPropsMapPath": "./src/lib/utility-props.map.ts"
}
}What to change per app:
brand— your app name (kebab-case)outDir—"../react-{APP_NAME}"(matches generated app folder)skipRoutes— routes to skip in HTML render (e.g. authenticated admin pages)
Consumed by maintain run, maintain validate, maintain clean. Schema: node_modules/@ui8kit/maintain/schemas/maintain.config.schema.json.
{
"$schema": "node_modules/@ui8kit/maintain/schemas/maintain.config.schema.json",
"root": ".",
"reportsDir": ".cursor/reports",
"continueOnError": true,
"maxParallel": 2,
"checkers": {
"invariants": {
"routes": {
"appFile": "src/App.tsx",
"required": ["/", "/admin", "/admin/dashboard"]
},
"fixtures": {
"pageFile": "fixtures/shared/page.json",
"requiredPageDomains": ["website", "admin"]
},
"blocks": {
"dir": "src/blocks",
"indexFile": "src/blocks/index.ts",
"recursive": true
},
"context": {
"file": "src/data/adapters/fixtures.adapter.ts",
"requiredSymbols": ["loadFixturesContextInput"]
}
},
"viewExports": { "pattern": "src/**/*View.tsx", "exportShape": "interface+function" },
"dataClassConflicts": { "scope": ["src"], "pattern": "**/*.tsx", "ignoreDataClasses": ["wrapper"] },
"componentTag": { "scope": ["src"], "pattern": "**/*.tsx", "tagMapPath": null },
"colorTokens": {
"scope": ["src"], "pattern": "**/*.tsx",
"tokenSource": "utility-props.map",
"utilityPropsMapPath": "./src/lib/utility-props.map.ts"
},
"genLint": {
"scope": ["src/blocks", "src/layouts", "src/partials"],
"pattern": "**/*.tsx",
"rules": { "GEN001": "error", "GEN002": "error", "GEN003": "warn", "GEN004": "error", "GEN005": "warn", "GEN006": "error", "GEN007": "error", "GEN008": "warn" }
},
"clean": {
"paths": ["../react-{APP_NAME}", "node_modules/.vite"],
"pathsByMode": {
"full": ["node_modules", "../react-{APP_NAME}"],
"dist": ["../react-{APP_NAME}", "node_modules/.vite"]
},
"includeTsBuildInfo": true
},
"lockedDirs": { "dirs": ["src/assets", "src/components", "src/variants"], "pattern": "**/*.{ts,tsx,css,json}" },
"viewHooks": { "pattern": "src/**/*View.tsx", "allowedHooks": [] },
"utilityPropLiterals": {
"scope": ["src/blocks", "src/layouts", "src/partials"],
"pattern": "**/*.tsx",
"utilityPropsMapPath": "./src/lib/utility-props.map.ts",
"allowDynamicInLoop": true
},
"utilityPropsWhitelist": {
"utilityPropsMapPath": "./src/lib/utility-props.map.ts",
"tailwindMapPath": "./assets/maps/tw-css-extended.json",
"additionalMapPaths": [
"./assets/maps/shadcn.map.json",
"./assets/maps/grid.map.json"
],
"maxSuggestions": 2
},
"orphanFiles": {
"scope": ["src"], "pattern": "**/*.{ts,tsx}",
"ignore": ["src/main.tsx", "src/vite-env.d.ts", "src/App.tsx"],
"aliases": { "@": "./src" }
},
"blockNesting": { "scope": ["src/blocks", "src/layouts", "src/partials"], "pattern": "**/*View.tsx" }
}
}What to change per app:
invariants.routes.required— list all your routes (must matchsrc/App.tsx)invariants.fixtures.requiredPageDomains— your domain names fromfixtures/shared/page.jsonclean.paths/clean.pathsByMode— replacereact-{APP_NAME}with your generated folder name
What maintain:validate checks (fast, run always):
invariants— routes in App.tsx match required list; fixture schema domains present; blocks exported from index; adapter function existsviewExports— every*View.tsxexports bothinterfaceandfunctioncontracts— blueprint.json matches App.tsx routes
What maintain:check checks additionally (full run):
dataClassConflicts— duplicatedata-classvalues across filescomponentTag—componentprop value is in allowed tag mapcolorTokens— bg/textColor values exist in utility-props.mapgenLint— GEN001–GEN008 rules in blocks/layouts/partialslockedDirs— files in locked dirs were not modifiedviewHooks— no React hooks inside*View.tsxutilityPropLiterals— no invalid prop literals in UI filesorphanFiles— no unused.ts/.tsxfiles
Consumed by blueprint:scan, blueprint:validate, blueprint:graph, and the contracts checker in maintain.
Start with a seed file, then run bun run blueprint:scan to fill it from code:
{
"version": "1",
"app": "@ui8kit/resta-dsl-{APP_NAME}",
"routes": [
{ "path": "/", "component": "HomePage", "view": "HomePageView" },
{ "path": "/admin", "component": "LoginPage", "view": "AdminLoginPageView" },
{ "path": "/admin/dashboard", "component": "DashboardPage", "view": "AdminDashboardPageView" }
],
"fixtures": [
{ "domain": "website", "file": "fixtures/home.json" },
{ "domain": "admin", "file": "fixtures/admin.json" },
{ "domain": "shared", "file": "fixtures/shared/site.json" },
{ "domain": "shared", "file": "fixtures/shared/navigation.json" },
{ "domain": "shared", "file": "fixtures/shared/page.json" }
]
}Lifecycle:
- Write seed
blueprint.jsonwith routes and fixtures matching your plan. - Build all blocks and routes.
bun run blueprint:scan— scans code and updates blueprint.bun run blueprint:validate— validates project matches blueprint.bun run blueprint:graph— produces dependency graph (optional, for inspection).
After blueprint:scan, the file grows to include entities, layouts, partials, components, context, domains. You can also use apps/dsl/blueprint.json as a reference for the full shape.
| Question | Answer | Folder |
|---|---|---|
| Primitive or composite (Button, Card, Stack)? | Yes | src/components/ |
| CVA variants (button, badge)? | Yes | src/variants/ |
| Section or full page? | Yes | src/blocks/ |
| Reusable fragment (header, footer)? | Yes | src/partials/ |
| Page wrapper? | Yes | src/layouts/ |
| Route that wires PageView? | Yes | src/routes/ |
| Context and data loading? | Yes | src/data/ |
| JSON data? | Yes | fixtures/ |
If Card exists in src/components/, do not build FeatureCard or EventCard from Stack/Group. Use Card with different content.
All commands below are run from the app directory (e.g. apps/dsl, apps/dsl-crm). apps/dsl-design has no lint:gen or test:contracts scripts.
For a full list of CLI commands and options (maintain, ui8kit-generate), see CLI_COMMANDS.md. For a short workflow sequence, see WORKFLOW.md. For a no-skip pipeline from Bootstrap to Release, use .project/PLAYBOOK.md.
cd apps/dsl # or cd apps/dsl-design
bun run dev| Command | Purpose |
|---|---|
bun run validate |
ui8kit-validate — config, DSL, props (including component+tag) |
bun run lint:dsl |
ui8kit-lint-dsl — If/Var/Loop check |
bun run lint:gen |
Generator lint — blocks/layouts rules (apps/dsl only) |
bun run lint |
ui8kit-lint — general lint (whitelist sync) |
bun run typecheck |
TypeScript |
ui8kit-validate checks: app config, DSL rules, props (utility-props, color tokens), and that component values are allowed tags (see component-tag-map.json).
Config: maintain.config.json in the app root. Schema: node_modules/@ui8kit/maintain/schemas/maintain.config.schema.json. See Section 9.2 for the full config structure.
| Script | Purpose |
|---|---|
bun run maintain:validate |
Validate checkers only (invariants, fixtures, view-exports, contracts) |
bun run maintain:check |
All checkers from maintain.config.json |
bun run maintain:props |
utility-props-whitelist only (props map vs tw-css-extended + shadcn + grid) |
For maintain CLI options (--cwd, --config, --check, etc.), run bunx maintain --help or see CLI_COMMANDS.md.
| Command | Purpose |
|---|---|
bun run generate |
Generate React (dsl → ../react, dsl-design → ../react-design) |
bun run finalize |
Build final app |
bun run dist:app |
Full pipeline (lint + validate + generate + finalize) |
After changing src/lib/utility-props.map.ts, rebuild the class map:
From app directory:
cd apps/dsl # or apps/dsl-design
bun run build:mapFrom repo root (scripts/CI):
bunx ui8kit-generate uikit-map --cwd apps/dsl
bunx ui8kit-generate uikit-map --cwd apps/dsl-designOptions: --props-map, --output, --tailwind-map, --shadcn-map, --grid-map, --log-level. See CLI_COMMANDS.md or bunx ui8kit-generate uikit-map --help.
| Command | Purpose |
|---|---|
bun run blueprint:scan |
Scan code and update blueprint.json |
bun run blueprint:validate |
Check project against blueprint |
bun run blueprint:graph |
Build dependency graph |
| Command | Purpose |
|---|---|
bun run clean:dist |
Clean generated output |
bun run clean |
Full clean (including node_modules) |
Run in this order from the app directory:
bun run lint:dsl— DSL (If, Var, Loop instead of JS)bun run lint:gen— generator rules (apps/dsl only; skip in dsl-design)bun run validate— config and props (ui8kit.config.json must be present)bun run maintain:validate— invariants, fixtures, view-exports, contracts (maintain.config.json must be present)bun run maintain:check— full checker setbun run typecheck— TypeScriptbun run blueprint:scan— update blueprint.json from codebun run blueprint:validate— verify project matches blueprint- If blocks/templates/fixtures changed —
bun run generatethenbun run finalize - Verify generated app —
cd ../react-{APP_NAME} && bun run typecheck - If
utility-props.map.tschanged —bun run build:mapthenbun run maintain:props
Or run the full pipeline at once: bun run dist:app. See WORKFLOW.md and .project/PLAYBOOK.md for the stage-by-stage tracker.
| Error | Fix |
|---|---|
gap="5" invalid |
Use gap="4" or gap="6" |
fontSize="huge" invalid |
Use fontSize="4xl" |
bg="red" invalid |
Use bg="destructive" or another token |
DSL: use <Loop> instead of .map |
Replace .map() with <Loop> |
DSL: use <If> instead of && |
Replace && with <If> |
| Block does not allow tag "div" | Block — only section, article, header, nav, main, footer, aside, figure, address, form |
| Text does not allow tag "div" | Text — only p, h1–h6, span, label, cite, q, etc. (see component-tag-map) |
| Box/Stack/Group + form control | input, textarea, select, button — only in Field |
Allowed tags map: shipped with @ui8kit/generator. Used by HtmlConverterService (HTML generation), ui8kit-validate, and Maintain checker componentTag.
The generator turns DSL components into:
- React — plain JSX without If/Var/Loop (in
../reactor../react-design) - HTML+CSS — for static platforms (Shopify, WordPress, etc.)
Generation config — ui8kit.config.json (or dist config) in the app root. After bun run generate + bun run finalize you can run the generated app:
cd ../react
bun install
bun run dev// Wrong
<Title>Welcome to RestA</Title>
<Text>Our menu items</Text>
// Correct
<Title><Var name="title" value={title} /></Title>
<Text><Var name="subtitle" value={subtitle} /></Text>// Wrong
<div className="flex gap-4">
<Box style={{ padding: "16px" }}>
// Correct
<Group gap="4">
<Stack p="4">// Wrong
<div><h1>Title</h1><p>Text</p></div>
// Correct
<Block><Title>Title</Title><Text>Text</Text></Block>// Wrong
{items.map(i => <Card key={i.id}>...</Card>)}
{show && <Modal />}
// Correct
<Loop each="items" as="item" data={items}>
{(item) => <Card key={item.id}>...</Card>}
</Loop>
<If test="show" value={show}><Modal /></If>// Wrong
<Block component="section">
<Stack>
// Correct
<Block component="section" data-class="hero-section">
<Stack data-class="hero-content">// Wrong — Stack mimicking Card
<Stack gap="4" p="4" rounded="lg" bg="card" border="" data-class="card">
// Correct — use Card
<Card data-class="feature-card">
<CardHeader />
<CardContent />
</Card>Task: Add a new “Featured Items” block to the landing page.
fixtures/landing.json:
{
"title": "Welcome to RestA",
"subtitle": "...",
"ctaText": "View Menu",
"ctaUrl": "/menu",
"secondaryCtaText": "Our Recipes",
"secondaryCtaUrl": "/recipes",
"featuredItems": [
{ "id": "1", "title": "Seasonal Dish", "description": "Fresh ingredients" },
{ "id": "2", "title": "Chef Special", "description": "Today's highlight" }
]
}src/data/adapters/types.ts or src/types/ — add type for featuredItems.
src/blocks/FeaturedItemsBlock.tsx:
import { Block, Grid, Card, CardHeader, CardTitle, CardDescription } from '@ui8kit/core';
import { Loop, Var } from '@ui8kit/dsl';
export interface FeaturedItemsBlockProps {
items?: { id: string; title: string; description: string }[];
}
export function FeaturedItemsBlock({ items }: FeaturedItemsBlockProps) {
return (
<Block component="section" py="16" bg="muted" data-class="featured-section">
<Grid grid="cols-2" gap="6" data-class="featured-grid">
<Loop each="items" as="item" data={items ?? []}>
{(item: { id: string; title: string; description: string }) => (
<Card key={item.id} data-class="featured-card">
<CardHeader>
<CardTitle order={4} data-class="featured-card-title">
<Var name="item.title" value={item.title} />
</CardTitle>
<CardDescription data-class="featured-card-description">
<Var name="item.description" value={item.description} />
</CardDescription>
</CardHeader>
</Card>
)}
</Loop>
</Grid>
</Block>
);
}src/blocks/index.ts:
export { FeaturedItemsBlock } from './FeaturedItemsBlock';src/blocks/landing/PageView.tsx (or equivalent):
import { HeroBlock, FeaturedItemsBlock } from '@/blocks';
// Add featuredItems to props
// In return add:
<FeaturedItemsBlock items={landing.featuredItems} />Ensure landing in context includes featuredItems. Check fixtures.adapter.ts and types.
bun run lint:dsl
bun run validate
bun run maintain:validate
bun run maintain:check
bun run typecheck
bun run blueprint:scan
bun run blueprint:validate-
bun run lint:dsl— DSL check -
bun run lint:gen— generator lint (apps/dsl only) -
bun run validate— config, props, component+tag -
bun run maintain:validate— validate set from app’s package.json -
bun run typecheck— TypeScript - If blocks changed —
bun run generate(andbun run finalizeif needed) - If
src/lib/utility-props.map.tschanged —bun run build:mapandbun run maintain:props - No hardcode — all data from context or props
- No
classNameorstyle - All semantic elements have
data-class -
componentuses only allowed tags (see component-tag-map) - Use If, Var, Loop instead of JS conditionals and loops
- Comments in English
- WORKFLOW.md — short command sequence (setup → pre-commit)
- CLI_COMMANDS.md — full CLI reference (maintain, ui8kit-generate)
- .project/PLAYBOOK.md — universal pipeline tracker (Bootstrap → Release); paste into agent chat to check progress
.cursor/rules/best-practices.mdc— code rules.cursor/rules/engine-dsl-enforcement.mdc— DSL rules.cursor/rules/project-structure.mdc— project structure.cursor/rules/ui8kit-architecture.mdc— UI8Kit architecturenode_modules/@ui8kit/maintain/schemas/maintain.config.schema.json— maintain.config.json schemaapps/dsl/maintain.config.json— reference maintain config with all checkersapps/dsl/blueprint.json— reference blueprint with full entity/layout/partial shape@ui8kit/generator(dist/lib/component-tag-map.json) — component → allowed tag map
Document: ONBOARDING-101. International (EN). Last updated: 2026-03.