fokus-styles 2.3.0 → 2.5.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.
Files changed (101) hide show
  1. package/CHANGELOG.md +36 -0
  2. package/dist/css/components.css +583 -83
  3. package/dist/css/components.css.map +1 -1
  4. package/dist/css/components.min.css +1 -1
  5. package/dist/css/components.min.css.map +1 -1
  6. package/dist/css/fokus-components.css +583 -83
  7. package/dist/css/fokus-components.css.map +1 -1
  8. package/dist/css/fokus-components.min.css +1 -1
  9. package/dist/css/fokus-components.min.css.map +1 -1
  10. package/dist/css/fokus-core.css +22 -0
  11. package/dist/css/fokus-core.css.map +1 -1
  12. package/dist/css/fokus-core.min.css +1 -1
  13. package/dist/css/fokus-core.min.css.map +1 -1
  14. package/dist/css/fokus-dark.css +10 -0
  15. package/dist/css/fokus-dark.css.map +1 -1
  16. package/dist/css/fokus-dark.min.css +1 -1
  17. package/dist/css/fokus-dark.min.css.map +1 -1
  18. package/dist/css/fokus-rtl.css +9166 -0
  19. package/dist/css/fokus-rtl.css.map +1 -0
  20. package/dist/css/fokus-rtl.min.css +2 -0
  21. package/dist/css/fokus-rtl.min.css.map +1 -0
  22. package/dist/css/fokus-utilities.css +158 -0
  23. package/dist/css/fokus-utilities.css.map +1 -1
  24. package/dist/css/fokus-utilities.min.css +1 -1
  25. package/dist/css/fokus-utilities.min.css.map +1 -1
  26. package/dist/css/fokus.css +1018 -258
  27. package/dist/css/fokus.css.map +1 -1
  28. package/dist/css/fokus.min.css +1 -1
  29. package/dist/css/fokus.min.css.map +1 -1
  30. package/dist/css/forms.css +322 -176
  31. package/dist/css/forms.css.map +1 -1
  32. package/dist/css/forms.min.css +1 -1
  33. package/dist/css/forms.min.css.map +1 -1
  34. package/dist/css/helpers.css +158 -0
  35. package/dist/css/helpers.css.map +1 -1
  36. package/dist/css/helpers.min.css +1 -1
  37. package/dist/css/helpers.min.css.map +1 -1
  38. package/dist/css/layout.css +22 -0
  39. package/dist/css/layout.css.map +1 -1
  40. package/dist/css/layout.min.css +1 -1
  41. package/dist/css/layout.min.css.map +1 -1
  42. package/dist/js/fokus.js +270 -25
  43. package/dist/js/fokus.js.map +2 -2
  44. package/dist/js/fokus.min.js +18 -18
  45. package/dist/js/fokus.min.js.map +3 -3
  46. package/docs/components/alert.md +69 -24
  47. package/docs/components/badge.md +23 -4
  48. package/docs/components/checkbox.md +16 -3
  49. package/docs/components/popover.md +20 -7
  50. package/docs/components/radio.md +11 -1
  51. package/docs/components/responsive.md +19 -0
  52. package/docs/components/switch.md +30 -8
  53. package/docs/components/tag.md +19 -4
  54. package/docs/components/toast.md +72 -59
  55. package/docs/components/tooltip.md +38 -2
  56. package/docs/guides/accessibility.md +7 -0
  57. package/docs/guides/rtl-and-system-preferences.md +28 -0
  58. package/docs/prompts/prompt-plan.md +4 -4
  59. package/docs/reference/accessibility-matrix.md +7 -7
  60. package/docs/reference/contrast-report.md +4 -0
  61. package/docs/reference/definitions.md +5 -1
  62. package/docs/reference/design-tokens.md +14 -0
  63. package/docs/reference/size-baseline.json +13 -13
  64. package/mockup/assets/example-theme.css +11 -0
  65. package/mockup/assets/showcase-contracts.js +6 -6
  66. package/mockup/examples/alerts.html +105 -31
  67. package/mockup/examples/badges-alerts.html +91 -59
  68. package/mockup/examples/check-radio-switch.html +575 -99
  69. package/mockup/examples/popover.html +81 -0
  70. package/mockup/examples/tag.html +97 -87
  71. package/mockup/examples/toast.html +113 -0
  72. package/mockup/examples/tooltip.html +68 -0
  73. package/mockup/forms.html +1 -1
  74. package/mockup/overlays-commands.html +2 -2
  75. package/package.json +2 -1
  76. package/packages/fokus-components/scss/components/_alerts.scss +128 -6
  77. package/packages/fokus-components/scss/components/_badges.scss +26 -10
  78. package/packages/fokus-components/scss/components/_index.scss +1 -0
  79. package/packages/fokus-components/scss/components/_popover.scss +131 -21
  80. package/packages/fokus-components/scss/components/_responsive.scss +23 -0
  81. package/packages/fokus-components/scss/components/_tag.scss +37 -14
  82. package/packages/fokus-components/scss/components/_toasts.scss +156 -11
  83. package/packages/fokus-components/scss/components/_tooltips.scss +70 -18
  84. package/packages/fokus-components/scss/forms/_check-radio-switch.scss +313 -215
  85. package/packages/fokus-components/scss/forms/_forms.scss +16 -0
  86. package/packages/fokus-core/scss/base/_accessibility.scss +14 -0
  87. package/packages/fokus-core/scss/tokens/_root.scss +11 -0
  88. package/packages/fokus-js/js/carousel.d.ts +4 -0
  89. package/packages/fokus-js/js/carousel.js +8 -0
  90. package/packages/fokus-js/js/core/positioning.js +18 -9
  91. package/packages/fokus-js/js/dropdown.d.ts +4 -0
  92. package/packages/fokus-js/js/dropdown.js +7 -0
  93. package/packages/fokus-js/js/popover.js +26 -1
  94. package/packages/fokus-js/js/tabs.d.ts +3 -0
  95. package/packages/fokus-js/js/tabs.js +5 -0
  96. package/packages/fokus-js/js/tag.js +17 -0
  97. package/packages/fokus-js/js/toast.js +77 -3
  98. package/packages/fokus-js/js/tooltip.d.ts +8 -1
  99. package/packages/fokus-js/js/tooltip.js +106 -13
  100. package/packages/fokus-utilities/scss/utilities/_api.scss +48 -0
  101. package/scss/entries/rtl-entry.scss +11 -0
@@ -25,7 +25,13 @@ marcação manualmente — só o elemento de referência com `title`/`data-fs="t
25
25
  ## Variações
26
26
 
27
27
  `data-placement` (`top` — padrão, `bottom`, `left`, `right`) no elemento
28
- de referência. Reposiciona automaticamente se não couber.
28
+ de referência. Reposiciona automaticamente se não couber. A posição efetiva
29
+ fica em `data-placement` no Tooltip gerado.
30
+
31
+ Também são aceitos alinhamentos compostos, como `top-start`, `top-end`,
32
+ `bottom-start`, `bottom-end`, `left-start` e `right-end`. A forma manual
33
+ equivalente é `{ placement: "top", align: "start" }`. Use `data-offset` ou
34
+ `offset` para controlar a distância entre o gatilho e a dica.
29
35
 
30
36
  ```html
