UX/UI — Fundamentos
Princípios de design, grid, espaçamento, botões, formulários, cards, modais, notificações, tabelas e tabs.
Princípios de Design
O design de interfaces da Sysled segue princípios que refletem os valores da marca e garantem uma experiência de usuário consistente, eficiente e profissional em todas as plataformas.
| Princípio | Descrição | Aplicação |
|---|---|---|
| Clareza | Informações devem ser apresentadas de forma direta e sem ambiguidade | Labels claros, hierarquia visual definida, feedback imediato |
| Consistência | Padrões visuais e de interação devem ser uniformes em toda a plataforma | Componentes reutilizáveis, tokens de design, nomenclatura padronizada |
| Eficiência | O usuário deve completar tarefas com o mínimo de esforço possível | Atalhos, autocompletar, valores padrão inteligentes, fluxos otimizados |
| Confiabilidade | O sistema deve transmitir segurança e previsibilidade | Estados de loading, confirmações, tratamento de erros, dados em tempo real |
| Acessibilidade | A interface deve ser utilizável por todos, independente de limitações | Contraste WCAG AA, navegação por teclado, textos alternativos, ARIA labels |
Sistema de Grid
A Sysled utiliza um sistema de grid de 12 colunas com gutters de 24px para layouts desktop e 16px para mobile. O grid garante alinhamento consistente e facilita a criação de layouts responsivos.
| Breakpoint | Largura | Colunas | Gutter | Margem |
|---|---|---|---|---|
| Mobile | 360px — 767px | 4 | 16px | 16px |
| Tablet | 768px — 1023px | 8 | 20px | 24px |
| Desktop | 1024px — 1439px | 12 | 24px | 32px |
| Large Desktop | 1440px+ | 12 | 24px | Auto (max 1200px) |
Sistema de Espaçamento
O sistema de espaçamento da Sysled é baseado em uma escala de 4px, garantindo consistência e harmonia visual em todos os componentes e layouts. Cada token de espaçamento é um múltiplo de 4px.
xs
sm
md
lg
xl
2xl
3xl
4xl
5xl
Border Radius
| Token | Valor | Uso |
|---|---|---|
| radius-sm | 4px | Badges, tags, chips |
| radius-md | 8px | Botões, inputs, dropdowns |
| radius-lg | 12px | Cards pequenos, tooltips |
| radius-xl | 16px | Cards grandes, modais, painéis |
| radius-full | 9999px | Avatares, pills, toggles |
Botões
Os botões são os principais elementos de ação da interface. Devem ser claramente distinguíveis e hierarquizados conforme a importância da ação que representam.
Hierarquia de Botões
Especificações
| Propriedade | Small | Default | Large |
|---|---|---|---|
| Padding | 8px 20px | 12px 28px | 16px 36px |
| Font Size | 0.8rem | 0.875rem | 1rem |
| Font Weight | 600 | 600 | 600 |
| Border Radius | 8px | 8px | 8px |
| Min Height | 32px | 40px | 48px |
Formulários e Inputs
Os campos de formulário devem ser claros, acessíveis e fornecer feedback visual imediato ao usuário. Labels devem estar sempre visíveis (não usar apenas placeholders como labels).
Especificações de Input
| Propriedade | Valor |
|---|---|
| Padding | 12px 16px |
| Border | 1.5px solid #E2E8F0 |
| Border (focus) | 1.5px solid #3A8FD6 |
| Border Radius | 8px |
| Font Size | 0.9rem (Inter) |
| Label Font Size | 0.8rem (Inter, weight 600) |
| Focus Ring | 0 0 0 3px rgba(58,143,214,0.15) |
| Min Height | 44px |
Cards
Cards são containers versáteis para agrupar informações relacionadas. Na plataforma Sysled, são utilizados para módulos, métricas, alertas e itens de lista. O design deve ser limpo, com hierarquia clara entre título, conteúdo e ações.
Gestão de Estoque
Controle inteligente de entradas, saídas e posições de estoque em tempo real com alertas automáticos.
Automação Logística
Automatize separações, alocações e movimentações com regras configuráveis por operação.
Especificações de Card
| Propriedade | Valor |
|---|---|
| Padding | 24px — 32px |
| Border | 1px solid #E2E8F0 |
| Border Radius | 16px |
| Background | #FFFFFF |
| Hover Shadow | 0 8px 32px rgba(0,0,0,0.08) |
| Hover Transform | translateY(-2px) |
| Transition | 0.3s ease |
Modais e Dialogs
Modais são sobreposições que capturam a atenção do usuário para ações importantes — confirmações, formulários, alertas e visualização de conteúdo. Devem ser usados com parcimônia para não interromper o fluxo.
Tipos de Modal
<!-- Modal Overlay -->
<div class="modal-overlay" id="myModal">
<div class="modal-container modal-md">
<!-- Header -->
<div class="modal-header">
<h3>Título do Modal</h3>
<button class="modal-close" aria-label="Fechar">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="2">
<line x1="18" y1="6" x2="6" y2="18"/>
<line x1="6" y1="6" x2="18" y2="18"/>
</svg>
</button>
</div>
<!-- Body -->
<div class="modal-body">
<p>Conteúdo do modal aqui.</p>
</div>
<!-- Footer -->
<div class="modal-footer">
<button class="btn-secondary-sm">Cancelar</button>
<button class="btn-primary-sm">Confirmar</button>
</div>
</div>
</div>
<!-- Tamanhos: modal-sm | modal-md | modal-lg | modal-full -->
<!-- Dialog de Confirmação -->
<div class="modal-overlay">
<div class="modal-container modal-sm">
<div class="modal-body" style="text-align:center; padding-top:2rem;">
<div class="modal-icon modal-icon-warning">!</div>
<h3>Tem certeza?</h3>
<p>Esta ação não pode ser desfeita.</p>
</div>
<div class="modal-footer" style="justify-content:center;">
<button class="btn-secondary-sm">Cancelar</button>
<button class="btn-danger-sm">Excluir</button>
</div>
</div>
</div>
<!-- Dialog de Alerta / Sucesso -->
<div class="modal-overlay">
<div class="modal-container modal-sm">
<div class="modal-body" style="text-align:center; padding-top:2rem;">
<div class="modal-icon modal-icon-success">✓</div>
<h3>Operação concluída</h3>
<p>Os dados foram salvos com sucesso.</p>
</div>
<div class="modal-footer" style="justify-content:center;">
<button class="btn-primary-sm">OK</button>
</div>
</div>
</div>
/* Modal Overlay */
.modal-overlay {
position: fixed;
inset: 0;
background: rgba(15, 23, 42, 0.6);
backdrop-filter: blur(4px);
display: flex;
align-items: center;
justify-content: center;
z-index: 1000;
padding: 1rem;
animation: modalFadeIn 0.2s ease;
}
@keyframes modalFadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
@keyframes modalSlideUp {
from { opacity: 0; transform: translateY(16px) scale(0.98); }
to { opacity: 1; transform: translateY(0) scale(1); }
}
/* Modal Container */
.modal-container {
background: #FFFFFF;
border-radius: 16px;
box-shadow: 0 8px 40px rgba(0,0,0,0.15),
0 2px 12px rgba(0,0,0,0.08);
width: 100%;
max-height: 85vh;
overflow: hidden;
display: flex;
flex-direction: column;
animation: modalSlideUp 0.25s ease;
}
/* Tamanhos */
.modal-sm { max-width: 400px; }
.modal-md { max-width: 560px; }
.modal-lg { max-width: 720px; }
.modal-full { max-width: 95vw; max-height: 95vh; }
/* Header */
.modal-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 1.25rem 1.5rem;
border-bottom: 1px solid #E2E8F0;
}
.modal-header h3 {
font-family: 'Montserrat', sans-serif;
font-size: 1.1rem;
font-weight: 700;
color: #0F172A;
margin: 0;
}
.modal-close {
width: 32px; height: 32px;
border: none; background: transparent;
border-radius: 8px; cursor: pointer;
display: flex; align-items: center;
justify-content: center; color: #64748B;
transition: all 0.15s;
}
.modal-close:hover {
background: #F1F5F9; color: #0F172A;
}
.modal-close svg { width: 18px; height: 18px; }
/* Body */
.modal-body {
padding: 1.5rem;
overflow-y: auto;
flex: 1;
}
/* Footer */
.modal-footer {
display: flex;
align-items: center;
justify-content: flex-end;
gap: 0.75rem;
padding: 1rem 1.5rem;
border-top: 1px solid #E2E8F0;
}
/* Icon para Dialogs */
.modal-icon {
width: 56px; height: 56px;
border-radius: 50%;
display: flex; align-items: center;
justify-content: center;
font-size: 1.5rem; font-weight: 700;
margin: 0 auto 1rem;
}
.modal-icon-warning {
background: #FEF3C7; color: #D97706;
}
.modal-icon-success {
background: #D1FAE5; color: #059669;
}
.modal-icon-danger {
background: #FEE2E2; color: #DC2626;
}
.modal-icon-info {
background: #DBEAFE; color: #2B6CB0;
}
/* Botão Danger */
.btn-danger-sm {
padding: 8px 20px;
font-family: 'Montserrat', sans-serif;
font-size: 0.8rem; font-weight: 600;
border: none; border-radius: 8px;
background: #DC2626; color: #FFFFFF;
cursor: pointer; transition: all 0.15s;
}
.btn-danger-sm:hover { background: #C53030; }
Modal Padrão (md)
Dialog de Confirmação (sm)
Dialog de Sucesso (sm)
Tamanhos de Modal
Diretrizes de Uso
| Diretriz | Descrição |
|---|---|
| Focus Trap | O foco do teclado deve ficar preso dentro do modal enquanto aberto. Tab e Shift+Tab ciclam entre os elementos focáveis. |
| Fechar com ESC | Pressionar Escape deve fechar o modal. Clicar no overlay (fundo escuro) também fecha. |
| Animação | Entrada com fadeIn (overlay) + slideUp (container). Duração: 200-250ms, easing: ease. |
| Scroll do Body | Adicionar overflow: hidden ao <body> quando modal está aberto para evitar scroll duplo. |
| ARIA | Usar role="dialog", aria-modal="true", e aria-labelledby apontando para o título. |
| Mobile | Em telas < 640px, modais devem expandir para largura total com border-radius apenas no topo (estilo bottom sheet). |
| Ações Destrutivas | Botões de exclusão/perigo usam cor #DC2626. Sempre exigir confirmação explícita. |
| Máximo de Profundidade | Evitar modais sobre modais (máximo 1 nível). Para fluxos complexos, usar stepper dentro do modal. |
Especificações
| Propriedade | Valor |
|---|---|
| Overlay Background | rgba(15, 23, 42, 0.6) + backdrop-filter: blur(4px) |
| Container Border Radius | 16px |
| Container Shadow | 0 8px 40px rgba(0,0,0,0.15), 0 2px 12px rgba(0,0,0,0.08) |
| Header Padding | 1.25rem 1.5rem |
| Body Padding | 1.5rem |
| Footer Padding | 1rem 1.5rem |
| Header Border | 1px solid #E2E8F0 |
| Footer Border | 1px solid #E2E8F0 |
| Close Button | 32px × 32px, radius 8px |
| Title Font | Montserrat, 1.1rem, weight 700 |
| Max Height | 85vh (body com overflow-y: auto) |
| Z-Index | 1000 (--z-modal) |
| Animation | fadeIn 200ms + slideUp 250ms, easing: ease |
Notificações e Toasts
Notificações fornecem feedback contextual ao usuário sobre ações realizadas, erros, avisos ou informações importantes. Toasts são temporários e não-intrusivos; Banners são persistentes e exigem ação.
Toast Notifications
<!-- Toast Container (posição fixa) -->
<div class="toast-container" role="alert" aria-live="polite">
<!-- Toast Sucesso -->
<div class="toast toast-success">
<div class="toast-icon">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="2">
<polyline points="20 6 9 17 4 12"/>
</svg>
</div>
<div class="toast-content">
<strong>Sucesso</strong>
<span>Produto cadastrado com sucesso.</span>
</div>
<button class="toast-close" aria-label="Fechar">×</button>
<div class="toast-progress"></div>
</div>
<!-- Toast Erro -->
<div class="toast toast-error">...</div>
<!-- Toast Aviso -->
<div class="toast toast-warning">...</div>
<!-- Toast Info -->
<div class="toast toast-info">...</div>
</div>
<!-- Banner de Alerta (inline, persistente) -->
<div class="alert-banner alert-warning">
<div class="alert-icon">⚠</div>
<div class="alert-content">
<strong>Atenção:</strong>
Sua licença expira em 7 dias.
<a href="#">Renovar agora</a>
</div>
<button class="alert-dismiss">×</button>
</div>
/* Toast Container */
.toast-container {
position: fixed;
top: 1.5rem;
right: 1.5rem;
z-index: 9999;
display: flex;
flex-direction: column;
gap: 0.75rem;
max-width: 380px;
}
/* Toast Base */
.toast {
display: flex;
align-items: flex-start;
gap: 0.75rem;
padding: 1rem 1.25rem;
border-radius: 12px;
background: #FFFFFF;
border-left: 4px solid;
box-shadow: 0 4px 20px rgba(0,0,0,0.12),
0 1px 4px rgba(0,0,0,0.06);
animation: toastSlideIn 0.3s ease;
position: relative;
overflow: hidden;
}
@keyframes toastSlideIn {
from { opacity: 0; transform: translateX(100%); }
to { opacity: 1; transform: translateX(0); }
}
/* Variantes */
.toast-success { border-color: #059669; }
.toast-error { border-color: #DC2626; }
.toast-warning { border-color: #D97706; }
.toast-info { border-color: #2B6CB0; }
/* Ícone */
.toast-icon {
width: 24px; height: 24px;
border-radius: 50%; flex-shrink: 0;
display: flex; align-items: center;
justify-content: center;
}
.toast-icon svg { width: 16px; height: 16px; }
.toast-success .toast-icon { background: #D1FAE5; color: #059669; }
.toast-error .toast-icon { background: #FEE2E2; color: #DC2626; }
.toast-warning .toast-icon { background: #FEF3C7; color: #D97706; }
.toast-info .toast-icon { background: #DBEAFE; color: #2B6CB0; }
/* Alert Banners (inline) */
.alert-banner {
display: flex;
align-items: center;
gap: 0.75rem;
padding: 0.875rem 1.25rem;
border-radius: 10px;
border: 1px solid;
font-family: 'Inter', sans-serif;
font-size: 0.875rem;
}
.alert-success { background: #F0FDF4; border-color: #BBF7D0; color: #166534; }
.alert-error { background: #FEF2F2; border-color: #FECACA; color: #991B1B; }
.alert-warning { background: #FFFBEB; border-color: #FDE68A; color: #92400E; }
.alert-info { background: #EFF6FF; border-color: #BFDBFE; color: #1E40AF; }
Toast Notifications
Alert Banners (Inline)
Banners são persistentes e exibidos inline no conteúdo. Usados para mensagens que exigem atenção contínua ou ação do usuário.
Posicionamento dos Toasts
Diretrizes de Uso
| Diretriz | Descrição |
|---|---|
| Duração | Toasts de sucesso/info: 5 segundos. Toasts de erro: persistente até dismiss manual. Avisos: 8 segundos. |
| Empilhamento | Máximo de 3 toasts visíveis. Novos toasts empurram os anteriores para baixo. Mais antigos saem primeiro (FIFO). |
| Progress Bar | Barra de progresso na base indica tempo restante antes do auto-dismiss. Pausar ao hover. |
| Ações | Toasts podem ter 1 ação (ex: "Desfazer"). Máximo de 1 botão de ação + botão de fechar. |
| ARIA | Container com role="alert" e aria-live="polite". Toasts de erro usar aria-live="assertive". |
| Mobile | Em telas < 640px, toasts ocupam largura total com margin: 0 1rem. Posição: bottom center (snackbar). |
| Banners vs Toasts | Usar toast para feedback de ação (salvar, excluir). Usar banner para estados persistentes (expiração, manutenção). |
| Cores Semânticas | Success: #059669 · Error: #DC2626 · Warning: #D97706 · Info: #2B6CB0 |
Especificações
| Propriedade | Toast | Banner |
|---|---|---|
| Max Width | 380px | 100% (inline) |
| Padding | 1rem 1.25rem | 0.875rem 1.25rem |
| Border Radius | 12px | 10px |
| Border | 4px left (cor semântica) | 1px solid (cor clara) |
| Background | #FFFFFF | Cor semântica clara (ex: #F0FDF4) |
| Shadow | 0 4px 20px rgba(0,0,0,0.12) | Nenhuma |
| Ícone | 28px circular com bg | Emoji ou SVG 1.1rem |
| Título | Montserrat, 0.85rem, 600 | Inter bold inline |
| Texto | Inter, 0.8rem, #64748B | Inter, 0.875rem |
| Animação | slideIn 300ms ease | Nenhuma (inline) |
| Z-Index | 9999 (--z-toast) | Herda do contexto |
| Auto-dismiss | 5s (progress bar) | Não (manual) |
Tabelas de Dados
Tabelas organizam dados estruturados em linhas e colunas para facilitar leitura, comparação e análise. No contexto Sysled, são usadas em listagens de produtos, relatórios logísticos, dashboards de estoque e históricos de operações.
Tabela Padrão
<div class="table-wrapper">
<table class="data-table">
<thead>
<tr>
<th><input type="checkbox" class="table-check" /></th>
<th class="sortable">Código <span class="sort-icon">▴▾</span></th>
<th class="sortable active-sort">Produto <span class="sort-icon">▴</span></th>
<th>Categoria</th>
<th class="text-right">Estoque</th>
<th class="text-right">Preço</th>
<th>Status</th>
<th>Ações</th>
</tr>
</thead>
<tbody>
<tr>
<td><input type="checkbox" class="table-check" /></td>
<td class="text-mono">#LED-001</td>
<td><strong>Painel Solar 200W</strong></td>
<td>Energia Solar</td>
<td class="text-right">1.240</td>
<td class="text-right">R$ 489,90</td>
<td><span class="badge badge-success">Ativo</span></td>
<td><button class="table-action">•••</button></td>
</tr>
</tbody>
</table>
</div>
/* Table Wrapper */
.table-wrapper {
overflow-x: auto;
border: 1px solid #E2E8F0;
border-radius: 12px;
}
.data-table {
width: 100%;
border-collapse: collapse;
font-family: 'Inter', sans-serif;
font-size: 0.875rem;
}
.data-table thead {
background: #F8FAFC;
border-bottom: 2px solid #E2E8F0;
}
.data-table th {
padding: 0.75rem 1rem;
font-family: 'Montserrat', sans-serif;
font-size: 0.75rem;
font-weight: 600;
color: #64748B;
text-transform: uppercase;
letter-spacing: 0.04em;
text-align: left;
}
.sortable { cursor: pointer; user-select: none; }
.sortable:hover { color: #2B6CB0; }
.active-sort { color: #2B6CB0; }
.data-table td {
padding: 0.75rem 1rem;
color: #334155;
border-bottom: 1px solid #F1F5F9;
}
.badge {
display: inline-block;
padding: 3px 10px;
border-radius: 99px;
font-size: 0.7rem;
font-weight: 600;
}
.badge-success { background: #D1FAE5; color: #059669; }
.badge-warning { background: #FEF3C7; color: #D97706; }
.badge-error { background: #FEE2E2; color: #DC2626; }
.badge-info { background: #DBEAFE; color: #2B6CB0; }
.badge-neutral { background: #F1F5F9; color: #64748B; }
Tabela com Sorting, Badges e Paginação
| Código ▴▾ | Produto ▴ | Categoria | Estoque | Preço | Status | Ações | |
|---|---|---|---|---|---|---|---|
| #LED-001 | Painel Solar 200W | Energia Solar | 1.240 | R$ 489,90 | Ativo | ••• | |
| #LED-042 | Luminária Industrial 150W | Iluminação | 856 | R$ 329,00 | Ativo | ••• | |
| #LED-078 | Refletor LED 500W IP65 | Iluminação Externa | 23 | R$ 1.249,00 | Baixo | ••• | |
| #LED-103 | Fita LED RGB 5m | Decoração | 0 | R$ 89,90 | Esgotado | ••• | |
| #LED-156 | Sensor de Presença PIR | Automação | 3.720 | R$ 42,50 | Ativo | ••• |
Badges de Status
Badges comunicam estados de forma visual e compacta. Use cores semânticas consistentes em toda a aplicação.
Variantes de Tabela
Diretrizes de Uso
| Diretriz | Descrição |
|---|---|
| Alinhamento | Texto à esquerda. Números à direita (para facilitar comparação). Ações centralizadas. |
| Sorting | Indicar coluna ativa com cor #2B6CB0. Setas ▴▾ para direção. Uma coluna ordenada por vez. |
| Seleção | Checkbox na primeira coluna. Header checkbox seleciona todos. Destacar row selecionada com background: #EFF6FF. |
| Empty State | Quando sem dados, exibir ilustração + mensagem + CTA. Nunca deixar a tabela vazia sem feedback. |
| Loading | Usar skeleton rows (5 linhas) com animação pulse enquanto dados carregam. |
| Paginação | Exibir "Mostrando X-Y de Z" + controles. Padrão: 10 itens/página. Opções: 10, 25, 50. |
| Responsivo | Desktop: tabela completa. Tablet: scroll horizontal. Mobile: card view com data-label em cada <td>. |
| Ações | Botão "•••" abre dropdown com opções (Editar, Duplicar, Excluir). Máximo 4 ações no dropdown. |
| Badges | Sempre usar white-space: nowrap. Texto curto (1 palavra). Se precisar de texto longo, abreviar e usar title para tooltip no hover. |
Especificações
| Propriedade | Valor |
|---|---|
| Wrapper Border | 1px solid #E2E8F0, radius 12px |
| Header Background | #F8FAFC |
| Header Font | Montserrat, 0.75rem, 600, uppercase, #64748B |
| Header Border | 2px solid #E2E8F0 (bottom) |
| Cell Padding | 0.75rem 1rem (padrão) · 0.5rem 0.75rem (compacta) |
| Cell Font | Inter, 0.875rem, #334155 |
| Row Border | 1px solid #F1F5F9 |
| Row Hover | background: #F8FAFC |
| Selected Row | background: #EFF6FF |
| Badge Padding | 3px 10px, radius 99px, font 0.7rem 600 |
| Pagination Button | 32px × 32px, radius 8px, border 1px #E2E8F0 |
| Checkbox | 16px × 16px, accent-color: #2B6CB0 |
Tabs e Navegação por Abas
Tabs organizam conteúdo em painéis alternáveis, permitindo ao usuário navegar entre seções relacionadas sem sair da página. Essenciais para dashboards, configurações e páginas de detalhe de produtos.
Variantes de Tabs
<!-- Tabs Underline (padrão) -->
<div class="tabs-container">
<nav class="tabs tabs-underline" role="tablist">
<button class="tab active" role="tab"
aria-selected="true" aria-controls="panel-1">
Visão Geral
</button>
<button class="tab" role="tab"
aria-selected="false" aria-controls="panel-2">
Estoque
</button>
<button class="tab" role="tab"
aria-selected="false" aria-controls="panel-3">
Histórico
</button>
<button class="tab disabled" role="tab"
aria-disabled="true">
Relatórios
</button>
</nav>
<div class="tab-panel active" id="panel-1" role="tabpanel">
<!-- Conteúdo da aba -->
</div>
</div>
<!-- Tabs Pill -->
<nav class="tabs tabs-pill" role="tablist">
<button class="tab active">Todos</button>
<button class="tab">Ativos</button>
<button class="tab">Inativos</button>
</nav>
<!-- Tabs Boxed -->
<nav class="tabs tabs-boxed" role="tablist">
<button class="tab active">Mensal</button>
<button class="tab">Trimestral</button>
<button class="tab">Anual</button>
</nav>
<!-- Tabs com Ícone + Badge -->
<nav class="tabs tabs-underline" role="tablist">
<button class="tab active">
<svg>...</svg> Pedidos
<span class="tab-badge">12</span>
</button>
<button class="tab">
<svg>...</svg> Devoluções
<span class="tab-badge">3</span>
</button>
</nav>
/* Tabs Container */
.tabs-container { width: 100%; }
/* Tabs Nav Base */
.tabs {
display: flex; gap: 0;
overflow-x: auto; scrollbar-width: none;
}
.tabs::-webkit-scrollbar { display: none; }
/* Tab Button Base */
.tab {
display: inline-flex; align-items: center; gap: 0.4rem;
padding: 0.75rem 1.25rem;
font-family: 'Montserrat', sans-serif;
font-size: 0.8rem; font-weight: 600;
color: #64748B; background: none;
border: none; cursor: pointer;
white-space: nowrap; transition: all 0.15s;
position: relative;
}
.tab:hover { color: #2B6CB0; }
.tab.disabled { opacity: 0.4; cursor: not-allowed; pointer-events: none; }
/* Underline Variant */
.tabs-underline { border-bottom: 2px solid #E2E8F0; }
.tabs-underline .tab { margin-bottom: -2px; border-bottom: 2px solid transparent; }
.tabs-underline .tab.active { color: #2B6CB0; border-bottom-color: #2B6CB0; }
/* Pill Variant */
.tabs-pill { background: #F1F5F9; border-radius: 10px; padding: 4px; gap: 4px; }
.tabs-pill .tab { border-radius: 8px; padding: 0.5rem 1rem; font-size: 0.75rem; }
.tabs-pill .tab.active { background: #FFFFFF; color: #0F172A; box-shadow: 0 1px 3px rgba(0,0,0,0.08); }
/* Boxed Variant */
.tabs-boxed { border: 1.5px solid #E2E8F0; border-radius: 10px; padding: 4px; gap: 0; display: inline-flex; }
.tabs-boxed .tab { border-radius: 8px; padding: 0.5rem 1.25rem; font-size: 0.75rem; }
.tabs-boxed .tab.active { background: #2B6CB0; color: #FFFFFF; }
/* Tab Badge */
.tab-badge {
display: inline-flex; align-items: center; justify-content: center;
min-width: 20px; height: 20px; border-radius: 99px;
font-size: 0.65rem; font-weight: 700;
background: #EFF6FF; color: #2B6CB0; padding: 0 5px;
}
.tab.active .tab-badge { background: #2B6CB0; color: #FFFFFF; }
Underline (Padrão)
Pill
Boxed
Com Ícone + Badge
Diretrizes de Uso
| Diretriz | Descrição |
|---|---|
| Quando Usar | Para alternar entre visões do mesmo contexto (ex: abas de um produto). Não usar para navegação entre páginas diferentes — use menu/sidebar. |
| Quantidade | Mínimo 2, máximo 6 tabs. Se precisar de mais, agrupar ou usar dropdown "Mais ▾". |
| Labels | Curtos (1-2 palavras). Substantivos, não verbos. Capitalização: Title Case. |
| Underline vs Pill | Underline: navegação principal de página. Pill: filtros dentro de uma seção. Boxed: alternância binária/ternária (ex: período). |
| Estado Disabled | Opacidade 0.4, cursor: not-allowed. Usar tooltip para explicar por que está desabilitado. |
| Badge | Contador numérico ao lado do label. Ativo: fundo azul + texto branco. Inativo: fundo claro + texto azul. Máx 3 dígitos. |
| ARIA | role="tablist" no nav, role="tab" nos botões, role="tabpanel" nos painéis. aria-selected e aria-controls. |
| Teclado | Setas ← → navegam entre tabs. Enter/Space ativa. Home/End vai para primeira/última. |
| Mobile | Scroll horizontal com overflow-x: auto e scrollbar-width: none. Nunca quebrar tabs em múltiplas linhas. |
Especificações
| Propriedade | Underline | Pill | Boxed |
|---|---|---|---|
| Padding | 0.75rem 1.25rem | 0.5rem 1rem | 0.5rem 1.25rem |
| Font Size | 0.8rem | 0.75rem | 0.75rem |
| Font | Montserrat, weight 600 | ||
| Cor Inativo | #64748B | ||
| Cor Ativo | #2B6CB0 | #0F172A | #FFFFFF |
| Indicador Ativo | Border-bottom 2px #2B6CB0 | Background #FFF + shadow | Background #2B6CB0 |
| Container | Border-bottom 2px #E2E8F0 | Background #F1F5F9, radius 10px | Border 1.5px #E2E8F0, radius 10px |
| Gap | 0 | 4px | 0 |
| Tab Radius | Nenhum | 8px | 8px |
| Badge Size | 20px height, radius 99px, font 0.65rem 700 | ||
| Transição | all 0.15s ease | ||
Grid System & Layout Columns
Sistema de grid responsivo de 12 colunas com gutters, breakpoints e containers — a base estrutural para layouts industriais consistentes em qualquer viewport.
| Propriedade | Valor | Nota |
|---|---|---|
| Colunas | 12 | Divisível por 2, 3, 4, 6 |
| Gutter XS–SM | 16px | Mobile compact |
| Gutter MD–LG | 24px | Tablet e desktop |
| Gutter XL–XXL | 32px | Widescreen |
| Container Max | 1320px | XXL breakpoint |
| Margin XS | 16px | Padding horizontal do body |
| Margin LG+ | auto (centered) | Container centralizado |
| Sidebar Width | 3 colunas (268px em LG) | Collapsa para icon-only em MD |
- Usar frações do grid (3, 4, 6, 8, 12)
- Alinhar elementos às linhas do grid
- Testar layout em todos os breakpoints
- Colapsar colunas em stack no mobile
- Manter gutters consistentes por breakpoint
- Usar valores fixos de px para largura
- Misturar gutters de diferentes breakpoints
- Colunas de 5 ou 7 (não divide por 12)
- Ignorar margins em mobile
- Nesting de grids > 2 níveis
Motion & Animation Tokens
Curvas de easing, durações padronizadas e tipos de transição — garantindo movimento consistente, fluido e com propósito em toda a plataforma SYSLED.
--duration-instant
--duration-fast
--duration-normal
--duration-slow
--duration-xslow
cubic-bezier(0, 0, 0.2, 1)
cubic-bezier(0.4, 0, 1, 1)
cubic-bezier(0.4, 0, 0.2, 1)
| Token | Valor | Uso |
|---|---|---|
--duration-instant | 100ms | Hover, focus, color change |
--duration-fast | 150ms | Toggle, switch, tooltip show |
--duration-normal | 200ms | Dropdown, accordion, fade |
--duration-slow | 300ms | Modal, page, complex enter |
--duration-xslow | 500ms | Drawer, full-screen transition |
--ease-enter | cubic-bezier(0, 0, 0.2, 1) | Elemento entrando no viewport |
--ease-exit | cubic-bezier(0.4, 0, 1, 1) | Elemento saindo do viewport |
--ease-morph | cubic-bezier(0.4, 0, 0.2, 1) | Transformação no local |
--ease-spring | cubic-bezier(0.34, 1.56, 0.64, 1) | Bounce sutil (feedback) |
- Usar ease-out para enter, ease-in para exit
- Respeitar prefers-reduced-motion
- Manter durações ≤ 500ms (exceto loading)
- Animar transform e opacity (GPU-accelerated)
- Usar tokens, nunca valores hardcoded
- Animar width, height, top, left (layout thrash)
- Animações > 1s para micro-interactions
- Bounce excessivo em contexto industrial
- linear ease para UI (só para progress bars)
- Ignorar acessibilidade de motion
Accessibility (a11y) Guidelines
Diretrizes de acessibilidade WCAG 2.1 AA aplicadas ao contexto industrial SYSLED — contraste, navegação por teclado, screen readers, focus management e aria-labels.
/* Botão com ação descrita */ <button aria-label="Fechar modal"> <svg>...</svg> </button> /* Live region para alertas */ <div role="alert" aria-live="assertive"> OEE caiu para 45% — ação necessária </div> /* Toggle com estado */ <button role="switch" aria-checked="true" aria-label="Notificações push"> </button> /* Tabela com caption */ <table aria-label="Equipamentos da Linha 3"> <thead>...</thead> </table>
| Critério | Requisito | Nota |
|---|---|---|
| Contraste Texto | 4.5:1 (normal) / 3:1 (large 18px+) | WCAG AA obrigatório |
| Contraste UI | 3:1 contra fundo adjacente | Ícones, bordas, focus rings |
| Focus Ring | 2px solid + 2px offset | Cor primary, visível em light e dark |
| Touch Target | Mínimo 44×44px | WCAG 2.5.5 (hit area, não visual) |
| Tab Order | Segue DOM order lógica | Nunca tabindex > 0 |
| Alt Text | Obrigatório em <img> informativos | Decorativos: alt="" (vazio) |
| aria-live | polite (toast) / assertive (alerta crítico) | Screen reader anuncia mudanças |
| Heading Hierarchy | Sequencial: h1 → h2 → h3 | Nunca pular níveis |
| Color Independence | Não depender só de cor para informação | Usar ícone + cor + texto |
| Zoom | Funcional até 200% zoom | Sem scroll horizontal |
- Usar :focus-visible (não :focus simples)
- Testar com screen reader (VoiceOver/NVDA)
- aria-label em botões com apenas ícone
- role="alert" para notificações críticas
- Garantir touch target 44×44px mínimo
- outline: none sem alternativa de foco
- Depender apenas de cor para status
- tabindex positivo (usar 0 ou -1)
- Texto em imagens (usar HTML real)
- Auto-play de mídia sem controle
Internationalization (i18n) Patterns
Padrões para suportar múltiplos idiomas e regiões na plataforma SYSLED — RTL, pluralização, formatação de datas/números/moedas, truncamento e adaptação de layouts.
// ICU Message Format {count, plural, =0 {Nenhum equipamento encontrado} one {{count} equipamento encontrado} other {{count} equipamentos encontrados} } // Resultado: 0 → "Nenhum equipamento encontrado" 1 → "1 equipamento encontrado" 5 → "5 equipamentos encontrados" // Data relativa {time, relative} → "há 2 minutos" | "2 minutes ago" | "vor 2 Minuten"
| Aspecto | Regra | Nota |
|---|---|---|
| Número | Usar Intl.NumberFormat(locale) | Separadores variam por região |
| Moeda | Símbolo + posição via Intl.NumberFormat | R$ antes, € depois (DE) |
| Data | Intl.DateTimeFormat(locale) | DD/MM (BR) vs MM/DD (US) |
| Hora | 24h (BR, DE) vs 12h AM/PM (US) | Configurável por preferência |
| Pluralização | ICU Message Format | Regras variam: árabe tem 6 formas |
| Text Expansion | Reservar +50% de espaço | DE e FR expandem significativamente |
| RTL | dir="rtl" + CSS logical props | margin-inline-start ao invés de margin-left |
| Ícones | Espelhar setas e chevrons em RTL | Não espelhar: relógio, check, play |
| Unidades | °C (métrico) / °F (imperial) | Configurável por tenant |
- Usar Intl APIs nativas do JS
- CSS logical properties (inline-start/end)
- Projetar com +50% de espaço para texto
- Externalizar todas as strings em arquivos i18n
- Testar UI com pseudo-locale (XXXXXX)
- Concatenar strings ("Olá " + nome)
- Hardcodar formatos de data/número
- Assumir que texto cabe em width fixo
- Usar margin-left/right (usar inline)
- Ignorar pluralização (!=1 não basta)