Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Create beautiful, accessible user interfaces with shadcn/ui components (built on Radix UI + Tailwind), Tailwind CSS utility-first styling, and canvas-based visual designs. Use when building user interfaces, implementing design systems, creating responsive layouts, adding accessible components (dialogs, dropdowns, forms, tables), customizing themes and colors, implementing dark mode, generating visual designs and posters, or establishing consistent styling patterns across applications.
.claude/skills/ckm-ui-styling/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-04 | ✗→✓ | ▲ Improved | 70% | 0% |
| case-08 | ✗→✓ | ▲ Improved | 1% | 0% |
| case-09 | ✗→✓ | ▲ Improved | 318% | 0% |
| case-10 | ✗→✓ | ▲ Improved | 83% | 0% |
| case-11 | ✗→✓ | ▲ Improved | 131% | 0% |
Comprehensive skill for creating beautiful, accessible user interfaces combining shadcn/ui components, Tailwind CSS utility styling, and canvas-based visual design systems.
Use when:
Script paths in this skill and its references/ are relative to the directory that contains this SKILL.md, not to the project: scripts/<file> is this skill's own scripts/ folder, and ../<skill>/scripts/<file> is a sibling sub-skill installed alongside it. Build the full path from that directory (Claude Code reports it as the skill's base directory when the skill loads) and keep the working directory at the project root — the scripts read and write project files such as docs/brand-guidelines.md, assets/design-tokens.json or src/ relative to it.
Install shadcn/ui with Tailwind:
bashnpx shadcn@latest init
CLI prompts for framework, TypeScript, paths, and theme preferences. This configures both shadcn/ui and Tailwind CSS.
Add components:
bashnpx shadcn@latest add button card dialog form
Use components with utility styling:
tsximport { Button } from "@/components/ui/button" import { Card, CardHeader, CardTitle, CardContent } from "@/components/ui/card" export function Dashboard() { return ( <div className="container mx-auto p-6 grid gap-6 md:grid-cols-2 lg:grid-cols-3"> <Card className="hover:shadow-lg transition-shadow"> <CardHeader> <CardTitle className="text-2xl font-bold">Analytics</CardTitle> </CardHeader> <CardContent className="space-y-4"> <p className="text-muted-foreground">View your metrics</p> <Button variant="default" className="w-full"> View Details </Button> </CardContent> </Card> </div> ) }
Vite projects:
bashnpm install -D tailwindcss @tailwindcss/vite
javascript// vite.config.ts import tailwindcss from '@tailwindcss/vite' export default { plugins: [tailwindcss()] }
css/* src/index.css */ @import "tailwindcss";
Comprehensive component catalog with usage patterns, installation, and composition examples.
See: references/shadcn-components.md
Covers:
Theme configuration, CSS variables, dark mode implementation, and component customization.
See: references/shadcn-theming.md
Covers:
ARIA patterns, keyboard navigation, screen reader support, and accessible component usage.
See: references/shadcn-accessibility.md
Covers:
Core utility classes for layout, spacing, typography, colors, borders, and shadows.
See: references/tailwind-utilities.md
Covers:
Mobile-first breakpoints, responsive utilities, and adaptive layouts.
See: references/tailwind-responsive.md
Covers:
Config file structure, custom utilities, plugins, and theme extensions.
See: references/tailwind-customization.md
Covers:
Canvas-based design philosophy, visual communication principles, and sophisticated compositions.
See: references/canvas-design-system.md
Covers:
Python automation for component installation and configuration generation.
Add shadcn/ui components with dependency handling:
bashpython scripts/shadcn_add.py button card dialog
Generate tailwind.config.js with custom theme:
bashpython scripts/tailwind_config_gen.py --colors brand:blue --fonts display:Inter
The generator refuses to create or replace a config when any sibling tailwind.config.js, .cjs, .mjs, or .ts file already exists. Review the reported config first, then pass --force only when the competing output is intentional:
bashpython scripts/tailwind_config_gen.py --colors brand:blue --force
Component Library
references/shadcn-components.md - Complete component catalogreferences/shadcn-theming.md - Theming and customizationreferences/shadcn-accessibility.md - Accessibility patternsStyling System
references/tailwind-utilities.md - Core utility classesreferences/tailwind-responsive.md - Responsive designreferences/tailwind-customization.md - Configuration and extensionsVisual Design
references/canvas-design-system.md - Design philosophy and canvas workflowsAutomation
scripts/shadcn_add.py - Component installationscripts/tailwind_config_gen.py - Config generationForm with validation:
tsximport { useForm } from "react-hook-form" import { zodResolver } from "@hookform/resolvers/zod" import * as z from "zod" import { Form, FormField, FormItem, FormLabel, FormControl, FormMessage } from "@/components/ui/form" import { Input } from "@/components/ui/input" import { Button } from "@/components/ui/button" const schema = z.object({ email: z.string().email(), password: z.string().min(8) }) export function LoginForm() { const form = useForm({ resolver: zodResolver(schema), defaultValues: { email: "", password: "" } }) return ( <Form {...form}> <form onSubmit={form.handleSubmit(console.log)} className="space-y-6"> <FormField control={form.control} name="email" render={({ field }) => ( <FormItem> <FormLabel>Email</FormLabel> <FormControl> <Input type="email" {...field} /> </FormControl> <FormMessage /> </FormItem> )} /> <Button type="submit" className="w-full">Sign In</Button> </form> </Form> ) }
Responsive layout with dark mode:
tsx<div className="min-h-screen bg-white dark:bg-gray-900"> <div className="container mx-auto px-4 py-8"> <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6"> <Card className="bg-white dark:bg-gray-800 border-gray-200 dark:border-gray-700"> <CardContent className="p-6"> <h3 className="text-xl font-semibold text-gray-900 dark:text-white"> Content </h3> </CardContent> </Card> </div> </div> </div>
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | fail→fail | 16,153 | 26,859 | +66% | 1 | 1 | 0% | 1,683 | 2,750 | +63% | 0 | 0 | — |
case-02 | fail→fail | 6,944 | 13,080 | +88% | 1 | 1 | 0% | 795 | 2,828 | +256% | 0 | 0 | — |
case-03 | fail→fail | 9,316 | 18,095 | +94% | 1 | 1 | 0% | 382 | 2,967 | +677% | 0 | 0 | — |
case-04 | fail→pass | 13,277 | 17,383 | +31% | 1 | 1 | 0% | 2,083 | 3,548 | +70% | 0 | 0 | — |
case-05 | pass→pass | 13,836 | 12,779 | -8% | 1 | 1 | 0% | 1,378 | 3,632 | +164% | 0 | 0 | — |
case-06 | pass→pass | 13,318 | 50,361 | +278% | 1 | 1 | 0% | 2,348 | 4,626 | +97% | 0 | 0 | — |
case-07 | pass→pass | 18,960 | 24,943 | +32% | 1 | 1 | 0% | 2,702 | 6,042 | +124% | 0 | 0 | — |
case-08 | fail→pass | 23,865 | 8,419 | -65% | 1 | 1 | 0% | 2,940 | 2,981 | +1% | 0 | 0 | — |
case-09 | fail→pass | 79,400 | 9,938 | -87% | 1 | 1 | 0% | 695 | 2,903 | +318% | 0 | 0 | — |
case-10 | fail→pass | 11,409 | 3,592 | -69% | 1 | 1 | 0% | 1,590 | 2,915 | +83% | 0 | 0 | — |
case-11 | fail→pass | 13,338 | 18,646 | +40% | 1 | 1 | 0% | 1,302 | 3,011 | +131% | 0 | 0 | — |
case-12 | pass→pass | 14,845 | 8,859 | -40% | 1 | 1 | 0% | 1,064 | 2,867 | +169% | 0 | 0 | — |
case-13 | fail→pass | 21,631 | 9,082 | -58% | 1 | 1 | 0% | 1,492 | 3,078 | +106% | 0 | 0 | — |
case-14 | fail→pass | 17,773 | 20,550 | +16% | 1 | 1 | 0% | 1,968 | 2,802 | +42% | 0 | 0 | — |
case-15 | pass→pass | 12,520 | 18,143 | +45% | 1 | 1 | 0% | 2,073 | 4,645 | +124% | 0 | 0 | — |
case-16 | pass→pass | 14,319 | 11,154 | -22% | 1 | 1 | 0% | 1,294 | 3,417 | +164% | 0 | 0 | — |
case-17 | fail→pass | 24,736 | 3,333 | -87% | 1 | 1 | 0% | 1,494 | 2,877 | +93% | 0 | 0 | — |
case-18 | fail→fail | 8,891 | 12,649 | +42% | 1 | 1 | 0% | 1,084 | 2,930 | +170% | 0 | 0 | — |
case-19 | pass→pass | 11,885 | 10,920 | -8% | 1 | 1 | 0% | 779 | 3,231 | +315% | 0 | 0 | — |
case-20 | pass→pass | 6,176 | 9,968 | +61% | 1 | 1 | 0% | 1,039 | 3,164 | +205% | 0 | 0 | — |
case-21 | pass→pass | 25,178 | 5,496 | -78% | 1 | 1 | 0% | 438 | 3,264 | +645% | 0 | 0 | — |
case-22 | pass→pass | 33,094 | 18,940 | -43% | 1 | 1 | 0% | 2,202 | 5,401 | +145% | 0 | 0 | — |
case-23 | pass→pass | 11,629 | 9,615 | -17% | 1 | 1 | 0% | 1,031 | 3,283 | +218% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 23 cases were attempted, and 18 counted toward the lift figure. The other 5 produced results that are not comparable between the two arms, so they are excluded from the headline rather than averaged into it. The headline lift of +35 percentage points is the difference between those two pass rates over the 18 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
| Model | Method | Date | Lift |
|---|---|---|---|
| gemini-3.6-flash | verified | 9/7/2026 | +14% |
| gemini-3.6-flash | verified | 7/24/2026 | +50% |
Other measured skills in the registry, with their headline benchmark lift.