Configuração completa do Tailwind CSS e shadcn/ui para criar componentes modernos, acessíveis e reutilizáveis.
O Tailwind CSS oferece utilitários de baixo nível para construir designs personalizados, enquanto o shadcn/ui fornece componentes pré-construídos e acessíveis.
# Inicializar shadcn/ui npx shadcn-ui@latest init # Responder as perguntas: # ✔ Would you like to use TypeScript? › yes # ✔ Which style would you like to use? › Default # ✔ Which color would you like to use as base color? › Slate # ✔ Where is your global CSS file? › src/app/globals.css # ✔ Would you like to use CSS variables for colors? › yes # ✔ Where is your tailwind.config.js located? › tailwind.config.ts # ✔ Configure the import alias for components? › src/components # ✔ Configure the import alias for utils? › src/lib/utils
# Instalar componentes básicos npx shadcn-ui@latest add button npx shadcn-ui@latest add card npx shadcn-ui@latest add input npx shadcn-ui@latest add label npx shadcn-ui@latest add badge npx shadcn-ui@latest add avatar npx shadcn-ui@latest add dropdown-menu npx shadcn-ui@latest add dialog npx shadcn-ui@latest add toast npx shadcn-ui@latest add table npx shadcn-ui@latest add form npx shadcn-ui@latest add select
@tailwind base; @tailwind components; @tailwind utilities; @layer base { :root { --background: 0 0% 100%; --foreground: 222.2 84% 4.9%; --card: 0 0% 100%; --card-foreground: 222.2 84% 4.9%; --popover: 0 0% 100%; --popover-foreground: 222.2 84% 4.9%; --primary: 221.2 83.2% 53.3%; --primary-foreground: 210 40% 98%; --secondary: 210 40% 96%; --secondary-foreground: 222.2 84% 4.9%; --muted: 210 40% 96%; --muted-foreground: 215.4 16.3% 46.9%; --accent: 210 40% 96%; --accent-foreground: 222.2 84% 4.9%; --destructive: 0 84.2% 60.2%; --destructive-foreground: 210 40% 98%; --border: 214.3 31.8% 91.4%; --input: 214.3 31.8% 91.4%; --ring: 221.2 83.2% 53.3%; --radius: 0.5rem; } .dark { --background: 222.2 84% 4.9%; --foreground: 210 40% 98%; --card: 222.2 84% 4.9%; --card-foreground: 210 40% 98%; --popover: 222.2 84% 4.9%; --popover-foreground: 210 40% 98%; --primary: 217.2 91.2% 59.8%; --primary-foreground: 222.2 84% 4.9%; --secondary: 217.2 32.6% 17.5%; --secondary-foreground: 210 40% 98%; --muted: 217.2 32.6% 17.5%; --muted-foreground: 215 20.2% 65.1%; --accent: 217.2 32.6% 17.5%; --accent-foreground: 210 40% 98%; --destructive: 0 62.8% 30.6%; --destructive-foreground: 210 40% 98%; --border: 217.2 32.6% 17.5%; --input: 217.2 32.6% 17.5%; --ring: 224.3 76.3% 94.1%; } } @layer base { * { @apply border-border; } body { @apply bg-background text-foreground; } }
{ "$schema": "https://ui.shadcn.com/schema.json", "style": "default", "rsc": true, "tsx": true, "tailwind": { "config": "tailwind.config.ts", "css": "src/app/globals.css", "baseColor": "slate", "cssVariables": true }, "aliases": { "components": "src/components", "utils": "src/lib/utils" } }
import { cn } from "@/lib/utils" interface LoadingProps { className?: string size?: "sm" | "md" | "lg" } export function Loading({ className, size = "md" }: LoadingProps) { const sizeClasses = { sm: "h-4 w-4", md: "h-6 w-6", lg: "h-8 w-8" } return ( <div className={cn( "animate-spin rounded-full border-2 border-gray-300 border-t-blue-600", sizeClasses[size], className )} /> ) } export function LoadingPage() { return ( <div className="flex items-center justify-center min-h-screen"> <div className="text-center"> <Loading size="lg" className="mx-auto mb-4" /> <p className="text-gray-600">Carregando...</p> </div> </div> ) }
import { LucideIcon } from "lucide-react" import { Button } from "./button" interface EmptyStateProps { icon: LucideIcon title: string description: string action?: { label: string onClick: () => void } } export function EmptyState({ icon: Icon, title, description, action }: EmptyStateProps) { return ( <div className="flex flex-col items-center justify-center py-12 text-center"> <div className="rounded-full bg-gray-100 p-6 mb-4"> <Icon className="h-12 w-12 text-gray-400" /> </div> <h3 className="text-lg font-semibold text-gray-900 mb-2"> {title} </h3> <p className="text-gray-600 mb-6 max-w-sm"> {description} </p> {action && ( <Button onClick={action.onClick}> {action.label} </Button> )} </div> ) }
import Link from "next/link" import { Button } from "@/components/ui/button" import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar" import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu" import { Bell, Settings, LogOut } from "lucide-react" export function Header() { return ( <header className="border-b bg-white"> <div className="container mx-auto px-4 py-4"> <div className="flex items-center justify-between"> <Link href="/" className="text-2xl font-bold text-gray-900"> Restaurantix </Link> <div className="flex items-center space-x-4"> <Button variant="ghost" size="icon"> <Bell className="h-5 w-5" /> </Button> <DropdownMenu> <DropdownMenuTrigger asChild> <Button variant="ghost" className="relative h-8 w-8 rounded-full"> <Avatar className="h-8 w-8"> <AvatarImage src="/avatar.png" alt="Avatar" /> <AvatarFallback>GM</AvatarFallback> </Avatar> </Button> </DropdownMenuTrigger> <DropdownMenuContent className="w-56" align="end" forceMount> <DropdownMenuLabel className="font-normal"> <div className="flex flex-col space-y-1"> <p className="text-sm font-medium leading-none"> Gustavo Miranda </p> <p className="text-xs leading-none text-muted-foreground"> gustavo@restaurantix.com </p> </div> </DropdownMenuLabel> <DropdownMenuSeparator /> <DropdownMenuItem> <Settings className="mr-2 h-4 w-4" /> <span>Configurações</span> </DropdownMenuItem> <DropdownMenuSeparator /> <DropdownMenuItem> <LogOut className="mr-2 h-4 w-4" /> <span>Sair</span> </DropdownMenuItem> </DropdownMenuContent> </DropdownMenu> </div> </div> </div> </header> ) }
import type { Metadata } from 'next' import { Inter } from 'next/font/google' import './globals.css' import { Toaster } from "@/components/ui/toaster" const inter = Inter({ subsets: ['latin'] }) export const metadata: Metadata = { title: 'Restaurantix - Dashboard', description: 'Sistema de gestão para restaurantes', } export default function RootLayout({ children, }: { children: React.ReactNode }) { return ( <html lang="pt-BR"> <body className={inter.className}> <div className="min-h-screen bg-background"> {children} </div> <Toaster /> </body> </html> ) }
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card" import { Badge } from "@/components/ui/badge" import { Button } from "@/components/ui/button" import { Header } from "@/components/layout/header" import { CheckCircle, Palette, Code, Layers } from "lucide-react" export default function HomePage() { return ( <div> <Header /> <main className="container mx-auto px-4 py-8"> <div className="text-center mb-12"> <Badge className="mb-4">Projeto Configurado</Badge> <h1 className="text-4xl font-bold mb-4"> Restaurantix Dashboard </h1> <p className="text-xl text-muted-foreground mb-8"> Next.js 14 + Tailwind CSS + shadcn/ui </p> </div> <div className="grid md:grid-cols-3 gap-6 mb-8"> <Card> <CardHeader> <CardTitle className="flex items-center"> <Code className="h-5 w-5 mr-2 text-blue-600" /> Next.js 14 </CardTitle> </CardHeader> <CardContent> <p className="text-muted-foreground"> App Router configurado com TypeScript e otimizações automáticas. </p> </CardContent> </Card> <Card> <CardHeader> <CardTitle className="flex items-center"> <Palette className="h-5 w-5 mr-2 text-cyan-600" /> Tailwind CSS </CardTitle> </CardHeader> <CardContent> <p className="text-muted-foreground"> Utility-first CSS framework com design system customizável. </p> </CardContent> </Card> <Card> <CardHeader> <CardTitle className="flex items-center"> <Layers className="h-5 w-5 mr-2 text-purple-600" /> shadcn/ui </CardTitle> </CardHeader> <CardContent> <p className="text-muted-foreground"> Componentes acessíveis e customizáveis baseados em Radix UI. </p> </CardContent> </Card> </div> <Card className="max-w-2xl mx-auto"> <CardHeader> <CardTitle className="flex items-center"> <CheckCircle className="h-5 w-5 mr-2 text-green-600" /> Tudo Funcionando! </CardTitle> </CardHeader> <CardContent className="space-y-4"> <p className="text-muted-foreground"> Seu ambiente de desenvolvimento está pronto para construir o frontend do Restaurantix. </p> <div className="flex gap-4"> <Button>Começar Dashboard</Button> <Button variant="outline">Ver Documentação</Button> </div> </CardContent> </Card> </main> </div> ) }
# Adicionar novos componentes shadcn/ui npx shadcn-ui@latest add [component-name] # Ver todos os componentes disponíveis npx shadcn-ui@latest add # Atualizar componentes existentes npx shadcn-ui@latest update # Verificar configuração npx shadcn-ui@latest diff
Pratique criando componentes com shadcn/ui:
Use os componentes para criar uma página de login:
Adicione um toggle para alternar entre temas:
Implemente toast notifications: