Visão Geral dos Componentes
O APAH Assistant utiliza uma biblioteca de componentes baseada no shadcn/ui, que combina Radix UI primitives com Tailwind CSS para criar interfaces acessíveis e personalizáveis.
Filosofia
Os componentes seguem os princípios:
- Acessibilidade - WCAG 2.1 compliance via Radix UI
- Composição - Componentes pequenos e combináveis
- Personalização - Código-fonte no projeto, não biblioteca externa
- Consistência - Design system unificado
Estrutura de Componentes
src/
├── components/
│ └── ui/ # Componentes base (shadcn/ui)
│ ├── accordion.tsx
│ ├── alert-dialog.tsx
│ ├── alert.tsx
│ ├── avatar.tsx
│ ├── button.tsx
│ ├── card.tsx
│ ├── dialog.tsx
│ ├── dropdown-menu.tsx
│ ├── input.tsx
│ ├── label.tsx
│ ├── scroll-area.tsx
│ ├── select.tsx
│ ├── separator.tsx
│ ├── switch.tsx
│ ├── tooltip.tsx
│ └── ...
└── app/
└── _components/ # Componentes de aplicação
├── navbar.tsx
├── nav-user.tsx
└── chat/
├── chat-interface.tsx
├── chat-input.tsx
└── chat-messages.tsx
Categorias de Componentes
Componentes UI Base
Componentes primitivos reutilizáveis em toda a aplicação:
| Componente | Descrição | Uso |
|---|---|---|
Button | Botões com variantes | Ações do utilizador |
Input | Campo de texto | Formulários |
Select | Lista de seleção | Escolhas predefinidas |
Dialog | Modal dialog | Confirmações, formulários |
Card | Container com estilo | Agrupar conteúdo |
Avatar | Imagem de utilizador | Perfis, listas |
Tooltip | Dica ao hover | Ajuda contextual |
Componentes de Aplicação
Componentes específicos construídos para o APAH Assistant:
| Componente | Descrição | Localização |
|---|---|---|
Navbar | Barra de navegação | Layout global |
NavUser | Menu do utilizador | Navbar |
ChatInterface | Interface de chat | Página principal |
ChatInput | Input de mensagens | Chat |
ChatMessages | Lista de mensagens | Chat |
Instalação de Novos Componentes
Usando CLI
# Adicionar um componente
pnpm cn add button
# Adicionar múltiplos
pnpm cn add button card dialog
Componentes Disponíveis
# Listar todos os componentes disponíveis
pnpm cn add
Utilitário cn()
Função helper para combinar classes CSS:
// src/lib/utils.ts
import { clsx, type ClassValue } from "clsx";
import { twMerge } from "tailwind-merge";
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}
Uso
import { cn } from "@/lib/utils";
<div className={cn(
"flex items-center",
isActive && "bg-primary",
className
)} />
Design Tokens
Cores
As cores são definidas como CSS variables em globals.css:
:root {
--background: 0 0% 100%;
--foreground: 222.2 84% 4.9%;
--card: 0 0% 100%;
--card-foreground: 222.2 84% 4.9%;
--primary: 222.2 47.4% 11.2%;
--primary-foreground: 210 40% 98%;
--secondary: 210 40% 96.1%;
--secondary-foreground: 222.2 47.4% 11.2%;
--muted: 210 40% 96.1%;
--muted-foreground: 215.4 16.3% 46.9%;
--accent: 210 40% 96.1%;
--accent-foreground: 222.2 47.4% 11.2%;
--destructive: 0 84.2% 60.2%;
--destructive-foreground: 210 40% 98%;
--border: 214.3 31.8% 91.4%;
--ring: 222.2 84% 4.9%;
--radius: 0.5rem;
}
.dark {
--background: 222.2 84% 4.9%;
--foreground: 210 40% 98%;
/* ... dark mode colors */
}
Uso com Tailwind
<div className="bg-background text-foreground">
<button className="bg-primary text-primary-foreground">
Ação
</button>
<p className="text-muted-foreground">
Texto secundário
</p>
</div>
Padrões de Componentes
Componente com Variantes
import { cva, type VariantProps } from "class-variance-authority";
import { cn } from "@/lib/utils";
const buttonVariants = cva(
"inline-flex items-center justify-center rounded-md text-sm font-medium",
{
variants: {
variant: {
default: "bg-primary text-primary-foreground hover:bg-primary/90",
destructive: "bg-destructive text-destructive-foreground",
outline: "border border-input bg-background hover:bg-accent",
secondary: "bg-secondary text-secondary-foreground",
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> {}
export function Button({ className, variant, size, ...props }: ButtonProps) {
return (
<button
className={cn(buttonVariants({ variant, size, className }))}
{...props}
/>
);
}
Componente Composto
// Card com sub-componentes
import { Card, CardHeader, CardTitle, CardContent, CardFooter } from "@/components/ui/card";
<Card>
<CardHeader>
<CardTitle>Título</CardTitle>
</CardHeader>
<CardContent>
<p>Conteúdo do card</p>
</CardContent>
<CardFooter>
<Button>Ação</Button>
</CardFooter>
</Card>
Acessibilidade
Todos os componentes seguem as diretrizes WCAG 2.1:
- Navegação por teclado - Tab, Enter, Escape funcionam corretamente
- Screen readers - ARIA labels e roles apropriados
- Contraste - Cores com contraste adequado
- Focus visible - Indicadores de foco claros
Exemplo de Acessibilidade
<Dialog>
<DialogTrigger asChild>
<Button>Abrir</Button>
</DialogTrigger>
<DialogContent>
{/* Focus é automaticamente gerido */}
{/* Escape fecha o dialog */}
{/* Aria-labelledby é adicionado automaticamente */}
<DialogHeader>
<DialogTitle>Título do Dialog</DialogTitle>
<DialogDescription>
Descrição para screen readers
</DialogDescription>
</DialogHeader>
<DialogFooter>
<DialogClose asChild>
<Button>Fechar</Button>
</DialogClose>
</DialogFooter>
</DialogContent>
</Dialog>
Próximos Tópicos
- Componentes UI - Detalhes dos componentes base
- Componentes de Chat - Componentes do chat