fokus-styles 2.0.0 → 2.3.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 +13 -0
- package/README.md +4 -3
- package/dist/css/components.css +510 -2
- 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 +4050 -0
- package/dist/css/fokus-components.css.map +1 -0
- package/dist/css/fokus-components.min.css +2 -0
- package/dist/css/fokus-components.min.css.map +1 -0
- package/dist/css/fokus-core.css +1563 -0
- package/dist/css/fokus-core.css.map +1 -0
- package/dist/css/fokus-core.min.css +2 -0
- package/dist/css/fokus-core.min.css.map +1 -0
- package/dist/css/fokus-dark.css +304 -0
- package/dist/css/fokus-dark.css.map +1 -0
- package/dist/css/fokus-dark.min.css +2 -0
- package/dist/css/fokus-dark.min.css.map +1 -0
- package/dist/css/fokus-utilities.css +4176 -0
- package/dist/css/fokus-utilities.css.map +1 -0
- package/dist/css/fokus-utilities.min.css +2 -0
- package/dist/css/fokus-utilities.min.css.map +1 -0
- package/dist/css/fokus.css +1339 -53
- 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 +115 -2
- 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 +376 -2
- 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 +560 -2
- 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 +347 -12
- package/dist/js/fokus.js.map +4 -4
- package/dist/js/fokus.min.js +17 -17
- package/dist/js/fokus.min.js.map +4 -4
- package/docs/components/accordion.md +3 -0
- package/docs/components/avatar.md +13 -0
- package/docs/components/button-group.md +13 -0
- package/docs/components/close-button.md +13 -0
- package/docs/components/code.md +13 -0
- package/docs/components/icon-link.md +13 -0
- package/docs/components/list-group.md +13 -0
- package/docs/components/modal.md +3 -0
- package/docs/components/navbar.md +3 -0
- package/docs/components/pagination.md +3 -0
- package/docs/components/placeholder.md +13 -0
- package/docs/components/ratio.md +13 -0
- package/docs/components/scrollspy.md +13 -0
- package/docs/components/skeleton.md +3 -0
- package/docs/components/table.md +3 -0
- package/docs/guides/javascript-api.md +9 -0
- package/docs/guides/utility-api.md +9 -0
- package/docs/reference/size-baseline.json +13 -13
- package/mockup/assets/example-theme.css +3 -1
- package/mockup/assets/example-theme.js +2 -0
- package/mockup/assets/showcase-contracts.js +2 -2
- package/mockup/assets/showcase.css +4 -2
- package/mockup/assets/showcase.js +2 -0
- package/mockup/examples/js-foundation.html +25 -0
- package/mockup/examples/tables-navbar.html +12 -8
- package/mockup/examples/theming.html +2 -1
- package/mockup/foundations.html +2 -2
- package/package.json +14 -1
- package/packages/fokus-cli/bin/fokus.mjs +18 -1
- package/packages/fokus-cli/lib/build.mjs +12 -2
- package/packages/fokus-components/scss/components/_accordion.scss +19 -0
- package/packages/fokus-components/scss/components/_avatar.scss +22 -0
- package/packages/fokus-components/scss/components/_button-group.scss +14 -0
- package/packages/fokus-components/scss/components/_close-button.scss +22 -0
- package/packages/fokus-components/scss/components/_code.scss +19 -0
- package/packages/fokus-components/scss/components/_icon-link.scss +8 -0
- package/packages/fokus-components/scss/components/_index.scss +10 -0
- package/packages/fokus-components/scss/components/_list-group.scss +26 -0
- package/packages/fokus-components/scss/components/_modal.scss +36 -0
- package/packages/fokus-components/scss/components/_navbar.scss +48 -0
- package/packages/fokus-components/scss/components/_offcanvas.scss +19 -0
- package/packages/fokus-components/scss/components/_pagination.scss +10 -0
- package/packages/fokus-components/scss/components/_placeholder.scss +13 -0
- package/packages/fokus-components/scss/components/_progress.scss +8 -0
- package/packages/fokus-components/scss/components/_ratio.scss +10 -0
- package/packages/fokus-components/scss/components/_scrollspy.scss +5 -0
- package/packages/fokus-components/scss/components/_skeleton.scss +19 -0
- package/packages/fokus-components/scss/components/_spinner.scss +9 -53
- package/packages/fokus-components/scss/components/_tables.scss +20 -0
- package/packages/fokus-components/scss/forms/_forms.scss +29 -0
- package/packages/fokus-core/scss/base/_accessibility.scss +45 -0
- package/packages/fokus-core/scss/base/_index.scss +1 -0
- package/packages/fokus-core/scss/layout/_grid.scss +18 -0
- package/packages/fokus-core/scss/settings/_config.scss +43 -0
- package/packages/fokus-core/scss/settings/_index.scss +1 -0
- package/packages/fokus-core/scss/themes/_dark.scss +38 -2
- package/packages/fokus-core/scss/tokens/_root.scss +14 -0
- package/packages/fokus-core/scss/tools/_mixins.scss +8 -0
- package/packages/fokus-js/js/accordion.js +7 -1
- package/packages/fokus-js/js/core/focus.js +20 -0
- package/packages/fokus-js/js/core/overlay.d.ts +2 -0
- package/packages/fokus-js/js/core/overlay.js +10 -0
- package/packages/fokus-js/js/core/register.js +4 -2
- package/packages/fokus-js/js/focus-trap.d.ts +3 -0
- package/packages/fokus-js/js/focus-trap.js +1 -0
- package/packages/fokus-js/js/fokus.d.ts +7 -0
- package/packages/fokus-js/js/fokus.js +7 -0
- package/packages/fokus-js/js/form-validation.d.ts +7 -0
- package/packages/fokus-js/js/form-validation.js +20 -0
- package/packages/fokus-js/js/modal.js +16 -2
- package/packages/fokus-js/js/navbar.d.ts +10 -0
- package/packages/fokus-js/js/navbar.js +27 -0
- package/packages/fokus-js/js/offcanvas.d.ts +4 -0
- package/packages/fokus-js/js/offcanvas.js +16 -2
- package/packages/fokus-js/js/pagination.d.ts +7 -0
- package/packages/fokus-js/js/pagination.js +38 -0
- package/packages/fokus-js/js/scrollspy.d.ts +7 -0
- package/packages/fokus-js/js/scrollspy.js +26 -0
- package/packages/fokus-js/js/spinner.d.ts +9 -0
- package/packages/fokus-js/js/spinner.js +18 -0
- package/packages/fokus-js/js/theme.d.ts +9 -0
- package/packages/fokus-js/js/theme.js +19 -0
- package/packages/fokus-js/js/toast.js +4 -0
- package/packages/fokus-utilities/scss/utilities/_api.scss +48 -0
- package/packages/fokus-utilities/scss/utilities/_index.scss +2 -0
- package/packages/fokus-utilities/scss/utilities/_states.scss +19 -0
- package/packages/fokus-utilities/scss/utilities/_visibility.scss +24 -0
- package/scss/entries/core-entry.scss +5 -0
- package/scss/entries/dark-entry.scss +3 -0
|
@@ -55,6 +55,9 @@ força todos fechados).
|
|
|
55
55
|
|
|
56
56
|
## A11y
|
|
57
57
|
|
|
58
|
+
Use `.fs-accordion-flush` para remover bordas externas. Com `data-fs-always-open="true"`,
|
|
59
|
+
um painel aberto não pode ser fechado isoladamente.
|
|
60
|
+
|
|
58
61
|
`aria-controls`/`aria-expanded` (botão) e `role="region"`/
|
|
59
62
|
`aria-labelledby` (painel) aplicados automaticamente. Cada botão é um
|
|
60
63
|
`<button>` nativo dentro de um heading (`<h3>`) — navegável por `Tab` e
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Avatar
|
|
2
|
+
|
|
3
|
+
## Visão geral
|
|
4
|
+
|
|
5
|
+
Use `.fs-avatar` para representar uma pessoa, equipe ou entidade com imagem ou iniciais.
|
|
6
|
+
|
|
7
|
+
## Estados
|
|
8
|
+
|
|
9
|
+
Combine `.fs-avatar-sm`, `.fs-avatar-lg` e `.fs-avatar-square` conforme o contexto.
|
|
10
|
+
|
|
11
|
+
## A11y
|
|
12
|
+
|
|
13
|
+
Imagens devem ter `alt` descritivo; iniciais devem ter um nome acessível com `aria-label`.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Button group
|
|
2
|
+
|
|
3
|
+
## Visão geral
|
|
4
|
+
|
|
5
|
+
`.fs-button-group` agrupa ações relacionadas em uma unidade visual.
|
|
6
|
+
|
|
7
|
+
## Estados
|
|
8
|
+
|
|
9
|
+
Use `.fs-button-group-vertical` em barras verticais e mantenha apenas uma ação primária.
|
|
10
|
+
|
|
11
|
+
## A11y
|
|
12
|
+
|
|
13
|
+
Use `role="group"` e `aria-label` quando o agrupamento não tiver um título visível.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Close button
|
|
2
|
+
|
|
3
|
+
## Visão geral
|
|
4
|
+
|
|
5
|
+
`.fs-close` fornece um controle compacto para fechar painéis, diálogos e toasts.
|
|
6
|
+
|
|
7
|
+
## Estados
|
|
8
|
+
|
|
9
|
+
O estado hover e o focus-visible são fornecidos pelo componente.
|
|
10
|
+
|
|
11
|
+
## A11y
|
|
12
|
+
|
|
13
|
+
Sempre informe `aria-label="Fechar"` e associe o controle ao conteúdo que ele fecha.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Code
|
|
2
|
+
|
|
3
|
+
## Visão geral
|
|
4
|
+
|
|
5
|
+
`.fs-code` estiliza código inline e `.fs-pre` apresenta blocos pré-formatados.
|
|
6
|
+
|
|
7
|
+
## Estados
|
|
8
|
+
|
|
9
|
+
O bloco mantém rolagem horizontal para preservar linhas longas.
|
|
10
|
+
|
|
11
|
+
## A11y
|
|
12
|
+
|
|
13
|
+
Use `code` para trechos curtos e `pre` para blocos; não transmita informação somente por cor.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Icon link
|
|
2
|
+
|
|
3
|
+
## Visão geral
|
|
4
|
+
|
|
5
|
+
`.fs-icon-link` alinha um ícone a um link textual.
|
|
6
|
+
|
|
7
|
+
## Estados
|
|
8
|
+
|
|
9
|
+
O ícone acompanha o hover do link e respeita redução de movimento.
|
|
10
|
+
|
|
11
|
+
## A11y
|
|
12
|
+
|
|
13
|
+
O texto do link deve continuar presente; o ícone decorativo deve usar `aria-hidden="true"`.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# List group
|
|
2
|
+
|
|
3
|
+
## Visão geral
|
|
4
|
+
|
|
5
|
+
`.fs-list-group` e `.fs-list-group-item` compõem listas de navegação ou seleção.
|
|
6
|
+
|
|
7
|
+
## Estados
|
|
8
|
+
|
|
9
|
+
Use `.is-active` para o item atual e elementos `a` ou `button` para itens interativos.
|
|
10
|
+
|
|
11
|
+
## A11y
|
|
12
|
+
|
|
13
|
+
Escolha uma lista semântica e forneça nome ao grupo quando ele representar uma região.
|
package/docs/components/modal.md
CHANGED
|
@@ -61,6 +61,9 @@ explícito fecha. Use para fluxos que exigem uma decisão explícita.
|
|
|
61
61
|
|
|
62
62
|
## A11y
|
|
63
63
|
|
|
64
|
+
Use `.fs-modal-sm`, `.fs-modal-lg`, `.fs-modal-xl`, `.fs-modal-fullscreen` e
|
|
65
|
+
`.fs-modal-scrollable` conforme o fluxo. Em telas menores o diálogo respeita safe areas.
|
|
66
|
+
|
|
64
67
|
Ao inicializar, o componente controla `aria-hidden` e, quando encontra
|
|
65
68
|
`.fs-modal-title`, cria automaticamente o vínculo `aria-labelledby`. Um
|
|
66
69
|
`id` existente no título é preservado.
|
|
@@ -40,6 +40,9 @@ composição (o que vai dentro) que muda.
|
|
|
40
40
|
|
|
41
41
|
## A11y
|
|
42
42
|
|
|
43
|
+
O padrão responsivo usa `.fs-navbar-toggler`, `.fs-navbar-collapse` e `data-fs-target`.
|
|
44
|
+
Mantenha `aria-expanded` e um nome acessível no botão.
|
|
45
|
+
|
|
43
46
|
- Use `<nav>` como elemento raiz (já no exemplo) — landmark de navegação
|
|
44
47
|
pra leitores de tela.
|
|
45
48
|
- Se houver mais de uma navbar na página, diferencie com
|
|
@@ -98,6 +98,9 @@ de `aria-label` (`"Anterior"`/`"Próxima"`) já que o SVG é `aria-hidden`.
|
|
|
98
98
|
|
|
99
99
|
## A11y
|
|
100
100
|
|
|
101
|
+
Envolva a paginação em `.fs-pagination-scroll` quando houver muitos itens. Use `nav`
|
|
102
|
+
nomeado, `aria-current="page"` e desabilite controles sem destino real.
|
|
103
|
+
|
|
101
104
|
- Envolva a lista num `<nav aria-label="Paginação">` — o elemento `<nav>`
|
|
102
105
|
não é gerado pelo framework, adicione no seu HTML.
|
|
103
106
|
- Marque a página atual com `aria-current="page"` no link ativo, além de
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Placeholder
|
|
2
|
+
|
|
3
|
+
## Visão geral
|
|
4
|
+
|
|
5
|
+
`.fs-placeholder` representa conteúdo ainda carregando e é visualmente distinto de Skeleton.
|
|
6
|
+
|
|
7
|
+
## Estados
|
|
8
|
+
|
|
9
|
+
Use `.fs-placeholder-glow`, `.fs-placeholder-wave` ou `.fs-placeholder-btn` conforme o formato.
|
|
10
|
+
|
|
11
|
+
## A11y
|
|
12
|
+
|
|
13
|
+
Anuncie carregamento com `aria-busy="true"` no contêiner e uma região live quando necessário.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Ratio
|
|
2
|
+
|
|
3
|
+
## Visão geral
|
|
4
|
+
|
|
5
|
+
`.fs-ratio` cria frames de mídia responsivos sem depender da altura do viewport.
|
|
6
|
+
|
|
7
|
+
## Estados
|
|
8
|
+
|
|
9
|
+
Escolha `.fs-ratio-1x1`, `.fs-ratio-4x3`, `.fs-ratio-16x9` ou `.fs-ratio-21x9`.
|
|
10
|
+
|
|
11
|
+
## A11y
|
|
12
|
+
|
|
13
|
+
Mantenha texto alternativo em imagens e legendas ou título acessível em mídia incorporada.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Scrollspy
|
|
2
|
+
|
|
3
|
+
## Visão geral
|
|
4
|
+
|
|
5
|
+
`data-fs-scrollspy` destaca o link da seção atualmente visível e pode ser usado em navegação lateral.
|
|
6
|
+
|
|
7
|
+
## Estados
|
|
8
|
+
|
|
9
|
+
O módulo JS aplica `.is-active` e emite `fs:scrollspy:activate`.
|
|
10
|
+
|
|
11
|
+
## A11y
|
|
12
|
+
|
|
13
|
+
Use uma navegação nomeada, mantenha foco visível e não remova a indicação textual do destino.
|
|
@@ -38,6 +38,9 @@ dados reais chegarem.
|
|
|
38
38
|
|
|
39
39
|
## A11y
|
|
40
40
|
|
|
41
|
+
As variantes `.fs-skeleton-card`, `.fs-skeleton-table` e `.fs-skeleton-avatar` são visuais;
|
|
42
|
+
anuncie o carregamento no contêiner com `aria-busy="true"`.
|
|
43
|
+
|
|
41
44
|
A animação para automaticamente sob `prefers-reduced-motion: reduce`
|
|
42
45
|
(fica estática, sem pulso/varredura). Se o carregamento for anunciado a
|
|
43
46
|
leitores de tela, envolva o conjunto de skeletons num contêiner com
|
package/docs/components/table.md
CHANGED
|
@@ -53,6 +53,9 @@ opcional que reusa `.fs-table`.
|
|
|
53
53
|
|
|
54
54
|
## A11y
|
|
55
55
|
|
|
56
|
+
Use `.fs-table-responsive` para rolagem horizontal, `.fs-table-caption` para legenda e
|
|
57
|
+
`.fs-table-empty` para estados sem dados. `.fs-table-priority` preserva colunas essenciais.
|
|
58
|
+
|
|
56
59
|
- Use `<th scope="col">`/`<th scope="row">` conforme o cabeçalho for de
|
|
57
60
|
coluna ou linha — não é aplicado automaticamente.
|
|
58
61
|
- Para tabelas grandes/complexas, considere `<caption>` descrevendo o
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# API JavaScript
|
|
2
|
+
|
|
3
|
+
Os módulos vanilla são SSR-safe quando importados e expõem instâncias idempotentes por
|
|
4
|
+
`getOrCreateInstance`. Modal, Theme, Navbar, Scrollspy e FormValidation também oferecem
|
|
5
|
+
destruição explícita com `dispose()`.
|
|
6
|
+
|
|
7
|
+
Os eventos comuns são `fs:show`, `fs:shown`, `fs:hide` e `fs:hidden`. Atributos declarativos
|
|
8
|
+
usam `data-fs-toggle`, `data-fs-target`, `data-fs-dismiss`, `data-fs-placement` e
|
|
9
|
+
`data-fs-theme`.
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# Utility API e configuração Sass
|
|
2
|
+
|
|
3
|
+
O Fokus Styles oferece presets públicos em `fokus-styles/core`, `fokus-styles/components`,
|
|
4
|
+
`fokus-styles/utilities` e `fokus-styles/themes`. O arquivo `fokus-styles/config` expõe o
|
|
5
|
+
mapa `$fs-config` para entradas Sass customizadas.
|
|
6
|
+
|
|
7
|
+
O mixin `fs-fluid-type()` usa `clamp()` para escalar valores entre dois limites sem exigir
|
|
8
|
+
JavaScript. Classes utilitárias novas seguem o namespace `fs-u-*` e as classes existentes
|
|
9
|
+
continuam válidas dentro da linha 2.x.
|
|
@@ -1,34 +1,34 @@
|
|
|
1
1
|
{
|
|
2
|
-
"measuredAt": "2026-09-
|
|
2
|
+
"measuredAt": "2026-09-07T22:40:15.289Z",
|
|
3
3
|
"unit": "bytes",
|
|
4
4
|
"files": {
|
|
5
5
|
"css/layout.min.css": {
|
|
6
|
-
"bytes":
|
|
7
|
-
"gzipBytes":
|
|
6
|
+
"bytes": 28539,
|
|
7
|
+
"gzipBytes": 5951
|
|
8
8
|
},
|
|
9
9
|
"css/forms.min.css": {
|
|
10
|
-
"bytes":
|
|
11
|
-
"gzipBytes":
|
|
10
|
+
"bytes": 33746,
|
|
11
|
+
"gzipBytes": 6986
|
|
12
12
|
},
|
|
13
13
|
"css/components.min.css": {
|
|
14
|
-
"bytes":
|
|
15
|
-
"gzipBytes":
|
|
14
|
+
"bytes": 92904,
|
|
15
|
+
"gzipBytes": 16191
|
|
16
16
|
},
|
|
17
17
|
"css/helpers.min.css": {
|
|
18
|
-
"bytes":
|
|
19
|
-
"gzipBytes":
|
|
18
|
+
"bytes": 61032,
|
|
19
|
+
"gzipBytes": 10609
|
|
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":
|
|
27
|
-
"gzipBytes":
|
|
26
|
+
"bytes": 164376,
|
|
27
|
+
"gzipBytes": 25875
|
|
28
28
|
},
|
|
29
29
|
"js/fokus.min.js": {
|
|
30
|
-
"bytes":
|
|
31
|
-
"gzipBytes":
|
|
30
|
+
"bytes": 107627,
|
|
31
|
+
"gzipBytes": 23063
|
|
32
32
|
}
|
|
33
33
|
}
|
|
34
34
|
}
|
|
@@ -3,7 +3,9 @@
|
|
|
3
3
|
const exampleTheme = new URLSearchParams(window.location.search).get("theme");
|
|
4
4
|
|
|
5
5
|
if (exampleTheme === "dark") {
|
|
6
|
+
document.documentElement.setAttribute("data-fs-theme", "dark");
|
|
6
7
|
document.documentElement.setAttribute("data-theme", "dark");
|
|
7
8
|
} else {
|
|
9
|
+
document.documentElement.setAttribute("data-fs-theme", "light");
|
|
8
10
|
document.documentElement.removeAttribute("data-theme");
|
|
9
11
|
}
|
|
@@ -7,7 +7,7 @@ window.showcaseContracts = {
|
|
|
7
7
|
},
|
|
8
8
|
theming: {
|
|
9
9
|
use: [{ label: "Tema de produto", text: "Use para alternar claro/escuro ou aplicar uma marca sem sobrescrever cores de componentes isoladamente." }],
|
|
10
|
-
api: [{ label: "data-theme=\"dark\"", code: true, text: "
|
|
10
|
+
api: [{ label: "data-fs-theme=\"light|dark|auto\"", code: true, text: "controla o color mode canônico em html ou em um contêiner." }, { label: "data-theme=\"dark\"", code: true, text: "permanece como compatibilidade para consumidores existentes." }, { label: "data-fs-brand=\"violet|corporate|vibrant\"", code: true, text: "seleciona uma marca publicada; pode ser combinado com data-fs-theme." }, { label: "--fs-color-*", code: true, text: "tokens semânticos consumidos pelos componentes; evite trocar cores literais em cada componente." }],
|
|
11
11
|
a11y: [{ label: "Contraste", text: "revalide contraste após criar marca ou sobrescrever token." }, { label: "Preferência", text: "não alterne tema sem controle explícito e persistência coerente para a pessoa usuária." }],
|
|
12
12
|
states: [{ label: "Claro e escuro", text: "teste superfície, texto, borda, foco e componentes sobrepostos em ambos." }],
|
|
13
13
|
},
|
|
@@ -62,7 +62,7 @@ window.showcaseContracts = {
|
|
|
62
62
|
"empty-state": { use: [{ label: "Sem conteúdo", text: "Use para primeira utilização, busca sem resultado ou região sem dados." }], api: [{ label: ".fs-empty-state", code: true, text: "contêiner centralizado." }, { label: ".fs-empty-state-icon / -title / -text / -action", code: true, text: "organizam ilustração, mensagem e CTA." }], a11y: [{ label: "Próximo passo", text: "título e ação devem explicar a situação fora do contexto visual." }], states: [{ label: "Cenário", text: "valide sem dados, sem resultados e carregamento concluído." }] },
|
|
63
63
|
skeleton: { use: [{ label: "Carregamento previsível", text: "Use para reservar espaço de conteúdo temporário e reduzir salto de layout." }], api: [{ label: ".fs-skeleton", code: true, text: "bloco-base." }, { label: ".fs-skeleton-text / -avatar / -button / -card", code: true, text: "formas prontas para estruturas comuns." }], a11y: [{ label: "Decoração", text: "oculte o skeleton de leitores de tela e anuncie carregamento na região adequada." }], states: [{ label: "Movimento", text: "teste animação reduzida, conteúdo lento e substituição pelo resultado." }] },
|
|
64
64
|
progress: { use: [{ label: "Operação em curso", text: "Use spinner para espera indeterminada e progress para avanço mensurável." }], api: [{ label: ".fs-spinner / .fs-spinner-sm / .fs-spinner-lg", code: true, text: "indicador indeterminado." }, { label: ".fs-progress > .fs-progress-bar", code: true, text: "trilha e preenchimento." }, { label: "role=\"progressbar\" + aria-valuenow/min/max", code: true, text: "expõe valor de progresso." }], a11y: [{ label: "Status", text: "informe o que está acontecendo e valor atual quando houver." }], states: [{ label: "Operação", text: "teste indeterminado, 0%, intermediário, concluído e erro." }] },
|
|
65
|
-
navbar: { use: [{ label: "Navegação global", text: "Use para destinos principais do produto, não para ações locais." }], api: [{ label: ".fs-navbar / .fs-navbar-brand / .fs-navbar-nav", code: true, text: "estrutura de marca e links." }, { label: ".fs-navbar-toggler + data-fs=\"navbar\"", code: true, text: "ativa colapso no viewport compacto." }], a11y: [{ label: "Destino atual", text: "use aria-current=\"page\" no link ativo e nomeie o nav." }], states: [{ label: "Compacto", text: "teste aberto, fechado, teclado e foco no menu móvel." }] },
|
|
65
|
+
navbar: { use: [{ label: "Navegação global", text: "Use para destinos principais do produto, não para ações locais." }], api: [{ label: ".fs-navbar / .fs-navbar-brand / .fs-navbar-nav", code: true, text: "estrutura de marca e links." }, { label: ".fs-navbar-toggler + data-fs-toggle=\"navbar\" + data-fs-target", code: true, text: "ativa colapso no viewport compacto e mantém aria-expanded." }], a11y: [{ label: "Destino atual", text: "use aria-current=\"page\" no link ativo e nomeie o nav." }], states: [{ label: "Compacto", text: "teste aberto, fechado, teclado e foco no menu móvel." }] },
|
|
66
66
|
breadcrumb: { use: [{ label: "Hierarquia", text: "Use para mostrar o caminho até a página atual em estruturas profundas." }], api: [{ label: ".fs-breadcrumb > .fs-breadcrumb-item", code: true, text: "lista de níveis; o último representa a página atual." }, { label: "aria-current=\"page\"", code: true, text: "marca o item atual sem link." }], a11y: [{ label: "Navegação", text: "envolva em nav com aria-label=\"Breadcrumb\"." }], states: [{ label: "Comprimento", text: "teste rótulos longos e reflow em telas pequenas." }] },
|
|
67
67
|
pagination: { use: [{ label: "Coleção paginada", text: "Use quando resultados são divididos em páginas e a pessoa precisa mudar de intervalo." }], api: [{ label: ".fs-pagination / .fs-page-item / .fs-page-link", code: true, text: "estrutura da lista de páginas." }, { label: ".is-active / .is-disabled", code: true, text: "página atual e limite indisponível." }], a11y: [{ label: "Nomes", text: "use aria-label no nav e aria-current=\"page\" na página atual." }], states: [{ label: "Limites", text: "teste primeira, última, ativa, disabled e foco." }] },
|
|
68
68
|
accordion: { use: [{ label: "Detalhe sob demanda", text: "Use para agrupar seções independentes e reduzir densidade inicial da página." }], api: [{ label: ".fs-accordion + data-fs=\"accordion\"", code: true, text: "contêiner que inicializa os painéis." }, { label: ".fs-accordion-button / .fs-accordion-collapse", code: true, text: "gatilho e região expansível de cada item." }, { label: "data-multiple=\"true\"", code: true, text: "permite vários painéis abertos; sem ele, abrir um fecha os demais." }], a11y: [{ label: "Semântica", text: "o componente sincroniza aria-expanded, aria-controls e região nomeada." }], states: [{ label: "Painel", text: "teste aberto inicial, exclusivo, múltiplo, teclado e movimento reduzido." }] },
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
color-scheme: light;
|
|
5
5
|
}
|
|
6
6
|
|
|
7
|
-
html[data-theme="dark"] { color-scheme: dark; }
|
|
7
|
+
html[data-theme="dark"], html[data-fs-theme="dark"] { color-scheme: dark; }
|
|
8
8
|
|
|
9
9
|
* { box-sizing: border-box; }
|
|
10
10
|
|
|
@@ -65,7 +65,9 @@ html[data-theme="dark"] .showcase-detail-body a,
|
|
|
65
65
|
html[data-theme="dark"] .showcase-token-html { color: var(--fs-alert-primary-text); }
|
|
66
66
|
html[data-theme="dark"] .showcase-token-css { color: var(--fs-alert-danger-text); }
|
|
67
67
|
html[data-theme="dark"] .showcase-token-example,
|
|
68
|
-
html[data-theme="dark"] .showcase-detail-example
|
|
68
|
+
html[data-theme="dark"] .showcase-detail-example,
|
|
69
|
+
html[data-fs-theme="dark"] .showcase-token-example,
|
|
70
|
+
html[data-fs-theme="dark"] .showcase-detail-example { color: var(--fs-alert-info-text); }
|
|
69
71
|
|
|
70
72
|
.showcase-preview { width: 100%; min-height: 560px; border: 1px solid var(--fs-color-border); border-radius: var(--fs-radius-md); background: var(--fs-color-subtle); }
|
|
71
73
|
.showcase-code-header { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-top: 16px; }
|
|
@@ -482,6 +482,7 @@ const apiExtensions = {
|
|
|
482
482
|
};
|
|
483
483
|
|
|
484
484
|
if (new URLSearchParams(window.location.search).get("theme") === "dark") {
|
|
485
|
+
document.documentElement.setAttribute("data-fs-theme", "dark");
|
|
485
486
|
document.documentElement.setAttribute("data-theme", "dark");
|
|
486
487
|
if (themeButton) themeButton.textContent = "Usar tema claro";
|
|
487
488
|
}
|
|
@@ -713,6 +714,7 @@ for (const item of catalog) {
|
|
|
713
714
|
|
|
714
715
|
themeButton?.addEventListener("click", () => {
|
|
715
716
|
const dark = document.documentElement.getAttribute("data-theme") !== "dark";
|
|
717
|
+
document.documentElement.setAttribute("data-fs-theme", dark ? "dark" : "light");
|
|
716
718
|
document.documentElement.toggleAttribute("data-theme", dark);
|
|
717
719
|
if (dark) document.documentElement.setAttribute("data-theme", "dark");
|
|
718
720
|
themeButton.textContent = dark ? "Usar tema claro" : "Usar tema escuro";
|
|
@@ -82,6 +82,21 @@
|
|
|
82
82
|
</div>
|
|
83
83
|
</section>
|
|
84
84
|
|
|
85
|
+
<section>
|
|
86
|
+
<h2 class="fs-u-fs-md fs-u-fw-medium fs-u-mb-2">4. API declarativa 2.3</h2>
|
|
87
|
+
<p class="fs-u-fs-sm fs-u-mb-3">Theme, validação e eventos comuns usam os exports vanilla sem dependências externas.</p>
|
|
88
|
+
<div class="fs-form-row">
|
|
89
|
+
<button type="button" class="fs-btn" id="theme-toggle">Alternar tema com FokusStyles.Theme</button>
|
|
90
|
+
<form class="fs-form-col" data-fs="form-validation" id="api-form" novalidate>
|
|
91
|
+
<label class="fs-form-label" for="api-required">Campo obrigatório</label>
|
|
92
|
+
<input class="fs-form-control" id="api-required" required aria-describedby="api-feedback">
|
|
93
|
+
<div class="fs-invalid-feedback" id="api-feedback">Informe um valor para continuar.</div>
|
|
94
|
+
<button class="fs-btn fs-btn-primary" type="submit">Validar formulário</button>
|
|
95
|
+
</form>
|
|
96
|
+
</div>
|
|
97
|
+
<p class="fs-u-fs-xs fs-u-mt-2" id="api-status" role="status" aria-live="polite"></p>
|
|
98
|
+
</section>
|
|
99
|
+
|
|
85
100
|
<section>
|
|
86
101
|
<h2 class="fs-u-fs-md fs-u-fw-medium fs-u-mb-2">2. Foco, teclado e overlay (FokusStyles.core.createFocusTrap / onEscapeKey / lockScroll / onClickOutside)</h2>
|
|
87
102
|
<p class="fs-u-fs-sm fs-u-mb-3">Abre um painel com scroll da página bloqueado, foco preso via Tab/Shift+Tab, fecha com Escape ou clique fora.</p>
|
|
@@ -184,6 +199,16 @@
|
|
|
184
199
|
core.collapse(collapsePanel);
|
|
185
200
|
}
|
|
186
201
|
});
|
|
202
|
+
|
|
203
|
+
const apiStatus = document.getElementById("api-status");
|
|
204
|
+
document.getElementById("theme-toggle").addEventListener("click", () => {
|
|
205
|
+
const next = FokusStyles.Theme.getOrCreateInstance().get() === "dark" ? "light" : "dark";
|
|
206
|
+
FokusStyles.Theme.set(next);
|
|
207
|
+
apiStatus.textContent = `Tema ${next} aplicado.`;
|
|
208
|
+
});
|
|
209
|
+
document.getElementById("api-form").addEventListener("fs:form:validated", (event) => {
|
|
210
|
+
apiStatus.textContent = event.detail.valid ? "Formulário válido." : "Formulário precisa de atenção.";
|
|
211
|
+
});
|
|
187
212
|
</script>
|
|
188
213
|
|
|
189
214
|
</body>
|
|
@@ -15,7 +15,8 @@
|
|
|
15
15
|
background-color: var(--fs-color-subtle);
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
html[data-theme="dark"] .theme-panel
|
|
18
|
+
html[data-theme="dark"] .theme-panel,
|
|
19
|
+
html[data-fs-theme="dark"] .theme-panel {
|
|
19
20
|
color: var(--fs-color-text);
|
|
20
21
|
background-color: var(--fs-color-surface);
|
|
21
22
|
}
|
|
@@ -25,14 +26,17 @@
|
|
|
25
26
|
</head>
|
|
26
27
|
<body>
|
|
27
28
|
<div class="theme-panel">
|
|
28
|
-
<nav class="fs-navbar fs-u-mb-3">
|
|
29
|
+
<nav class="fs-navbar fs-u-mb-3" data-fs="navbar" aria-label="Navegação principal">
|
|
29
30
|
<a href="#" class="fs-navbar-brand">FokusStyles</a>
|
|
30
|
-
<
|
|
31
|
-
|
|
32
|
-
<
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
31
|
+
<button type="button" class="fs-navbar-toggler" data-fs-toggle="navbar" data-fs-target="#demo-navbar-menu" aria-controls="demo-navbar-menu" aria-expanded="false">Menu</button>
|
|
32
|
+
<div class="fs-navbar-collapse" id="demo-navbar-menu" hidden>
|
|
33
|
+
<ul class="fs-navbar-nav">
|
|
34
|
+
<li><a href="#" class="fs-nav-link is-active" aria-current="page">Início</a></li>
|
|
35
|
+
<li><a href="#" class="fs-nav-link">Projetos <span class="fs-badge fs-badge-primary fs-badge-sm">3</span></a></li>
|
|
36
|
+
<li><a href="#" class="fs-nav-link is-disabled">Configurações</a></li>
|
|
37
|
+
</ul>
|
|
38
|
+
<button type="button" class="fs-btn fs-btn-primary fs-btn-sm">Entrar</button>
|
|
39
|
+
</div>
|
|
36
40
|
</nav>
|
|
37
41
|
|
|
38
42
|
<div class="fs-table-responsive">
|
package/mockup/foundations.html
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<!doctype html><html lang="pt-BR"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>FokusStyles — Fundamentos</title><link rel="stylesheet" href="../dist/css/fonts.css"><link rel="stylesheet" href="../dist/css/components.css"><link rel="stylesheet" href="../dist/css/forms.css"><link rel="stylesheet" href="../dist/css/helpers.css"><link rel="stylesheet" href="assets/showcase.css"></head><body><main class="showcase-shell"><header class="showcase-hero"><p class="showcase-eyebrow">Fokus Styles · laboratório</p><h1 class="showcase-title">Fundamentos e infraestrutura</h1><p class="showcase-lead">Tokens, layout, temas, ícones e utilitários JavaScript que sustentam os componentes. Cada preview é executável; o código mostrado é extraído do próprio exemplo.</p></header><div class="showcase-toolbar"><button class="fs-btn fs-btn-primary" type="button" data-showcase-theme>Usar tema escuro</button><a class="fs-btn" href="README.md">Índice dos laboratórios</a></div><nav class="showcase-nav" aria-label="Componentes desta página" data-showcase-navigation></nav><div data-showcase-sections></div></main><script id="showcase-catalog" type="application/json">[
|
|
2
2
|
{"id":"layout","title":"Layout","description":"Estruturas de página, grid, containers e comportamento responsivo para compor interfaces previsíveis.","use":"Use como base de páginas e regiões; não use utilitários para substituir semântica HTML.","api":".fs-container, .fs-row, .fs-col-* e utilitários fs-u-*.","a11y":"Mantenha landmarks, ordem lógica de leitura e títulos hierárquicos.","states":"Breakpoints, densidade e regiões de conteúdo.","source":"examples/layout.html","height":900},
|
|
3
|
-
{"id":"theming","title":"Temas e marcas","description":"Alternância de data-theme e data-fs-brand sobre os tokens semânticos do FokusStyles.","use":"Use para personalizar a aparência sem alterar markup de componentes.","api":"data-theme=dark e data-fs-brand; tokens --fs-*.","a11y":"Verifique contraste após qualquer marca personalizada.","states":"Claro, escuro e marcas alternativas.","source":"examples/theming.html","height":650},
|
|
3
|
+
{"id":"theming","title":"Temas e marcas","description":"Alternância de data-fs-theme, compatibilidade data-theme e data-fs-brand sobre os tokens semânticos do FokusStyles.","use":"Use para personalizar a aparência sem alterar markup de componentes.","api":"data-fs-theme=light|dark|auto, data-theme e data-fs-brand; tokens --fs-*.","a11y":"Verifique contraste após qualquer marca personalizada.","states":"Claro, escuro, automático e marcas alternativas.","source":"examples/theming.html","height":650},
|
|
4
4
|
{"id":"icons","title":"Ícones","description":"Catálogo e uso de ícones com rótulos acessíveis e alinhamento consistente.","use":"Use ícones para reforçar ações conhecidas; mantenha texto quando houver ambiguidade.","api":"Classes de ícone e utilitários de tamanho publicados.","a11y":"Ícones decorativos são ocultos; botões somente com ícone recebem nome acessível.","states":"Tamanhos, cores e ícones em controles.","source":"examples/icons.html","height":1200},
|
|
5
5
|
{"id":"divider","title":"Divider","description":"Separadores visuais para organizar grupos relacionados sem substituir hierarquia de conteúdo.","use":"Separe blocos próximos em listas, painéis e formulários.","api":".fs-divider e modificadores de orientação.","a11y":"Não use o separador como único indicador de mudança de assunto.","states":"Horizontal, vertical e espaçamentos adjacentes.","source":"examples/divider.html","height":520},
|
|
6
|
-
{"id":"js-foundation","title":"Infraestrutura JavaScript","description":"Posicionamento, foco,
|
|
6
|
+
{"id":"js-foundation","title":"Infraestrutura JavaScript","description":"Posicionamento, foco, overlays, temas, validação e transições reutilizados pelos componentes interativos.","use":"Use as APIs públicas apenas quando montar comportamento avançado fora dos componentes prontos.","api":"FokusStyles.core, Theme.set(), FormValidation, eventos fs:* e data-fs-toggle.","a11y":"Preserve foco, Escape, bloqueio de scroll, aria-invalid e preferência por movimento reduzido.","states":"Flip de posicionamento, focus trap, tema, validação, clique externo e transições.","source":"examples/js-foundation.html","height":1500}
|
|
7
7
|
]</script><script src="../dist/js/fokus.js"></script><script src="assets/showcase-contracts.js"></script><script src="assets/showcase.js"></script></body></html>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fokus-styles",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.3.0",
|
|
4
4
|
"description": "Fokus Styles is a complete CSS framework with components, utilities, vanilla JavaScript, icons, React wrappers, and a CLI.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"css",
|
|
@@ -48,6 +48,19 @@
|
|
|
48
48
|
"./forms.css": "./dist/css/forms.css",
|
|
49
49
|
"./components.css": "./dist/css/components.css",
|
|
50
50
|
"./helpers.css": "./dist/css/helpers.css",
|
|
51
|
+
"./core": "./dist/css/fokus-core.css",
|
|
52
|
+
"./components": "./dist/css/fokus-components.css",
|
|
53
|
+
"./utilities": "./dist/css/fokus-utilities.css",
|
|
54
|
+
"./themes": "./dist/css/fokus-dark.css",
|
|
55
|
+
"./config": "./packages/fokus-core/scss/settings/_config.scss",
|
|
56
|
+
"./js/theme": {
|
|
57
|
+
"types": "./packages/fokus-js/js/theme.d.ts",
|
|
58
|
+
"default": "./packages/fokus-js/js/theme.js"
|
|
59
|
+
},
|
|
60
|
+
"./js/focus-trap": {
|
|
61
|
+
"types": "./packages/fokus-js/js/focus-trap.d.ts",
|
|
62
|
+
"default": "./packages/fokus-js/js/focus-trap.js"
|
|
63
|
+
},
|
|
51
64
|
"./fonts.css": "./dist/css/fonts.css",
|
|
52
65
|
"./js": {
|
|
53
66
|
"types": "./packages/fokus-js/js/fokus.d.ts",
|
|
@@ -10,6 +10,7 @@ const HELP = `fokus — CLI do Fokus Styles
|
|
|
10
10
|
|
|
11
11
|
Uso:
|
|
12
12
|
fokus build <entry.scss> -o <saida.css> [--minify]
|
|
13
|
+
fokus build <entry.scss> -o <saida.css> [--preset core|components|utilities|full]
|
|
13
14
|
fokus theme <nome> --primary <#hex> [-o <saida.css>]
|
|
14
15
|
fokus analyze <arquivo...>
|
|
15
16
|
|
|
@@ -40,6 +41,12 @@ function parseFlags(args) {
|
|
|
40
41
|
} else if (arg === "--primary") {
|
|
41
42
|
flags.primary = args[i + 1];
|
|
42
43
|
i += 1;
|
|
44
|
+
} else if (arg === "--preset") {
|
|
45
|
+
flags.preset = args[i + 1];
|
|
46
|
+
i += 1;
|
|
47
|
+
} else if (arg === "--exclude") {
|
|
48
|
+
flags.exclude = args[i + 1]?.split(",").filter(Boolean) ?? [];
|
|
49
|
+
i += 1;
|
|
43
50
|
} else {
|
|
44
51
|
positional.push(arg);
|
|
45
52
|
}
|
|
@@ -58,7 +65,17 @@ async function runBuild(args) {
|
|
|
58
65
|
return;
|
|
59
66
|
}
|
|
60
67
|
|
|
61
|
-
const {
|
|
68
|
+
const presetEntries = { core: ["scss", "entries", "core-entry.scss"], components: ["scss", "entries", "components-entry.scss"], utilities: ["scss", "entries", "utilities-entry.scss"], full: ["scss", "fokus.scss"] };
|
|
69
|
+
const projectRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../../..");
|
|
70
|
+
const resolvedEntry = flags.preset && presetEntries[flags.preset]
|
|
71
|
+
? path.join(projectRoot, ...presetEntries[flags.preset])
|
|
72
|
+
: entry;
|
|
73
|
+
if (flags.preset && !presetEntries[flags.preset]) {
|
|
74
|
+
console.error(`Preset desconhecido: ${flags.preset}`);
|
|
75
|
+
process.exitCode = 1;
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
const { outPath } = await buildCss({ entry: resolvedEntry, out: flags.out, minify: Boolean(flags.minify), exclude: flags.exclude ?? [] });
|
|
62
79
|
console.log(`CSS compilado em ${outPath}`);
|
|
63
80
|
}
|
|
64
81
|
|
|
@@ -9,7 +9,7 @@ import cssnano from "cssnano";
|
|
|
9
9
|
// `fokus-styles/scss` ou qualquer outro pacote Sass) usando o mesmo pipeline
|
|
10
10
|
// do build interno do FokusStyles (sass -> autoprefixer -> cssnano opcional),
|
|
11
11
|
// sem exigir que o consumidor monte a própria toolchain.
|
|
12
|
-
export async function buildCss({ entry, out, minify = false, loadPaths = [] }) {
|
|
12
|
+
export async function buildCss({ entry, out, minify = false, loadPaths = [], exclude = [] }) {
|
|
13
13
|
const entryPath = path.resolve(entry);
|
|
14
14
|
const outPath = path.resolve(out);
|
|
15
15
|
|
|
@@ -20,7 +20,17 @@ export async function buildCss({ entry, out, minify = false, loadPaths = [] }) {
|
|
|
20
20
|
style: "expanded",
|
|
21
21
|
});
|
|
22
22
|
|
|
23
|
-
const
|
|
23
|
+
const excluded = exclude.map((name) => String(name).replace(/^fs-/, "").trim()).filter(Boolean);
|
|
24
|
+
const excludePlugin = {
|
|
25
|
+
postcssPlugin: "fokus-exclude-components",
|
|
26
|
+
Once(root) {
|
|
27
|
+
if (excluded.length === 0) return;
|
|
28
|
+
root.walkRules((rule) => {
|
|
29
|
+
if (excluded.some((name) => rule.selector.includes(`.fs-${name}`) || rule.selector.includes(`[data-fs=\"${name}\"]`))) rule.remove();
|
|
30
|
+
});
|
|
31
|
+
},
|
|
32
|
+
};
|
|
33
|
+
const plugins = minify ? [excludePlugin, autoprefixer, cssnano] : [excludePlugin, autoprefixer];
|
|
24
34
|
const result = await postcss(plugins).process(compiled.css, {
|
|
25
35
|
from: entryPath,
|
|
26
36
|
to: outPath,
|
|
@@ -27,6 +27,19 @@
|
|
|
27
27
|
border-bottom-left-radius: var(--fs-radius-md);
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
+
.fs-accordion-flush .fs-accordion-item {
|
|
31
|
+
border-inline: 0;
|
|
32
|
+
border-radius: 0;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
.fs-accordion-flush .fs-accordion-item:first-child {
|
|
36
|
+
border-block-start: 0;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
.fs-accordion-always-open .fs-accordion-button[aria-expanded="true"]::after {
|
|
40
|
+
transform: rotate(-135deg);
|
|
41
|
+
}
|
|
42
|
+
|
|
30
43
|
.fs-accordion-header {
|
|
31
44
|
margin: 0;
|
|
32
45
|
}
|
|
@@ -79,4 +92,10 @@
|
|
|
79
92
|
border-top: 1px solid var(--fs-color-border);
|
|
80
93
|
background-color: var(--fs-color-surface-raised);
|
|
81
94
|
}
|
|
95
|
+
|
|
96
|
+
@media (prefers-reduced-motion: reduce) {
|
|
97
|
+
.fs-accordion-button::after {
|
|
98
|
+
transition: none;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
82
101
|
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/* stylelint-disable declaration-block-single-line-max-declarations */
|
|
2
|
+
@layer components {
|
|
3
|
+
.fs-avatar {
|
|
4
|
+
display: inline-flex;
|
|
5
|
+
flex: 0 0 auto;
|
|
6
|
+
align-items: center;
|
|
7
|
+
justify-content: center;
|
|
8
|
+
width: 2.5rem;
|
|
9
|
+
height: 2.5rem;
|
|
10
|
+
overflow: hidden;
|
|
11
|
+
color: var(--fs-color-on-primary);
|
|
12
|
+
font-weight: 600;
|
|
13
|
+
background: var(--fs-color-primary);
|
|
14
|
+
border-radius: 50%;
|
|
15
|
+
object-fit: cover;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
.fs-avatar-sm { width: 2rem; height: 2rem; font-size: .8125rem; }
|
|
19
|
+
.fs-avatar-lg { width: 3.5rem; height: 3.5rem; font-size: 1.25rem; }
|
|
20
|
+
.fs-avatar-square { border-radius: var(--fs-radius-md); }
|
|
21
|
+
.fs-avatar img { width: 100%; height: 100%; object-fit: cover; }
|
|
22
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/* stylelint-disable declaration-block-single-line-max-declarations */
|
|
2
|
+
@layer components {
|
|
3
|
+
.fs-button-group {
|
|
4
|
+
display: inline-flex;
|
|
5
|
+
vertical-align: middle;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
.fs-button-group > .fs-btn { border-radius: 0; }
|
|
9
|
+
.fs-button-group > .fs-btn:first-child { border-start-start-radius: var(--fs-radius-sm); border-end-start-radius: var(--fs-radius-sm); }
|
|
10
|
+
.fs-button-group > .fs-btn:last-child { border-start-end-radius: var(--fs-radius-sm); border-end-end-radius: var(--fs-radius-sm); }
|
|
11
|
+
.fs-button-group > .fs-btn + .fs-btn { margin-inline-start: -1px; }
|
|
12
|
+
.fs-button-group-vertical { flex-direction: column; align-items: flex-start; }
|
|
13
|
+
.fs-button-group-vertical > .fs-btn { width: 100%; }
|
|
14
|
+
}
|