@aurea-uds/core 0.1.0 → 0.2.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 +1138 -677
- package/dist/aurea.css.d.ts +3 -0
- package/dist/aurea.js +53 -0
- package/package.json +2 -2
package/dist/aurea.css
CHANGED
|
@@ -56,6 +56,7 @@
|
|
|
56
56
|
--z-modal:80;
|
|
57
57
|
--z-toast:100;
|
|
58
58
|
--sidebar-width:16.5rem;
|
|
59
|
+
--sidebar-rail:5.5rem;
|
|
59
60
|
--topbar-height:4rem;
|
|
60
61
|
--content-max:102.5rem;
|
|
61
62
|
--breakpoint-sm:640px;
|
|
@@ -272,681 +273,1141 @@
|
|
|
272
273
|
--card-pad:1.5rem;
|
|
273
274
|
--section-gap:2rem;
|
|
274
275
|
}
|
|
275
|
-
/* Aurea core — component styles. Built with @aurea-uds/tokens into dist/aurea.css. Load @aurea-uds/fonts/css for IBM Plex. */
|
|
276
|
-
/* ========================================================================== */
|
|
277
|
-
/* 02. RESET, ACESSIBILIDADE E BASE */
|
|
278
|
-
/* ========================================================================== */
|
|
279
|
-
* { box-sizing:border-box; }
|
|
280
|
-
html { scroll-behavior:smooth; }
|
|
281
|
-
body { margin:0; background:var(--background); color:var(--foreground); font-family:var(--font-ui); font-size:var(--text-md); line-height:var(--leading-normal); }
|
|
282
|
-
::selection { background:var(--selection); }
|
|
283
|
-
button,input,textarea,select { font:inherit; }
|
|
284
|
-
button,a,[role="button"],input,textarea,select { outline:none; }
|
|
285
|
-
:focus-visible { outline:2px solid var(--focus-strong); outline-offset:2px; box-shadow:none; border-color:var(--focus-strong)!important; }
|
|
286
|
-
button { color:inherit; }
|
|
287
|
-
a { color:var(--primary); text-underline-offset:3px; }
|
|
288
|
-
img,svg { display:block; }
|
|
289
|
-
/* O `display:none` que o browser dá pro atributo `hidden` vem da folha do UA e PERDE de
|
|
290
|
-
qualquer `display:` de classe nossa — o painel some do fluxo de acessibilidade mas
|
|
291
|
-
continua na tela. Já mordeu o popover do MediaPlayer e as abas do catálogo. Aqui a
|
|
292
|
-
regra fecha a classe inteira do bug, em vez de um remendo por componente.
|
|
293
|
-
`until-found` fica de fora: ali o `hidden` é revelável e o browser cuida. */
|
|
294
|
-
[hidden]:not([hidden="until-found"]) { display:none!important; }
|
|
295
|
-
.sr-only { position:absolute!important; width:1px!important; height:1px!important; padding:0!important; margin:-1px!important; overflow:hidden!important; clip:rect(0,0,0,0)!important; white-space:nowrap!important; border:0!important; }
|
|
296
|
-
.icon { width:var(--icon-md); height:var(--icon-md); fill:currentColor; flex:none; }
|
|
297
|
-
.icon-sm { width:var(--icon-sm); height:var(--icon-sm); } .icon-lg { width:var(--icon-lg); height:var(--icon-lg); } .icon-xl { width:var(--icon-xl); height:var(--icon-xl); }
|
|
298
|
-
.muted { color:var(--muted-foreground); }
|
|
299
|
-
.stack { display:flex; flex-direction:column; gap:var(--space-4); }
|
|
300
|
-
.cluster { display:flex; align-items:center; flex-wrap:wrap; gap:var(--space-3); }
|
|
301
|
-
/* Grid: o terceiro primitivo de layout, com o mesmo gap do .stack. A pele nasceu na Fase 11
|
|
302
|
-
(achado A13): o componente existia desde sempre e saía `display:block` — empilhava. Ninguém
|
|
303
|
-
via porque o chrome do catálogo REDEFINIA .grid, então ele parecia pronto na nossa
|
|
304
|
-
documentação e chegava sem nada no consumidor. `min(...,100%)` porque `minmax(15rem,1fr)`
|
|
305
|
-
estoura o contêiner quando o texto vai a 200% e a coluna fica maior que a caixa (lição do
|
|
306
|
-
M12); --grid-min é o botão de ajuste, no idioma de --qr-module e --demo-height. */
|
|
307
|
-
.grid { display:grid; grid-template-columns:repeat(auto-fill,minmax(min(var(--grid-min,15rem),100%),1fr)); gap:var(--space-4); }
|
|
308
|
-
|
|
309
|
-
/* ========================================================================== */
|
|
310
|
-
/* 03. APP SHELL E DOCUMENTAÇÃO */
|
|
311
|
-
/* ========================================================================== */
|
|
312
|
-
/* Regiões do AppShell — SUPERFÍCIES FLUTUANTES (decisão do Victor, 23/07/2026): topo,
|
|
313
|
-
lateral e conteúdo são caixas soltas de raio --radius-card, separadas por --space-4 e
|
|
314
|
-
descoladas da borda da janela pelo padding do shell. NADA de barra colada com linha
|
|
315
|
-
divisória: caixa retangular genérica é proibida (CLAUDE.md).
|
|
316
|
-
Arranjo escolhido: o TOPO atravessa toda a largura (grid-column 1/-1) e leva a marca;
|
|
317
|
-
a lateral começa abaixo dele. Ambos grudam na rolagem por `sticky` — o conteúdo passa
|
|
318
|
-
por baixo e as duas caixas seguem visíveis.
|
|
319
|
-
ATENÇÃO: `.app-shell` NÃO se toca. O catálogo escrito à mão (apps/docs) usa essa MESMA
|
|
320
|
-
classe como raiz, com filhos próprios (.doc-sidebar/.main-wrap) — mexer nela desloca as
|
|
321
|
-
26 telas de referência inteiras (aconteceu; o portão visual pegou). Por isso a folga
|
|
322
|
-
entre as caixas sai da MARGEM de cada filho, não de gap/padding no shell. As linhas
|
|
323
|
-
extras do grid nascem sozinhas por auto-placement. */
|
|
324
|
-
.app-shell { min-height:100vh; display:grid; grid-template-columns:var(--sidebar-width) minmax(0,1fr); background:var(--background); }
|
|
325
|
-
/* A base do topo carrega só o ARRANJO; cada variante traz a própria superfície.
|
|
326
|
-
floating = caixa solta (a que já estava) · flush = faixa encostada nas bordas com fio
|
|
327
|
-
embaixo, para o topo virar moldura da janela · pill = cápsula centrada, de cabeçalho
|
|
328
|
-
de site. Só floating e flush entram no grid do AppShell; a pill é peça avulsa (numa
|
|
329
|
-
pílula não cabem busca + densidade + tema + comando). */
|
|
330
|
-
.topbar { grid-column:1/-1; position:sticky; z-index:var(--z-sticky); display:flex; align-items:center; justify-content:space-between; gap:var(--space-4); min-height:var(--topbar-height); }
|
|
331
|
-
.topbar-floating { top:var(--space-4); margin:var(--space-4) var(--space-4) 0; padding:var(--space-2) var(--space-4); border:1px solid var(--border); border-radius:var(--radius-card); background:var(--card); color:var(--card-foreground); }
|
|
332
|
-
/* flush: encostada nas bordas. SEM fio embaixo (pedido do Victor — a referência não tem)
|
|
333
|
-
e sem superfície própria: ela se funde ao fundo e cola no conteúdo, que começa logo
|
|
334
|
-
abaixo. É o topo do nosso catálogo. */
|
|
335
|
-
.topbar-flush { top:0; padding:var(--space-2) var(--space-5); background:var(--background); }
|
|
336
|
-
.topbar-pill { top:var(--space-4); width:max-content; max-width:calc(100% - var(--space-8)); margin:var(--space-4) auto 0; justify-content:flex-start; gap:var(--space-2); padding:var(--space-2); border:1px solid var(--border); border-radius:var(--radius-control); background:var(--card); color:var(--card-foreground); }
|
|
337
|
-
/* .brand nasceu como cabeçalho VERTICAL da lateral (borda embaixo + respiro); no topo
|
|
338
|
-
ele deita, então some com a borda e a margem — o lockup em si continua o mesmo. */
|
|
339
|
-
.topbar .brand { min-height:auto; margin:0; padding:0; border:0; }
|
|
340
|
-
/* altura fixa = janela menos topo e as 3 folgas; assim a caixa parece cheia mesmo com
|
|
341
|
-
poucos itens (é o que as referências mostram) e sobra o que rolar por dentro. */
|
|
342
|
-
.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)); margin:var(--space-4); display:flex; flex-direction:column; gap:var(--space-1); padding:var(--space-4); overflow:auto; scrollbar-width:none; border:1px solid var(--sidebar-border); border-radius:var(--radius-card); background:var(--sidebar); color:var(--sidebar-foreground); }
|
|
343
|
-
/* a lateral ROLA mas nunca mostra a barra (pedido do Victor): a barra nativa risca a
|
|
344
|
-
superfície flutuante e não segue o raio de 22px. `scrollbar-width` cobre Firefox e
|
|
345
|
-
Chrome 121+; o pseudo cobre Safari e Chrome antigo. */
|
|
346
|
-
.sidebar::-webkit-scrollbar { width:0; height:0; }
|
|
347
|
-
/* com o topo `flush` a barra COLA no conteúdo (pedido do Victor): a folga acima da lateral
|
|
348
|
-
e do conteúdo é --space-1 (4px), e o --shell-top acompanha. */
|
|
349
|
-
/* flush cola no conteúdo com --space-05 (2px) — o degrau mais apertado da escala, criado
|
|
350
|
-
pra este caso (o Victor aprovou os 2px; tokenizado, não cru). */
|
|
351
|
-
.app-shell-flush .sidebar { --shell-top:calc(var(--topbar-height) + var(--space-05)); margin-block-start:var(--space-05); }
|
|
352
|
-
.app-shell-flush .content { margin-block-start:var(--space-05); }
|
|
353
|
-
.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); }
|
|
354
|
-
/* O topo é presentational — não conhece o conteúdo — então garante o mínimo universal:
|
|
355
|
-
os filhos encolhem em vez de forçar rolagem lateral. O COLAPSO de conteúdo (esconder a
|
|
356
|
-
busca, trocar a nav por menu) é do consumidor, com os utilitários responsivos abaixo. */
|
|
357
|
-
.topbar > * { min-width:0; }
|
|
358
|
-
/* QUEBRA POR CONTEÚDO. O `flex-wrap` vivia só dentro do @media abaixo, ou seja, a barra só
|
|
359
|
-
quebrava quando a JANELA era estreita. Texto a 200% (WCAG 1.4.4) aumenta o conteúdo sem
|
|
360
|
-
mexer no viewport: o @media não disparava e a nav passava 27px da borda direita, em todas
|
|
361
|
-
as páginas. Quebrar por conteúdo cobre os dois casos e não muda nada quando cabe. */
|
|
362
|
-
.topbar,.topbar > nav { flex-wrap:wrap; }
|
|
363
|
-
/* Colapso do shell abaixo de md (767 = md-1, escala): a lateral deixa de flutuar e de ter
|
|
364
|
-
altura de janela — empilha acima do conteúdo, que passa a ocupar a largura toda. Sem
|
|
365
|
-
estado (o off-canvas com botão é refino da Fase 3); o essencial é nunca rolar de lado. */
|
|
366
|
-
/* Abaixo de md o topo QUEBRA em duas linhas em vez de esconder a navegação: perder o acesso
|
|
367
|
-
a uma área inteira não é colapso responsivo, é bug. A nav vai pra segunda linha, centrada
|
|
368
|
-
e na largura toda; badge e selo (secundários) seguem escondidos pelos utilitários. */
|
|
369
|
-
@media (max-width:767px) { .app-shell { grid-template-columns:minmax(0,1fr); } .sidebar { position:static; height:auto; } .content { margin-inline-start:var(--space-4); }
|
|
370
|
-
.topbar { flex-wrap:wrap; row-gap:var(--space-2); }
|
|
371
|
-
.topbar > nav { order:3; flex-basis:100%; justify-content:center; }
|
|
372
|
-
/* e a nav quebra entre os PRÓPRIOS itens: mandar a barra pra segunda linha não bastava,
|
|
373
|
-
as cinco áreas somam 371px e estouravam a 320px (auditoria 26/07, A1). */
|
|
374
|
-
.topbar > nav { flex-wrap:wrap; row-gap:var(--space-1); } }
|
|
375
|
-
|
|
376
|
-
/* ── GAVETA DE NAVEGAÇÃO (achado A8) ───────────────────────────────────────────────
|
|
377
|
-
Empilhar a lateral acima do conteúdo passava, no gate de rolagem lateral, e era
|
|
378
|
-
inutilizável: medido em 375px, o `h1` da página começava 4,6 telas abaixo, atrás dos 65
|
|
379
|
-
itens da navegação. Abaixo de md ela sai do fluxo e vira gaveta.
|
|
380
|
-
SEM UMA LINHA DE JS: o <aside> é um popover nativo e o botão do topo é o invoker. O
|
|
381
|
-
navegador entrega Escape, clique fora para fechar, `aria-expanded` no disparador, a volta
|
|
382
|
-
do foco ao fechar e o ::backdrop. Baseline desde 04/2025; verificado em 30/07/2026.
|
|
383
|
-
Não-modal de propósito (o foco não fica preso), como o CommandPaletteShell: declarar
|
|
384
|
-
modalidade sem entregá-la é pior que não declarar. */
|
|
385
|
-
.btn.nav-toggle { display:none; }
|
|
386
|
-
/* O UA aplica um bloco inteiro a `[popover]` mesmo FECHADO — `width:fit-content`, `inset:0`,
|
|
387
|
-
`margin:auto`, borda e padding próprios. A `.sidebar` já sobrescreve quase tudo; sobravam
|
|
388
|
-
largura e insets, e isso encolheu a lateral do DESKTOP de 232px para 225px. Quem pegou foi o
|
|
389
|
-
gate de pixel, não o olho: 4.573 pixels de diferença numa captura de 0,03 de proporção. */
|
|
390
|
-
.sidebar[popover]:not(:popover-open) { width:auto; inset-inline:auto; inset-block-end:auto; }
|
|
391
|
-
/* Quem esconde a gaveta fechada é ESTA regra, não o `display:none` do UA para `[popover]`:
|
|
392
|
-
regra de autor vence regra de UA por ORIGEM, e a `.sidebar` já declara `display:flex`.
|
|
393
|
-
Medido: sem esta linha a lateral seguia empilhada em 375px, com o botão já visível.
|
|
394
|
-
O @supports é a rede: em navegador sem popover a regra inteira não aplica, a lateral volta a
|
|
395
|
-
empilhar (o comportamento de antes, feio e usável) em vez de sumir sem botão que a abra. */
|
|
396
|
-
@supports selector(:popover-open) {
|
|
397
|
-
@media (max-width:767px) {
|
|
398
|
-
.sidebar[popover]:not(:popover-open) { display:none; }
|
|
399
|
-
.btn.nav-toggle { display:inline-flex; }
|
|
400
|
-
/* altura EXPLÍCITA, não `auto`: medido em 30/07/2026, com `top` e `bottom` postos e
|
|
401
|
-
`height:auto` o Chromium resolveu pela altura do CONTEÚDO (3.498px de lateral rolando a
|
|
402
|
-
página inteira), não pelas âncoras. `dvh` porque a barra do navegador móvel some e volta. */
|
|
403
|
-
.sidebar[popover]:popover-open { position:fixed; inset-block:var(--space-4); inset-inline-start:var(--space-4); width:min(20rem,calc(100vw - var(--space-8))); height:calc(100dvh - var(--space-4) * 2); margin:0; }
|
|
404
|
-
.sidebar[popover]::backdrop { background:var(--overlay); }
|
|
405
|
-
}
|
|
406
|
-
}
|
|
407
|
-
|
|
408
|
-
/* ──
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
.
|
|
425
|
-
|
|
426
|
-
.
|
|
427
|
-
.
|
|
428
|
-
|
|
429
|
-
.
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
.
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
.
|
|
442
|
-
.
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
.
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
.
|
|
452
|
-
.
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
.
|
|
456
|
-
.
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
.
|
|
460
|
-
.
|
|
461
|
-
.
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
.
|
|
466
|
-
.
|
|
467
|
-
.
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
/*
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
.btn
|
|
477
|
-
.btn
|
|
478
|
-
.btn
|
|
479
|
-
|
|
480
|
-
.
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
.
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
/*
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
.btn
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
.btn-
|
|
496
|
-
.btn-
|
|
497
|
-
.btn-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
.btn-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
/*
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
.
|
|
514
|
-
.
|
|
515
|
-
.
|
|
516
|
-
.
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
.
|
|
520
|
-
|
|
521
|
-
.
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
.
|
|
525
|
-
.
|
|
526
|
-
|
|
527
|
-
.
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
.
|
|
533
|
-
|
|
534
|
-
.
|
|
535
|
-
.
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
.
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
.
|
|
543
|
-
.
|
|
544
|
-
.
|
|
545
|
-
|
|
546
|
-
.
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
.
|
|
565
|
-
.
|
|
566
|
-
.
|
|
567
|
-
.
|
|
568
|
-
.
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
[
|
|
572
|
-
|
|
573
|
-
/*
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
.
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
.
|
|
583
|
-
|
|
584
|
-
.
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
.
|
|
588
|
-
.
|
|
589
|
-
/*
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
.
|
|
594
|
-
.
|
|
595
|
-
.
|
|
596
|
-
.
|
|
597
|
-
.
|
|
598
|
-
.
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
.
|
|
604
|
-
.
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
.
|
|
612
|
-
.
|
|
613
|
-
.
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
.
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
.
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
.
|
|
627
|
-
.
|
|
628
|
-
.
|
|
629
|
-
.
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
.
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
.
|
|
637
|
-
.
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
.
|
|
644
|
-
.
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
.
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
.
|
|
651
|
-
.
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
.
|
|
656
|
-
.
|
|
657
|
-
.
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
.
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
.
|
|
670
|
-
.
|
|
671
|
-
.
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
.
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
.
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
.
|
|
684
|
-
|
|
685
|
-
.
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
.
|
|
691
|
-
.
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
.
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
.
|
|
700
|
-
.
|
|
701
|
-
|
|
702
|
-
.
|
|
703
|
-
.
|
|
704
|
-
.
|
|
705
|
-
/*
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
.
|
|
710
|
-
.
|
|
711
|
-
.
|
|
712
|
-
.
|
|
713
|
-
.
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
/*
|
|
719
|
-
|
|
720
|
-
.
|
|
721
|
-
.
|
|
722
|
-
|
|
723
|
-
.
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
.
|
|
734
|
-
.
|
|
735
|
-
.
|
|
736
|
-
.
|
|
737
|
-
.
|
|
738
|
-
.
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
.
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
.
|
|
748
|
-
.
|
|
749
|
-
.
|
|
750
|
-
.
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
.
|
|
754
|
-
.
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
/*
|
|
760
|
-
.
|
|
761
|
-
|
|
762
|
-
.
|
|
763
|
-
.
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
.
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
.
|
|
771
|
-
.
|
|
772
|
-
.
|
|
773
|
-
/*
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
.
|
|
784
|
-
.
|
|
785
|
-
.
|
|
786
|
-
.
|
|
787
|
-
.
|
|
788
|
-
|
|
789
|
-
.
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
.
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
.
|
|
803
|
-
.
|
|
804
|
-
.
|
|
805
|
-
.
|
|
806
|
-
.
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
.
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
.
|
|
817
|
-
.
|
|
818
|
-
.
|
|
819
|
-
.
|
|
820
|
-
.
|
|
821
|
-
.
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
.
|
|
825
|
-
.
|
|
826
|
-
.
|
|
827
|
-
.
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
.
|
|
837
|
-
.
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
.
|
|
843
|
-
.
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
.
|
|
852
|
-
.
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
.
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
.
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
.
|
|
864
|
-
.
|
|
865
|
-
.
|
|
866
|
-
.
|
|
867
|
-
.
|
|
868
|
-
/*
|
|
869
|
-
.
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
.
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
.
|
|
883
|
-
.
|
|
884
|
-
.
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
.
|
|
892
|
-
th
|
|
893
|
-
.
|
|
894
|
-
.
|
|
895
|
-
.
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
.
|
|
916
|
-
|
|
917
|
-
.media-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
}
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
.badge-
|
|
929
|
-
.
|
|
930
|
-
.
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
}
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
}
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
276
|
+
/* Aurea core — component styles. Built with @aurea-uds/tokens into dist/aurea.css. Load @aurea-uds/fonts/css for IBM Plex. */
|
|
277
|
+
/* ========================================================================== */
|
|
278
|
+
/* 02. RESET, ACESSIBILIDADE E BASE */
|
|
279
|
+
/* ========================================================================== */
|
|
280
|
+
* { box-sizing:border-box; }
|
|
281
|
+
html { scroll-behavior:smooth; }
|
|
282
|
+
body { margin:0; background:var(--background); color:var(--foreground); font-family:var(--font-ui); font-size:var(--text-md); line-height:var(--leading-normal); }
|
|
283
|
+
::selection { background:var(--selection); }
|
|
284
|
+
button,input,textarea,select { font:inherit; }
|
|
285
|
+
button,a,[role="button"],input,textarea,select { outline:none; }
|
|
286
|
+
:focus-visible { outline:2px solid var(--focus-strong); outline-offset:2px; box-shadow:none; border-color:var(--focus-strong)!important; }
|
|
287
|
+
button { color:inherit; }
|
|
288
|
+
a { color:var(--primary); text-underline-offset:3px; }
|
|
289
|
+
img,svg { display:block; }
|
|
290
|
+
/* O `display:none` que o browser dá pro atributo `hidden` vem da folha do UA e PERDE de
|
|
291
|
+
qualquer `display:` de classe nossa — o painel some do fluxo de acessibilidade mas
|
|
292
|
+
continua na tela. Já mordeu o popover do MediaPlayer e as abas do catálogo. Aqui a
|
|
293
|
+
regra fecha a classe inteira do bug, em vez de um remendo por componente.
|
|
294
|
+
`until-found` fica de fora: ali o `hidden` é revelável e o browser cuida. */
|
|
295
|
+
[hidden]:not([hidden="until-found"]) { display:none!important; }
|
|
296
|
+
.sr-only { position:absolute!important; width:1px!important; height:1px!important; padding:0!important; margin:-1px!important; overflow:hidden!important; clip:rect(0,0,0,0)!important; white-space:nowrap!important; border:0!important; }
|
|
297
|
+
.icon { width:var(--icon-md); height:var(--icon-md); fill:currentColor; flex:none; }
|
|
298
|
+
.icon-sm { width:var(--icon-sm); height:var(--icon-sm); } .icon-lg { width:var(--icon-lg); height:var(--icon-lg); } .icon-xl { width:var(--icon-xl); height:var(--icon-xl); }
|
|
299
|
+
.muted { color:var(--muted-foreground); }
|
|
300
|
+
.stack { display:flex; flex-direction:column; gap:var(--space-4); }
|
|
301
|
+
.cluster { display:flex; align-items:center; flex-wrap:wrap; gap:var(--space-3); }
|
|
302
|
+
/* Grid: o terceiro primitivo de layout, com o mesmo gap do .stack. A pele nasceu na Fase 11
|
|
303
|
+
(achado A13): o componente existia desde sempre e saía `display:block` — empilhava. Ninguém
|
|
304
|
+
via porque o chrome do catálogo REDEFINIA .grid, então ele parecia pronto na nossa
|
|
305
|
+
documentação e chegava sem nada no consumidor. `min(...,100%)` porque `minmax(15rem,1fr)`
|
|
306
|
+
estoura o contêiner quando o texto vai a 200% e a coluna fica maior que a caixa (lição do
|
|
307
|
+
M12); --grid-min é o botão de ajuste, no idioma de --qr-module e --demo-height. */
|
|
308
|
+
.grid { display:grid; grid-template-columns:repeat(auto-fill,minmax(min(var(--grid-min,15rem),100%),1fr)); gap:var(--space-4); }
|
|
309
|
+
|
|
310
|
+
/* ========================================================================== */
|
|
311
|
+
/* 03. APP SHELL E DOCUMENTAÇÃO */
|
|
312
|
+
/* ========================================================================== */
|
|
313
|
+
/* Regiões do AppShell — SUPERFÍCIES FLUTUANTES (decisão do Victor, 23/07/2026): topo,
|
|
314
|
+
lateral e conteúdo são caixas soltas de raio --radius-card, separadas por --space-4 e
|
|
315
|
+
descoladas da borda da janela pelo padding do shell. NADA de barra colada com linha
|
|
316
|
+
divisória: caixa retangular genérica é proibida (CLAUDE.md).
|
|
317
|
+
Arranjo escolhido: o TOPO atravessa toda a largura (grid-column 1/-1) e leva a marca;
|
|
318
|
+
a lateral começa abaixo dele. Ambos grudam na rolagem por `sticky` — o conteúdo passa
|
|
319
|
+
por baixo e as duas caixas seguem visíveis.
|
|
320
|
+
ATENÇÃO: `.app-shell` NÃO se toca. O catálogo escrito à mão (apps/docs) usa essa MESMA
|
|
321
|
+
classe como raiz, com filhos próprios (.doc-sidebar/.main-wrap) — mexer nela desloca as
|
|
322
|
+
26 telas de referência inteiras (aconteceu; o portão visual pegou). Por isso a folga
|
|
323
|
+
entre as caixas sai da MARGEM de cada filho, não de gap/padding no shell. As linhas
|
|
324
|
+
extras do grid nascem sozinhas por auto-placement. */
|
|
325
|
+
.app-shell { min-height:100vh; display:grid; grid-template-columns:var(--sidebar-width) minmax(0,1fr); background:var(--background); }
|
|
326
|
+
/* A base do topo carrega só o ARRANJO; cada variante traz a própria superfície.
|
|
327
|
+
floating = caixa solta (a que já estava) · flush = faixa encostada nas bordas com fio
|
|
328
|
+
embaixo, para o topo virar moldura da janela · pill = cápsula centrada, de cabeçalho
|
|
329
|
+
de site. Só floating e flush entram no grid do AppShell; a pill é peça avulsa (numa
|
|
330
|
+
pílula não cabem busca + densidade + tema + comando). */
|
|
331
|
+
.topbar { grid-column:1/-1; position:sticky; z-index:var(--z-sticky); display:flex; align-items:center; justify-content:space-between; gap:var(--space-4); min-height:var(--topbar-height); }
|
|
332
|
+
.topbar-floating { top:var(--space-4); margin:var(--space-4) var(--space-4) 0; padding:var(--space-2) var(--space-4); border:1px solid var(--border); border-radius:var(--radius-card); background:var(--card); color:var(--card-foreground); }
|
|
333
|
+
/* flush: encostada nas bordas. SEM fio embaixo (pedido do Victor — a referência não tem)
|
|
334
|
+
e sem superfície própria: ela se funde ao fundo e cola no conteúdo, que começa logo
|
|
335
|
+
abaixo. É o topo do nosso catálogo. */
|
|
336
|
+
.topbar-flush { top:0; padding:var(--space-2) var(--space-5); background:var(--background); }
|
|
337
|
+
.topbar-pill { top:var(--space-4); width:max-content; max-width:calc(100% - var(--space-8)); margin:var(--space-4) auto 0; justify-content:flex-start; gap:var(--space-2); padding:var(--space-2); border:1px solid var(--border); border-radius:var(--radius-control); background:var(--card); color:var(--card-foreground); }
|
|
338
|
+
/* .brand nasceu como cabeçalho VERTICAL da lateral (borda embaixo + respiro); no topo
|
|
339
|
+
ele deita, então some com a borda e a margem — o lockup em si continua o mesmo. */
|
|
340
|
+
.topbar .brand { min-height:auto; margin:0; padding:0; border:0; }
|
|
341
|
+
/* altura fixa = janela menos topo e as 3 folgas; assim a caixa parece cheia mesmo com
|
|
342
|
+
poucos itens (é o que as referências mostram) e sobra o que rolar por dentro. */
|
|
343
|
+
.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)); margin:var(--space-4); display:flex; flex-direction:column; gap:var(--space-1); padding:var(--space-4); overflow:auto; scrollbar-width:none; border:1px solid var(--sidebar-border); border-radius:var(--radius-card); background:var(--sidebar); color:var(--sidebar-foreground); }
|
|
344
|
+
/* a lateral ROLA mas nunca mostra a barra (pedido do Victor): a barra nativa risca a
|
|
345
|
+
superfície flutuante e não segue o raio de 22px. `scrollbar-width` cobre Firefox e
|
|
346
|
+
Chrome 121+; o pseudo cobre Safari e Chrome antigo. */
|
|
347
|
+
.sidebar::-webkit-scrollbar { width:0; height:0; }
|
|
348
|
+
/* com o topo `flush` a barra COLA no conteúdo (pedido do Victor): a folga acima da lateral
|
|
349
|
+
e do conteúdo é --space-1 (4px), e o --shell-top acompanha. */
|
|
350
|
+
/* flush cola no conteúdo com --space-05 (2px) — o degrau mais apertado da escala, criado
|
|
351
|
+
pra este caso (o Victor aprovou os 2px; tokenizado, não cru). */
|
|
352
|
+
.app-shell-flush .sidebar { --shell-top:calc(var(--topbar-height) + var(--space-05)); margin-block-start:var(--space-05); }
|
|
353
|
+
.app-shell-flush .content { margin-block-start:var(--space-05); }
|
|
354
|
+
.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
|
+
/* O topo é presentational — não conhece o conteúdo — então garante o mínimo universal:
|
|
356
|
+
os filhos encolhem em vez de forçar rolagem lateral. O COLAPSO de conteúdo (esconder a
|
|
357
|
+
busca, trocar a nav por menu) é do consumidor, com os utilitários responsivos abaixo. */
|
|
358
|
+
.topbar > * { min-width:0; }
|
|
359
|
+
/* QUEBRA POR CONTEÚDO. O `flex-wrap` vivia só dentro do @media abaixo, ou seja, a barra só
|
|
360
|
+
quebrava quando a JANELA era estreita. Texto a 200% (WCAG 1.4.4) aumenta o conteúdo sem
|
|
361
|
+
mexer no viewport: o @media não disparava e a nav passava 27px da borda direita, em todas
|
|
362
|
+
as páginas. Quebrar por conteúdo cobre os dois casos e não muda nada quando cabe. */
|
|
363
|
+
.topbar,.topbar > nav { flex-wrap:wrap; }
|
|
364
|
+
/* Colapso do shell abaixo de md (767 = md-1, escala): a lateral deixa de flutuar e de ter
|
|
365
|
+
altura de janela — empilha acima do conteúdo, que passa a ocupar a largura toda. Sem
|
|
366
|
+
estado (o off-canvas com botão é refino da Fase 3); o essencial é nunca rolar de lado. */
|
|
367
|
+
/* Abaixo de md o topo QUEBRA em duas linhas em vez de esconder a navegação: perder o acesso
|
|
368
|
+
a uma área inteira não é colapso responsivo, é bug. A nav vai pra segunda linha, centrada
|
|
369
|
+
e na largura toda; badge e selo (secundários) seguem escondidos pelos utilitários. */
|
|
370
|
+
@media (max-width:767px) { .app-shell { grid-template-columns:minmax(0,1fr); } .sidebar { position:static; height:auto; } .content { margin-inline-start:var(--space-4); }
|
|
371
|
+
.topbar { flex-wrap:wrap; row-gap:var(--space-2); }
|
|
372
|
+
.topbar > nav { order:3; flex-basis:100%; justify-content:center; }
|
|
373
|
+
/* e a nav quebra entre os PRÓPRIOS itens: mandar a barra pra segunda linha não bastava,
|
|
374
|
+
as cinco áreas somam 371px e estouravam a 320px (auditoria 26/07, A1). */
|
|
375
|
+
.topbar > nav { flex-wrap:wrap; row-gap:var(--space-1); } }
|
|
376
|
+
|
|
377
|
+
/* ── GAVETA DE NAVEGAÇÃO (achado A8) ───────────────────────────────────────────────
|
|
378
|
+
Empilhar a lateral acima do conteúdo passava, no gate de rolagem lateral, e era
|
|
379
|
+
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 md ela sai do fluxo e vira gaveta.
|
|
381
|
+
SEM UMA LINHA DE JS: o <aside> é um popover nativo e o botão do topo é o invoker. O
|
|
382
|
+
navegador entrega Escape, clique fora para fechar, `aria-expanded` no disparador, a volta
|
|
383
|
+
do foco ao fechar e o ::backdrop. Baseline desde 04/2025; verificado em 30/07/2026.
|
|
384
|
+
Não-modal de propósito (o foco não fica preso), como o CommandPaletteShell: declarar
|
|
385
|
+
modalidade sem entregá-la é pior que não declarar. */
|
|
386
|
+
.btn.nav-toggle { display:none; }
|
|
387
|
+
/* O UA aplica um bloco inteiro a `[popover]` mesmo FECHADO — `width:fit-content`, `inset:0`,
|
|
388
|
+
`margin:auto`, borda e padding próprios. A `.sidebar` já sobrescreve quase tudo; sobravam
|
|
389
|
+
largura e insets, e isso encolheu a lateral do DESKTOP de 232px para 225px. Quem pegou foi o
|
|
390
|
+
gate de pixel, não o olho: 4.573 pixels de diferença numa captura de 0,03 de proporção. */
|
|
391
|
+
.sidebar[popover]:not(:popover-open) { width:auto; inset-inline:auto; inset-block-end:auto; }
|
|
392
|
+
/* Quem esconde a gaveta fechada é ESTA regra, não o `display:none` do UA para `[popover]`:
|
|
393
|
+
regra de autor vence regra de UA por ORIGEM, e a `.sidebar` já declara `display:flex`.
|
|
394
|
+
Medido: sem esta linha a lateral seguia empilhada em 375px, com o botão já visível.
|
|
395
|
+
O @supports é a rede: em navegador sem popover a regra inteira não aplica, a lateral volta a
|
|
396
|
+
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:767px) {
|
|
399
|
+
.sidebar[popover]:not(:popover-open) { display:none; }
|
|
400
|
+
.btn.nav-toggle { display:inline-flex; }
|
|
401
|
+
/* altura EXPLÍCITA, não `auto`: medido em 30/07/2026, com `top` e `bottom` postos e
|
|
402
|
+
`height:auto` o Chromium resolveu pela altura do CONTEÚDO (3.498px de lateral rolando a
|
|
403
|
+
página inteira), não pelas âncoras. `dvh` porque a barra do navegador móvel some e volta. */
|
|
404
|
+
.sidebar[popover]:popover-open { position:fixed; inset-block:var(--space-4); inset-inline-start:var(--space-4); width:min(20rem,calc(100vw - var(--space-8))); height:calc(100dvh - var(--space-4) * 2); margin:0; }
|
|
405
|
+
.sidebar[popover]::backdrop { background:var(--overlay); }
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
/* ── A LISTA DA LATERAL (PLANO-1.0, item B1) ───────────────────────────────────────
|
|
410
|
+
Até 06/08/2026 a Sidebar era só a caixa: item, grupo, aninhamento e marca de item atual
|
|
411
|
+
existiam neste arquivo como `.doc-nav`, que é CHROME do catálogo (achado A6) e nunca foi
|
|
412
|
+
API pública. Consequência medida: nenhum consumidor conseguia desenhar uma navegação sem
|
|
413
|
+
reescrevê-la, e a peça já estava aqui.
|
|
414
|
+
ITEM ATUAL por [aria-current] e não por uma classe `.active`: o estado que o leitor de tela
|
|
415
|
+
anuncia e o que o olho vê passam a ser o MESMO atributo, então não há como marcar um e
|
|
416
|
+
esquecer o outro — que é como o `.doc-nav` carrega `.active` E `aria-current` até hoje. */
|
|
417
|
+
.sidebar-nav { display:flex; flex-direction:column; gap:var(--space-1); }
|
|
418
|
+
.sidebar-list { display:flex; flex-direction:column; gap:var(--space-1); margin:0; padding:0; list-style:none; }
|
|
419
|
+
/* `width:100%` + `font:inherit` + `border:0`: o item também é <button> quando não navega, e
|
|
420
|
+
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-control); background:transparent; color:var(--sidebar-foreground); font:inherit; font-size:var(--text-sm); text-align:start; text-decoration:none; cursor:pointer; }
|
|
422
|
+
.sidebar-item .icon { flex:none; opacity:.8; }
|
|
423
|
+
/* min-width:0 é o que deixa o ellipsis funcionar dentro do flex; sem ele o nome longo empurra
|
|
424
|
+
o acessório para fora da caixa em vez de cortar. */
|
|
425
|
+
.sidebar-label { flex:1; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
|
|
426
|
+
@media (hover:hover) and (pointer:fine) { .sidebar-item:hover { background:var(--secondary); } }
|
|
427
|
+
.sidebar-item[aria-current] { background:var(--secondary); color:var(--foreground); font-weight:var(--weight-medium); }
|
|
428
|
+
.sidebar-item[aria-current] .icon { opacity:1; }
|
|
429
|
+
.sidebar-group-label { margin:var(--space-3) var(--space-2) 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
|
+
.sidebar-sub { padding-inline-start:var(--space-5); }
|
|
431
|
+
/* RECOLHIDA — a lateral vira trilho de ícones. O rótulo NÃO some do DOM: o React troca a
|
|
432
|
+
classe dele por `.sr-only`, então quem usa leitor de tela continua ouvindo o nome. Ícone
|
|
433
|
+
sozinho não nomeia nada, e essa é a falha clássica do padrão de trilho. */
|
|
434
|
+
.sidebar-collapsed { padding-inline:var(--space-2); }
|
|
435
|
+
.sidebar-collapsed .sidebar-item { justify-content:center; padding-inline:var(--space-2); }
|
|
436
|
+
.sidebar-collapsed .sidebar-sub { padding-inline-start:0; }
|
|
437
|
+
/* A ÚNICA regra que toca `.app-shell`, e está aqui porque a coluna do grid é do shell: a
|
|
438
|
+
lateral não consegue encolher a própria coluna.
|
|
439
|
+
O `>` NÃO É ENFEITE, e quem achou isso foi o navegador em 06/08/2026. Escrita como
|
|
440
|
+
`:has(.sidebar-collapsed)`, a regra casa com QUALQUER descendente — e a página do catálogo
|
|
441
|
+
que mostra uma lateral recolhida como EXEMPLO, dentro do <main>, recolhia a lateral de
|
|
442
|
+
verdade do documento inteiro. Medido: a coluna do shell saía 88px numa página que não pediu
|
|
443
|
+
nada. Gate nenhum via, porque `sidebar.html` não tem baseline de pixel. */
|
|
444
|
+
.app-shell:has(> .sidebar-collapsed) { grid-template-columns:var(--sidebar-rail) minmax(0,1fr); }
|
|
445
|
+
|
|
446
|
+
/* ── Visibilidade responsiva — o mecanismo PADRÃO de colapsar uma barra ─────────────
|
|
447
|
+
Regra do projeto (enraizada): todo layout horizontal que aperta esconde o secundário
|
|
448
|
+
por breakpoint da escala, nunca rola de lado. Estas 4 classes são esse mecanismo; o
|
|
449
|
+
gate (check 4b) trava os breakpoints. Mobile-first: `below` some acima do ponto. */
|
|
450
|
+
@media (max-width:1023px) { .hide-below-lg { display:none !important; } }
|
|
451
|
+
@media (max-width:767px) { .hide-below-md { display:none !important; } }
|
|
452
|
+
.brand { min-height:var(--topbar-height); display:flex; align-items:center; gap:12px; padding:2px 4px 16px; margin:0 0 16px; border:0; border-bottom:1px solid var(--sidebar-border); border-radius:0; background:transparent; box-shadow:none; }
|
|
453
|
+
.brand strong { display:block; letter-spacing:var(--tracking-tight); } .brand small { display:block; color:var(--muted-foreground); font-size:11px; }
|
|
454
|
+
/* minmax(0,1fr): a trilha `auto` do grid crescia até o nome mais longo e os itens
|
|
455
|
+
VAZAVAM pela direita da caixa (colavam na borda de um lado e sobrava do outro). */
|
|
456
|
+
.doc-nav { padding:0 0 8px; display:grid; grid-template-columns:minmax(0,1fr); gap:var(--space-1); }
|
|
457
|
+
/* .nav-group-label é o rótulo de categoria SEM ser título de documento: mesma aparência do
|
|
458
|
+
h4 histórico, mas o grupo se nomeia por aria-labelledby e não entra na lista de títulos
|
|
459
|
+
do leitor de tela (senão a lateral vem antes do h1 da página). É o jeito preferido. */
|
|
460
|
+
.doc-nav h4,.doc-nav .nav-group-label { margin:16px 8px 8px; color:var(--subtle-foreground); font-size:11px; letter-spacing:var(--tracking-widest); text-transform:uppercase; }
|
|
461
|
+
.doc-nav .nav-group { display:grid; gap:var(--space-1); }
|
|
462
|
+
.doc-nav a { display:flex; align-items:center; gap:10px; min-height:40px; margin:0; padding:9px 12px; border:1px solid transparent; border-radius:var(--radius-lg); background:transparent; color:var(--muted-foreground); text-decoration:none; box-shadow:none; }
|
|
463
|
+
.doc-nav a:hover { color:var(--sidebar-accent-foreground); background:var(--sidebar-accent); border-color:var(--sidebar-border); }
|
|
464
|
+
.doc-nav a.active { border-color:var(--sidebar-border); }
|
|
465
|
+
/* o glifo da família fica na frente do rótulo e nunca encolhe o texto */
|
|
466
|
+
.doc-nav a .icon { flex:none; opacity:.8; }
|
|
467
|
+
.doc-nav a.active .icon { opacity:1; }
|
|
468
|
+
.eyebrow { display:inline-flex; align-items:center; gap:8px; color:var(--primary); font-size:12px; font-weight:var(--weight-bold); letter-spacing:var(--tracking-widest); text-transform:uppercase; }
|
|
469
|
+
/* No claro o amarelo puro como texto não alcança 4.5:1 (axe). Aprofundar com --foreground
|
|
470
|
+
mantém o matiz da marca e passa o AA; no escuro segue o amarelo cheio. */
|
|
471
|
+
[data-theme="light"] .eyebrow { color:color-mix(in oklab, var(--primary) 45%, var(--foreground)); }
|
|
472
|
+
.demo { min-width:0; padding:var(--card-pad); border:1px solid var(--border); border-radius:var(--radius-card); background:var(--card); color:var(--card-foreground); box-shadow:none; }
|
|
473
|
+
|
|
474
|
+
/* ========================================================================== */
|
|
475
|
+
/* 04. COMPONENTES PRIMITIVOS */
|
|
476
|
+
/* ========================================================================== */
|
|
477
|
+
.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
|
+
.btn:disabled { opacity:.45; cursor:not-allowed; }
|
|
480
|
+
/* Cinco alturas, todas de token de densidade (autorizado pelo Victor em 24/07/2026). O raio
|
|
481
|
+
é pill em TODAS — o tamanho muda altura e respiro, nunca a forma. Em ponteiro grosso o
|
|
482
|
+
mínimo de 44px lá embaixo pega o xs e o sm, então alvo pequeno não vira alvo impossível. */
|
|
483
|
+
.btn-xs { height:var(--control-h-xs); padding:0 10px; gap:6px; font-size:var(--text-xs); border-radius:var(--radius-control); }
|
|
484
|
+
.btn-sm { height:var(--control-h-sm); padding:0 12px; font-size:var(--text-xs); border-radius:var(--radius-control); }
|
|
485
|
+
.btn-lg { height:var(--control-h-lg); padding:0 20px; font-size:var(--text-base); border-radius:var(--radius-control); }
|
|
486
|
+
.btn-xl { height:var(--control-h-xl); padding:0 26px; gap:10px; font-size:var(--text-base); border-radius:var(--radius-control); }
|
|
487
|
+
/* Largura total: para rodapé de formulário e tela estreita. Só a largura muda. */
|
|
488
|
+
.btn-block { width:100%; }
|
|
489
|
+
.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; }
|
|
491
|
+
.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; }
|
|
493
|
+
.btn-outline { --btn-bg:transparent; --btn-fg:var(--foreground); --btn-border:var(--border-strong); }
|
|
494
|
+
.btn-outline:hover:not(:disabled) { --btn-bg:var(--accent); }
|
|
495
|
+
.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); }
|
|
497
|
+
.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); }
|
|
499
|
+
/* Destrutivo em OUTRAS hierarquias: apagar não é sempre a ação principal da tela. O tom é o
|
|
500
|
+
mesmo (--destructive); o que muda é o peso. No claro o vermelho puro como texto não chega
|
|
501
|
+
a 4.5:1, então aprofunda com --foreground — mesmo tratamento do amarelo. */
|
|
502
|
+
.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); }
|
|
504
|
+
.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); }
|
|
506
|
+
[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
|
+
/* LINK: aparência de texto, sem cápsula e sem altura de controle — o sublinhado é o que diz
|
|
508
|
+
que é clicável, então ele existe desde o repouso (revelar no hover esconde a affordance).
|
|
509
|
+
Três tons: neutro, marca e destrutivo. No claro a marca aprofunda, senão não passa o AA. */
|
|
510
|
+
/* `min-height:24px` = WCAG 2.2 SC 2.5.8. A exceção de "alvo em linha" NÃO cobre este caso:
|
|
511
|
+
medido no catálogo, o "Learn more" é ação isolada dentro de um flex, não link no meio de
|
|
512
|
+
prosa, e media 67×13px. O texto continua onde está — a caixa cresce centrada nele. */
|
|
513
|
+
.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; }
|
|
515
|
+
.btn-link-primary { --btn-fg:var(--primary); }
|
|
516
|
+
[data-theme="light"] .btn-link-primary { --btn-fg:color-mix(in oklab, var(--primary) 45%, var(--foreground)); }
|
|
517
|
+
.btn-link-danger { --btn-fg:var(--destructive); }
|
|
518
|
+
/* pressed entra na linguagem única de "selecionado" acima; só-ícone não leva o fio. */
|
|
519
|
+
/* Kbd — a tecla que dispara a ação, dentro ou fora do botão. Peça própria, não decoração. */
|
|
520
|
+
.kbd { display:inline-flex; align-items:center; justify-content:center; gap:2px; min-width:20px; height:20px; padding:0 6px; border:1px solid var(--border-strong); border-radius:var(--radius-sm); background:var(--secondary); color:var(--muted-foreground); font:500 11px/1 var(--font-code); }
|
|
521
|
+
/* dentro do botão a tecla herda a cor do rótulo, SEM opacidade: a 85% o contraste caía
|
|
522
|
+
para 3.67:1 (axe) e o atalho é informação, não enfeite. O fundo já o separa. */
|
|
523
|
+
/* Dentro do botão a tecla NÃO ganha fundo próprio: qualquer tinta escurece a cápsula e o
|
|
524
|
+
rótulo perde contraste (no primary claro caiu a 3.55:1). Sem fundo, o contraste da
|
|
525
|
+
tecla é o MESMO do rótulo — se o botão passa, ela passa. A borda de currentColor separa. */
|
|
526
|
+
.btn .kbd { background:transparent; border-color:color-mix(in oklab, currentColor 35%, transparent); color:inherit; }
|
|
527
|
+
|
|
528
|
+
/* nav: item de navegação. Normal = ghost silencioso; o ATIVO (aria-current) ganha texto
|
|
529
|
+
amarelo + traço embaixo — SEM borda (pedido do Victor). aria-current, não uma variante
|
|
530
|
+
"ativa", porque ele também anuncia "página atual" pro leitor de tela. O traço é
|
|
531
|
+
::after, pill, alinhado ao padding do texto; posição pelo eixo bloco (não espelha RTL). */
|
|
532
|
+
.btn-nav { --btn-bg:transparent; --btn-fg:var(--muted-foreground); --btn-border:transparent; }
|
|
533
|
+
.btn-nav:hover:not(:disabled) { --btn-fg:var(--foreground); }
|
|
534
|
+
.btn-icon { width:var(--control-h-md); padding:0; border-radius:var(--radius-md); }
|
|
535
|
+
.btn-icon.btn-xs { width:var(--control-h-xs); border-radius:var(--radius-sm); }
|
|
536
|
+
.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); }
|
|
537
|
+
/* xs e xl também precisam de largura: sem isto a altura mudava e a largura ficava na do
|
|
538
|
+
md, e o alvo só-ícone deixava de ser quadrado (achado na onda Actions, 24/07). */
|
|
539
|
+
.btn-icon.btn-xl { width:var(--control-h-xl); border-radius:var(--radius-md); }
|
|
540
|
+
/* wrap, não estoura: os botões do grupo são pills com gap (não segmentos colados), então
|
|
541
|
+
quebrar linha em tela estreita preserva o visual. Único gap de eixo horizontal que a
|
|
542
|
+
auditoria da Etapa 3 achou sem proteção — o resto já rola (tabela) ou já quebra (toolbar). */
|
|
543
|
+
.btn-group { display:inline-flex; flex-wrap:wrap; align-items:center; gap:4px; } .btn-group .btn { border-radius:var(--radius-control); margin-inline-start:0; }
|
|
544
|
+
.btn-group .btn:first-child { margin-inline-start:0; border-radius:var(--radius-control); }
|
|
545
|
+
.btn-group .btn:last-child { border-radius:var(--radius-control); }
|
|
546
|
+
/* Toolbar: contêiner de controles; o roving tabindex vem do Base UI. */
|
|
547
|
+
.toolbar { display:flex; align-items:center; flex-wrap:wrap; gap:6px; padding:6px; border:1px solid var(--border); border-radius:var(--radius-control); background:var(--secondary); }
|
|
548
|
+
/* Vertical sai da pílula: --radius-control (999px) clampa em 50% da menor
|
|
549
|
+
dimensão e vira um oval num container alto. */
|
|
550
|
+
.toolbar[data-orientation="vertical"] { flex-direction:column; align-items:stretch; border-radius:var(--radius-lg); }
|
|
551
|
+
.toolbar-group { display:flex; align-items:center; gap:var(--space-1); }
|
|
552
|
+
.toolbar-sep { flex:none; align-self:stretch; inline-size:1px; min-block-size:20px; margin-inline:4px; margin-block:0; background:var(--border); border:0; }
|
|
553
|
+
.toolbar[data-orientation="vertical"] .toolbar-sep { align-self:auto; inline-size:auto; block-size:1px; min-block-size:0; margin-inline:0; margin-block:4px; }
|
|
554
|
+
/* Spinner: o glifo existia desde sempre e só era usado por dentro do Button. Ganhou componente
|
|
555
|
+
e escala no Lote 1, e o eixo passou a ser o dos ÍCONES — mas o tamanho base continua o que
|
|
556
|
+
sempre foi: `--icon-sm` são os mesmos 16px de antes, então nenhum botão em nenhuma página
|
|
557
|
+
mudou de aparência. Cheguei a apontar o base para `--icon-md` (20px) e os baselines de pixel
|
|
558
|
+
acusaram na hora: teria trocado o tamanho do spinner dentro de todo botão sem ninguém pedir.
|
|
559
|
+
A casa do spinner é dentro de um controle, ao lado de texto; os degraus maiores existem para
|
|
560
|
+
quando ele é a única coisa na tela. */
|
|
561
|
+
.spinner { width:var(--icon-sm); height:var(--icon-sm); border:2px solid currentColor; border-right-color:transparent; border-radius:50%; animation:spin .7s linear infinite; }
|
|
562
|
+
.spinner-md { width:var(--icon-md); height:var(--icon-md); }
|
|
563
|
+
.spinner-lg { width:var(--icon-lg); height:var(--icon-lg); } @keyframes spin { to { transform:rotate(360deg); } }
|
|
564
|
+
|
|
565
|
+
.field { display:flex; flex-direction:column; gap:7px; }
|
|
566
|
+
.label { display:flex; justify-content:space-between; gap:12px; color:var(--foreground); font-size:var(--text-sm); font-weight:var(--weight-semibold); }
|
|
567
|
+
.hint { color:var(--muted-foreground); font-size:var(--text-xs); }
|
|
568
|
+
.input,.textarea,.select { width:100%; min-width:0; height:var(--control-h-md); padding:0 13px; color:var(--foreground); background:var(--field-bg); border:1px solid var(--border-strong); border-radius:var(--radius-control); transition:border-color var(--duration-fast),background-color var(--duration-fast),box-shadow var(--duration-fast); }
|
|
569
|
+
.input:hover,.textarea:hover,.select:hover { border-color:var(--border); }
|
|
570
|
+
.input::placeholder,.textarea::placeholder { color:var(--subtle-foreground); }
|
|
571
|
+
.input:disabled,.textarea:disabled,.select:disabled { opacity:.5; cursor:not-allowed; }
|
|
572
|
+
.input[aria-invalid="true"],.textarea[aria-invalid="true"] { border-color:var(--danger-400); }
|
|
573
|
+
.textarea { min-height:104px; padding:12px 14px; resize:vertical; border-radius:var(--radius-lg); }
|
|
574
|
+
/* chevron: cor = --muted-foreground de cada tema (dark #a1a1a1, light #505050); tokenizar na Fase 1 */
|
|
575
|
+
.select { appearance:none; padding-inline-end:38px; background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1.5l5 5 5-5' fill='none' stroke='%23a1a1a1' stroke-width='1.6'/%3E%3C/svg%3E"); background-position:calc(100% - 12px) 50%; background-size:12px 8px; background-repeat:no-repeat; }
|
|
576
|
+
[data-theme="light"] .select { background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1.5l5 5 5-5' fill='none' stroke='%23505050' stroke-width='1.6'/%3E%3C/svg%3E"); }
|
|
577
|
+
/* Widgets nativos dos campos (calendário do type=date, dropdown do select, scrollbar do textarea) seguem o tema */
|
|
578
|
+
.input,.textarea,.select { color-scheme:dark; }
|
|
579
|
+
[data-theme="light"] .input,[data-theme="light"] .textarea,[data-theme="light"] .select { color-scheme:light; }
|
|
580
|
+
/* NumberField (Lote 1): menos · campo · mais numa peça só, que é onde as referências convergem.
|
|
581
|
+
O campo é `.input` — a mesma altura, o mesmo raio, o mesmo foco de qualquer campo do sistema.
|
|
582
|
+
Largura em token para caber de 3 a 4 dígitos sem virar caixa de texto. */
|
|
583
|
+
.number-field { display:inline-flex; }
|
|
584
|
+
.number-field-group { display:inline-flex; align-items:center; gap:var(--space-1); }
|
|
585
|
+
.number-field-input { width:var(--space-16); text-align:center; }
|
|
586
|
+
/* OTPField (Lote 1): um quadrado por dígito. O lado é a altura de controle, então a fila mede o
|
|
587
|
+
mesmo que qualquer campo ao lado dela; o padding zera porque o dígito é centralizado. */
|
|
588
|
+
.otp-field { display:inline-flex; align-items:center; gap:var(--space-2); }
|
|
589
|
+
.otp-slot { width:var(--control-h-md); padding:0; text-align:center; font-family:var(--font-code); }
|
|
590
|
+
/* HoverCard (Lote 1) reusa `.popover`; só limita a largura, porque prévia é conteúdo, não rótulo. */
|
|
591
|
+
.hover-card { max-width:min(20rem,calc(100vw - var(--space-8))); }
|
|
592
|
+
.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; }
|
|
593
|
+
.field-error { color:var(--danger-400); font-size:var(--text-xs); }
|
|
594
|
+
.checkbox,.radio { display:inline-flex; align-items:flex-start; gap:9px; color:var(--foreground); cursor:pointer; }
|
|
595
|
+
.checkbox input,.radio input { position:absolute; opacity:0; pointer-events:none; }
|
|
596
|
+
.control-mark { width:18px; height:18px; margin-top:1px; display:grid; place-items:center; border:1px solid var(--border-strong); background:var(--field-bg); }
|
|
597
|
+
.checkbox .control-mark { border-radius:5px; } .radio .control-mark { border-radius:50%; }
|
|
598
|
+
.checkbox input:checked + .control-mark { background:var(--control-selected); border-color:var(--control-selected); color:var(--control-selected-foreground); }
|
|
599
|
+
.checkbox input:focus-visible + .control-mark,.radio input:focus-visible + .control-mark,.switch input:focus-visible + .switch-track { outline:2px solid var(--focus-strong); outline-offset:2px; }
|
|
600
|
+
.checkbox input:checked + .control-mark:after { content:""; width:8px; height:4px; border-left:2px solid currentColor; border-bottom:2px solid currentColor; transform:rotate(-45deg) translateY(-1px); }
|
|
601
|
+
.radio input:checked + .control-mark:after { content:""; width:8px; height:8px; border-radius:50%; background:var(--control-selected); }
|
|
602
|
+
.switch { display:inline-flex; align-items:center; gap:10px; cursor:pointer; } .switch input { position:absolute; opacity:0; }
|
|
603
|
+
.switch-track { position:relative; width:42px; height:24px; border-radius:999px; background:var(--muted); border:1px solid var(--border); transition:background var(--duration-base) var(--ease-standard),border-color var(--duration-base) var(--ease-standard); }
|
|
604
|
+
.switch-track:after { content:""; position:absolute; inset-inline-start:3px; top:3px; width:16px; height:16px; border-radius:50%; background:var(--muted-foreground); transition:transform var(--duration-base) var(--ease-standard),background var(--duration-base) var(--ease-standard); }
|
|
605
|
+
.switch input:checked + .switch-track { background:var(--control-selected); border-color:var(--control-selected); } .switch input:checked + .switch-track:after { transform:translateX(18px); background:var(--control-selected-foreground); }
|
|
606
|
+
.range { width:100%; accent-color:var(--primary); }
|
|
607
|
+
/* A CÁPSULA é que tem de medir igual a um botão/campo ao lado — não o botão de dentro.
|
|
608
|
+
Antes: `height:30px` no filho, cru. Em comfortable dava 30+3+3=36 = --control-h-md por
|
|
609
|
+
coincidência; em compact a cápsula ficava 4px alta demais e em spacious 4px baixa demais,
|
|
610
|
+
porque a densidade não alcançava a regra (achado A1, 26/07/2026). Com a altura na cápsula
|
|
611
|
+
os filhos esticam sozinhos (align-items é stretch por padrão) e a conta fecha nas TRÊS. */
|
|
612
|
+
.segmented { display:inline-flex; gap:3px; padding:3px; min-height:var(--control-h-md); border-radius:var(--radius-control); background:var(--muted); border:1px solid transparent; box-shadow:none; }
|
|
613
|
+
.segmented button { padding:0 14px; border:0; border-radius:var(--radius-control); color:var(--muted-foreground); background:transparent; cursor:pointer; }
|
|
614
|
+
.segmented button.active { color:var(--foreground); background:var(--secondary); box-shadow:none; border:0; }
|
|
615
|
+
/* Toggle (Lote 1): pressionado usa o MESMO par de tokens do segmento aceso, logo acima. Um
|
|
616
|
+
toggle solto e um segmento selecionado dizem a mesma coisa — se parecessem diferentes, o
|
|
617
|
+
sistema teria dois idiomas para "isto está ligado". O estado vem do motor (`data-pressed`),
|
|
618
|
+
não de uma classe que alguém precisa lembrar de pôr. */
|
|
619
|
+
.toggle[data-pressed] { --btn-bg:var(--secondary); --btn-fg:var(--primary); }
|
|
620
|
+
/* ── SELECIONADO: uma linguagem só ────────────────────────────────────────────────────
|
|
621
|
+
Item de nav ativo, aba ativa, item de lista lateral e botão alternado ligado mostram o
|
|
622
|
+
MESMO sinal: cápsula elevada + rótulo na cor da marca + fio amarelo encostado na borda de
|
|
623
|
+
baixo. Um usuário aprende uma vez e reconhece em todo lugar (pedido do Victor, 24/07).
|
|
624
|
+
O fio é 2px (fração de pixel borra), inset 15px, e o amarelo entra a 75% — cheio ele
|
|
625
|
+
grita mais que o próprio rótulo. No tema CLARO o amarelo puro como texto dá 1.6:1, longe
|
|
626
|
+
dos 4.5:1 do AA, então o rótulo aprofunda com --foreground e segue âmbar. */
|
|
627
|
+
.is-selected,.btn-nav[aria-current]:not([aria-current="false"]),.btn[aria-pressed="true"],
|
|
628
|
+
.segmented button.active,.doc-nav a.active { --btn-bg:var(--secondary); --btn-fg:var(--primary); position:relative; background:var(--secondary); color:var(--primary); }
|
|
629
|
+
.is-selected::after,.btn-nav[aria-current]:not([aria-current="false"])::after,
|
|
630
|
+
.btn[aria-pressed="true"]:not(.btn-icon)::after,.segmented button.active::after,
|
|
631
|
+
.doc-nav a.active::after { content:""; position:absolute; inset-inline:15px; bottom:0; height:2px; border-radius:var(--radius-full); background:color-mix(in oklab, var(--primary) 75%, transparent); }
|
|
632
|
+
[data-theme="light"] .is-selected,[data-theme="light"] .btn-nav[aria-current]:not([aria-current="false"]),
|
|
633
|
+
[data-theme="light"] .btn[aria-pressed="true"],[data-theme="light"] .segmented button.active,
|
|
634
|
+
[data-theme="light"] .doc-nav a.active { --btn-fg:color-mix(in oklab, var(--primary) 45%, var(--foreground)); color:color-mix(in oklab, var(--primary) 45%, var(--foreground)); }
|
|
635
|
+
|
|
636
|
+
/* `line-height:1` não é enfeite: sem ele o .badge herdava a entrelinha do pai e a MESMA
|
|
637
|
+
classe media 26px dentro do .brand (lh 18px) e 22px dentro de um .btn-nav (lh 12px) — na
|
|
638
|
+
mesma barra (achado A2, 26/07/2026). Geometria de componente não se decide pelo contexto.
|
|
639
|
+
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; }
|
|
641
|
+
/* O glifo do badge tem tamanho PRÓPRIO, senão o `.icon` padrão (20px) estoura os 24px e o
|
|
642
|
+
badge com ícone fica mais alto que o badge de texto na mesma fila. 16px + 3+3 de padding
|
|
643
|
+
+ 1+1 de borda = 24px exatos: com ícone ou sem, mede o mesmo. */
|
|
644
|
+
.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); }
|
|
646
|
+
|
|
647
|
+
.badge-success { color:var(--success-400); background:var(--success-bg); border-color:color-mix(in srgb,var(--success-400) 35%,var(--border)); }
|
|
648
|
+
.badge-warning { color:var(--warning-400); background:var(--warning-bg); border-color:color-mix(in srgb,var(--warning-400) 35%,var(--border)); }
|
|
649
|
+
.badge-danger { color:var(--danger-400); background:var(--danger-bg); border-color:color-mix(in srgb,var(--danger-400) 35%,var(--border)); }
|
|
650
|
+
.badge-info,.badge-running { color:var(--info-400); background:var(--info-bg); border-color:color-mix(in srgb,var(--info-400) 35%,var(--border)); }
|
|
651
|
+
.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
|
+
/* Status: condição operacional (DIRECTION §3.6) — NÃO é o pill do Badge; é ponto + rótulo
|
|
653
|
+
solto na linha. A variante pinta o .status (o ponto herda por currentColor) e o rótulo
|
|
654
|
+
volta pra --foreground, senão a frase inteira fica colorida e ilegível no claro. Os
|
|
655
|
+
nomes operacionais e os semânticos apontam pro mesmo par de tokens de propósito. */
|
|
656
|
+
.status { display:inline-flex; align-items:center; gap:var(--space-2); color:var(--muted-foreground); font-size:var(--text-sm); }
|
|
657
|
+
.status-label { color:var(--foreground); }
|
|
658
|
+
.status-online,.status-success { color:var(--success-400); }
|
|
659
|
+
.status-away,.status-warning { color:var(--warning-400); }
|
|
660
|
+
.status-busy,.status-danger { color:var(--danger-400); }
|
|
661
|
+
.status-running,.status-info { color:var(--info-400); }
|
|
662
|
+
/* offline é o único que NÃO é "mais uma cor": --subtle-foreground e --muted-foreground
|
|
663
|
+
colapsam no MESMO valor no tema claro, então offline sairia idêntico ao neutral. Ponto
|
|
664
|
+
VAZADO resolve nos dois temas e ainda diz a coisa certa (ausência, não outro estado).
|
|
665
|
+
Escopado em .status-offline: o .status-dot dentro do Badge (Chat) segue preenchido. */
|
|
666
|
+
.status-offline { color:var(--subtle-foreground); }
|
|
667
|
+
.status-offline .status-dot { background:transparent; box-shadow:inset 0 0 0 2px currentColor,0 0 0 3px color-mix(in srgb,currentColor 14%,transparent); }
|
|
668
|
+
|
|
669
|
+
.card { min-width:0; padding:var(--card-pad); border:1px solid var(--border); border-radius:var(--radius-card); background:var(--card); color:var(--card-foreground); box-shadow:none; }
|
|
670
|
+
.card-raised { box-shadow:none; } .card-inset { background:var(--secondary); }
|
|
671
|
+
.card-interactive { cursor:pointer; transition:transform var(--duration-fast),border-color var(--duration-fast),background var(--duration-fast); }
|
|
672
|
+
.card-interactive:hover { transform:translateY(-2px); border-color:var(--border-strong); background:var(--surface-2); }
|
|
673
|
+
.card-selected { border-color:var(--primary-outline); box-shadow:inset 0 0 0 1px var(--primary-outline); }
|
|
674
|
+
.card-danger { border-color:color-mix(in srgb,var(--danger-400) 50%,var(--border)); background:var(--danger-bg); }
|
|
675
|
+
.card-header { display:flex; align-items:flex-start; justify-content:space-between; gap:16px; margin-bottom:16px; }
|
|
676
|
+
.card-title { margin:0; font-size:var(--text-base); } .card-subtitle { margin:3px 0 0; color:var(--muted-foreground); font-size:var(--text-sm); }
|
|
677
|
+
|
|
678
|
+
.alert { display:grid; grid-template-columns:auto minmax(0,1fr) auto; align-items:start; gap:12px; padding:14px 16px; border:1px solid var(--border); border-radius:var(--radius-lg); background:var(--secondary); box-shadow:none; }
|
|
679
|
+
/* Banner: aviso de largura de página. Base ANTES das variantes — mesma
|
|
680
|
+
especificidade, então a última regra vence e a base apagaria as cores. */
|
|
681
|
+
.banner { display:grid; grid-template-columns:auto minmax(0,1fr) auto; align-items:center; gap:12px; padding:14px 18px; border:1px solid var(--border); border-radius:var(--radius-card); background:var(--secondary); }
|
|
682
|
+
.alert-info,.banner-info { border-color:color-mix(in srgb,var(--info-400) 38%,var(--border)); background:var(--info-bg); }
|
|
683
|
+
.alert-success,.banner-success { border-color:color-mix(in srgb,var(--success-400) 38%,var(--border)); background:var(--success-bg); }
|
|
684
|
+
.alert-warning,.banner-warning { border-color:color-mix(in srgb,var(--warning-400) 38%,var(--border)); background:var(--warning-bg); }
|
|
685
|
+
.alert-danger,.banner-danger { border-color:color-mix(in srgb,var(--danger-400) 38%,var(--border)); background:var(--danger-bg); }
|
|
686
|
+
.alert h4 { margin:0 0 2px; font-size:var(--text-sm); } .alert p { margin:0; color:var(--muted-foreground); font-size:var(--text-sm); }
|
|
687
|
+
.banner strong { font-size:var(--text-sm); } .banner p { margin:0; color:var(--muted-foreground); font-size:var(--text-sm); }
|
|
688
|
+
@media (max-width:640px) { .banner { grid-template-columns:auto minmax(0,1fr); } .banner>:last-child { grid-column:2; justify-self:start; } }
|
|
689
|
+
.progress { height:8px; overflow:hidden; border-radius:999px; background:var(--surface-3); }
|
|
690
|
+
.progress>span { display:block; height:100%; border-radius:inherit; background:var(--primary); }
|
|
691
|
+
.skeleton { position:relative; overflow:hidden; border-radius:7px; background:var(--surface-3); }
|
|
692
|
+
.skeleton { animation:skeleton-pulse 1.35s ease-in-out infinite; } @keyframes skeleton-pulse { 50% { opacity:.55; } }
|
|
693
|
+
|
|
694
|
+
/* Mesma regra da .segmented: a cápsula carrega a altura da densidade, o filho estica. */
|
|
695
|
+
.tabs { display:inline-flex; gap:3px; padding:3px; min-height:var(--control-h-md); border:0; border-radius:var(--radius-control); background:var(--muted); }
|
|
696
|
+
.tab { position:relative; padding:0 14px; border:0; border-radius:var(--radius-control); background:transparent; color:var(--muted-foreground); cursor:pointer; }
|
|
697
|
+
.tab.active,.tab[data-active] { color:var(--foreground); background:var(--secondary); } .tab.active:after,.tab[data-active]:after { display:none; }
|
|
698
|
+
.breadcrumb { display:flex; align-items:center; flex-wrap:wrap; gap:7px; color:var(--muted-foreground); font-size:var(--text-sm); }
|
|
699
|
+
.breadcrumb a { color:inherit; text-decoration:none; } .breadcrumb a:hover { color:var(--foreground); }
|
|
700
|
+
.pagination { display:flex; gap:6px; align-items:center; }
|
|
701
|
+
/* `:not(.btn)` porque isto é o quadrado de NÚMERO de página. Sem o :not, o seletor de
|
|
702
|
+
elemento vencia o `.btn-sm` do Anterior/Próxima por especificidade (0,1,1 contra 0,1,0)
|
|
703
|
+
e forçava 34px neles — era a terceira altura de .btn-sm que a auditoria mediu (B3). */
|
|
704
|
+
.pagination button:not(.btn) { width:var(--control-h-md); height:var(--control-h-md); border:1px solid var(--border); border-radius:var(--radius-control); background:transparent; color:var(--muted-foreground); cursor:pointer; }
|
|
705
|
+
.pagination button.active { color:var(--primary-foreground); background:var(--primary); border-color:var(--primary); }
|
|
706
|
+
/* Stepper (Lote 2): o container NUNCA existiu no core — só no docs.css, e lá era
|
|
707
|
+
`repeat(4,1fr)`, com o número de etapas cravado. `grid-auto-flow:column` distribui N etapas
|
|
708
|
+
iguais sem saber quanto é N, e colunas iguais são o que faz a linha conectora (`.step:before`,
|
|
709
|
+
ancorada em -50%) cair no lugar certo. */
|
|
710
|
+
.stepper { display:grid; grid-auto-flow:column; grid-auto-columns:1fr; }
|
|
711
|
+
.step { position:relative; text-align:center; color:var(--subtle-foreground); }
|
|
712
|
+
/* O ponto também morava só no docs.css, como o container. Os estados dele (`.step.done`,
|
|
713
|
+
`.step.active`) estavam no core e miravam uma regra que não existia aqui — meia peça. */
|
|
714
|
+
.step-dot { position:relative; z-index:1; width:32px; height:32px; margin:0 auto var(--space-2); display:grid; place-items:center; border-radius:50%; border:1px solid var(--border-strong); background:var(--surface-1); }
|
|
715
|
+
/* Erro é o estado que faltava: uma etapa que reprovou na validação não tinha como se mostrar.
|
|
716
|
+
Cor semântica, não a primária — reprovar não é progredir. */
|
|
717
|
+
.step-error { color:var(--danger-400); } .step-error .step-dot { color:var(--danger-400); border-color:var(--danger-400); }
|
|
718
|
+
.step-optional { display:block; color:var(--muted-foreground); font-size:var(--text-xs); }
|
|
719
|
+
/* Etapa clicável (fluxo não-linear): o alvo é um botão de verdade, com o foco do sistema. */
|
|
720
|
+
.step-trigger { display:contents; border:0; padding:0; background:none; color:inherit; font:inherit; cursor:pointer; }
|
|
721
|
+
.step:before { content:""; position:absolute; top:15px; inset-inline-start:-50%; width:100%; height:2px; background:var(--border); } .step:first-child:before { display:none; }
|
|
722
|
+
.step.done,.step.active { color:var(--foreground); } .step.done:before,.step.active:before { background:var(--primary); } .step.done .step-dot { color:var(--primary-foreground); background:var(--primary); border-color:var(--primary); } .step.active .step-dot { border-color:var(--primary); box-shadow:0 0 0 4px color-mix(in srgb,var(--primary) 14%,transparent); }
|
|
723
|
+
|
|
724
|
+
/* TABELA DE DADOS — e só ela. Estas cinco regras eram de ELEMENTO (`table`, `th,td`, `th`,
|
|
725
|
+
`tr:last-child td`, `tbody tr:hover`) e valiam para toda <table> do documento, inclusive a de
|
|
726
|
+
um consumidor. Nenhum gate as via: os checks 15 e 18 comparam CLASSE, e seletor de elemento
|
|
727
|
+
não tem nenhuma. O Lote 4 pagou a conta — a grade do calendário nasceu com `min-width:720px`
|
|
728
|
+
e o cabeçalho de dias herdou a faixa cinza, a caixa alta e o espaçamento de letra de um
|
|
729
|
+
cabeçalho de tabela de dados. Medido no navegador em 01 e 02/08/2026.
|
|
730
|
+
Escopar é seguro e foi medido antes: `Table` embrulha em `.table-region` e emite `.table` na
|
|
731
|
+
tabela; `DataGrid` embrulha em `.table-wrap`; e as 3 tabelas da página legada estão todas
|
|
732
|
+
dentro de `.table-wrap`. Nenhuma <table> do repositório fica descoberta — o gate de pixel é
|
|
733
|
+
quem prova, e ele tem de acusar ZERO mudança nas superfícies de tabela. */
|
|
734
|
+
.table-wrap { overflow:auto; border:1px solid var(--border); border-radius:var(--radius-card); background:var(--card); }
|
|
735
|
+
.table,.table-wrap table { width:100%; border-collapse:collapse; min-width:720px; }
|
|
736
|
+
.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
|
+
.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; }
|
|
738
|
+
.table tr:last-child td,.table-wrap tr:last-child td { border-bottom:0; }
|
|
739
|
+
.table tbody tr:hover,.table-wrap tbody tr:hover { background:var(--surface-2); }
|
|
740
|
+
/* Avatar (Lote 2): `overflow:hidden` e o `object-fit` da imagem são a correção do defeito que
|
|
741
|
+
a medição achou — foto que não é quadrada vazava inteira da caixa redonda, porque
|
|
742
|
+
`border-radius` não recorta filho sozinho. Foto de perfil raramente vem quadrada, então isto
|
|
743
|
+
quebrava no primeiro uso real. */
|
|
744
|
+
/* B5 do PLANO-1.0: os 36px viraram --control-h-md, que É 2.25rem = os mesmos 36px em
|
|
745
|
+
`comfortable`. Nada muda de aparência aqui; o que muda é que agora a DENSIDADE alcança o
|
|
746
|
+
avatar, e a escala é a mesma dos controles — avatar e botão na mesma linha ficam da mesma
|
|
747
|
+
altura por construção, em vez de por coincidência. */
|
|
748
|
+
.avatar { width:var(--control-h-md); height:var(--control-h-md); display:grid; place-items:center; overflow:hidden; border-radius:50%; background:var(--surface-3); color:var(--foreground); border:1px solid var(--border-strong); font-weight:var(--weight-bold); }
|
|
749
|
+
.avatar-sm { width:var(--control-h-sm); height:var(--control-h-sm); font-size:var(--text-xs); }
|
|
750
|
+
.avatar-lg { width:var(--control-h-lg); height:var(--control-h-lg); }
|
|
751
|
+
.avatar img { width:100%; height:100%; object-fit:cover; }
|
|
752
|
+
/* O container do grupo NUNCA teve regra: a sobreposição abaixo existia e os avatares empilhavam
|
|
753
|
+
na vertical porque o pai saía `display:block`. Mesma falha do `.stepper` e do `.command`. */
|
|
754
|
+
.avatar-group { display:inline-flex; align-items:center; }
|
|
755
|
+
.avatar-group .avatar { margin-inline-start:-9px; border:2px solid var(--surface-1); } .avatar-group .avatar:first-child { margin-inline-start:0; }
|
|
756
|
+
/* O "+N" é um avatar como os outros — mesma caixa, mesma borda —, só que menor por dentro e sem
|
|
757
|
+
peso de nome próprio: ele conta, não identifica. */
|
|
758
|
+
.avatar-count { background:var(--secondary); color:var(--muted-foreground); font-size:var(--text-xs); font-weight:var(--weight-medium); }
|
|
759
|
+
.kpi { display:flex; flex-direction:column; gap:5px; }
|
|
760
|
+
/* DataList: chave/valor em duas colunas. O termo é secundário — mesmo idioma do cabeçalho de
|
|
761
|
+
tabela. Sem a regra (achado A13) o <dl> saía cru: dt e dd empilhados, com o recuo de 40px
|
|
762
|
+
que o navegador dá ao <dd>. */
|
|
763
|
+
.data-list { display:grid; grid-template-columns:max-content minmax(0,1fr); gap:var(--space-2) var(--space-6); margin:0; }
|
|
764
|
+
.data-list dt { color:var(--muted-foreground); }
|
|
765
|
+
.data-list dd { min-width:0; margin:0; }
|
|
766
|
+
/* Timeline: o trilho existia e o PONTO não — `.timeline-dot` media 0×0 (achado A13), então a
|
|
767
|
+
linha do tempo saía com a régua e sem as marcas, e o <ol> ainda mostrava a numeração do
|
|
768
|
+
navegador. Agora os dois derivam do MESMO token: o trilho fica a meio diâmetro do ponto, e
|
|
769
|
+
alinham por construção em vez de por número copiado. Saem junto os 24px de recuo e os 7px
|
|
770
|
+
do trilho — dois px crus a menos na catraca do check 12. */
|
|
771
|
+
.timeline { position:relative; display:flex; flex-direction:column; gap:var(--space-5); margin:0; padding:0; list-style:none; }
|
|
772
|
+
.timeline:before { content:""; position:absolute; inset-inline-start:calc(var(--space-3) / 2); inset-block:var(--space-2); width:1px; background:var(--border); }
|
|
773
|
+
.timeline > li { display:flex; align-items:flex-start; gap:var(--space-3); }
|
|
774
|
+
/* A coluna de conteúdo é uma PILHA, e não por estética: sem isso o item empilhava quando
|
|
775
|
+
tinha descrição (o <p> é bloco) e colava título e horário na mesma linha quando não tinha —
|
|
776
|
+
"Shipped14:20". Geometria de componente não depende do conteúdo (achados A2/A3). */
|
|
777
|
+
.timeline > li > div { display:flex; flex-direction:column; gap:var(--space-05); min-width:0; }
|
|
778
|
+
.timeline > li > div > p { margin:0; }
|
|
779
|
+
.timeline-dot { flex:none; width:var(--space-3); height:var(--space-3); border-radius:var(--radius-full); margin-block-start:var(--space-1); background:var(--primary); box-shadow:0 0 0 var(--space-05) var(--surface-1); }
|
|
780
|
+
/* Accordion: anatomia medida no Carbon em 30/07/2026 (REFERENCES.md) — cabeçalho na altura de
|
|
781
|
+
controle, divisória de 1px por item, corpo com respiro pequeno em cima e grande embaixo.
|
|
782
|
+
O marcador é o NATIVO do <summary>: gira sozinho e custa zero código, e é por isso que o
|
|
783
|
+
summary continua `display:list-item` — pôr flex nele apagaria o marcador. */
|
|
784
|
+
.accordion { display:flex; flex-direction:column; border-block:1px solid var(--border); }
|
|
785
|
+
.accordion > details + details { border-top:1px solid var(--border); }
|
|
786
|
+
.accordion > details > summary { min-height:var(--control-h-md); padding-block:var(--space-2); padding-inline:var(--space-5) var(--space-2); color:var(--foreground); font-weight:var(--weight-medium); line-height:var(--leading-normal); cursor:pointer; }
|
|
787
|
+
.accordion > details > summary:hover { background:var(--surface-hover); }
|
|
788
|
+
.accordion > details > summary::marker { color:var(--muted-foreground); }
|
|
789
|
+
/* Recuo INLINE, não `padding-left`: o marcador do <summary> troca de lado em RTL e o corpo
|
|
790
|
+
tem de acompanhar. Foi o gate de RTL que cobrou — ele mede o eixo inline do core. */
|
|
791
|
+
.accordion-content { padding-block:0 var(--space-6); padding-inline:var(--space-5) var(--space-2); color:var(--muted-foreground); }
|
|
792
|
+
/* Mockup do kit de origem (apps/docs) — não é o Accordion do sistema, que é <details> nativo. */
|
|
793
|
+
|
|
794
|
+
/* QRCode: superfície flutuante Aurea, mas o miolo é de alto contraste FIXO — um QR
|
|
795
|
+
não segue o tema (dark inverteria e nem todo leitor decodifica invertido). Override
|
|
796
|
+
consciente por --qr-module/--qr-quiet. Módulos suaves (rx no SVG) sem perder escaneabilidade. */
|
|
797
|
+
/* B6 do PLANO-1.0: o tamanho saiu dos atributos width/height do SVG e virou --qr-size, no
|
|
798
|
+
mesmo idioma dos dois botões de ajuste que este componente já tinha. `md` são os 10rem =
|
|
799
|
+
160px de antes (com box-sizing:border-box o quiet zone continua por dentro da medida).
|
|
800
|
+
Três degraus e não um número livre: o número livre era o que obrigava pixel cru no
|
|
801
|
+
consumidor. Quem precisar de um QR de cartaz sobrescreve --qr-size, que é o mesmo caminho
|
|
802
|
+
já documentado para trocar a cor dos módulos. */
|
|
803
|
+
.qrcode { --qr-module:#0b0b0c; --qr-quiet:#fff; --qr-size:10rem; box-sizing:border-box; width:var(--qr-size); height:var(--qr-size); padding:var(--space-3); border:1px solid var(--border); border-radius:var(--radius-card); background:var(--qr-quiet); }
|
|
804
|
+
.qrcode-sm { --qr-size:7.5rem; }
|
|
805
|
+
.qrcode-lg { --qr-size:15rem; }
|
|
806
|
+
.qrcode .qr-mod { fill:var(--qr-module); }
|
|
807
|
+
.qrcode .qr-eye-gap { fill:var(--qr-quiet); }
|
|
808
|
+
|
|
809
|
+
/* `overflow:auto` faz do <pre> uma REGIÃO ROLÁVEL, e região rolável sem nada focável dentro
|
|
810
|
+
é inalcançável por teclado (axe scrollable-region-focusable). Quem escreve esta marcação à
|
|
811
|
+
mão precisa de `tabindex="0"` no <pre> — o componente React já o emite. */
|
|
812
|
+
.code-block { overflow:auto; padding:16px; border:1px solid var(--border); border-radius:var(--radius-lg); background:var(--surface-inset); color:var(--muted-foreground); font:12px/1.7 var(--font-code); }
|
|
813
|
+
/* Bloco de código COM copiar: a moldura não rola, o <pre> rola por dentro e o gatilho fica
|
|
814
|
+
parado no canto. O comportamento é do core (aurea.js, [data-aurea-copy]); aqui só a pele.
|
|
815
|
+
O check em amarelo é confirmação instantânea — dura 1,5s e volta ao ícone de copiar. */
|
|
816
|
+
/* checkbox com rótulo fora da tela: sem o texto, o alvo é só a marca — nada de gap sobrando. */
|
|
817
|
+
.checkbox-bare { gap:0; }
|
|
818
|
+
.code-block-wrap { position:relative; display:flex; min-width:0; }
|
|
819
|
+
.code-block-wrap > .code-block { flex:1; min-width:0; margin:0; }
|
|
820
|
+
.copy-code { position:absolute; top:var(--space-3); inset-inline-end:var(--space-3); z-index:1; }
|
|
821
|
+
.copy-code .c-done { display:none; }
|
|
822
|
+
.copy-code.is-done .c-copy { display:none; } .copy-code.is-done .c-done { display:block; color:var(--primary); }
|
|
823
|
+
/* "On this page": índice da própria página, fixo na lateral. Navegação, não decoração —
|
|
824
|
+
por isso é <nav> com rótulo, e o item atual se marca com aria-current. */
|
|
825
|
+
.toc { position:sticky; top:var(--space-4); align-self:start; display:grid; gap:var(--space-1); max-height:calc(100vh - var(--space-8)); overflow:auto; scrollbar-width:none; padding:var(--space-2) 0; }
|
|
826
|
+
.toc::-webkit-scrollbar { width:0; }
|
|
827
|
+
.toc-label { margin:0 0 var(--space-1); padding-inline:var(--space-3); color:var(--subtle-foreground); font-size:11px; letter-spacing:var(--tracking-widest); text-transform:uppercase; }
|
|
828
|
+
.toc a { padding:var(--space-1) var(--space-3); border-inline-start:2px solid var(--border); color:var(--muted-foreground); font-size:var(--text-sm); text-decoration:none; }
|
|
829
|
+
.toc a:hover { color:var(--foreground); }
|
|
830
|
+
.toc a[aria-current] { border-inline-start-color:var(--primary); color:var(--foreground); }
|
|
831
|
+
.toc a.toc-sub { padding-inline-start:var(--space-6); }
|
|
832
|
+
/* `--log-h` é a QUINTA válvula de altura do core, no idioma de `--chart-h` (I4), `--qr-size` (B6),
|
|
833
|
+
`--datagrid-max-h` (F6) e `--media-h` (I7). Default 240px — o de antes —, então nada muda para
|
|
834
|
+
quem não passa nada. A causa é medida em 11/08/2026: a altura era FIXA, e por isso três prévias
|
|
835
|
+
do catálogo gastavam 240px para mostrar três linhas de log e rolavam dentro da caixa de
|
|
836
|
+
demonstração, contra a ADR-0002. Altura é decisão da página, e não vira prop (check 23). */
|
|
837
|
+
.log-stream { height:var(--log-h,240px); overflow:auto; padding:14px; border:1px solid var(--border); border-radius:var(--radius-md); background:var(--surface-inset); font:12px/1.65 var(--font-code); }
|
|
838
|
+
.log-line { display:grid; grid-template-columns:86px 72px minmax(0,1fr); gap:12px; padding:3px 0; } .log-time { color:var(--subtle-foreground); } .log-level { color:var(--primary); } .log-level.warn { color:var(--warning-400); } .log-level.error { color:var(--danger-400); }
|
|
839
|
+
/* O nível pintado a partir da classe que o pacote React emite no CONTAINER (`log-error`).
|
|
840
|
+
As duas formas convivem de propósito: `.log-level.warn` serve a apps/docs/index.html,
|
|
841
|
+
escrito à mão, e sai junto com ele na Parte D. Medido no item E13: sem estas três, a
|
|
842
|
+
prop `level` do LogStream não pintava nada — pele que existe e não faz nada. */
|
|
843
|
+
.log-info .log-level { color:var(--info-400); }
|
|
844
|
+
.log-warn .log-level { color:var(--warning-400); }
|
|
845
|
+
.log-error .log-level { color:var(--danger-400); }
|
|
846
|
+
|
|
847
|
+
/* ========================================================================== */
|
|
848
|
+
/* 05. GRÁFICOS E VISUALIZAÇÃO */
|
|
849
|
+
/* ========================================================================== */
|
|
850
|
+
/* I4 do PLANO-1.0: a altura vira válvula de escape, no MESMO idioma do `--qr-size` e do
|
|
851
|
+
`--datagrid-max-h` — quem sabe quanta tela tem é quem hospeda, e altura não vira prop
|
|
852
|
+
numérica (check 23). O default é os mesmos 220px de antes, então nada muda para quem já
|
|
853
|
+
usa: medido, zero pixel nas capturas. O que ele destrava é a composição — o `I4
|
|
854
|
+
AnalyticsWorkbench` põe gráfico E tabela na mesma caixa de altura fixa (ADR-0002), e com
|
|
855
|
+
220 fixos as duas peças sozinhas já passavam do vão do painel em 95px. O fallback é
|
|
856
|
+
deliberado e é o que o check 29 exige de quem não declara o token. */
|
|
857
|
+
.chart { width:100%; height:var(--chart-h,220px); } .chart-bar.alt { fill:var(--chart-1); } .health-cell.warn { background:var(--warning-500); } .health-cell.bad { background:var(--danger-500); } .health-cell.off { background:var(--surface-3); }
|
|
858
|
+
/* Chart (Lote 3) — a pele do desenho que o Recharts emite. Os seletores são de ELEMENTO, nunca
|
|
859
|
+
`.recharts-*`: nome de classe de terceiro dentro do core reprovaria o check 15 (o core só
|
|
860
|
+
declara classe que a Aurea produz), e é a mesma razão pela qual a pele do CodeEditor vem pelo
|
|
861
|
+
EditorView.theme do CodeMirror e não daqui. O motor põe stroke/fill como ATRIBUTO de
|
|
862
|
+
apresentação (#ccc nas linhas de grade, #666 nos rótulos), e CSS vence atributo — medido em
|
|
863
|
+
01/08/2026: 20 <line> e 9 <text> num gráfico de linha com grade e dois eixos. */
|
|
864
|
+
.chart svg line { stroke:var(--border); } .chart svg text { fill:var(--muted-foreground); font-size:var(--text-xs); }
|
|
865
|
+
.chart-tooltip { display:grid; gap:var(--space-1); pointer-events:none; } .chart-tooltip strong { color:var(--foreground); font-weight:var(--weight-medium); }
|
|
866
|
+
.chart-tooltip b { margin-inline-start:auto; color:var(--foreground); font-variant-numeric:tabular-nums; }
|
|
867
|
+
.chart-legend { display:flex; flex-wrap:wrap; justify-content:center; gap:var(--space-1) var(--space-4); margin:0; padding:0; list-style:none; }
|
|
868
|
+
.chart-key { display:flex; align-items:center; gap:var(--space-2); color:var(--muted-foreground); font-size:var(--text-xs); } .chart-key i { flex:none; width:9px; height:9px; border-radius:var(--radius-sm); }
|
|
869
|
+
/* O cursor precisa de DOIS pontos de especificidade porque `.chart svg line` acima já tem dois
|
|
870
|
+
elementos: `.chart .chart-cursor` (0,2,0) vence `.chart svg line` (0,1,2). Em gráfico de linha
|
|
871
|
+
o cursor é <line> e em barra é <rect>, então a regra dá stroke E fill — cada um usa o seu. */
|
|
872
|
+
.chart .chart-cursor { stroke:var(--border-strong); fill:var(--secondary); }
|
|
873
|
+
/* Calendar (Lote 4) — a pele da grade que o react-day-picker emite. Mesma regra do Chart e pela
|
|
874
|
+
mesma razão: nada de `.rdp-*` aqui (o check 15 reprova nome de classe que a Aurea não produz).
|
|
875
|
+
Três nomes vêm renomeados pela API `classNames` do motor; o resto é ELEMENTO e `data-*`, e
|
|
876
|
+
atributo não é classe. Estado medido em 01/08/2026: data-selected, data-today, data-outside,
|
|
877
|
+
data-disabled e data-hidden — este último é obrigatório, senão o dia escondido das pontas
|
|
878
|
+
APARECE (o motor conta com o CSS dele para escondê-lo, e nós não importamos o CSS dele). */
|
|
879
|
+
.calendar { display:inline-block; padding:var(--space-3); border:1px solid var(--border); border-radius:var(--radius-card); background:var(--surface-1); }
|
|
880
|
+
.calendar-months { position:relative; }
|
|
881
|
+
.calendar nav { position:absolute; inset-block-start:0; inset-inline:0; display:flex; justify-content:space-between; }
|
|
882
|
+
.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); }
|
|
884
|
+
.calendar nav button:disabled { opacity:.4; cursor:not-allowed; }
|
|
885
|
+
.calendar nav svg { width:var(--icon-sm); height:var(--icon-sm); fill:currentColor; }
|
|
886
|
+
.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); }
|
|
887
|
+
/* Estas três já foram um DESFAZIMENTO: `width:auto`, `min-width:0`, `height:auto` e
|
|
888
|
+
`border-bottom:0` existiam só para anular a regra de elemento da seção de dados. A regra foi
|
|
889
|
+
escopada em 02/08/2026 e o antídoto saiu junto — corrigir onde nasce, não onde dói.
|
|
890
|
+
O `padding:0` FICA, e não é antídoto: é o 1px que o NAVEGADOR dá a `<td>`/`<th>`. Tirá-lo
|
|
891
|
+
junto com o resto engordou a célula para 38px e a grade para 266px — medido, e voltado. */
|
|
892
|
+
.calendar table { border-collapse:collapse; margin-block-start:var(--space-2); }
|
|
893
|
+
.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
|
+
.calendar td { padding:0; text-align:center; }
|
|
895
|
+
.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); }
|
|
897
|
+
.calendar td[data-today] button { font-weight:var(--weight-medium); box-shadow:inset 0 0 0 1px var(--border-strong); }
|
|
898
|
+
.calendar td[data-selected] button { background:var(--primary); color:var(--primary-foreground); font-weight:var(--weight-medium); box-shadow:none; }
|
|
899
|
+
.calendar td[data-outside] button { color:var(--subtle-foreground); }
|
|
900
|
+
.calendar td button:disabled { opacity:.35; cursor:not-allowed; }
|
|
901
|
+
.calendar td[data-hidden] { visibility:hidden; }
|
|
902
|
+
.message { display:grid; grid-template-columns:auto minmax(0,1fr); gap:12px; } .message-bubble { padding:12px 14px; border-radius:6px var(--radius-lg) var(--radius-lg) var(--radius-lg); background:var(--secondary); } .message-bubble p { margin:0; }
|
|
903
|
+
/* Chat React (Fase 5.4): estrutura fixa em classe, não inline (auditoria 18/07, M14). */
|
|
904
|
+
.message-status { display:flex; align-items:center; flex-wrap:wrap; gap:var(--space-3); margin-block-start:var(--space-2); }
|
|
905
|
+
.message-composer { display:flex; align-items:center; gap:var(--space-3); flex-wrap:nowrap; } .message-composer .input-wrap { flex:1; min-width:0; }
|
|
906
|
+
|
|
907
|
+
/* ========================================================================== */
|
|
908
|
+
/* 07. MÍDIA */
|
|
909
|
+
/* ========================================================================== */
|
|
910
|
+
/* `--media-h` e `--media-ar` são VÁLVULA DE ESCAPE, autorizada pelo Victor em 11/08/2026 no item
|
|
911
|
+
I7 do PLANO-1.0, e no idioma exato das três que já existem: `--chart-h` (I4), `--qr-size` (B6) e
|
|
912
|
+
`--datagrid-max-h` (F6). O default é o de antes — 360px e 16/9 —, então nada muda para quem não
|
|
913
|
+
passa nada. A causa é medida: numa caixa de demonstração de 826px de largura o `aspect-ratio`
|
|
914
|
+
produz 464px de altura, contra um vão útil de 354; o player não cabe numa composição, e o
|
|
915
|
+
`MediaPlayer` não repassa `style` para esta caixa (as props sobrando vão para o `<video>`, de
|
|
916
|
+
propósito). Quem sabe quanta altura tem é a página, e altura não vira prop (check 23).
|
|
917
|
+
⚠ `--media-ar:auto` SÓ é seguro junto com `--media-h`. A razão do `aspect-ratio` aqui não é
|
|
918
|
+
estética: é CLS. O exemplo de playlist do `media-chrome` declara o mesmo com o motivo escrito —
|
|
919
|
+
sem proporção fixa, um player com `preload=none` mede zero até a mídia chegar e a página SALTA.
|
|
920
|
+
Tirar a proporção sem fixar a altura devolve o salto. */
|
|
921
|
+
.media-player { position:relative; isolation:isolate; overflow:hidden; min-height:var(--media-h,360px); aspect-ratio:var(--media-ar,16/9); border:1px solid var(--border); border-radius:var(--radius-card); background:var(--media-canvas); color:var(--media-foreground); }
|
|
922
|
+
.media-viewport { position:absolute; inset:0; display:grid; place-items:center; background:var(--media-canvas); }
|
|
923
|
+
.media-fill { width:100%; height:100%; object-fit:contain; } /* vídeo React preenche o viewport (auditoria 18/07, M14) */
|
|
924
|
+
.media-placeholder { display:grid; place-items:center; gap:10px; color:var(--media-muted); text-align:center; }
|
|
925
|
+
.media-placeholder .icon { width:48px; height:48px; color:var(--media-foreground); }
|
|
926
|
+
.media-placeholder strong { color:var(--media-foreground); }
|
|
927
|
+
.media-overlay-title { position:absolute; top:16px; inset-inline:16px; z-index:2; display:flex; align-items:flex-start; justify-content:space-between; gap:16px; color:var(--media-foreground); }
|
|
928
|
+
.media-overlay-title>div>span { display:block; color:var(--media-muted); font-size:var(--text-xs); }
|
|
929
|
+
.media-player .badge { border-color:var(--media-track); background:var(--media-track); color:var(--media-foreground); }
|
|
930
|
+
.media-controls { position:absolute; inset:auto 0 0; z-index:3; display:grid; gap:8px; padding:12px; background:var(--media-scrim); color:var(--media-foreground); }
|
|
931
|
+
.media-seek { --media-played:34%; position:relative; height:20px; display:flex; align-items:center; }
|
|
932
|
+
.media-seek-track { position:absolute; inset-inline:0; height:4px; overflow:hidden; border-radius:var(--radius-full); background:var(--media-track); pointer-events:none; }
|
|
933
|
+
.media-seek-buffered,.media-seek-played { position:absolute; inset-block:0; inset-inline:0 auto; border-radius:inherit; }
|
|
934
|
+
.media-seek-buffered { width:62%; background:var(--media-buffered); }
|
|
935
|
+
.media-seek-played { width:var(--media-played); background:var(--primary); }
|
|
936
|
+
.media-seek input { position:relative; z-index:1; width:100%; height:20px; margin:0; appearance:none; background:transparent; cursor:pointer; }
|
|
937
|
+
.media-seek input::-webkit-slider-runnable-track { height:4px; background:transparent; }
|
|
938
|
+
.media-seek input::-webkit-slider-thumb { width:14px; height:14px; margin-top:-5px; appearance:none; border:2px solid var(--media-foreground); border-radius:50%; background:var(--primary); }
|
|
939
|
+
.media-seek input::-moz-range-track { height:4px; background:transparent; }
|
|
940
|
+
.media-seek input::-moz-range-thumb { width:12px; height:12px; border:2px solid var(--media-foreground); border-radius:50%; background:var(--primary); }
|
|
941
|
+
.media-seek input:focus-visible { outline:2px solid var(--primary); outline-offset:3px; box-shadow:none; }
|
|
942
|
+
.media-control-row,.media-control-group { display:flex; align-items:center; gap:4px; }
|
|
943
|
+
.media-control-row { min-width:0; justify-content:space-between; }
|
|
944
|
+
.media-control { min-width:36px; height:36px; display:inline-grid; place-items:center; padding:0 8px; border:1px solid transparent; border-radius:var(--radius-md); background:transparent; color:var(--media-foreground); cursor:pointer; }
|
|
945
|
+
.media-control:hover,.media-control.active { background:var(--media-track); color:var(--primary); }
|
|
946
|
+
.media-time { padding:0 7px; color:var(--media-muted); font:var(--text-xs) var(--font-code); white-space:nowrap; }
|
|
947
|
+
.media-volume { width:82px; accent-color:var(--primary); }
|
|
948
|
+
|
|
949
|
+
/* ========================================================================== */
|
|
950
|
+
/* 08. ÍCONES */
|
|
951
|
+
/* ========================================================================== */
|
|
952
|
+
.icon-search { max-width:420px; }
|
|
953
|
+
.icon-category { margin-top:26px; } .icon-category h3 { margin:0 0 12px; font-size:var(--text-base); }
|
|
954
|
+
.icon-grid { display:grid; grid-template-columns:repeat(auto-fill,minmax(150px,1fr)); gap:8px; }
|
|
955
|
+
.icon-tile { min-height:86px; display:flex; flex-direction:column; align-items:flex-start; justify-content:space-between; gap:12px; padding:13px; border:1px solid var(--border); border-radius:var(--radius-lg); background:var(--secondary); color:var(--muted-foreground); cursor:pointer; text-align:start; transition:transform var(--duration-fast) var(--ease-standard),border-color var(--duration-fast),color var(--duration-fast); }
|
|
956
|
+
@media (hover:hover) and (pointer:fine) { .icon-tile:hover { color:var(--foreground); border-color:var(--primary); transform:translateY(-1px); } } .icon-tile:focus-visible { color:var(--foreground); border-color:var(--primary); } .icon-tile span { overflow:hidden; max-width:100%; text-overflow:ellipsis; white-space:nowrap; font:11px var(--font-code); }
|
|
957
|
+
.dialog-backdrop { position:fixed; inset:0; z-index:var(--z-modal); background:var(--overlay); backdrop-filter:blur(5px); }
|
|
958
|
+
.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
|
+
.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; }
|
|
960
|
+
.drawer-wrap { position:fixed; inset:0; z-index:var(--z-drawer); display:none; background:var(--overlay); } .drawer-wrap.open { display:block; }
|
|
961
|
+
.drawer-backdrop { position:fixed; inset:0; z-index:var(--z-drawer); background:var(--overlay); }
|
|
962
|
+
.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
|
+
.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); }
|
|
964
|
+
/* CommandPaletteShell (Fase 11, achado A13). A superfície já existia aqui sob o nome
|
|
965
|
+
`.command` — e `.command` é chrome da página de docs manual: nenhum componente React a
|
|
966
|
+
emite. Ela passava no check 15 por ACIDENTE, porque o rótulo padrão "Type a command…" põe
|
|
967
|
+
a palavra "command" numa string do pacote React e o gate lê as strings do fonte. Foi para
|
|
968
|
+
apps/docs/docs.css, que é a casa dela; o sistema fica com os dois nomes que o componente
|
|
969
|
+
emite de verdade. Não-modal de propósito, como a gaveta do AppShell (ADR-0003): o foco não
|
|
970
|
+
fica preso, então não declaramos modalidade que não entregamos. */
|
|
971
|
+
.command-overlay { position:fixed; inset:0; z-index:var(--z-modal); display:grid; place-items:start center; padding:var(--space-16) var(--space-5) var(--space-5); background:var(--overlay); backdrop-filter:blur(5px); }
|
|
972
|
+
.command-palette { display:grid; gap:var(--space-2); width:min(680px,100%); max-height:100%; overflow:auto; padding:var(--space-4); border:1px solid var(--border); border-radius:var(--radius-card); background:var(--popover); color:var(--popover-foreground); box-shadow:var(--shadow-lg); }
|
|
973
|
+
/* Camadas flutuantes (Base UI): tooltip, popover, menu. z-modal para funcionarem sobre diálogos; empilhamento mútuo pela ordem do portal no DOM. */
|
|
974
|
+
.tooltip { z-index:var(--z-modal); max-width:260px; padding:6px 10px; border:1px solid var(--border); border-radius:var(--radius-md); background:var(--surface-inset); color:var(--foreground); font-size:var(--text-xs); line-height:var(--leading-normal); box-shadow:var(--shadow-md); }
|
|
975
|
+
.popover { z-index:var(--z-modal); min-width:220px; max-width:min(360px,calc(100vw - 32px)); padding:16px; border:1px solid var(--border); border-radius:var(--radius-card); background:var(--popover); color:var(--popover-foreground); box-shadow:var(--shadow-lg); display:grid; gap:10px; }
|
|
976
|
+
.menu { z-index:var(--z-modal); min-width:200px; max-width:min(320px,calc(100vw - 32px)); padding:6px; border:1px solid var(--border); border-radius:var(--radius-lg); background:var(--popover); color:var(--popover-foreground); box-shadow:var(--shadow-lg); }
|
|
977
|
+
.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
|
+
.menu-item[data-highlighted],.menu-item:hover:not([data-disabled]) { background:var(--surface-3); outline:none; }
|
|
979
|
+
.menu-item[data-disabled] { opacity:.5; cursor:not-allowed; }
|
|
980
|
+
.menu-sep { height:1px; margin:6px 4px; background:var(--border); border:0; }
|
|
981
|
+
/* Combobox: reaproveita .menu/.menu-item; só dimensionamento e a coluna do check são próprios.
|
|
982
|
+
--anchor-width e --available-height vêm do positioner do Base UI. */
|
|
983
|
+
.combobox-group { position:relative; display:flex; align-items:center; }
|
|
984
|
+
.combobox-group .input { padding-inline-end:calc(var(--control-h-sm) * 2 + 10px); }
|
|
985
|
+
.combobox-actions { position:absolute; inset-inline-end:5px; display:flex; align-items:center; gap:2px; }
|
|
986
|
+
.combobox-popup { inline-size:var(--anchor-width); min-inline-size:0; max-inline-size:none; max-block-size:min(320px,var(--available-height)); overflow-y:auto; overscroll-behavior:contain; }
|
|
987
|
+
.combobox-empty { padding:12px 10px; color:var(--muted-foreground); font-size:var(--text-sm); }
|
|
988
|
+
.combobox-empty:empty { display:none; }
|
|
989
|
+
.combobox-item { display:grid; grid-template-columns:16px minmax(0,1fr); gap:8px; align-items:center; }
|
|
990
|
+
.combobox-check { display:inline-flex; color:var(--primary); }
|
|
991
|
+
/* MultiCombobox (Fase 4) — chips de multi-seleção + grupos. Espaçamento novo tokenizado. */
|
|
992
|
+
.combobox-multi { display:flex; align-items:center; gap:var(--space-2); width:100%; min-width:0; min-height:var(--control-h-md); padding:var(--space-1) var(--space-2); background:var(--field-bg); border:1px solid var(--border-strong); border-radius:var(--radius-control); transition:border-color var(--duration-fast); }
|
|
993
|
+
.combobox-multi:hover { border-color:var(--border); }
|
|
994
|
+
.combobox-multi:focus-within { outline:2px solid var(--focus-strong); outline-offset:2px; border-color:var(--focus-strong); }
|
|
995
|
+
.combobox-multi>.combobox-actions { position:static; inset-inline-end:auto; }
|
|
996
|
+
.combobox-chips { display:flex; flex:1; flex-wrap:wrap; align-items:center; gap:var(--space-2); min-width:0; }
|
|
997
|
+
.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
|
+
.combobox-chip-remove { display:inline-flex; padding:0; border:0; background:transparent; color:var(--muted-foreground); border-radius:var(--radius-full); cursor:pointer; }
|
|
999
|
+
.combobox-chip-remove:hover { color:var(--foreground); }
|
|
1000
|
+
.combobox-chip-input { flex:1; min-width:6ch; height:var(--control-h-sm); border:0; background:transparent; padding:0 var(--space-1); color:var(--foreground); }
|
|
1001
|
+
.combobox-chip-input::placeholder { color:var(--subtle-foreground); }
|
|
1002
|
+
.combobox-chip-input:focus-visible { outline:none; }
|
|
1003
|
+
.combobox-group-label { padding:var(--space-1) var(--space-2); color:var(--muted-foreground); font-size:var(--text-xs); font-weight:var(--weight-semibold); }
|
|
1004
|
+
/* FileInput / DropZone (Fase 4) — só a UI de entrada; upload real é a Fase 5.
|
|
1005
|
+
A zona é um <label> em volta do <input> nativo: clique/foco/teclado de graça,
|
|
1006
|
+
o arrastar-soltar é reforço. Espaçamento novo tokenizado; superfície = card. */
|
|
1007
|
+
.dropzone { display:flex; flex-direction:column; align-items:center; gap:var(--space-2); padding:var(--space-8) var(--space-6); text-align:center; color:var(--muted-foreground); background:var(--surface-inset); border:1px dashed var(--border-strong); border-radius:var(--radius-card); cursor:pointer; transition:border-color var(--duration-fast),background-color var(--duration-fast); }
|
|
1008
|
+
.dropzone:hover { border-color:var(--border); }
|
|
1009
|
+
.dropzone:focus-within { outline:2px solid var(--focus-strong); outline-offset:2px; border-color:var(--focus-strong); }
|
|
1010
|
+
.dropzone[data-dragging="true"] { border-style:solid; border-color:var(--primary); background:var(--surface-2); }
|
|
1011
|
+
.dropzone strong { color:var(--foreground); }
|
|
1012
|
+
.dropzone-icon { color:var(--primary); }
|
|
1013
|
+
.dropzone-hint { font-size:var(--text-xs); }
|
|
1014
|
+
.file-list { display:flex; flex-direction:column; gap:var(--space-2); margin-top:var(--space-3); padding:0; list-style:none; }
|
|
1015
|
+
.file-item { display:flex; align-items:center; gap:var(--space-3); padding:var(--space-2) var(--space-3); background:var(--surface-2); border:1px solid var(--border); border-radius:var(--radius-md); }
|
|
1016
|
+
.file-progress { flex-basis:var(--space-20); flex-shrink:0; } /* largura fixa da barra de upload (auditoria 18/07, M14) */
|
|
1017
|
+
.file-name { flex:1; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
|
|
1018
|
+
.file-size { flex:none; color:var(--muted-foreground); font-size:var(--text-xs); font-variant-numeric:tabular-nums; }
|
|
1019
|
+
|
|
1020
|
+
/* TreeView (Fase 4) — árvore headless própria (Base UI 1.6 não tem tree).
|
|
1021
|
+
role=tree/treeitem/group + roving tabindex vêm do React; aqui só a pele.
|
|
1022
|
+
A indentação por nível é padding-inline-start inline (style). Espaço novo
|
|
1023
|
+
tokenizado; a linha não é pill (radius-sm, como o .menu-item). */
|
|
1024
|
+
.tree, .tree-group { margin:0; padding:0; list-style:none; }
|
|
1025
|
+
.tree-item { outline:none; }
|
|
1026
|
+
.tree-node { display:flex; align-items:center; gap:var(--space-2); min-height:var(--control-h-md); padding-block:var(--space-1); padding-inline-end:var(--space-3); border-radius:var(--radius-sm); color:var(--foreground); cursor:pointer; user-select:none; }
|
|
1027
|
+
.tree-node:hover { background:var(--surface-3); }
|
|
1028
|
+
.tree-item:focus-visible > .tree-node { outline:2px solid var(--focus-strong); outline-offset:-2px; }
|
|
1029
|
+
.tree-node[data-selected] { background:color-mix(in srgb,var(--primary) 14%,transparent); }
|
|
1030
|
+
.tree-twist { flex:none; color:var(--muted-foreground); transition:transform var(--duration-fast); }
|
|
1031
|
+
.tree-item[aria-expanded="true"] > .tree-node > .tree-twist { transform:rotate(90deg); }
|
|
1032
|
+
.tree-item[aria-expanded="false"]:dir(rtl) > .tree-node > .tree-twist { transform:rotate(180deg); }
|
|
1033
|
+
.tree-indent { flex:none; width:1rem; }
|
|
1034
|
+
.tree-label { flex:1; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
|
|
1035
|
+
|
|
1036
|
+
/* NotificationCenter (Fase 4) — compõe o .popover; aqui só o gatilho com count,
|
|
1037
|
+
o cabeçalho e as linhas do feed. Espaço novo tokenizado; a linha usa radius-sm
|
|
1038
|
+
(não é pill, como o .menu-item/.tree-node). O painel NÃO é live region — a
|
|
1039
|
+
chegada é anunciada por um role=status à parte no React. */
|
|
1040
|
+
.notification-trigger { position:relative; display:inline-flex; }
|
|
1041
|
+
.notification-count { position:absolute; inset-block-start:-4px; inset-inline-end:-4px; display:inline-flex; align-items:center; justify-content:center; min-width:18px; height:18px; padding:0 var(--space-1); border-radius:var(--radius-control); background:var(--primary); color:var(--primary-foreground); font-size:var(--text-xs); font-weight:var(--weight-semibold); line-height:var(--leading-none); pointer-events:none; }
|
|
1042
|
+
.notification-panel { width:min(360px,calc(100vw - 32px)); max-width:none; padding:0; gap:0; display:flex; flex-direction:column; overflow:hidden; }
|
|
1043
|
+
.notification-head { display:flex; align-items:center; justify-content:space-between; gap:var(--space-3); padding:var(--space-3) var(--space-4); border-block-end:1px solid var(--border); }
|
|
1044
|
+
.notification-head strong { font-size:var(--text-sm); }
|
|
1045
|
+
.notification-list { margin:0; padding:var(--space-1); overflow-y:auto; max-height:min(60vh,440px); }
|
|
1046
|
+
.notification-sublist { margin:0; padding:0; list-style:none; }
|
|
1047
|
+
.notification-group-label { margin:0; padding:var(--space-2) var(--space-3) var(--space-1); color:var(--muted-foreground); font-size:var(--text-xs); font-weight:var(--weight-semibold); letter-spacing:var(--tracking-wide); text-transform:uppercase; }
|
|
1048
|
+
.notification-item { display:grid; grid-template-columns:auto minmax(0,1fr) auto; align-items:baseline; column-gap:var(--space-3); row-gap:var(--space-1); width:100%; padding:var(--space-3); border:0; border-radius:var(--radius-sm); background:none; color:var(--foreground); font:inherit; text-align:start; }
|
|
1049
|
+
button.notification-item { cursor:pointer; }
|
|
1050
|
+
button.notification-item:hover { background:var(--surface-3); }
|
|
1051
|
+
.notification-item:focus-visible { outline:2px solid var(--focus-strong); outline-offset:-2px; }
|
|
1052
|
+
.notification-dot { grid-row:1/-1; align-self:center; width:8px; height:8px; border-radius:50%; background:var(--primary); }
|
|
1053
|
+
.notification-item[data-read] .notification-dot { visibility:hidden; }
|
|
1054
|
+
.notification-item-title { grid-column:2; display:flex; align-items:center; gap:var(--space-2); min-width:0; font-size:var(--text-sm); font-weight:var(--weight-medium); }
|
|
1055
|
+
.notification-item[data-read] .notification-item-title { color:var(--muted-foreground); font-weight:var(--weight-regular); }
|
|
1056
|
+
.notification-item-desc { grid-column:2; color:var(--muted-foreground); font-size:var(--text-xs); }
|
|
1057
|
+
.notification-time { grid-column:3; grid-row:1; color:var(--muted-foreground); font-size:var(--text-xs); white-space:nowrap; }
|
|
1058
|
+
.notification-empty { margin:0; padding:var(--space-8) var(--space-4); color:var(--muted-foreground); font-size:var(--text-sm); text-align:center; }
|
|
1059
|
+
/* EmptyState é o membro GRANDE da família "aqui não há nada" — .notification-empty acima e
|
|
1060
|
+
.datagrid-empty abaixo já respondiam a mesma pergunta do mesmo jeito (centralizado, muted),
|
|
1061
|
+
e o componente público era o único dos três sem regra (achado A13). A ordem das partes
|
|
1062
|
+
(imagem → título → corpo → ação) é a do padrão de empty state do Carbon, registrado em
|
|
1063
|
+
REFERENCES.md; a pele é Aurea. O título vinha com o h3 do NAVEGADOR (16,38px). */
|
|
1064
|
+
.empty-state { display:flex; flex-direction:column; align-items:center; gap:var(--space-3); padding:var(--space-10) var(--space-6); color:var(--muted-foreground); text-align:center; }
|
|
1065
|
+
.empty-state > .icon { color:var(--subtle-foreground); }
|
|
1066
|
+
.empty-state p { max-width:46ch; margin:0; }
|
|
1067
|
+
.empty-title { margin:0; color:var(--foreground); font-size:var(--text-base); font-weight:var(--weight-semibold); line-height:var(--leading-tight); }
|
|
1068
|
+
|
|
1069
|
+
/* DataGrid (Fase 4) — TanStack Table v8 headless; aqui só a pele, que reusa
|
|
1070
|
+
.table-wrap/table/th/td. aria-sort fica só no th ordenado; a linha
|
|
1071
|
+
selecionada usa data-selected (o estado acessível é o checkbox da linha).
|
|
1072
|
+
Espaço novo tokenizado. */
|
|
1073
|
+
.datagrid { display:grid; gap:var(--space-3); }
|
|
1074
|
+
.datagrid > .table-wrap:focus-visible { outline:none; box-shadow:0 0 0 3px color-mix(in oklab,var(--focus) 38%,transparent); }
|
|
1075
|
+
.datagrid > .pagination { justify-self:end; }
|
|
1076
|
+
/* Fix aditivo: os 34px dos botões numéricos do .pagination esmagavam os botões
|
|
1077
|
+
de TEXTO (Anterior/Próxima) do Pagination React. Não muda os numéricos. */
|
|
1078
|
+
/* text-transform/letter-spacing: o UA reseta nos <button>; herdar explícito
|
|
1079
|
+
para o cabeçalho ordenável ficar idêntico ao th estático. */
|
|
1080
|
+
.datagrid-sort { display:inline-flex; align-items:center; gap:var(--space-1); padding:0; border:0; background:none; color:inherit; font:inherit; text-transform:inherit; letter-spacing:inherit; cursor:pointer; }
|
|
1081
|
+
.datagrid-sort:focus-visible { outline:2px solid var(--focus-strong); outline-offset:2px; border-radius:var(--radius-sm); }
|
|
1082
|
+
.datagrid-sort-icon { flex:none; color:var(--subtle-foreground); }
|
|
1083
|
+
th[aria-sort] .datagrid-sort-icon { color:var(--primary); }
|
|
1084
|
+
.datagrid-selcol { width:1%; white-space:nowrap; }
|
|
1085
|
+
|
|
1086
|
+
/* F3: a linha de filtro é do PRÓPRIO cabeçalho, então herda o recuo lateral das
|
|
1087
|
+
células e nasce alinhada com a coluna — ninguém escreve alinhamento aqui.
|
|
1088
|
+
As três declarações abaixo foram provadas UMA A UMA no navegador, tirando cada
|
|
1089
|
+
uma e medindo de novo: sem `text-transform` o campo digita em MAIÚSCULAS; sem
|
|
1090
|
+
`vertical-align` os dois controles (36px e 40px) começam em linhas diferentes;
|
|
1091
|
+
sem `gap:0` o rótulo só-para-leitor-de-tela cobra 7px de vão.
|
|
1092
|
+
Três outras foram escritas e MORRERAM na mesma prova, por não mudarem nada:
|
|
1093
|
+
`height:auto` (o conteúdo já passa de --row-h, e altura em célula é mínimo),
|
|
1094
|
+
`letter-spacing:normal` (o .input não herda o entreletras do cabeçalho) e
|
|
1095
|
+
`line-height:0` no rótulo (o <label> vazio já não ocupa altura). */
|
|
1096
|
+
.datagrid-filters th { padding-block:var(--space-2); text-transform:none; vertical-align:top; }
|
|
1097
|
+
.datagrid-filters .field { gap:0; }
|
|
1098
|
+
|
|
1099
|
+
/* F6: cabeçalho fixo. A `.table-wrap` já é o contêiner de rolagem (overflow:auto),
|
|
1100
|
+
e é ELE que ancora o sticky — não a janela. Por isso a altura precisa de teto:
|
|
1101
|
+
sem teto a caixa nunca rola por dentro e o cabeçalho nunca teria de grudar.
|
|
1102
|
+
`--datagrid-max-h` é a válvula de escape, no mesmo idioma do `--qr-size`: quem
|
|
1103
|
+
sabe quanta tela tem é o consumidor, e altura não vira prop numérica (check 23).
|
|
1104
|
+
A linha de FILTRO gruda logo abaixo do cabeçalho, e o deslocamento é `--row-h`
|
|
1105
|
+
porque é essa a altura da célula de cabeçalho — medido, não deduzido. */
|
|
1106
|
+
.datagrid-sticky .table-wrap { max-block-size:var(--datagrid-max-h,60vh); }
|
|
1107
|
+
/* `z-index:1` fica DECLARADO COMO PRECAUÇÃO, não como fato provado: não consegui
|
|
1108
|
+
medi-lo (elementFromPoint usa coordenada de janela, e a grade está a 7000px de
|
|
1109
|
+
rolagem na página de prova). É prática corrente para cabeçalho fixo — sem ele o
|
|
1110
|
+
fundo da linha do corpo pode passar por cima —, e removê-lo por não ter gate
|
|
1111
|
+
seria trocar risco real por pureza de método. As outras três regras daqui foram
|
|
1112
|
+
provadas. */
|
|
1113
|
+
.datagrid-sticky thead th { position:sticky; inset-block-start:0; z-index:1; }
|
|
1114
|
+
.datagrid-sticky .datagrid-filters th { inset-block-start:var(--row-h); }
|
|
1115
|
+
|
|
1116
|
+
/* F7: a alça de redimensionar. Fina, colada no fim da célula e da altura toda —
|
|
1117
|
+
alvo estreito de propósito, porque largo demais rouba o clique do cabeçalho
|
|
1118
|
+
ordenável que está ao lado. O `th` precisa de `position:relative` para ancorá-la. */
|
|
1119
|
+
.datagrid thead th:has(> .datagrid-resizer) { position:relative; }
|
|
1120
|
+
.datagrid-resizer { position:absolute; inset-block:0; inset-inline-end:0; inline-size:var(--space-1); padding:0; border:0; background:none; cursor:col-resize; touch-action:none; }
|
|
1121
|
+
.datagrid-resizer:hover, .datagrid-resizer:focus-visible { background:var(--primary); }
|
|
1122
|
+
.datagrid-resizer:focus-visible { outline:2px solid var(--focus-strong); outline-offset:1px; }
|
|
1123
|
+
|
|
1124
|
+
/* F8: o esqueleto dentro da célula. Sem altura própria ele mede zero — a `.skeleton`
|
|
1125
|
+
é uma <div> vazia — e a linha de carregamento sairia rasa demais para parecer uma
|
|
1126
|
+
linha. `1em` acompanha a tipografia em vez de fixar pixel. */
|
|
1127
|
+
.datagrid-skeleton { block-size:1em; }
|
|
1128
|
+
|
|
1129
|
+
/* F9: lista e detalhe lado a lado.
|
|
1130
|
+
LIMITE DECLARADO: a decisão é por largura de JANELA, não da grade. O `@container`
|
|
1131
|
+
seria mais certo — quem sabe se o painel cabe é a grade — e foi tentado: exige
|
|
1132
|
+
`container-type:inline-size` num ancestral, e pôr isso na `.datagrid` dá a ela
|
|
1133
|
+
contenção de tamanho, o que COLAPSA a grade dentro de um pai que encolhe para
|
|
1134
|
+
caber (float, inline-block, item de flex com largura automática). Trocar um
|
|
1135
|
+
defeito de layout raro por um defeito de layout comum não vale. Consequência
|
|
1136
|
+
honesta: grade estreita dentro de janela larga divide mesmo assim — o escape é
|
|
1137
|
+
`--datagrid-detail-w`. */
|
|
1138
|
+
.datagrid-split { display:grid; gap:var(--space-3); align-items:start; }
|
|
1139
|
+
@media (min-width:768px) {
|
|
1140
|
+
.datagrid-split { grid-template-columns:1fr var(--datagrid-detail-w,20rem); }
|
|
1141
|
+
}
|
|
1142
|
+
.datagrid-detail { border:1px solid var(--border); border-radius:var(--radius-card); background:var(--card); padding:var(--space-4); }
|
|
1143
|
+
.datagrid-detail-head { display:flex; justify-content:flex-end; }
|
|
1144
|
+
|
|
1145
|
+
/* G2: a ação de conjunto fica à direita, acima da fila — perto do que ela comanda e
|
|
1146
|
+
fora do caminho de leitura da lista. */
|
|
1147
|
+
.file-queue-actions { display:flex; justify-content:flex-end; }
|
|
1148
|
+
|
|
1149
|
+
/* G3/G4: a miniatura ocupa o mesmo espaço do ícone que ela substitui — a linha da
|
|
1150
|
+
fila não muda de altura por ter prévia. `object-fit:cover` porque foto não é
|
|
1151
|
+
quadrada e deformar a prévia mente sobre o arquivo. */
|
|
1152
|
+
.file-thumb { flex:none; inline-size:var(--space-5); block-size:var(--space-5); border-radius:var(--radius-sm); object-fit:cover; }
|
|
1153
|
+
.file-checksum { color:var(--muted-foreground); font-family:var(--font-code); font-size:var(--text-xs); }
|
|
1154
|
+
|
|
1155
|
+
/* G5/G6: a escolha do conflito e o recibo. Os dois são superfície de card porque são
|
|
1156
|
+
blocos de decisão e de registro, não linhas da fila. */
|
|
1157
|
+
.file-conflict { display:flex; flex-wrap:wrap; align-items:center; gap:var(--space-2); padding:var(--space-3); border:1px solid var(--border); border-radius:var(--radius-card); background:var(--surface-2); }
|
|
1158
|
+
.file-receipt { padding:var(--space-3); border:1px solid var(--border); border-radius:var(--radius-card); background:var(--card); }
|
|
1159
|
+
.file-receipt-head { display:flex; align-items:center; justify-content:space-between; gap:var(--space-2); }
|
|
1160
|
+
.file-receipt ul { margin:0; padding-inline-start:var(--space-5); color:var(--muted-foreground); font-size:var(--text-sm); }
|
|
1161
|
+
|
|
1162
|
+
/* Parte H — a camada operacional. As três primeiras peças são COMPOSIÇÃO: `.card` já
|
|
1163
|
+
dá a superfície flutuante, `.status` já dá o estado, `.data-list` já dá o par
|
|
1164
|
+
termo/valor. O que falta é só o arranjo — e arranjo é o que não vem de graça. */
|
|
1165
|
+
.agent-card { display:grid; gap:var(--space-3); }
|
|
1166
|
+
.agent-card-head { display:flex; align-items:center; gap:var(--space-3); }
|
|
1167
|
+
.agent-card-id { display:grid; flex:1; min-inline-size:0; }
|
|
1168
|
+
.agent-card-desc { margin:0; color:var(--muted-foreground); }
|
|
1169
|
+
.agent-caps { display:flex; flex-wrap:wrap; gap:var(--space-2); margin:0; padding:0; list-style:none; }
|
|
1170
|
+
.agent-cap { display:inline-flex; align-items:center; gap:var(--space-1); padding:var(--space-1) var(--space-3); border:1px solid var(--border); border-radius:var(--radius-control); font-size:var(--text-sm); }
|
|
1171
|
+
.agent-card-actions { display:flex; justify-content:flex-end; gap:var(--space-2); }
|
|
1172
|
+
.agent-inspector { display:grid; gap:var(--space-4); }
|
|
1173
|
+
.agent-inspector-head { display:flex; align-items:center; justify-content:space-between; }
|
|
1174
|
+
.agent-inspector-section { display:grid; gap:var(--space-2); }
|
|
1175
|
+
.agent-inspector-label { margin:0; color:var(--subtle-foreground); font-size:var(--text-xs); letter-spacing:var(--tracking-wider); text-transform:uppercase; font-weight:var(--weight-bold); }
|
|
1176
|
+
|
|
1177
|
+
/* H.b — a execução. O passo dobrável é `<details>` nativo, então a marca de aberto/
|
|
1178
|
+
fechado já é do navegador; o que se acrescenta é o arranjo e o traço de estado. */
|
|
1179
|
+
.invocation { display:grid; gap:var(--space-4); }
|
|
1180
|
+
.invocation-head { display:flex; align-items:center; justify-content:space-between; gap:var(--space-3); }
|
|
1181
|
+
.invocation-io { display:grid; gap:var(--space-2); }
|
|
1182
|
+
.invocation-label { margin:0; color:var(--subtle-foreground); font-size:var(--text-xs); letter-spacing:var(--tracking-wider); text-transform:uppercase; font-weight:var(--weight-bold); }
|
|
1183
|
+
.invocation-steps { display:grid; gap:var(--space-2); margin:0; padding:0; list-style:none; }
|
|
1184
|
+
/* O estado do passo entra por BORDA lateral, não por cor de fundo: fundo colorido
|
|
1185
|
+
compete com o texto do passo, e a borda diz o mesmo sem disputar leitura. */
|
|
1186
|
+
.invocation-step { padding-inline-start:var(--space-3); border-inline-start:2px solid var(--border); }
|
|
1187
|
+
.invocation-step[data-state="running"] { border-inline-start-color:var(--primary); }
|
|
1188
|
+
.invocation-step[data-state="error"] { border-inline-start-color:var(--danger-400); }
|
|
1189
|
+
.invocation-step-body { padding-block-start:var(--space-2); }
|
|
1190
|
+
|
|
1191
|
+
.task-queue { display:grid; gap:var(--space-3); margin:0; padding:0; list-style:none; }
|
|
1192
|
+
.task-item { display:grid; gap:var(--space-2); padding:var(--space-3); border:1px solid var(--border); border-radius:var(--radius-card); background:var(--card); }
|
|
1193
|
+
.task-line { display:flex; align-items:center; gap:var(--space-2); }
|
|
1194
|
+
.task-title { flex:1; min-inline-size:0; font-weight:var(--weight-medium); }
|
|
1195
|
+
.task-desc { margin:0; color:var(--muted-foreground); font-size:var(--text-sm); }
|
|
1196
|
+
|
|
1197
|
+
/* H.c — governança da ação. */
|
|
1198
|
+
.approval { display:grid; gap:var(--space-3); }
|
|
1199
|
+
.approval-head { display:flex; align-items:center; justify-content:space-between; gap:var(--space-3); }
|
|
1200
|
+
.approval-desc, .approval-reason { margin:0; }
|
|
1201
|
+
.approval-reason { color:var(--muted-foreground); font-size:var(--text-sm); }
|
|
1202
|
+
/* Negar vem ANTES de aprovar na ordem de leitura e de tabulação: a ação destrutiva
|
|
1203
|
+
não deve ser a primeira a receber foco nem a mais fácil de acertar sem ler. */
|
|
1204
|
+
.approval-actions { display:flex; justify-content:flex-end; gap:var(--space-2); }
|
|
1205
|
+
.tool-permission-list { display:grid; gap:var(--space-3); margin:0; padding:0; list-style:none; }
|
|
1206
|
+
.tool-permission-item { display:flex; flex-wrap:wrap; align-items:center; justify-content:space-between; gap:var(--space-3); }
|
|
1207
|
+
.tool-permission-id { display:flex; flex-wrap:wrap; align-items:baseline; gap:var(--space-2); min-inline-size:0; }
|
|
1208
|
+
.tool-permission-scope { padding:0 var(--space-1); border-radius:var(--radius-sm); background:var(--surface-2); font-family:var(--font-code); font-size:var(--text-xs); }
|
|
1209
|
+
|
|
1210
|
+
/* H.d — a observação. */
|
|
1211
|
+
.event-stream { display:grid; gap:var(--space-2); }
|
|
1212
|
+
/* O feed é do GRUPO: `role="feed"` só aceita `article` como filho, então o rótulo do grupo
|
|
1213
|
+
mora FORA dele. Ver o comentário do EventStream — a regra é do ARIA, não de estilo. */
|
|
1214
|
+
.event-group-feed { display:grid; gap:var(--space-2); }
|
|
1215
|
+
.event-group { margin:var(--space-3) 0 0; color:var(--subtle-foreground); font-size:var(--text-xs); letter-spacing:var(--tracking-wider); text-transform:uppercase; font-weight:var(--weight-bold); }
|
|
1216
|
+
.event-item { display:flex; align-items:baseline; gap:var(--space-3); }
|
|
1217
|
+
.event-body { display:grid; flex:1; min-inline-size:0; }
|
|
1218
|
+
/* O ponto do evento é o MESMO tamanho e a mesma escala do ponto do Status: dois
|
|
1219
|
+
indicadores de gravidade com tamanhos diferentes na mesma tela leem como coisas
|
|
1220
|
+
diferentes, e não são. */
|
|
1221
|
+
.event-dot { flex:none; inline-size:var(--space-2); block-size:var(--space-2); border-radius:var(--radius-full); background:var(--muted-foreground); }
|
|
1222
|
+
.event-item[data-severity="success"] .event-dot { background:var(--success-400); }
|
|
1223
|
+
.event-item[data-severity="warning"] .event-dot { background:var(--warning-400); }
|
|
1224
|
+
.event-item[data-severity="danger"] .event-dot { background:var(--danger-400); }
|
|
1225
|
+
.event-time { flex:none; color:var(--muted-foreground); font-family:var(--font-code); font-size:var(--text-xs); }
|
|
1226
|
+
|
|
1227
|
+
.trace { display:grid; gap:var(--space-1); margin:0; padding:0; list-style:none; }
|
|
1228
|
+
/* A profundidade do span entra como RECUO, e por variável: a cascata precisa mostrar
|
|
1229
|
+
quem chamou quem, e recuar por nível é o que a referência faz sem precisar desenhar
|
|
1230
|
+
linhas de ligação. */
|
|
1231
|
+
/* O RÓTULO é quem cede, e a barra tem piso. A primeira versão era
|
|
1232
|
+
`minmax(6rem,14rem) 1fr`, e num painel estreito o rótulo comia tudo: medido no catálogo em
|
|
1233
|
+
09/08/2026, as colunas resolviam `224px 24px` e a cascata inteira desenhava em 24px — barras
|
|
1234
|
+
de 24, 5, 6 e **0** pixels. O `1fr` só recebe a sobra, e não havia sobra. Com o mínimo do
|
|
1235
|
+
lado da BARRA e o rótulo podendo encolher até zero, quem trunca é o texto, que já tem
|
|
1236
|
+
reticência para isso — e o dado, que é a barra, nunca desaparece. */
|
|
1237
|
+
.trace-span { display:grid; grid-template-columns:minmax(0,14rem) minmax(8rem,1fr); align-items:center; gap:var(--space-3); padding-inline-start:calc(var(--trace-depth,0) * var(--space-3)); }
|
|
1238
|
+
.trace-label { overflow:hidden; text-overflow:ellipsis; white-space:nowrap; font-size:var(--text-sm); }
|
|
1239
|
+
.trace-track { position:relative; block-size:var(--space-3); border-radius:var(--radius-full); background:var(--surface-2); }
|
|
1240
|
+
.trace-bar { position:absolute; inset-block:0; border-radius:var(--radius-full); background:var(--primary); }
|
|
1241
|
+
.trace-span[data-error] .trace-bar { background:var(--danger-400); }
|
|
1242
|
+
|
|
1243
|
+
.health-matrix { display:grid; grid-template-columns:repeat(auto-fill,minmax(12rem,1fr)); gap:var(--space-3); margin:0; padding:0; list-style:none; }
|
|
1244
|
+
.health-cell { display:grid; gap:var(--space-1); padding:var(--space-3); border:1px solid var(--border); border-radius:var(--radius-card); background:var(--card); }
|
|
1245
|
+
.health-name { font-weight:var(--weight-medium); }
|
|
1246
|
+
|
|
1247
|
+
/* H.e — custo e memória.
|
|
1248
|
+
O número total é o `TotalMetric` da referência, e ele é UMA linha de arranjo: a escala
|
|
1249
|
+
tipográfica da Aurea vai só até `--text-base`, e o `text-3xl` de lá é a identidade DELA.
|
|
1250
|
+
Aqui o destaque vem do peso e do contraste com o rótulo em `--text-xs`, não de um degrau
|
|
1251
|
+
de fonte que a escala não tem. Compartilhado pelo ModelUsage e pelo CostMeter porque é a
|
|
1252
|
+
mesma coisa nos dois — duas classes diriam que são duas. */
|
|
1253
|
+
.metric-total { display:flex; align-items:baseline; gap:var(--space-2); }
|
|
1254
|
+
.metric-total strong { font-size:var(--text-base); font-weight:var(--weight-bold); font-variant-numeric:tabular-nums; }
|
|
1255
|
+
|
|
1256
|
+
.model-usage { display:grid; gap:var(--space-3); }
|
|
1257
|
+
.model-usage-list { display:grid; gap:var(--space-2); margin:0; padding:0; list-style:none; }
|
|
1258
|
+
/* Mesma regra do `.trace-span`, e pelo mesmo defeito medido no mesmo dia: o mínimo fica do
|
|
1259
|
+
lado da BARRA, e o nome do modelo encolhe com reticência. Barra sem piso desaparece antes
|
|
1260
|
+
do texto, e é ela que carrega o dado. */
|
|
1261
|
+
.model-usage-row { display:grid; grid-template-columns:minmax(0,12rem) minmax(8rem,1fr) auto; align-items:center; gap:var(--space-3); }
|
|
1262
|
+
.model-usage-name { overflow:hidden; text-overflow:ellipsis; white-space:nowrap; font-size:var(--text-sm); }
|
|
1263
|
+
/* A trilha mede o MESMO que a do rastro: são os dois um `meter`, e dois medidores de
|
|
1264
|
+
alturas diferentes na mesma aplicação leem como coisas diferentes — não são. */
|
|
1265
|
+
.model-usage-track { position:relative; block-size:var(--space-3); border-radius:var(--radius-full); background:var(--surface-2); }
|
|
1266
|
+
.model-usage-bar { position:absolute; inset-block:0; inset-inline-start:0; border-radius:var(--radius-full); background:var(--primary); }
|
|
1267
|
+
.model-usage-value { font-size:var(--text-sm); font-variant-numeric:tabular-nums; }
|
|
1268
|
+
|
|
1269
|
+
.cost-meter { display:grid; gap:var(--space-3); }
|
|
1270
|
+
.cost-meter-track { position:relative; block-size:var(--space-3); border-radius:var(--radius-full); background:var(--surface-2); }
|
|
1271
|
+
.cost-meter-bar { position:absolute; inset-block:0; inset-inline-start:0; border-radius:var(--radius-full); background:var(--primary); }
|
|
1272
|
+
/* Estourar o teto muda a BARRA, e não o fundo do cartão: fundo colorido num cartão inteiro
|
|
1273
|
+
é o que a Aurea não faz, e a barra é justamente a peça que fala de quantidade.
|
|
1274
|
+
O limite BRANDO não tem cor, e isso foi MEDIDO, não escolhido por gosto: no tema escuro
|
|
1275
|
+
`--warning-400` É `var(--brand-yellow)`, o mesmo valor de `--primary`. Uma barra "de
|
|
1276
|
+
aviso" ali sairia idêntica à barra normal — degrau invisível justo em metade dos temas.
|
|
1277
|
+
O aviso do limite brando sai por PALAVRA, no Alert, que é o mesmo critério dos cinco
|
|
1278
|
+
estados do HealthMatrix. A cor fica para o limite rígido, onde o vermelho separa nos dois. */
|
|
1279
|
+
.cost-meter[data-state="over"] .cost-meter-bar { background:var(--danger-400); }
|
|
1280
|
+
|
|
1281
|
+
.memory-ledger { display:grid; gap:var(--space-2); margin:0; padding:0; list-style:none; }
|
|
1282
|
+
.memory-entry { display:grid; gap:var(--space-1); padding:var(--space-3); border:1px solid var(--border); border-radius:var(--radius-card); background:var(--card); }
|
|
1283
|
+
.memory-line { display:flex; flex-wrap:wrap; align-items:baseline; gap:var(--space-2); }
|
|
1284
|
+
.memory-content { flex:1; min-inline-size:0; }
|
|
1285
|
+
.memory-time { flex:none; color:var(--muted-foreground); font-family:var(--font-code); font-size:var(--text-xs); }
|
|
1286
|
+
/* Esquecida continua NA LISTA, riscada: o livro-razão registra o lançamento, não o estado
|
|
1287
|
+
final. Sumir com a linha apaga exatamente o que se procura quando o agente para de
|
|
1288
|
+
saber uma coisa. O risco não é a única marca — o selo ao lado diz "Forgotten" em palavra. */
|
|
1289
|
+
.memory-entry[data-operation="forgotten"] .memory-content { color:var(--muted-foreground); text-decoration:line-through; }
|
|
1290
|
+
.memory-provenance { font-size:var(--text-sm); }
|
|
1291
|
+
.memory-provenance summary { color:var(--subtle-foreground); font-size:var(--text-xs); letter-spacing:var(--tracking-wider); text-transform:uppercase; cursor:pointer; }
|
|
1292
|
+
.memory-source { margin:var(--space-2) 0 0; color:var(--muted-foreground); font-size:var(--text-sm); }
|
|
1293
|
+
|
|
1294
|
+
/* H.f — a relação. */
|
|
1295
|
+
.agent-messages { display:grid; gap:var(--space-2); margin:0; padding:0; list-style:none; }
|
|
1296
|
+
.agent-message { display:grid; gap:var(--space-1); padding:var(--space-3); border:1px solid var(--border); border-radius:var(--radius-card); background:var(--card); }
|
|
1297
|
+
/* O recado que FALHOU muda a borda, não o fundo: é o mesmo tratamento do passo do
|
|
1298
|
+
`InvocationPanel`, e pelo mesmo motivo — fundo colorido disputa leitura com o texto. */
|
|
1299
|
+
.agent-message[data-kind="error"] { border-color:var(--danger-400); }
|
|
1300
|
+
.agent-message-line { display:flex; flex-wrap:wrap; align-items:center; gap:var(--space-2); }
|
|
1301
|
+
.agent-message-route { display:inline-flex; align-items:center; gap:var(--space-1); flex:1; min-inline-size:0; }
|
|
1302
|
+
.agent-message-arrow { flex:none; color:var(--muted-foreground); }
|
|
1303
|
+
.agent-message-time { flex:none; color:var(--muted-foreground); font-family:var(--font-code); font-size:var(--text-xs); }
|
|
1304
|
+
.agent-message-body { margin:0; }
|
|
1305
|
+
.agent-message-why { font-size:var(--text-sm); }
|
|
1306
|
+
.agent-message-why summary { color:var(--subtle-foreground); font-size:var(--text-xs); letter-spacing:var(--tracking-wider); text-transform:uppercase; font-weight:var(--weight-bold); cursor:pointer; }
|
|
1307
|
+
.agent-message-reason { margin:var(--space-2) 0 0; color:var(--muted-foreground); font-size:var(--text-sm); }
|
|
1308
|
+
|
|
1309
|
+
.automation { display:grid; gap:var(--space-3); }
|
|
1310
|
+
.automation-head { display:flex; align-items:flex-start; justify-content:space-between; gap:var(--space-3); }
|
|
1311
|
+
.automation-id { display:grid; flex:1; min-inline-size:0; }
|
|
1312
|
+
/* Regra DESLIGADA não some e não muda de cor — ela recua. O interruptor ao lado já diz o
|
|
1313
|
+
estado em palavra e em teclado; apagar o cartão esconderia justamente a regra que alguém
|
|
1314
|
+
precisa achar para religar. */
|
|
1315
|
+
.automation:not([data-enabled]) .automation-id { color:var(--muted-foreground); }
|
|
1316
|
+
.automation-foot { display:flex; flex-wrap:wrap; align-items:center; gap:var(--space-3); }
|
|
1317
|
+
|
|
1318
|
+
/* H14 — o grafo. A pele do MOTOR entra por variável, e nenhuma classe dele aparece aqui:
|
|
1319
|
+
o `@xyflow/react` publica tudo como `var(--xy-algo, --xy-algo-default)`, e sobrescrever a
|
|
1320
|
+
variável na nossa raiz vale para toda a árvore dele (propriedade customizada herda). Escrever
|
|
1321
|
+
`.react-flow__*` no core resolveria igual e criaria uma classe que componente nenhum daqui
|
|
1322
|
+
produz — o check 15 existe exatamente para barrar isso. O consumidor importa o `base.css`
|
|
1323
|
+
do motor (estrutura); o `style.css` dele, que é a identidade deles, nunca entra. */
|
|
1324
|
+
/* `inline-size:100%` não é enfeite, é a mesma lição do Chart: painel de demo é flex, e uma
|
|
1325
|
+
caixa de largura automática entra como item que encolhe — medido no catálogo, a tela do grafo
|
|
1326
|
+
deu 71px e os nós saíram todos para fora dela. A tela ocupa o que lhe derem. */
|
|
1327
|
+
.dependency-graph { inline-size:100%; block-size:28rem; border:1px solid var(--border); border-radius:var(--radius-card); background:var(--card); overflow:hidden;
|
|
1328
|
+
--xy-background-color:transparent;
|
|
1329
|
+
--xy-background-pattern-color:var(--border);
|
|
1330
|
+
--xy-edge-stroke:var(--muted-foreground);
|
|
1331
|
+
--xy-edge-stroke-selected:var(--primary);
|
|
1332
|
+
--xy-edge-label-color:var(--muted-foreground);
|
|
1333
|
+
--xy-edge-label-background-color:var(--card);
|
|
1334
|
+
--xy-connectionline-stroke:var(--primary);
|
|
1335
|
+
--xy-attribution-background-color:transparent; }
|
|
1336
|
+
/* A atribuição do motor (que a licença dele manda manter, e mantemos) traz `color:#999`
|
|
1337
|
+
CRAVADO no `init.css` — o único ponto em que a folha "estrutural" carrega cor. Reprovou
|
|
1338
|
+
contraste no axe, tema claro. A regra é de ELEMENTO e vem ESCOPADA à nossa classe, que é a
|
|
1339
|
+
lição que o `table { min-width:720px }` do Lote 4 deixou: regra de elemento no core não tem
|
|
1340
|
+
gate, então escreva-a presa a alguma coisa nossa ou ela é global.
|
|
1341
|
+
E leva `[href]` para GANHAR de `.react-flow__attribution a`, que tem a MESMA especificidade
|
|
1342
|
+
e vem depois na folha do consumidor. Subir por atributo evita citar a classe deles aqui —
|
|
1343
|
+
classe de terceiro no nosso core é o que o check 15 barra, e com razão. */
|
|
1344
|
+
.dependency-graph a[href] { color:var(--muted-foreground); }
|
|
1345
|
+
/* O nó é uma superfície flutuante como qualquer outra da Aurea — mesmo raio de cartão, mesma
|
|
1346
|
+
borda, mesmo fundo. É aqui que ele deixa de parecer React Flow. */
|
|
1347
|
+
.graph-node { position:relative; inline-size:180px; }
|
|
1348
|
+
.graph-node-body { display:grid; gap:var(--space-05); inline-size:100%; padding:var(--space-2) var(--space-3); border:1px solid var(--border); border-radius:var(--radius-card); background:var(--card); color:var(--foreground); font-family:inherit; font-size:var(--text-sm); text-align:start; }
|
|
1349
|
+
button.graph-node-body { cursor:pointer; }
|
|
1350
|
+
.graph-node[data-selected] .graph-node-body { border-color:var(--primary); }
|
|
1351
|
+
.graph-node-label { overflow:hidden; text-overflow:ellipsis; white-space:nowrap; font-weight:var(--weight-medium); }
|
|
1352
|
+
/* `kind` é TEXTO, e não cor: a referência pinta dez tipos de nó em dez cores, e é a quarta vez
|
|
1353
|
+
que esta parte recusa a mesma coisa. */
|
|
1354
|
+
.graph-node-kind { color:var(--subtle-foreground); font-size:var(--text-xs); letter-spacing:var(--tracking-wider); text-transform:uppercase; }
|
|
1355
|
+
.graph-handle { inline-size:var(--space-2); block-size:var(--space-2); border:1px solid var(--border); background:var(--surface-2); }
|
|
1356
|
+
.datagrid tbody tr[data-selected] { background:color-mix(in srgb,var(--primary) 14%,transparent); }
|
|
1357
|
+
.datagrid-empty { padding:var(--space-6); color:var(--muted-foreground); text-align:center; }
|
|
1358
|
+
|
|
1359
|
+
/* ========================================================================== */
|
|
1360
|
+
/* 10. RESPONSIVIDADE */
|
|
1361
|
+
/* ========================================================================== */
|
|
1362
|
+
/* Aperto de tela baixa: é densidade do SHELL DOS DOCS (.doc-sidebar), não da .doc-nav em
|
|
1363
|
+
si — sem o escopo ela zerava o gap de qualquer nav, inclusive a do catálogo. */
|
|
1364
|
+
/* Legado do catálogo escrito à mão: `display:block` desmancha o grid e empilha a lateral.
|
|
1365
|
+
Agora vale SÓ para o shell que não tem gaveta — o `apps/docs/index.html`, que traz a
|
|
1366
|
+
própria `.doc-sidebar` e o próprio JS de abrir. O shell do sistema segue em grid de uma
|
|
1367
|
+
coluna com a gaveta acima. Sai junto com aquela página (achado M13). */
|
|
1368
|
+
@media (max-width:640px) { :root { --card-pad:16px; --section-gap:16px; } .doc-actions .select { flex:1; width:auto!important; min-width:0; } .doc-actions>.btn-primary { width:var(--control-h-md); padding:0; font-size:0; } .doc-actions>.btn-primary .mono { display:none; } .tabs,.pagination,.segmented { max-width:100%; overflow-x:auto; } .step:nth-child(3):before { display:none; } .alert { grid-template-columns:auto minmax(0,1fr); } .alert>:last-child { grid-column:2; justify-self:start; } .log-line { grid-template-columns:70px 58px minmax(0,1fr); gap:6px; } .queue-item>.badge { grid-column:2; justify-self:start; } .event-row>.badge { grid-column:3; justify-self:start; } .message { grid-template-columns:1fr; } .media-player { min-height:330px; aspect-ratio:auto; } .media-overlay-title { top:12px; left:12px; right:12px; } .media-controls { padding:10px; } .media-control-row { align-items:flex-start; flex-direction:column; gap:8px; } .media-control-row>.media-control-group { width:100%; justify-content:space-between; } .media-optional,.media-volume { display:none; } .media-time { padding:0 4px; font-size:11px; } .toast-stack { inset-inline:12px; bottom:12px; } .toast { width:100%; min-width:0; grid-template-columns:minmax(0,1fr) auto; } .drawer { width:100%; padding:20px; } .dialog { padding:20px; } }
|
|
1369
|
+
@media (prefers-reduced-motion:reduce) { *,*:before,*:after { scroll-behavior:auto!important; animation-duration:.01ms!important; animation-iteration-count:1!important; transition-duration:.01ms!important; } }
|
|
1370
|
+
|
|
1371
|
+
/* Padrões compostos Aurea 1.7: blocos de operação, não widgets de domínio. */
|
|
1372
|
+
/* Alvo de toque. `.tabs`/`.segmented` no lugar de `.tab`/`.segmented button`: agora a altura
|
|
1373
|
+
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); } }
|
|
1375
|
+
@media (forced-colors:active) {
|
|
1376
|
+
:focus-visible { outline:2px solid CanvasText; outline-offset:2px; box-shadow:none; }
|
|
1377
|
+
.badge,.pattern-gallery button[aria-pressed="true"],.device-grid button[aria-pressed="true"] { border:1px solid CanvasText; }
|
|
1378
|
+
}
|
|
1379
|
+
|
|
1380
|
+
|
|
1381
|
+
/* Aurea v1.7 stabilization patch — visual design unchanged. */
|
|
1382
|
+
.badge-running{background:var(--success-bg);color:var(--success-400);border-color:color-mix(in oklab,var(--success-400) 32%,transparent)}
|
|
1383
|
+
.table-region{overflow:auto;outline:none;border-radius:var(--radius-lg)}
|
|
1384
|
+
.table-region:focus-visible{box-shadow:0 0 0 3px color-mix(in oklab,var(--focus) 38%,transparent)}
|
|
1385
|
+
/* CONTAINER QUERY, e não breakpoint de viewport — Parte D, item D3 (08/08/2026).
|
|
1386
|
+
Era `@media (max-width:400px)`, o último dos seis legados do achado M13. Trocar por um degrau
|
|
1387
|
+
da escala (639px) teria resolvido o gate e mantido o defeito: a largura que aperta os
|
|
1388
|
+
controles é a do PLAYER, não a da janela. Um player numa lateral de 400px dentro de uma tela
|
|
1389
|
+
de 1920px tem exatamente o mesmo aperto, e o breakpoint de viewport nunca o via.
|
|
1390
|
+
`.media-player` já era `position:relative` + `overflow:hidden`, então a contenção não muda o
|
|
1391
|
+
posicionamento do que está dentro — medido antes de escrever. */
|
|
1392
|
+
.media-player { container-type:inline-size; }
|
|
1393
|
+
@container (max-width:400px) {
|
|
1394
|
+
.media-controls { flex-wrap:wrap; overflow:visible!important; gap:8px; }
|
|
1395
|
+
.media-control { min-width:var(--target-min); min-height:var(--target-min); }
|
|
1396
|
+
}
|
|
1397
|
+
@media(pointer:coarse){
|
|
1398
|
+
.media-control,.btn-icon{min-width:var(--target-min);min-height:var(--target-min)}
|
|
1399
|
+
}
|
|
1400
|
+
|
|
1401
|
+
/* ========================================================================== */
|
|
1402
|
+
/* 12. RTL */
|
|
1403
|
+
/* ========================================================================== */
|
|
1404
|
+
/* O eixo inline do CSS acima já é lógico e espelha sozinho. Aqui ficam só os
|
|
1405
|
+
casos sem forma lógica: background-position e translateX.
|
|
1406
|
+
Deliberadamente NÃO espelham:
|
|
1407
|
+
- o check do .checkbox e o .spinner são glifos desenhados com borda;
|
|
1408
|
+
- .map-* e .builder-* são geografia/arte com rotate() fixo — espelhar os
|
|
1409
|
+
nós sem as arestas quebraria o desenho;
|
|
1410
|
+
- .drawer-left/.drawer-right prometem lado físico no nome (prop side). */
|
|
1411
|
+
:dir(rtl) .select { background-position:12px 50%; }
|
|
1412
|
+
:dir(rtl) .switch input:checked + .switch-track:after { transform:translateX(-18px); }
|
|
952
1413
|
}
|