31
37
  <button class="fs-btn" data-fs="tooltip" data-placement="right" title="Mais opções">⋮</button>
@@ -36,6 +42,25 @@ de referência. Reposiciona automaticamente se não couber.
36
42
  `.is-open` — controlado pelo JS ao mostrar/esconder; não defina
37
43
  manualmente.
38
44
 
45
+ ### Delays e touch
46
+
47
+ Use `data-show-delay` e `data-hide-delay` para evitar abertura acidental:
48
+
49
+ ```html
50
+ <button data-fs="tooltip" data-show-delay="300" data-hide-delay="100"
51
+ title="Ajuda complementar">Ajuda</button>
52
+ ```
53
+
54
+ O Tooltip também responde a foco e ponteiro. Em touch, toque fora fecha a dica.
55
+ Escape fecha sem mover o foco. Tooltips concorrentes são fechados quando uma
56
+ nova dica é aberta.
57
+
58
+ Sem configuração, a abertura é imediata; use `data-show-delay` para evitar
59
+ aberturas acidentais em interações com ponteiro. O foco de teclado abre
60
+ imediatamente. Elementos `disabled` não
61
+ recebem foco nem eventos nativos: envolva-os em um elemento focável quando
62
+ precisar explicar seu estado.
63
+
39
64
  ## A11y
40
65
 
41
66
  O JS aplica `aria-describedby` no elemento de referência, apontando pro
@@ -68,10 +93,21 @@ de outro lugar, não de um atributo estático; é assim que
68
93
 
69
94
  `--fs-tooltip-bg`, `--fs-tooltip-text`, `--fs-radius-sm`.
70
95
 
96
+ O componente também expõe tokens locais como `--fs-tooltip-max-width`,
97
+ `--fs-tooltip-padding-block`, `--fs-tooltip-padding-inline`,
98
+ `--fs-tooltip-radius`, `--fs-tooltip-shadow` e `--fs-tooltip-arrow-size`.
99
+ Eles podem ser sobrescritos por instância sem alterar tokens globais.
100
+
71
101
  ## Exemplo
72
102
 
73
103
  ```html
74
104
  <button type="button" class="fs-btn-close" data-fs="tooltip" title="Fechar" aria-label="Fechar"></button>
75
105
  ```
76
106
 
