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.