Meio Ambiente
Licenciamento, fiscalização e denúncias ambientais num fluxo só, do protocolo ao parecer.
Tokens, componentes e padrões em CSS puro. Uma folha de estilo, sem build no consumidor, sem framework — funciona igual num site em Astro e numa tela em PHP.
O símbolo é o raio dentro do anel. Ele é vetorial e vive em src/logo/ —
nunca reconstrua a marca a partir de um PNG.
currentColor, e um SVG carregado por
<img src> é um documento isolado: não herda a cor da página. Cole o
conteúdo do arquivo direto no HTML (ou use um componente que faça isso) — pela tag
<img> ela sai sempre preta.
| Arquivo | Quando usar |
|---|---|
si-mark.svg | Padrão. Fundo claro, disco branco próprio. |
si-mark-badge.svg | Avatar, favicon, perfil de rede social — onde a marca precisa do próprio fundo indigo. |
si-mark-mono.svg | Uma cor só, herdada por currentColor. Fundo escuro, carimbo, fax. |
A assinatura com o nome não é um arquivo — é composição, para o texto continuar sendo texto (selecionável, traduzível, legível por leitor de tela).
<a class="si-cluster" href="/" style="--si-gap: 12px">
<img src="/si-mark.svg" alt="" width="40" height="40">
<strong>SiteIncrível<sup>®</sup></strong>
</a>
® aparece na primeira ocorrência de cada página e no rodapé.
As duas cores da marca saíram do símbolo. As razões de contraste abaixo são
calculadas no build a partir do 00-tokens.css — se um par cair abaixo
do mínimo WCAG, o build quebra.
--si-gradient-deep vai de #1E3268 a #3A59AE.
Uma cor de texto conferida só contra o escuro passa no rodapé e reprova no topo do
hero — é assim que --si-color-highlight
(4.50:1 · reprovado sobre o
indigo claro) deixa de servir para texto. Use --si-color-on-brand.
--si-color-accent mede 4.54:1 · AA
sobre branco: passa AA para texto normal por uma margem de 0,04 e reprova em AAA. Link,
label e texto pequeno usam --si-color-accent-ink
(7.17:1 · AAA). O accent fica para
fundo de botão, onde é o branco por cima que precisa contrastar.
Lexend, escala 1.200. Os dois maiores tamanhos usam clamp() e encolhem
sozinhos no celular — não existe media query de tipografia.
--si-text-4xlGestão pública digital--si-text-3xlTítulo de seção--si-text-2xlSubtítulo--si-text-xlTítulo de card--si-text-lgParágrafo de abertura--si-text-mdCorpo de texto padrão--si-text-smApoio, label, legenda--si-text-xsBadge, nota de rodapé.si-measure faz isso. Acima disso o olho perde a linha ao voltar.
Base de 4px. O número no token é o multiplicador: --si-space-4 é 4×4=16px.
--si-space-14px--si-space-28px--si-space-312px--si-space-416px — respiro padrão dentro de componente--si-space-624px — padding de card--si-space-832px--si-space-1248px--si-space-1664px — entre seções no mobile--si-space-2496px — entre seções no desktoprgba(30,50,104,…). Preto puro sobre uma
paleta azul suja a cor e deixa a interface com aparência de fotocópia.
Quatro primitivas cobrem toda a composição. Nenhuma delas define cor.
Colunas que se reorganizam sozinhas. Você controla a largura mínima da coluna pela
variável --si-grid-min; não há media query.
.si-grid {
display: grid;
gap: var(--si-space-6);
grid-template-columns: repeat(auto-fit, minmax(min(var(--si-grid-min, 260px), 100%), 1fr));
}
.si-stack {
display: flex;
flex-direction: column;
gap: var(--si-gap, var(--si-space-4));
}
.si-cluster {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: var(--si-gap, var(--si-space-3));
}
.si-container {
width: 100%;
max-width: var(--si-container);
margin-inline: auto;
padding-inline: var(--si-container-pad);
}
.si-section {
padding-block: var(--si-space-16);
}
@media (min-width: 900px) {
.si-section {
padding-block: var(--si-space-24);
}
}
| Breakpoint | Valor | O que muda |
|---|---|---|
| sm | 640px | duas colunas em grades densas |
| md | 900px | menu vira horizontal, painel de nav passa a flutuar, seções ganham respiro |
| lg | 1200px | largura máxima do container |
@media não lê var(). Os três números acima aparecem crus no
CSS, e é por isso que estão documentados aqui: esta tabela é a fonte de verdade deles.
Funciona igual em <button> e <a>. Altura mínima
de 44px em todas as variantes — é o alvo de toque do WCAG 2.5.8.
<button class="si-btn si-btn--primary">Falar com especialista</button>
<a class="si-btn si-btn--outline" href="/solucoes">Ver soluções</a>
<button class="si-btn si-btn--primary si-btn--lg si-btn--block">Ocupa a linha</button>
.si-btn {
display: inline-flex;
align-items: center;
justify-content: center;
gap: var(--si-space-2);
min-height: 44px;
padding: var(--si-space-3) var(--si-space-5);
border: 1px solid transparent;
border-radius: var(--si-radius-md);
font-size: var(--si-text-md);
font-weight: var(--si-weight-semibold);
line-height: 1.2;
text-align: center;
text-decoration: none;
cursor: pointer;
transition: background-color var(--si-dur) var(--si-ease),
border-color var(--si-dur) var(--si-ease),
color var(--si-dur) var(--si-ease),
box-shadow var(--si-dur) var(--si-ease),
transform var(--si-dur-fast) var(--si-ease);
}
.si-btn:focus-visible {
outline: none;
box-shadow: var(--si-focus-ring);
}
.si-btn--primary {
background: var(--si-color-accent);
color: #FFFFFF;
}
.si-btn--primary:hover {
background: var(--si-color-accent-ink);
color: #FFFFFF;
}
.si-btn--outline {
background: transparent;
border-color: var(--si-color-line-strong);
color: var(--si-color-accent-ink);
}
.si-btn--outline:hover {
border-color: var(--si-color-accent-ink);
background: var(--si-color-accent-soft);
color: var(--si-color-accent-ink);
}
.si-btn--invert {
background: #FFFFFF;
color: var(--si-color-primary);
}
.si-btn--outline-invert {
background: transparent;
border-color: var(--si-color-on-brand-line);
color: #FFFFFF;
}
.si-btn--lg {
min-height: 52px;
padding: var(--si-space-4) var(--si-space-8);
font-size: var(--si-text-lg);
}
| Variante | Quando usar | Quantas por tela |
|---|---|---|
--primary | A ação que a página quer que aconteça | uma por bloco |
--brand | Ação principal em contexto institucional (indigo em vez de azul) | uma por bloco |
--outline | Alternativa legítima à ação principal | à vontade |
--ghost | Ação terciária, dentro de tabela ou toolbar | à vontade |
--invert | Sobre fundo escuro ou faixa de marca | uma por faixa |
--outline-invert | O secundário do --invert, na mesma faixa escura | à vontade |
outline:none.
Botão que só tem ícone precisa de aria-label.
Superfície de conteúdo. --interactive faz o card inteiro responder ao
clique, mas quem recebe o foco continua sendo o link de dentro — card não vira botão.
Licenciamento, fiscalização e denúncias ambientais num fluxo só, do protocolo ao parecer.
Abertura, triagem e SLA de atendimento interno.
Sem borda, com sombra. Use sobre fundo alternado.
<article class="si-card si-card--interactive">
<div class="si-card__body">
<span class="si-card__icon" aria-hidden="true"><svg …></svg></span>
<h3 class="si-card__title"><a href="/solucoes/meio-ambiente">Meio Ambiente</a></h3>
<p class="si-card__text">Licenciamento, fiscalização e denúncias…</p>
</div>
</article>
.si-card {
display: flex;
flex-direction: column;
background: var(--si-color-surface);
border: 1px solid var(--si-color-line);
border-radius: var(--si-radius-lg);
overflow: hidden;
}
.si-card__body {
display: flex;
flex-direction: column;
gap: var(--si-space-3);
padding: var(--si-space-6);
flex: 1;
}
.si-card__icon {
display: grid;
place-items: center;
width: 48px;
height: 48px;
border-radius: var(--si-radius-md);
background: var(--si-color-accent-soft);
color: var(--si-color-accent-ink);
margin-bottom: var(--si-space-1);
}
.si-card__title {
font-size: var(--si-text-xl);
margin: 0;
}
.si-card--interactive {
position: relative;
transition: border-color var(--si-dur) var(--si-ease),
box-shadow var(--si-dur) var(--si-ease),
transform var(--si-dur) var(--si-ease);
}
.si-card--interactive:hover {
border-color: var(--si-color-accent);
box-shadow: var(--si-shadow-lg);
transform: translateY(-2px);
}
.si-card--interactive:focus-within {
box-shadow: var(--si-focus-ring);
}
<a> faz o leitor de tela anunciar todo o
conteúdo como um único rótulo gigante. O ::after no título mantém o alvo
de clique grande e o rótulo do link curto.
Rótulo curto, nunca clicável. Se precisar clicar, é .si-btn--sm.
.si-badge {
display: inline-flex;
align-items: center;
gap: var(--si-space-1);
padding: 3px var(--si-space-2);
border-radius: var(--si-radius-sm);
font-size: var(--si-text-xs);
font-weight: var(--si-weight-semibold);
line-height: 1.5;
white-space: nowrap;
background: var(--si-color-surface-sunken);
color: var(--si-color-ink-soft);
width: fit-content;
}
.si-badge--accent {
background: var(--si-color-accent-soft);
color: var(--si-color-accent-ink);
}
.si-badge--pill {
border-radius: var(--si-radius-full);
padding-inline: var(--si-space-3);
}
.si-badge--eyebrow {
background: transparent;
color: var(--si-color-accent-ink);
padding: 0;
text-transform: uppercase;
letter-spacing: var(--si-tracking-wide);
}
width: fit-content porque num container flex em coluna —
como o .si-card__body — o item esticaria para a linha inteira, e
inline-flex não protege disso: quem decide o tamanho do item é o
container. Note que não é align-self: flex-start: isso
impediria o esticão mas também sequestraria o alinhamento do pai, e um
--eyebrow dentro de um cabeçalho centralizado iria parar na esquerda.
Todo campo tem <label> real ligado por for/id.
Placeholder não substitui rótulo: some quando a pessoa começa a digitar.
<div class="si-field">
<label class="si-field__label" for="email" data-required>E-mail</label>
<input class="si-input" id="email" type="email"
aria-invalid="true" aria-describedby="email-erro">
<span class="si-field__error" id="email-erro">Informe um e-mail completo.</span>
</div>
.si-field {
display: flex;
flex-direction: column;
gap: var(--si-space-2);
}
.si-field__label {
font-size: var(--si-text-sm);
font-weight: var(--si-weight-medium);
color: var(--si-color-ink);
}
.si-input {
width: 100%;
min-height: 44px;
padding: var(--si-space-3) var(--si-space-4);
border: 1px solid var(--si-color-line-strong);
border-radius: var(--si-radius-md);
background: var(--si-color-surface);
color: var(--si-color-ink);
font-size: var(--si-text-md);
transition: border-color var(--si-dur-fast) var(--si-ease),
box-shadow var(--si-dur-fast) var(--si-ease);
}
.si-select {
width: 100%;
min-height: 44px;
padding: var(--si-space-3) var(--si-space-4);
border: 1px solid var(--si-color-line-strong);
border-radius: var(--si-radius-md);
background: var(--si-color-surface);
color: var(--si-color-ink);
font-size: var(--si-text-md);
transition: border-color var(--si-dur-fast) var(--si-ease),
box-shadow var(--si-dur-fast) var(--si-ease);
}
.si-select {
appearance: none;
padding-right: var(--si-space-10);
background-image: linear-gradient(45deg, transparent 50%, currentColor 50%),
linear-gradient(135deg, currentColor 50%, transparent 50%);
background-position: calc(100% - 22px) calc(50% + 2px), calc(100% - 16px) calc(50% + 2px);
background-size: 6px 6px, 6px 6px;
background-repeat: no-repeat;
}
.si-textarea {
width: 100%;
min-height: 44px;
padding: var(--si-space-3) var(--si-space-4);
border: 1px solid var(--si-color-line-strong);
border-radius: var(--si-radius-md);
background: var(--si-color-surface);
color: var(--si-color-ink);
font-size: var(--si-text-md);
transition: border-color var(--si-dur-fast) var(--si-ease),
box-shadow var(--si-dur-fast) var(--si-ease);
}
.si-textarea {
min-height: 120px;
resize: vertical;
line-height: var(--si-leading-normal);
}
.si-field__error {
display: flex;
align-items: flex-start;
gap: var(--si-space-1);
font-size: var(--si-text-sm);
font-weight: var(--si-weight-medium);
color: var(--si-color-danger);
}
.si-check {
display: flex;
align-items: flex-start;
gap: var(--si-space-3);
cursor: pointer;
font-size: var(--si-text-sm);
line-height: var(--si-leading-snug);
}
aria-invalid="true" no controle,
aria-describedby apontando para a mensagem, e a mensagem em texto. Borda
vermelha sozinha reprova no WCAG 1.4.1 — quem não distingue a cor não fica sabendo de nada.
.si-alert {
display: flex;
gap: var(--si-space-3);
padding: var(--si-space-4) var(--si-space-5);
border-radius: var(--si-radius-md);
border-left: 3px solid var(--si-color-line-strong);
background: var(--si-color-surface-alt);
color: var(--si-color-ink-soft);
font-size: var(--si-text-sm);
line-height: var(--si-leading-snug);
}
.si-alert__title {
font-weight: var(--si-weight-semibold);
color: var(--si-color-ink);
}
.si-alert--info {
border-left-color: var(--si-color-info);
background: var(--si-color-info-soft);
color: var(--si-color-accent-ink);
}
.si-alert--success {
border-left-color: var(--si-color-success);
background: var(--si-color-success-soft);
color: var(--si-color-success);
}
.si-alert--warning {
border-left-color: var(--si-color-warning);
background: var(--si-color-warning-soft);
color: var(--si-color-warning);
}
.si-alert--danger {
border-left-color: var(--si-color-danger);
background: var(--si-color-danger-soft);
color: var(--si-color-danger);
}
role="status" para informação e sucesso (anuncia sem interromper) e
role="alert" só para erro (interrompe o leitor de tela). A cor do alerta
não diz nada a quem usa leitor.
.si-prose é o envelope para conteúdo vindo de CMS ou markdown, onde não
dá para pôr classe em cada elemento. Escopo fechado: só afeta os filhos.
Desde 2013 desenvolvemos software de gestão para o setor público brasileiro.
Atendemos prefeituras, câmaras municipais e hospitais com sistemas que nascem da rotina de quem opera o serviço, não de um catálogo genérico.
O sistema precisa caber na rotina do servidor, não o contrário.
.si-prose {
max-width: 68ch;
color: var(--si-color-ink-soft);
}
.si-prose__lead {
font-size: var(--si-text-lg);
color: var(--si-color-ink);
line-height: var(--si-leading-snug);
}
.si-prose blockquote {
border-left: 3px solid var(--si-color-accent);
padding-left: var(--si-space-5);
color: var(--si-color-ink);
font-size: var(--si-text-lg);
}
O conjunto mínimo — só o que um componente não expressa sozinho. Isto não é um framework utilitário e não vai virar um.
| Classe | O que faz |
|---|---|
.si-visually-hidden | Some da tela, continua no leitor de tela. Para rótulos que a interface comunica visualmente. |
.si-skip-link | "Pular para o conteúdo". Invisível até receber foco pelo teclado. Primeiro elemento do <body>. |
.si-measure | Limita a 68 caracteres por linha. |
.si-lead | Parágrafo de abertura, maior e em cinza. |
.si-surface-alt / .si-surface-brand | Fundo de seção alternada / faixa de marca com texto branco. |
.si-hidden@sm / .si-hidden@lg | Esconde abaixo de 900px / a partir de 900px. |
Composições recorrentes do site institucional. Não são componentes novos: são arranjos das classes acima. Se você precisar de um padrão que não está aqui, monte-o com o que existe antes de escrever CSS.
Desde 2013 atendendo prefeituras, câmaras municipais e hospitais.
<section class="si-surface-brand si-section">
<div class="si-container">
<div class="si-stack si-measure">
<span class="si-badge si-badge--eyebrow">Gestão pública</span>
<h1>Software de gestão feito para quem atende o cidadão</h1>
<p class="si-lead">Desde 2013 atendendo prefeituras, câmaras e hospitais.</p>
<div class="si-cluster">
<a class="si-btn si-btn--invert si-btn--lg" href="…">Falar com especialista</a>
<a class="si-btn si-btn--outline-invert si-btn--lg" href="…">Ver soluções</a>
</div>
</div>
</div>
</section>
.si-grid com .si-card--interactive. Uma coluna no celular,
duas no tablet, três ou quatro no desktop — sem media query.
Licenciamento, fiscalização e denúncias.
Abertura, triagem e SLA de atendimento.
Gestão e locação de áreas de lazer públicas.
// package.json — sempre travado numa tag
"dependencies": {
"siteincrivel-ds": "github:kayquer/siteincrivel-design-system#v1.0.0"
}
// no layout base, uma vez só
import 'siteincrivel-ds/css';
# no deploy DAQUELE projeto, travado na mesma tag
curl -L -o assets/siteincrivel.min.css \
https://raw.githubusercontent.com/kayquer/siteincrivel-design-system/v1.0.0/dist/siteincrivel.min.css
<link rel="stylesheet" href="/assets/siteincrivel.min.css">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="stylesheet"
href="https://fonts.googleapis.com/css2?family=Lexend:wght@400;500;600;700&display=swap">
--si-font-sans: "Lexend", system-ui, … mas não
carrega a Lexend. Quem consome decide: Google Fonts, self-host ou nada — sem a Lexend o
fallback de sistema assume e nada quebra.
dist/siteincrivel.css é gerado. O CI roda
git diff --exit-code dist/ depois do build: qualquer edição manual
derruba o pipeline. Mexa no src/css/.
npm run lint:tokens falha se um hex aparecer solto num
componente — e o site consumidor tem o mesmo grep no CI dele.
.si-card precisa variar num projeto, ele ganha um
modificador --variant aqui. Sobrescrever de fora quebra em silêncio na
próxima versão e o próximo consumidor não herda a correção.
src/css/ no
npm run docs. Não os escreva à mão: npm run docs:check
compara e falha. Documentação que diverge do código é pior que documentação nenhuma.
| Comando | O que faz |
|---|---|
npm run build | Concatena src/css/ em dist/. Falha se dois arquivos tiverem o mesmo prefixo numérico. |
npm run docs | Preenche esta página a partir do CSS real. |
npm run docs:check | Só confere, não escreve. É o gate do CI. |
npm run lint:tokens | Procura hex cru fora do 00-tokens.css. |
npm run check | Os três acima + git diff --exit-code dist/. Rode antes de abrir PR. |
npm run dev | Rebuild automático a cada save no src/. |
src/css/NN-nome.css com um prefixo numérico livre — a posição
define a cascata.si-: .si-nome,
.si-nome__parte, .si-nome--variante.var(--si-*). Faltou um token? Crie no
00-tokens.css com o comentário de uso.index.html, com o exemplo vivo e um
<code data-ds-css="…"> vazio.npm run check. Se o seletor não existir, o build avisa.