77
- Mockup: [laboratório do componente](../../mockup/overlays-commands.html#tooltip).
107
+ O Tooltip não deve conter links ou botões e não substitui label, texto de erro
108
+ ou instrução essencial; nesses casos use conteúdo visível ou Popover.
109
+
110
+ Quando o gatilho já possui `aria-describedby`, o Tooltip acrescenta somente o
111
+ próprio ID e restaura a lista original ao executar `dispose()`.
112
+
113
+ Mockup: [laboratório independente do componente](../../mockup/examples/tooltip.html).
@@ -84,6 +84,13 @@ para que leitores de tela anunciem a mensagem ao focar o campo. O
84
84
  framework não injeta esse atributo automaticamente, porque o `id` do texto
85
85
  de apoio é definido por você.
86
86
 
87
+ Para Checkbox, Radio e Switch, mantenha o input nativo e associe o texto com
88
+ `for`/`id`. Opções relacionadas devem ficar em `fieldset` com `legend`.
89
+ Mensagens de ajuda e erro devem ser referenciadas por `aria-describedby`, e
90
+ um campo inválido pode usar `aria-invalid="true"` junto de `.is-invalid`.
91
+ Switch não recebe `role="switch"` automaticamente; a semântica oficial é a
92
+ do checkbox nativo, inclusive para a tecla `Space`.
93
+
87
94
  ## Testes automatizados
88
95
 
89
96
  A regressão visual (`npm run test:visual`, Playwright) cobre
@@ -0,0 +1,28 @@
1
+ # RTL e preferências do sistema
2
+
3
+ ## Visão geral
4
+
5
+ O Fokus Styles 2.4 mantém os componentes em fluxo lógico e oferece o preset
6
+ `fokus-styles/rtl.css` para documentos com `dir="rtl"`. O preset complementa
7
+ a folha principal e preserva `margin-inline`, `padding-inline`, `inset-inline`
8
+ e `border-inline-*`.
9
+
10
+ ```html
11
+ <html dir="rtl" data-fs-theme="auto">
12
+ <link rel="stylesheet" href="fokus-styles/dist/css/fokus.css">
13
+ <link rel="stylesheet" href="fokus-styles/dist/css/fokus-rtl.css">
14
+ </html>
15
+ ```
16
+
17
+ ## Estados de sistema
18
+
19
+ O framework respeita `prefers-reduced-motion`, `prefers-contrast: more`,
20
+ `forced-colors: active` e `prefers-color-scheme`. Em alto contraste, bordas e
21
+ foco passam a usar espessuras maiores; em forced colors, ações preservam
22
+ `ButtonText` e o foco usa `Highlight`.
23
+
24
+ ## A11y
25
+
26
+ O atributo `dir` deve ser aplicado ao documento ou ao contêiner que define o
27
+ fluxo de leitura. Use `dir="auto"` quando a direção do texto inserido não for
28
+ conhecida.
@@ -5,12 +5,12 @@ Copie o texto abaixo e substitua os campos entre colchetes antes de usar.
5
5
  ```text
6
6
  Atue como responsável pelo planejamento técnico e de design do Fokus Styles.
7
7
 
8
- Quero criar um plano, sem implementar ainda, para o componente **[NOME DO COMPONENTE]**.
8
+ Quero criar um plano, sem implementar ainda, para o componente abaixo:
9
9
 
10
- ## Contexto do pedido
10
+ ## Contexto do pedido 8011827-28.2026
11
11
 
12
- - Objetivo: [REFINAMENTO VISUAL DO COMPONENTE CAROUSEL;
13
- Sugira correções, alterações e implementações de códigos, funcionalidades de mudanças de visual para algo com maior qualidade e usabilidade]
12
+ - Objetivo: [REFINAMENTO VISUAL DO COMPONENTE TOOLTIP;
13
+ Sugira correções, alterações e implementações de códigos, funcionalidades de mudanças de visual para algo com maior qualidade, responsividade, acessibilidade, criatividade e usabilidade]
14
14
 
15
15
  Antes de propor o plano, investigue o repositório e use a documentação como fonte de verdade. Não presuma que o componente está isolado: determine suas dependências, impactos visuais, comportamentais, públicos e de manutenção.
16
16
 
@@ -19,13 +19,13 @@ renderizado de fato nos mockups (via o gate axe).
19
19
  |---|---|---|---|---|---|
20
20
  | [Accordion](../components/accordion.md) | Nativo (`<button>`) | Automático | — | ✓ | Sim |
21
21
  | [Alert Dialog](../components/alert-dialog.md) | Herdado do Modal | Herdado do Modal | Focus trap + devolve ao gatilho | ✓ | Sim |
22
- | [Alert](../components/alert.md) | N/A | Manual (`role="alert"`/`"status"`) | | ✓ | Sim |
22
+ | [Alert](../components/alert.md) | Nativo quando houver link/botão | Manual (`role="alert"`/`"status"`) | Nenhuma automática | ✓ | Sim |
23
23
  | [Badge](../components/badge.md) | N/A (decorativo) | N/A | — | ✓ | Sim |
24
24
  | [Breadcrumb](../components/breadcrumb.md) | Nativo (links) | Manual (`<nav aria-label>`) | — | ✓ | Sim |
25
25
  | [Button](../components/button.md) | Nativo | Manual (`aria-label` em só-ícone) | Foco visível padrão | ✓ | Sim |
26
26
  | [Card](../components/card.md) | Nativo (stretched-link) | — | Foco no link real | ✓ | Sim |
27
27
  | [Carousel](../components/carousel.md) | Automático (setas/Home/End e arraste) | Automático (`aria-hidden`, `aria-current`, toggle) | Recebe foco no contêiner; autoplay pausável | ✓ | Sim |
28
- | [Checkbox](../components/checkbox.md) | Nativo | Nativo | | ✓ | Sim |
28
+ | [Checkbox](../components/checkbox.md) | Nativo (Space) | Nativo + `aria-describedby` manual | Foco visível no controle | ✓ | Sim |
29
29
  | [Collapse](../components/collapse.md) | Nativo (gatilho) | Automático | — | ✓ | Sim |
30
30
  | [Divider](../components/divider.md) | N/A | Implícito (`<hr>`) | — | ✓ | Sim |
31
31
  | [Dropdown](../components/dropdown.md) | Automático (setas/Escape) | Automático | Primeiro item ao abrir; devolve ao gatilho | ✓ | Sim |
@@ -41,20 +41,20 @@ renderizado de fato nos mockups (via o gate axe).
41
41
  | [Pagination](../components/pagination.md) | Nativo (links) | Manual (`<nav aria-label>`, `aria-current`) | — | ✓ | Sim |
42
42
  | [Popover](../components/popover.md) | Automático (Escape) | Automático | Devolve ao gatilho | ✓ | Sim |
43
43
  | [Spinner / Progress](../components/progress.md) | N/A | Manual (`role`+`aria-label`/`aria-valuenow`) | — | ✓ | Sim |
44
- | [Radio](../components/radio.md) | Nativo (setas no grupo) | Nativo | | ✓ | Sim |
44
+ | [Radio](../components/radio.md) | Nativo (setas no grupo) | Nativo + `fieldset/legend` manual | Foco visível no controle | ✓ | Sim |
45
45
  | [Rating](../components/rating.md) | Nativo (setas no grupo) | Manual (`aria-label` por estrela) | — | ✓ | Sim |
46
46
  | [Segmented Control](../components/segmented-control.md) | Nativo (setas no grupo) | Nativo | — | ✓ | Sim |
47
47
  | [Select (custom)](../components/select.md) | Herdado do Dropdown | Automático (`role="listbox"`) | Herdado do Dropdown | ✓ | Sim |
48
48
  | [Skeleton](../components/skeleton.md) | N/A | Manual (`aria-busy` opcional) | — | ✓ | Sim |
49
49
  | [Stepper](../components/stepper.md) | Automático (passos clicáveis) | Automático (`aria-current="step"`) | — | ✓ | Sim |
50
- | [Switch](../components/switch.md) | Nativo | Nativo (+ `aria-label` sem texto visível) | | ✓ | Sim |
50
+ | [Switch](../components/switch.md) | Nativo (Space) | Checkbox nativo; nome manual sem texto | Foco visível no trilho | ✓ | Sim |
51
51
  | [Table](../components/table.md) | Nativo | Manual (`scope`, `<caption>`) | — | ✓ | Sim |
52
52
  | [Tabs](../components/tabs.md) | Automático (roving tabindex) | Automático (`role="tab"`/`"tabpanel"`) | — | ✓ | Sim |
53
- | [Tag](../components/tag.md) | Nativo (botão de fechar) | Manual (`aria-label` descritivo) | | ✓ | Sim |
53
+ | [Tag](../components/tag.md) | Nativo (botão de fechar) | Manual (`aria-label` descritivo, `aria-busy`) | Foco preservado/transferido após dismiss | ✓ | Sim |
54
54
  | [Tile](../components/tile.md) | Nativo (link real) | — | Foco no link, não no `::after` | ✓ | Sim |
55
55
  | [Timeline](../components/timeline.md) | N/A (sem interação própria) | — | — | ✓ | Sim |
56
- | [Toast](../components/toast.md) | N/A | Automático (`role="status"`, `aria-live`) | | ✓ | Sim |
57
- | [Tooltip](../components/tooltip.md) | Nativo (`focus`/`blur`) | Automático (`aria-describedby`) | | ✓ | Sim |
56
+ | [Toast](../components/toast.md) | Nativo em fechamento e ações | Automático (`role="status"`, `aria-live="polite"`) | Nenhuma automática; pausa em foco/hover | ✓ | Sim |
57
+ | [Tooltip](../components/tooltip.md) | Nativo (`focus`/`blur`, ponteiro e Escape) | Automático (`role="tooltip"`, `aria-describedby`) | Nenhuma; foco permanece no gatilho | ✓ | Sim |
58
58
 
59
59
  ## Legenda
60
60
 
@@ -20,6 +20,10 @@ e AAA:
20
20
  para o texto de botões sólidos, que é sempre bold/UI, não corpo de leitura).
21
21
  - **7:1** — AAA, opcional.
22
22
 
23
+ Checkbox, Radio e Switch reutilizam esses tokens semânticos para bordas,
24
+ superfícies, estados de ação e validação. O relatório visual dos laboratórios
25
+ também deve ser executado após qualquer ajuste nos tokens locais dos controles.
26
+
23
27
  `npm run contrast -- --strict` sai com código de erro se algum par ficar
24
28
  abaixo do mínimo AA — é o comando rodado pelo gate `contrast:check` do CI
25
29
  (`.github/workflows/ci.yml`).
@@ -527,7 +527,7 @@ abas habilitadas (`role="tablist"`/`"tab"`/`"tabpanel"`, `aria-selected`,
527
527
  `tabindex` roving), disparando `fs:tab:changed`.
528
528
 
529
529
  **Toast** (`.fs-toast-container`/`.fs-toast`/`.fs-toast-header`/`.fs-toast-body`,
530
- variantes de cor de estado via `.fs-toast-#{nome}`,
530
+ slots opcionais de ícone, ações e progresso, variantes de cor de estado via `.fs-toast-#{nome}`,
531
531
  `packages/fokus-components/scss/components/_toasts.scss`): `packages/fokus-js/js/toast.js` usa `expand()`/`collapse()`
532
532
  para mostrar/esconder, com timer de auto-dismiss configurável (`data-delay`,
533
533
  `data-autohide="false"` para desativar) e dismiss via
