@skra/ui 0.2.0 → 0.3.1
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/LICENSE +201 -201
- package/NOTICE +7 -7
- package/README.md +39 -39
- package/consumer-guide/README.md +84 -84
- package/consumer-guide/forms-and-binding.md +19 -3
- package/consumer-guide/layout.md +19 -0
- package/consumer-guide/overlays.md +100 -100
- package/consumer-guide/setup-and-gotchas.md +165 -103
- package/consumer-guide/theming-and-encapsulation.md +129 -115
- package/fesm2022/skra-ui-accordion.mjs +61 -43
- package/fesm2022/skra-ui-accordion.mjs.map +1 -1
- package/fesm2022/skra-ui-alert.mjs +26 -18
- package/fesm2022/skra-ui-alert.mjs.map +1 -1
- package/fesm2022/skra-ui-avatar.mjs +12 -8
- package/fesm2022/skra-ui-avatar.mjs.map +1 -1
- package/fesm2022/skra-ui-badge.mjs +19 -13
- package/fesm2022/skra-ui-badge.mjs.map +1 -1
- package/fesm2022/skra-ui-button.mjs +28 -19
- package/fesm2022/skra-ui-button.mjs.map +1 -1
- package/fesm2022/skra-ui-checkbox.mjs +3 -3
- package/fesm2022/skra-ui-checkbox.mjs.map +1 -1
- package/fesm2022/skra-ui-chip.mjs +15 -11
- package/fesm2022/skra-ui-chip.mjs.map +1 -1
- package/fesm2022/skra-ui-combobox.mjs +135 -130
- package/fesm2022/skra-ui-combobox.mjs.map +1 -1
- package/fesm2022/skra-ui-core.mjs +95 -73
- package/fesm2022/skra-ui-core.mjs.map +1 -1
- package/fesm2022/skra-ui-counter.mjs +9 -6
- package/fesm2022/skra-ui-counter.mjs.map +1 -1
- package/fesm2022/skra-ui-datepicker.mjs +334 -307
- package/fesm2022/skra-ui-datepicker.mjs.map +1 -1
- package/fesm2022/skra-ui-dialog.mjs +5 -4
- package/fesm2022/skra-ui-dialog.mjs.map +1 -1
- package/fesm2022/skra-ui-field.mjs +65 -43
- package/fesm2022/skra-ui-field.mjs.map +1 -1
- package/fesm2022/skra-ui-icon.mjs +5 -4
- package/fesm2022/skra-ui-icon.mjs.map +1 -1
- package/fesm2022/skra-ui-input.mjs +53 -12
- package/fesm2022/skra-ui-input.mjs.map +1 -1
- package/fesm2022/skra-ui-kbd.mjs +3 -3
- package/fesm2022/skra-ui-kbd.mjs.map +1 -1
- package/fesm2022/skra-ui-layout.mjs +33 -21
- package/fesm2022/skra-ui-layout.mjs.map +1 -1
- package/fesm2022/skra-ui-link.mjs +3 -3
- package/fesm2022/skra-ui-link.mjs.map +1 -1
- package/fesm2022/skra-ui-loading-indicator.mjs +3 -3
- package/fesm2022/skra-ui-loading-indicator.mjs.map +1 -1
- package/fesm2022/skra-ui-menu.mjs +8 -7
- package/fesm2022/skra-ui-menu.mjs.map +1 -1
- package/fesm2022/skra-ui-multi-select.mjs +102 -98
- package/fesm2022/skra-ui-multi-select.mjs.map +1 -1
- package/fesm2022/skra-ui-native-select.mjs +35 -17
- package/fesm2022/skra-ui-native-select.mjs.map +1 -1
- package/fesm2022/skra-ui-popover.mjs +5 -4
- package/fesm2022/skra-ui-popover.mjs.map +1 -1
- package/fesm2022/skra-ui-progress.mjs +7 -5
- package/fesm2022/skra-ui-progress.mjs.map +1 -1
- package/fesm2022/skra-ui-radio.mjs +3 -3
- package/fesm2022/skra-ui-radio.mjs.map +1 -1
- package/fesm2022/skra-ui-rating-display.mjs +14 -9
- package/fesm2022/skra-ui-rating-display.mjs.map +1 -1
- package/fesm2022/skra-ui-select.mjs +98 -95
- package/fesm2022/skra-ui-select.mjs.map +1 -1
- package/fesm2022/skra-ui-skeleton.mjs +16 -13
- package/fesm2022/skra-ui-skeleton.mjs.map +1 -1
- package/fesm2022/skra-ui-star-rating.mjs +18 -11
- package/fesm2022/skra-ui-star-rating.mjs.map +1 -1
- package/fesm2022/skra-ui-status-dot.mjs +7 -5
- package/fesm2022/skra-ui-status-dot.mjs.map +1 -1
- package/fesm2022/skra-ui-switch.mjs +3 -3
- package/fesm2022/skra-ui-switch.mjs.map +1 -1
- package/fesm2022/skra-ui-tabs.mjs +43 -29
- package/fesm2022/skra-ui-tabs.mjs.map +1 -1
- package/fesm2022/skra-ui-textarea.mjs +41 -15
- package/fesm2022/skra-ui-textarea.mjs.map +1 -1
- package/fesm2022/skra-ui-toast.mjs +35 -23
- package/fesm2022/skra-ui-toast.mjs.map +1 -1
- package/fesm2022/skra-ui-tooltip.mjs +12 -9
- package/fesm2022/skra-ui-tooltip.mjs.map +1 -1
- package/fesm2022/skra-ui-typography.mjs +9 -6
- package/fesm2022/skra-ui-typography.mjs.map +1 -1
- package/package.json +6 -6
- package/types/skra-ui-field.d.ts +3 -3
- package/types/skra-ui-field.d.ts.map +1 -1
- package/types/skra-ui-input.d.ts +24 -1
- package/types/skra-ui-input.d.ts.map +1 -1
- package/types/skra-ui-native-select.d.ts +9 -1
- package/types/skra-ui-native-select.d.ts.map +1 -1
- package/types/skra-ui-textarea.d.ts +10 -1
- package/types/skra-ui-textarea.d.ts.map +1 -1
|
@@ -1,103 +1,165 @@
|
|
|
1
|
-
# Setup & Gotchas
|
|
2
|
-
|
|
3
|
-
> Load this for: first integration, import errors, "which package exports what", and the
|
|
4
|
-
> boolean-attribute / inline-style pitfalls. Core rules live in `README.md` — this is the detail.
|
|
5
|
-
|
|
6
|
-
## Public entry points (`@skra/ui/*`)
|
|
7
|
-
|
|
8
|
-
Import **only** from these. Never a deep `libs/ui/...` path (not part of the public API;
|
|
9
|
-
will break on any internal refactor). Skra ships as **one package, `@skra/ui`**, with a
|
|
10
|
-
secondary entry point per component (ADR 0054 / O-31 consolidation). The primary `@skra/ui`
|
|
11
|
-
entry exports nothing — always import from a `@skra/ui/<entry>` subpath.
|
|
12
|
-
|
|
13
|
-
| Entry point | Exports (representative) |
|
|
14
|
-
| ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
|
|
15
|
-
| `@skra/ui/button` | `button[skraButton]` |
|
|
16
|
-
| `@skra/ui/link` | `a[skraLink]` |
|
|
17
|
-
| `@skra/ui/icon` | icon container atom |
|
|
18
|
-
| `@skra/ui/typography` | `[skraTypography]` |
|
|
19
|
-
| `@skra/ui/layout` | `[skraStack]`, `[skraBox]`, `[skraGrid]` |
|
|
20
|
-
| `@skra/ui/kbd` | `kbd[skraKbd]` |
|
|
21
|
-
| `@skra/ui/field` | `<skra-field>`, `[skraLabel]`, `[skraHint]`, `[skraError]`, `fieldset[skraFieldset]`, `legend[skraLegend]`, `SkraFieldContext` |
|
|
22
|
-
| `@skra/ui/input` | `input[skraInput]` |
|
|
23
|
-
| `@skra/ui/textarea` | `textarea[skraTextarea]`, auto-resize |
|
|
24
|
-
| `@skra/ui/checkbox` | `input[skraCheckbox]` |
|
|
25
|
-
| `@skra/ui/radio` | `input[skraRadio]` |
|
|
26
|
-
| `@skra/ui/switch` | `input[skraSwitch]` |
|
|
27
|
-
| `@skra/ui/counter` | `skra-counter` |
|
|
28
|
-
| `@skra/ui/star-rating` | `skra-star-rating` |
|
|
29
|
-
| `@skra/ui/native-select` | `select[skraSelect]` (`SkraNativeSelect`) |
|
|
30
|
-
| `@skra/ui/dialog` | `dialog[skraDialog]` |
|
|
31
|
-
| `@skra/ui/tooltip` | `[skraTooltip]` |
|
|
32
|
-
| `@skra/ui/popover` | `skra-popover` |
|
|
33
|
-
| `@skra/ui/menu` | `skra-menu`, `skra-menu-item` |
|
|
34
|
-
| `@skra/ui/select` | `skra-select` (+ `SkraOption` & option directives, re-exported) |
|
|
35
|
-
| `@skra/ui/multi-select` | `skra-multi-select` (+ `SkraOption` & option directives, re-exported) |
|
|
36
|
-
| `@skra/ui/combobox` | `skra-combobox` (+ `SkraOption` & option directives, re-exported) |
|
|
37
|
-
| `@skra/ui/datepicker` | `skra-datepicker` |
|
|
38
|
-
| `@skra/ui/alert` | `skra-alert` |
|
|
39
|
-
| `@skra/ui/badge` | `skra-badge` |
|
|
40
|
-
| `@skra/ui/chip` | `skra-chip` |
|
|
41
|
-
| `@skra/ui/toast` | `skra-toast`, `skra-toast-region` |
|
|
42
|
-
| `@skra/ui/tabs` | `[skraTabs]`, `[skraTablist]`, `button[skraTab]`, `[skraTabPanel]` |
|
|
43
|
-
| `@skra/ui/accordion` | `[skraAccordion]` family |
|
|
44
|
-
| `@skra/ui/progress` | `progress[skraProgress]` |
|
|
45
|
-
| `@skra/ui/skeleton` | `skra-skeleton` |
|
|
46
|
-
| `@skra/ui/loading-indicator` | `[skraLoadingIndicator]` |
|
|
47
|
-
| `@skra/ui/avatar` | `skra-avatar` |
|
|
48
|
-
| `@skra/ui/status-dot` | `skra-status-dot` |
|
|
49
|
-
| `@skra/ui/rating-display` | `skra-rating-display` |
|
|
50
|
-
|
|
51
|
-
> `@skra/ui/core` exists for cross-component shared satellites (`SkraOption` & option
|
|
52
|
-
> directives, `SkraSelectTrigger`, `SKRA_LISTBOX_PANEL`). Prefer the re-exports from
|
|
53
|
-
> `select` / `multi-select` / `combobox`; you rarely import `core` directly.
|
|
54
|
-
>
|
|
55
|
-
> `@skra/shadow-dom-bridge` is a delivery utility, not a component — see
|
|
56
|
-
> `theming-and-encapsulation.md`.
|
|
57
|
-
|
|
58
|
-
## Gotcha 1 — boolean attributes: write the bare form
|
|
59
|
-
|
|
60
|
-
Skra boolean inputs carry `{ transform: booleanAttribute }` (lint-enforced across `libs/ui`
|
|
61
|
-
by `boolean-input-validator.mjs`), so the **native bare attribute works** — write it the
|
|
62
|
-
HTML-idiomatic way:
|
|
63
|
-
|
|
64
|
-
```html
|
|
65
|
-
<skra-toast dismissible>…</skra-toast>
|
|
66
|
-
<!-- ✓ -->
|
|
67
|
-
<div skraStack wrap>…</div>
|
|
68
|
-
<!-- ✓ -->
|
|
69
|
-
```
|
|
70
|
-
|
|
71
|
-
`[dismissible]="true"` also compiles but is unnecessary. (Historically the transform was
|
|
72
|
-
missing and the bare form broke under `strictTemplates` — F-18; closed library-wide in
|
|
73
|
-
session 10, so the workaround is no longer needed.)
|
|
74
|
-
|
|
75
|
-
## Gotcha 2 — never style with inline `style` or `[style.--var]`
|
|
76
|
-
|
|
77
|
-
Skra reflects state as `data-skra-*` attributes and maps them to tokens in CSS (ADR 021,
|
|
78
|
-
CSP-safe). To override a token for a subtree, scope it with a **CSS class or a data-attribute
|
|
79
|
-
on an ancestor**, never inline:
|
|
80
|
-
|
|
81
|
-
```html
|
|
82
|
-
<!-- ✗ CSP-violating, and not how the token cascade is meant to work -->
|
|
83
|
-
<skra-badge style="--skra-comp-badge-background: red">…</skra-badge>
|
|
84
|
-
|
|
85
|
-
<!-- ✓ scope on an ancestor, in a stylesheet -->
|
|
86
|
-
<div class="danger-zone"><skra-badge>…</skra-badge></div>
|
|
87
|
-
```
|
|
88
|
-
|
|
89
|
-
```css
|
|
90
|
-
.danger-zone {
|
|
91
|
-
--skra-comp-badge-background: var(--skra-sys-color-danger-base);
|
|
92
|
-
}
|
|
93
|
-
```
|
|
94
|
-
|
|
95
|
-
## Gotcha 3 — components carry `margin: 0` (ADR 022)
|
|
96
|
-
|
|
97
|
-
Spacing between components is **never** a margin on the component. Use a layout atom
|
|
98
|
-
(`[skraStack]`/`[skraBox]`/`[skraGrid]`) — see `layout.md`.
|
|
99
|
-
|
|
100
|
-
## Gotcha 4 —
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
1
|
+
# Setup & Gotchas
|
|
2
|
+
|
|
3
|
+
> Load this for: first integration, import errors, "which package exports what", and the
|
|
4
|
+
> boolean-attribute / inline-style pitfalls. Core rules live in `README.md` — this is the detail.
|
|
5
|
+
|
|
6
|
+
## Public entry points (`@skra/ui/*`)
|
|
7
|
+
|
|
8
|
+
Import **only** from these. Never a deep `libs/ui/...` path (not part of the public API;
|
|
9
|
+
will break on any internal refactor). Skra ships as **one package, `@skra/ui`**, with a
|
|
10
|
+
secondary entry point per component (ADR 0054 / O-31 consolidation). The primary `@skra/ui`
|
|
11
|
+
entry exports nothing — always import from a `@skra/ui/<entry>` subpath.
|
|
12
|
+
|
|
13
|
+
| Entry point | Exports (representative) |
|
|
14
|
+
| ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
|
|
15
|
+
| `@skra/ui/button` | `button[skraButton]` |
|
|
16
|
+
| `@skra/ui/link` | `a[skraLink]` |
|
|
17
|
+
| `@skra/ui/icon` | icon container atom |
|
|
18
|
+
| `@skra/ui/typography` | `[skraTypography]` |
|
|
19
|
+
| `@skra/ui/layout` | `[skraStack]`, `[skraBox]`, `[skraGrid]` |
|
|
20
|
+
| `@skra/ui/kbd` | `kbd[skraKbd]` |
|
|
21
|
+
| `@skra/ui/field` | `<skra-field>`, `[skraLabel]`, `[skraHint]`, `[skraError]`, `fieldset[skraFieldset]`, `legend[skraLegend]`, `SkraFieldContext` |
|
|
22
|
+
| `@skra/ui/input` | `input[skraInput]` |
|
|
23
|
+
| `@skra/ui/textarea` | `textarea[skraTextarea]`, auto-resize |
|
|
24
|
+
| `@skra/ui/checkbox` | `input[skraCheckbox]` |
|
|
25
|
+
| `@skra/ui/radio` | `input[skraRadio]` |
|
|
26
|
+
| `@skra/ui/switch` | `input[skraSwitch]` |
|
|
27
|
+
| `@skra/ui/counter` | `skra-counter` |
|
|
28
|
+
| `@skra/ui/star-rating` | `skra-star-rating` |
|
|
29
|
+
| `@skra/ui/native-select` | `select[skraSelect]` (`SkraNativeSelect`) |
|
|
30
|
+
| `@skra/ui/dialog` | `dialog[skraDialog]` |
|
|
31
|
+
| `@skra/ui/tooltip` | `[skraTooltip]` |
|
|
32
|
+
| `@skra/ui/popover` | `skra-popover` |
|
|
33
|
+
| `@skra/ui/menu` | `skra-menu`, `skra-menu-item` |
|
|
34
|
+
| `@skra/ui/select` | `skra-select` (+ `SkraOption` & option directives, re-exported) |
|
|
35
|
+
| `@skra/ui/multi-select` | `skra-multi-select` (+ `SkraOption` & option directives, re-exported) |
|
|
36
|
+
| `@skra/ui/combobox` | `skra-combobox` (+ `SkraOption` & option directives, re-exported) |
|
|
37
|
+
| `@skra/ui/datepicker` | `skra-datepicker` |
|
|
38
|
+
| `@skra/ui/alert` | `skra-alert` |
|
|
39
|
+
| `@skra/ui/badge` | `skra-badge` |
|
|
40
|
+
| `@skra/ui/chip` | `skra-chip` |
|
|
41
|
+
| `@skra/ui/toast` | `skra-toast`, `skra-toast-region` |
|
|
42
|
+
| `@skra/ui/tabs` | `[skraTabs]`, `[skraTablist]`, `button[skraTab]`, `[skraTabPanel]` |
|
|
43
|
+
| `@skra/ui/accordion` | `[skraAccordion]` family |
|
|
44
|
+
| `@skra/ui/progress` | `progress[skraProgress]` |
|
|
45
|
+
| `@skra/ui/skeleton` | `skra-skeleton` |
|
|
46
|
+
| `@skra/ui/loading-indicator` | `[skraLoadingIndicator]` |
|
|
47
|
+
| `@skra/ui/avatar` | `skra-avatar` |
|
|
48
|
+
| `@skra/ui/status-dot` | `skra-status-dot` |
|
|
49
|
+
| `@skra/ui/rating-display` | `skra-rating-display` |
|
|
50
|
+
|
|
51
|
+
> `@skra/ui/core` exists for cross-component shared satellites (`SkraOption` & option
|
|
52
|
+
> directives, `SkraSelectTrigger`, `SKRA_LISTBOX_PANEL`). Prefer the re-exports from
|
|
53
|
+
> `select` / `multi-select` / `combobox`; you rarely import `core` directly.
|
|
54
|
+
>
|
|
55
|
+
> `@skra/shadow-dom-bridge` is a delivery utility, not a component — see
|
|
56
|
+
> `theming-and-encapsulation.md`.
|
|
57
|
+
|
|
58
|
+
## Gotcha 1 — boolean attributes: write the bare form
|
|
59
|
+
|
|
60
|
+
Skra boolean inputs carry `{ transform: booleanAttribute }` (lint-enforced across `libs/ui`
|
|
61
|
+
by `boolean-input-validator.mjs`), so the **native bare attribute works** — write it the
|
|
62
|
+
HTML-idiomatic way:
|
|
63
|
+
|
|
64
|
+
```html
|
|
65
|
+
<skra-toast dismissible>…</skra-toast>
|
|
66
|
+
<!-- ✓ -->
|
|
67
|
+
<div skraStack wrap>…</div>
|
|
68
|
+
<!-- ✓ -->
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
`[dismissible]="true"` also compiles but is unnecessary. (Historically the transform was
|
|
72
|
+
missing and the bare form broke under `strictTemplates` — F-18; closed library-wide in
|
|
73
|
+
session 10, so the workaround is no longer needed.)
|
|
74
|
+
|
|
75
|
+
## Gotcha 2 — never style with inline `style` or `[style.--var]`
|
|
76
|
+
|
|
77
|
+
Skra reflects state as `data-skra-*` attributes and maps them to tokens in CSS (ADR 021,
|
|
78
|
+
CSP-safe). To override a token for a subtree, scope it with a **CSS class or a data-attribute
|
|
79
|
+
on an ancestor**, never inline:
|
|
80
|
+
|
|
81
|
+
```html
|
|
82
|
+
<!-- ✗ CSP-violating, and not how the token cascade is meant to work -->
|
|
83
|
+
<skra-badge style="--skra-comp-badge-background: red">…</skra-badge>
|
|
84
|
+
|
|
85
|
+
<!-- ✓ scope on an ancestor, in a stylesheet -->
|
|
86
|
+
<div class="danger-zone"><skra-badge>…</skra-badge></div>
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
```css
|
|
90
|
+
.danger-zone {
|
|
91
|
+
--skra-comp-badge-background: var(--skra-sys-color-danger-base);
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## Gotcha 3 — components carry `margin: 0` (ADR 022)
|
|
96
|
+
|
|
97
|
+
Spacing between components is **never** a margin on the component. Use a layout atom
|
|
98
|
+
(`[skraStack]`/`[skraBox]`/`[skraGrid]`) — see `layout.md`.
|
|
99
|
+
|
|
100
|
+
## Gotcha 4 — `skraTypography` takes its role **on the selector**, not a `role` input
|
|
101
|
+
|
|
102
|
+
`[skraTypography]` is the one atom whose main input is aliased to its own selector. The
|
|
103
|
+
property is called `role`, but you never write `role="…"` — that would collide with the
|
|
104
|
+
native ARIA `role` attribute, which is exactly why it is aliased:
|
|
105
|
+
|
|
106
|
+
```html
|
|
107
|
+
<!-- ✓ the role goes on the selector attribute -->
|
|
108
|
+
<h1 skraTypography="headline-medium">Materials</h1>
|
|
109
|
+
<p skraTypography="body-small">Everything uploaded this month.</p>
|
|
110
|
+
|
|
111
|
+
<!-- ✓ bare = the default role, `body-medium` -->
|
|
112
|
+
<span skraTypography>Plain body text</span>
|
|
113
|
+
|
|
114
|
+
<!-- ✗ `role` is the ARIA attribute here; the type error is TS2322 on an unknown input -->
|
|
115
|
+
<h1 skraTypography role="headline-medium">Materials</h1>
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Roles (ADR 036): `display-{large,medium,small}` · `headline-*` · `title-*` · `body-*` ·
|
|
119
|
+
`label-*`. The other inputs are plain and unaliased:
|
|
120
|
+
|
|
121
|
+
```html
|
|
122
|
+
<p skraTypography="body-small" severity="error">Upload failed.</p>
|
|
123
|
+
<p skraTypography="body-medium" truncate="multi" [lineClamp]="3">Long description…</p>
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
`skraTypography` is a **component on a native host** — put it on the element that already
|
|
127
|
+
carries the right semantics (`<h1>`, `<p>`, `<span>`). It sets no heading level and no role
|
|
128
|
+
of its own; the visual scale and the document outline stay independent.
|
|
129
|
+
|
|
130
|
+
## Gotcha 5 — form atoms work standalone; `<skra-field>` is what adds label/hint/error wiring
|
|
131
|
+
|
|
132
|
+
`input[skraInput]`, `textarea[skraTextarea]` and `select[skraSelect]` inject
|
|
133
|
+
`SkraFieldContext` **optionally** (ADR 013 §3.7), exactly like `skraCheckbox` / `skraRadio` /
|
|
134
|
+
`skraSwitch`. Outside a field they render the skin and touch nothing else — your `id`,
|
|
135
|
+
`disabled` and `readonly` are left alone:
|
|
136
|
+
|
|
137
|
+
```html
|
|
138
|
+
<!-- ✓ toolbar search: native implicit label, no field ceremony -->
|
|
139
|
+
<label>Search <input skraInput type="search" placeholder="Filter by name…" /></label>
|
|
140
|
+
|
|
141
|
+
<!-- ✓ genuinely label-less control: aria-label on the native host (ADR 033 Path 3) -->
|
|
142
|
+
<input skraInput type="search" aria-label="Search materials" />
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
Reach for `<skra-field>` when you need what the field actually provides — a visible label
|
|
146
|
+
associated by DI, a hint, an error, and the `required`/`disabled`/`readonly`/`hasError`
|
|
147
|
+
cascade across all of them:
|
|
148
|
+
|
|
149
|
+
```html
|
|
150
|
+
<skra-field [hasError]="!email().valid">
|
|
151
|
+
<label skraLabelFor>Email</label>
|
|
152
|
+
<input skraInput type="email" />
|
|
153
|
+
<skra-hint>We only use this for receipts.</skra-hint>
|
|
154
|
+
<skra-error>Enter a valid address.</skra-error>
|
|
155
|
+
</skra-field>
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
> The field does **not** guarantee an accessible name — `<skra-field><input skraInput /></skra-field>`
|
|
159
|
+
> with no `label[skraLabelFor]` renders an unnamed control. Naming is on you either way; run
|
|
160
|
+
> axe over your screens.
|
|
161
|
+
|
|
162
|
+
## Gotcha 6 — don't reach for relative imports as an escape hatch
|
|
163
|
+
|
|
164
|
+
If something you need isn't on a public `@skra/ui/*` entry point, that is a finding (raise it),
|
|
165
|
+
not a reason to import from `libs/`. The public surface is the contract.
|
|
@@ -1,115 +1,129 @@
|
|
|
1
|
-
# Theming & Encapsulation
|
|
2
|
-
|
|
3
|
-
> Load this for: loading the token layers, switching skin / dark mode / density at runtime,
|
|
4
|
-
> and mounting Skra under Shadow DOM / a microfrontend / an iframe. Governed by ADR 002
|
|
5
|
-
> (token cascade), ADR 042 §3.4 (value/structure split), ADR 012 §5 (delivery), ADR 036 §3.7
|
|
6
|
-
> (baseline hook), ADR 0047 (runtime-scoped skins).
|
|
7
|
-
|
|
8
|
-
## Mental model — values inherit, structure does not
|
|
9
|
-
|
|
10
|
-
Skra styling has two layers, and they behave differently across DOM boundaries:
|
|
11
|
-
|
|
12
|
-
- **Token VALUES** (`--skra-*` custom properties) — set by the theme/comp/skin artifacts.
|
|
13
|
-
They **inherit** through the DOM, **including across a Shadow DOM boundary**. This is what
|
|
14
|
-
lets a runtime skin swap re-paint a shadow subtree with zero component changes (F-9).
|
|
15
|
-
- **STRUCTURE** (selector-bearing rules: `:host`, `data-skra-*` state, slices, the variant
|
|
16
|
-
matrix) — injected as document-scoped `<style>` (Emulated, ADR 001). These do **not** cross
|
|
17
|
-
into a shadow root; inside one you need the bridge (below).
|
|
18
|
-
|
|
19
|
-
## Loading the token layers (global stylesheet)
|
|
20
|
-
|
|
21
|
-
A consumer imports the built token artifacts once, globally. **Order matters** (F-14): base →
|
|
22
|
-
comp → dark → slices → scoped skins → overlay presets (overlays last so they keep overriding
|
|
23
|
-
a skin at equal specificity):
|
|
24
|
-
|
|
25
|
-
```scss
|
|
26
|
-
|
|
27
|
-
@
|
|
28
|
-
@
|
|
29
|
-
@
|
|
30
|
-
@
|
|
31
|
-
@
|
|
32
|
-
@
|
|
33
|
-
@
|
|
34
|
-
@
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
@
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
`
|
|
68
|
-
`data-skra-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
(
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
-
|
|
114
|
-
|
|
115
|
-
|
|
1
|
+
# Theming & Encapsulation
|
|
2
|
+
|
|
3
|
+
> Load this for: loading the token layers, switching skin / dark mode / density at runtime,
|
|
4
|
+
> and mounting Skra under Shadow DOM / a microfrontend / an iframe. Governed by ADR 002
|
|
5
|
+
> (token cascade), ADR 042 §3.4 (value/structure split), ADR 012 §5 (delivery), ADR 036 §3.7
|
|
6
|
+
> (baseline hook), ADR 0047 (runtime-scoped skins).
|
|
7
|
+
|
|
8
|
+
## Mental model — values inherit, structure does not
|
|
9
|
+
|
|
10
|
+
Skra styling has two layers, and they behave differently across DOM boundaries:
|
|
11
|
+
|
|
12
|
+
- **Token VALUES** (`--skra-*` custom properties) — set by the theme/comp/skin artifacts.
|
|
13
|
+
They **inherit** through the DOM, **including across a Shadow DOM boundary**. This is what
|
|
14
|
+
lets a runtime skin swap re-paint a shadow subtree with zero component changes (F-9).
|
|
15
|
+
- **STRUCTURE** (selector-bearing rules: `:host`, `data-skra-*` state, slices, the variant
|
|
16
|
+
matrix) — injected as document-scoped `<style>` (Emulated, ADR 001). These do **not** cross
|
|
17
|
+
into a shadow root; inside one you need the bridge (below).
|
|
18
|
+
|
|
19
|
+
## Loading the token layers (global stylesheet)
|
|
20
|
+
|
|
21
|
+
A consumer imports the built token artifacts once, globally. **Order matters** (F-14): base →
|
|
22
|
+
comp → dark → slices → scoped skins → overlay presets (overlays last so they keep overriding
|
|
23
|
+
a skin at equal specificity):
|
|
24
|
+
|
|
25
|
+
```scss
|
|
26
|
+
@use 'pkg:@skra/tokens/skra/_theme.scss' as *; // ref + sys token values (:root)
|
|
27
|
+
@use 'pkg:@skra/tokens/skra/_comp.scss' as *; // component token defaults
|
|
28
|
+
@use 'pkg:@skra/tokens/skra/_theme.dark.scss' as *; // dark-scheme value block
|
|
29
|
+
@use 'pkg:@skra/tokens/skra/_baseline.scss' as *; // typography/canvas baseline (needs the hook — below)
|
|
30
|
+
@use 'pkg:@skra/tokens/skra/_form-controls.scss' as *; // native control skin slice
|
|
31
|
+
@use 'pkg:@skra/tokens/skra/_overlay.scss' as *; // ::backdrop scrim + modal scroll-lock slice
|
|
32
|
+
@use 'pkg:@skra/tokens/skra/utilities.scss' as *; // skra-sr-only etc.
|
|
33
|
+
@use 'pkg:@skra/tokens/skra/_density.compact.scss' as *;
|
|
34
|
+
@use 'pkg:@skra/tokens/skra/_density.comfortable.scss' as *;
|
|
35
|
+
|
|
36
|
+
/* additional skins ship as runtime-scoped artifacts (ADR 0047), keyed to [data-skra-skin] */
|
|
37
|
+
@use 'pkg:@skra/tokens/brutal/_theme.scoped.scss' as *;
|
|
38
|
+
@use 'pkg:@skra/tokens/brutal/_theme.dark.scoped.scss' as *;
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
**`as *` is mandatory, and it is the single most common first-build failure.** `@use`
|
|
42
|
+
derives a namespace from the file name, so `_theme.scss` and `_theme.dark.scss` both claim
|
|
43
|
+
`theme` and Sass aborts with _"There's already a module with namespace 'theme'"_ — likewise
|
|
44
|
+
for the `_density.compact` / `_density.comfortable` pair and every `.scoped` pair. The layers
|
|
45
|
+
declare CSS custom properties and no Sass members, so there is nothing for a namespace to
|
|
46
|
+
disambiguate and `as *` costs nothing.
|
|
47
|
+
|
|
48
|
+
**`pkg:` needs no build config.** It uses the Sass package importer, which `@angular/build`
|
|
49
|
+
enables by default. If your toolchain does not, drop the prefix and add `node_modules` to the
|
|
50
|
+
load path — `"stylePreprocessorOptions": { "includePaths": ["node_modules"] }` in the build
|
|
51
|
+
target, then `@use '@skra/tokens/skra/_theme.scss' as *;`. Without one of the two, a bare
|
|
52
|
+
`@skra/tokens/...` specifier does not resolve at all.
|
|
53
|
+
|
|
54
|
+
**Do not use `@import`** — deprecated in Dart Sass and slated for removal.
|
|
55
|
+
|
|
56
|
+
> Exact specifiers depend on how the tokens reach your project (npm package vs vendored
|
|
57
|
+
> `dist/`). The layer set and order are the contract; the paths are not.
|
|
58
|
+
|
|
59
|
+
## Switching skin / scheme / density at runtime
|
|
60
|
+
|
|
61
|
+
These are **consumer-scope attributes** on an ancestor element — NOT component inputs and NOT
|
|
62
|
+
state reflection. Any subtree carrying them re-resolves its token values:
|
|
63
|
+
|
|
64
|
+
| Attribute | Values | Default (omit the attribute) |
|
|
65
|
+
| ------------------------- | -------------------------------------------------- | ---------------------------- |
|
|
66
|
+
| `data-skra-skin` | `brutal`, … (whatever scoped artifacts you loaded) | the base skin |
|
|
67
|
+
| `data-skra-color-scheme` | `dark` | light |
|
|
68
|
+
| `data-skra-density` | `compact`, `comfortable` | default density |
|
|
69
|
+
|
|
70
|
+
```html
|
|
71
|
+
<div [attr.data-skra-skin]="skin()" [attr.data-skra-color-scheme]="scheme() === 'dark' ? 'dark' : null" [attr.data-skra-density]="density() === 'default' ? null : density()">
|
|
72
|
+
<!-- Skra subtree re-paints when any of these change -->
|
|
73
|
+
</div>
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Bind `null` for the default so you don't emit `data-skra-color-scheme="light"` etc. — the
|
|
77
|
+
base values live at `:root`, the attribute only _overrides_.
|
|
78
|
+
|
|
79
|
+
### The baseline hook (`data-skra-baseline`)
|
|
80
|
+
|
|
81
|
+
`_baseline.scss` (document typography + canvas background) is **inert until you add the
|
|
82
|
+
`data-skra-baseline` attribute** to the scope that should own it (ADR 036 §3.7). It is
|
|
83
|
+
**static** — always present, not toggled like the design-context attributes above:
|
|
84
|
+
|
|
85
|
+
- `<html data-skra-baseline>` — Skra owns the page (canvas bg reaches the viewport, sets the
|
|
86
|
+
rem root). Use when Skra is your whole app.
|
|
87
|
+
- `<div data-skra-baseline>` — guest/microfrontend: scopes typography + canvas to the subtree
|
|
88
|
+
only, leaving the host shell's `<html>` untouched.
|
|
89
|
+
|
|
90
|
+
## Mounting Skra inside a Shadow DOM root (microfrontend isolation)
|
|
91
|
+
|
|
92
|
+
Inside a shadow root, token **values still inherit** (skins work), but the document-scoped
|
|
93
|
+
STRUCTURE does not reach in. Adopt it with the bridge:
|
|
94
|
+
|
|
95
|
+
```ts
|
|
96
|
+
import { registerSkraStyles } from '@skra/shadow-dom-bridge';
|
|
97
|
+
|
|
98
|
+
// after the Skra content inside the shadow root has rendered at least once
|
|
99
|
+
registerSkraStyles(shadowRoot);
|
|
100
|
+
// rebuild the cached aggregate if more Skra styles were injected since:
|
|
101
|
+
registerSkraStyles(shadowRoot, { refresh: true });
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
`registerSkraStyles(shadowRoot, options?)` adopts Skra's **structural** stylesheets
|
|
105
|
+
(per-component Emulated rules incl. the variant matrix, `_baseline`, `_form-controls`,
|
|
106
|
+
`utilities`) into the root. It deliberately **skips the token value layers** — those inherit,
|
|
107
|
+
and re-declaring them inside would freeze shell-driven skin morph (ADR 042 §3.4). Options:
|
|
108
|
+
`marker` (default `/skra/i`), `fallback` (`<style>` injection when constructable stylesheets
|
|
109
|
+
are unavailable, default `true`), `refresh`. There is also `unregisterSkraStyles(shadowRoot)`.
|
|
110
|
+
|
|
111
|
+
**This applies to Angular `ViewEncapsulation.ShadowDom` too**, not only foreign `attachShadow()`
|
|
112
|
+
roots: Angular replicates _component_ styles into a ShadowDom root but NOT the global slices
|
|
113
|
+
(F-12) — so without the bridge, `skra-sr-only` content renders visible (e.g. the datepicker's
|
|
114
|
+
per-day SR labels overflow the grid — F-15), `::backdrop`/scroll-lock are missing (F-7), etc.
|
|
115
|
+
|
|
116
|
+
## Limits that do NOT cross a boundary (by design)
|
|
117
|
+
|
|
118
|
+
The native top-layer substrate (ADR 0044) has per-context limits — these are architecture, not
|
|
119
|
+
bugs:
|
|
120
|
+
|
|
121
|
+
- **Modal scroll-lock** uses `html:has(dialog[skraDialog]:modal)`, and `:has()` does not pierce
|
|
122
|
+
a shadow boundary — a dialog opened from inside a shadow root does **not** lock the outer page
|
|
123
|
+
scroll (F-6). For shift-free locking when Skra owns the page, set
|
|
124
|
+
`html { scrollbar-gutter: stable }` globally.
|
|
125
|
+
- **`::backdrop` scrim** lives in the `_overlay.scss` global slice; inside a shadow root it
|
|
126
|
+
needs the bridge (F-7).
|
|
127
|
+
- **iframe** — overlays open in the **iframe's own top layer, contained to the frame** (F-11);
|
|
128
|
+
they cannot overlay the parent app. Pass design context into the frame via query params /
|
|
129
|
+
reload. This is a fundamental limit of the per-document top layer, not a defect.
|