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.
- package/CHANGELOG.md +36 -0
- package/dist/css/components.css +583 -83
- package/dist/css/components.css.map +1 -1
- package/dist/css/components.min.css +1 -1
- package/dist/css/components.min.css.map +1 -1
- package/dist/css/fokus-components.css +583 -83
- package/dist/css/fokus-components.css.map +1 -1
- package/dist/css/fokus-components.min.css +1 -1
- package/dist/css/fokus-components.min.css.map +1 -1
- package/dist/css/fokus-core.css +22 -0
- package/dist/css/fokus-core.css.map +1 -1
- package/dist/css/fokus-core.min.css +1 -1
- package/dist/css/fokus-core.min.css.map +1 -1
- package/dist/css/fokus-dark.css +10 -0
- package/dist/css/fokus-dark.css.map +1 -1
- package/dist/css/fokus-dark.min.css +1 -1
- package/dist/css/fokus-dark.min.css.map +1 -1
- package/dist/css/fokus-rtl.css +9166 -0
- package/dist/css/fokus-rtl.css.map +1 -0
- package/dist/css/fokus-rtl.min.css +2 -0
- package/dist/css/fokus-rtl.min.css.map +1 -0
- package/dist/css/fokus-utilities.css +158 -0
- package/dist/css/fokus-utilities.css.map +1 -1
- package/dist/css/fokus-utilities.min.css +1 -1
- package/dist/css/fokus-utilities.min.css.map +1 -1
- package/dist/css/fokus.css +1018 -258
- package/dist/css/fokus.css.map +1 -1
- package/dist/css/fokus.min.css +1 -1
- package/dist/css/fokus.min.css.map +1 -1
- package/dist/css/forms.css +322 -176
- package/dist/css/forms.css.map +1 -1
- package/dist/css/forms.min.css +1 -1
- package/dist/css/forms.min.css.map +1 -1
- package/dist/css/helpers.css +158 -0
- package/dist/css/helpers.css.map +1 -1
- package/dist/css/helpers.min.css +1 -1
- package/dist/css/helpers.min.css.map +1 -1
- package/dist/css/layout.css +22 -0
- package/dist/css/layout.css.map +1 -1
- package/dist/css/layout.min.css +1 -1
- package/dist/css/layout.min.css.map +1 -1
- package/dist/js/fokus.js +270 -25
- package/dist/js/fokus.js.map +2 -2
- package/dist/js/fokus.min.js +18 -18
- package/dist/js/fokus.min.js.map +3 -3
- package/docs/components/alert.md +69 -24
- package/docs/components/badge.md +23 -4
- package/docs/components/checkbox.md +16 -3
- package/docs/components/popover.md +20 -7
- package/docs/components/radio.md +11 -1
- package/docs/components/responsive.md +19 -0
- package/docs/components/switch.md +30 -8
- package/docs/components/tag.md +19 -4
- package/docs/components/toast.md +72 -59
- package/docs/components/tooltip.md +38 -2
- package/docs/guides/accessibility.md +7 -0
- package/docs/guides/rtl-and-system-preferences.md +28 -0
- package/docs/prompts/prompt-plan.md +4 -4
- package/docs/reference/accessibility-matrix.md +7 -7
- package/docs/reference/contrast-report.md +4 -0
- package/docs/reference/definitions.md +5 -1
- package/docs/reference/design-tokens.md +14 -0
- package/docs/reference/size-baseline.json +13 -13
- package/mockup/assets/example-theme.css +11 -0
- package/mockup/assets/showcase-contracts.js +6 -6
- package/mockup/examples/alerts.html +105 -31
- package/mockup/examples/badges-alerts.html +91 -59
- package/mockup/examples/check-radio-switch.html +575 -99
- package/mockup/examples/popover.html +81 -0
- package/mockup/examples/tag.html +97 -87
- package/mockup/examples/toast.html +113 -0
- package/mockup/examples/tooltip.html +68 -0
- package/mockup/forms.html +1 -1
- package/mockup/overlays-commands.html +2 -2
- package/package.json +2 -1
- package/packages/fokus-components/scss/components/_alerts.scss +128 -6
- package/packages/fokus-components/scss/components/_badges.scss +26 -10
- package/packages/fokus-components/scss/components/_index.scss +1 -0
- package/packages/fokus-components/scss/components/_popover.scss +131 -21
- package/packages/fokus-components/scss/components/_responsive.scss +23 -0
- package/packages/fokus-components/scss/components/_tag.scss +37 -14
- package/packages/fokus-components/scss/components/_toasts.scss +156 -11
- package/packages/fokus-components/scss/components/_tooltips.scss +70 -18
- package/packages/fokus-components/scss/forms/_check-radio-switch.scss +313 -215
- package/packages/fokus-components/scss/forms/_forms.scss +16 -0
- package/packages/fokus-core/scss/base/_accessibility.scss +14 -0
- package/packages/fokus-core/scss/tokens/_root.scss +11 -0
- package/packages/fokus-js/js/carousel.d.ts +4 -0
- package/packages/fokus-js/js/carousel.js +8 -0
- package/packages/fokus-js/js/core/positioning.js +18 -9
- package/packages/fokus-js/js/dropdown.d.ts +4 -0
- package/packages/fokus-js/js/dropdown.js +7 -0
- package/packages/fokus-js/js/popover.js +26 -1
- package/packages/fokus-js/js/tabs.d.ts +3 -0
- package/packages/fokus-js/js/tabs.js +5 -0
- package/packages/fokus-js/js/tag.js +17 -0
- package/packages/fokus-js/js/toast.js +77 -3
- package/packages/fokus-js/js/tooltip.d.ts +8 -1
- package/packages/fokus-js/js/tooltip.js +106 -13
- package/packages/fokus-utilities/scss/utilities/_api.scss +48 -0
- package/scss/entries/rtl-entry.scss +11 -0
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="pt-BR">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>Fokus Styles — Popovers</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
|
+
<link rel="stylesheet" href="../assets/example-theme.css">
|
|
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-row, .demo-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
|
|
23
|
+
.demo-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-heading { display: flex; flex-wrap: wrap; align-items: end; justify-content: space-between; gap: 12px; margin-block-end: 14px; }
|
|
27
|
+
.demo-heading h2, .demo-card h3, .demo-reference h2, .demo-playground h3 { margin: 0; }
|
|
28
|
+
.demo-heading p, .demo-card p, .demo-reference p, .demo-status p { color: var(--fs-color-muted); }
|
|
29
|
+
.demo-heading p { max-inline-size: 650px; 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: 180px; padding: 20px; }
|
|
33
|
+
.demo-card p { margin: 10px 0 20px; line-height: 1.55; }
|
|
34
|
+
.demo-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-weight: 700; }
|
|
35
|
+
.demo-playground { padding: clamp(18px, 3vw, 28px); }
|
|
36
|
+
.demo-columns { display: grid; grid-template-columns: minmax(0, 1.35fr) minmax(240px, .65fr); gap: 28px; }
|
|
37
|
+
.demo-group + .demo-group { margin-block-start: 28px; }
|
|
38
|
+
.demo-group h3 { font-size: 1rem; }
|
|
39
|
+
.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-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-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; }
|
|
46
|
+
.demo-reference { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 24px; padding: clamp(18px, 3vw, 28px); }
|
|
47
|
+
.demo-reference ul { margin: 0; padding-inline-start: 20px; color: var(--fs-color-muted); line-height: 1.8; }
|
|
48
|
+
.demo-reference code { color: var(--fs-color-primary); }
|
|
49
|
+
@media (max-width: 760px) { .demo-hero, .demo-columns, .demo-reference { grid-template-columns: 1fr; } .demo-grid { grid-template-columns: 1fr; } }
|
|
50
|
+
</style>
|
|
51
|
+
</head>
|
|
52
|
+
<body>
|
|
53
|
+
<main class="demo-page">
|
|
54
|
+
<header class="demo-hero"><div><p class="demo-eyebrow">Fokus Styles · painel contextual</p><h1 class="demo-title">Popovers para contexto rico sem bloquear a página.</h1><p class="demo-lead">Popover complementa um gatilho com informações, ações ou confirmação curta. Ele permite foco interno, mas não é um Modal e não cria focus trap.</p><div class="demo-row"><span class="fs-badge fs-badge-soft-primary">Click</span><span class="fs-badge fs-badge-soft-success">Focus</span><span class="fs-badge fs-badge-outline-primary">Hover</span></div></div><span class="fs-badge fs-badge-outline-primary">não modal</span></header>
|
|
55
|
+
<nav class="demo-toolbar" aria-label="Controles da demonstração"><div class="demo-actions"><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></div><span class="fs-form-text">Use Tab, Escape e clique externo para conferir os contratos.</span></nav>
|
|
56
|
+
<section class="demo-section" aria-labelledby="compare-title"><div class="demo-heading"><div><h2 id="compare-title">Escolha o padrão certo</h2><p>Tooltip explica rapidamente; Popover oferece contexto e ações; Modal concentra uma tarefa.</p></div></div><div class="demo-grid"><article class="demo-card"><span class="demo-icon" aria-hidden="true">?</span><h3>Tooltip</h3><p>Texto breve e não interativo, normalmente associado a um ícone.</p><span class="fs-badge fs-badge-outline-info">breve</span></article><article class="demo-card"><span class="demo-icon" aria-hidden="true">+</span><h3>Popover</h3><p>Conteúdo contextual com título, descrição e controles opcionais.</p><button class="fs-btn fs-btn-primary fs-btn-sm" type="button" data-fs="popover" data-fs-target="#pop-card">Abrir Popover</button></article><article class="demo-card"><span class="demo-icon" aria-hidden="true">□</span><h3>Modal</h3><p>Tarefa que exige atenção concentrada e gerenciamento de foco.</p><span class="fs-badge fs-badge-outline-warning">bloqueia</span></article></div></section>
|
|
57
|
+
<section class="demo-section" aria-labelledby="playground-title"><div class="demo-heading"><div><h2 id="playground-title">Laboratório de Popovers</h2><p>Compare triggers, placements, alinhamentos, hover card, ações internas e fallback de viewport.</p></div><span class="fs-badge fs-badge-outline-success">interativo</span></div><div class="demo-playground"><div class="demo-columns"><div><div class="demo-group"><h3>Triggers e posições</h3><p>Click é o padrão; focus e hover oferecem alternativas para contextos apropriados.</p><div class="demo-sample"><span class="demo-label">Click · top</span><button class="fs-btn fs-btn-outline-primary fs-btn-sm" type="button" data-fs="popover" data-fs-target="#pop-click" data-placement="top">Abrir acima</button><button class="fs-btn fs-btn-outline-primary fs-btn-sm" type="button" data-fs="popover" data-fs-target="#pop-bottom" data-placement="bottom">Abrir abaixo</button></div><div class="demo-sample"><span class="demo-label">Focus · right</span><button class="fs-btn fs-btn-outline-success fs-btn-sm" type="button" data-fs="popover" data-fs-target="#pop-focus" data-trigger="focus" data-placement="right">Foque neste botão</button></div><div class="demo-sample"><span class="demo-label">Manual · left</span><button class="fs-btn fs-btn-outline-warning fs-btn-sm" type="button" id="manual-trigger" data-fs="popover" data-fs-target="#pop-manual" data-trigger="manual" data-placement="left">Abrir manual</button></div></div><div class="demo-group"><h3>Hover card e ações</h3><p>O painel pode conter controles sem se comportar como diálogo modal.</p><div class="demo-sample"><span class="demo-label">Hover</span><a href="#profile" class="fs-btn fs-btn-outline-secondary fs-btn-sm" data-fs="popover" data-fs-target="#pop-hover" data-trigger="hover">Passe o mouse ou use foco</a></div><div class="demo-sample"><span class="demo-label">Ações</span><button class="fs-btn fs-btn-primary fs-btn-sm" type="button" data-fs="popover" data-fs-target="#pop-actions" data-placement="bottom">Confirmar alteração</button></div></div><div class="demo-group"><h3>Conteúdo longo</h3><p>Textos quebram e ações continuam acessíveis em telas estreitas.</p><div class="demo-sample"><button class="fs-btn fs-btn-outline-danger fs-btn-sm" type="button" data-fs="popover" data-fs-target="#pop-long" data-placement="bottom">Ver detalhes do erro</button></div></div></div><aside class="demo-status" aria-labelledby="status-title"><div><h3 id="status-title">Contrato do Popover</h3><p>Escape fecha e devolve foco; clique externo fecha apenas o trigger click; não há focus trap.</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-popover
|
|
58
|
+
.fs-popover-header
|
|
59
|
+
.fs-popover-body
|
|
60
|
+
.fs-popover-footer
|
|
61
|
+
.fs-popover-arrow
|
|
62
|
+
.fs-popover-hover-card
|
|
63
|
+
|
|
64
|
+
data-trigger
|
|
65
|
+
data-placement
|
|
66
|
+
data-align</code></pre></aside></div></div></section>
|
|
67
|
+
<section class="demo-section" aria-labelledby="a11y-title"><div class="demo-heading"><div><h2 id="a11y-title">Contratos preservados</h2><p>O painel é acessível por teclado e continua não modal.</p></div></div><div class="demo-reference"><div><h2>Acessibilidade</h2><ul><li><code>aria-expanded</code> e <code>aria-controls</code> acompanham o gatilho.</li><li>Escape fecha e devolve foco ao gatilho.</li><li>Ícones decorativos usam <code>aria-hidden="true"</code>.</li><li>Controles internos mantêm foco visível.</li></ul></div><div><h2>Limites</h2><ul><li>Não use Popover para conteúdo essencial apenas em hover.</li><li>Não há bloqueio de scroll ou focus trap.</li><li>Use Modal para tarefas extensas ou críticas.</li><li>RTL, dark mode, reduced motion e forced colors são considerados.</li></ul></div></div></section>
|
|
68
|
+
</main>
|
|
69
|
+
<div class="fs-popover" id="pop-card"><div class="fs-popover-header"><span class="fs-popover-icon" aria-hidden="true">+</span><div class="fs-popover-heading"><strong class="fs-popover-title">Contexto rápido</strong></div><button class="fs-btn-close fs-popover-close" type="button" data-fs-dismiss="popover" aria-label="Fechar contexto rápido"></button></div><div class="fs-popover-body"><p class="fs-popover-text">Popover pode explicar uma ação sem interromper o restante da página.</p></div></div>
|
|
70
|
+
<div class="fs-popover" id="pop-click"><div class="fs-popover-arrow" aria-hidden="true"></div><div class="fs-popover-header">Posicionado acima</div><div class="fs-popover-body">Use Escape ou clique fora para fechar.</div></div>
|
|
71
|
+
<div class="fs-popover" id="pop-bottom"><div class="fs-popover-arrow" aria-hidden="true"></div><div class="fs-popover-header">Posicionado abaixo</div><div class="fs-popover-body">A seta acompanha o placement.</div></div>
|
|
72
|
+
<div class="fs-popover" id="pop-manual"><div class="fs-popover-arrow" aria-hidden="true"></div><div class="fs-popover-header">Popover manual</div><div class="fs-popover-body">Este painel só responde à chamada explícita de <code>toggle()</code>.</div></div>
|
|
73
|
+
<div class="fs-popover" id="pop-focus"><div class="fs-popover-arrow" aria-hidden="true"></div><div class="fs-popover-header">Foco preservado</div><div class="fs-popover-body"><button type="button" class="fs-btn fs-btn-primary fs-btn-sm" data-fs-dismiss="popover">Fechar e voltar</button></div></div>
|
|
74
|
+
<div class="fs-popover fs-popover-hover-card" id="pop-hover"><div class="fs-popover-arrow" aria-hidden="true"></div><div class="fs-popover-body"><span class="fs-avatar fs-avatar-sm" aria-hidden="true">JS</span><div><strong>Fokus Styles</strong><p class="fs-u-m-0">Prévia contextual com hover e foco.</p></div></div></div>
|
|
75
|
+
<div class="fs-popover" id="pop-actions"><div class="fs-popover-arrow" aria-hidden="true"></div><div class="fs-popover-header">Confirmar alteração</div><div class="fs-popover-body">A ação será aplicada ao projeto atual.</div><div class="fs-popover-footer"><button type="button" class="fs-btn fs-btn-sm" data-fs-dismiss="popover">Cancelar</button><button type="button" class="fs-btn fs-btn-primary fs-btn-sm" data-fs-dismiss="popover">Confirmar</button></div></div>
|
|
76
|
+
<div class="fs-popover" id="pop-long"><div class="fs-popover-arrow" aria-hidden="true"></div><div class="fs-popover-header">Detalhes da falha</div><div class="fs-popover-body"><p class="fs-popover-text">Não foi possível concluir a operação porque o serviço remoto demorou além do limite esperado. Verifique sua conexão, revise os dados e tente novamente.</p></div></div>
|
|
77
|
+
<script src="../../dist/js/fokus.js"></script><script>
|
|
78
|
+
const page = document.querySelector(".demo-page"); const status = document.getElementById("demo-status"); const direction = document.getElementById("toggle-direction"); const manual = document.getElementById("manual-trigger"); manual.addEventListener("click", () => FokusStyles.Popover.getInstance(manual).toggle()); document.getElementById("reset-demo").addEventListener("click", () => location.reload()); direction.addEventListener("click", () => { const rtl = page.getAttribute("dir") !== "rtl"; page.setAttribute("dir", rtl ? "rtl" : "ltr"); direction.setAttribute("aria-pressed", String(rtl)); direction.textContent = rtl ? "Voltar para LTR" : "Testar RTL"; status.textContent = rtl ? "Direção RTL ativada." : "Direção LTR ativada."; }); document.querySelectorAll('[data-fs="popover"]').forEach((trigger) => { trigger.addEventListener("fs:popover:shown", () => { status.textContent = "Popover aberto."; }); trigger.addEventListener("fs:popover:hidden", () => { status.textContent = "Popover fechado."; }); });
|
|
79
|
+
</script>
|
|
80
|
+
</body>
|
|
81
|
+
</html>
|
package/mockup/examples/tag.html
CHANGED
|
@@ -1,105 +1,115 @@
|
|
|
1
|
-
<!
|
|
2
|
-
<html lang="
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="pt-BR">
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="UTF-8">
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
-
<title>
|
|
7
|
-
|
|
8
|
-
<!-- STYLES IMPORT -->
|
|
6
|
+
<title>Fokus Styles — Tags</title>
|
|
9
7
|
<link rel="stylesheet" href="../../dist/css/fonts.css">
|
|
10
8
|
<link rel="stylesheet" href="../../dist/css/components.css">
|
|
11
9
|
<link rel="stylesheet" href="../../dist/css/helpers.css">
|
|
12
|
-
<style>
|
|
13
|
-
.theme-panel {
|
|
14
|
-
display: flex;
|
|
15
|
-
flex-direction: column;
|
|
16
|
-
gap: 16px;
|
|
17
|
-
padding: 24px;
|
|
18
|
-
background-color: var(--fs-color-subtle);
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
html[data-theme="dark"] .theme-panel {
|
|
22
|
-
color: var(--fs-color-text);
|
|
23
|
-
background-color: var(--fs-color-surface);
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
.tag-row {
|
|
27
|
-
display: flex;
|
|
28
|
-
flex-wrap: wrap;
|
|
29
|
-
gap: 8px;
|
|
30
|
-
}
|
|
31
|
-
</style>
|
|
32
10
|
<link rel="stylesheet" href="../assets/example-theme.css">
|
|
33
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-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); }
|
|
23
|
+
.demo-actions, .demo-tag-row { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
|
|
24
|
+
.demo-section { margin-block-start: 32px; }
|
|
25
|
+
.demo-heading { display: flex; flex-wrap: wrap; align-items: end; justify-content: space-between; gap: 12px; margin-block-end: 14px; }
|
|
26
|
+
.demo-heading h2, .demo-card h3, .demo-reference h2 { margin: 0; }
|
|
27
|
+
.demo-heading p, .demo-card p, .demo-reference p, .demo-status p { color: var(--fs-color-muted); }
|
|
28
|
+
.demo-heading p { max-inline-size: 650px; margin: 0; font-size: .9rem; }
|
|
29
|
+
.demo-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 16px; }
|
|
30
|
+
.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); }
|
|
31
|
+
.demo-card { min-block-size: 180px; padding: 20px; }
|
|
32
|
+
.demo-card p { margin: 10px 0 20px; line-height: 1.55; }
|
|
33
|
+
.demo-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-weight: 700; }
|
|
34
|
+
.demo-playground { padding: clamp(18px, 3vw, 28px); }
|
|
35
|
+
.demo-columns { display: grid; grid-template-columns: minmax(0, 1.35fr) minmax(240px, .65fr); gap: 28px; }
|
|
36
|
+
.demo-group + .demo-group { margin-block-start: 28px; }
|
|
37
|
+
.demo-group h3 { margin: 0; font-size: 1rem; }
|
|
38
|
+
.demo-group > p { margin: 6px 0 14px; color: var(--fs-color-muted); font-size: .9rem; }
|
|
39
|
+
.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); }
|
|
40
|
+
.demo-sample + .demo-sample { margin-block-start: 10px; }
|
|
41
|
+
.demo-label { flex: 1 1 150px; color: var(--fs-color-muted); font-size: .82rem; }
|
|
42
|
+
.demo-status { display: flex; flex-direction: column; gap: 16px; padding: 20px; border-radius: var(--fs-radius-sm); background: var(--demo-soft-bg); }
|
|
43
|
+
.demo-status h3 { margin: 0; }
|
|
44
|
+
.demo-status p { margin: 8px 0 0; font-size: .9rem; line-height: 1.5; }
|
|
45
|
+
.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; }
|
|
46
|
+
.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; }
|
|
47
|
+
.demo-reference { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 24px; padding: clamp(18px, 3vw, 28px); }
|
|
48
|
+
.demo-reference p { margin: 8px 0 16px; line-height: 1.5; }
|
|
49
|
+
.demo-reference ul { margin: 0; padding-inline-start: 20px; color: var(--fs-color-muted); line-height: 1.8; }
|
|
50
|
+
.demo-reference code { color: var(--fs-color-primary); }
|
|
51
|
+
@media (max-width: 760px) { .demo-hero, .demo-columns, .demo-reference { grid-template-columns: 1fr; } .demo-grid { grid-template-columns: 1fr; } }
|
|
52
|
+
</style>
|
|
34
53
|
</head>
|
|
35
54
|
<body>
|
|
36
|
-
<
|
|
37
|
-
<
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
<
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
</
|
|
47
|
-
<span class="fs-badge fs-badge-
|
|
48
|
-
|
|
49
|
-
<button type="button" class="fs-btn-close" data-fs-dismiss="tag" aria-label="Remover tag Aprovado"></button>
|
|
50
|
-
</span>
|
|
51
|
-
<span class="fs-badge fs-badge-danger fs-tag" data-fs="tag">
|
|
52
|
-
Bloqueado
|
|
53
|
-
<button type="button" class="fs-btn-close" data-fs-dismiss="tag" aria-label="Remover tag Bloqueado"></button>
|
|
54
|
-
</span>
|
|
55
|
-
</div>
|
|
56
|
-
|
|
57
|
-
<p class="fs-u-fs-sm fs-u-fw-medium fs-u-mt-3 fs-u-mb-0">Agrupamento (.fs-tag-group)</p>
|
|
58
|
-
<div class="fs-tag-group">
|
|
59
|
-
<span class="fs-badge fs-tag" data-fs="tag">Design<button type="button" class="fs-btn-close" data-fs-dismiss="tag" aria-label="Remover"></button></span>
|
|
60
|
-
<span class="fs-badge fs-tag" data-fs="tag">Frontend<button type="button" class="fs-btn-close" data-fs-dismiss="tag" aria-label="Remover"></button></span>
|
|
61
|
-
<span class="fs-badge fs-tag" data-fs="tag">Backend<button type="button" class="fs-btn-close" data-fs-dismiss="tag" aria-label="Remover"></button></span>
|
|
62
|
-
<span class="fs-badge fs-tag" data-fs="tag">DevOps<button type="button" class="fs-btn-close" data-fs-dismiss="tag" aria-label="Remover"></button></span>
|
|
63
|
-
<span class="fs-badge fs-tag" data-fs="tag">QA<button type="button" class="fs-btn-close" data-fs-dismiss="tag" aria-label="Remover"></button></span>
|
|
64
|
-
</div>
|
|
55
|
+
<main class="demo-page">
|
|
56
|
+
<header class="demo-hero">
|
|
57
|
+
<div>
|
|
58
|
+
<p class="demo-eyebrow">Fokus Styles · extensão do Badge</p>
|
|
59
|
+
<h1 class="demo-title">Tags para filtros, categorias e seleções removíveis.</h1>
|
|
60
|
+
<p class="demo-lead">Tag mantém o visual compacto do Badge, mas adiciona uma ação explícita de remoção. Ícones ajudam a orientar a leitura; o texto continua sendo a fonte principal do significado.</p>
|
|
61
|
+
<div class="demo-tag-row" aria-label="Exemplos rápidos">
|
|
62
|
+
<span class="fs-badge fs-tag fs-badge-soft-primary" data-fs="tag">Frontend<button type="button" class="fs-btn-close" data-fs-dismiss="tag" aria-label="Remover tag Frontend"></button></span>
|
|
63
|
+
<span class="fs-badge fs-tag fs-badge-success" data-fs="tag"><span class="fs-tag-icon" aria-hidden="true"><svg class="fs-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m5 12 4 4L19 6"/></svg></span><span class="fs-tag-label">Aprovado</span><button type="button" class="fs-btn-close" data-fs-dismiss="tag" aria-label="Remover tag Aprovado"></button></span>
|
|
64
|
+
</div>
|
|
65
|
+
</div>
|
|
66
|
+
<span class="fs-badge fs-badge-outline-primary">Badge + dismiss</span>
|
|
67
|
+
</header>
|
|
65
68
|
|
|
66
|
-
<
|
|
67
|
-
|
|
68
|
-
<span class="fs-
|
|
69
|
-
|
|
70
|
-
<span class="fs-tag-label">Documento</span>
|
|
71
|
-
<button type="button" class="fs-btn-close" data-fs-dismiss="tag" aria-label="Remover tag Documento"></button>
|
|
72
|
-
</span>
|
|
73
|
-
<span class="fs-badge fs-tag fs-tag-truncate" data-fs="tag" style="--fs-tag-max-inline-size: 190px;" title="Revisão de acessibilidade e compatibilidade entre navegadores">
|
|
74
|
-
<span class="fs-tag-label">Revisão de acessibilidade e compatibilidade entre navegadores</span>
|
|
75
|
-
<button type="button" class="fs-btn-close" data-fs-dismiss="tag" aria-label="Remover tag Revisão de acessibilidade e compatibilidade entre navegadores"></button>
|
|
76
|
-
</span>
|
|
77
|
-
<span class="fs-badge fs-badge-warning fs-tag is-loading" data-fs="tag" aria-busy="true">
|
|
78
|
-
<span class="fs-tag-label">Salvando</span>
|
|
79
|
-
<span class="fs-spinner" aria-hidden="true"></span>
|
|
80
|
-
<button type="button" class="fs-btn-close" data-fs-dismiss="tag" aria-label="Remover tag Salvando" disabled></button>
|
|
81
|
-
</span>
|
|
82
|
-
</div>
|
|
69
|
+
<nav class="demo-toolbar" aria-label="Controles da demonstração">
|
|
70
|
+
<div class="demo-actions"><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></div>
|
|
71
|
+
<span class="fs-form-text">Remova tags com o botão de fechar ou use Tab e Enter.</span>
|
|
72
|
+
</nav>
|
|
83
73
|
|
|
84
|
-
<
|
|
85
|
-
|
|
86
|
-
<
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
74
|
+
<section class="demo-section" aria-labelledby="why-title">
|
|
75
|
+
<div class="demo-heading"><div><h2 id="why-title">Badge, Tag ou Alert?</h2><p>Escolha pelo papel da informação: ler, gerenciar uma seleção ou compreender uma mensagem.</p></div></div>
|
|
76
|
+
<div class="demo-grid">
|
|
77
|
+
<article class="demo-card"><span class="demo-icon" aria-hidden="true">#</span><h3>Badge informa</h3><p>Status e classificação são estáticos e complementam um item que já tem contexto.</p><span class="fs-badge fs-badge-success">Ativo</span></article>
|
|
78
|
+
<article class="demo-card"><span class="demo-icon" aria-hidden="true">×</span><h3>Tag remove</h3><p>Filtros e categorias selecionadas podem ser gerenciados pelo botão de fechar.</p><span class="fs-badge fs-tag fs-badge-primary" data-fs="tag">Urgente<button type="button" class="fs-btn-close" data-fs-dismiss="tag" aria-label="Remover tag Urgente"></button></span></article>
|
|
79
|
+
<article class="demo-card"><span class="demo-icon" aria-hidden="true">!</span><h3>Alert explica</h3><p>Quando existe contexto, consequência ou próxima ação, use uma mensagem persistente.</p><div class="fs-alert fs-alert-warning" role="status">Revise os filtros antes de continuar.</div></article>
|
|
80
|
+
</div>
|
|
81
|
+
</section>
|
|
90
82
|
|
|
91
|
-
<
|
|
92
|
-
|
|
93
|
-
<
|
|
94
|
-
<span class="fs-tag-label">
|
|
95
|
-
<span class="fs-tag-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
83
|
+
<section class="demo-section" aria-labelledby="playground-title">
|
|
84
|
+
<div class="demo-heading"><div><h2 id="playground-title">Laboratório de Tags</h2><p>Os exemplos abaixo demonstram ícones, estados, grupos e truncamento sem transformar a Tag inteira em botão.</p></div><span class="fs-badge fs-badge-outline-success">interativo</span></div>
|
|
85
|
+
<div class="demo-playground"><div class="demo-columns"><div>
|
|
86
|
+
<div class="demo-group"><h3>Ícones e estados</h3><p>Use ícones decorativos quando o texto já comunica o significado. O botão de fechar permanece a única ação.</p><div class="demo-sample"><span class="demo-label">Ícone inicial</span><span class="fs-badge fs-tag fs-badge-soft-primary" data-fs="tag"><span class="fs-tag-icon" aria-hidden="true"><svg class="fs-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 13V6a2 2 0 0 0-2-2H6a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h7"/><path d="M16 2v4M8 2v4M4 10h16"/><path d="m16 16 2 2 4-4"/></svg></span><span class="fs-tag-label">Documento</span><button type="button" class="fs-btn-close" data-fs-dismiss="tag" aria-label="Remover tag Documento"></button></span></div><div class="demo-sample"><span class="demo-label">Ícone final</span><span class="fs-badge fs-tag fs-badge-soft-success" data-fs="tag"><span class="fs-tag-label">Sincronizado</span><span class="fs-tag-icon fs-tag-icon-end" aria-hidden="true"><svg class="fs-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m5 12 4 4L19 6"/></svg></span><button type="button" class="fs-btn-close" data-fs-dismiss="tag" aria-label="Remover tag Sincronizado"></button></span></div><div class="demo-sample"><span class="demo-label">Loading</span><span class="fs-badge fs-tag fs-badge-warning is-loading" data-fs="tag" aria-busy="true"><span class="fs-tag-label">Salvando</span><span class="fs-spinner" aria-hidden="true"></span><button type="button" class="fs-btn-close" data-fs-dismiss="tag" aria-label="Remover tag Salvando" disabled></button></span></div></div>
|
|
87
|
+
<div class="demo-group"><h3>Grupo, truncamento e overflow</h3><p>Em grupos densos, o foco revela a Tag ativa e o overflow representa itens que não cabem.</p><div class="demo-sample"><span class="demo-label">Grupo</span><div class="fs-tag-group" aria-label="Filtros selecionados"><span class="fs-badge fs-tag" data-fs="tag">Design<button type="button" class="fs-btn-close" data-fs-dismiss="tag" aria-label="Remover tag Design"></button></span><span class="fs-badge fs-tag" data-fs="tag">Frontend<button type="button" class="fs-btn-close" data-fs-dismiss="tag" aria-label="Remover tag Frontend"></button></span><span class="fs-badge fs-tag" data-fs="tag">Backend<button type="button" class="fs-btn-close" data-fs-dismiss="tag" aria-label="Remover tag Backend"></button></span><button type="button" class="fs-badge fs-tag fs-tag-overflow" aria-label="Mostrar mais 3 tags" aria-expanded="false">+3</button></div></div><div class="demo-sample"><span class="demo-label">Truncada</span><span class="fs-badge fs-tag fs-tag-truncate fs-badge-soft-primary" data-fs="tag" style="--fs-tag-max-inline-size: 230px;" title="Revisão de acessibilidade e compatibilidade entre navegadores"><span class="fs-tag-icon" aria-hidden="true">◆</span><span class="fs-tag-label">Revisão de acessibilidade e compatibilidade entre navegadores</span><button type="button" class="fs-btn-close" data-fs-dismiss="tag" aria-label="Remover tag Revisão de acessibilidade e compatibilidade entre navegadores"></button></span></div></div>
|
|
88
|
+
<div class="demo-group"><h3>Protegida</h3><p>Quando a seleção não pode ser removida, não renderize um botão desabilitado: comunique a proteção.</p><div class="demo-sample"><span class="demo-label">Somente leitura</span><span class="fs-badge fs-tag fs-badge-warning fs-tag-protected" aria-disabled="true"><span class="fs-tag-label">Obrigatória</span><span class="fs-tag-lock" aria-hidden="true"><svg class="fs-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="18" height="11" x="3" y="11" rx="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/></svg></span></span></div></div>
|
|
89
|
+
</div><aside class="demo-status" aria-labelledby="status-title"><div><h3 id="status-title">Contrato da Tag</h3><p>A Tag herda a aparência do Badge, mas somente o botão de fechar remove o elemento.</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-badge.fs-tag
|
|
90
|
+
.fs-tag-icon
|
|
91
|
+
.fs-tag-icon-end
|
|
92
|
+
.fs-tag-label
|
|
93
|
+
.fs-btn-close
|
|
94
|
+
.fs-tag-truncate
|
|
95
|
+
.fs-tag-group
|
|
101
96
|
|
|
97
|
+
/* tokens locais */
|
|
98
|
+
--fs-tag-icon-size
|
|
99
|
+
--fs-tag-icon-gap
|
|
100
|
+
--fs-tag-close-size</code></pre></aside></div></div>
|
|
101
|
+
</section>
|
|
102
102
|
|
|
103
|
+
<section class="demo-section" aria-labelledby="a11y-title"><div class="demo-heading"><div><h2 id="a11y-title">Contratos preservados</h2><p>Ícones são apoio visual: estados importantes continuam escritos e o nome da remoção fica no botão.</p></div></div><div class="demo-reference"><div><h2>Uso acessível</h2><ul><li>Use <code>aria-hidden="true"</code> quando o ícone for decorativo.</li><li>Inclua o nome da Tag no <code>aria-label</code> do fechamento.</li><li>Não use Tag somente com ícone como padrão.</li><li>O texto completo continua no DOM quando houver truncamento.</li></ul></div><div><h2>Interação</h2><ul><li>Tab chega ao botão de remoção, não à Tag inteira.</li><li>Após remover com foco, a próxima Tag recebe foco.</li><li>Loading usa <code>aria-busy</code> e bloqueia remoção.</li><li>RTL usa propriedades lógicas e mantém a ordem visual.</li></ul></div></div></section>
|
|
104
|
+
</main>
|
|
103
105
|
<script src="../../dist/js/fokus.js"></script>
|
|
106
|
+
<script>
|
|
107
|
+
const page = document.querySelector(".demo-page");
|
|
108
|
+
const status = document.getElementById("demo-status");
|
|
109
|
+
const directionButton = document.getElementById("toggle-direction");
|
|
110
|
+
document.querySelectorAll('[data-fs-dismiss="tag"]').forEach((button) => button.addEventListener("click", () => { status.textContent = "Tag removida da demonstração."; }));
|
|
111
|
+
document.getElementById("reset-demo").addEventListener("click", () => { page.setAttribute("dir", "ltr"); directionButton.setAttribute("aria-pressed", "false"); directionButton.textContent = "Testar RTL"; status.textContent = "Demonstração restaurada em LTR."; });
|
|
112
|
+
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."; });
|
|
113
|
+
</script>
|
|
104
114
|
</body>
|
|
105
115
|
</html>
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="pt-BR">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>Fokus Styles — Toasts</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
|
+
<link rel="stylesheet" href="../assets/example-theme.css">
|
|
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-toast-row, .demo-action-row { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
|
|
23
|
+
.demo-toast-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 ul { margin: 0; padding-inline-start: 20px; color: var(--fs-color-muted); line-height: 1.8; }
|
|
50
|
+
.demo-reference code { color: var(--fs-color-primary); }
|
|
51
|
+
@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; } }
|
|
52
|
+
</style>
|
|
53
|
+
</head>
|
|
54
|
+
<body>
|
|
55
|
+
<main class="demo-page">
|
|
56
|
+
<header class="demo-hero"><div><p class="demo-eyebrow">Fokus Styles · feedback flutuante</p><h1 class="demo-title">Toasts que confirmam ações sem interromper o fluxo.</h1><p class="demo-lead">Toast é temporário, empilhado e acionado por JavaScript. Use-o para confirmações breves; para mensagens persistentes, prefira Alert.</p><div class="demo-toast-row" aria-label="Características"><span class="fs-badge fs-badge-soft-info">Flutuante</span><span class="fs-badge fs-badge-soft-success">Auto-dismiss</span><span class="fs-badge fs-badge-outline-primary">CSS + JS</span></div></div><span class="fs-badge fs-badge-outline-primary">role=status</span></header>
|
|
57
|
+
<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></div><span class="fs-form-text">Passe o mouse ou use Tab sobre o Toast para pausar o timer.</span></nav>
|
|
58
|
+
<section class="demo-section" aria-labelledby="why-title"><div class="demo-section-heading"><div><h2 id="why-title">Toast, Alert ou Notification Center?</h2><p>Escolha pela duração e pelo contexto da mensagem, sem transformar uma confirmação breve em uma interrupção.</p></div></div><div class="demo-grid"><article class="demo-card"><span class="demo-card-icon" aria-hidden="true">✓</span><h3>Toast confirma</h3><p>Feedback breve após uma ação não bloqueante, com auto-dismiss e pausa durante interação.</p><button class="fs-btn fs-btn-success fs-btn-sm" type="button" data-show-toast="success">Mostrar Toast</button></article><article class="demo-card"><span class="demo-card-icon" aria-hidden="true">!</span><h3>Alert permanece</h3><p>Mensagem contextual que precisa continuar visível na região afetada.</p><span class="fs-badge fs-btn-sm fs-badge-warning">Persistente</span></article><article class="demo-card"><span class="demo-card-icon" aria-hidden="true">≡</span><h3>Notification Center guarda</h3><p>Histórico para notificações que precisam ser revisitadas ou marcadas como lidas.</p><span class="fs-badge fs-btn-sm fs-badge-outline-info">Histórico</span></article></div></section>
|
|
59
|
+
<section class="demo-section" aria-labelledby="playground-title"><div class="demo-section-heading"><div><h2 id="playground-title">Laboratório de Toasts</h2><p>Compare variantes, slots, tempo, ações, empilhamento e comportamento em telas estreitas.</p></div><span class="fs-badge fs-badge-outline-success">interativo</span></div><div class="demo-playground"><div class="demo-playground-grid"><div><div class="demo-demo-group"><h3>Variantes e composição</h3><p>Ícones são apoio visual; títulos, corpo e ações permanecem compreensíveis sem depender da cor.</p><div class="demo-sample"><span class="demo-sample-label">Variantes</span><button class="fs-btn fs-btn-outline-success fs-btn-sm" type="button" data-show-toast="success">Sucesso</button><button class="fs-btn fs-btn-outline-warning fs-btn-sm" type="button" data-show-toast="warning">Atenção</button><button class="fs-btn fs-btn-outline-danger fs-btn-sm" type="button" data-show-toast="danger">Erro persistente</button></div><div class="demo-sample"><span class="demo-sample-label">Progresso</span><button class="fs-btn fs-btn-primary fs-btn-sm" type="button" data-show-toast="progress">Mostrar com progresso</button><button class="fs-btn fs-btn-secondary fs-btn-sm" type="button" data-show-toast="action">Mostrar com ação</button></div></div><div class="demo-demo-group"><h3>Empilhamento e responsividade</h3><p>Toasts usam uma única região fixa, preservam a largura em 320px e permanecem acima do conteúdo.</p><div class="demo-sample"><span class="demo-sample-label">Empilhar</span><button class="fs-btn fs-btn-outline-primary fs-btn-sm" type="button" id="stack-toast">Adicionar três Toasts</button></div><div class="demo-sample"><span class="demo-sample-label">Persistente</span><button class="fs-btn fs-btn-outline-danger fs-btn-sm" type="button" data-show-toast="persistent">Sem auto-dismiss</button></div></div><div class="demo-demo-group"><h3>Contratos de pausa</h3><p>Hover e foco pausam o timer; o fechamento manual não move o foco para outro Toast.</p><div class="demo-sample"><span class="demo-sample-label">Teclado</span><button class="fs-btn fs-btn-outline-secondary fs-btn-sm" type="button" data-show-toast="keyboard">Mostrar e navegar com Tab</button></div></div></div><aside class="demo-status" aria-labelledby="status-title"><div><h3 id="status-title">Contrato do Toast</h3><p>A API atual continua pública: `show`, `hide`, `toggle`, `dispose`, `data-delay` e `data-autohide`.</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-toast-container
|
|
60
|
+
.fs-toast
|
|
61
|
+
.fs-toast-header
|
|
62
|
+
.fs-toast-body
|
|
63
|
+
.fs-toast-icon
|
|
64
|
+
.fs-toast-actions
|
|
65
|
+
.fs-toast-progress
|
|
66
|
+
|
|
67
|
+
data-toast-progress="true"
|
|
68
|
+
data-delay="4000"
|
|
69
|
+
data-autohide="false"</code></pre></aside></div></div></section>
|
|
70
|
+
<section class="demo-section" aria-labelledby="a11y-title"><div class="demo-section-heading"><div><h2 id="a11y-title">Contratos preservados</h2><p>O Toast anuncia sem interromper, não rouba foco e oferece tempo para leitura e interação.</p></div></div><div class="demo-reference"><div><h2>Acessibilidade</h2><ul><li><code>role="status"</code> e <code>aria-live="polite"</code> são aplicados pelo JS.</li><li>O Toast não recebe foco automaticamente.</li><li>Hover e foco pausam o auto-dismiss.</li><li>Ícones decorativos usam <code>aria-hidden="true"</code>.</li></ul></div><div><h2>Limites</h2><ul><li>Use Alert para mensagens persistentes.</li><li>Use <code>data-autohide="false"</code> para erros que exigem ação.</li><li>Não comunique somente por cor ou ícone.</li><li>RTL, dark mode, reduced motion e forced colors são considerados.</li></ul></div></div></section>
|
|
71
|
+
</main>
|
|
72
|
+
<div class="fs-toast-container"><div class="fs-toast fs-toast-success" data-fs="toast" id="toast-success" data-delay="4000"><div class="fs-toast-header"><span class="fs-toast-icon" aria-hidden="true">✓</span><div class="fs-toast-heading"><strong class="fs-toast-title">Operação concluída</strong><span class="fs-toast-meta">Agora</span></div><button class="fs-btn-close fs-toast-close" type="button" data-fs-dismiss="toast" aria-label="Fechar operação concluída"></button></div><div class="fs-toast-body">O arquivo foi salvo com sucesso.</div></div><div class="fs-toast fs-toast-warning" data-fs="toast" id="toast-progress" data-delay="6000" data-toast-progress="true"><div class="fs-toast-header"><span class="fs-toast-icon" aria-hidden="true">!</span><div class="fs-toast-heading"><strong class="fs-toast-title">Atenção</strong><span class="fs-toast-meta" data-toast-countdown>6 segundos</span></div><button class="fs-btn-close fs-toast-close" type="button" data-fs-dismiss="toast" aria-label="Fechar atenção"></button></div><div class="fs-toast-body">Este Toast demonstra a barra de tempo restante.</div><div class="fs-toast-progress" aria-hidden="true"></div></div><div class="fs-toast fs-toast-danger" data-fs="toast" id="toast-persistent" data-autohide="false"><div class="fs-toast-header"><span class="fs-toast-icon" aria-hidden="true">!</span><div class="fs-toast-heading"><strong class="fs-toast-title">Falha ao sincronizar</strong></div><button class="fs-btn-close fs-toast-close" type="button" data-fs-dismiss="toast" aria-label="Fechar falha ao sincronizar"></button></div><div class="fs-toast-body">Tente novamente ou consulte os detalhes da operação.</div><div class="fs-toast-actions"><button type="button" class="fs-btn fs-btn-danger fs-btn-sm">Tentar novamente</button><a href="#detalhes" class="fs-toast-link">Ver detalhes</a></div></div></div>
|
|
73
|
+
<script src="../../dist/js/fokus.js"></script><script>
|
|
74
|
+
const page = document.querySelector(".demo-page");
|
|
75
|
+
const status = document.getElementById("demo-status");
|
|
76
|
+
const directionButton = document.getElementById("toggle-direction");
|
|
77
|
+
const countdownTimers = new Map();
|
|
78
|
+
const updateCountdown = (toast) => {
|
|
79
|
+
const label = toast.querySelector("[data-toast-countdown]");
|
|
80
|
+
if (!label) return;
|
|
81
|
+
const delay = Number(toast.dataset.delay) || 4000;
|
|
82
|
+
const state = countdownTimers.get(toast) || { remaining: delay, last: performance.now() };
|
|
83
|
+
if (!toast.classList.contains("is-paused") && getComputedStyle(toast).display !== "none") {
|
|
84
|
+
const now = performance.now();
|
|
85
|
+
state.remaining = Math.max(0, state.remaining - (now - state.last));
|
|
86
|
+
state.last = now;
|
|
87
|
+
} else {
|
|
88
|
+
state.last = performance.now();
|
|
89
|
+
}
|
|
90
|
+
label.textContent = `${Math.ceil(state.remaining / 1000)} segundos`;
|
|
91
|
+
countdownTimers.set(toast, state);
|
|
92
|
+
};
|
|
93
|
+
const show = (id) => {
|
|
94
|
+
const toast = document.getElementById(id);
|
|
95
|
+
const instance = FokusStyles.Toast.getInstance(toast);
|
|
96
|
+
const delay = Number(toast.dataset.delay) || 4000;
|
|
97
|
+
countdownTimers.set(toast, { remaining: delay, last: performance.now() });
|
|
98
|
+
instance.show();
|
|
99
|
+
updateCountdown(toast);
|
|
100
|
+
status.textContent = `Toast ${toast.className.match(/fs-toast-(\\w+)/)?.[1] || ""} exibido.`;
|
|
101
|
+
};
|
|
102
|
+
setInterval(() => document.querySelectorAll("[data-toast-countdown]").forEach((label) => updateCountdown(label.closest(".fs-toast"))), 100);
|
|
103
|
+
document.querySelectorAll("[data-show-toast]").forEach((button) => button.addEventListener("click", () => {
|
|
104
|
+
const type = button.dataset.showToast;
|
|
105
|
+
show(type === "progress" ? "toast-progress" : type === "warning" ? "toast-progress" : type === "danger" || type === "persistent" ? "toast-persistent" : "toast-success");
|
|
106
|
+
}));
|
|
107
|
+
document.getElementById("stack-toast").addEventListener("click", () => { show("toast-success"); setTimeout(() => show("toast-progress"), 80); setTimeout(() => show("toast-persistent"), 160); });
|
|
108
|
+
document.getElementById("reset-demo").addEventListener("click", () => location.reload());
|
|
109
|
+
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."; });
|
|
110
|
+
document.querySelectorAll(".fs-toast").forEach((toast) => toast.addEventListener("fs:toast:hidden", () => { status.textContent = "Toast ocultado."; }));
|
|
111
|
+
</script>
|
|
112
|
+
</body>
|
|
113
|
+
</html>
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="pt-BR">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
|
+
<title>Fokus Styles — Tooltip</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
|
+
<link rel="stylesheet" href="../assets/example-theme.css">
|
|
11
|
+
<script src="../assets/example-theme.js"></script>
|
|
12
|
+
<style>
|
|
13
|
+
.tooltip-stage { display: grid; min-block-size: 240px; place-items: center; padding: 32px; border: 1px dashed var(--fs-color-border); border-radius: var(--fs-radius-md); background: var(--fs-color-subtle); }
|
|
14
|
+
.tooltip-actions { display: flex; flex-wrap: wrap; align-items: center; justify-content: center; gap: 10px; }
|
|
15
|
+
.tooltip-truncated { max-inline-size: 220px; margin: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
16
|
+
.tooltip-contract code { display: block; padding: 12px; border-radius: var(--fs-radius-sm); background: var(--fs-color-subtle); font-size: .8rem; line-height: 1.7; }
|
|
17
|
+
:root { --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); }
|
|
18
|
+
* { box-sizing: border-box; }
|
|
19
|
+
body { min-block-size: 100vh; margin: 0; color: var(--fs-color-text); background: var(--fs-color-bg-subtle); }
|
|
20
|
+
.demo-page { max-inline-size: 1080px; margin-inline: auto; padding: clamp(20px, 4vw, 56px) clamp(16px, 4vw, 40px) 64px; }
|
|
21
|
+
.showcase-hero { margin-block-end: 32px; }
|
|
22
|
+
.showcase-eyebrow { margin: 0 0 8px; color: var(--demo-accent); font-size: .75rem; font-weight: 700; letter-spacing: .08em; text-transform: uppercase; }
|
|
23
|
+
.showcase-title { max-inline-size: 760px; margin: 0; font-size: clamp(2rem, 4vw, 3.5rem); line-height: 1.05; letter-spacing: -.04em; }
|
|
24
|
+
.showcase-lead { max-inline-size: 720px; margin: 16px 0 0; color: var(--fs-color-muted); font-size: 1.05rem; line-height: 1.6; }
|
|
25
|
+
.demo-badges { display: flex; flex-wrap: wrap; gap: 8px; margin-block-start: 20px; }
|
|
26
|
+
.showcase-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); }
|
|
27
|
+
.demo-section { margin-block-start: 32px; }
|
|
28
|
+
.demo-heading { display: flex; flex-wrap: wrap; align-items: end; justify-content: space-between; gap: 12px; margin-block-end: 14px; }
|
|
29
|
+
.demo-heading h2, .demo-card h3, .demo-status h3 { margin: 0; }
|
|
30
|
+
.demo-heading p, .demo-card p, .demo-status p { color: var(--fs-color-muted); }
|
|
31
|
+
.demo-heading p { max-inline-size: 650px; margin: 0; font-size: .9rem; }
|
|
32
|
+
.demo-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 16px; }
|
|
33
|
+
.demo-card, .demo-playground, .demo-status { 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); }
|
|
34
|
+
.demo-card { min-block-size: 180px; padding: 20px; }
|
|
35
|
+
.demo-card p { margin: 10px 0 20px; line-height: 1.55; }
|
|
36
|
+
.demo-card-kicker { display: block; margin-block-end: 8px; color: var(--fs-color-muted); font-size: .75rem; font-weight: 700; letter-spacing: .08em; text-transform: uppercase; }
|
|
37
|
+
.demo-playground { padding: clamp(18px, 3vw, 28px); }
|
|
38
|
+
.demo-playground > .demo-grid { margin-block-start: 24px; }
|
|
39
|
+
.demo-status { display: flex; flex-direction: column; gap: 16px; margin-block-start: 24px; padding: 20px; background: var(--demo-soft-bg); }
|
|
40
|
+
.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; }
|
|
41
|
+
@media (max-width: 760px) { .demo-grid { grid-template-columns: 1fr; } .showcase-toolbar { justify-content: flex-start; } }
|
|
42
|
+
</style>
|
|
43
|
+
</head>
|
|
44
|
+
<body>
|
|
45
|
+
<main class="showcase-shell demo-page">
|
|
46
|
+
<header class="showcase-hero">
|
|
47
|
+
<p class="showcase-eyebrow">Fokus Styles · extensão de interface</p>
|
|
48
|
+
<h1 class="showcase-title">Tooltip</h1>
|
|
49
|
+
<p class="showcase-lead">Dicas curtas para complementar um controle existente, com foco, hover, posicionamento adaptável e suporte aos temas do sistema.</p>
|
|
50
|
+
<div class="demo-badges" aria-label="Características do componente"><span class="fs-badge fs-badge-outline-primary">não interativo</span><span class="fs-badge fs-badge-outline-success">acessível por foco</span><span class="fs-badge fs-badge-outline-secondary">role=tooltip</span></div>
|
|
51
|
+
</header>
|
|
52
|
+
<div class="showcase-toolbar" aria-label="Controles da demonstração"><button class="fs-btn fs-btn-primary" type="button" data-showcase-theme>Usar tema escuro</button><button class="fs-btn" type="button" id="toggle-direction" aria-pressed="false">Testar RTL</button><button class="fs-btn" type="button" id="reset-demo">Resetar</button></div>
|
|
53
|
+
<section class="demo-section" aria-labelledby="compare-title"><div class="demo-heading"><div><h2 id="compare-title">Escolha o padrão certo</h2><p>Use cada componente conforme a quantidade e a importância do conteúdo.</p></div></div><div class="demo-grid"><article class="demo-card"><span class="demo-card-kicker">breve</span><h3>Tooltip</h3><p>Explica rapidamente um ícone, termo ou controle já nomeado.</p><span class="fs-badge fs-badge-outline-info">não interativo</span></article><article class="demo-card"><span class="demo-card-kicker">contextual</span><h3>Popover</h3><p>Oferece contexto mais rico, descrição, links ou ações.</p><span class="fs-badge fs-badge-outline-warning">conteúdo rico</span></article><article class="demo-card"><span class="demo-card-kicker">essencial</span><h3>Texto visível</h3><p>Instruções, erros e informações importantes não devem ficar ocultos.</p><span class="fs-badge fs-badge-outline-success">sempre disponível</span></article></div></section>
|
|
54
|
+
<section class="demo-section" aria-labelledby="playground-title"><div class="demo-heading"><div><h2 id="playground-title">Laboratório de Tooltip</h2><p>Experimente posições, delays, foco, fallback de viewport e truncamento.</p></div><span class="fs-badge fs-badge-outline-success">interativo</span></div><div class="demo-playground"><div class="tooltip-stage" aria-label="Controles de posicionamento"><div class="tooltip-actions"><button class="fs-btn fs-btn-outline-primary" type="button" data-fs="tooltip" data-placement="top" title="Tooltip acima do controle">Top</button><button class="fs-btn fs-btn-outline-primary" type="button" data-fs="tooltip" data-placement="bottom" title="Tooltip abaixo do controle">Bottom</button><button class="fs-btn fs-btn-outline-primary" type="button" data-fs="tooltip" data-placement="left" title="Tooltip à esquerda">Left</button><button class="fs-btn fs-btn-outline-primary" type="button" data-fs="tooltip" data-placement="right" title="Tooltip à direita">Right</button><button class="fs-btn fs-btn-outline-warning" type="button" data-fs="tooltip" data-show-delay="400" data-hide-delay="150" title="Aparece após um pequeno atraso">Com delay</button><button class="fs-btn-close" type="button" aria-label="Ajuda sobre configurações" data-fs="tooltip" title="Ajuda sobre configurações"></button></div></div><div class="demo-grid"><article class="demo-card"><span class="demo-card-kicker">conteúdo</span><h3>Truncamento e fallback</h3><p class="tooltip-truncated" data-fs="tooltip" data-title="Este é o texto completo preservado para o usuário">Este texto foi truncado visualmente</p><button class="fs-btn fs-btn-outline-danger fs-btn-sm" type="button" data-fs="tooltip" data-placement="bottom" title="Uma mensagem longa quebra em várias linhas sem causar overflow horizontal">Mensagem longa</button></article><article class="demo-card"><span class="demo-card-kicker">teclado</span><h3>Foco e Escape</h3><p>Use Tab para entrar no controle, leia a dica e pressione Escape para fechar.</p><button class="fs-btn fs-btn-success fs-btn-sm" type="button" data-fs="tooltip" title="A dica também aparece quando o controle recebe foco">Focar com teclado</button></article></div><aside class="demo-status tooltip-contract" aria-labelledby="contract-title"><div><h3 id="contract-title">Contrato do componente</h3><p>O Tooltip complementa o gatilho e nunca contém ações próprias.</p></div><p id="demo-status" class="demo-status-output" role="status" aria-live="polite">Demonstração pronta.</p><code>data-fs="tooltip" · title / data-title · data-placement · data-show-delay / data-hide-delay</code></aside></div></section>
|
|
55
|
+
<section class="demo-section" aria-labelledby="a11y-title"><div class="demo-heading"><div><h2 id="a11y-title">Acessibilidade e limites</h2><p>O conteúdo essencial continua visível e o Tooltip não assume o papel de Popover.</p></div></div><div class="demo-grid"><article class="demo-card"><h3>Foco</h3><p>O foco permanece no elemento de referência. Escape fecha a dica sem criar um novo foco.</p></article><article class="demo-card"><h3>Sem interação</h3><p>Não coloque links, botões ou controles dentro de um Tooltip. Use Popover quando necessário.</p></article><article class="demo-card"><h3>Sem dependência de cor</h3><p>Ícones, hover e cor apenas complementam uma mensagem que já deve ser compreensível.</p></article></div></section>
|
|
56
|
+
<section class="demo-section" aria-labelledby="alignment-title"><div class="demo-heading"><div><h2 id="alignment-title">Alinhamento e offset</h2><p>A API aceita combinações como <code>top-start</code> e preserva o alinhamento durante o fallback.</p></div></div><div class="demo-grid"><article class="demo-card"><h3>Superior</h3><div class="tooltip-actions"><button class="fs-btn fs-btn-sm fs-btn-outline-primary" data-fs="tooltip" data-placement="top-start" title="Superior à esquerda">start</button><button class="fs-btn fs-btn-sm fs-btn-outline-primary" data-fs="tooltip" data-placement="top-end" title="Superior à direita">end</button></div></article><article class="demo-card"><h3>Inferior</h3><div class="tooltip-actions"><button class="fs-btn fs-btn-sm fs-btn-outline-primary" data-fs="tooltip" data-placement="bottom-start" title="Inferior à esquerda">start</button><button class="fs-btn fs-btn-sm fs-btn-outline-primary" data-fs="tooltip" data-placement="bottom-end" title="Inferior à direita">end</button></div></article><article class="demo-card"><h3>Distância</h3><button class="fs-btn fs-btn-sm fs-btn-outline-warning" data-fs="tooltip" data-offset="16" title="Este Tooltip usa um offset de 16 pixels">Offset 16px</button></article></div></section>
|
|
57
|
+
</main>
|
|
58
|
+
<script src="../../dist/js/fokus.js"></script>
|
|
59
|
+
<script>
|
|
60
|
+
const page = document.querySelector(".demo-page");
|
|
61
|
+
const status = document.getElementById("demo-status");
|
|
62
|
+
const direction = document.getElementById("toggle-direction");
|
|
63
|
+
direction.addEventListener("click", () => { const rtl = page.getAttribute("dir") !== "rtl"; page.setAttribute("dir", rtl ? "rtl" : "ltr"); direction.setAttribute("aria-pressed", String(rtl)); direction.textContent = rtl ? "Voltar para LTR" : "Testar RTL"; status.textContent = rtl ? "Direção RTL ativada." : "Direção LTR ativada."; });
|
|
64
|
+
document.getElementById("reset-demo").addEventListener("click", () => location.reload());
|
|
65
|
+
document.querySelectorAll('[data-fs="tooltip"]').forEach((element) => { element.addEventListener("fs:tooltip:shown", () => { const open = document.querySelector(".fs-tooltip.is-open"); status.textContent = `Tooltip aberto · placement efetivo: ${open?.dataset.placement ?? "auto"}.`; }); element.addEventListener("fs:tooltip:hidden", () => { status.textContent = "Tooltip fechado."; }); });
|
|
66
|
+
</script>
|
|
67
|
+
</body>
|
|
68
|
+
</html>
|
package/mockup/forms.html
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
{"id":"input","title":"Input","description":"Campos de texto, e-mail, senha e textarea com ajuda e validação.","use":"Colete uma informação por campo com label persistente.","api":".fs-form-control, .is-valid, .is-invalid e texto de ajuda.","a11y":"Associe label, descrição e erro por for/id e aria-describedby.","states":"Foco, disabled, readonly, sucesso e erro.","source":"examples/forms-advanced.html","height":900},
|
|
3
3
|
{"id":"select","title":"Select","description":"Seleção nativa estilizada para opções conhecidas e limitadas.","use":"Use quando a lista é curta; use combobox para busca.","api":".fs-select, option e atributos nativos.","a11y":"Mantenha label explícito e não simule select nativo sem necessidade.","states":"Placeholder, disabled, erro e integração em modal.","source":"examples/modal-select.html","height":820},
|
|
4
4
|
{"id":"input-group","title":"Input group","description":"Campo combinado com prefixos, sufixos e ações relacionadas.","use":"Agrupe somente controles que fazem parte da mesma entrada.","api":".fs-input-group e seus addons.","a11y":"Não esconda a finalidade do campo atrás de ícone sem rótulo.","states":"Prefixo, sufixo, botão e validação.","source":"examples/input-group.html","height":700},
|
|
5
|
-
{"id":"check-radio-switch","title":"Checkbox, radio e switch","description":"Controles
|
|
5
|
+
{"id":"check-radio-switch","title":"Checkbox, radio e switch","description":"Controles nativos refinados para escolha múltipla, escolha única e alternância de preferência.","use":"Checkbox para escolhas independentes, radio para exclusivas e switch para estado imediato.","api":".fs-check, .fs-radio, .fs-switch e tokens locais.","a11y":"Use fieldset/legend para grupos, aria-describedby para ajuda/erro e nome acessível em switch sem texto.","states":"Hover, active, marcado, indeterminado, valid, invalid, disabled, RTL e foco.","source":"examples/check-radio-switch.html","height":1120},
|
|
6
6
|
{"id":"range","title":"Range","description":"Entrada numérica contínua com limites conhecidos.","use":"Use quando a aproximação visual é mais útil que digitação precisa.","api":"input[type=range] e classes FokusStyles associadas.","a11y":"Forneça label, mínimo, máximo e valor atual compreensível.","states":"Valores, disabled e marcas de referência.","source":"examples/range.html","height":700},
|
|
7
7
|
{"id":"segmented-control","title":"Segmented control","description":"Alternância rápida entre poucas opções relacionadas.","use":"Use para modos mutuamente exclusivos de uma mesma visão.","api":".fs-segmented-control e controles internos.","a11y":"Use radio/tab conforme a semântica; mantenha estado selecionado anunciado.","states":"Selecionado, foco, disabled e ícones.","source":"examples/segmented-control.html","height":760},
|
|
8
8
|
{"id":"rating","title":"Rating","description":"Avaliação ordinal com estrelas ou equivalentes.","use":"Use para uma escala pequena e familiar; exponha também o valor textual.","api":".fs-rating e controles internos.","a11y":"Disponibilize valor e instrução para teclado e leitor de tela.","states":"Leitura, edição, meio valor e disabled.","source":"examples/rating.html","height":1100},
|