@@ -810,6 +810,10 @@ Tag removível (`.fs-tag`, `packages/fokus-components/scss/components/_tag.scss`
810
810
  tamanho do botão de fechar (14×14px) para caber num badge. Precisa de
811
811
  JavaScript, mas de forma mínima:
812
812
  `packages/fokus-js/js/tag.js` (`FokusStyles.Tag`) só ouve o clique em `[data-fs-dismiss="tag"]`.
813
+ Tags suportam ícones decorativos iniciais ou de estado finais em
814
+ `.fs-tag-icon`/`.fs-tag-icon-end`; esses ícones não substituem o texto
815
+ acessível. Quando o botão focado remove uma Tag de um grupo, o foco é
816
+ transferido para a próxima Tag ou, na ausência dela, para a anterior.
813
817
  Antes de remover o elemento do DOM, dispara o evento **cancelável**
814
818
  `fs:tag:dismissed` (mesmo espírito de `fs:stepper:beforechange`) —
815
819
  `preventDefault()` bloqueia a remoção.
@@ -88,6 +88,20 @@ legendas possam ser ajustados por instância: `--fs-carousel-radius`,
88
88
  `--fs-carousel-caption-scrim`. Consulte a [página do Carousel](../components/carousel.md)
89
89
  para os nomes completos e seus fallbacks.
90
90
 
91
+ ### Check, Radio e Switch
92
+
93
+ Os controles nativos de seleção expõem tokens locais, sempre com fallback
94
+ para a escala semântica do framework:
95
+
96
+ | Família | Tokens |
97
+ |---|---|
98
+ | Checkbox | `--fs-check-size`, `--fs-check-border-width`, `--fs-check-radius`, `--fs-check-gap`, `--fs-check-mark-color` |
99
+ | Radio | `--fs-radio-size`, `--fs-radio-dot-size`, `--fs-radio-radius`, `--fs-radio-gap` |
100
+ | Switch | `--fs-switch-track-width`, `--fs-switch-track-height`, `--fs-switch-thumb-width`, `--fs-switch-thumb-height`, `--fs-switch-thumb-offset`, `--fs-switch-on-offset`, `--fs-switch-radius`, `--fs-switch-thumb-radius`, `--fs-switch-gap`, `--fs-switch-track-bg`, `--fs-switch-track-border`, `--fs-switch-track-checked-bg`, `--fs-switch-thumb-bg`, `--fs-switch-thumb-border`, `--fs-switch-thumb-shadow`, `--fs-switch-track-shadow`, `--fs-switch-hover-ring`, `--fs-switch-hover-shadow`, `--fs-switch-active-scale` |
101
+
102
+ Eles podem ser sobrescritos no wrapper do controle sem `!important`. As
103
+ variantes `sm` e `lg` alteram os valores desses tokens.
104
+
91
105
  ## Tipografia
92
106
 
93
107
  | Token | Valor | Uso |
@@ -1,34 +1,34 @@
1
1
  {
2
- "measuredAt": "2026-09-07T22:40:15.289Z",
2
+ "measuredAt": "2026-09-08T15:45:28.076Z",
3
3
  "unit": "bytes",
4
4
  "files": {
5
5
  "css/layout.min.css": {
6
- "bytes": 28539,
7
- "gzipBytes": 5951
6
+ "bytes": 28993,
7
+ "gzipBytes": 6049
8
8
  },
9
9
  "css/forms.min.css": {
10
- "bytes": 33746,
11
- "gzipBytes": 6986
10
+ "bytes": 40484,
11
+ "gzipBytes": 7974
12
12
  },
13
13
  "css/components.min.css": {
14
- "bytes": 92904,
15
- "gzipBytes": 16191
14
+ "bytes": 104877,
15
+ "gzipBytes": 18264
16
16
  },
17
17
  "css/helpers.min.css": {
18
- "bytes": 61032,
19
- "gzipBytes": 10609
18
+ "bytes": 63446,
19
+ "gzipBytes": 11044
20
20
  },
21
21
  "css/fonts.min.css": {
22
22
  "bytes": 1505,
23
23
  "gzipBytes": 409
24
24
  },
25
25
  "css/fokus.min.css": {
26
- "bytes": 164376,
27
- "gzipBytes": 25875
26
+ "bytes": 184593,
27
+ "gzipBytes": 28935
28
28
  },
29
29
  "js/fokus.min.js": {
30
- "bytes": 107627,
31
- "gzipBytes": 23063
30
+ "bytes": 115015,
31
+ "gzipBytes": 24623
32
32
  }
33
33
  }
34
34
  }
@@ -6,3 +6,14 @@ html[data-fs-theme="dark"] body {
6
6
  color: var(--fs-color-text);
7
7
  background-color: var(--fs-color-surface);
8
8
  }
9
+
10
+ /* Textos de contrato aparecem sobre superfícies escuras nos laboratórios. */
11
+ html[data-theme="dark"] :is(.demo-eyebrow, .showcase-eyebrow),
12
+ html[data-fs-theme="dark"] :is(.demo-eyebrow, .showcase-eyebrow) {
13
+ color: var(--fs-alert-primary-text);
14
+ }
15
+
16
+ html[data-theme="dark"] .demo-reference code,
17
+ html[data-fs-theme="dark"] .demo-reference code {
18
+ color: var(--fs-alert-primary-text);
19
+ }
@@ -43,9 +43,9 @@ window.showcaseContracts = {
43
43
  },
44
44
  alert: {
45
45
  use: [{ label: "Feedback contextual", text: "Use para sucesso, aviso, erro ou informação persistente relacionada à região afetada." }],
46
- api: [{ label: ".fs-alert", code: true, text: "contêiner-base da mensagem." }, { label: ".fs-alert-{primary|secondary|success|warning|danger|info}", code: true, text: "seleciona intenção visual." }, { label: ".fs-alert-heading / .fs-alert-link", code: true, text: "destacam título e ação contextual." }],
47
- a11y: [{ label: "Urgência", text: "use role=alert somente para mensagem que precisa ser anunciada imediatamente." }],
48
- states: [{ label: "Conteúdo", text: "teste mensagens com link, título, múltiplas linhas e ação de fechamento." }],
46
+ api: [{ label: ".fs-alert", code: true, text: "contêiner-base da mensagem persistente." }, { label: ".fs-alert-{primary|secondary|success|warning|danger|info}", code: true, text: "seleciona intenção visual." }, { label: ".fs-alert-group / .fs-alert-inline", code: true, text: "organizam mensagens relacionadas e versões compactas." }, { label: ".fs-alert-icon / -content / -heading / -text", code: true, text: "organizam ícone, título e descrição opcionais." }, { label: ".fs-alert-actions / -link / -close", code: true, text: "compõem ações, link contextual e fechamento visual manual." }],
47
+ a11y: [{ label: "Urgência", text: "use role=alert somente para mensagem urgente e role=status para atualização calma; o componente não injeta ARIA nem move foco." }],
48
+ states: [{ label: "Conteúdo", text: "teste grupo, inline, loading com aria-busy, ícone decorativo, ações, fechamento manual, forced colors e telas estreitas." }],
49
49
  },
50
50
  input: { use: [{ label: "Dado curto", text: "Use para texto, e-mail, senha ou número quando a pessoa deve digitar um valor identificável." }], api: [{ label: ".fs-form-control", code: true, text: "estiliza input e textarea nativos." }, { label: ".is-valid / .is-invalid", code: true, text: "aplicam feedback visual de validação." }, { label: "disabled / readonly / aria-describedby", code: true, text: "controlam disponibilidade, edição e associação com ajuda ou erro." }], a11y: [{ label: "Rótulo", text: "associe label ao id; placeholder não substitui o nome persistente." }, { label: "Erro", text: "conecte ajuda e mensagem de erro com aria-describedby." }], states: [{ label: "Validação", text: "teste foco, readonly, disabled, sucesso e erro com mensagem longa." }] },
51
51
  select: { use: [{ label: "Opções conhecidas", text: "Use select nativo para listas curtas; use Combobox quando a busca reduzir esforço." }], api: [{ label: ".fs-select", code: true, text: "aplica acabamento FokusStyles ao select nativo." }, { label: "required / disabled / multiple", code: true, text: "mantêm comportamento nativo de obrigatoriedade, indisponibilidade e múltipla escolha." }], a11y: [{ label: "Nome", text: "forneça label visível e não simule select com div sem necessidade." }], states: [{ label: "Opções", text: "teste placeholder, selecionado, disabled, erro e teclado nativo." }] },
@@ -72,13 +72,13 @@ window.showcaseContracts = {
72
72
  "nested-menu": { use: [{ label: "Navegação hierárquica", text: "Use para poucos níveis de destinos relacionados; não esconda arquitetura complexa em menus profundos." }], api: [{ label: ".fs-nested-menu + data-fs=\"nested-menu\"", code: true, text: "raiz e auto-init." }, { label: "[data-fs-nested-menu-toggle] / [data-fs-nested-menu-submenu]", code: true, text: "identificam gatilho e submenu." }], a11y: [{ label: "Expansão", text: "o toggle precisa de nome e aria-expanded atualizado; preserve Escape e foco." }], states: [{ label: "Subnível", text: "teste aberto, fechado, item ativo, teclado e clique externo." }] },
73
73
  "tree-view": { use: [{ label: "Estrutura aninhada", text: "Use para arquivos, permissões ou categorias com relação pai-filho explícita." }], api: [{ label: ".fs-tree / .fs-tree-item", code: true, text: "lista e nó da árvore." }, { label: ".fs-tree-toggle / .fs-tree-children", code: true, text: "controle de expansão e grupo filho." }], a11y: [{ label: "Hierarquia", text: "mantenha rótulos, níveis e estado expandido disponíveis; controles precisam de nome." }], states: [{ label: "Nó", text: "teste expandido, recolhido, selecionado, foco e nomes longos." }] },
74
74
  dropdown: { use: [{ label: "Ações contextuais", text: "Use para agrupar poucas ações secundárias ligadas a um gatilho." }], api: [{ label: "data-fs=\"dropdown\"", code: true, text: "inicializa o grupo." }, { label: "[data-fs-dropdown-toggle] / .fs-dropdown-menu", code: true, text: "gatilho e menu." }], a11y: [{ label: "Foco", text: "aria-expanded, Escape e retorno de foco são geridos; cada item precisa de texto claro." }], states: [{ label: "Menu", text: "teste aberto, fechado, teclado, borda de viewport e disabled." }] },
75
- tooltip: { use: [{ label: "Dica curta", text: "Use para explicar ícone ou termo; não esconda instrução necessária exclusivamente no hover." }], api: [{ label: "data-fs=\"tooltip\" + title", code: true, text: "inicializa e fornece o texto da dica." }, { label: "data-fs-tooltip-placement", code: true, text: "define top, right, bottom ou left." }], a11y: [{ label: "Alternativa", text: "a dica deve aparecer por foco e não ser a única fonte de informação essencial." }], states: [{ label: "Posição", text: "teste hover, foco, Escape, viewport e conteúdo longo." }] },
75
+ tooltip: { use: [{ label: "Dica curta", text: "Use para explicar ícone ou termo; não esconda instrução necessária exclusivamente no hover." }], api: [{ label: "data-fs=\"tooltip\" + title/data-title", code: true, text: "inicializa e fornece o texto da dica." }, { label: "data-placement + data-show-delay/data-hide-delay", code: true, text: "define posição e atrasos opcionais." }], a11y: [{ label: "Alternativa", text: "a dica aparece por foco, usa aria-describedby e não é a única fonte de informação essencial." }], states: [{ label: "Posição", text: "teste hover, foco, Escape, touch, fallback, RTL, viewport e conteúdo longo." }] },
76
76
  "hover-card": { use: [{ label: "Prévia rica", text: "Use para contexto adicional associado a link ou botão, sem interromper a tarefa principal." }], api: [{ label: "data-fs=\"hover-card\"", code: true, text: "inicializa o gatilho." }, { label: "data-fs-hover-card-content / -placement", code: true, text: "define conteúdo e posição." }], a11y: [{ label: "Foco", text: "o conteúdo deve poder ser alcançado por foco quando contiver ações." }], states: [{ label: "Sobreposição", text: "teste hover, foco, atraso, Escape e limite da viewport." }] },
77
- popover: { use: [{ label: "Informação ou ação curta", text: "Use para conteúdo acionado explicitamente; prefira Modal para tarefa extensa." }], api: [{ label: "data-fs=\"popover\"", code: true, text: "inicializa gatilho." }, { label: "data-fs-popover-content / -placement", code: true, text: "conteúdo e posição." }], a11y: [{ label: "Controle", text: "botão precisa de nome e aria-expanded; preserve fechamento por Escape." }], states: [{ label: "Visibilidade", text: "teste aberto, fechado, foco interno e clique externo." }] },
77
+ popover: { use: [{ label: "Informação ou ação curta", text: "Use para conteúdo acionado explicitamente; prefira Modal para tarefa extensa." }], api: [{ label: "data-fs=\"popover\" / data-fs-target", code: true, text: "inicializa o gatilho e associa o painel." }, { label: "data-trigger / data-placement / data-align", code: true, text: "configuram interação, posição e alinhamento." }, { label: ".fs-popover-icon / -heading / -text / -close", code: true, text: "slots opcionais para conteúdo rico." }], a11y: [{ label: "Controle", text: "aria-expanded e aria-controls acompanham o gatilho; Escape devolve foco e Popover não cria focus trap." }], states: [{ label: "Visibilidade", text: "teste aberto, fechado, foco interno, clique externo, hover, fallback de viewport e RTL." }] },
78
78
  offcanvas: { use: [{ label: "Painel lateral", text: "Use para navegação, filtros ou contexto secundário sem abandonar a página." }], api: [{ label: "data-fs=\"offcanvas\" + data-fs-target=\"#id\"", code: true, text: "gatilho e painel." }, { label: ".fs-offcanvas / .fs-offcanvas-start|end", code: true, text: "estrutura e lado de entrada." }], a11y: [{ label: "Diálogo", text: "foco é contido no painel e Escape o fecha; título deve nomear o contexto." }], states: [{ label: "Painel", text: "teste aberto, backdrop, Escape, foco e viewport estreito." }] },
79
79
  modal: { use: [{ label: "Tarefa concentrada", text: "Use para confirmação, formulário curto ou detalhe que exige atenção temporária." }], api: [{ label: "data-fs=\"modal\" + data-fs-target=\"#id\"", code: true, text: "gatilho e diálogo." }, { label: ".fs-modal / .fs-modal-dialog / .fs-modal-content", code: true, text: "camadas estruturais." }], a11y: [{ label: "Foco", text: "role=dialog, aria-modal, título e retorno de foco são essenciais." }], states: [{ label: "Diálogo", text: "teste abrir, fechar, backdrop, Escape, formulário e conteúdo alto." }] },
80
80
  "alert-dialog": { use: [{ label: "Confirmação crítica", text: "Use antes de ação destrutiva ou irreversível; mantenha a consequência explícita." }], api: [{ label: "data-fs=\"alert-dialog\"", code: true, text: "inicializa o diálogo de alerta." }, { label: "data-fs-alert-dialog-confirm", code: true, text: "identifica a ação de confirmação." }], a11y: [{ label: "Urgência", text: "use role=alertdialog, texto descritivo e foco inicial na opção segura." }], states: [{ label: "Decisão", text: "teste confirmar, cancelar, Escape quando permitido e foco restaurado." }] },
81
81
  "command-palette": { use: [{ label: "Atalho de comandos", text: "Use para navegação e ações frequentes de usuários avançados; mantenha alternativas visíveis." }], api: [{ label: "data-fs=\"command-palette\"", code: true, text: "inicializa gatilho e paleta." }, { label: "data-fs-command-palette-input / -list", code: true, text: "identificam busca e resultados." }], a11y: [{ label: "Diálogo", text: "foco, Escape, navegação por setas e anúncio de resultados devem ser preservados." }], states: [{ label: "Busca", text: "teste fechada, aberta, filtrada, vazia, teclado e comandos desabilitados." }] },
82
- toast: { use: [{ label: "Feedback transitório", text: "Use para confirmação breve que não exige decisão imediata; erros críticos devem persistir em contexto." }], api: [{ label: ".fs-toast-container / .fs-toast", code: true, text: "região empilhada e mensagem." }, { label: "data-fs=\"toast\" / data-autohide", code: true, text: "inicializa e configura fechamento automático." }], a11y: [{ label: "Anúncio", text: "use região live apropriada e não mova foco para feedback não crítico." }], states: [{ label: "Tempo", text: "teste entrada, auto-fechamento, pausa por hover quando houver e fechamento manual." }] },
82
+ toast: { use: [{ label: "Feedback transitório", text: "Use para confirmação breve que não exige decisão imediata; erros críticos devem persistir em contexto." }], api: [{ label: ".fs-toast-container / .fs-toast", code: true, text: "região fixa, empilhada e mensagem flutuante." }, { label: ".fs-toast-icon / -heading / -actions / -progress", code: true, text: "slots opcionais para ícone, título, ações e indicador de tempo." }, { label: "data-fs=\"toast\" / data-delay / data-autohide / data-toast-progress", code: true, text: "inicializam e configuram duração, persistência e progresso." }], a11y: [{ label: "Anúncio", text: "role=status e aria-live=polite são aplicados automaticamente; foco não é movido e hover/foco pausam o timer." }], states: [{ label: "Tempo", text: "teste entrada, auto-dismiss, pausa por hover/foco, progresso, empilhamento, RTL e fechamento manual." }] },
83
83
  "notification-center": { use: [{ label: "Histórico de notificações", text: "Use quando mensagens precisam ser revisitadas, filtradas ou marcadas como lidas." }], api: [{ label: "data-fs=\"notification-center\"", code: true, text: "inicializa o painel." }, { label: "data-fs-notification-toggle / -list", code: true, text: "gatilho e lista de notificações." }], a11y: [{ label: "Contagem", text: "o botão deve expor contagem e estado expandido; itens precisam de ações nomeadas." }], states: [{ label: "Lista", text: "teste vazia, não lida, lida, aberta, teclado e marcação em lote." }] },
84
84
  };
@@ -1,41 +1,115 @@
1
1
  <!doctype html>
2
2
  <html lang="pt-BR">
3
3
  <head>
4
- <meta charset="utf-8">
5
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>Mockup - Alerts</title>
7
- <link rel="stylesheet" href="../../dist/css/fonts.css">
8
- <link rel="stylesheet" href="../../dist/css/components.css">
9
- <link rel="stylesheet" href="../../dist/css/helpers.css">
10
- <style>
11
- .theme-panel { padding: 24px; }
12
-
13
- html[data-theme="dark"] .theme-panel {
14
- color: var(--fs-color-text);
15
- background-color: var(--fs-color-surface);
16
- }
17
-
18
- .fs-alert { max-width: 480px; }
19
- </style>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Fokus Styles Alerts</title>
7
+ <link rel="stylesheet" href="../../dist/css/fonts.css">
8
+ <link rel="stylesheet" href="../../dist/css/components.css">
9
+ <link rel="stylesheet" href="../../dist/css/helpers.css">
20
10
  <link rel="stylesheet" href="../assets/example-theme.css">
21
11
  <script src="../assets/example-theme.js"></script>
12
+ <style>
13
+ :root { --demo-max-width: 1080px; --demo-panel-bg: var(--fs-color-surface); --demo-soft-bg: var(--fs-color-subtle); --demo-panel-border: var(--fs-color-border); --demo-accent: var(--fs-color-primary); }
14
+ * { box-sizing: border-box; }
15
+ body { min-block-size: 100vh; margin: 0; color: var(--fs-color-text); background: var(--fs-color-bg-subtle); }
16
+ .demo-page { max-inline-size: var(--demo-max-width); margin-inline: auto; padding: clamp(20px, 4vw, 56px) clamp(16px, 4vw, 40px) 64px; }
17
+ .demo-hero { display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: end; gap: 24px; margin-block-end: 32px; }
18
+ .demo-eyebrow { margin: 0 0 8px; color: var(--demo-accent); font-size: .75rem; font-weight: 700; letter-spacing: .08em; text-transform: uppercase; }
19
+ html[data-fs-theme="dark"] .demo-eyebrow { color: var(--fs-color-text); }
20
+ .demo-title { max-inline-size: 760px; margin: 0; font-size: clamp(2rem, 4vw, 3.5rem); line-height: 1.05; letter-spacing: -.04em; }
21
+ .demo-lead { max-inline-size: 720px; margin: 16px 0 0; color: var(--fs-color-muted); font-size: 1.05rem; line-height: 1.6; }
22
+ .demo-alert-row, .demo-action-row { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
23
+ .demo-alert-row { margin-block-start: 20px; }
24
+ .demo-toolbar { display: flex; flex-wrap: wrap; justify-content: space-between; gap: 12px; margin-block: 24px; padding: 12px; border: var(--fs-border-width) solid var(--demo-panel-border); border-radius: var(--fs-radius-md); background: var(--demo-panel-bg); }
25
+ .demo-section { margin-block-start: 32px; }
26
+ .demo-section-heading { display: flex; flex-wrap: wrap; align-items: end; justify-content: space-between; gap: 12px; margin-block-end: 14px; }
27
+ .demo-section-heading h2, .demo-card h3, .demo-reference h2, .demo-playground h3 { margin: 0; }
28
+ .demo-section-heading p, .demo-card p, .demo-reference p, .demo-status p { color: var(--fs-color-muted); }
29
+ .demo-section-heading p { max-inline-size: 620px; margin: 0; font-size: .9rem; }
30
+ .demo-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 16px; }
31
+ .demo-card, .demo-playground, .demo-reference { border: var(--fs-border-width) solid var(--demo-panel-border); border-radius: var(--fs-radius-md); background: var(--demo-panel-bg); box-shadow: var(--fs-shadow-sm); }
32
+ .demo-card { min-block-size: 190px; padding: 20px; }
33
+ .demo-card p { margin: 10px 0 20px; line-height: 1.55; }
34
+ .demo-card-icon { display: grid; inline-size: 36px; block-size: 36px; place-items: center; margin-block-end: 16px; border-radius: var(--fs-radius-sm); color: var(--fs-color-primary); background: var(--demo-soft-bg); font-size: 1.2rem; font-weight: 700; }
35
+ .demo-playground { padding: clamp(18px, 3vw, 28px); }
36
+ .demo-playground-grid { display: grid; grid-template-columns: minmax(0, 1.35fr) minmax(240px, .65fr); gap: 28px; }
37
+ .demo-demo-group + .demo-demo-group { margin-block-start: 28px; }
38
+ .demo-demo-group h3 { font-size: 1rem; }
39
+ .demo-demo-group > p { margin: 6px 0 14px; color: var(--fs-color-muted); font-size: .9rem; }
40
+ .demo-sample { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; padding: 16px; border: var(--fs-border-width) solid var(--demo-panel-border); border-radius: var(--fs-radius-sm); background: var(--demo-soft-bg); }
41
+ .demo-sample + .demo-sample { margin-block-start: 10px; }
42
+ .demo-sample-label { flex: 1 1 150px; color: var(--fs-color-muted); font-size: .82rem; }
43
+ .demo-status { display: flex; flex-direction: column; gap: 16px; padding: 20px; border-radius: var(--fs-radius-sm); background: var(--demo-soft-bg); }
44
+ .demo-status h3 { margin: 0; }
45
+ .demo-status p { margin: 8px 0 0; font-size: .9rem; line-height: 1.5; }
46
+ .demo-status-output { margin: 0; padding: 12px; border-inline-start: 3px solid var(--demo-accent); color: var(--fs-color-text) !important; background: var(--demo-panel-bg); font-size: .85rem !important; }
47
+ .demo-code { margin: 0; padding: 14px; overflow: auto; border-radius: var(--fs-radius-sm); color: var(--fs-color-text); background: var(--demo-panel-bg); font: .76rem/1.6 var(--fs-font-mono, ui-monospace, monospace); white-space: pre-wrap; }
48
+ .demo-reference { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 24px; padding: clamp(18px, 3vw, 28px); }
49
+ .demo-reference p { margin: 8px 0 16px; line-height: 1.5; }
50
+ .demo-reference ul { margin: 0; padding-inline-start: 20px; color: var(--fs-color-muted); line-height: 1.8; }
51
+ .demo-reference code { color: var(--fs-color-primary); }
52
+ .demo-note { margin-block-start: 14px !important; font-size: .85rem; }
53
+ html[data-demo-forced-colors] .demo-page { --demo-panel-bg: Canvas; --demo-soft-bg: Canvas; --demo-panel-border: CanvasText; color: CanvasText; background: Canvas; }
54
+ @media (max-width: 760px) { .demo-hero, .demo-playground-grid, .demo-reference { grid-template-columns: 1fr; } .demo-hero { align-items: start; } .demo-grid { grid-template-columns: 1fr; } }
55
+ </style>
22
56
  </head>
23
57
  <body>
24
- <main class="theme-panel">
25
- <h1 class="fs-u-fs-lg fs-u-fw-medium">Alertas</h1>
26
- <p class="fs-u-fs-sm fs-u-mt-1 fs-u-mb-3">Mensagens persistentes com bordas semânticas vivas e cantos retos.</p>
58
+ <main class="demo-page">
59
+ <header class="demo-hero">
60
+ <div>
61
+ <p class="demo-eyebrow">Fokus Styles · mensagem persistente</p>
62
+ <h1 class="demo-title">Alertas que explicam estados e orientam próximas ações.</h1>
63
+ <p class="demo-lead">Use Alert para feedback contextual que precisa permanecer visível. Ícones, títulos e ações são opcionais; o texto continua sendo a fonte principal do significado.</p>
64
+ <div class="demo-alert-row" aria-label="Exemplos rápidos"><span class="fs-badge fs-badge-soft-info">Informação</span><span class="fs-badge fs-badge-soft-success">Sucesso</span><span class="fs-badge fs-badge-soft-warning">Atenção</span><span class="fs-badge fs-badge-soft-danger">Erro</span></div>
65
+ </div>
66
+ <span class="fs-badge fs-badge-outline-primary">CSS-only</span>
67
+ </header>
68
+
69
+ <nav class="demo-toolbar" aria-label="Controles da demonstração"><div class="demo-action-row"><button class="fs-btn fs-btn-primary fs-btn-sm" type="button" id="reset-demo">Restaurar demonstração</button><button class="fs-btn fs-btn-outline-primary fs-btn-sm" type="button" id="toggle-direction" aria-pressed="false">Testar RTL</button><button class="fs-btn fs-btn-outline-secondary fs-btn-sm" type="button" id="toggle-forced-colors" aria-pressed="false">Testar alto contraste</button></div><span class="fs-form-text">Compare variantes, composições e estados sem depender apenas de cor.</span></nav>
70
+
71
+ <section class="demo-section" aria-labelledby="why-title">
72
+ <div class="demo-section-heading"><div><h2 id="why-title">Escolha o padrão certo</h2><p>Badge resume, Tag gerencia uma seleção, Alert explica contexto e Toast comunica algo transitório.</p></div></div>
73
+ <div class="demo-grid">
74
+ <article class="demo-card"><span class="demo-card-icon" aria-hidden="true">#</span><h3>Badge informa</h3><p>Status e classificação são curtos e complementam um item que já tem contexto.</p><span class="fs-badge fs-badge-success">Ativo</span></article>
75
+ <article class="demo-card"><span class="demo-card-icon" aria-hidden="true">!</span><h3>Alert explica</h3><p>Quando existe consequência, instrução ou próxima ação, use uma mensagem persistente.</p><div class="fs-alert fs-alert-warning" role="status">Revise o cadastro antes de publicar.</div></article>
76
+ <article class="demo-card"><span class="demo-card-icon" aria-hidden="true">↗</span><h3>Toast avisa</h3><p>Para confirmação breve que não exige leitura prolongada, prefira uma mensagem transitória.</p><span class="fs-badge fs-badge-outline-info">Temporário</span></article>
77
+ </div>
78
+ </section>
79
+
80
+ <section class="demo-section" aria-labelledby="playground-title">
81
+ <div class="demo-section-heading"><div><h2 id="playground-title">Laboratório de Alertas</h2><p>Compare variantes, anatomia, ações, fechamento manual, grupos, inline, loading e mensagens longas.</p></div><span class="fs-badge fs-badge-outline-success">ao vivo</span></div>
82
+ <div class="demo-playground"><div class="demo-playground-grid"><div>
83
+ <div class="demo-demo-group"><h3>Variantes semânticas</h3><p>O texto comunica a intenção; a barra lateral reforça a hierarquia visual.</p><div class="demo-sample"><span class="demo-sample-label">Neutro e informação</span><div class="fs-alert">Informação neutra para o contexto atual.</div><div class="fs-alert fs-alert-info" role="status">Há uma nova versão pronta para revisão.</div></div><div class="demo-sample"><span class="demo-sample-label">Sucesso, atenção e erro</span><div class="fs-alert fs-alert-success" role="status">Operação concluída com sucesso.</div><div class="fs-alert fs-alert-warning" role="status">Revise antes de continuar.</div><div class="fs-alert fs-alert-danger" role="alert">Não foi possível salvar.</div></div></div>
84
+ <div class="demo-demo-group"><h3>Anatomia e ações</h3><p>Ícone, título, descrição, link, botão e fechamento são slots opcionais e composicionais.</p><div class="demo-sample"><span class="demo-sample-label">Mensagem rica</span><div class="fs-alert fs-alert-warning" role="status"><span class="fs-alert-icon" aria-hidden="true"><svg class="fs-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="m12 3 9 17H3L12 3Z"/><path d="M12 9v4m0 3h.01"/></svg></span><div class="fs-alert-content"><h3 class="fs-alert-heading">Sessão expirando</h3><p class="fs-alert-text">Salve seu trabalho para não perder alterações.</p><div class="fs-alert-actions"><button class="fs-btn fs-btn-warning fs-btn-sm" type="button">Continuar sessão</button><a class="fs-alert-link" href="#ajuda">Saiba mais</a></div></div></div></div><div class="demo-sample"><span class="demo-sample-label">Fechamento manual</span><div class="fs-alert fs-alert-success"><div class="fs-alert-content"><h3 class="fs-alert-heading">Convite enviado</h3><p class="fs-alert-text">A pessoa receberá um e-mail para entrar no projeto.</p></div><button class="fs-btn-close fs-alert-close" type="button" aria-label="Fechar aviso de convite enviado"></button></div></div></div>
85
+ <div class="demo-demo-group"><h3>Densidade, grupos e loading</h3><p>Use a variante inline perto de campos e aria-busy durante operações assíncronas.</p><div class="demo-sample"><span class="demo-sample-label">Inline</span><div class="fs-alert fs-alert-inline fs-alert-warning" role="status"><span class="fs-alert-icon" aria-hidden="true">!</span><div class="fs-alert-content"><p class="fs-alert-text">Campo obrigatório.</p></div></div></div><div class="demo-sample"><span class="demo-sample-label">Grupo</span><div class="fs-alert-group" aria-label="Mensagens do projeto"><div class="fs-alert fs-alert-info">A sincronização será iniciada em instantes.</div><div class="fs-alert fs-alert-success">Última sincronização concluída.</div></div></div><div class="demo-sample"><span class="demo-sample-label">Loading</span><div class="fs-alert fs-alert-info" role="status" aria-busy="true"><span class="fs-spinner fs-spinner-sm" aria-hidden="true"></span><div class="fs-alert-content"><p class="fs-alert-text">Atualizando dados…</p></div></div></div></div>
86
+ <div class="demo-demo-group"><h3>Mensagem longa e responsiva</h3><p>Conteúdo, links e ações quebram sem causar overflow horizontal em telas estreitas.</p><div class="demo-sample"><span class="demo-sample-label">Responsivo</span><div class="fs-alert fs-alert-primary" role="status"><div class="fs-alert-content"><h3 class="fs-alert-heading">Mensagem com várias linhas</h3><p class="fs-alert-text">Alertas preservam a leitura em telas estreitas e permitem que títulos, descrições e links quebrem livremente.</p><div class="fs-alert-actions"><a class="fs-alert-link" href="#documentacao">Abrir documentação com endereço longo</a></div></div></div></div></div>
87
+ </div><aside class="demo-status" aria-labelledby="status-title"><div><h3 id="status-title">Contrato do Alert</h3><p>Alert permanece no contexto, não move foco e não fecha sozinho. O fechamento visual depende da lógica da aplicação.</p></div><p class="demo-status-output" id="demo-status" role="status" aria-live="polite">Demonstração pronta.</p><pre class="demo-code"><code>.fs-alert
88
+ .fs-alert-{color}
89
+ .fs-alert-icon
90
+ .fs-alert-content
91
+ .fs-alert-actions
92
+ .fs-alert-close
93
+ .fs-alert-group
94
+ .fs-alert-inline
95
+
96
+ /* tokens locais */
97
+ --fs-alert-radius
98
+ --fs-alert-gap
99
+ --fs-alert-icon-size</code></pre></aside></div></div>
100
+ </section>
27
101
 
28
- <div class="fs-alert fs-u-mb-2">Alerta neutro: mensagem informativa padrão sem cor de estado.</div>
29
- <div class="fs-alert fs-alert-primary fs-u-mb-2">Alerta primary: ação recomendada ou destaque neutro de marca.</div>
30
- <div class="fs-alert fs-alert-success fs-u-mb-2">Alerta success: operação concluída com sucesso.</div>
31
- <div class="fs-alert fs-alert-warning fs-u-mb-2">Alerta warning: atenção, revise antes de continuar.</div>
32
- <div class="fs-alert fs-alert-danger fs-u-mb-2">Alerta danger: falha ao processar a solicitação.</div>
33
- <div class="fs-alert fs-alert-info">Alerta info: informação adicional relevante para o usuário.</div>
34
- </main>
35
- <script>
36
- if (new URLSearchParams(window.location.search).get("theme") === "dark") {
37
- document.documentElement.setAttribute("data-theme", "dark");
38
- }
39
- </script>
102
+ <section class="demo-section" aria-labelledby="a11y-title"><div class="demo-section-heading"><div><h2 id="a11y-title">Contratos preservados</h2><p>O significado permanece disponível sem depender de cor, ícone, foco automático ou desaparecimento temporizado.</p></div></div><div class="demo-reference"><div><h2>Uso acessível</h2><ul><li>Use <code>role="status"</code> para atualização calma e <code>role="alert"</code> somente para urgência.</li><li>Ícones decorativos usam <code>aria-hidden="true"</code>.</li><li>Botões e links mantêm nomes e foco visível.</li><li>Loading usa <code>aria-busy="true"</code> e texto compreensível.</li></ul></div><div><h2>Limites do componente</h2><ul><li>Alert não injeta ARIA nem move foco.</li><li>Não use Alert para decisões críticas; prefira Alert Dialog.</li><li>Não use Alert para feedback temporário; prefira Toast.</li><li>RTL, dark mode e forced colors preservam a leitura.</li></ul></div></div></section>
103
+ </main>
104
+ <script>
105
+ const page = document.querySelector(".demo-page");
106
+ const status = document.getElementById("demo-status");
107
+ const directionButton = document.getElementById("toggle-direction");
108
+ const forcedColorsButton = document.getElementById("toggle-forced-colors");
109
+ document.querySelectorAll(".fs-alert-close").forEach((button) => button.addEventListener("click", () => { button.closest(".fs-alert")?.remove(); status.textContent = "Alert removido manualmente da demonstração."; }));
110
+ document.getElementById("reset-demo").addEventListener("click", () => { location.reload(); });
111
+ directionButton.addEventListener("click", () => { const rtl = page.getAttribute("dir") !== "rtl"; page.setAttribute("dir", rtl ? "rtl" : "ltr"); directionButton.setAttribute("aria-pressed", String(rtl)); directionButton.textContent = rtl ? "Voltar para LTR" : "Testar RTL"; status.textContent = rtl ? "Direção RTL ativada." : "Direção LTR ativada."; });
112
+ forcedColorsButton.addEventListener("click", () => { const active = forcedColorsButton.getAttribute("aria-pressed") !== "true"; forcedColorsButton.setAttribute("aria-pressed", String(active)); forcedColorsButton.textContent = active ? "Voltar ao tema" : "Testar alto contraste"; document.documentElement.toggleAttribute("data-demo-forced-colors", active); status.textContent = active ? "Modo de alto contraste demonstrativo ativado." : "Modo de alto contraste demonstrativo desativado."; });
113
+ </script>
40
114
  </body>
41
115
  </html>