Skip to main content

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:

  1. Acessibilidade - WCAG 2.1 compliance via Radix UI
  2. Composição - Componentes pequenos e combináveis
  3. Personalização - Código-fonte no projeto, não biblioteca externa
  4. 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:

ComponenteDescriçãoUso
ButtonBotões com variantesAções do utilizador
InputCampo de textoFormulários
SelectLista de seleçãoEscolhas predefinidas
DialogModal dialogConfirmações, formulários
CardContainer com estiloAgrupar conteúdo
AvatarImagem de utilizadorPerfis, listas
TooltipDica ao hoverAjuda contextual

Componentes de Aplicação

Componentes específicos construídos para o APAH Assistant:

ComponenteDescriçãoLocalização
NavbarBarra de navegaçãoLayout global
NavUserMenu do utilizadorNavbar
ChatInterfaceInterface de chatPágina principal
ChatInputInput de mensagensChat
ChatMessagesLista de mensagensChat

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