🚀 Oferta especial: 60% OFF no CrazyStack - Últimas vagas!Garantir vaga →
Voltar ao Curso
MÓDULO 1
AULA 2

Tailwind CSS + shadcn/ui

Configuração completa do Tailwind CSS e shadcn/ui para criar componentes modernos, acessíveis e reutilizáveis.

40 min
Design System
Componentes
🎨 Por que Tailwind + shadcn/ui?

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.

Vantagens do Tailwind

  • Utility-first: Classes pequenas e focadas
  • Customização: Design system flexível
  • Performance: CSS otimizado automaticamente
  • Responsivo: Mobile-first por padrão
  • Dark mode: Suporte nativo

Vantagens do shadcn/ui

  • Acessibilidade: WAI-ARIA compliant
  • Customizável: Código copiável
  • TypeScript: Totalmente tipado
  • Radix UI: Primitivos robustos
  • Moderno: Design contemporâneo
Passo 1: Instalando shadcn/ui

Inicializar shadcn/ui no projeto

# 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 essenciais

# 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
Passo 2: Configuração do Tema

src/app/globals.css

@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;
  }
}

components.json (gerado automaticamente)

{
  "$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"
  }
}
Passo 3: Componentes Customizados

src/components/ui/loading.tsx

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>
  )
}

src/components/ui/empty-state.tsx

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>
  )
}

src/components/layout/header.tsx

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>
  )
}
Passo 4: Layout Principal

src/app/layout.tsx (atualizado)

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>
  )
}

src/app/page.tsx (com shadcn/ui)

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>
  )
}
🧪 Testando os Componentes

Verificar se tudo está funcionando

Componentes shadcn/ui renderizando corretamente
Tema dark/light funcionando
Responsividade em diferentes telas
Acessibilidade com navegação por teclado

Comandos úteis para desenvolvimento

# 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
🎯 Exercício Prático

Pratique criando componentes com shadcn/ui:

1. Criar página de login

Use os componentes para criar uma página de login:

  • • Card para container principal
  • • Input para email
  • • Button para submit
  • • Loading state

2. Implementar tema dark/light

Adicione um toggle para alternar entre temas:

  • • Instalar next-themes
  • • Criar ThemeProvider
  • • Botão toggle no header
  • • Persistir preferência

3. Criar sistema de notificações

Implemente toast notifications:

  • • Usar componente Toast
  • • Hook useToast personalizado
  • • Diferentes tipos (success, error, info)
  • • Auto-dismiss configurável

💡 Dicas de design:

  • • Use espaçamento consistente (4, 8, 16, 24px)
  • • Mantenha hierarquia visual clara
  • • Teste acessibilidade com navegação por teclado
  • • Sempre teste em diferentes tamanhos de tela