
Ui Design System
- 91 installs
- 8 repo stars
- Updated February 6, 2026
- hieutrtr/ai1-skills
Generates consistent React/TypeScript UI components and design tokens following an 8pt grid, Tailwind tokens, shadcn/ui, and WCAG 2.1 AA.
About
Enforces spacing, color, typography, and accessibility standards across React/TypeScript projects, generating components, layouts, and design tokens. A developer uses it when creating new UI components or reviewing UI for design consistency.
- 8pt spacing grid, Tailwind tokens, and shadcn/ui primitives
- WCAG 2.1 AA compliance and responsive breakpoints
Ui Design System by the numbers
- 91 all-time installs (skills.sh)
- +3 installs in the week ending Aug 2, 2026 (Skillselion tracking)
- Ranked #1,156 of 1,880 Design & UI/UX skills by installs in the Skillselion catalog
- Data as of Aug 2, 2026 (Skillselion catalog sync)
npx skills add https://github.com/hieutrtr/ai1-skills --skill ui-design-systemAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 91 |
|---|---|
| repo stars | ★ 8 |
| Last updated | February 6, 2026 |
| Repository | hieutrtr/ai1-skills ↗ |
What it does
Generates consistent React/TypeScript UI components and design tokens following an 8pt grid, Tailwind tokens, shadcn/ui, and WCAG 2.1 AA.
Files
UI Design System
When to Use
Activate this skill when:
- Creating new UI components that must follow a design system
- Building page layouts with consistent spacing and structure
- Setting up or extending design tokens (colors, typography, spacing)
- Choosing colors, fonts, or spacing values for a project
- Reviewing UI code for design consistency and accessibility
- Integrating shadcn/ui components into existing layouts
Do NOT use this skill for:
- Backend API implementation (use
python-backend-expert) - Component or hook testing (use
react-testing-patterns) - E2E browser testing (use
e2e-testing) - General React patterns unrelated to design system (use
react-frontend-expert) - Deployment or CI/CD (use
deployment-pipeline)
Instructions
Step 0: Read Existing Design Tokens
Before generating any UI code, check the project for existing tokens:
1. Read tailwind.config.ts (or .js) for custom theme extensions 2. Read src/styles/globals.css or app/globals.css for CSS custom properties 3. Read components.json if shadcn/ui is configured
If no design tokens exist, generate a starter set and ask the user to confirm before proceeding (see Edge Cases).
Design Tokens
Color Tokens
Define colors as CSS custom properties consumed by Tailwind. Never use hardcoded hex/rgb values in components.
CSS custom properties (HSL format for shadcn/ui compatibility):
/* globals.css */
@layer base {
:root {
--background: 0 0% 100%;
--foreground: 222 47% 11%;
--primary: 221 83% 53%;
--primary-foreground: 210 40% 98%;
--secondary: 210 40% 96%;
--secondary-foreground: 222 47% 11%;
--muted: 210 40% 96%;
--muted-foreground: 215 16% 47%;
--accent: 210 40% 96%;
--accent-foreground: 222 47% 11%;
--destructive: 0 84% 60%;
--destructive-foreground: 210 40% 98%;
--border: 214 32% 91%;
--input: 214 32% 91%;
--ring: 221 83% 53%;
--radius: 0.5rem;
}
.dark {
--background: 222 47% 11%;
--foreground: 210 40% 98%;
--primary: 217 91% 60%;
--primary-foreground: 222 47% 11%;
--secondary: 217 33% 17%;
--secondary-foreground: 210 40% 98%;
--muted: 217 33% 17%;
--muted-foreground: 215 20% 65%;
--accent: 217 33% 17%;
--accent-foreground: 210 40% 98%;
--destructive: 0 63% 31%;
--destructive-foreground: 210 40% 98%;
--border: 217 33% 17%;
--input: 217 33% 17%;
--ring: 224 76% 48%;
}
}Tailwind config mapping:
// tailwind.config.ts
export default {
theme: {
extend: {
colors: {
background: "hsl(var(--background))",
foreground: "hsl(var(--foreground))",
primary: {
DEFAULT: "hsl(var(--primary))",
foreground: "hsl(var(--primary-foreground))",
},
secondary: {
DEFAULT: "hsl(var(--secondary))",
foreground: "hsl(var(--secondary-foreground))",
},
muted: {
DEFAULT: "hsl(var(--muted))",
foreground: "hsl(var(--muted-foreground))",
},
accent: {
DEFAULT: "hsl(var(--accent))",
foreground: "hsl(var(--accent-foreground))",
},
destructive: {
DEFAULT: "hsl(var(--destructive))",
foreground: "hsl(var(--destructive-foreground))",
},
border: "hsl(var(--border))",
input: "hsl(var(--input))",
ring: "hsl(var(--ring))",
},
},
},
} satisfies Config;Color usage rules:
- Always use semantic token classes:
bg-primary,text-foreground,border-border - Never use raw Tailwind palette colors (
bg-blue-500) in component code - Every color must have a dark mode variant defined
- Use
foregroundvariants for text on colored backgrounds
Typography Scale
Define a typographic scale using Tailwind's font-size utilities:
| Token | Size | Line Height | Usage |
|---|---|---|---|
text-xs | 12px | 16px | Captions, helper text |
text-sm | 14px | 20px | Secondary text, labels |
text-base | 16px | 24px | Body text (default) |
text-lg | 18px | 28px | Subheadings |
text-xl | 20px | 28px | Section headings |
text-2xl | 24px | 32px | Page headings |
text-3xl | 30px | 36px | Hero headings |
Typography rules:
- Set a base font in
tailwind.config.ts:fontFamily: { sans: ["Inter", "system-ui", "sans-serif"] } - Use
font-medium(500) for headings and labels,font-normal(400) for body - Use
tracking-tightfor headingstext-2xland above - Limit line length with
max-w-prose(65ch) for readability
Spacing (8pt Grid)
All spacing values follow an 8pt base grid:
| Tailwind Class | Value | Use Case |
|---|---|---|
p-1 / gap-1 | 4px | Inline icon padding, tight gaps |
p-2 / gap-2 | 8px | Compact element spacing |
p-3 / gap-3 | 12px | Input padding, small card padding |
p-4 / gap-4 | 16px | Standard component padding |
p-6 / gap-6 | 24px | Card padding, section gaps |
p-8 / gap-8 | 32px | Section padding |
p-12 / gap-12 | 48px | Page section spacing |
p-16 / gap-16 | 64px | Major layout spacing |
Spacing rules:
- Use
gap-*for flex/grid children instead of individual margins - Prefer
space-y-*for vertical stacking of sibling elements - Cards:
p-6padding withgap-4between internal elements - Page sections:
py-12orpy-16vertical padding - Never mix spacing systems (no
margin: 13px)
Component Structure
Hierarchy: Container > Layout > Content
Every component follows a three-layer structure:
// Container: outer wrapper with spacing, background, border
<Card className="p-6">
{/* Layout: flex/grid arrangement */}
<div className="flex items-center gap-4">
{/* Content: actual UI elements */}
<Avatar src={user.avatar} alt={user.name} />
<div className="space-y-1">
<h3 className="text-sm font-medium">{user.name}</h3>
<p className="text-sm text-muted-foreground">{user.role}</p>
</div>
</div>
</Card>Semantic HTML
Use the correct HTML element for every purpose:
| Element | Use For | Not |
|---|---|---|
<button> | Clickable actions | <div onClick> |
<a> | Navigation links | <button> for links |
<nav> | Navigation regions | <div> |
<main> | Primary page content | <div> |
<article> | Self-contained content (card, post) | <div> |
<section> | Thematic grouping with heading | <div> |
<aside> | Sidebar or tangential content | <div> |
<header> | Introductory content for a section | <div> |
<footer> | Footer content for a section | <div> |
<ul> / <ol> | Lists of items | <div> for each item |
shadcn/ui Primitives
Prefer shadcn/ui components over custom implementations:
| Need | Use | Not |
|---|---|---|
| Buttons | <Button> | Custom <button> with styles |
| Modals | <Dialog> | Custom modal with portal |
| Dropdowns | <DropdownMenu> | Custom dropdown |
| Cards | <Card> | Styled <div> |
| Inputs | <Input> | Styled <input> |
| Selects | <Select> | Native <select> |
| Tooltips | <Tooltip> | Custom tooltip |
| Tabs | <Tabs> | Custom tab component |
| Tables | <Table> | Plain <table> |
| Alerts | <Alert> | Custom banner div |
If shadcn/ui is not installed, fall back to plain Tailwind with equivalent patterns and consistent class ordering.
TypeScript Component Interfaces
Export props as TypeScript interfaces with JSDoc descriptions:
/** Props for the UserProfileCard component. */
interface UserProfileCardProps {
/** User data to display. */
user: User;
/** Called when the edit button is clicked. */
onEdit?: (userId: string) => void;
/** Visual variant of the card. */
variant?: "default" | "compact";
/** Additional CSS classes applied to the root element. */
className?: string;
}
export function UserProfileCard({
user,
onEdit,
variant = "default",
className,
}: UserProfileCardProps) {
// ...
}Props rules:
- Name interface
{ComponentName}Props - Include
className?: stringon every component for composition - Use
variantprop for visual variations, not separate components - Default optional props in destructuring, not in interface
- Use union types for constrained string values:
"sm" | "md" | "lg"
Responsive Design
Breakpoints
Use Tailwind's mobile-first breakpoints:
| Prefix | Min Width | Target |
|---|---|---|
| (none) | 0px | Mobile (default) |
sm: | 640px | Large phones / small tablets |
md: | 768px | Tablets |
lg: | 1024px | Desktops |
xl: | 1280px | Large desktops |
Responsive rules:
- Design mobile-first: base styles for mobile, then add breakpoint overrides
- Use
grid-cols-1 md:grid-cols-2 lg:grid-cols-3for responsive grids - Stack navigation vertically on mobile:
flex-col md:flex-row - Hide non-essential elements on mobile:
hidden md:block - Set max container width:
max-w-7xl mx-auto px-4 sm:px-6 lg:px-8
Responsive Layout Pattern
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
{items.map((item) => (
<ItemCard key={item.id} item={item} />
))}
</div>
</div>Accessibility (WCAG 2.1 AA)
Contrast Ratios
- Normal text (< 18px or < 14px bold): minimum 4.5:1 contrast ratio
- Large text (>= 18px or >= 14px bold): minimum 3:1 contrast ratio
- UI components and graphical objects: minimum 3:1 contrast ratio
Interactive Element Requirements
Every interactive element must have:
1. Visible label or aria-label: <Button aria-label="Close dialog">X</Button> 2. Focus indicator: Tailwind's ring utilities: focus-visible:ring-2 focus-visible:ring-ring 3. Keyboard access: Reachable via Tab, activatable via Enter/Space 4. Disabled state: Both visual and aria-disabled or disabled attribute
ARIA Patterns
- Icon-only buttons:
aria-label="Delete item" - Loading states:
aria-busy="true"on the loading container - Dynamic content updates:
aria-live="polite"on the container - Form errors:
aria-invalid="true"on the input,role="alert"on the message - Modals:
aria-modal="true", focus trap, Escape to close - Navigation landmarks:
<nav aria-label="Main navigation">
Examples
User Profile Card
interface UserProfileCardProps {
user: { id: string; name: string; role: string; avatarUrl: string };
onEdit?: (userId: string) => void;
className?: string;
}
export function UserProfileCard({ user, onEdit, className }: UserProfileCardProps) {
return (
<Card className={cn("p-6", className)}>
<div className="flex items-center gap-4">
<Avatar className="h-12 w-12">
<AvatarImage src={user.avatarUrl} alt={user.name} />
<AvatarFallback>{user.name.charAt(0)}</AvatarFallback>
</Avatar>
<div className="space-y-1">
<h3 className="text-sm font-medium leading-none">{user.name}</h3>
<p className="text-sm text-muted-foreground">{user.role}</p>
</div>
</div>
{onEdit && (
<div className="mt-4">
<Button
variant="outline"
size="sm"
onClick={() => onEdit(user.id)}
aria-label={`Edit ${user.name}'s profile`}
>
Edit Profile
</Button>
</div>
)}
</Card>
);
}SaaS Dashboard Design Tokens Setup
Tailwind config extension:
// tailwind.config.ts
import type { Config } from "tailwindcss";
export default {
theme: {
extend: {
colors: {
background: "hsl(var(--background))",
foreground: "hsl(var(--foreground))",
primary: {
DEFAULT: "hsl(var(--primary))",
foreground: "hsl(var(--primary-foreground))",
},
secondary: {
DEFAULT: "hsl(var(--secondary))",
foreground: "hsl(var(--secondary-foreground))",
},
muted: {
DEFAULT: "hsl(var(--muted))",
foreground: "hsl(var(--muted-foreground))",
},
accent: {
DEFAULT: "hsl(var(--accent))",
foreground: "hsl(var(--accent-foreground))",
},
destructive: {
DEFAULT: "hsl(var(--destructive))",
foreground: "hsl(var(--destructive-foreground))",
},
border: "hsl(var(--border))",
input: "hsl(var(--input))",
ring: "hsl(var(--ring))",
},
fontFamily: {
sans: ["Inter", "system-ui", "sans-serif"],
},
borderRadius: {
lg: "var(--radius)",
md: "calc(var(--radius) - 2px)",
sm: "calc(var(--radius) - 4px)",
},
},
},
} satisfies Config;Responsive Dashboard Layout
export function DashboardLayout({ children }: { children: React.ReactNode }) {
return (
<div className="min-h-screen bg-background">
<nav className="border-b border-border" aria-label="Main navigation">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 h-16 flex items-center">
{/* nav content */}
</div>
</nav>
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
<div className="grid grid-cols-1 lg:grid-cols-[240px_1fr] gap-8">
<aside className="hidden lg:block" aria-label="Sidebar">
{/* sidebar content */}
</aside>
<main>{children}</main>
</div>
</div>
</div>
);
}Edge Cases
- No existing design tokens: Generate a starter token set (see
references/design-tokens-reference.md) and present it to the user for confirmation before writing any component code. Ask: "No design tokens found. Here's a starter set — should I apply these?"
- shadcn/ui not installed: Fall back to plain Tailwind with equivalent patterns. Use
<button className="...">instead of<Button>, styled<div>instead of<Card>. Maintain the same spacing and color token approach.
- Component overlap: If a requested component duplicates an existing one, flag it: "A similar
UserCardcomponent exists atsrc/components/UserCard.tsx. Should I extend it or create a separate component?"
- Dark mode tokens missing: If
:roottokens exist but.darkvariants are absent, generate matching dark variants before proceeding. Every semantic color token must have both light and dark values.
- Custom brand colors: When the user provides specific brand hex values, convert them to HSL and integrate into the token system. Never use the hex values directly in components.
- Inconsistent spacing in existing code: Flag the inconsistency, suggest the closest 8pt grid values, and ask whether to normalize existing components or only apply the grid to new code.
See references/design-tokens-reference.md for starter token sets, color palette guide, and typography scales.
Design Tokens Reference
Starter token sets, color palettes, and typography scales for the ui-design-system skill.
Starter Token Sets
Neutral SaaS (Default)
A clean, professional palette suitable for dashboards and B2B applications.
:root {
/* Background & Foreground */
--background: 0 0% 100%;
--foreground: 222 47% 11%;
/* Brand */
--primary: 221 83% 53%;
--primary-foreground: 210 40% 98%;
/* Secondary */
--secondary: 210 40% 96%;
--secondary-foreground: 222 47% 11%;
/* Muted */
--muted: 210 40% 96%;
--muted-foreground: 215 16% 47%;
/* Accent */
--accent: 210 40% 96%;
--accent-foreground: 222 47% 11%;
/* Destructive */
--destructive: 0 84% 60%;
--destructive-foreground: 210 40% 98%;
/* Borders & Inputs */
--border: 214 32% 91%;
--input: 214 32% 91%;
--ring: 221 83% 53%;
/* Radius */
--radius: 0.5rem;
}
.dark {
--background: 222 47% 11%;
--foreground: 210 40% 98%;
--primary: 217 91% 60%;
--primary-foreground: 222 47% 11%;
--secondary: 217 33% 17%;
--secondary-foreground: 210 40% 98%;
--muted: 217 33% 17%;
--muted-foreground: 215 20% 65%;
--accent: 217 33% 17%;
--accent-foreground: 210 40% 98%;
--destructive: 0 63% 31%;
--destructive-foreground: 210 40% 98%;
--border: 217 33% 17%;
--input: 217 33% 17%;
--ring: 224 76% 48%;
}Warm Consumer
A friendlier palette for consumer-facing products.
:root {
--background: 0 0% 100%;
--foreground: 20 14% 4%;
--primary: 24 95% 53%;
--primary-foreground: 60 9% 98%;
--secondary: 60 5% 96%;
--secondary-foreground: 24 10% 10%;
--muted: 60 5% 96%;
--muted-foreground: 25 5% 45%;
--accent: 60 5% 96%;
--accent-foreground: 24 10% 10%;
--destructive: 0 84% 60%;
--destructive-foreground: 60 9% 98%;
--border: 20 6% 90%;
--input: 20 6% 90%;
--ring: 24 95% 53%;
--radius: 0.75rem;
}
.dark {
--background: 20 14% 4%;
--foreground: 60 9% 98%;
--primary: 20 90% 48%;
--primary-foreground: 60 9% 98%;
--secondary: 12 6% 15%;
--secondary-foreground: 60 9% 98%;
--muted: 12 6% 15%;
--muted-foreground: 24 5% 64%;
--accent: 12 6% 15%;
--accent-foreground: 60 9% 98%;
--destructive: 0 63% 31%;
--destructive-foreground: 60 9% 98%;
--border: 12 6% 15%;
--input: 12 6% 15%;
--ring: 20 90% 48%;
}Minimal Dark-First
A dark-first palette for developer tools and technical products.
:root {
--background: 0 0% 4%;
--foreground: 0 0% 95%;
--primary: 142 71% 45%;
--primary-foreground: 144 80% 10%;
--secondary: 0 0% 15%;
--secondary-foreground: 0 0% 95%;
--muted: 0 0% 15%;
--muted-foreground: 0 0% 64%;
--accent: 0 0% 15%;
--accent-foreground: 0 0% 95%;
--destructive: 0 63% 31%;
--destructive-foreground: 0 0% 95%;
--border: 0 0% 15%;
--input: 0 0% 15%;
--ring: 142 71% 45%;
--radius: 0.375rem;
}Color Palette Guide
Semantic Token Mapping
| Token | Purpose | Example Usage |
|---|---|---|
background | Page and card backgrounds | bg-background |
foreground | Primary text color | text-foreground |
primary | Brand actions (buttons, links) | bg-primary text-primary-foreground |
secondary | Secondary actions, tags | bg-secondary text-secondary-foreground |
muted | Subtle backgrounds, disabled states | bg-muted text-muted-foreground |
accent | Hover states, highlights | bg-accent text-accent-foreground |
destructive | Delete, error, danger | bg-destructive text-destructive-foreground |
border | Borders, dividers | border-border |
input | Form input borders | border-input |
ring | Focus rings | ring-ring |
Adding Custom Semantic Colors
When a project needs additional semantic colors (e.g., success, warning, info):
:root {
--success: 142 76% 36%;
--success-foreground: 0 0% 100%;
--warning: 38 92% 50%;
--warning-foreground: 0 0% 0%;
--info: 199 89% 48%;
--info-foreground: 0 0% 100%;
}Add to Tailwind config:
colors: {
success: {
DEFAULT: "hsl(var(--success))",
foreground: "hsl(var(--success-foreground))",
},
warning: {
DEFAULT: "hsl(var(--warning))",
foreground: "hsl(var(--warning-foreground))",
},
info: {
DEFAULT: "hsl(var(--info))",
foreground: "hsl(var(--info-foreground))",
},
}Hex to HSL Conversion
When given brand hex values, convert to HSL for token integration:
| Hex | HSL | Token |
|---|---|---|
#2563EB | 221 83% 53% | --primary |
#DC2626 | 0 84% 60% | --destructive |
#16A34A | 142 76% 36% | --success |
#F59E0B | 38 92% 50% | --warning |
Typography Scales
Default Scale (Inter)
// tailwind.config.ts
fontFamily: {
sans: ["Inter", "system-ui", "sans-serif"],
},| Element | Classes | Rendered |
|---|---|---|
| Page title | text-3xl font-bold tracking-tight | 30px, bold |
| Section heading | text-2xl font-semibold tracking-tight | 24px, semibold |
| Card heading | text-lg font-medium | 18px, medium |
| Body | text-base | 16px, normal |
| Label | text-sm font-medium | 14px, medium |
| Caption | text-xs text-muted-foreground | 12px, muted |
Monospace Scale (for code/developer tools)
fontFamily: {
sans: ["Inter", "system-ui", "sans-serif"],
mono: ["JetBrains Mono", "Fira Code", "monospace"],
},Use font-mono for code blocks, terminal output, and data tables with numeric alignment.
Spacing Quick Reference
4px = p-1, gap-1, m-1 (icon padding, tight inline gaps)
8px = p-2, gap-2, m-2 (compact spacing)
12px = p-3, gap-3, m-3 (input padding)
16px = p-4, gap-4, m-4 (standard component gap)
24px = p-6, gap-6, m-6 (card padding, section gap)
32px = p-8, gap-8, m-8 (section padding)
48px = p-12, gap-12, m-12 (page section vertical spacing)
64px = p-16, gap-16, m-16 (major layout sections)Common Component Spacing
| Component | Padding | Internal Gap | Margin |
|---|---|---|---|
| Button | px-4 py-2 | gap-2 (icon + text) | — |
| Card | p-6 | gap-4 | — |
| Form field | — | gap-2 (label + input) | space-y-4 between fields |
| Modal | p-6 | gap-4 | — |
| Page section | py-12 | gap-6 | — |
| Table cell | px-4 py-3 | — | — |
| Navbar | px-4 sm:px-6 lg:px-8 | gap-4 | h-16 |