Design System v2.0 · 2026-05-29
Practix DS
Tokens, componentes e padrões visuais do produto. Esta página é a versão ao vivo da documentação canônica em docs/design-system.md. Estrutura inspirada no BaSe Sebrae, adaptada pra marca Practix (warm SMB, terracota signature, Aspekta).
4 camadas
Primitives → Semantic → Component → Theme. Componente lê semantic, nunca raw.
Rampa pareada
Cada cor tem 9 tons + on-color garantido AA. Bug de texto ilegível morre na raiz.
Tema por contexto
Brand fixa em todo lugar. Tema muda só na landing por segmento e mini-site público.
Release notes
- v2.0 (2026-05-29): rampa tonal completa 9 tons + on-color pareado pra brand/segmento/feedback/neutro. Componentes novos: Avatar, Skeleton, Spinner, Tooltip, StatePages. Página /design-system refeita com sumário lateral e 7 seções.
- v1.0 (2026-05-28): formaliza arquitetura 4 camadas, introduz SegmentTheme, Card, EmptyState, Alert. Sem breaking changes.
01 · Fundamento
Cores
Cada família tem 9 tons (10 escuro, 90 claro) com on-color pareado. Componente escolhe o tom, o on-color garante contraste AA automático. Tom marcado comoBaseé o que vira alias semântico legacy (--signature, --action, etc).
Brand
Identidade Practix. Terracota é a assinatura visual. Sálvia é a cor funcional primária (ação, foco, primário).
Terracota
Cor de assinatura da marca. Tom 40 é o que aparece no ponto do logo.
PractixTheme.colors.brand.terracota--terracota-10
#431407
--terracota-20
#7C2D12
--terracota-30
#9A3412
--terracota-40
#C2510C
--terracota-50
#EA580C
--terracota-60
#F97316
--terracota-70
#FB923C
--terracota-80
#FDBA74
--terracota-90
#FFEDD5
--on-terracota-10
#FFFFFF
--on-terracota-20
#FFFFFF
--on-terracota-30
#FFFFFF
--on-terracota-40
#FFFFFF
--on-terracota-50
#09090B
--on-terracota-60
#09090B
--on-terracota-70
#09090B
--on-terracota-80
#09090B
--on-terracota-90
#09090B
Sálvia
Cor funcional primária. Botão CTA, focus ring, link inline. Tom 40 é o que vira --action.
PractixTheme.colors.brand.salvia--salvia-10
#052E16
--salvia-20
#14532D
--salvia-30
#166534
--salvia-40
#15803D
--salvia-50
#16A34A
--salvia-60
#22C55E
--salvia-70
#4ADE80
--salvia-80
#86EFAC
--salvia-90
#DCFCE7
--on-salvia-10
#FFFFFF
--on-salvia-20
#FFFFFF
--on-salvia-30
#FFFFFF
--on-salvia-40
#FFFFFF
--on-salvia-50
#FFFFFF
--on-salvia-60
#09090B
--on-salvia-70
#09090B
--on-salvia-80
#09090B
--on-salvia-90
#09090B
Segmento
Cores aplicadas por contexto via [data-segment]. Cada negócio atendido tem uma identidade sutil sem perder a marca Practix global.
Grafite
Segmento barbearia. Neutro escuro masculino, herança barbearia clássica.
PractixTheme.colors.segmento.grafite--grafite-10
#020617
--grafite-20
#0F172A
--grafite-30
#1E293B
--grafite-40
#334155
--grafite-50
#475569
--grafite-60
#64748B
--grafite-70
#94A3B8
--grafite-80
#CBD5E1
--grafite-90
#F1F5F9
--on-grafite-10
#FFFFFF
--on-grafite-20
#FFFFFF
--on-grafite-30
#FFFFFF
--on-grafite-40
#FFFFFF
--on-grafite-50
#FFFFFF
--on-grafite-60
#FFFFFF
--on-grafite-70
#09090B
--on-grafite-80
#09090B
--on-grafite-90
#09090B
Magenta
Segmento salão de beleza. Família beauty.
PractixTheme.colors.segmento.magenta--magenta-10
#500724
--magenta-20
#831843
--magenta-30
#9D174D
--magenta-40
#BE185D
--magenta-50
#DB2777
--magenta-60
#EC4899
--magenta-70
#F472B6
--magenta-80
#F9A8D4
--magenta-90
#FCE7F3
--on-magenta-10
#FFFFFF
--on-magenta-20
#FFFFFF
--on-magenta-30
#FFFFFF
--on-magenta-40
#FFFFFF
--on-magenta-50
#FFFFFF
--on-magenta-60
#FFFFFF
--on-magenta-70
#09090B
--on-magenta-80
#09090B
--on-magenta-90
#09090B
Teal
Segmento clínica. Transmite calma e profissionalismo.
PractixTheme.colors.segmento.teal--teal-10
#042F2E
--teal-20
#134E4A
--teal-30
#115E59
--teal-40
#0F766E
--teal-50
#0D9488
--teal-60
#14B8A6
--teal-70
#2DD4BF
--teal-80
#5EEAD4
--teal-90
#CCFBF1
--on-teal-10
#FFFFFF
--on-teal-20
#FFFFFF
--on-teal-30
#FFFFFF
--on-teal-40
#FFFFFF
--on-teal-50
#FFFFFF
--on-teal-60
#FFFFFF
--on-teal-70
#09090B
--on-teal-80
#09090B
--on-teal-90
#09090B
Violeta
Segmento fotografia. Família criativa.
PractixTheme.colors.segmento.violeta--violeta-10
#2E1065
--violeta-20
#4C1D95
--violeta-30
#5B21B6
--violeta-40
#6D28D9
--violeta-50
#7C3AED
--violeta-60
#8B5CF6
--violeta-70
#A78BFA
--violeta-80
#C4B5FD
--violeta-90
#EDE9FE
--on-violeta-10
#FFFFFF
--on-violeta-20
#FFFFFF
--on-violeta-30
#FFFFFF
--on-violeta-40
#FFFFFF
--on-violeta-50
#FFFFFF
--on-violeta-60
#FFFFFF
--on-violeta-70
#09090B
--on-violeta-80
#09090B
--on-violeta-90
#09090B
Feedback de interface
Cores RESERVADAS pra mensagens de sistema (sucesso, aviso, erro, info). Nunca decoração — o usuário aprende que essas cores significam estado.
SucessoReservado
Confirmação positiva: agendado, salvo, enviado. Mesma família visual da sálvia, distinta semanticamente.
PractixTheme.colors.feedback.sucesso--sucesso-10
#052E16
--sucesso-20
#14532D
--sucesso-30
#166534
--sucesso-40
#15803D
--sucesso-50
#16A34A
--sucesso-60
#22C55E
--sucesso-70
#4ADE80
--sucesso-80
#86EFAC
--sucesso-90
#DCFCE7
--on-sucesso-10
#FFFFFF
--on-sucesso-20
#FFFFFF
--on-sucesso-30
#FFFFFF
--on-sucesso-40
#FFFFFF
--on-sucesso-50
#FFFFFF
--on-sucesso-60
#09090B
--on-sucesso-70
#09090B
--on-sucesso-80
#09090B
--on-sucesso-90
#09090B
AvisoReservado
Atenção, ação reversível mas notável. Trial vence em 3 dias, slot duplicado, etc.
PractixTheme.colors.feedback.aviso--aviso-10
#451A03
--aviso-20
#78350F
--aviso-30
#92400E
--aviso-40
#B45309
--aviso-50
#D97706
--aviso-60
#F59E0B
--aviso-70
#FBBF24
--aviso-80
#FCD34D
--aviso-90
#FEF3C7
--on-aviso-10
#FFFFFF
--on-aviso-20
#FFFFFF
--on-aviso-30
#FFFFFF
--on-aviso-40
#FFFFFF
--on-aviso-50
#FFFFFF
--on-aviso-60
#09090B
--on-aviso-70
#09090B
--on-aviso-80
#09090B
--on-aviso-90
#09090B
ErroReservado
Erro de validação ou falha de operação. Algo deu errado e o usuário precisa saber.
PractixTheme.colors.feedback.erro--erro-10
#450A0A
--erro-20
#7F1D1D
--erro-30
#991B1B
--erro-40
#B91C1C
--erro-50
#DC2626
--erro-60
#EF4444
--erro-70
#F87171
--erro-80
#FCA5A5
--erro-90
#FEE2E2
--on-erro-10
#FFFFFF
--on-erro-20
#FFFFFF
--on-erro-30
#FFFFFF
--on-erro-40
#FFFFFF
--on-erro-50
#FFFFFF
--on-erro-60
#09090B
--on-erro-70
#09090B
--on-erro-80
#09090B
--on-erro-90
#09090B
InfoReservado
Informação neutra, dica contextual. Azul-petróleo que não compete com Action.
PractixTheme.colors.feedback.info--feedback-info-10
#083344
--feedback-info-20
#164E63
--feedback-info-30
#155E75
--feedback-info-40
#0E7490
--feedback-info-50
#0891B2
--feedback-info-60
#06B6D4
--feedback-info-70
#22D3EE
--feedback-info-80
#67E8F9
--feedback-info-90
#CFFAFE
--on-feedback-info-10
#FFFFFF
--on-feedback-info-20
#FFFFFF
--on-feedback-info-30
#FFFFFF
--on-feedback-info-40
#FFFFFF
--on-feedback-info-50
#FFFFFF
--on-feedback-info-60
#09090B
--on-feedback-info-70
#09090B
--on-feedback-info-80
#09090B
--on-feedback-info-90
#09090B
Neutro
Escala única de cinza (zinc). Usada em texto, bordas, hover, divisores.
Cinza
Tom 10 vira --foreground. Tom 50 vira --muted-foreground. Tom 90 vira --border.
PractixTheme.colors.neutro.cinza--cinza-10
#09090B
--cinza-20
#18181B
--cinza-30
#27272A
--cinza-40
#3F3F46
--cinza-50
#52525B
--cinza-60
#71717A
--cinza-70
#A1A1AA
--cinza-80
#D4D4D8
--cinza-90
#E4E4E7
--on-cinza-10
#FFFFFF
--on-cinza-20
#FFFFFF
--on-cinza-30
#FFFFFF
--on-cinza-40
#FFFFFF
--on-cinza-50
#FFFFFF
--on-cinza-60
#FFFFFF
--on-cinza-70
#09090B
--on-cinza-80
#09090B
--on-cinza-90
#09090B
02 · Tema
Tema por segmento
Tema por segmento
Cada segmento atendido pelo Practix tem identidade visual sutil aplicada via [data-segment="..."] no <html>. Os tokens --theme-* mudam conforme o contexto sem perder a marca Practix global.
Tokens do tema atual
Accent
--theme-accentAccent text
--theme-accent-textAccent soft
--theme-accent-softAccent strong
--theme-accent-strongExemplo aplicado
Destaque do segmento
Mini-card de demonstração
Border-left, eyebrow e badge abaixo reagem ao segmento ativo. Trocando o switch, o tom muda mas a estrutura permanece igual.
03 · Fundamento
Tipografia
Fonte única Aspekta VF (variable 100-900, SIL OFL). Auto-hospedada via next/font/local. Escolhida pelo peso flexível, calor SMB e contraste bom em telas baratas.
Pesos disponíveis
font-weight: 100
O movimento de uma agenda viva.
font-weight: 300
O movimento de uma agenda viva.
font-weight: 400
O movimento de uma agenda viva.
font-weight: 500
O movimento de uma agenda viva.
font-weight: 600
O movimento de uma agenda viva.
font-weight: 700
O movimento de uma agenda viva.
font-weight: 800
O movimento de uma agenda viva.
Escala
Line-height múltiplo de 4 cria ritmo vertical consistente.
text-[40px] leading-[44px] font-bold font-heading40 / 44 px
text-3xl leading-[40px] font-bold font-heading30 / 40 px
text-2xl leading-[32px] font-semibold font-heading24 / 32 px
text-xl leading-[28px] font-semibold font-heading20 / 28 px
text-lg leading-[28px]18 / 28 px
text-base leading-[24px]16 / 24 px
text-sm leading-[20px]14 / 20 px
text-xs leading-[16px]12 / 16 px
Hierarquia em uso
Resultados do mês
Receita por profissional
Você atendeu 142 clientes em maio. Marcos lidera com 38 atendimentos e Camila vem na sequência com 31. A taxa de conclusão ficou em 89%.
Dados atualizados há 2 minutos. Inclui agendamentos confirmados e concluídos.
O ponto da marca Practix é QUADRADO. Nunca redondo.
A regra vale especificamente para o ponto que segue o nome "Practix" (logomarca, header, footer, assinaturas, qualquer peça que carregue o nome da marca). Não se estende a pontos finais de frases normais de copy, que devem usar o ponto natural da fonte.
No site, a fonte Aspekta VF já renderiza "." como um quadrado pequeno em pesos bold, e isso atende. Mas como garantia (caso a fonte mude algum dia, ou em contextos onde a fonte é Inter ou system-ui), a logo e qualquer menção textual à marca usam um span quadrado explícitona cor signature em vez do caractere ".".
CERTO
practix
Span quadrado explícito, 0.22em x 0.22em, cor signature, baseline.
ERRADO
practix
Mesmo span, mas com border-radius:50%. Descaracteriza a marca.
- Aplica-se à logo (em qualquer tamanho) e a qualquer menção textual ao nome "Practix" em peça de marca (não em copy de produto).
- O componente
PractixLogojá aplica corretamente em todo o site. Pra inserir o nome em texto solto, replicar o span:
practix<span
aria-hidden="true"
style={{
display: "inline-block",
background: "var(--signature)",
width: "0.22em",
height: "0.22em",
verticalAlign: "baseline",
marginLeft: "0.06em",
}}
/>- Tamanho proporcional via
em(escala junto com o texto). Nunca px fixo. - Sempre
aria-hidden: leitor de tela lê "Practix" antes; o quadrado é puramente decorativo.
04 · Fundamento
Fundamentos
Spacing
Tailwind base 4 (1 unidade = 4px). Use direto via utilities. Não criamos tokens custom de spacing — adiciona ruído sem ganho real.
space-14pxspace-28pxspace-312pxspace-416pxspace-624pxspace-832pxspace-1248pxspace-1664pxRadius
Base 8px (--radius), escalado via multipliers.
--radius-sm~5px
--radius-md~6px
--radius-lg8px
--radius-xl~11px
--radius-2xl~14px
--radius-3xl~18px
--radius-4xl~21px
Elevação (surfaces tonais)
Use surface tonal em vez de shadow pesado. Funciona melhor em telas baratas sob luz natural do salão. Modais e CTA escuro são exceções autorizadas.
Nível 0
backgroundBody da página
Nível 1
cardConteúdo focal
Nível 2
surface-creamCallout, painel
Nível 3
+ shadow-2xlModal (exceção)
Breakpoints
Mobile-first. Microempresário usa metade no celular, metade no notebook do salão.
| Token | Mínimo | Uso típico |
|---|---|---|
(default) | 0px | Mobile portrait |
sm | 640px | Mobile landscape, tablet pequeno |
md | 768px | Tablet |
lg | 1024px | Laptop, notebook do salão |
xl | 1280px | Desktop padrão |
2xl | 1536px | Desktop largo |
Ícones
Biblioteca única lucide-react. Importação por ícone (tree-shakeable). Decorativos recebem aria-hidden="true"; funcionais sem texto visível recebem aria-label explícito.
3 estilos de apresentação
Escolha pelo peso visual. Inline some no texto. Tonal dá presença sem competir. Colored shadow(estilo Fly.io) cria personalidade visual marcante — use com moderação em destaques pra fugir do padrão de SaaS "IA-genérico".
Inline
Dentro de texto, badge ou botão. Tamanho 14-18px, cor herda do contexto.
Tonal
Agenda do dia
12 atendimentos
Fundo na cor light (--COR-90) + ícone na cor base. Padrão Material 3.
Colored shadow (destaque)
Agenda do dia
12 atendimentos
Fundo branco + ícone colorido com glow da mesma cor. Estilo Fly.io. Reserve pra hero, destaques de feature, cards de dashboard chave.
Tamanhos
Pareie com a escala tipográfica. Ícone ~= altura da linha do texto adjacente.
10px12px14px16px18px20px24px28px32px40px48pxCatálogo (recortado pra rotina Practix)
Lucide tem 1500+ ícones. Listo aqui os mais relevantes pro domínio (agenda, pessoas, comunicação, financeiro, UI, status, mídia, ações). Use lucide-react direto pra qualquer outro.
Tempo & Agenda
tone=terracotaCalendarCalendarCheckCalendarXCalendarPlusClockBellPessoas
tone=salviaUserUsersUserPlusUserCheckUserXBriefcaseStarHeartComunicação
tone=tealMessageCircleMessageSquareSendMailPhoneAtSignVideoMicComércio & Financeiro
tone=violetaDollarSignCreditCardReceiptWalletTrendingUpTrendingDownPieChartBarChart3UI & Navegação
tone=cinzaHomeLayoutGridListMenuSearchFilterSettingsMoreVerticalArrowLeftArrowRightChevronRightChevronDownAções
tone=magentaPlusEditTrash2SaveCopyShare2DownloadUploadEyeEyeOffRefreshCcwExternalLinkStatus & Feedback
tone=grafiteCheckXAlertTriangleAlertCircleInfoSparklesAwardZapMídia & Conteúdo
tone=terracotaImageCameraFileTextFolderPaperclipGlobeMapPinDomínio Practix
tone=salviaScissorsCoffeeSunMoonActivityInboxAcessibilidade
Decorativo
Ícone que aparece junto a texto explicativo. Não é alvo de interação por si só.
<Calendar size={16} aria-hidden="true" />
<span>14 de junho</span>Funcional
Ícone que é o único conteúdo do botão (sem texto visível). Precisa de label pra leitor de tela.
<button aria-label="Excluir agendamento">
<Trash2 size={16} aria-hidden="true" />
</button>05 · Biblioteca
Componentes
Primitivos atuais do Practix DS. Roadmap de novos primitivos (Input, Select, Tabs, DataTable, Dialog) na seção 6.2 do doc canônico, criados sob demanda real.
Badge
Rótulo curto de estado ou categoria. Cores de status seguem a regra reservada.
Sem ícone
Com ícone
<Badge variant="success">Confirmado</Badge>
<Badge variant="success" icon={<Check size={11} />}>Pago</Badge>Card
Surface tonal em 4 tons. Substitui shadow pesado por elevação tonal warm-cream.
Tom default
Branco sobre cream. Padrão pra conteúdo focal do dashboard.
Tom warm
Terracota muito suave. Empty states acolhedores e prompts.
Tom cream
Bege. Benchmarks, callouts secundários, painéis laterais.
Tom muted
Zinc-50 quase invisível. Cards aninhados e agrupadores.
<Card tone="default">
<CardHeader><CardTitle>Receita do mês</CardTitle></CardHeader>
<CardDescription>Soma de comandas pagas em maio.</CardDescription>
</Card>Alert
Mensagem inline em 4 tons reservados. role='status' pra success/warning/info, role='alert' pra error.
Agendamento confirmado
Plano vence em 3 dias
Falha ao salvar
Novidade na agenda
<Alert tone="success" title="Agendamento confirmado">
Cliente recebeu confirmação por WhatsApp.
</Alert>
<Alert tone="error" title="Falha ao salvar" onClose={() => setOpen(false)}>
Verifique a conexão e tente novamente.
</Alert>EmptyState
Estado vazio padronizado. Título diz o que falta, descrição chama pra próxima ação.
Nenhuma mensagem por enquanto
Quando um cliente mandar WhatsApp, a conversa aparece aqui pra responder ou deixar a IA assumir.
<EmptyState
icon={Inbox}
title="Nenhuma mensagem por enquanto"
description="Quando um cliente mandar WhatsApp, a conversa aparece aqui."
actionLabel="Conectar WhatsApp"
actionHref="/app/configuracoes?tab=whatsapp"
/>Avatar
Foto de usuário/profissional/cliente. Fallback de initials tonal quando sem imagem.
Sizes
Tons (fallback)
<Avatar name="Marcos Silva" size={40} />
<Avatar name="Camila" tone="magenta" />
<Avatar src="/avatars/cliente.jpg" name="Pedro" size={56} />Tooltip
Dica curta no hover/focus. Bom pra ícones, labels técnicos (CNPJ), botões sem texto.
<Tooltip content="Adicionar novo agendamento">
<Button size="icon" aria-label="Adicionar"><Plus size={16} /></Button>
</Tooltip>Skeleton
Placeholder com shimmer. Reserva espaço exato do conteúdo final pra evitar layout shift.
<Skeleton shape="circle" className="h-12 w-12" />
<Skeleton shape="text" className="w-1/3" />
<Skeleton className="h-32 w-full" />Spinner
Indicador mudo de loading. Pra IA, prefira mensagens intermediárias didáticas em vez disso.
<Spinner size="md" />
<Spinner size="lg" tone="action" label="Carregando agenda" />Inputs especializados
Inputs pt-BR de fábrica: telefone com máscara dinâmica, moeda BRL, upload de imagem com preview.
Telefone (PhoneInput)
<PhoneInput name="phone" required />
import { CurrencyInput } from "@/components/ui/CurrencyInput"
<CurrencyInput name="price" />
import { UploadImage } from "@/components/ui/UploadImage"
<UploadImage shape="circle" onUpload={handleUpload} />06 · Padrões
Padrões
Páginas de estado
Componentes em @/components/ds/StatePages. Aplicadas automaticamente em not-found.tsx, error.tsx e loading.tsx do Next 16.
Carregando...
Página não encontrada
O endereço que você abriu não existe ou foi removido.
Algo deu errado
Tentamos processar sua ação mas tivemos um problema.
Acesso restrito
Você não tem permissão pra ver essa página.
Sem conexão com o sistema
Não conseguimos falar com nossos servidores agora.
// src/app/not-found.tsx
import { NotFoundPage } from "@/components/ds/StatePages"
export default function NotFound() {
return <NotFoundPage />
}
// src/app/error.tsx
"use client"
import { ErrorPage } from "@/components/ds/StatePages"
export default function Error({ error, reset }) {
return <ErrorPage reset={reset} />
}Voz e copy
Copy é parte do design system, não decoração. Microempresário BR é frustrado por SaaS gringo traduzido que soa robótico. Calor de copy diferencia mais que cor de botão.
Regras de copy
- Sem em-dash (—) em texto visível. Reescreva com ponto ou vírgula.
- Sem anglicismo gratuito. 'Onboarding' → 'Ativação'.
- Pessoa: 'você' direto, não 'vamos juntos' institucional.
- Botão usa verbo + objeto: 'Cadastrar cliente', não 'Cadastrar'.
- Empty state chama pra ação, não só descreve a ausência.
- Erro de validação: específico e acionável, não genérico.
- Máximo 1 ! por frase. Emoji com moderação.
Acessibilidade
Contraste
AA mínimo 4.5:1
Todo texto pequeno (< 18pt) verificado via axe-core emtest/e2e/accessibility.spec.ts. Quando decorativo não passa, criamos variante -text.
Focus visible
Outline 2px sólido
Aplicado em layer base. Nunca remover sem substituir por visual alternativo.
Teclado
Tab, Enter, Escape
- Tab navega entre interativos
- Enter ativa ação primária
- Escape fecha modal/drawer
- Espaço marca/desmarca checkbox
Ícones
aria-hidden ou aria-label
Decorativo recebe aria-hidden="true". Funcional sem texto visível recebe aria-label explícito.
Checklist pré-entrega
Antes de marcar tela como pronta, percorra estes 10 itens. Pode virar comando do Claude (/check-ui) numa próxima iteração.
- Tokens semânticos em vez de hex hardcoded
- Status colors (success/warning/error/info) só pra mensagens de sistema
- On-color pareado pra qualquer fundo colorido (texto AA garantido)
- Contraste AA verificado em texto pequeno
- Focus ring visível (não removido com outline:none)
- Aria-label em botão sem texto visível
- Sem em-dash em texto visível
- Empty state com call to action explícito
- Loading didático (não 'carregando...' mudo) em fluxos de IA
- Erro acionável: diz o que fazer pra resolver