@aurea-uds/core 0.2.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/aurea.css +595 -49
- package/package.json +1 -1
package/dist/aurea.css
CHANGED
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
--z-modal:80;
|
|
57
57
|
--z-toast:100;
|
|
58
58
|
--sidebar-width:16.5rem;
|
|
59
|
-
--sidebar-rail:
|
|
59
|
+
--sidebar-rail:4.25rem;
|
|
60
60
|
--topbar-height:4rem;
|
|
61
61
|
--content-max:102.5rem;
|
|
62
62
|
--breakpoint-sm:640px;
|
|
@@ -286,6 +286,20 @@ button,a,[role="button"],input,textarea,select { outline:none; }
|
|
|
286
286
|
:focus-visible { outline:2px solid var(--focus-strong); outline-offset:2px; box-shadow:none; border-color:var(--focus-strong)!important; }
|
|
287
287
|
button { color:inherit; }
|
|
288
288
|
a { color:var(--primary); text-underline-offset:3px; }
|
|
289
|
+
/* LINK DE TEXTO NO TEMA CLARO — reprovação de WCAG AA que estava latente desde sempre, e que só
|
|
290
|
+
apareceu quando o catálogo ganhou a primeira página com link em corpo de texto (a do `Prose`,
|
|
291
|
+
item L5). Medido em 15/08/2026 na página gerada: o amarelo puro sobre a superfície clara dá
|
|
292
|
+
**1,68:1**, contra os 4,5:1 do AA. Com a correção, **6,92:1**.
|
|
293
|
+
NÃO é decisão de aparência nova, e é por isso que entra sem interromper o lote: a fórmula, o
|
|
294
|
+
guarda de tema e a razão já estão neste arquivo, escritos para o `.btn-link-primary` (linha
|
|
295
|
+
~247) e para a linguagem de selecionado — "no tema claro o amarelo puro como texto dá 1.6:1,
|
|
296
|
+
longe dos 4.5:1 do AA, então o rótulo aprofunda com --foreground e segue âmbar". O bare `<a>`
|
|
297
|
+
simplesmente nunca tinha recebido o mesmo tratamento.
|
|
298
|
+
`:not([class])` é a parte cirúrgica, e não zelo: sem ele esta regra (0,2,1) passaria por cima
|
|
299
|
+
de `.btn` (0,1,0), de `.doc-nav a` e de `.toc a`, trocando a cor de componentes que já resolvem
|
|
300
|
+
a própria. Assim ela alcança exatamente o que está quebrado — o link que o conteúdo escreve —
|
|
301
|
+
e nenhum outro. Limite declarado: link de conteúdo que carregue classe própria é do consumidor. */
|
|
302
|
+
[data-theme="light"] a:not([class]) { color:color-mix(in oklab, var(--primary) 45%, var(--foreground)); }
|
|
289
303
|
img,svg { display:block; }
|
|
290
304
|
/* O `display:none` que o browser dá pro atributo `hidden` vem da folha do UA e PERDE de
|
|
291
305
|
qualquer `display:` de classe nossa — o painel some do fluxo de acessibilidade mas
|
|
@@ -299,6 +313,13 @@ img,svg { display:block; }
|
|
|
299
313
|
.muted { color:var(--muted-foreground); }
|
|
300
314
|
.stack { display:flex; flex-direction:column; gap:var(--space-4); }
|
|
301
315
|
.cluster { display:flex; align-items:center; flex-wrap:wrap; gap:var(--space-3); }
|
|
316
|
+
/* SEPARATOR. `background` num box de 1px e NÃO `border`: assim a espessura não depende de qual
|
|
317
|
+
lado do border está escrito, e a mesma classe serve nas duas orientações. `<hr>` chega com
|
|
318
|
+
margem e borda do navegador — as duas são zeradas aqui, senão a régua vem com folga que ninguém
|
|
319
|
+
pediu. O vertical precisa de `align-self:stretch` para acompanhar a altura do irmão mais alto, e
|
|
320
|
+
de um piso: sem `min-height` ele mede ZERO num pai que não define altura, e desaparece. */
|
|
321
|
+
.separator { flex:none; width:100%; height:1px; margin:0; border:0; background:var(--border); }
|
|
322
|
+
.separator-vertical { align-self:stretch; width:1px; height:auto; min-height:var(--space-2); }
|
|
302
323
|
/* Grid: o terceiro primitivo de layout, com o mesmo gap do .stack. A pele nasceu na Fase 11
|
|
303
324
|
(achado A13): o componente existia desde sempre e saía `display:block` — empilhava. Ninguém
|
|
304
325
|
via porque o chrome do catálogo REDEFINIA .grid, então ele parecia pronto na nossa
|
|
@@ -322,7 +343,11 @@ img,svg { display:block; }
|
|
|
322
343
|
26 telas de referência inteiras (aconteceu; o portão visual pegou). Por isso a folga
|
|
323
344
|
entre as caixas sai da MARGEM de cada filho, não de gap/padding no shell. As linhas
|
|
324
345
|
extras do grid nascem sozinhas por auto-placement. */
|
|
325
|
-
.app-shell { min-height:100vh; display:grid; grid-template-columns:var(--sidebar-
|
|
346
|
+
.app-shell { --sidebar-track:calc(var(--sidebar-width) + var(--space-4)*2); min-height:100vh; display:grid; grid-template-columns:var(--sidebar-track) minmax(0,1fr); background:var(--background); }
|
|
347
|
+
/* `--sidebar-width` nomeia o PAINEL, não a trilha inteira. A variante flutuante ainda gasta
|
|
348
|
+
`--space-4` dos dois lados; reservar só o token fazia 264px virarem 232px. A rente não tem
|
|
349
|
+
margem, então a trilha dela continua sendo o token cru. */
|
|
350
|
+
.app-shell:has(> .sidebar-flush) { --sidebar-track:var(--sidebar-width); }
|
|
326
351
|
/* A base do topo carrega só o ARRANJO; cada variante traz a própria superfície.
|
|
327
352
|
floating = caixa solta (a que já estava) · flush = faixa encostada nas bordas com fio
|
|
328
353
|
embaixo, para o topo virar moldura da janela · pill = cápsula centrada, de cabeçalho
|
|
@@ -340,7 +365,41 @@ img,svg { display:block; }
|
|
|
340
365
|
.topbar .brand { min-height:auto; margin:0; padding:0; border:0; }
|
|
341
366
|
/* altura fixa = janela menos topo e as 3 folgas; assim a caixa parece cheia mesmo com
|
|
342
367
|
poucos itens (é o que as referências mostram) e sobra o que rolar por dentro. */
|
|
343
|
-
|
|
368
|
+
/* E O TETO É O CONTAINER, NÃO A JANELA. A altura acima é da JANELA, e sozinha ela faz a lateral
|
|
369
|
+
presumir que é dona da tela: dentro de qualquer caixa menor ela calcula 818px, transborda e é
|
|
370
|
+
CORTADA — medido em 18/08/2026 na prévia do catálogo, 818px dentro de 272px, sem nem rolar.
|
|
371
|
+
`max-height:100%` contra um pai de altura INDEFINIDA é ignorado, então no `AppShell` (onde a
|
|
372
|
+
lateral realmente é dona da tela) nada muda — conferido: 818px antes e depois, idêntico. Contra
|
|
373
|
+
um pai de altura definida ele manda, e a caixa passa a caber. Os `--space-4`*2 descontam a
|
|
374
|
+
própria margem, senão ela transborda pelo dobro da folga.
|
|
375
|
+
O intento da altura continua o mesmo: caixa cheia mesmo com poucos itens, e o que sobra rola
|
|
376
|
+
por dentro (`overflow:auto`, logo abaixo). O que muda é de onde vem o teto. */
|
|
377
|
+
/* O PAINEL DÁ QUASE NADA E A LINHA DÁ O RESTO — o mesmo padrão do `.card:has(> .nav-list)`.
|
|
378
|
+
Era `--space-4` (16px): o item começava a 17px da borda e ocupava 88% da largura, com o
|
|
379
|
+
realce flutuando no meio do painel. */
|
|
380
|
+
/* A LATERAL FLUTUA POR PADRÃO — é a identidade desta casa, e o `CLAUDE.md` manda.
|
|
381
|
+
EM 18/08/2026 O PADRÃO FOI INVERTIDO AQUI, E ESTAVA ERRADO. O Victor abriu exceção para a
|
|
382
|
+
lateral PODER ser rente; uma sessão leu "abriu exceção" como "troque o padrão". São coisas
|
|
383
|
+
diferentes, e ele separou as duas em 20/08/2026:
|
|
384
|
+
"uma coisa é a gente ter o componente outra coisa é usar no nosso próprio projeto,
|
|
385
|
+
o nosso vai ser tudo flutuante mas isso não impede da gente ter outras variações para
|
|
386
|
+
as pessoas que nos acharem no npm usarem"
|
|
387
|
+
E o estrago não foi de gosto: as 106 páginas do catálogo montam por `AppShell`, que chama a
|
|
388
|
+
`Sidebar` SEM variante — inverter o padrão reescreveu o catálogo inteiro, e quatro baselines
|
|
389
|
+
de pixel foram regravadas para aceitar. Ver ADR-0034.
|
|
390
|
+
Esta regra é a de `b0599be`, íntegra: estado conhecido bom, provado pelo portão de pixel em vez
|
|
391
|
+
de reafinado à mão. */
|
|
392
|
+
.sidebar { --shell-top:calc(var(--topbar-height) + var(--space-4)*2); position:sticky; top:var(--shell-top); align-self:start; height:calc(100vh - var(--shell-top) - var(--space-4)); max-height:calc(100% - var(--space-4)*2); margin:var(--space-4); display:flex; flex-direction:column; gap:var(--space-1); padding:var(--space-1); overflow:auto; scrollbar-width:none; border:1px solid var(--sidebar-border); border-radius:var(--radius-card); background:var(--sidebar); color:var(--sidebar-foreground); }
|
|
393
|
+
/* RENTE É A VARIANTE, e ela existe porque o Victor mediu quatro aplicativos que rodam na mão:
|
|
394
|
+
Cloudflare, Sophos, o app do Claude e o painel do próprio HeroUI. Nenhum flutua a lateral — ela
|
|
395
|
+
encosta nas bordas, ocupa a altura toda e se separa do conteúdo por UM TRAÇO, não por um vão.
|
|
396
|
+
É a ÚNICA exceção à caixa flutuante nesta casa, e não generaliza: card, diálogo e painel seguem
|
|
397
|
+
flutuando.
|
|
398
|
+
Ela desfaz exatamente o que a flutuante põe — as duas contas de altura, a margem, o raio e de
|
|
399
|
+
que lados vem a borda —, e é por isso que a trava de pele exige que as duas MEÇAM diferente:
|
|
400
|
+
variante que desenha igual é enfeite. `--shell-top` volta a ser só a altura do topo, porque sem
|
|
401
|
+
margem não há as duas folgas a descontar. */
|
|
402
|
+
.sidebar-flush { --shell-top:var(--topbar-height); height:calc(100vh - var(--shell-top)); max-height:100%; margin:0; border:0; border-inline-end:1px solid var(--sidebar-border); border-radius:0; }
|
|
344
403
|
/* a lateral ROLA mas nunca mostra a barra (pedido do Victor): a barra nativa risca a
|
|
345
404
|
superfície flutuante e não segue o raio de 22px. `scrollbar-width` cobre Firefox e
|
|
346
405
|
Chrome 121+; o pseudo cobre Safari e Chrome antigo. */
|
|
@@ -349,7 +408,10 @@ img,svg { display:block; }
|
|
|
349
408
|
e do conteúdo é --space-1 (4px), e o --shell-top acompanha. */
|
|
350
409
|
/* flush cola no conteúdo com --space-05 (2px) — o degrau mais apertado da escala, criado
|
|
351
410
|
pra este caso (o Victor aprovou os 2px; tokenizado, não cru). */
|
|
352
|
-
|
|
411
|
+
/* E ela NÃO vale para a lateral rente: `.app-shell-flush .sidebar` é mais específico que
|
|
412
|
+
`.sidebar-flush` e roubaria dela a margem e o `--shell-top`. Esta regra é sobre o TOPO
|
|
413
|
+
encostado, não sobre a lateral — a rente já encosta sozinha. */
|
|
414
|
+
.app-shell-flush .sidebar:not(.sidebar-flush) { --shell-top:calc(var(--topbar-height) + var(--space-05)); margin-block-start:var(--space-05); }
|
|
353
415
|
.app-shell-flush .content { margin-block-start:var(--space-05); }
|
|
354
416
|
.content { min-width:0; margin:var(--space-4) var(--space-4) var(--space-4) 0; padding:var(--space-6); border:1px solid var(--border); border-radius:var(--radius-card); background:var(--card); color:var(--card-foreground); }
|
|
355
417
|
/* O topo é presentational — não conhece o conteúdo — então garante o mínimo universal:
|
|
@@ -361,13 +423,14 @@ img,svg { display:block; }
|
|
|
361
423
|
mexer no viewport: o @media não disparava e a nav passava 27px da borda direita, em todas
|
|
362
424
|
as páginas. Quebrar por conteúdo cobre os dois casos e não muda nada quando cabe. */
|
|
363
425
|
.topbar,.topbar > nav { flex-wrap:wrap; }
|
|
364
|
-
/* Colapso do shell abaixo de
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
426
|
+
/* Colapso do shell abaixo de lg (1023 = lg-1, escala): a lateral do AppShell deixa o fluxo e
|
|
427
|
+
vira a gaveta definida abaixo. A regra mira o filho `[popover]` de propósito: uma `Sidebar`
|
|
428
|
+
standalone dentro de um preview estreito continua sendo o componente inteiro, não herda
|
|
429
|
+
comportamento do shell que não existe ao redor dela. */
|
|
430
|
+
/* Abaixo de lg o topo QUEBRA em duas linhas em vez de esconder a navegação: perder o acesso
|
|
431
|
+
a uma área inteira não é colapso responsivo, é bug. A nav vai pra segunda linha, centrada
|
|
432
|
+
e na largura toda; badge e selo (secundários) seguem escondidos pelos utilitários. */
|
|
433
|
+
@media (max-width:1023px) { .app-shell { grid-template-columns:minmax(0,1fr); } .app-shell > .sidebar[popover] { position:static; height:auto; } .content { margin-inline-start:var(--space-4); }
|
|
371
434
|
.topbar { flex-wrap:wrap; row-gap:var(--space-2); }
|
|
372
435
|
.topbar > nav { order:3; flex-basis:100%; justify-content:center; }
|
|
373
436
|
/* e a nav quebra entre os PRÓPRIOS itens: mandar a barra pra segunda linha não bastava,
|
|
@@ -375,12 +438,13 @@ img,svg { display:block; }
|
|
|
375
438
|
.topbar > nav { flex-wrap:wrap; row-gap:var(--space-1); } }
|
|
376
439
|
|
|
377
440
|
/* ── GAVETA DE NAVEGAÇÃO (achado A8) ───────────────────────────────────────────────
|
|
378
|
-
Empilhar a lateral acima do conteúdo passava, no gate de rolagem lateral, e era
|
|
441
|
+
Empilhar a lateral acima do conteúdo passava, no gate de rolagem lateral, e era
|
|
379
442
|
inutilizável: medido em 375px, o `h1` da página começava 4,6 telas abaixo, atrás dos 65
|
|
380
|
-
itens da navegação. Abaixo de
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
443
|
+
itens da navegação. Abaixo de lg ela sai do fluxo e vira gaveta — o mesmo corte da
|
|
444
|
+
referência e o breakpoint `--breakpoint-lg` já publicado pela Aurea.
|
|
445
|
+
A MECÂNICA é nativa: o <aside> é popover e o botão do topo é o invoker. O navegador entrega
|
|
446
|
+
Escape, clique fora, `aria-expanded` e ::backdrop; o AppShell normaliza entrada e retorno de
|
|
447
|
+
foco nos três motores, porque o WebKit não os entregou sozinho (ADR-0019).
|
|
384
448
|
Não-modal de propósito (o foco não fica preso), como o CommandPaletteShell: declarar
|
|
385
449
|
modalidade sem entregá-la é pior que não declarar. */
|
|
386
450
|
.btn.nav-toggle { display:none; }
|
|
@@ -394,14 +458,16 @@ img,svg { display:block; }
|
|
|
394
458
|
Medido: sem esta linha a lateral seguia empilhada em 375px, com o botão já visível.
|
|
395
459
|
O @supports é a rede: em navegador sem popover a regra inteira não aplica, a lateral volta a
|
|
396
460
|
empilhar (o comportamento de antes, feio e usável) em vez de sumir sem botão que a abra. */
|
|
397
|
-
@supports selector(:popover-open) {
|
|
398
|
-
@media (max-width:
|
|
461
|
+
@supports selector(:popover-open) {
|
|
462
|
+
@media (max-width:1023px) {
|
|
399
463
|
.sidebar[popover]:not(:popover-open) { display:none; }
|
|
400
464
|
.btn.nav-toggle { display:inline-flex; }
|
|
401
465
|
/* altura EXPLÍCITA, não `auto`: medido em 30/07/2026, com `top` e `bottom` postos e
|
|
402
466
|
`height:auto` o Chromium resolveu pela altura do CONTEÚDO (3.498px de lateral rolando a
|
|
403
467
|
página inteira), não pelas âncoras. `dvh` porque a barra do navegador móvel some e volta. */
|
|
404
|
-
|
|
468
|
+
/* 296px no teto: `--sidebar-width` (264) + as duas folgas externas (32), a mesma medida
|
|
469
|
+
do drawer da referência. Em viewport menor, a segunda conta sempre preserva as bordas. */
|
|
470
|
+
.sidebar[popover]:popover-open { position:fixed; inset-block:var(--space-4); inset-inline-start:var(--space-4); width:min(calc(var(--sidebar-width) + var(--space-4)*2),calc(100vw - var(--space-8))); height:calc(100dvh - var(--space-4) * 2); margin:0; }
|
|
405
471
|
.sidebar[popover]::backdrop { background:var(--overlay); }
|
|
406
472
|
}
|
|
407
473
|
}
|
|
@@ -418,7 +484,7 @@ img,svg { display:block; }
|
|
|
418
484
|
.sidebar-list { display:flex; flex-direction:column; gap:var(--space-1); margin:0; padding:0; list-style:none; }
|
|
419
485
|
/* `width:100%` + `font:inherit` + `border:0`: o item também é <button> quando não navega, e
|
|
420
486
|
sem isto o botão vem com a caixa do navegador e quebra a fila. */
|
|
421
|
-
.sidebar-item { display:flex; align-items:center; gap:var(--space-2); width:100%; min-height:var(--control-h-md); padding:var(--space-1) var(--space-3); border:0; border-radius:var(--radius-
|
|
487
|
+
.sidebar-item { display:flex; align-items:center; gap:var(--space-2); width:100%; min-height:var(--control-h-md); padding:var(--space-1) var(--space-3); border:0; border-radius:calc(var(--radius-card) - var(--space-1)); background:transparent; color:var(--sidebar-foreground); font:inherit; font-size:var(--text-sm); text-align:start; text-decoration:none; cursor:pointer; }
|
|
422
488
|
.sidebar-item .icon { flex:none; opacity:.8; }
|
|
423
489
|
/* min-width:0 é o que deixa o ellipsis funcionar dentro do flex; sem ele o nome longo empurra
|
|
424
490
|
o acessório para fora da caixa em vez de cortar. */
|
|
@@ -426,22 +492,216 @@ img,svg { display:block; }
|
|
|
426
492
|
@media (hover:hover) and (pointer:fine) { .sidebar-item:hover { background:var(--secondary); } }
|
|
427
493
|
.sidebar-item[aria-current] { background:var(--secondary); color:var(--foreground); font-weight:var(--weight-medium); }
|
|
428
494
|
.sidebar-item[aria-current] .icon { opacity:1; }
|
|
429
|
-
|
|
495
|
+
/* Alinha com o TEXTO do item: padding do painel (4) + padding do item (12) = 16px. Com o
|
|
496
|
+
painel apertado, os 8px de antes deixavam a rubrica fora do prumo da lista. */
|
|
497
|
+
.sidebar-group-label { margin:var(--space-3) var(--space-3) var(--space-1); color:var(--subtle-foreground); font-size:var(--text-xs); font-weight:var(--weight-medium); letter-spacing:var(--tracking-widest); text-transform:uppercase; }
|
|
430
498
|
.sidebar-sub { padding-inline-start:var(--space-5); }
|
|
431
499
|
/* RECOLHIDA — a lateral vira trilho de ícones. O rótulo NÃO some do DOM: o React troca a
|
|
432
500
|
classe dele por `.sr-only`, então quem usa leitor de tela continua ouvindo o nome. Ícone
|
|
433
501
|
sozinho não nomeia nada, e essa é a falha clássica do padrão de trilho. */
|
|
434
502
|
.sidebar-collapsed { padding-inline:var(--space-2); }
|
|
435
|
-
|
|
503
|
+
/* NO TRILHO O ITEM É QUADRADO E CENTRADO, não uma linha esticada. Medido no fonte da referência
|
|
504
|
+
em 18/08/2026: `NavButton` com `iconOnly` é `size-9` — 36×36, exatamente o nosso `--control-h-md`.
|
|
505
|
+
Antes o item herdava `width:100%` e virava 71×36 dentro de um trilho de 88: um retângulo largo
|
|
506
|
+
que, com o raio de linha, fechava nas pontas e lia como CÁPSULA. Quadrado é o que a referência
|
|
507
|
+
desenha, e é o que o olho reconhece como botão de ícone. */
|
|
508
|
+
.sidebar-collapsed .sidebar-item { justify-content:center; width:var(--control-h-md); padding-inline:0; margin-inline:auto; }
|
|
436
509
|
.sidebar-collapsed .sidebar-sub { padding-inline-start:0; }
|
|
437
|
-
/*
|
|
438
|
-
lateral não consegue encolher a própria
|
|
510
|
+
/* Estas regras tocam `.app-shell` porque a coluna do grid é do shell: a
|
|
511
|
+
lateral não consegue encolher a própria trilha. `--sidebar-rail` continua sendo a largura
|
|
512
|
+
do PAINEL; a flutuante soma as margens na trilha, a rente usa o token cru.
|
|
439
513
|
O `>` NÃO É ENFEITE, e quem achou isso foi o navegador em 06/08/2026. Escrita como
|
|
440
514
|
`:has(.sidebar-collapsed)`, a regra casa com QUALQUER descendente — e a página do catálogo
|
|
441
515
|
que mostra uma lateral recolhida como EXEMPLO, dentro do <main>, recolhia a lateral de
|
|
442
516
|
verdade do documento inteiro. Medido: a coluna do shell saía 88px numa página que não pediu
|
|
443
517
|
nada. Gate nenhum via, porque `sidebar.html` não tem baseline de pixel. */
|
|
444
|
-
.app-shell:has(> .sidebar-collapsed) {
|
|
518
|
+
.app-shell:has(> .sidebar-collapsed) { --sidebar-track:calc(var(--sidebar-rail) + var(--space-4)*2); }
|
|
519
|
+
.app-shell:has(> .sidebar-collapsed.sidebar-flush) { --sidebar-track:var(--sidebar-rail); }
|
|
520
|
+
|
|
521
|
+
/* ── Navegação inferior — o vocabulário de APLICATIVO ───────────────────────────────
|
|
522
|
+
Lacuna estrutural medida no `CONSUMIDOR-GARAGEM.md` §4.1: `.topbar` e `.sidebar` são
|
|
523
|
+
vocabulário de DESKTOP, e aplicativo não navega assim.
|
|
524
|
+
`sticky` e NÃO `fixed`, e a razão é o catálogo: fixo escapa do contêiner e a prévia
|
|
525
|
+
cobriria a página inteira — o mesmo defeito que o `:has(> …)` da linha acima pagou.
|
|
526
|
+
Grudado no fim do fluxo dá o mesmo efeito e degrada para estático sem rolagem.
|
|
527
|
+
O `max()` do recuo inferior é o que impede a barra de ficar SOB o indicador de tela
|
|
528
|
+
cheia do iPhone. Ele só tem efeito se o consumidor declarar `viewport-fit=cover` na
|
|
529
|
+
meta viewport (está na ficha); sem isso `env()` devolve 0 e o `max()` entrega o
|
|
530
|
+
espaço normal — degrada, não quebra. Nenhuma das nove referências usa isto: medido,
|
|
531
|
+
`grep -ri safe-area-inset Referencia/` = 0. Veio de pesquisa, pelo BUILDING.md §2
|
|
532
|
+
passo 4. */
|
|
533
|
+
.bottom-nav { --bottomnav-ring:2rem; --bottomnav-surface:var(--card); position:sticky; bottom:0; z-index:var(--z-sticky); display:flex; gap:var(--space-1); margin:var(--space-4); padding:var(--space-1); padding-bottom:max(var(--space-1),env(safe-area-inset-bottom)); border:1px solid var(--border); border-radius:var(--radius-control); background:var(--card); color:var(--card-foreground); box-shadow:var(--shadow-md); }
|
|
534
|
+
/* `--control-h-lg` e não `--control-h-md`: alvo de toque. O md dá 36px em comfortable e
|
|
535
|
+
o alvo confortável de dedo é maior — com o recuo, o item renderiza acima de 44px. */
|
|
536
|
+
.bottom-nav-item { flex:1; min-width:0; display:flex; flex-direction:column; align-items:center; justify-content:center; gap:var(--space-1); min-height:var(--control-h-lg); padding:var(--space-1); border:0; border-radius:var(--radius-control); background:transparent; color:var(--muted-foreground); font:inherit; font-size:var(--text-xs); text-align:center; text-decoration:none; cursor:pointer; }
|
|
537
|
+
.bottom-nav-item .icon { flex:none; opacity:.8; }
|
|
538
|
+
.bottom-nav-label { max-width:100%; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
|
|
539
|
+
/* O CONTADOR PENDURA NO ÍCONE — canto superior da ponta, encostando na borda dele.
|
|
540
|
+
A primeira versão pendurava no ITEM, com `inset-inline-start:50%`: em barra de coluna passa
|
|
541
|
+
despercebido, e na variante de LINHA os 50% do item caem no meio do RÓTULO. O Victor achou
|
|
542
|
+
assim, com o número cobrindo o nome inteiro (17/08/2026).
|
|
543
|
+
A pesquisa que corrigiu: Material 3 põe o distintivo "sobre o ícone, no canto superior
|
|
544
|
+
direito", e os guias de barra de abas do iOS dizem o mesmo com a razão junto — encostar na
|
|
545
|
+
borda é para ser visto, sobrepor conteúdo é hesitação num controle que se usa o tempo todo.
|
|
546
|
+
`inset-inline-end` e não `right`: o eixo espelha sozinho em RTL, e transform não espelharia.
|
|
547
|
+
O `min-` da caixa é para o contador ter onde se ancorar quando o item não tem ícone.
|
|
548
|
+
A CAIXA É `--icon-lg` (24px) e o contador `--space-4` (16px) porque a PROPORÇÃO foi medida
|
|
549
|
+
em aplicativo que roda: WhatsApp, YouTube, Mercado Livre e Shopee, nos prints do Victor de
|
|
550
|
+
17/08/2026 — ícone ~24, contador ~16, razão **0,67**. A primeira versão tinha ícone de 16 e
|
|
551
|
+
contador de 18: razão 1,12, o contador MAIOR que o ícone. Foi o que ele viu e mandou refazer. */
|
|
552
|
+
/* A MOLDURA É FIXA E VALE PARA OS SETE INDICADORES. Medido em 20/08/2026, e quem viu foi o
|
|
553
|
+
Victor: os três indicadores redondos saíam com a BARRA MAIS LARGA que os outros quatro, porque
|
|
554
|
+
só eles davam largura à moldura e a largura do item vinha do conteúdo. Trocar de indicador
|
|
555
|
+
mudava a medida da barra — que é o defeito estrutural que esta reescrita existe para acabar.
|
|
556
|
+
O achado 8 da auditoria pede a mesma coisa por outro caminho: o contador tem de se ancorar na
|
|
557
|
+
caixa do ícone e ficar PREVISÍVEL, e caixa que muda de tamanho conforme a variante não é
|
|
558
|
+
ancoragem previsível. Agora quem pinta é o indicador; quem MEDE é sempre esta regra.
|
|
559
|
+
|
|
560
|
+
O NÚMERO É PESQUISADO, e a exigência é do Victor em 20/08/2026: *"vá pesquisar e descubra qual
|
|
561
|
+
padrão é usado, pesquise não invente medidas"*. Ele pegou 2.25rem saído da minha cabeça.
|
|
562
|
+
`2rem` = 32px é a ALTURA DO INDICADOR ATIVO do Material 3 (`m3.material.io/components/
|
|
563
|
+
navigation-bar/specs` e a doc do `material-components-android`: indicador 64×32dp, canto
|
|
564
|
+
totalmente arredondado, sobre um ícone de 24dp). 24 de ícone dentro de 32 deixa 4 de folga de
|
|
565
|
+
cada lado, que é a mesma folga de 4dp que o M3 usa entre o indicador e o rótulo.
|
|
566
|
+
O ALVO DE TOQUE não é este número e continua sendo o `min-height` do item: `--control-h-lg`
|
|
567
|
+
entrega 42px em comfortable e 48 em spacious, contra os 44pt mínimos da HIG da Apple e os
|
|
568
|
+
48dp do Material. A moldura é a caixa do DESENHO; o alvo é o item inteiro. */
|
|
569
|
+
.bottom-nav-mark { position:relative; flex:none; display:inline-flex; align-items:center; justify-content:center; width:var(--bottomnav-ring); height:var(--bottomnav-ring); border-radius:var(--radius-control); }
|
|
570
|
+
/* `inline-flex` no envoltório: como `<span>` solto ele é caixa de LINHA, e mede a entrelinha do
|
|
571
|
+
item (18px) em vez do contador que carrega (16px). A diferença não aparece na tela — aparece
|
|
572
|
+
na medição, e medição errada é o que faz a razão parecer 0,75 quando é 0,67. */
|
|
573
|
+
.bottom-nav-badge { position:absolute; display:inline-flex; inset-block-start:calc(var(--space-05)*-1); inset-inline-end:calc(var(--space-05)*-1); }
|
|
574
|
+
/* O contador da barra usa `<Badge size="xs">`, e NÃO uma sobrescrita local de `.badge` aqui.
|
|
575
|
+
A primeira versão sobrescrevia, e o `geometry.spec` reprovou com razão: `.badge` tem altura
|
|
576
|
+
ÚNICA por contrato, e uma regra contextual fazia o mesmo nome medir 16px numa página e 24px
|
|
577
|
+
noutra. Tamanho é do componente, não do lugar onde ele cai. */
|
|
578
|
+
/* SEM NÚMERO, É PONTO. Três dos quatro aplicativos medidos usam o ponto puro para "tem coisa
|
|
579
|
+
nova" — YouTube (Inscrições), WhatsApp (Atualizações e Você). O consumidor pede pelo vazio:
|
|
580
|
+
`badge: ""`. A cor é a de AVISO e não a da marca, também como nos quatro: o contador não é
|
|
581
|
+
decoração do item atual, é alerta.
|
|
582
|
+
⚠ Ponto não tem nome acessível — não há o que ler. Quem usa ponto põe o sentido no RÓTULO do
|
|
583
|
+
item (`label: <>Updates <span className="sr-only">(new)</span></>`). Está na ficha. */
|
|
584
|
+
.bottom-nav-badge:empty { width:var(--space-2); height:var(--space-2); border-radius:var(--radius-full); background:var(--primary); }
|
|
585
|
+
/* SEM REALCE DE PASSAR O MOUSE, e isso é decisão do Victor em 20/08/2026: *"essa bola cinza
|
|
586
|
+
que aparece quando mouse passar em cima pode retirar, é um recurso irrelevante no celular"*.
|
|
587
|
+
Ele tem razão pelo escopo do componente: esta barra é o chrome de aplicativo de TELEFONE, e o
|
|
588
|
+
dedo não tem estado de passar por cima. Pior: o realce cinza desenhava a MESMA forma que o
|
|
589
|
+
indicador `subtle` usa para dizer "você está aqui", então passar o mouse por um item inativo
|
|
590
|
+
fingia seleção — dois significados no mesmo desenho.
|
|
591
|
+
O FOCO CONTINUA, e a diferença não é de gosto: `:focus-visible` é o que o teclado enxerga, vem
|
|
592
|
+
do contrato de foco global e o `geometry.spec` cobra. Tirar hover é tirar enfeite; tirar foco
|
|
593
|
+
seria tirar acesso. */
|
|
594
|
+
.bottom-nav-item[aria-current] { color:var(--foreground); font-weight:var(--weight-medium); }
|
|
595
|
+
.bottom-nav-item[aria-current] .icon { opacity:1; }
|
|
596
|
+
/* ── A GRAMÁTICA: DOIS EIXOS, E ELES NÃO SE MISTURAM ────────────────────────────────
|
|
597
|
+
Reescrito em 20/08/2026 pela auditoria de UI/UX do BottomNav que o Victor encomendou.
|
|
598
|
+
O achado dela não é estético, é ESTRUTURAL: `flat`, `surface`, `pill` e `dock` eram quatro
|
|
599
|
+
implementações conceitualmente separadas — cada uma dizia "esta é a página atual" com uma
|
|
600
|
+
linguagem diferente (uma pela cor, outra por cápsula cinza, outra por bloco amarelo), e duas
|
|
601
|
+
delas escondiam o rótulo. Trocar de variante trocava o MODELO MENTAL do componente, não a
|
|
602
|
+
forma dele.
|
|
603
|
+
|
|
604
|
+
Agora são dois eixos independentes, e é isso que faz variante ser variante:
|
|
605
|
+
|
|
606
|
+
• LAYOUT (a barra) — `floating` (padrão, pílula solta) e `edge` (encostada, fio em cima).
|
|
607
|
+
• INDICADOR (o item) — a FORMA que marca o atual. A SEMÂNTICA é sempre a mesma: ouro.
|
|
608
|
+
|
|
609
|
+
O que TODO indicador compartilha, e por isso vive aqui em cima e não dentro de cada um:
|
|
610
|
+
altura de alvo, moldura do ícone, tipografia, ancoragem do contador, rótulo sempre visível.
|
|
611
|
+
A auditoria pedia exatamente isso — "a variante altera a forma, não toda a lógica visual".
|
|
612
|
+
|
|
613
|
+
O RÓTULO NUNCA SOME em nenhum indicador. Era o defeito 6/7 da auditoria: a `dock` escondia
|
|
614
|
+
todos e a `pill` escondia os inativos, e as sete telas que o Victor desenhou mostram o rótulo
|
|
615
|
+
em todos os itens, nas sete. Ícone sozinho só funciona quando o símbolo é inequívoco, e
|
|
616
|
+
`Garage`/`Fuel`/`Service` não são.
|
|
617
|
+
|
|
618
|
+
`--bottomnav-ring` é a moldura do ícone quando o indicador é redondo. Válvula no idioma do
|
|
619
|
+
`--chart-h` e do `--datagrid-max-h`: quem hospeda decide, o componente não vira prop nova. */
|
|
620
|
+
.bottom-nav-edge { --bottomnav-surface:var(--background); margin:0; padding-inline:0; border-width:1px 0 0; border-radius:0; background:var(--background); box-shadow:none; }
|
|
621
|
+
/* `--primary-emphasis` e não `--primary`: sem fundo, a marca vira TEXTO, e o amarelo puro
|
|
622
|
+
sobre o claro não se lê. É o mesmo token que o `.badge-primary` já usa por essa razão. */
|
|
623
|
+
.bottom-nav-ind-none .bottom-nav-item[aria-current] { color:var(--primary-emphasis); }
|
|
624
|
+
.bottom-nav-ind-none .bottom-nav-item[aria-current] .icon { opacity:1; }
|
|
625
|
+
/* SUAVE e PÍLULA envolvem ícone E rótulo — muda só o raio. O fundo é o amarelo a 12%, a mesma
|
|
626
|
+
receita do `.badge-primary`, e não um cinza: a auditoria mediu que a cápsula cinza da antiga
|
|
627
|
+
`surface` tirava a cor da marca justo do estado mais importante da barra. */
|
|
628
|
+
.bottom-nav-ind-subtle .bottom-nav-item[aria-current],.bottom-nav-ind-pill .bottom-nav-item[aria-current] { background:color-mix(in srgb,var(--primary) 12%,transparent); color:var(--primary-emphasis); }
|
|
629
|
+
.bottom-nav-ind-subtle .bottom-nav-item[aria-current] .icon,.bottom-nav-ind-pill .bottom-nav-item[aria-current] .icon { opacity:1; }
|
|
630
|
+
.bottom-nav-ind-subtle .bottom-nav-item { border-radius:calc(var(--radius-card) - var(--space-1)); }
|
|
631
|
+
.bottom-nav-ind-pill .bottom-nav-item { border-radius:var(--radius-control); }
|
|
632
|
+
/* OS QUATRO REDONDOS envolvem SÓ O ÍCONE, e o rótulo fica embaixo, fora da forma. É o que as
|
|
633
|
+
quatro telas do Victor mostram e o que o WhatsApp faz no aplicativo que roda; pintar o item
|
|
634
|
+
inteiro não aparece em nenhum dos dois lugares. A moldura vira quadrado de lado fixo para ser
|
|
635
|
+
CÍRCULO e não elipse — o rótulo é que é largo, e ele está fora daqui. */
|
|
636
|
+
.bottom-nav-ind-circle .bottom-nav-mark,.bottom-nav-ind-circle-raised .bottom-nav-mark,.bottom-nav-ind-circle-outline .bottom-nav-mark { border-radius:var(--radius-full); }
|
|
637
|
+
.bottom-nav-ind-circle .bottom-nav-item[aria-current],.bottom-nav-ind-circle-raised .bottom-nav-item[aria-current],.bottom-nav-ind-circle-outline .bottom-nav-item[aria-current] { color:var(--primary-emphasis); }
|
|
638
|
+
.bottom-nav-ind-circle .bottom-nav-item[aria-current] .icon,.bottom-nav-ind-circle-raised .bottom-nav-item[aria-current] .icon,.bottom-nav-ind-circle-outline .bottom-nav-item[aria-current] .icon { opacity:1; }
|
|
639
|
+
.bottom-nav-ind-circle .bottom-nav-item[aria-current] .bottom-nav-mark { background:color-mix(in srgb,var(--primary) 14%,transparent); }
|
|
640
|
+
/* ELEVADO: a moldura é uma superfície PRÓPRIA sobre a barra — por isso `--popover` e sombra, e
|
|
641
|
+
não uma cor de fundo. É o único indicador que usa profundidade em vez de cor para separar. */
|
|
642
|
+
.bottom-nav-ind-circle-raised .bottom-nav-item[aria-current] .bottom-nav-mark { background:var(--popover); box-shadow:var(--shadow-md); }
|
|
643
|
+
.bottom-nav-ind-circle-outline .bottom-nav-item[aria-current] .bottom-nav-mark { border:1px solid var(--primary-emphasis); }
|
|
644
|
+
/* CHEIO é o único que preenche com o amarelo sólido, e por isso é o único que troca o primeiro
|
|
645
|
+
plano inteiro para `--primary-foreground` — ícone, rótulo e contador juntos dentro da forma.
|
|
646
|
+
A auditoria recusou a antiga `pill` sólida (achado 5: "o ativo assume presença de CTA"), e a
|
|
647
|
+
diferença aqui é a FORMA: círculo do tamanho do alvo de toque lê como destino marcado; cápsula
|
|
648
|
+
larga, esticada pelo rótulo, lê como botão de ação primária dentro da navegação. */
|
|
649
|
+
/* 3.5rem = 56px, e também é número de fora: é a largura do indicador ativo do Material 3
|
|
650
|
+
Expressive (56dp) e a altura do indicador expandido na mesma doc. É a única forma aqui que
|
|
651
|
+
envolve ícone E rótulo, então usa a medida expandida e não a de 32. */
|
|
652
|
+
.bottom-nav-ind-circle-bold .bottom-nav-item[aria-current] { flex:none; width:var(--bottomnav-bold,3.5rem); border-radius:var(--radius-full); background:var(--primary); color:var(--primary-foreground); }
|
|
653
|
+
.bottom-nav-ind-circle-bold .bottom-nav-item[aria-current] .icon { opacity:1; }
|
|
654
|
+
/* O contador do `circle-bold` INVERTE, como o do `.btn-primary`, e a volta atrás está medida.
|
|
655
|
+
Tentei em 20/08/2026 deixá-lo dourado com um anel da cor da barra em volta — é o que as telas
|
|
656
|
+
do Victor mostram. O gate de contraste reprovou no tema CLARO: sobre o amarelo sólido, nem o
|
|
657
|
+
fundo do contador nem o anel branco chegam a 3:1. No escuro passava, e é assim que este defeito
|
|
658
|
+
se esconde. Fio fino não substitui contraste quando as duas cores são claras. */
|
|
659
|
+
.bottom-nav-ind-circle-bold .bottom-nav-item[aria-current] .badge { background:var(--primary-foreground); color:var(--primary); border-color:transparent; }
|
|
660
|
+
.bottom-nav-ind-circle-bold .bottom-nav-item[aria-current] .bottom-nav-badge { color:var(--primary-foreground); }
|
|
661
|
+
|
|
662
|
+
/* ── A LINHA DE LISTA TOCÁVEL (§4.3 do CONSUMIDOR-GARAGEM) ──────────────────────────
|
|
663
|
+
A LISTA NÃO PINTA SUPERFÍCIE. Quem dá o card é o `Card`, e repintar aqui seria um segundo
|
|
664
|
+
lugar para a mesma coisa — a `DIRECTION.md` §3.9 diz que composto REUSA o menor.
|
|
665
|
+
E SEM DIVISOR entre as linhas, que é decisão e não esquecimento: o realce de linha desta casa
|
|
666
|
+
é a PÍLULA (é o que `.sidebar-item` faz), e pílula com traço embaixo se contradizem — o traço
|
|
667
|
+
promete grade, a pílula promete alvo. Quem agrupa é a superfície de fora. */
|
|
668
|
+
.nav-list { display:flex; flex-direction:column; gap:var(--space-05); margin:0; padding:0; list-style:none; }
|
|
669
|
+
/* ── O RAIO DA LINHA SAI DE UMA CONTA, NÃO DE UM NÚMERO ─────────────────────────────
|
|
670
|
+
`raio do filho = raio do pai − padding do pai`. É o que faz o vão ser CONSTANTE na curva; sem
|
|
671
|
+
isso a curva da linha e a curva do painel divergem e o olho vê o desalinhamento antes de
|
|
672
|
+
qualquer medição — foi o Victor que achou, riscando o canto num print (18/08/2026).
|
|
673
|
+
POR QUE A PÍLULA NÃO SERVE AQUI, medido: `999px` não desenha 999 — o navegador limita à METADE
|
|
674
|
+
DA ALTURA. Então o raio efetivo da pílula MUDA com a altura da linha: 21px numa linha de um
|
|
675
|
+
texto, 26,8px numa de dois. Com o card em 22px e padding 4px o erro era de **8,8px**, e nenhum
|
|
676
|
+
raio de card conserta os dois casos ao mesmo tempo. A conta conserta sempre.
|
|
677
|
+
Escrito como `calc()` de propósito: se alguém mudar o raio do card ou o padding, a linha segue
|
|
678
|
+
sozinha. Número cravado divergiria em silêncio. */
|
|
679
|
+
.nav-list-row { display:flex; align-items:center; gap:var(--space-3); width:100%; min-height:var(--control-h-lg); padding:var(--space-2) var(--space-3); border:0; border-radius:calc(var(--radius-card) - var(--space-1)); background:transparent; color:var(--foreground); font:inherit; font-size:var(--text-sm); text-align:start; text-decoration:none; cursor:pointer; }
|
|
680
|
+
.nav-list-row .icon { flex:none; opacity:.8; }
|
|
681
|
+
/* O TEXTO É A ÚNICA TRILHA QUE ENCOLHE, e o par `flex:1` + `min-width:0` é o que faz isso: sem o
|
|
682
|
+
`min-width:0` o conteúdo mínimo do flex é o texto inteiro, e o rótulo longo empurra o valor e a
|
|
683
|
+
seta para fora da linha em vez de reticenciar. É a mesma família do defeito que a `pill` da
|
|
684
|
+
barra inferior pagou em 18/08/2026 — lá o rótulo virava zero, aqui ele expulsaria os vizinhos. */
|
|
685
|
+
.nav-list-text { flex:1; min-width:0; display:flex; flex-direction:column; }
|
|
686
|
+
.nav-list-label { overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
|
|
687
|
+
.nav-list-description { overflow:hidden; color:var(--muted-foreground); font-size:var(--text-xs); text-overflow:ellipsis; white-space:nowrap; }
|
|
688
|
+
/* O VALOR E A SETA NÃO ENCOLHEM: valor cortado MENTE (um "R$ 1.2" no lugar de "R$ 1.234"), e seta
|
|
689
|
+
cortada deixa de dizer que a linha abre. Quem cede espaço é o texto, que reticencia dizendo. */
|
|
690
|
+
.nav-list-value { flex:none; color:var(--muted-foreground); font-size:var(--text-sm); }
|
|
691
|
+
.nav-list-row .nav-list-chevron { flex:none; opacity:.6; }
|
|
692
|
+
@media (hover:hover) and (pointer:fine) { .nav-list-row:hover { background:var(--secondary); } }
|
|
693
|
+
/* `aria-disabled` e NÃO `:disabled` — a razão está escrita ao lado de `.btn:disabled`, medida em
|
|
694
|
+
13/08/2026: `:disabled` tira da ordem de foco e quem usa teclado nunca acha a linha. */
|
|
695
|
+
.nav-list-row[aria-disabled="true"] { opacity:.45; cursor:not-allowed; }
|
|
696
|
+
/* O PAINEL DÁ QUASE NADA, A LINHA DÁ O RESTO. Medido em 18/08/2026, depois de o Victor circular a
|
|
697
|
+
folga no print: com o `--card-pad` de 20px o realce da linha começava a 21px da borda e o texto
|
|
698
|
+
a 65px, e a linha ocupava só 88% da largura — realce flutuando no meio de uma caixa grande em
|
|
699
|
+
vez de preencher o painel. `--card-pad` é padding de CONTEÚDO; lista de linhas tocáveis quer
|
|
700
|
+
outra escala. É a anatomia dos dois exemplos de `NavigationMenu` que ele mandou: o item leva
|
|
701
|
+
`p-3` (que a nossa linha já tem) e o contêiner quase não leva nada.
|
|
702
|
+
Filho DIRETO de propósito: um card com layout grande e uma lista pequena dentro não pode perder
|
|
703
|
+
o padding dele. E `:has()` já é idioma desta folha (quatro usos antes deste). */
|
|
704
|
+
.card:has(> .nav-list) { padding:var(--space-1); }
|
|
445
705
|
|
|
446
706
|
/* ── Visibilidade responsiva — o mecanismo PADRÃO de colapsar uma barra ─────────────
|
|
447
707
|
Regra do projeto (enraizada): todo layout horizontal que aperta esconde o secundário
|
|
@@ -475,8 +735,14 @@ img,svg { display:block; }
|
|
|
475
735
|
/* 04. COMPONENTES PRIMITIVOS */
|
|
476
736
|
/* ========================================================================== */
|
|
477
737
|
.btn { --btn-bg:transparent; --btn-fg:var(--foreground); --btn-border:transparent; display:inline-flex; align-items:center; justify-content:center; gap:8px; height:var(--control-h-md); padding:0 15px; border:1px solid var(--btn-border); border-radius:var(--radius-control); background:var(--btn-bg); color:var(--btn-fg); font-size:var(--text-sm); font-weight:var(--weight-medium); line-height:var(--leading-none); text-decoration:none; white-space:nowrap; cursor:pointer; transition:transform var(--duration-fast) var(--ease-standard),background var(--duration-fast),border-color var(--duration-fast),color var(--duration-fast),opacity var(--duration-fast); }
|
|
478
|
-
.btn:hover:not(:disabled) { transform:none; } .btn:active:not(:disabled) { transform:scale(.97); opacity:.9; }
|
|
479
|
-
|
|
738
|
+
.btn:hover:not(:disabled):not([aria-disabled="true"]) { transform:none; } .btn:active:not(:disabled) { transform:scale(.97); opacity:.9; }
|
|
739
|
+
/* Duas formas de "não dá para fazer isto", e a diferença é QUEM alcança a explicação.
|
|
740
|
+
`:disabled` tira o controle da ordem de foco — medido em 13/08/2026: quem usa teclado nunca
|
|
741
|
+
chega nele, então uma dica pendurada ali não é lida por ninguém. O embrulho de <span> que a
|
|
742
|
+
referência do MUI documenta resolve o ponteiro e NÃO resolve o teclado (o span nasce com
|
|
743
|
+
tabIndex -1, medido). `[aria-disabled="true"]` mantém o controle focável e inerte — é ele
|
|
744
|
+
que se usa quando existe um PORQUÊ a dizer. A pele é a mesma; o que muda é o alcance. */
|
|
745
|
+
.btn:disabled,.btn[aria-disabled="true"] { opacity:.45; cursor:not-allowed; }
|
|
480
746
|
/* Cinco alturas, todas de token de densidade (autorizado pelo Victor em 24/07/2026). O raio
|
|
481
747
|
é pill em TODAS — o tamanho muda altura e respiro, nunca a forma. Em ponteiro grosso o
|
|
482
748
|
mínimo de 44px lá embaixo pega o xs e o sm, então alvo pequeno não vira alvo impossível. */
|
|
@@ -487,22 +753,22 @@ img,svg { display:block; }
|
|
|
487
753
|
/* Largura total: para rodapé de formulário e tela estreita. Só a largura muda. */
|
|
488
754
|
.btn-block { width:100%; }
|
|
489
755
|
.btn-primary { --btn-bg:var(--primary); --btn-fg:var(--primary-foreground); --btn-border:transparent; }
|
|
490
|
-
.btn-primary:hover:not(:disabled) { --btn-bg:var(--primary-hover); --btn-border:transparent; }
|
|
756
|
+
.btn-primary:hover:not(:disabled):not([aria-disabled="true"]) { --btn-bg:var(--primary-hover); --btn-border:transparent; }
|
|
491
757
|
.btn-secondary { --btn-bg:var(--secondary); --btn-fg:var(--secondary-foreground); --btn-border:transparent; }
|
|
492
|
-
.btn-secondary:hover:not(:disabled) { --btn-bg:var(--secondary-hover); --btn-border:transparent; }
|
|
758
|
+
.btn-secondary:hover:not(:disabled):not([aria-disabled="true"]) { --btn-bg:var(--secondary-hover); --btn-border:transparent; }
|
|
493
759
|
.btn-outline { --btn-bg:transparent; --btn-fg:var(--foreground); --btn-border:var(--border-strong); }
|
|
494
|
-
.btn-outline:hover:not(:disabled) { --btn-bg:var(--accent); }
|
|
760
|
+
.btn-outline:hover:not(:disabled):not([aria-disabled="true"]) { --btn-bg:var(--accent); }
|
|
495
761
|
.btn-ghost { --btn-bg:transparent; --btn-fg:var(--foreground); --btn-border:transparent; }
|
|
496
|
-
.btn-ghost:hover:not(:disabled) { --btn-bg:var(--accent); --btn-fg:var(--accent-foreground); }
|
|
762
|
+
.btn-ghost:hover:not(:disabled):not([aria-disabled="true"]) { --btn-bg:var(--accent); --btn-fg:var(--accent-foreground); }
|
|
497
763
|
.btn-danger { --btn-bg:var(--destructive); --btn-fg:var(--destructive-foreground); --btn-border:transparent; }
|
|
498
|
-
.btn-danger:hover:not(:disabled) { --btn-bg:var(--danger-400); }
|
|
764
|
+
.btn-danger:hover:not(:disabled):not([aria-disabled="true"]) { --btn-bg:var(--danger-400); }
|
|
499
765
|
/* Destrutivo em OUTRAS hierarquias: apagar não é sempre a ação principal da tela. O tom é o
|
|
500
766
|
mesmo (--destructive); o que muda é o peso. No claro o vermelho puro como texto não chega
|
|
501
767
|
a 4.5:1, então aprofunda com --foreground — mesmo tratamento do amarelo. */
|
|
502
768
|
.btn-danger-outline { --btn-bg:transparent; --btn-fg:var(--destructive); --btn-border:var(--destructive); }
|
|
503
|
-
.btn-danger-outline:hover:not(:disabled) { --btn-bg:color-mix(in oklab, var(--destructive) 14%, transparent); }
|
|
769
|
+
.btn-danger-outline:hover:not(:disabled):not([aria-disabled="true"]) { --btn-bg:color-mix(in oklab, var(--destructive) 14%, transparent); }
|
|
504
770
|
.btn-danger-ghost { --btn-bg:transparent; --btn-fg:var(--destructive); --btn-border:transparent; }
|
|
505
|
-
.btn-danger-ghost:hover:not(:disabled) { --btn-bg:color-mix(in oklab, var(--destructive) 14%, transparent); }
|
|
771
|
+
.btn-danger-ghost:hover:not(:disabled):not([aria-disabled="true"]) { --btn-bg:color-mix(in oklab, var(--destructive) 14%, transparent); }
|
|
506
772
|
[data-theme="light"] .btn-danger-outline,[data-theme="light"] .btn-danger-ghost,[data-theme="light"] .btn-link-danger { --btn-fg:color-mix(in oklab, var(--destructive) 62%, var(--foreground)); }
|
|
507
773
|
/* LINK: aparência de texto, sem cápsula e sem altura de controle — o sublinhado é o que diz
|
|
508
774
|
que é clicável, então ele existe desde o repouso (revelar no hover esconde a affordance).
|
|
@@ -511,7 +777,7 @@ img,svg { display:block; }
|
|
|
511
777
|
medido no catálogo, o "Learn more" é ação isolada dentro de um flex, não link no meio de
|
|
512
778
|
prosa, e media 67×13px. O texto continua onde está — a caixa cresce centrada nele. */
|
|
513
779
|
.btn-link { height:auto; min-height:24px; padding:0; gap:6px; border:0; border-radius:var(--radius-sm); --btn-bg:transparent; --btn-fg:var(--foreground); text-decoration:underline; text-underline-offset:3px; text-decoration-thickness:1px; }
|
|
514
|
-
.btn-link:hover:not(:disabled) { text-decoration-thickness:2px; }
|
|
780
|
+
.btn-link:hover:not(:disabled):not([aria-disabled="true"]) { text-decoration-thickness:2px; }
|
|
515
781
|
.btn-link-primary { --btn-fg:var(--primary); }
|
|
516
782
|
[data-theme="light"] .btn-link-primary { --btn-fg:color-mix(in oklab, var(--primary) 45%, var(--foreground)); }
|
|
517
783
|
.btn-link-danger { --btn-fg:var(--destructive); }
|
|
@@ -530,7 +796,7 @@ img,svg { display:block; }
|
|
|
530
796
|
"ativa", porque ele também anuncia "página atual" pro leitor de tela. O traço é
|
|
531
797
|
::after, pill, alinhado ao padding do texto; posição pelo eixo bloco (não espelha RTL). */
|
|
532
798
|
.btn-nav { --btn-bg:transparent; --btn-fg:var(--muted-foreground); --btn-border:transparent; }
|
|
533
|
-
.btn-nav:hover:not(:disabled) { --btn-fg:var(--foreground); }
|
|
799
|
+
.btn-nav:hover:not(:disabled):not([aria-disabled="true"]) { --btn-fg:var(--foreground); }
|
|
534
800
|
.btn-icon { width:var(--control-h-md); padding:0; border-radius:var(--radius-md); }
|
|
535
801
|
.btn-icon.btn-xs { width:var(--control-h-xs); border-radius:var(--radius-sm); }
|
|
536
802
|
.btn-icon.btn-sm { width:var(--control-h-sm); border-radius:var(--radius-sm); } .btn-icon.btn-lg { width:var(--control-h-lg); border-radius:var(--radius-md); }
|
|
@@ -590,6 +856,10 @@ img,svg { display:block; }
|
|
|
590
856
|
/* HoverCard (Lote 1) reusa `.popover`; só limita a largura, porque prévia é conteúdo, não rótulo. */
|
|
591
857
|
.hover-card { max-width:min(20rem,calc(100vw - var(--space-8))); }
|
|
592
858
|
.input-wrap { position:relative; } .input-wrap>.icon { position:absolute; inset-inline-start:12px; top:50%; transform:translateY(-50%); color:var(--subtle-foreground); pointer-events:none; } .input-wrap .input { padding-inline-start:40px; }
|
|
859
|
+
/* Form (M1): empilhar campos com respiro. Nada além disso — sem borda, sem fundo, sem largura.
|
|
860
|
+
Formulário é ARRANJO, e arranjo é do Stack/Grid; se esta classe crescer, ela vira uma terceira
|
|
861
|
+
maneira de fazer layout e passa a brigar com as duas que já existem. */
|
|
862
|
+
.form { display:grid; gap:var(--space-5); }
|
|
593
863
|
.field-error { color:var(--danger-400); font-size:var(--text-xs); }
|
|
594
864
|
.checkbox,.radio { display:inline-flex; align-items:flex-start; gap:9px; color:var(--foreground); cursor:pointer; }
|
|
595
865
|
.checkbox input,.radio input { position:absolute; opacity:0; pointer-events:none; }
|
|
@@ -637,17 +907,81 @@ img,svg { display:block; }
|
|
|
637
907
|
classe media 26px dentro do .brand (lh 18px) e 22px dentro de um .btn-nav (lh 12px) — na
|
|
638
908
|
mesma barra (achado A2, 26/07/2026). Geometria de componente não se decide pelo contexto.
|
|
639
909
|
O .chip do catálogo já tinha aprendido isso; o .badge não. */
|
|
640
|
-
.badge { display:inline-flex; align-items:center; gap:6px; min-height:var(--space-6); padding:3px 9px; border:1px solid var(--border); border-radius:var(--radius-control); background:var(--secondary); color:var(--secondary-foreground); font-size:var(--text-xs); font-weight:var(--weight-medium); line-height:var(--leading-none); white-space:nowrap; }
|
|
910
|
+
.badge { --badge-accent:var(--secondary-foreground); display:inline-flex; align-items:center; justify-content:center; gap:6px; min-height:var(--space-6); padding:3px 9px; border:1px solid var(--border); border-radius:var(--radius-control); background:var(--secondary); color:var(--secondary-foreground); font-size:var(--text-xs); font-weight:var(--weight-medium); line-height:var(--leading-none); white-space:nowrap; }
|
|
641
911
|
/* O glifo do badge tem tamanho PRÓPRIO, senão o `.icon` padrão (20px) estoura os 24px e o
|
|
642
912
|
badge com ícone fica mais alto que o badge de texto na mesma fila. 16px + 3+3 de padding
|
|
643
913
|
+ 1+1 de borda = 24px exatos: com ícone ou sem, mede o mesmo. */
|
|
644
914
|
.badge .icon { width:var(--icon-sm); height:var(--icon-sm); }
|
|
645
|
-
.badge-primary { color:var(--primary-emphasis); border-color:var(--primary-outline); background:color-mix(in srgb,var(--primary) 10%,transparent); }
|
|
915
|
+
.badge-primary { --badge-accent:var(--primary-emphasis); color:var(--primary-emphasis); border-color:var(--primary-outline); background:color-mix(in srgb,var(--primary) 10%,transparent); }
|
|
916
|
+
/* TAMANHOS. O md é a base (24px), que não muda — é o que as 103 fichas já usam. */
|
|
917
|
+
.badge-xs { min-height:var(--space-4); padding:0 var(--space-1); border-width:0; }
|
|
918
|
+
.badge-sm { min-height:var(--space-5); padding:0 var(--space-2); }
|
|
919
|
+
.badge-lg { min-height:var(--space-7); padding:0 var(--space-3); font-size:var(--text-sm); }
|
|
920
|
+
/* ACESSÓRIOS de dentro do chip. O ponto herda a cor do texto por `currentColor`, então ele
|
|
921
|
+
acompanha variante e ênfase sem uma regra por combinação. */
|
|
922
|
+
.badge-dot { flex:none; width:var(--space-2); height:var(--space-2); border-radius:var(--radius-full); background:currentColor; }
|
|
923
|
+
.badge-image { flex:none; width:var(--space-4); height:var(--space-4); border-radius:var(--radius-full); object-fit:cover; }
|
|
924
|
+
.badge-dismiss { display:inline-flex; align-items:center; justify-content:center; flex:none; width:var(--space-4); height:var(--space-4); margin-inline-end:calc(var(--space-1)*-1); padding:0; border:0; border-radius:var(--radius-full); background:transparent; color:inherit; cursor:pointer; }
|
|
925
|
+
.badge-dismiss:hover { background:color-mix(in srgb,currentColor 16%,transparent); }
|
|
926
|
+
.badge-dismiss .icon { width:var(--space-3); height:var(--space-3); }
|
|
646
927
|
|
|
647
|
-
|
|
648
|
-
.
|
|
649
|
-
|
|
650
|
-
|
|
928
|
+
/* ── O BADGE SOBREPOSTO — contador no canto de outra coisa ──────────────────────────
|
|
929
|
+
Anatomia da `Badge.js` da MUI, medida no fonte: raiz `relative/inline-flex/flex-shrink:0`,
|
|
930
|
+
o contador `absolute` com `z-index:1`, e o deslocamento do canto valendo **0 para caixa
|
|
931
|
+
quadrada e 14% para redonda** — é `overlap`, e sem ele o contador de um avatar fica solto
|
|
932
|
+
fora do círculo.
|
|
933
|
+
O ANEL DE RECORTE é o que faltava e o que fazia parecer sujo: um fio da cor da superfície
|
|
934
|
+
entre o contador e o que está atrás. Três fontes independentes: `ring-[1.5px] ring-bg-primary`
|
|
935
|
+
da Untitled UI, `border-background border-2` do ReUI e `box-shadow: 0 0 0 2px background.paper`
|
|
936
|
+
do exemplo de contato da MUI.
|
|
937
|
+
A superfície de trás NÃO é adivinhável em CSS — dentro de um card ela é `--card`, não
|
|
938
|
+
`--background`. Por isso ela é uma propriedade: sobrescreva `--badge-ring` no contêiner. */
|
|
939
|
+
.badge-anchor { position:relative; display:inline-flex; vertical-align:middle; flex-shrink:0; }
|
|
940
|
+
.badge-overlay { --badge-ring:var(--background); --badge-inset:0%; position:absolute; z-index:1; min-height:var(--space-4); padding:0 var(--space-1); box-shadow:0 0 0 var(--space-05) var(--badge-ring); }
|
|
941
|
+
.badge-on-circle { --badge-inset:14%; }
|
|
942
|
+
/* O ponto sozinho não tem conteúdo, então ele precisa de medida própria. */
|
|
943
|
+
.badge-overlay.badge-is-dot { width:var(--space-2); min-width:var(--space-2); height:var(--space-2); min-height:var(--space-2); padding:0; }
|
|
944
|
+
.badge-at-top-end { inset-block-start:var(--badge-inset); inset-inline-end:var(--badge-inset); transform:translate(50%,-50%); }
|
|
945
|
+
.badge-at-top-start { inset-block-start:var(--badge-inset); inset-inline-start:var(--badge-inset); transform:translate(-50%,-50%); }
|
|
946
|
+
.badge-at-bottom-end { inset-block-end:var(--badge-inset); inset-inline-end:var(--badge-inset); transform:translate(50%,50%); }
|
|
947
|
+
.badge-at-bottom-start { inset-block-end:var(--badge-inset); inset-inline-start:var(--badge-inset); transform:translate(-50%,50%); }
|
|
948
|
+
/* `transform` não espelha sozinho, e `inset-inline-*` sim: em RTL o par sai trocado e o
|
|
949
|
+
contador vai para dentro da caixa. As quatro regras abaixo são o preço disso. */
|
|
950
|
+
.badge-at-top-end:dir(rtl) { transform:translate(-50%,-50%); }
|
|
951
|
+
.badge-at-top-start:dir(rtl) { transform:translate(50%,-50%); }
|
|
952
|
+
.badge-at-bottom-end:dir(rtl) { transform:translate(-50%,50%); }
|
|
953
|
+
.badge-at-bottom-start:dir(rtl) { transform:translate(50%,50%); }
|
|
954
|
+
/* ACESSÓRIO SOBRE A MARCA — o badge INVERTE quando o que está debaixo dele é o amarelo.
|
|
955
|
+
Achado pelo Victor em 17/08/2026 na barra inferior, e a pergunta obrigatória do CLAUDE.md
|
|
956
|
+
("quem mais tem esse problema?") devolveu um defeito ANTIGO e publicado: `.badge-primary`
|
|
957
|
+
dentro de `.btn-primary` é amarelo translúcido com texto `--primary-emphasis` — medido no
|
|
958
|
+
navegador, texto `oklch(0.795 0.184 86.047)` sobre fundo `oklch(0.795 0.184 86.047)`.
|
|
959
|
+
Contraste 1:1, literalmente invisível, e nenhum gate via: check nenhum compara a cor de um
|
|
960
|
+
filho com a do pai.
|
|
961
|
+
A inversão é a saída óbvia e a única que não inventa cor nova — o par da marca já existe
|
|
962
|
+
(`--primary`/`--primary-foreground`) e é o mesmo que o botão usa.
|
|
963
|
+
O ENVOLTÓRIO leva só a cor de texto, nunca fundo: ele é a caixa de posição do acessório, e
|
|
964
|
+
pintá-lo desenharia um retângulo atrás de um badge que é pílula. */
|
|
965
|
+
.btn-primary .badge { background:var(--primary-foreground); color:var(--primary); border-color:transparent; }
|
|
966
|
+
/* O `circle-bold` é o único indicador que preenche com o amarelo sólido, então é o único onde
|
|
967
|
+
o contador cai sobre a cor da marca e precisa da mesma inversão do botão primário. Os outros
|
|
968
|
+
seis deixam o contador sobre a superfície da barra, onde ele já se lê. */
|
|
969
|
+
|
|
970
|
+
.badge-success { --badge-accent:var(--success-400); color:var(--success-400); background:var(--success-bg); border-color:color-mix(in srgb,var(--success-400) 35%,var(--border)); }
|
|
971
|
+
.badge-warning { --badge-accent:var(--warning-400); color:var(--warning-400); background:var(--warning-bg); border-color:color-mix(in srgb,var(--warning-400) 35%,var(--border)); }
|
|
972
|
+
.badge-danger { --badge-accent:var(--danger-400); color:var(--danger-400); background:var(--danger-bg); border-color:color-mix(in srgb,var(--danger-400) 35%,var(--border)); }
|
|
973
|
+
.badge-info,.badge-running { --badge-accent:var(--info-400); color:var(--info-400); background:var(--info-bg); border-color:color-mix(in srgb,var(--info-400) 35%,var(--border)); }
|
|
974
|
+
/* ── AS TRÊS ÊNFASES — e elas vêm DEPOIS das variantes de propósito ─────────────────
|
|
975
|
+
Cada variante declara só o ACENTO; as ênfases saem dele. Antes cada variante escrevia
|
|
976
|
+
cor + fundo + borda na mão, e uma ênfase nova custaria seis regras novas.
|
|
977
|
+
A POSIÇÃO NO ARQUIVO É A REGRA, não estilo: `.badge-solid` e `.badge-success` têm a MESMA
|
|
978
|
+
especificidade (0,1,0), então quem vem depois vence. Escritas antes, as três não faziam
|
|
979
|
+
nada em quatro das seis variantes — medido no navegador, `solid` e `outline` devolviam o
|
|
980
|
+
número do `soft`. É a mesma armadilha que o `.banner` e o `.data-list` já pagaram aqui.
|
|
981
|
+
`--background` como texto do sólido serve aos DOIS temas porque o acento já vira com o
|
|
982
|
+
tema: `--success-400` é verde CLARO no escuro e verde ESCURO no claro. */
|
|
983
|
+
.badge-solid { background:var(--badge-accent); color:var(--background); border-color:transparent; }
|
|
984
|
+
.badge-outline { background:transparent; color:var(--badge-accent); border-color:var(--badge-accent); }
|
|
651
985
|
.status-dot { width:8px; height:8px; border-radius:50%; background:currentColor; box-shadow:0 0 0 3px color-mix(in srgb,currentColor 14%,transparent); }
|
|
652
986
|
/* Status: condição operacional (DIRECTION §3.6) — NÃO é o pill do Badge; é ponto + rótulo
|
|
653
987
|
solto na linha. A variante pinta o .status (o ponto herda por currentColor) e o rótulo
|
|
@@ -688,6 +1022,10 @@ img,svg { display:block; }
|
|
|
688
1022
|
@media (max-width:640px) { .banner { grid-template-columns:auto minmax(0,1fr); } .banner>:last-child { grid-column:2; justify-self:start; } }
|
|
689
1023
|
.progress { height:8px; overflow:hidden; border-radius:999px; background:var(--surface-3); }
|
|
690
1024
|
.progress>span { display:block; height:100%; border-radius:inherit; background:var(--primary); }
|
|
1025
|
+
/* DataState (M3): a região que troca de cara. Ela NÃO desenha nada próprio — só empilha o que
|
|
1026
|
+
já existe (esqueleto, alerta, estado vazio, conteúdo) com respiro entre eles. Se ganhar borda
|
|
1027
|
+
ou fundo, viram duas superfícies para a mesma coisa — o defeito que a `.table-region` cobrou. */
|
|
1028
|
+
.data-state { display:grid; gap:var(--space-3); }
|
|
691
1029
|
.skeleton { position:relative; overflow:hidden; border-radius:7px; background:var(--surface-3); }
|
|
692
1030
|
.skeleton { animation:skeleton-pulse 1.35s ease-in-out infinite; } @keyframes skeleton-pulse { 50% { opacity:.55; } }
|
|
693
1031
|
|
|
@@ -731,7 +1069,16 @@ img,svg { display:block; }
|
|
|
731
1069
|
tabela; `DataGrid` embrulha em `.table-wrap`; e as 3 tabelas da página legada estão todas
|
|
732
1070
|
dentro de `.table-wrap`. Nenhuma <table> do repositório fica descoberta — o gate de pixel é
|
|
733
1071
|
quem prova, e ele tem de acusar ZERO mudança nas superfícies de tabela. */
|
|
734
|
-
|
|
1072
|
+
/* `.table-region` entrou nesta regra em 13/08/2026, e a linha acima ("o gate tem de acusar ZERO
|
|
1073
|
+
mudança nas superfícies de tabela") vale para o ESCOPO daquele lote, não para sempre: aqui a
|
|
1074
|
+
mudança de superfície é o pedido. O `Table` embrulhava em `.table-region`, que tinha só
|
|
1075
|
+
`overflow` e `--radius-lg` — sem borda e sem fundo. Resultado na tela: a tabela aparecia solta
|
|
1076
|
+
na página enquanto o `DataGrid`, do lado, tinha a caixa flutuante da marca. Duas peles para a
|
|
1077
|
+
mesma coisa, e a do componente mais simples era a errada.
|
|
1078
|
+
Somar o seletor em vez de copiar os valores: copiar deixaria duas listas para manter em sincronia,
|
|
1079
|
+
que é como as duas peles divergiram. O que continua só do `.table-region` é o foco — ele é
|
|
1080
|
+
`tabIndex=0` (região rolável precisa ser alcançável pelo teclado) e o `.table-wrap` não é. */
|
|
1081
|
+
.table-wrap,.table-region { overflow:auto; border:1px solid var(--border); border-radius:var(--radius-card); background:var(--card); }
|
|
735
1082
|
.table,.table-wrap table { width:100%; border-collapse:collapse; min-width:720px; }
|
|
736
1083
|
.table th,.table td,.table-wrap th,.table-wrap td { height:var(--row-h); padding:0 14px; text-align:start; border-bottom:1px solid var(--border); }
|
|
737
1084
|
.table th,.table-wrap th { color:var(--subtle-foreground); background:var(--surface-2); font-size:var(--text-xs); letter-spacing:var(--tracking-wider); text-transform:uppercase; }
|
|
@@ -760,9 +1107,27 @@ img,svg { display:block; }
|
|
|
760
1107
|
/* DataList: chave/valor em duas colunas. O termo é secundário — mesmo idioma do cabeçalho de
|
|
761
1108
|
tabela. Sem a regra (achado A13) o <dl> saía cru: dt e dd empilhados, com o recuo de 40px
|
|
762
1109
|
que o navegador dá ao <dd>. */
|
|
763
|
-
|
|
764
|
-
|
|
1110
|
+
/* A coluna do TERMO era `max-content`, que não encolhe nunca: um `dt` comprido empurrava a
|
|
1111
|
+
PÁGINA para fora em telas estreitas — medido em 16/08/2026 a 320px, quando a página de hooks
|
|
1112
|
+
estreou termos de frase inteira e a varredura acusou +179px de rolagem lateral. `auto` sizes
|
|
1113
|
+
igual quando há espaço (por isso nenhuma baseline mudou) e cai para min-content quando não há,
|
|
1114
|
+
deixando o termo quebrar em vez de arrastar o documento. O defeito era do COMPONENTE, não da
|
|
1115
|
+
página nova: o `DataList` é publicado, e quem escrevesse um termo longo levava o mesmo.
|
|
1116
|
+
`auto` foi TENTADO primeiro e não resolveu — medido, não suposto: a trilha `auto` tem
|
|
1117
|
+
max-content como máximo e não encolhe abaixo dele quando o contêiner aperta, e a sobra caiu de
|
|
1118
|
+
179px para 90px em vez de zerar. `minmax(0,max-content)` é o que encolhe: com espaço mede
|
|
1119
|
+
igual ao que media antes (nenhuma baseline mudou), sem espaço o termo quebra. */
|
|
1120
|
+
.data-list { display:grid; grid-template-columns:minmax(0,max-content) minmax(0,1fr); gap:var(--space-2) var(--space-6); margin:0; }
|
|
1121
|
+
.data-list dt { min-width:0; color:var(--muted-foreground); }
|
|
765
1122
|
.data-list dd { min-width:0; margin:0; }
|
|
1123
|
+
/* E no ESTREITO ele empilha — a outra metade da correção. Medido a 320px com termos de frase
|
|
1124
|
+
inteira: duas colunas não cabem, o termo resolvia em 164px, a calha comia o resto e a coluna do
|
|
1125
|
+
valor ficava com `0px`, jogando o texto para fora da PÁGINA. Mesmo remédio que `.message` e
|
|
1126
|
+
`.log-line` já usam aqui. A regra mora DEPOIS da base de propósito: escrita antes, ela não
|
|
1127
|
+
valia nada — mídia não soma especificidade, então a base vencia por vir por último. Foi
|
|
1128
|
+
exatamente o que aconteceu na primeira tentativa, e é a armadilha que o comentário do
|
|
1129
|
+
`.banner` lá em cima já registrava. */
|
|
1130
|
+
@media (max-width:640px) { .data-list { grid-template-columns:minmax(0,1fr); } }
|
|
766
1131
|
/* Timeline: o trilho existia e o PONTO não — `.timeline-dot` media 0×0 (achado A13), então a
|
|
767
1132
|
linha do tempo saía com a régua e sem as marcas, e o <ol> ainda mostrava a numeração do
|
|
768
1133
|
navegador. Agora os dois derivam do MESMO token: o trilho fica a meio diâmetro do ponto, e
|
|
@@ -880,7 +1245,7 @@ img,svg { display:block; }
|
|
|
880
1245
|
.calendar-months { position:relative; }
|
|
881
1246
|
.calendar nav { position:absolute; inset-block-start:0; inset-inline:0; display:flex; justify-content:space-between; }
|
|
882
1247
|
.calendar nav button { display:inline-flex; align-items:center; justify-content:center; width:var(--control-h-sm); height:var(--control-h-sm); padding:0; border:0; border-radius:var(--radius-control); background:transparent; color:var(--muted-foreground); cursor:pointer; }
|
|
883
|
-
.calendar nav button:hover:not(:disabled) { background:var(--secondary); color:var(--foreground); }
|
|
1248
|
+
.calendar nav button:hover:not(:disabled):not([aria-disabled="true"]) { background:var(--secondary); color:var(--foreground); }
|
|
884
1249
|
.calendar nav button:disabled { opacity:.4; cursor:not-allowed; }
|
|
885
1250
|
.calendar nav svg { width:var(--icon-sm); height:var(--icon-sm); fill:currentColor; }
|
|
886
1251
|
.calendar-caption { display:flex; align-items:center; justify-content:center; height:var(--control-h-sm); color:var(--foreground); font-size:var(--text-sm); font-weight:var(--weight-medium); }
|
|
@@ -893,7 +1258,7 @@ img,svg { display:block; }
|
|
|
893
1258
|
.calendar th { padding:0 0 var(--space-1); text-align:center; color:var(--muted-foreground); font-size:var(--text-xs); font-weight:var(--weight-regular); }
|
|
894
1259
|
.calendar td { padding:0; text-align:center; }
|
|
895
1260
|
.calendar td button { display:flex; align-items:center; justify-content:center; width:var(--control-h-md); height:var(--control-h-md); border:0; border-radius:var(--radius-control); background:transparent; color:var(--foreground); font-size:var(--text-sm); font-variant-numeric:tabular-nums; cursor:pointer; }
|
|
896
|
-
.calendar td button:hover:not(:disabled) { background:var(--secondary); }
|
|
1261
|
+
.calendar td button:hover:not(:disabled):not([aria-disabled="true"]) { background:var(--secondary); }
|
|
897
1262
|
.calendar td[data-today] button { font-weight:var(--weight-medium); box-shadow:inset 0 0 0 1px var(--border-strong); }
|
|
898
1263
|
.calendar td[data-selected] button { background:var(--primary); color:var(--primary-foreground); font-weight:var(--weight-medium); box-shadow:none; }
|
|
899
1264
|
.calendar td[data-outside] button { color:var(--subtle-foreground); }
|
|
@@ -946,6 +1311,151 @@ img,svg { display:block; }
|
|
|
946
1311
|
.media-time { padding:0 7px; color:var(--media-muted); font:var(--text-xs) var(--font-code); white-space:nowrap; }
|
|
947
1312
|
.media-volume { width:82px; accent-color:var(--primary); }
|
|
948
1313
|
|
|
1314
|
+
/* IMAGEM (PLANO-1.0, item L4). A caixa é reservada ANTES do byte chegar — é a mesma razão que o
|
|
1315
|
+
comentário do `--media-ar` acima registra: sem proporção, a imagem mede zero até carregar e a
|
|
1316
|
+
página salta quando ela chega (CLS). O fundo é a superfície do esqueleto, e o bitmap a cobre ao
|
|
1317
|
+
pintar — marcador de carregamento sem uma linha de JavaScript.
|
|
1318
|
+
|
|
1319
|
+
⚠ NÃO declare `aspect-ratio` aqui, e isto foi MEDIDO em 15/08/2026, não deduzido. A primeira
|
|
1320
|
+
versão desta regra tinha `aspect-ratio:var(--image-ratio,auto)`, e o fallback `auto` PASSA POR
|
|
1321
|
+
CIMA do `aspect-ratio: auto <width> / <height>` que o navegador deriva dos atributos do
|
|
1322
|
+
elemento. Resultado medido numa caixa de 400px: com `width=300 height=200` e sem a prop `ratio`,
|
|
1323
|
+
a imagem saía **400×0** — ou seja, a regra escrita para impedir o salto de layout produzia
|
|
1324
|
+
exatamente o salto. Sem a declaração, o mesmo caso reserva 400×267. Quem tem proporção declarada
|
|
1325
|
+
recebe `aspect-ratio` inline, do componente; quem não tem fica com o comportamento do navegador,
|
|
1326
|
+
que já é o certo. */
|
|
1327
|
+
.image { display:block; width:100%; height:auto; object-fit:cover; border-radius:var(--radius-lg); background:var(--surface-3); }
|
|
1328
|
+
.image-contain { object-fit:contain; background:transparent; }
|
|
1329
|
+
.image-broken { display:grid; place-items:center; color:var(--muted-foreground); }
|
|
1330
|
+
|
|
1331
|
+
/* GALERIA (PLANO-1.0, item L2). A grade se adapta sozinha — `auto-fill` com um mínimo, e não um
|
|
1332
|
+
número de colunas —, porque quem sabe quanta largura existe é o contêiner, não o componente.
|
|
1333
|
+
`--gallery-min` é a válvula, no idioma do `--carousel-slide` e do `--qr-size`.
|
|
1334
|
+
O SELECIONADO NÃO INVENTA LINGUAGEM: o ladrilho recebe `.is-selected`, a mesma classe da aba
|
|
1335
|
+
ativa, do item de lateral e do botão alternado — cápsula elevada, rótulo na cor da marca e o
|
|
1336
|
+
fio amarelo embaixo. É a regra "uma linguagem só" deste arquivo, e por isso aqui não há uma
|
|
1337
|
+
segunda maneira de dizer "este está escolhido". O recuo existe para a cápsula aparecer: com
|
|
1338
|
+
`padding:0` o fundo do selecionado fica exatamente sob a foto e não se vê.
|
|
1339
|
+
Foco também não tem regra própria: a global do topo deste arquivo já desenha o anel, e ele é
|
|
1340
|
+
de cor DIFERENTE da seleção — senão focar e escolher viram o mesmo sinal. */
|
|
1341
|
+
.gallery { display:grid; grid-template-columns:repeat(auto-fill,minmax(var(--gallery-min,8rem),1fr)); gap:var(--space-3); margin:0; padding:0; list-style:none; }
|
|
1342
|
+
.gallery-item { min-width:0; }
|
|
1343
|
+
.gallery-tile { display:grid; gap:var(--space-1); width:100%; padding:var(--space-1); border:0; border-radius:var(--radius-lg); font:inherit; text-align:start; cursor:pointer; }
|
|
1344
|
+
/* `:not(.is-selected)` e não uma regra de selecionado própria, e a diferença foi MEDIDA em
|
|
1345
|
+
15/08/2026: `.gallery-tile` e `.is-selected` têm a MESMA especificidade, e a do ladrilho vem
|
|
1346
|
+
depois no arquivo — então `background:transparent` matava a cápsula da linguagem da casa e o
|
|
1347
|
+
selecionado saía só com o fio amarelo. Reescrever a cápsula aqui seria a segunda cópia da
|
|
1348
|
+
linguagem "uma linguagem só" existe para impedir; tirar o fundo só de quem NÃO está escolhido
|
|
1349
|
+
deixa a regra de lá governar sem competição. O fundo de botão do navegador precisa morrer de
|
|
1350
|
+
qualquer forma — ele é `buttonface`, não transparente. */
|
|
1351
|
+
.gallery-tile:not(.is-selected) { background:transparent; color:var(--muted-foreground); }
|
|
1352
|
+
.gallery-caption { font-size:var(--text-sm); }
|
|
1353
|
+
|
|
1354
|
+
/* PROSA (PLANO-1.0, item L5). É a PELE do texto longo, não o parser — quem transforma Markdown em
|
|
1355
|
+
elementos é o consumidor. Aqui só se decide como esses elementos se leem.
|
|
1356
|
+
|
|
1357
|
+
⚠ TODA regra abaixo é ESCOPADA em `.prose`, e isso não é estilo de escrita: é a trava do item, e
|
|
1358
|
+
ela tem uma conta paga atrás. As cinco regras de tabela deste arquivo eram de ELEMENTO e valiam
|
|
1359
|
+
para toda <table> do documento — o calendário nasceu com 720px de largura por causa disso, e
|
|
1360
|
+
nenhum gate viu, porque os checks 15 e 18 comparam CLASSE e seletor de elemento não tem
|
|
1361
|
+
nenhuma. Escrever `blockquote{...}` aqui repetiria o Lote 4 com outro elemento.
|
|
1362
|
+
|
|
1363
|
+
O tamanho sobe para `--text-base`: o corpo do documento é `--text-md` (14px), que serve a
|
|
1364
|
+
interface e é pequeno para ler artigo. A entrelinha é `--leading-relaxed`, que existe no token
|
|
1365
|
+
e não tinha uso. A MEDIDA de linha é `--prose-measure`, no idioma das outras válvulas.
|
|
1366
|
+
|
|
1367
|
+
A FONTE não muda: `--font-editorial` (IBM Plex Serif) existe e está sem uso, e usá-la aqui
|
|
1368
|
+
seria decisão de APARÊNCIA que o BUILDING.md §3.1 manda levar ao Victor com imagem do antes e
|
|
1369
|
+
do depois. Quem quiser serifa na prosa aponta a variável; a Aurea não decide isso sozinha. */
|
|
1370
|
+
.prose { max-width:var(--prose-measure,68ch); font-size:var(--text-base); line-height:var(--leading-relaxed); }
|
|
1371
|
+
.prose > * + * { margin-block-start:var(--space-4); }
|
|
1372
|
+
.prose > h2,.prose > h3,.prose > h4 { margin-block-start:var(--space-8); }
|
|
1373
|
+
.prose h1,.prose h2,.prose h3,.prose h4 { line-height:var(--leading-tight); font-weight:var(--weight-semibold); }
|
|
1374
|
+
.prose h1 { font-size:var(--text-3xl); }
|
|
1375
|
+
.prose h2 { font-size:var(--text-2xl); }
|
|
1376
|
+
.prose h3 { font-size:var(--text-xl); }
|
|
1377
|
+
.prose h4 { font-size:var(--text-lg); }
|
|
1378
|
+
.prose blockquote { margin-inline:0; padding-inline-start:var(--space-4); border-inline-start:2px solid var(--border-strong); color:var(--muted-foreground); }
|
|
1379
|
+
.prose ul,.prose ol { padding-inline-start:var(--space-6); }
|
|
1380
|
+
.prose li + li { margin-block-start:var(--space-2); }
|
|
1381
|
+
/* `0.9em` e não um token: código dentro do texto se mede em relação à LINHA em que está, e um
|
|
1382
|
+
valor absoluto o deixaria maior que o parágrafo dentro de um h2. É o mesmo motivo pelo qual o
|
|
1383
|
+
`.badge` tem `line-height:1` — geometria que depende do contexto se escreve relativa. */
|
|
1384
|
+
.prose code { padding:var(--space-05) var(--space-1); border-radius:var(--radius-xs); background:var(--surface-2); font-family:var(--font-code); font-size:0.9em; }
|
|
1385
|
+
.prose pre { overflow-x:auto; padding:var(--space-4); border:1px solid var(--border); border-radius:var(--radius-card); background:var(--surface-2); }
|
|
1386
|
+
.prose pre code { padding:0; border-radius:0; background:transparent; font-size:var(--text-sm); }
|
|
1387
|
+
.prose img { max-width:100%; height:auto; border-radius:var(--radius-lg); }
|
|
1388
|
+
.prose hr { border:0; border-block-start:1px solid var(--border); }
|
|
1389
|
+
.prose figcaption { color:var(--muted-foreground); font-size:var(--text-sm); }
|
|
1390
|
+
/* A tabela da prosa NÃO é a tabela de dados: sem `min-width:720px`, sem faixa de cabeçalho em
|
|
1391
|
+
caixa alta e sem hover de linha. Uma tabela de três colunas no meio de um artigo não é uma
|
|
1392
|
+
grade — forçá-la a 720px é exatamente o defeito que o calendário sofreu. */
|
|
1393
|
+
.prose table { width:100%; border-collapse:collapse; font-size:var(--text-md); }
|
|
1394
|
+
.prose th,.prose td { padding:var(--space-2) var(--space-3); text-align:start; border-block-end:1px solid var(--border); }
|
|
1395
|
+
.prose th { color:var(--subtle-foreground); font-weight:var(--weight-semibold); }
|
|
1396
|
+
|
|
1397
|
+
/* LISTA ORDENÁVEL (PLANO-1.0, item L3). Duas regras aqui não são decoração e quebram
|
|
1398
|
+
comportamento se saírem:
|
|
1399
|
+
• `touch-action:none` na alça — sem ela o navegador rola a página em vez de deixar arrastar, e
|
|
1400
|
+
o componente não funciona em toque nenhum. É o par obrigatório do `setPointerCapture`;
|
|
1401
|
+
• `cursor:grab`/`grabbing`, que é a única affordance de que aquilo se arrasta.
|
|
1402
|
+
O item PEGO se marca com `data-grabbed`, e o sinal é ELEVAÇÃO e borda — não cor sozinha
|
|
1403
|
+
(WCAG 1.4.1), e não a cápsula de `.is-selected`: pego e escolhido são coisas diferentes e não
|
|
1404
|
+
podem falar a mesma língua. */
|
|
1405
|
+
.sortable-list { display:grid; gap:var(--space-2); margin:0; padding:0; list-style:none; }
|
|
1406
|
+
.sortable-item { display:flex; align-items:center; gap:var(--space-2); padding:var(--space-2) var(--space-3); border:1px solid var(--border); border-radius:var(--radius-lg); background:var(--card); }
|
|
1407
|
+
.sortable-item[data-grabbed] { border-color:var(--border-strong); box-shadow:var(--shadow-lg); }
|
|
1408
|
+
.sortable-handle { display:grid; place-items:center; min-width:var(--space-6); min-height:var(--space-6); padding:0; border:0; border-radius:var(--radius-sm); background:transparent; color:var(--muted-foreground); cursor:grab; touch-action:none; }
|
|
1409
|
+
.sortable-handle[aria-pressed="true"] { cursor:grabbing; color:var(--primary); }
|
|
1410
|
+
.sortable-label { min-width:0; }
|
|
1411
|
+
|
|
1412
|
+
/* EDITOR POR BLOCOS (PLANO-1.0, item N1). A Aurea é dona da MOLDURA; o texto rico de dentro é do
|
|
1413
|
+
consumidor — ADR-0025, e o comentário do `inputs.tsx` traz a medição inteira.
|
|
1414
|
+
Duas regras aqui são COMPORTAMENTO e não enfeite, as mesmas da lista ordenável, porque o
|
|
1415
|
+
protocolo de arrasto é o mesmo (`useReorder`):
|
|
1416
|
+
• `touch-action:none` na alça — sem ela o navegador rola a página em vez de deixar arrastar, e
|
|
1417
|
+
o componente não funciona em toque nenhum. É o par obrigatório do `setPointerCapture`;
|
|
1418
|
+
• `cursor:grab`/`grabbing`, a única affordance de que aquilo se arrasta.
|
|
1419
|
+
`min-width:0` no corpo NÃO é enfeite tampouco: sem ele um filho largo (imagem, tabela, bloco de
|
|
1420
|
+
código) estoura a coluna do grid em vez de encolher, e a página ganha rolagem lateral — que é
|
|
1421
|
+
exatamente o que o `catalog-sweep` reprova.
|
|
1422
|
+
O raio é `--radius-lg` e não `--radius-card`: bloco é LINHA de uma lista, no mesmo idioma do
|
|
1423
|
+
`.sortable-item`, e não um cartão flutuante. Sai da identidade que já existe, sem decisão de
|
|
1424
|
+
aparência nova. O bloco PEGO se marca com `data-grabbed`, e o sinal é ELEVAÇÃO e borda — não
|
|
1425
|
+
cor sozinha (WCAG 1.4.1). */
|
|
1426
|
+
.block-editor { display:grid; gap:var(--space-3); margin:0; padding:0; list-style:none; }
|
|
1427
|
+
.block-item { display:grid; grid-template-columns:auto minmax(0,1fr); gap:var(--space-3); padding:var(--space-3); border:1px solid var(--border); border-radius:var(--radius-lg); background:var(--card); }
|
|
1428
|
+
.block-item[data-grabbed] { border-color:var(--border-strong); box-shadow:var(--shadow-lg); }
|
|
1429
|
+
.block-rail { display:grid; grid-auto-rows:min-content; gap:var(--space-1); }
|
|
1430
|
+
.block-handle,.block-remove { display:grid; place-items:center; min-width:var(--space-6); min-height:var(--space-6); padding:0; border:0; border-radius:var(--radius-sm); background:transparent; color:var(--muted-foreground); }
|
|
1431
|
+
.block-handle { cursor:grab; touch-action:none; }
|
|
1432
|
+
.block-handle[aria-pressed="true"] { cursor:grabbing; color:var(--primary); }
|
|
1433
|
+
.block-body { min-width:0; }
|
|
1434
|
+
|
|
1435
|
+
/* CARROSSEL (PLANO-1.0, item L1). A pele É o motor: o encaixe, a rolagem por toque, a inércia e
|
|
1436
|
+
as setas de teclado saem de `scroll-snap` num contêiner nativo — o React só descobre em qual
|
|
1437
|
+
slide se está. Por isso estas regras não são decoração, e apagá-las quebra comportamento.
|
|
1438
|
+
`--carousel-slide` é a válvula que faz o caso `multiple` da referência sem uma prop: quantos
|
|
1439
|
+
aparecem por vez é decisão de layout da página, no idioma do `--qr-size` e do `--media-h`.
|
|
1440
|
+
`scroll-behavior:smooth` mora AQUI e não no JavaScript de propósito: `scrollBy` sem `behavior`
|
|
1441
|
+
obedece ao valor computado, e a regra global de `prefers-reduced-motion` deste arquivo já o
|
|
1442
|
+
derruba para `auto` — quem pediu menos movimento recebe menos movimento, sem código novo. */
|
|
1443
|
+
.carousel { display:grid; gap:var(--space-3); }
|
|
1444
|
+
.carousel-track { display:flex; gap:var(--space-3); overflow-x:auto; overflow-y:hidden; scroll-snap-type:inline mandatory; scroll-behavior:smooth; overscroll-behavior-inline:contain; scrollbar-width:none; }
|
|
1445
|
+
.carousel-track::-webkit-scrollbar { width:0; height:0; }
|
|
1446
|
+
/* Sem regra de foco própria: a global do topo deste arquivo já dá o anel, e ela usa
|
|
1447
|
+
`--focus-strong` — que é `--primary` no escuro e `--brand-yellow-foreground` no claro. A versão
|
|
1448
|
+
anterior desta linha fixava `--primary` nos dois e teria trocado a cor do foco num deles. */
|
|
1449
|
+
.carousel-slide { flex:0 0 var(--carousel-slide,100%); min-width:0; scroll-snap-align:start; }
|
|
1450
|
+
.carousel-controls { display:flex; align-items:center; justify-content:center; gap:var(--space-2); }
|
|
1451
|
+
.carousel-dots { display:flex; align-items:center; gap:var(--space-1); }
|
|
1452
|
+
/* 24×24 de ALVO com 8×8 de desenho: abaixo disso é reprovação do WCAG 2.2 AA (2.5.8), e uma fila
|
|
1453
|
+
de pontos é onde esse erro nasce. O ponto vem do ::before para não custar um elemento nem uma
|
|
1454
|
+
classe a mais — o alvo é o botão inteiro, o desenho é o miolo. */
|
|
1455
|
+
.carousel-dot { width:var(--space-6); height:var(--space-6); display:grid; place-items:center; padding:0; border:0; background:transparent; cursor:pointer; }
|
|
1456
|
+
.carousel-dot::before { content:""; width:var(--space-2); height:var(--space-2); border-radius:var(--radius-full); background:var(--border); }
|
|
1457
|
+
.carousel-dot[aria-current="true"]::before { background:var(--primary); }
|
|
1458
|
+
|
|
949
1459
|
/* ========================================================================== */
|
|
950
1460
|
/* 08. ÍCONES */
|
|
951
1461
|
/* ========================================================================== */
|
|
@@ -957,10 +1467,28 @@ img,svg { display:block; }
|
|
|
957
1467
|
.dialog-backdrop { position:fixed; inset:0; z-index:var(--z-modal); background:var(--overlay); backdrop-filter:blur(5px); }
|
|
958
1468
|
.dialog { position:fixed; top:50%; left:50%; transform:translate(-50%,-50%); z-index:var(--z-modal); width:min(560px,calc(100vw - 40px)); max-height:calc(100vh - 40px); overflow:auto; padding:24px; border:1px solid var(--border); border-radius:var(--radius-card); background:var(--popover); color:var(--popover-foreground); box-shadow:var(--shadow-lg); }
|
|
959
1469
|
.dialog>header,.drawer>header { display:flex; align-items:flex-start; justify-content:space-between; gap:16px; margin-bottom:16px; } .dialog-body { display:grid; gap:14px; }
|
|
1470
|
+
/* O ConfirmDialog é o MESMO `.dialog` — não nasce uma segunda superfície flutuante, que seria a
|
|
1471
|
+
pele divergindo como divergiu entre `.table-wrap` e `.table-region`. O que muda é a caixa mais
|
|
1472
|
+
estreita (uma frase e dois botões não precisam de 560px) e o rodapé alinhado ao fim, com o
|
|
1473
|
+
destrutivo por último: a ordem visual é cancelar → agir, e é onde as quatro referências batem. */
|
|
1474
|
+
.dialog-confirm { width:min(420px,calc(100vw - 40px)); }
|
|
1475
|
+
.dialog-confirm>footer { display:flex; justify-content:flex-end; gap:var(--space-2); margin-block-start:var(--space-5); }
|
|
960
1476
|
.drawer-wrap { position:fixed; inset:0; z-index:var(--z-drawer); display:none; background:var(--overlay); } .drawer-wrap.open { display:block; }
|
|
961
1477
|
.drawer-backdrop { position:fixed; inset:0; z-index:var(--z-drawer); background:var(--overlay); }
|
|
962
1478
|
.drawer { position:fixed; top:0; bottom:0; z-index:var(--z-drawer); width:min(480px,92vw); padding:24px; background:var(--surface-1); box-shadow:var(--shadow-lg); overflow:auto; } .drawer-right { right:0; border-left:1px solid var(--border); } .drawer-left { left:0; border-right:1px solid var(--border); }
|
|
963
1479
|
.toast-stack { position:fixed; inset-inline-end:22px; bottom:22px; z-index:var(--z-toast); width:min(360px,calc(100vw - 44px)); display:flex; flex-direction:column; gap:10px; } .toast { display:flex; align-items:center; gap:11px; padding:14px; border:1px solid var(--border); border-radius:var(--radius-lg); background:var(--popover); color:var(--popover-foreground); box-shadow:var(--shadow-md); transition:opacity .28s var(--ease-standard), transform .28s var(--ease-emphasized); } .toast>:first-child { flex:1; min-width:0; } .toast-text { display:flex; flex-direction:column; gap:2px; min-width:0; } .toast[data-starting-style],.toast[data-ending-style] { opacity:0; transform:translateY(12px); }
|
|
1480
|
+
/* As quatro faces do `AureaToastType`, escritas em 16/08/2026 — elas NÃO existiam, e o tipo
|
|
1481
|
+
publicado não pintava nada. O `system.tsx` emite `toast-${t.type}` desde sempre; classe por
|
|
1482
|
+
template é o ponto CEGO do check 18, que só enxerga classe literal — o mesmo defeito que o
|
|
1483
|
+
`log-${level}` já tinha tido. Varridos os 18 padrões dinâmicos do pacote React: este era o
|
|
1484
|
+
ÚNICO órfão (o `ev-${e.id}` do agents.tsx é `id`, não classe).
|
|
1485
|
+
Mecanismo REUSADO do .alert/.banner, não inventado: borda tingida + fundo semântico. Vêm
|
|
1486
|
+
DEPOIS da base `.toast` de propósito — mesma especificidade, a última regra vence, e é a
|
|
1487
|
+
armadilha que o comentário do .banner já registrava lá em cima. */
|
|
1488
|
+
.toast-info { border-color:color-mix(in srgb,var(--info-400) 38%,var(--border)); background:var(--info-bg); }
|
|
1489
|
+
.toast-success { border-color:color-mix(in srgb,var(--success-400) 38%,var(--border)); background:var(--success-bg); }
|
|
1490
|
+
.toast-warning { border-color:color-mix(in srgb,var(--warning-400) 38%,var(--border)); background:var(--warning-bg); }
|
|
1491
|
+
.toast-danger { border-color:color-mix(in srgb,var(--danger-400) 38%,var(--border)); background:var(--danger-bg); }
|
|
964
1492
|
/* CommandPaletteShell (Fase 11, achado A13). A superfície já existia aqui sob o nome
|
|
965
1493
|
`.command` — e `.command` é chrome da página de docs manual: nenhum componente React a
|
|
966
1494
|
emite. Ela passava no check 15 por ACIDENTE, porque o rótulo padrão "Type a command…" põe
|
|
@@ -977,6 +1505,18 @@ img,svg { display:block; }
|
|
|
977
1505
|
.menu-item { display:flex; align-items:center; gap:10px; width:100%; min-height:var(--control-h-md); padding:0 10px; border:0; border-radius:var(--radius-sm); background:transparent; color:var(--foreground); font:inherit; text-align:start; cursor:pointer; }
|
|
978
1506
|
.menu-item[data-highlighted],.menu-item:hover:not([data-disabled]) { background:var(--surface-3); outline:none; }
|
|
979
1507
|
.menu-item[data-disabled] { opacity:.5; cursor:not-allowed; }
|
|
1508
|
+
/* CommandPalette (M6): a lista dentro da paleta. Reusa `.menu` e `.menu-item` — a paleta nao
|
|
1509
|
+
inventa uma segunda pele de lista, que e o defeito que a `.table-region` cobrou caro. O que se
|
|
1510
|
+
acrescenta e o teto de altura (a lista rola, a caixa nao cresce sem fim) e a linha do item,
|
|
1511
|
+
que tem icone, rotulo que se estica e atalho no fim.
|
|
1512
|
+
DEPOIS do `.menu-item` de proposito: as duas regras tem a MESMA especificidade, entao quem vem
|
|
1513
|
+
por ultimo vence. Escrita antes, a grade de tres colunas era apagada pelo `display:flex` do
|
|
1514
|
+
menu — medido em 13/08/2026, e o gate de pele foi quem pegou. */
|
|
1515
|
+
.command-positioner { inline-size:var(--anchor-width); }
|
|
1516
|
+
.command-list { max-height:min(50vh,360px); overflow-y:auto; overscroll-behavior:contain; }
|
|
1517
|
+
.command-item { display:grid; grid-template-columns:auto minmax(0,1fr) auto; gap:var(--space-3); align-items:center; }
|
|
1518
|
+
.command-item-label { min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
|
|
1519
|
+
|
|
980
1520
|
.menu-sep { height:1px; margin:6px 4px; background:var(--border); border:0; }
|
|
981
1521
|
/* Combobox: reaproveita .menu/.menu-item; só dimensionamento e a coluna do check são próprios.
|
|
982
1522
|
--anchor-width e --available-height vêm do positioner do Base UI. */
|
|
@@ -993,6 +1533,10 @@ img,svg { display:block; }
|
|
|
993
1533
|
.combobox-multi:hover { border-color:var(--border); }
|
|
994
1534
|
.combobox-multi:focus-within { outline:2px solid var(--focus-strong); outline-offset:2px; border-color:var(--focus-strong); }
|
|
995
1535
|
.combobox-multi>.combobox-actions { position:static; inset-inline-end:auto; }
|
|
1536
|
+
/* o campo de chips não é `.input`, então a regra de :disabled dos campos não o alcança. Quem
|
|
1537
|
+
marca aqui é o `data-disabled` que o Base UI põe no InputGroup a partir do estado da Root. */
|
|
1538
|
+
.combobox-multi[data-disabled] { opacity:.5; cursor:not-allowed; }
|
|
1539
|
+
.combobox-multi[data-disabled] .combobox-chip-input { cursor:not-allowed; }
|
|
996
1540
|
.combobox-chips { display:flex; flex:1; flex-wrap:wrap; align-items:center; gap:var(--space-2); min-width:0; }
|
|
997
1541
|
.combobox-chip { display:inline-flex; align-items:center; gap:var(--space-1); padding-block:var(--space-1); padding-inline:var(--space-3) var(--space-1); background:var(--secondary); color:var(--foreground); border-radius:var(--radius-control); font-size:var(--text-sm); }
|
|
998
1542
|
.combobox-chip-remove { display:inline-flex; padding:0; border:0; background:transparent; color:var(--muted-foreground); border-radius:var(--radius-full); cursor:pointer; }
|
|
@@ -1371,7 +1915,7 @@ button.graph-node-body { cursor:pointer; }
|
|
|
1371
1915
|
/* Padrões compostos Aurea 1.7: blocos de operação, não widgets de domínio. */
|
|
1372
1916
|
/* Alvo de toque. `.tabs`/`.segmented` no lugar de `.tab`/`.segmented button`: agora a altura
|
|
1373
1917
|
mora na cápsula, então é ela que precisa crescer — o filho estica atrás. */
|
|
1374
|
-
@media (pointer:coarse) { .btn:not(.btn-link),.tabs,.segmented,.pagination button,.schedule-grid button,.media-control { min-height:var(--target-min); } .btn-icon,.media-control { min-width:var(--target-min); } }
|
|
1918
|
+
@media (pointer:coarse) { .btn:not(.btn-link),.tabs,.segmented,.pagination button,.schedule-grid button,.media-control,.nav-list-row { min-height:var(--target-min); } .btn-icon,.media-control { min-width:var(--target-min); } }
|
|
1375
1919
|
@media (forced-colors:active) {
|
|
1376
1920
|
:focus-visible { outline:2px solid CanvasText; outline-offset:2px; box-shadow:none; }
|
|
1377
1921
|
.badge,.pattern-gallery button[aria-pressed="true"],.device-grid button[aria-pressed="true"] { border:1px solid CanvasText; }
|
|
@@ -1380,7 +1924,9 @@ button.graph-node-body { cursor:pointer; }
|
|
|
1380
1924
|
|
|
1381
1925
|
/* Aurea v1.7 stabilization patch — visual design unchanged. */
|
|
1382
1926
|
.badge-running{background:var(--success-bg);color:var(--success-400);border-color:color-mix(in oklab,var(--success-400) 32%,transparent)}
|
|
1383
|
-
|
|
1927
|
+
/* superfície (overflow, borda, fundo, raio) mora junto com a do `.table-wrap` lá em cima — aqui
|
|
1928
|
+
fica só o que é DESTA região: ela é focável, e o anel dela é o box-shadow abaixo. */
|
|
1929
|
+
.table-region{outline:none}
|
|
1384
1930
|
.table-region:focus-visible{box-shadow:0 0 0 3px color-mix(in oklab,var(--focus) 38%,transparent)}
|
|
1385
1931
|
/* CONTAINER QUERY, e não breakpoint de viewport — Parte D, item D3 (08/08/2026).
|
|
1386
1932
|
Era `@media (max-width:400px)`, o último dos seis legados do achado M13. Trocar por um degrau
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aurea-uds/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "The Aurea UDS stylesheet and its vanilla behaviour. Import @aurea-uds/fonts/css before this.",
|
|
5
5
|
"keywords": ["aurea", "design-system", "css", "design-tokens"],
|
|
6
6
|
"license": "Apache-2.0",
|