Construa um Design System escalável e profissional usando Shadcn/UI. Tokens, componentes, documentação e governança para equipes grandes.
Crie um Design System que escala para centenas de desenvolvedores. Tokens consistentes, componentes reutilizáveis e documentação completa.
Design Systems reduzem tempo de desenvolvimento em 40% e garantem consistência visual em toda aplicação. Shadcn/UI oferece a base perfeita para construir sistemas escaláveis.
Google (Material), Apple (Human Interface), Microsoft (Fluent), Shopify (Polaris), Atlassian (Design System). Todas as grandes empresas tech investem pesadamente em Design Systems para escalar desenvolvimento.
UI/UX uniforme em todas as telas. Reduz decisões de design e acelera desenvolvimento.
Desenvolvimento 3x mais rápido com componentes pré-construídos e documentados.
Suporte para equipes grandes com governança clara e versionamento controlado.
Identidade visual unificada em todos os produtos. Tokens de design garantem harmonia entre cores, tipografia e espaçamentos.
Desenvolvimento 40% mais rápido com componentes prontos. Designers e desenvolvedores trabalham com a mesma linguagem.
Atualizações centralizadas propagam para toda aplicação. Um bugfix ou melhoria beneficia todos os produtos.
Regras e padrões definidos para uso correto dos componentes. Documentação completa e exemplos práticos.
Suporte para equipes grandes com versionamento semântico e breaking changes controlados.
TypeScript completo com IntelliSense, documentação inline e exemplos de uso em tempo real.
Estrutura sólida para um Design System escalável. Tokens, componentes, padrões e documentação organizados de forma sistemática.
Design Systems bem estruturados reduzem debt técnico e facilitam onboarding de novos desenvolvedores. Organização clara resulta em adoção mais rápida e manutenção simplificada.
Valores atômicos que definem o visual do sistema. Cores, espaçamentos, tipografia e outros elementos visuais centralizados.
Primary, secondary, semantic colors
Margins, paddings, gaps consistentes
Font families, sizes, weights, line heights
// tokens/colors.ts
export const colors = {
// Brand colors
brand: {
primary: {
50: 'hsl(210, 100%, 98%)',
100: 'hsl(210, 100%, 95%)',
200: 'hsl(210, 100%, 90%)',
300: 'hsl(210, 100%, 80%)',
400: 'hsl(210, 100%, 70%)',
500: 'hsl(210, 100%, 50%)', // Base
600: 'hsl(210, 100%, 45%)',
700: 'hsl(210, 100%, 35%)',
800: 'hsl(210, 100%, 25%)',
900: 'hsl(210, 100%, 15%)',
950: 'hsl(210, 100%, 8%)'
}
},
// Semantic colors
semantic: {
success: {
light: 'hsl(142, 76%, 45%)',
DEFAULT: 'hsl(142, 76%, 36%)',
dark: 'hsl(142, 76%, 25%)'
},
warning: {
light: 'hsl(38, 92%, 60%)',
DEFAULT: 'hsl(38, 92%, 50%)',
dark: 'hsl(38, 92%, 40%)'
},
error: {
light: 'hsl(0, 84%, 70%)',
DEFAULT: 'hsl(0, 84%, 60%)',
dark: 'hsl(0, 84%, 50%)'
}
},
// Neutral colors
neutral: {
0: 'hsl(0, 0%, 100%)',
50: 'hsl(210, 20%, 98%)',
100: 'hsl(220, 14%, 96%)',
200: 'hsl(220, 13%, 91%)',
300: 'hsl(216, 12%, 84%)',
400: 'hsl(218, 11%, 65%)',
500: 'hsl(220, 9%, 46%)',
600: 'hsl(215, 14%, 34%)',
700: 'hsl(217, 19%, 27%)',
800: 'hsl(215, 28%, 17%)',
900: 'hsl(221, 39%, 11%)',
950: 'hsl(224, 71%, 4%)',
1000: 'hsl(0, 0%, 0%)'
}
}
// tokens/spacing.ts
export const spacing = {
0: '0px',
px: '1px',
0.5: '0.125rem', // 2px
1: '0.25rem', // 4px
1.5: '0.375rem', // 6px
2: '0.5rem', // 8px
2.5: '0.625rem', // 10px
3: '0.75rem', // 12px
3.5: '0.875rem', // 14px
4: '1rem', // 16px
5: '1.25rem', // 20px
6: '1.5rem', // 24px
7: '1.75rem', // 28px
8: '2rem', // 32px
9: '2.25rem', // 36px
10: '2.5rem', // 40px
11: '2.75rem', // 44px
12: '3rem', // 48px
14: '3.5rem', // 56px
16: '4rem', // 64px
20: '5rem', // 80px
24: '6rem', // 96px
28: '7rem', // 112px
32: '8rem', // 128px
36: '9rem', // 144px
40: '10rem', // 160px
44: '11rem', // 176px
48: '12rem', // 192px
52: '13rem', // 208px
56: '14rem', // 224px
60: '15rem', // 240px
64: '16rem', // 256px
72: '18rem', // 288px
80: '20rem', // 320px
96: '24rem' // 384px
}Elementos básicos não decomponíveis
Combinação de primitivos
Seções complexas da interface
// components/ui/button.tsx
import * as React from 'react'
import { Slot } from '@radix-ui/react-slot'
import { cva, type VariantProps } from 'class-variance-authority'
import { cn } from '@/lib/utils'
// Variants usando CVA
const buttonVariants = cva(
// Base classes
"inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background 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-input 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: "h-10 w-10"
}
},
defaultVariants: {
variant: "default",
size: "default"
}
}
)
export interface ButtonProps
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
VariantProps<typeof buttonVariants> {
asChild?: boolean
}
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
({ className, variant, size, asChild = false, ...props }, ref) => {
const Comp = asChild ? Slot : "button"
return (
<Comp
className={cn(buttonVariants({ variant, size, className }))}
ref={ref}
{...props}
/>
)
}
)
Button.displayName = "Button"
export { Button, buttonVariants }/ * Button Component * * @description * Botão primário do sistema. Use para ações principais * como submit de formulários, CTAs e navegação. * * @example * // Botão primário * <Button variant="default">Salvar</Button> * * // Botão secundário * <Button variant="outline">Cancelar</Button> * * // Botão com ícone * <Button size="icon"> * <PlusIcon className="h-4 w-4" /> * </Button> * * @accessibility * - Suporte completo a keyboard navigation * - ARIA labels automáticos * - Focus visible para screen readers * - Estados disabled acessíveis * * @variants * - default: Ação primária (azul) * - destructive: Ações destrutivas (vermelho) * - outline: Ação secundária (borda) * - secondary: Ação terciária (cinza) * - ghost: Ação sutil (transparente) * - link: Ação de link (sublinhado) * * @sizes * - default: 40px altura (padrão) * - sm: 36px altura (compacto) * - lg: 44px altura (destaque) * - icon: 40x40px (apenas ícone) */
Implementações reais de Design Systems usando Shadcn/UI. Veja como grandes empresas estruturam seus sistemas.
Vercel, Linear, Supabase e Cal.com usam Shadcn/UI como base para seus Design Systems. Aprenda com implementações que servem milhões de usuários diariamente.
// Uso dos botões no Design System
import { Button } from '@/components/ui/button'
// Primary actions
<Button variant="default">Save Changes</Button>
<Button variant="default" className="bg-green-600 hover:bg-green-700">
Create New
</Button>
<Button variant="default" className="bg-purple-600 hover:bg-purple-700">
Upgrade Plan
</Button>
// Secondary actions
<Button variant="outline">Cancel</Button>
<Button variant="outline">Learn More</Button>
// Destructive actions
<Button variant="destructive">Delete Account</Button>
<Button variant="outline" className="border-red-300 text-red-700 hover:bg-red-50">
Remove Item
</Button>
// Sizes
<Button size="sm">Small</Button>
<Button size="default">Default</Button>
<Button size="lg">Large</Button>
// With icons
<Button>
<PlusIcon className="mr-2 h-4 w-4" />
Add Item
</Button>
// Loading state
<Button disabled>
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
Loading...
</Button>// Form components com validação
import { useForm } from 'react-hook-form'
import { zodResolver } from '@hookform/resolvers/zod'
import * as z from 'zod'
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage
} from '@/components/ui/form'
import { Input } from '@/components/ui/input'
import { Button } from '@/components/ui/button'
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'
import { Checkbox } from '@/components/ui/checkbox'
const formSchema = z.object({
email: z.string().email('Email inválido'),
password: z.string().min(8, 'Mínimo 8 caracteres'),
country: z.string().min(1, 'Selecione um país'),
terms: z.boolean().refine(val => val === true, {
message: 'Você deve aceitar os termos'
})
})
export function SignupForm() {
const form = useForm<z.infer<typeof formSchema>>({
resolver: zodResolver(formSchema),
defaultValues: {
email: '',
password: '',
country: '',
terms: false
}
})
function onSubmit(values: z.infer<typeof formSchema>) {
console.log(values)
}
return (
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-6">
<FormField
control={form.control}
name="email"
render={({ field }) => (
<FormItem>
<FormLabel>Email</FormLabel>
<FormControl>
<Input placeholder="seu@email.com" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="password"
render={({ field }) => (
<FormItem>
<FormLabel>Password</FormLabel>
<FormControl>
<Input type="password" placeholder="••••••••" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="country"
render={({ field }) => (
<FormItem>
<FormLabel>Country</FormLabel>
<Select onValueChange={field.onChange} defaultValue={field.value}>
<FormControl>
<SelectTrigger>
<SelectValue placeholder="Select country" />
</SelectTrigger>
</FormControl>
<SelectContent>
<SelectItem value="br">Brazil</SelectItem>
<SelectItem value="us">United States</SelectItem>
</SelectContent>
</Select>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="terms"
render={({ field }) => (
<FormItem className="flex flex-row items-start space-x-3 space-y-0">
<FormControl>
<Checkbox
checked={field.value}
onCheckedChange={field.onChange}
/>
</FormControl>
<div className="space-y-1 leading-none">
<FormLabel>I agree to the terms</FormLabel>
</div>
<FormMessage />
</FormItem>
)}
/>
<Button type="submit" className="w-full">
Create Account
</Button>
</form>
</Form>
)
}Documentação viva e interativa para seu Design System. Storybook + Shadcn/UI para máxima produtividade da equipe.
Storybook detecta automaticamente Next.js e configura tudo.
// .storybook/main.ts
import type { StorybookConfig } from '@storybook/nextjs'
const config: StorybookConfig = {
stories: ['../src//*.stories.@(js|jsx|ts|tsx|mdx)'],
addons: [
'@storybook/addon-essentials',
'@storybook/addon-interactions',
'@storybook/addon-a11y',
'@storybook/addon-docs'
],
framework: {
name: '@storybook/nextjs',
options: {}
},
typescript: {
check: false,
reactDocgen: 'react-docgen-typescript'
}
}
export default config// .storybook/preview.ts
import type { Preview } from '@storybook/react'
import '../src/app/globals.css'
const preview: Preview = {
parameters: {
actions: { argTypesRegex: '^on[A-Z].*' },
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/
}
},
docs: {
toc: true
}
},
globalTypes: {
theme: {
description: 'Global theme for components',
defaultValue: 'light',
toolbar: {
title: 'Theme',
icon: 'paintbrush',
items: ['light', 'dark'],
dynamicTitle: true
}
}
}
}
export default preview// stories/Button.stories.tsx
import type { Meta, StoryObj } from '@storybook/react'
import { Button } from '@/components/ui/button'
import { PlusIcon } from 'lucide-react'
const meta: Meta<typeof Button> = {
title: 'UI/Button',
component: Button,
parameters: {
layout: 'centered',
docs: {
description: {
component: 'Botão primário do Design System. Use para ações principais como submit de formulários e CTAs.'
}
}
},
tags: ['autodocs'],
argTypes: {
variant: {
control: { type: 'select' },
options: ['default', 'destructive', 'outline', 'secondary', 'ghost', 'link']
},
size: {
control: { type: 'select' },
options: ['default', 'sm', 'lg', 'icon']
},
asChild: {
control: { type: 'boolean' }
}
}
}
export default meta
type Story = StoryObj<typeof meta>
export const Default: Story = {
args: {
children: 'Button'
}
}
export const Secondary: Story = {
args: {
variant: 'secondary',
children: 'Secondary'
}
}
export const Destructive: Story = {
args: {
variant: 'destructive',
children: 'Delete'
}
}
export const Outline: Story = {
args: {
variant: 'outline',
children: 'Outline'
}
}
export const WithIcon: Story = {
args: {
children: (
<>
<PlusIcon className="mr-2 h-4 w-4" />
Add Item
</>
)
}
}
export const Loading: Story = {
args: {
disabled: true,
children: 'Loading...'
}
}
export const AllSizes: Story = {
render: () => (
<div className="flex items-center gap-4">
<Button size="sm">Small</Button>
<Button size="default">Default</Button>
<Button size="lg">Large</Button>
</div>
)
}
export const AllVariants: Story = {
render: () => (
<div className="flex flex-wrap gap-4">
<Button variant="default">Default</Button>
<Button variant="secondary">Secondary</Button>
<Button variant="destructive">Destructive</Button>
<Button variant="outline">Outline</Button>
<Button variant="ghost">Ghost</Button>
<Button variant="link">Link</Button>
</div>
)
}Acesse http://localhost:6006 para ver sua documentação interativa.