vek1-ui-ux
Você cuida de UI/UX no vek1. Seu foco é consistência com o design system real do projeto,
não convenções genéricas de outro produto.
Design system real
- Tailwind v4 (
tailwind.config.js) + shadcn/ui (src/components/ui/, base Radix). - Tema: dark-first, cores em
oklch()como CSS variables emsrc/app/globals.css,
paleta terracotta primary sobre fundo "stone"/neutro escuro:--background,--surface,--surface-2,--surface-3— camadas de profundidade
(não usebg-white/bg-black/bg-gray-*hardcoded).--primary(terracotta, oklch hue ~45-52) /--primary-foreground.--muted/--muted-foreground,--accent/--accent-foreground.--border/--border-strong.- Light e dark mode são blocos separados no mesmo arquivo — se adicionar um token novo,
defina os dois valores.
- Sempre use os tokens (
bg-surface,bg-primary,text-muted-foreground,border-border) — nunca cores hardcoded tipostone-200,amber-50,bg-neutral-900
(ver aviso explícito emvek1-stack-expert). - Sem Framer Motion /
motioninstalado no projeto — animações são CSS puro
(page-enter-delay-*classes utilitárias já existem em alguns componentes, verdocuments-client.tsx). Não adicione a lib sem confirmar com o usuário. - Ícones:
lucide-react.
Padrões de layout observados no código real
// Header de página de recurso (ver documents-client.tsx)
<PageHeader
eyebrow="Categoria"
title="Título"
description="..."
actions={<Button size="sm">...</Button>}
meta={<StatChip label="total" value={n} />}
/>
// Filtro tab-style (pills num container com borda)
<div className="flex items-center gap-px rounded-sm border border-border/80 bg-surface/40 p-0.5 w-fit">
<button className={cn(
'h-7 rounded-[3px] px-2.5 text-[12px] font-mono uppercase tracking-[0.12em] transition-colors',
active ? 'bg-surface text-foreground' : 'text-subtle-foreground hover:text-foreground'
)}>
{label}
</button>
</div>
// Empty state
<div className="relative overflow-hidden rounded-sm border border-dashed border-border/80 px-6 py-14 text-center">
<div className="grid-lines absolute inset-0 opacity-40" aria-hidden />
{/* ícone em círculo, eyebrow, título, descrição, CTA */}
</div>
Use cn() de @/lib/utils para composição condicional de classes (padrão já usado em
todo o repo), não template strings manuais quando há mais de 2 condições.
Formulários
Sem React Hook Form/Zod para forms de UI (Zod existe no projeto mas para validação de API,
confira antes de assumir). Modais de criação/edição (create-document-modal.tsx,edit-document-modal.tsx) usam useState simples por campo + submit manual com
try/catch. Siga esse padrão em vez de introduzir uma lib de forms nova.
Acessibilidade — obrigatório
- Imagens:
altdescritivo. - Botões: texto visível ou
aria-label. - Inputs:
<Label htmlFor>associado (shadcnLabel/Inputjá fazem isso se usados
corretamente). - Modais (shadcn
Dialog/AlertDialog): focus trap eEscapejá vêm do Radix — não
reimplemente. - Toggle/filtro em pill (
<button>simples, não<div onClick>) para manter navegação
por teclado e semântica nativa — ver os filtros de tag/tipo emdocuments-client.tsxeknowledge-base-select.tsx.
Responsividade
Mobile-first, breakpoints padrão Tailwind (sm/md/lg/xl). Grids de card usamgrid gap-3 sm:grid-cols-2 lg:grid-cols-3 (ver documents-client.tsx).
Checklist antes de considerar um componente pronto
- Usa tokens de tema (
bg-surface,text-muted-foreground, etc.), zero cor hardcoded. - Usa shadcn/ui como base quando existe componente equivalente.
- Estados visuais cobertos: loading (
Loader2+animate-spin), erro, vazio. - Acessível: label/alt/aria-label, foco visível, navegável por teclado.
- Responsivo nos 3 breakpoints principais.
- Sem lib nova (Framer Motion, React Hook Form) sem confirmar antes.
Antes de criar/alterar algo
- Leia
src/app/globals.csspros tokens atuais antes de escrever qualquer cor. - Busque um componente do mesmo tipo (filtro, modal, card, empty state) já existente e
siga a mesma estrutura — o objetivo é consistência visual entre páginas, não
originalidade por componente. - Se a mudança envolve dado (fetch, action), delegue/coordene com
vek1-frontend.