Estilos e customização
A AlpopUI expõe um contrato de tema via CSS custom properties (--alpop-*). Um app consumidor sobrescreve qualquer um desses tokens no seu próprio :root, depois de importar @alpop/ui/styles, e os componentes se re-tematizam, sem rebuild da biblioteca e sem precisar do código SCSS.
@import "@alpop/ui/styles";
:root {
--alpop-color-primary: #0066cc;
}Com isso, o preenchimento dos botões primários, estados ativos e a maioria das superfícies de marca passam a usar o novo azul.
Pré-requisito: fonte raiz de 62,5%
Os componentes usam rem assumindo html { font-size: 62.5% } (logo, 1.5rem = 15px). Sem isso todos os tamanhos saem errados. É o primeiro item a conferir quando o layout parece fora de escala. Veja Começando.
Tokens públicos
Estes 28 tokens são o contrato. São estáveis: um rename é uma mudança breaking. Tudo o que não está aqui (nomes de classe, estrutura de DOM, as variáveis SCSS internas) é privado e pode mudar sem aviso.
Marca
| Token | Padrão |
|---|---|
--alpop-color-primary | #8E49D6 |
--alpop-color-primary-dark | #7D3CC7 |
--alpop-color-primary-light | #F3E9FC |
--alpop-color-primary-high-light | #F4ECFB |
--alpop-color-primary-ultra-light | #FAF7FE |
--alpop-color-primary-soft-border | derivado de --alpop-color-primary via color-mix |
--alpop-color-primary-outline-border | derivado de --alpop-color-primary via color-mix |
Texto
| Token | Padrão |
|---|---|
--alpop-color-text-primary | #29304D |
--alpop-color-text-secondary | #66708F |
--alpop-color-text-muted | #8A92A8 |
--alpop-color-text-navy | #31375F |
Superfície e borda
| Token | Padrão |
|---|---|
--alpop-color-white | #FFFFFF |
--alpop-color-surface-soft | #FAF7FE |
--alpop-color-page-background | #FAFAFD |
--alpop-color-border-soft | #E7E3EF |
--alpop-color-input-border-hover | #E0DDE8 |
Status
| Token | Padrão |
|---|---|
--alpop-color-success | #16964B |
--alpop-color-success-text | #16813D |
--alpop-color-success-background | #EAF8EF |
--alpop-color-info | #4169E1 |
--alpop-color-info-background | #EEF3FF |
--alpop-color-danger | #E53935 |
--alpop-color-danger-background | #FDECEC |
--alpop-color-warning | #F97316 |
--alpop-color-warning-background | #FFF1E6 |
Estado desabilitado e efeito
| Token | Padrão |
|---|---|
--alpop-color-disabled-text | #A2A8B8 |
--alpop-color-disabled-background | #EEF0F4 |
--alpop-shadow-subtle | 0 1px 2px rgba(20, 24, 40, 0.03) |
Tokens que andam juntos
Alguns tokens formam pares ou grupos. Ao sobrescrever um, considere os relacionados para não obter um resultado desalinhado:
- Marca:
--alpop-color-primarye--alpop-color-primary-dark(estados hover/ativo derivam do par). Os tokens--alpop-color-primary-soft-bordere--alpop-color-primary-outline-borderacompanham automaticamente o primary (viacolor-mix), então não precisam ser tocados. - Status: cada cor de status tem seu par
-background(esuccesstambém tem-text). Trocar só um dos dois gera contraste inconsistente.
Limitação conhecida: escurecimento de hover no botão primário
As bordas e superfícies translúcidas derivadas do primary já acompanham um override (usam color-mix). Resta um caso: o estado hover do botão sólido (primary / outline), que escurece a cor via color.adjust($primary-color, $lightness: -5%) em tempo de build. Um clareamento/escurecimento por lightness HSL não tem equivalente estático em var(), então esse hover específico continua calculado sobre o roxo original mesmo após trocar --alpop-color-primary: o estado de repouso re-tematiza, o hover escurecido não.
Na prática, ao trocar o primary, o hover do botão fica levemente fora de tom (um escurecimento do roxo antigo em cima da nova cor de repouso). Fechar isso por completo exigiria um token --alpop-color-primary-hover explícito, fora do escopo desta primeira versão do contrato. Se precisar do efeito exato, sobrescreva o estado hover do botão no app.
O que evitar
:deep()sobre estilos internos. A maioria dos componentes usa<style scoped>;:deep()alcança a árvore interna, que não é contrato e pode mudar em qualquer versão. Prefira sobrescrever um token.- Seletores estruturais (
.card > div > span:nth-child(2)). Pelo mesmo motivo: a estrutura não é contrato, os tokens são. !importantpara vencer a biblioteca. Se um ajuste só funciona assim, normalmente falta uma prop, um slot, ou um token novo. Vale abrir a questão.
Quando o token não basta
Se vários apps precisam do mesmo ajuste, ou se a mudança é de comportamento e não só de aparência, o lugar certo é a biblioteca: um novo token, uma prop, uma variante ou um slot. Novos tokens podem ser promovidos sem quebrar nada; o contrato só cresce.