Skip to main content Inicio Creadores jal-co ui tailwind-design-system
tailwind-design-system Build scalable design systems with Tailwind CSS v4, design tokens, component libraries, and responsive patterns. Use when creating component libraries, implementing design systems, or standardizing UI patterns.
Ir a la instalación Skills Marketplace Descubre y explora habilidades de IA creadas por la comunidad.
Instalar con Codex o Claude Copia este prompt, pégalo en Codex, Claude u otro asistente, y deja que revise la página de la skill y la instale por ti.
Copiar promptMostrar detalles del prompt Un comando directo omite el prompt de revisión. Revisa el origen antes de ejecutarlo.
npx skills add https://github.com/jal-co/ui --skill tailwind-design-systemEl comando permanece en una sola línea. Desplázate horizontalmente para revisarlo antes de copiarlo.
¿Prefieres una copia local? Descarga los archivos que SkillsMP tiene disponibles ahora.
Descargar Zip Descargando... Create, review, and document jalco ui registry items with shadcn-style ergonomics, strong variants, and docs-to-registry alignment. Use for adding jalco ui components or blocks, updating registry.json, writing component docs, reviewing API and composition quality, or navigating the jalco-ui docs and registry structure. Use proactively when working in /Users/justin/Documents/Github/jalco-ui or when the user asks to create a component, improve variants, write docs, or audit registry readiness.
The canonical standard for building React components. Covers composition, accessibility, typing, styling, state, polymorphism, asChild, data attributes, design tokens, and artifact taxonomy from the components.build specification by Hayden Bleasel and shadcn.
Use for building any React component, reviewing component architecture, auditing accessibility and keyboard navigation, choosing between as/asChild, designing variant APIs with CVA, applying data-attribute-driven styling, classifying artifacts (primitive/component/block/pattern/template/utility), extending HTML types, and managing controlled/uncontrolled state.
Use proactively when creating or reviewing React components, designing component APIs, building design systems, implementing keyboard navigation, adding ARIA support, structuring compound components, choosing element flexibility patterns, or applying Tailwind styling systems.
Examples:
- user: "Build an accessible dialog" → semantic HTML, focus trap, keyboard map, ARIA, asChild trigger
- user: "Re
Build jalco ui components through a deliberate workflow: clarify requirements, judge scope and file boundaries, prefer strong default states and restrained variants, implement with shadcn-style ergonomics, and ship aligned docs. Use when the user asks to create a component, build a component, make a new UI component, add a registry component, create a docs component, add variants, refactor a component, or review public components, demos, or docs-facing UI.
Ocupaciones relacionadas SOC
Basado en la clasificación ocupacional SOC
name tailwind-design-system description Build scalable design systems with Tailwind CSS v4, design tokens, component libraries, and responsive patterns. Use when creating component libraries, implementing design systems, or standardizing UI patterns.
Tailwind Design System (v4)
Build production-ready design systems with Tailwind CSS v4, including CSS-first configuration, design tokens, component variants, responsive patterns, and accessibility.
Note : This skill targets Tailwind CSS v4 (2024+). For v3 projects, refer to the upgrade guide .
When to Use This Skill
Creating a component library with Tailwind v4
Implementing design tokens and theming with CSS-first configuration
Building responsive and accessible components
Standardizing UI patterns across a codebase
Migrating from Tailwind v3 to v4
Setting up dark mode with native CSS features
Key v4 Changes
v3 Pattern v4 Pattern tailwind.config.ts@theme in CSS@tailwind base/components/utilities@import "tailwindcss"darkMode: "class"@custom-variant dark (&:where(.dark, .dark *))theme.extend.colors@theme { --color-*: value }require("tailwindcss-animate")CSS @keyframes in @theme + @starting-style for entry animations
Quick Start
@import "tailwindcss" ;
@theme {
--color-background : oklch (100% 0 0 );
--color-foreground : oklch (14.5% 0.025 264 );
--color-primary : oklch (14.5% 0.025 264 );
--color-primary-foreground : oklch (98% 0.01 264 );
--color-secondary : oklch (96% 0.01 264 );
--color-secondary-foreground : oklch (14.5% 0.025 264 );
--color-muted : oklch (96% 0.01 264 );
--color-muted-foreground : oklch (46% 0.02 264 );
--color-accent : oklch (96% 0.01 264 );
--color-accent-foreground : oklch (14.5% 0.025 264 );
--color-destructive : oklch (53% 0.22 27 );
--color-destructive-foreground : oklch (98% 0.01 264 );
--color-border : oklch (91% 0.01 264 );
--color-ring : oklch (14.5% 0.025 264 );
--color-card : oklch (100% 0 0 );
--color-card-foreground : oklch (14.5% 0.025 264 );
--color-ring-offset : oklch (100% 0 0 );
--radius-sm : 0.25rem ;
--radius-md : 0.375rem ;
--radius-lg : 0.5rem ;
--radius-xl : 0.75rem ;
--animate-fade-in : fade-in 0.2s ease-out;
--animate-fade-out : fade-out 0.2s ease-in;
--animate-slide-in : slide-in 0.3s ease-out;
--animate-slide-out : slide-out 0.3s ease-in;
@keyframes fade-in {
from {
opacity : 0 ;
}
to {
opacity : 1 ;
}
}
@keyframes fade-out {
from {
opacity : 1 ;
}
to {
opacity : 0 ;
}
}
@keyframes slide-in {
from {
transform : translateY (-0.5rem );
opacity : 0 ;
}
to {
transform : translateY (0 );
opacity : 1 ;
}
}
@keyframes slide-out {
from {
transform : translateY (0 );
opacity : 1 ;
}
to {
transform : translateY (-0.5rem );
opacity : 0 ;
}
}
}
@custom-variant dark (&:where(.dark, .dark *));
.dark {
--color-background : oklch (14.5% 0.025 264 );
--color-foreground : oklch (98% 0.01 264 );
--color-primary : oklch (98% 0.01 264 );
--color-primary-foreground : oklch (14.5% 0.025 264 );
--color-secondary : oklch (22% 0.02 264 );
--color-secondary-foreground : oklch (98% 0.01 264 );
--color-muted : oklch (22% 0.02 264 );
--color-muted-foreground : oklch (65% 0.02 264 );
--color-accent : oklch (22% 0.02 264 );
--color-accent-foreground : oklch (98% 0.01 264 );
--color-destructive : oklch (42% 0.15 27 );
--color-destructive-foreground : oklch (98% 0.01 264 );
--color-border : oklch (22% 0.02 264 );
--color-ring : oklch (83% 0.02 264 );
--color-card : oklch (14.5% 0.025 264 );
--color-card-foreground : oklch (98% 0.01 264 );
--color-ring-offset : oklch (14.5% 0.025 264 );
}
@layer base {
* {
@apply border-border;
}
body {
@apply bg-background text-foreground antialiased;
}
}
Core Concepts
1. Design Token Hierarchy Brand Tokens (abstract)
└── Semantic Tokens (purpose)
└── Component Tokens (specific)
Example:
oklch(45% 0.2 260) → --color-primary → bg-primary
2. Component Architecture Base styles → Variants → Sizes → States → Overrides
Patterns
Pattern 1: CVA (Class Variance Authority) Components
import { Slot } from '@radix-ui/react-slot'
import { cva, type VariantProps } from 'class-variance-authority'
import { cn } from '@/lib/utils'
const buttonVariants = cva (
'inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50' ,
{
variants : {
variant : {
default : 'bg-primary text-primary-foreground hover:bg-primary/90' ,
destructive : 'bg-destructive text-destructive-foreground hover:bg-destructive/90' ,
outline : 'border border-border bg-background hover:bg-accent hover:text-accent-foreground' ,
secondary : 'bg-secondary text-secondary-foreground hover:bg-secondary/80' ,
ghost : 'hover:bg-accent hover:text-accent-foreground' ,
link : 'text-primary underline-offset-4 hover:underline' ,
},
size : {
default : 'h-10 px-4 py-2' ,
sm : 'h-9 rounded-md px-3' ,
lg : 'h-11 rounded-md px-8' ,
icon : 'size-10' ,
},
},
defaultVariants : {
variant : 'default' ,
size : 'default' ,
},
}
)
export interface ButtonProps
extends React .ButtonHTMLAttributes <HTMLButtonElement >,
VariantProps <typeof buttonVariants> {
asChild ?: boolean
}
export function Button ({
className,
variant,
size,
asChild = false ,
ref,
...props
}: ButtonProps & { ref?: React.Ref<HTMLButtonElement> } ) {
const Comp = asChild ? Slot : 'button'
return (
<Comp
className ={cn(buttonVariants({ variant , size , className }))}
ref ={ref}
{...props }
/>
)
}
<Button variant="destructive" size="lg" >Delete </Button >
<Button variant ="outline" > Cancel</Button >
<Button asChild > <Link href ="/home" > Home</Link > </Button >
Pattern 2: Compound Components (React 19)
import { cn } from '@/lib/utils'
export function Card ({
className,
ref,
...props
}: React .HTMLAttributes <HTMLDivElement > & { ref?: React.Ref<HTMLDivElement> } ) {
return (
<div
ref ={ref}
className ={cn(
'rounded-lg border border-border bg-card text-card-foreground shadow-sm ',
className
)}
{...props }
/>
)
}
export function CardHeader ({
className,
ref,
...props
}: React .HTMLAttributes <HTMLDivElement > & { ref?: React.Ref<HTMLDivElement> } ) {
return (
<div
ref ={ref}
className ={cn( 'flex flex-col space-y-1.5 p-6 ', className )}
{...props }
/>
)
}
export function CardTitle ({
className,
ref,
...props
}: React .HTMLAttributes <HTMLHeadingElement > & { ref?: React.Ref<HTMLHeadingElement> } ) {
return (
<h3
ref ={ref}
className ={cn( 'text-2xl font-semibold leading-none tracking-tight ', className )}
{...props }
/>
)
}
export function CardDescription ({
className,
ref,
...props
}: React .HTMLAttributes <HTMLParagraphElement > & { ref?: React.Ref<HTMLParagraphElement> } ) {
return (
<p
ref ={ref}
className ={cn( 'text-sm text-muted-foreground ', className )}
{...props }
/>
)
}
export function CardContent ({
className,
ref,
...props
}: React .HTMLAttributes <HTMLDivElement > & { ref?: React.Ref<HTMLDivElement> } ) {
return (
<div ref ={ref} className ={cn( 'p-6 pt-0 ', className )} {...props } />
)
}
export function CardFooter ({
className,
ref,
...props
}: React .HTMLAttributes <HTMLDivElement > & { ref?: React.Ref<HTMLDivElement> } ) {
return (
<div
ref ={ref}
className ={cn( 'flex items-center p-6 pt-0 ', className )}
{...props }
/>
)
}
<Card >
<CardHeader >
<CardTitle > Account</CardTitle >
<CardDescription > Manage your account settings</CardDescription >
</CardHeader >
<CardContent >
<form > ...</form >
</CardContent >
<CardFooter >
<Button > Save</Button >
</CardFooter >
</Card >
Pattern 3: Form Components
import { cn } from '@/lib/utils'
export interface InputProps extends React .InputHTMLAttributes <HTMLInputElement > {
error ?: string
ref ?: React .Ref <HTMLInputElement >
}
export function Input ({ className, type , error, ref, ...props }: InputProps ) {
return (
<div className ="relative" >
<input
type ={type}
className ={cn(
'flex h-10 w-full rounded-md border border-border bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 ',
error && 'border-destructive focus-visible:ring-destructive ',
className
)}
ref ={ref}
aria-invalid ={!!error}
aria-describedby ={error ? `${props.id }-error ` : undefined }
{...props }
/>
{error && (
<p
id ={ `${props.id }-error `}
className ="mt-1 text-sm text-destructive"
role ="alert"
>
{error}
</p >
)}
</div >
)
}
import { cva, type VariantProps } from 'class-variance-authority'
const labelVariants = cva (
'text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70'
)
export function Label ({
className,
ref,
...props
}: React .LabelHTMLAttributes <HTMLLabelElement > & { ref?: React.Ref<HTMLLabelElement> } ) {
return (
<label ref ={ref} className ={cn(labelVariants(), className )} {...props } />
)
}
import { useForm } from 'react-hook-form'
import { zodResolver } from '@hookform/resolvers/zod'
import { z } from 'zod'
const schema = z.object ({
email : z.string ().email ('Invalid email address' ),
password : z.string ().min (8 , 'Password must be at least 8 characters' ),
})
function LoginForm ( ) {
const { register, handleSubmit, formState : { errors } } = useForm ({
resolver : zodResolver (schema),
})
return (
<form onSubmit ={handleSubmit(onSubmit)} className ="space-y-4" >
<div className ="space-y-2" >
<Label htmlFor ="email" > Email</Label >
<Input
id ="email"
type ="email"
{...register ('email ')}
error ={errors.email?.message}
/>
</div >
<div className ="space-y-2" >
<Label htmlFor ="password" > Password</Label >
<Input
id ="password"
type ="password"
{...register ('password ')}
error ={errors.password?.message}
/>
</div >
<Button type ="submit" className ="w-full" > Sign In</Button >
</ >
)
}
Pattern 4: Responsive Grid System
import { cn } from '@/lib/utils'
import { cva, type VariantProps } from 'class-variance-authority'
const gridVariants = cva ('grid' , {
variants : {
cols : {
1 : 'grid-cols-1' ,
2 : 'grid-cols-1 sm:grid-cols-2' ,
3 : 'grid-cols-1 sm:grid-cols-2 lg:grid-cols-3' ,
4 : 'grid-cols-1 sm:grid-cols-2 lg:grid-cols-4' ,
5 : 'grid-cols-2 sm:grid-cols-3 lg:grid-cols-5' ,
6 : 'grid-cols-2 sm:grid-cols-3 lg:grid-cols-6' ,
},
gap : {
none : 'gap-0' ,
sm : 'gap-2' ,
md : 'gap-4' ,
lg : 'gap-6' ,
xl : 'gap-8' ,
},
},
defaultVariants : {
cols : 3 ,
gap : 'md' ,
},
})
interface GridProps
extends React .HTMLAttributes <HTMLDivElement >,
VariantProps <typeof gridVariants> {}
export function Grid ({ className, cols, gap, ...props }: GridProps ) {
return (
<div className ={cn(gridVariants({ cols , gap , className }))} {...props } />
)
}
const containerVariants = cva ('mx-auto w-full px-4 sm:px-6 lg:px-8' , {
variants : {
size : {
sm : 'max-w-screen-sm' ,
md : 'max-w-screen-md' ,
lg : 'max-w-screen-lg' ,
xl : 'max-w-screen-xl' ,
'2xl' : 'max-w-screen-2xl' ,
full : 'max-w-full' ,
},
},
defaultVariants : {
size : 'xl' ,
},
})
interface ContainerProps
extends React .HTMLAttributes <HTMLDivElement >,
VariantProps <typeof containerVariants> {}
export function Container ({ className, size, ...props }: ContainerProps ) {
return (
<div className ={cn(containerVariants({ size , className }))} {...props } />
)
}
<Container >
<Grid cols ={4} gap ="lg" >
{products.map((product) => (
<ProductCard key ={product.id} product ={product} />
))}
</Grid >
</Container >
Pattern 5: Native CSS Animations (v4)
@theme {
--animate-dialog-in : dialog-fade-in 0.2s ease-out;
--animate-dialog-out : dialog-fade-out 0.15s ease-in;
}
@keyframes dialog-fade-in {
from {
opacity : 0 ;
transform : scale (0.95 ) translateY (-0.5rem );
}
to {
opacity : 1 ;
transform : scale (1 ) translateY (0 );
}
}
@keyframes dialog-fade-out {
from {
opacity : 1 ;
transform : scale (1 ) translateY (0 );
}
to {
opacity : 0 ;
transform : scale (0.95 ) translateY (-0.5rem );
}
}
[popover] {
transition :
opacity 0.2s ,
transform 0.2s ,
display 0.2s allow-discrete;
opacity : 0 ;
transform : scale (0.95 );
}
[popover] :popover-open {
opacity : 1 ;
transform : scale (1 );
}
@starting-style {
[popover] :popover-open {
opacity : 0 ;
transform : scale (0.95 );
}
}
import * as DialogPrimitive from '@radix-ui/react-dialog'
import { cn } from '@/lib/utils'
const DialogPortal = DialogPrimitive .Portal
export function DialogOverlay ({
className,
ref,
...props
}: React .ComponentPropsWithoutRef <typeof DialogPrimitive .Overlay > & {
ref?: React.Ref<HTMLDivElement>
} ) {
return (
<DialogPrimitive.Overlay
ref ={ref}
className ={cn(
'fixed inset-0 z-50 bg-black /80 ',
'data- [state =open]:animate-fade-in data- [state =closed]:animate-fade-out ',
className
)}
{...props }
/>
)
}
export function DialogContent ({
className,
children,
ref,
...props
}: React .ComponentPropsWithoutRef <typeof DialogPrimitive .Content > & {
ref?: React.Ref<HTMLDivElement>
} ) {
return (
<DialogPortal >
<DialogOverlay />
<DialogPrimitive.Content
ref ={ref}
className ={cn(
'fixed left-1 /2 top-1 /2 z-50 grid w-full max-w-lg -translate-x-1 /2 -translate-y-1 /2 gap-4 border border-border bg-background p-6 shadow-lg sm:rounded-lg ',
'data- [state =open]:animate-dialog-in data- [state =closed]:animate-dialog-out ',
className
)}
{...props }
>
{children}
</DialogPrimitive.Content >
</DialogPortal >
)
}
Pattern 6: Dark Mode with CSS (v4)
'use client'
import { createContext, useContext, useEffect, useState } from 'react'
type Theme = 'dark' | 'light' | 'system'
interface ThemeContextType {
theme : Theme
setTheme : (theme : Theme ) => void
resolvedTheme : 'dark' | 'light'
}
const ThemeContext = createContext<ThemeContextType | undefined >(undefined )
export function ThemeProvider ({
children,
defaultTheme = 'system' ,
storageKey = 'theme' ,
}: {
children: React.ReactNode
defaultTheme?: Theme
storageKey?: string
} ) {
const [theme, setTheme] = useState<Theme >(defaultTheme)
const [resolvedTheme, setResolvedTheme] = useState<'dark' | 'light' >('light' )
useEffect (() => {
const stored = localStorage .getItem (storageKey) as Theme | null
if (stored) setTheme (stored)
}, [storageKey])
useEffect (() => {
const root = document .documentElement
root.classList .remove ('light' , 'dark' )
const resolved = theme === 'system'
? (window .matchMedia ('(prefers-color-scheme: dark)' ).matches ? 'dark' : 'light' )
: theme
root.classList .add (resolved)
setResolvedTheme (resolved)
const metaThemeColor = document .querySelector ('meta[name="theme-color"]' )
if (metaThemeColor) {
metaThemeColor.setAttribute ('content' , resolved === 'dark' ? '#09090b' : '#ffffff' )
}
}, [theme])
return (
<ThemeContext.Provider value ={{
theme ,
setTheme: (newTheme ) => {
localStorage.setItem(storageKey, newTheme)
setTheme(newTheme)
},
resolvedTheme,
}}>
{children}
</ThemeContext.Provider >
)
}
export const useTheme = ( ) => {
const context = useContext (ThemeContext )
if (!context) throw new Error ('useTheme must be used within ThemeProvider' )
return context
}
import { Moon , Sun } from 'lucide-react'
import { useTheme } from '@/providers/ThemeProvider'
export function ThemeToggle ( ) {
const { resolvedTheme, setTheme } = useTheme ()
return (
<Button
variant ="ghost"
size ="icon"
onClick ={() => setTheme(resolvedTheme === 'dark' ? 'light' : 'dark')}
>
<Sun className ="size-5 rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0" />
<Moon className ="absolute size-5 rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100" />
<span className ="sr-only" > Toggle theme</span >
</Button >
)
}
Utility Functions
import { type ClassValue , clsx } from "clsx" ;
import { twMerge } from "tailwind-merge" ;
export function cn (...inputs : ClassValue [] ) {
return twMerge (clsx (inputs));
}
export const focusRing = cn (
"focus-visible:outline-none focus-visible:ring-2" ,
"focus-visible:ring-ring focus-visible:ring-offset-2" ,
);
export const disabled = "disabled:pointer-events-none disabled:opacity-50" ;
Advanced v4 Patterns
Custom Utilities with @utility Define reusable custom utilities:
@utility line-t {
@apply relative before :absolute before :top-0 before :-left-[100vw ] before :h-px before :w-[200vw ] before :bg-gray-950 /5 dark :before :bg-white/10 ;
}
@utility text-gradient {
@apply bg-gradient-to-r from-primary to-accent bg-clip-text text-transparent;
}
Theme Modifiers
@theme inline {
--font-sans : var (--font-inter), system-ui;
}
@theme static {
--color-brand : oklch (65% 0.15 240 );
}
@import "tailwindcss" theme(static);
Namespace Overrides @theme {
--color- *: initial;
--color-white : #fff ;
--color-black : #000 ;
--color-primary : oklch (45% 0.2 260 );
--color-secondary : oklch (65% 0.15 200 );
}
Semi-transparent Color Variants @theme {
--color-primary-50 : color-mix (in oklab, var (--color-primary) 5% , transparent);
--color-primary-100 : color-mix (
in oklab,
var (--color-primary) 10% ,
transparent
);
--color-primary-200 : color-mix (
in oklab,
var (--color-primary) 20% ,
transparent
);
}
Container Queries @theme {
--container-xs : 20rem ;
--container-sm : 24rem ;
--container-md : 28rem ;
--container-lg : 32rem ;
}
v3 to v4 Migration Checklist
Best Practices
Do's
MUST use @theme blocks — CSS-first configuration is v4's core pattern
SHOULD use OKLCH colors — better perceptual uniformity than HSL
SHOULD compose with CVA — type-safe variants
MUST use semantic tokens — bg-primary not bg-blue-500
SHOULD use size-* — new shorthand for w-* h-*
MUST include accessibility — ARIA attributes, focus states
Don'ts
MUST NOT use tailwind.config.ts — use CSS @theme instead
MUST NOT use @tailwind directives — use @import "tailwindcss"
MUST NOT use forwardRef — React 19 passes ref as prop
SHOULD NOT use arbitrary values — extend @theme instead
MUST NOT hardcode colors — use semantic tokens
MUST NOT skip dark mode — test both themes
form