UI Design System Enforcement
This skill enforces consistency across all application UI. It is the default for all app interface work — dashboards, forms, settings, CRUD, data display. For creative/marketing work, use ultra-frontend instead.
Hard Rules
- shadcn-svelte first. Never build custom components when shadcn-svelte has one. Before creating anything, check
src/lib/components/ui/ for existing components.
- Theme tokens only. Use CSS variables (
bg-primary, text-muted-foreground, border-border) — never raw Tailwind colors (bg-blue-500, text-gray-500). See the Color section below.
- Scan before building. Before creating any new page or component, find and read an existing similar one in the project. Match its structure, spacing, and patterns. State what you're matching.
- One way to do things. If the project already does X a certain way, do it that way. Don't introduce a second pattern.
- Svelte 5 only. Use
$state, $derived, $bindable, $props, snippets. No legacy Svelte 4 patterns (stores, export let, slots).
Pre-Build Checklist
Before writing any UI code:
- Glob for similar existing pages/components in the project
- Read at least one to understand established patterns
- State which patterns you're following and why
- Check
src/lib/components/ui/ for relevant shadcn components
- If no similar code exists, state the conventions you'll establish and why
Colors & Theme Tokens
Always Use Semantic Tokens
bg-background, bg-foreground
bg-primary, text-primary, bg-primary-foreground
bg-secondary, text-secondary
bg-muted, text-muted-foreground
bg-accent, text-accent-foreground
bg-destructive, text-destructive
bg-success, text-success
border-border, border-input
ring-ring
Status Colors — Use Semantic Tokens with Opacity
<!-- CORRECT -->
<span class="bg-success/10 text-success">Active</span>
<span class="bg-destructive/10 text-destructive">Failed</span>
<span class="bg-warning/10 text-warning">Pending</span>
<span class="bg-muted text-muted-foreground">Draft</span>
<!-- WRONG — raw Tailwind colors -->
<span class="bg-green-100 text-green-700">Active</span>
<span class="bg-red-100 text-red-700">Failed</span>
<span class="bg-amber-100 text-amber-700">Pending</span>
Exceptions
- Email/brand preview components that render non-themed UI may use hardcoded colors
- Charts may use
--chart-1 through --chart-5 tokens or raw colors for data series differentiation
- Dynamic brand colors from database/API (e.g., customer portal branding) use inline
style: bindings
Layout Conventions
Dashboard Content Wrapper
The dashboard layout provides p-4 padding. Pages within it follow this structure:
<div class="flex flex-1 flex-col overflow-auto">
<div class="w-full px-6 lg:px-8 py-6 space-y-6">
<!-- Page content -->
</div>
</div>
Max-Width by Page Type
| Page Type |
Max-Width |
Example |
| Forms / Settings |
max-w-3xl mx-auto |
Profile settings, edit forms |
| Detail pages |
max-w-6xl mx-auto |
Organization page, subscription detail |
| Data tables / Dashboards |
No max-width |
Subscriptions list, dashboard overview |
| Catalog / Grid views |
max-w-7xl mx-auto or max-w-screen-xl mx-auto |
Product catalog, feature grid |
Section Spacing
- Between major page sections:
space-y-6
- Grid gaps between cards/items:
gap-6
- Tight item groups (form fields, button groups, related items):
gap-4 or space-y-4
- Within a component (label to input, icon to text):
gap-2 or space-y-2
Responsive Breakpoints
md: (768px) — two-column layouts
lg: (1024px) — three-column layouts, sidebar shows
- Use
@container queries when component responsiveness is independent of viewport
Component Patterns
Button Variants
| Variant |
Use Case |
default |
Primary action (one per visible area) |
outline |
Secondary actions (most common) |
ghost |
Tertiary/icon-only actions |
destructive |
Danger/delete actions |
secondary |
Alternative secondary styling |
link |
Text-only navigation-style actions |
Button Sizes
| Size |
Use Case |
default (h-9) |
Standard buttons |
sm (h-8) |
Toolbars, table rows, compact areas |
lg (h-10) |
Hero/primary call-to-action |
icon (h-9 w-9) |
Icon-only buttons |
icon-sm (h-8 w-8) |
Compact icon buttons |
Icons
- In menus/dropdowns:
h-4 w-4 with mr-2 spacing
- Standalone/page-level:
h-5 w-5
- Feature/empty-state illustrations:
h-6 w-6 or larger
- Source:
@tabler/icons-svelte (project standard)
Feedback Patterns
| Feedback Type |
Component |
When |
| Async operation result |
toast.success() / toast.error() |
After API calls, saves, sends |
| Reversible confirmation |
ConfirmationModal (variant="default") |
Pause, resume, send |
| Destructive confirmation |
ConfirmationModal (variant="destructive") |
Delete, cancel, disable |
| Complex confirmation |
Custom Dialog with options |
Actions requiring reason selection |
| Creation flow |
Sheet with step tabs |
Creating new entities |
| Inline editing |
Direct in-page form |
Quick field edits |
Data Display
| Pattern |
Component |
When |
| Tabular data |
TanStack Table + shadcn Table primitives |
Lists with sorting/selection/pagination |
| Metric cards |
Card with header + content |
Dashboard KPIs |
| Detail view |
DetailPageLayout with sidebar |
Entity detail pages |
| Empty state |
Empty.Root + Empty.Title + Empty.Description |
No data / first-time use |
| Loading |
Skeleton components with staggered animation |
Initial data load |
| Loading overlay |
InlineLoadingOverlay |
Refreshing existing data |
| Status indicators |
Badge with semantic token colors |
Row status, entity state |
Dropdown Menus
<DropdownMenu.Root bind:open={dropdownOpen}>
<DropdownMenu.Trigger>
{#snippet child({ props })}
<Button variant="ghost" size="icon" {...props}>
<DotsVerticalIcon />
<span class="sr-only">Open menu</span>
</Button>
{/snippet}
</DropdownMenu.Trigger>
<DropdownMenu.Content align="end" class="w-48">
<DropdownMenu.Group>
<!-- Standard actions first -->
<DropdownMenu.Item>
<EyeIcon class="mr-2 h-4 w-4" />View Details
</DropdownMenu.Item>
<DropdownMenu.Separator />
<!-- Destructive actions last -->
<DropdownMenu.Item variant="destructive">
<TrashIcon class="mr-2 h-4 w-4" />Delete
</DropdownMenu.Item>
</DropdownMenu.Group>
</DropdownMenu.Content>
</DropdownMenu.Root>
Form Patterns
When to Use Which
| Context |
Pattern |
Validation |
| Simple inline (login, quick edit) |
Native $state + manual |
Inline checks |
| Creation in Sheet/Dialog |
State object + errorsByPath |
Zod schema, submit-first |
| Settings pages |
Store-based + updateField() |
Store-level validation |
| Server-validated |
Formsnap + SvelteKit form actions |
Server-side + fail() |
Form Field Spacing
<!-- Standard form layout -->
<div class="grid gap-6 md:grid-cols-2">
<div class="space-y-2">
<Form.Field>
<Form.Control>
{#snippet children({ props })}
<Form.Label>Field Name</Form.Label>
<Input {...props} />
{/snippet}
</Form.Control>
<Form.Description>Helper text</Form.Description>
<Form.FieldErrors />
</Form.Field>
</div>
</div>
- Label to input:
space-y-2 (via Form.Field)
- Between form fields:
gap-6 (in grid) or space-y-4 (in stack)
- Form sections:
space-y-6
- Submit buttons: Full-width
w-full in modals/sheets, right-aligned in page forms
Page Headers
<!-- Standard page header -->
<div class="flex items-center justify-between">
<div>
<h1 class="text-2xl font-semibold tracking-tight">{title}</h1>
<p class="text-muted-foreground">{description}</p>
</div>
<div class="flex items-center gap-2">
<!-- Action buttons -->
</div>
</div>
What This Skill Does NOT Cover
- Creative/marketing UI — use ultra-frontend instead
- Architectural decisions (folder structure, service patterns, data layer)
- Business logic in components
- Accessibility beyond what shadcn provides (shadcn handles ARIA by default)
1---2name: ui-design-33description: Enforces consistent UI within shadcn-svelte + TailwindCSS 4 design system. Use when building ANY app UI — pages, components, forms, dashboards, settings, data display. Prevents style drift and ensures design system compliance.4---5
6# UI Design System Enforcement
7
8This skill enforces consistency across all application UI. It is the default for all app interface work — dashboards, forms, settings, CRUD, data display. For creative/marketing work, use ultra-frontend instead.
9
10## Hard Rules
11
121. **shadcn-svelte first.** Never build custom components when shadcn-svelte has one. Before creating anything, check `src/lib/components/ui/` for existing components.
132. **Theme tokens only.** Use CSS variables (`bg-primary`, `text-muted-foreground`, `border-border`) — never raw Tailwind colors (`bg-blue-500`, `text-gray-500`). See the Color section below.
143. **Scan before building.** Before creating any new page or component, find and read an existing similar one in the project. Match its structure, spacing, and patterns. State what you're matching.
154. **One way to do things.** If the project already does X a certain way, do it that way. Don't introduce a second pattern.
165. **Svelte 5 only.** Use `$state`, `$derived`, `$bindable`, `$props`, snippets. No legacy Svelte 4 patterns (stores, `export let`, slots).
17
18## Pre-Build Checklist
19
20Before writing any UI code:
21
221. **Glob** for similar existing pages/components in the project
232. **Read** at least one to understand established patterns
243. **State** which patterns you're following and why
254. **Check** `src/lib/components/ui/` for relevant shadcn components
265. If no similar code exists, state the conventions you'll establish and why
27
28## Colors & Theme Tokens
29
30### Always Use Semantic Tokens
31
32```
33bg-background, bg-foreground
34bg-primary, text-primary, bg-primary-foreground
35bg-secondary, text-secondary
36bg-muted, text-muted-foreground
37bg-accent, text-accent-foreground
38bg-destructive, text-destructive
39bg-success, text-success
40border-border, border-input
41ring-ring
42```
43
44### Status Colors — Use Semantic Tokens with Opacity
45
46```svelte
47<!-- CORRECT -->
48<span class="bg-success/10 text-success">Active</span>
49<span class="bg-destructive/10 text-destructive">Failed</span>
50<span class="bg-warning/10 text-warning">Pending</span>
51<span class="bg-muted text-muted-foreground">Draft</span>
52
53<!-- WRONG — raw Tailwind colors -->
54<span class="bg-green-100 text-green-700">Active</span>
55<span class="bg-red-100 text-red-700">Failed</span>
56<span class="bg-amber-100 text-amber-700">Pending</span>
57```
58
59### Exceptions
60
61- **Email/brand preview components** that render non-themed UI may use hardcoded colors
62- **Charts** may use `--chart-1` through `--chart-5` tokens or raw colors for data series differentiation
63- **Dynamic brand colors** from database/API (e.g., customer portal branding) use inline `style:` bindings
64
65## Layout Conventions
66
67### Dashboard Content Wrapper
68
69The dashboard layout provides `p-4` padding. Pages within it follow this structure:
70
71```svelte
72<div class="flex flex-1 flex-col overflow-auto">
73 <div class="w-full px-6 lg:px-8 py-6 space-y-6">
74 <!-- Page content -->
75 </div>
76</div>
77```
78
79### Max-Width by Page Type
80
81| Page Type | Max-Width | Example |
82|-----------|-----------|---------|
83| Forms / Settings | `max-w-3xl mx-auto` | Profile settings, edit forms |
84| Detail pages | `max-w-6xl mx-auto` | Organization page, subscription detail |
85| Data tables / Dashboards | No max-width | Subscriptions list, dashboard overview |
86| Catalog / Grid views | `max-w-7xl mx-auto` or `max-w-screen-xl mx-auto` | Product catalog, feature grid |
87
88### Section Spacing
89
90- **Between major page sections**: `space-y-6`
91- **Grid gaps between cards/items**: `gap-6`
92- **Tight item groups** (form fields, button groups, related items): `gap-4` or `space-y-4`
93- **Within a component** (label to input, icon to text): `gap-2` or `space-y-2`
94
95### Responsive Breakpoints
96
97- `md:` (768px) — two-column layouts
98- `lg:` (1024px) — three-column layouts, sidebar shows
99- Use `@container` queries when component responsiveness is independent of viewport
100
101## Component Patterns
102
103### Button Variants
104
105| Variant | Use Case |
106|---------|----------|
107| `default` | Primary action (one per visible area) |
108| `outline` | Secondary actions (most common) |
109| `ghost` | Tertiary/icon-only actions |
110| `destructive` | Danger/delete actions |
111| `secondary` | Alternative secondary styling |
112| `link` | Text-only navigation-style actions |
113
114### Button Sizes
115
116| Size | Use Case |
117|------|----------|
118| `default` (h-9) | Standard buttons |
119| `sm` (h-8) | Toolbars, table rows, compact areas |
120| `lg` (h-10) | Hero/primary call-to-action |
121| `icon` (h-9 w-9) | Icon-only buttons |
122| `icon-sm` (h-8 w-8) | Compact icon buttons |
123
124### Icons
125
126- **In menus/dropdowns**: `h-4 w-4` with `mr-2` spacing
127- **Standalone/page-level**: `h-5 w-5`
128- **Feature/empty-state illustrations**: `h-6 w-6` or larger
129- **Source**: `@tabler/icons-svelte` (project standard)
130
131### Feedback Patterns
132
133| Feedback Type | Component | When |
134|---------------|-----------|------|
135| Async operation result | `toast.success()` / `toast.error()` | After API calls, saves, sends |
136| Reversible confirmation | `ConfirmationModal` (variant="default") | Pause, resume, send |
137| Destructive confirmation | `ConfirmationModal` (variant="destructive") | Delete, cancel, disable |
138| Complex confirmation | Custom Dialog with options | Actions requiring reason selection |
139| Creation flow | `Sheet` with step tabs | Creating new entities |
140| Inline editing | Direct in-page form | Quick field edits |
141
142### Data Display
143
144| Pattern | Component | When |
145|---------|-----------|------|
146| Tabular data | TanStack Table + shadcn Table primitives | Lists with sorting/selection/pagination |
147| Metric cards | Card with header + content | Dashboard KPIs |
148| Detail view | DetailPageLayout with sidebar | Entity detail pages |
149| Empty state | Empty.Root + Empty.Title + Empty.Description | No data / first-time use |
150| Loading | Skeleton components with staggered animation | Initial data load |
151| Loading overlay | InlineLoadingOverlay | Refreshing existing data |
152| Status indicators | Badge with semantic token colors | Row status, entity state |
153
154### Dropdown Menus
155
156```svelte
157<DropdownMenu.Root bind:open={dropdownOpen}>
158 <DropdownMenu.Trigger>
159 {#snippet child({ props })}
160 <Button variant="ghost" size="icon" {...props}>
161 <DotsVerticalIcon />
162 <span class="sr-only">Open menu</span>
163 </Button>
164 {/snippet}
165 </DropdownMenu.Trigger>
166 <DropdownMenu.Content align="end" class="w-48">
167 <DropdownMenu.Group>
168 <!-- Standard actions first -->
169 <DropdownMenu.Item>
170 <EyeIcon class="mr-2 h-4 w-4" />View Details
171 </DropdownMenu.Item>
172 <DropdownMenu.Separator />
173 <!-- Destructive actions last -->
174 <DropdownMenu.Item variant="destructive">
175 <TrashIcon class="mr-2 h-4 w-4" />Delete
176 </DropdownMenu.Item>
177 </DropdownMenu.Group>
178 </DropdownMenu.Content>
179</DropdownMenu.Root>
180```
181
182## Form Patterns
183
184### When to Use Which
185
186| Context | Pattern | Validation |
187|---------|---------|------------|
188| Simple inline (login, quick edit) | Native `$state` + manual | Inline checks |
189| Creation in Sheet/Dialog | State object + `errorsByPath` | Zod schema, submit-first |
190| Settings pages | Store-based + `updateField()` | Store-level validation |
191| Server-validated | Formsnap + SvelteKit form actions | Server-side + `fail()` |
192
193### Form Field Spacing
194
195```svelte
196<!-- Standard form layout -->
197<div class="grid gap-6 md:grid-cols-2">
198 <div class="space-y-2">
199 <Form.Field>
200 <Form.Control>
201 {#snippet children({ props })}
202 <Form.Label>Field Name</Form.Label>
203 <Input {...props} />
204 {/snippet}
205 </Form.Control>
206 <Form.Description>Helper text</Form.Description>
207 <Form.FieldErrors />
208 </Form.Field>
209 </div>
210</div>
211```
212
213- **Label to input**: `space-y-2` (via Form.Field)
214- **Between form fields**: `gap-6` (in grid) or `space-y-4` (in stack)
215- **Form sections**: `space-y-6`
216- **Submit buttons**: Full-width `w-full` in modals/sheets, right-aligned in page forms
217
218## Page Headers
219
220```svelte
221<!-- Standard page header -->
222<div class="flex items-center justify-between">
223 <div>
224 <h1 class="text-2xl font-semibold tracking-tight">{title}</h1>
225 <p class="text-muted-foreground">{description}</p>
226 </div>
227 <div class="flex items-center gap-2">
228 <!-- Action buttons -->
229 </div>
230</div>
231```
232
233## What This Skill Does NOT Cover
234
235- **Creative/marketing UI** — use ultra-frontend instead
236- **Architectural decisions** (folder structure, service patterns, data layer)
237- **Business logic** in components
238- **Accessibility beyond what shadcn provides** (shadcn handles ARIA by default)