actual-css 0.5.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (101) hide show
  1. package/CHANGELOG.md +63 -0
  2. package/dist/actual.css +1 -0
  3. package/dist/actual.full.css +589 -131
  4. package/dist/actual.full.js +1 -1
  5. package/dist/actual.full.min.css +1 -1
  6. package/dist/actual.min.css +1 -1
  7. package/docs/design-notes/browser-support.md +39 -14
  8. package/docs/design-notes/css-anchor.md +1 -1
  9. package/docs/design-notes/enhancement-contract.md +30 -13
  10. package/docs/design-notes/forced-colors.md +11 -7
  11. package/docs/design-notes/grid.md +4 -4
  12. package/docs/design-notes/naming.md +15 -9
  13. package/docs/design-notes/platform-alignment.md +60 -0
  14. package/docs/design-notes/popover-manual-poc.md +248 -0
  15. package/docs/design-notes/references.md +4 -0
  16. package/docs/design-notes/steps.md +748 -0
  17. package/docs/design-notes/surface-reparenting.md +122 -0
  18. package/docs/design-notes/widget-primitives.md +32 -15
  19. package/docs/navigation.json +1 -0
  20. package/docs/pages/components/alert.md +13 -8
  21. package/docs/pages/components/aura.md +102 -6
  22. package/docs/pages/components/avatar.md +9 -6
  23. package/docs/pages/components/badge.md +11 -7
  24. package/docs/pages/components/button.md +17 -17
  25. package/docs/pages/components/card.md +15 -8
  26. package/docs/pages/components/chat.md +1 -1
  27. package/docs/pages/components/dialog.md +10 -7
  28. package/docs/pages/components/drawer.md +1 -1
  29. package/docs/pages/components/fab.md +12 -10
  30. package/docs/pages/components/indicator.md +6 -6
  31. package/docs/pages/components/list.md +11 -2
  32. package/docs/pages/components/navbar.md +1 -1
  33. package/docs/pages/components/pagination.md +1 -1
  34. package/docs/pages/components/rating.md +4 -4
  35. package/docs/pages/components/skeleton.md +1 -1
  36. package/docs/pages/components/spinner.md +1 -1
  37. package/docs/pages/components/steps.md +376 -52
  38. package/docs/pages/components/table.md +5 -5
  39. package/docs/pages/enhancements/flyout.md +104 -12
  40. package/docs/pages/enhancements/overview.md +2 -2
  41. package/docs/pages/enhancements/scrollspy.md +1 -1
  42. package/docs/pages/enhancements/status-bar.md +20 -0
  43. package/docs/pages/enhancements/tabs.md +1 -1
  44. package/docs/pages/enhancements/tooltip.md +12 -0
  45. package/docs/pages/examples/overview.md +2 -0
  46. package/docs/pages/forms/choice-cards.md +4 -4
  47. package/docs/pages/forms/combobox.md +87 -0
  48. package/docs/pages/forms/floating-field.md +5 -3
  49. package/docs/pages/forms/joined-controls.md +4 -4
  50. package/docs/pages/forms/labels.md +8 -5
  51. package/docs/pages/forms/overview.md +25 -18
  52. package/docs/pages/forms/password.md +3 -2
  53. package/docs/pages/forms/range.md +13 -2
  54. package/docs/pages/forms/states-and-sizes.md +6 -4
  55. package/docs/pages/forms/validation.md +13 -9
  56. package/docs/pages/foundations/philosophy.md +15 -1
  57. package/docs/pages/foundations/theming.md +8 -0
  58. package/docs/pages/foundations/tokens.md +9 -6
  59. package/docs/pages/foundations/typography.md +1 -1
  60. package/docs/pages/guides/bootstrap.md +3 -3
  61. package/docs/pages/guides/introduction.md +1 -1
  62. package/docs/pages/guides/modular-import.md +21 -10
  63. package/docs/pages/guides/progressive-enhancement.md +96 -73
  64. package/docs/pages/guides/tailwind.md +2 -2
  65. package/docs/pages/layout/app-shell.md +4 -4
  66. package/docs/pages/layout/choosing.md +10 -6
  67. package/docs/pages/layout/grid.md +11 -10
  68. package/docs/pages/layout/stack.md +10 -6
  69. package/docs/pages/patterns/structured-lists.md +7 -7
  70. package/docs/pages/utilities/accessibility.md +1 -1
  71. package/docs/pages/utilities/flex.md +20 -13
  72. package/docs/pages/utilities/links.md +1 -1
  73. package/docs/pages/utilities/list.md +1 -1
  74. package/docs/pages/utilities/overflow.md +14 -7
  75. package/docs/pages/utilities/overview.md +22 -0
  76. package/docs/pages/utilities/shape.md +1 -1
  77. package/docs/pages/utilities/sizing-wrapping.md +10 -8
  78. package/docs/pages/utilities/spacing.md +26 -19
  79. package/docs/pages/utilities/text.md +1 -1
  80. package/llms.txt +27 -4
  81. package/package.json +7 -4
  82. package/scripts/reserved-classes.json +4 -1
  83. package/src/css/components/fab.css +44 -8
  84. package/src/css/components/flyout.css +49 -3
  85. package/src/css/components/join.css +7 -0
  86. package/src/css/components/list.css +3 -1
  87. package/src/css/components/menu.css +16 -10
  88. package/src/css/components/steps.css +277 -69
  89. package/src/css/components/tooltip.css +37 -1
  90. package/src/css/core/tokens.css +1 -0
  91. package/src/css/effects/aura.css +101 -11
  92. package/src/css/forms/custom-select.css +1 -1
  93. package/src/css/forms/floating-field.css +8 -6
  94. package/src/css/layout/container-query.css +37 -13
  95. package/src/css/layout/grid.css +7 -7
  96. package/src/css/layout/index.css +1 -1
  97. package/src/js/password.js +1 -1
  98. package/src/js/status.js +38 -2
  99. package/src/js/surface.js +28 -12
  100. package/src/js/tooltip.js +2 -2
  101. package/src/js/floating.js +0 -432
@@ -0,0 +1,122 @@
1
+ # Surface reparenting breaks inherited presentation context
2
+
3
+ Known defect, present on the current Minimal floor. Tracked separately from
4
+ Popover adoption on purpose: Popover is the likely cure, but the defect exists
5
+ now and needs to be understood on its own terms.
6
+
7
+ ## What happens
8
+
9
+ `surface.js` moves an open surface out of its place in the document:
10
+
11
+ ```text
12
+ getSurfaceRoot() → nearest <dialog>, else body
13
+ mountSurface() → root.append(panel)
14
+ restoreSurface() → put it back once the exit transition ends
15
+ ```
16
+
17
+ The move exists for good reasons — it escapes `overflow` clipping on any
18
+ ancestor and sidesteps `z-index` stacking. But it also takes the panel out of
19
+ every scope that reached it *by inheritance*. The panel is styled by one set of
20
+ ancestors while it is closed, and by a different set from the moment it becomes
21
+ visible.
22
+
23
+ Actual leans on inheritance deliberately: theme islands, density, `.inverted`,
24
+ and the custom properties an application scopes to its own containers all
25
+ travel that way. So this is not one bug about themes. It is the same bug in as
26
+ many forms as there are inherited contexts.
27
+
28
+ ## Measured
29
+
30
+ Chrome, one `.flyout` per scope, values read off the panel before and after the
31
+ click that opens it:
32
+
33
+ | Scope | Closed | Open |
34
+ | ------------------------- | ---------------------------------------- | -------------------- |
35
+ | `[data-theme="dark"]` | `background: rgb(30, 26, 35)` | `rgb(255, 255, 255)` |
36
+ | `.sm` | `--control-size: 2rem` | `2.375rem` |
37
+ | `.inverted` | `--ui-fg: light-dark(hsl(0 0% 100%), …)` | *(unset)* |
38
+ | app-scoped `--app-accent` | `rgb(1, 2, 3)` | *(unset)* |
39
+ | `.sm` inside a `<dialog>` | `--control-size: 2rem` | `2.375rem` |
40
+
41
+ Every context breaks. Three consequences are worth stating plainly:
42
+
43
+ - The theme case is visible, not theoretical: a flyout inside a dark island
44
+ turns light at the moment it opens.
45
+ - The density case is measurable: menu rows render 38px instead of the 32px the
46
+ scope asked for.
47
+ - The dialog case shows the `getSurfaceRoot()` special case does not help. The
48
+ panel does land inside the dialog, and still loses any scope sitting between
49
+ that dialog and its trigger. Choosing a nearer root loses less context; it
50
+ does not preserve it.
51
+
52
+ Locked by `tests/browser/surface-inherited-context.test.js`, whose assertions
53
+ describe the contract Actual owes and are marked `test.failing` until the
54
+ transport stops reparenting.
55
+
56
+ ## Why the obvious repairs are wrong
57
+
58
+ **Copying `data-theme` onto the moved panel.** Fixes the one symptom that was
59
+ noticed and leaves the other four broken. It also turns an inherited contract
60
+ into an enumerated one, so every future scope has to be added to a list.
61
+
62
+ **Snapshotting custom properties before the move.** There is no bounded set to
63
+ snapshot — an application's own hooks are unknowable by definition, and
64
+ `getComputedStyle` cannot enumerate inherited custom properties portably. It
65
+ would also freeze values that should stay live.
66
+
67
+ **Mounting to a nearer root.** The dialog row above is the experiment: it
68
+ reduces the damage without addressing the cause.
69
+
70
+ The cause is that the panel is moved at all. The repair is a transport that
71
+ promotes the element without relocating it.
72
+
73
+ ## The actual repair
74
+
75
+ A native popover renders in the top layer while staying exactly where the
76
+ author put it in the DOM. That removes the reason to reparent, and with it the
77
+ whole mechanism:
78
+
79
+ ```text
80
+ mountedSurfaces
81
+ getSurfaceRoot()
82
+ mountSurface()
83
+ restoreSurface()
84
+ ```
85
+
86
+ `popover="manual"` is the mode that fits: it supplies the top layer and nothing
87
+ else, so `surface.js` keeps its dismissal policy, Escape ordering, focus
88
+ restoration and placement orchestration unchanged. See
89
+ [platform-alignment](platform-alignment.md) for the responsibility split and
90
+ the criteria a component must meet before adopting Popover, and
91
+ [browser-support](browser-support.md) for which tier guarantees it.
92
+
93
+ That change is gated on Popover being available across Minimal. It is not
94
+ available there today, and adopting it behind a capability branch would keep
95
+ the reparenting code alive for the fallback path — so the defect would remain
96
+ for exactly the browsers the branch was written to serve.
97
+
98
+ ## Until then
99
+
100
+ The honest interim position is a documented limitation, not a patch:
101
+
102
+ > A surface managed by `surface.js` is presented in the document's own context,
103
+ > not its trigger's. Do not rely on a scoped theme, density, or custom property
104
+ > reaching an anchored surface through inheritance.
105
+
106
+ Applications that need a scoped surface today have two workarounds, both of
107
+ which put the scope where the panel lands rather than where it starts: declare
108
+ the scope on an ancestor that survives the move (`body`, or the dialog), or
109
+ carry the scope on the panel element itself.
110
+
111
+ ## Experiment
112
+
113
+ A branch has demonstrated the repair without changing any other contract:
114
+ [popover-manual-poc](popover-manual-poc.md) records what it measured, what it
115
+ refuted, and the checklist for the release that adopts it. Read that before
116
+ attempting this again — it also holds the negative results.
117
+
118
+ ## Review trigger
119
+
120
+ Revisit when Popover is guaranteed across Minimal, when a further inherited
121
+ context is found to break, or if a transport that preserves DOM ancestry
122
+ becomes available by another route.
@@ -4,21 +4,38 @@ The JS runtime doubles as a primitive kit for building custom widgets.
4
4
 
5
5
  ## Primitives
6
6
 
7
- | Need | Primitive | Import |
8
- |------|-----------|--------|
9
- | Opt-in discovery + lifecycle + cleanup | `enhance`, `registerEnhancement` | `actual-css/js/enhance` |
10
- | Per-document LIFO Escape dismissal | `escape` (`registerEscapeDismissal`) | `actual-css/js/escape` |
11
- | Positioning, flip/shift, `--available-height`, `data-placement` | `floating` (`track`, `reposition`, `repositionAt`) | `actual-css/js/floating` |
12
- | Open/close, sheet on mobile, backdrop, outside-click, Escape, focus restore | `surface` | `actual-css/js/surface` |
13
- | Directional item lookup, edges, wrapping, RTL | `keys` (`itemForKey`, `firstItem`, `lastItem`, `nextItem`) | `actual-css/js/keys` |
14
- | Menu-item vocabulary, usable-item filtering | `menu` | `actual-css/js/menu` |
15
- | Focusable lookup, visibility | `focus` | `actual-css/js/focus` |
16
- | One remembered tab stop with directional navigation | `focus-group` (`connectFocusGroup`) | `actual-css/js/focus-group` |
17
- | Caret-safe value rewriting | `input` (`onTextInput`, `setCaret`, `dispatchInput`) | `actual-css/js/input` |
18
- | Character policies | `filter` | `actual-css/js/filter` |
19
- | Event namespace | `events` (`EVENTS`, `ACTUAL_EVENT_PREFIX`) | `actual-css/js/events` |
20
- | Declarative triggers | `command` (`registerCommands`) | `actual-css/js/command` |
21
- | State classes the runtime writes | `selectors` (`CLASSES`) | `actual-css/js/selectors` |
7
+ | Need | Primitive | Import |
8
+ | ---------------------------------- | ------------- | --------------------------- |
9
+ | Discovery, lifecycle, cleanup | `enhance` | `actual-css/js/enhance` |
10
+ | Per-document LIFO Escape dismissal | `escape` | `actual-css/js/escape` |
11
+ | Positioning, flip and shift | `floating` | `@lekoala/floating` |
12
+ | Open/close, sheet, backdrop, focus | `surface` | `actual-css/js/surface` |
13
+ | Directional item lookup | `keys` | `actual-css/js/keys` |
14
+ | Menu-item vocabulary | `menu` | `actual-css/js/menu` |
15
+ | Focusable lookup, visibility | `focus` | `actual-css/js/focus` |
16
+ | One remembered tab stop | `focus-group` | `actual-css/js/focus-group` |
17
+ | Caret-safe value rewriting | `input` | `actual-css/js/input` |
18
+ | Character policies | `filter` | `actual-css/js/filter` |
19
+ | Event namespace | `events` | `actual-css/js/events` |
20
+ | Declarative triggers | `command` | `actual-css/js/command` |
21
+ | State classes the runtime writes | `selectors` | `actual-css/js/selectors` |
22
+
23
+ The named exports each module carries:
24
+
25
+ - `enhance` — `enhance`, `registerEnhancement`
26
+ - `escape` — `registerEscapeDismissal`
27
+ - `floating` — `autoUpdate`, `reposition`, `repositionAt`, plus
28
+ `--available-height` and `data-placement`
29
+ - `keys` — `itemForKey`, `firstItem`, `lastItem`, `nextItem`, with edge,
30
+ wrapping and RTL handling
31
+ - `focus-group` — `connectFocusGroup`
32
+ - `input` — `onTextInput`, `setCaret`, `dispatchInput`
33
+ - `events` — `EVENTS`, `ACTUAL_EVENT_PREFIX`
34
+ - `command` — `registerCommands`
35
+ - `selectors` — `CLASSES`
36
+
37
+ `surface` also owns outside-click and Escape dismissal, and focus restore.
38
+ `menu` filters to usable items.
22
39
 
23
40
  ## Focus group
24
41
 
@@ -77,6 +77,7 @@
77
77
  "pages": [
78
78
  "overview",
79
79
  "controls",
80
+ "combobox",
80
81
  "labels",
81
82
  "floating-field",
82
83
  "states-and-sizes",
@@ -21,13 +21,16 @@ itself needs to change, not `.soft`.
21
21
 
22
22
  ## Class reference
23
23
 
24
- | Class | Kind | Description |
25
- |------------------|-------------|-----------------------------------------------------------------------------------|
26
- | `.alert` | Component | Inline status surface; soft by default. |
27
- | `.alert-icon` | Composition | Decorative leading icon; pins to the first grid column. |
28
- | `.alert-dismiss` | Modifier | Compact dismiss button using the `--icon-close` mask and the `--dismiss` command. |
29
- | `.callout` | Variant | Neutral panel with a thick accent border on the leading edge. |
30
- | `.admonition` | Variant | Structured box with a tinted title bar and body on the page surface. |
24
+ | Class | Kind | Description |
25
+ | ---------------- | ----------- | --------------------------------------------- |
26
+ | `.alert` | Component | Inline status surface; soft by default. |
27
+ | `.alert-icon` | Composition | Decorative leading icon, in the first column. |
28
+ | `.alert-dismiss` | Modifier | Compact dismiss button. |
29
+ | `.callout` | Variant | Neutral panel, thick accent leading edge. |
30
+ | `.admonition` | Variant | Tinted title bar, body on the page surface. |
31
+
32
+ `.alert-dismiss` draws itself with the `--icon-close` mask and acts through the
33
+ `--dismiss` command.
31
34
  | `.alert-title` | Composition | Admonition title bar; hosts the dismiss button. |
32
35
  | `.alert-body` | Composition | Admonition body resting on the page surface. |
33
36
  | Shared intents | Intent | `.primary`, `.secondary`, `.success`, `.warning`, `.danger`. |
@@ -117,10 +120,12 @@ Use `.alert-dismiss` to let a user remove an inline alert. The button is transpa
117
120
  ```html demo
118
121
  <div class="alert warning" id="warning-alert">
119
122
  <i class="ti ti-alert-triangle alert-icon" aria-hidden="true"></i>
120
- <div>
123
+
124
+ <div class="stack" style="--gap: var(--space-10)">
121
125
  <strong>Warning</strong>
122
126
  <p>Something needs your attention.</p>
123
127
  </div>
128
+
124
129
  <button
125
130
  class="alert-dismiss"
126
131
  type="button"
@@ -5,6 +5,32 @@
5
5
  Aura adds a decorative intent-colored border and halo around one direct child.
6
6
  It is visual emphasis only and does not change the child's semantics.
7
7
 
8
+ ```html demo
9
+ <div class="cluster" style="gap: 2.5rem">
10
+ <span class="aura primary">
11
+ <article class="card" style="inline-size: 13rem">
12
+ <p><strong>Rotating arc</strong></p>
13
+ <p class="muted">One lit segment travels around the frame.</p>
14
+ </article>
15
+ </span>
16
+
17
+ <span class="aura aura-glow primary">
18
+ <article class="card" style="inline-size: 13rem">
19
+ <p><strong>Breathing glow</strong></p>
20
+ <p class="muted">The whole perimeter lights and breathes.</p>
21
+ </article>
22
+ </span>
23
+ </div>
24
+ ```
25
+
26
+ `.aura` sends a short accent of light travelling around the frame, over a
27
+ permanent halo that keeps the object present between two passes. Add
28
+ `.aura-glow` to light the whole perimeter evenly instead, as a halo that
29
+ breathes slowly.
30
+
31
+ Aura reads best when it has enough perimeter to travel around, and is naturally
32
+ more pronounced on dark surfaces. It still composes on small shapes:
33
+
8
34
  ```html demo
9
35
  <div class="cluster">
10
36
  <span class="aura primary">
@@ -14,22 +40,92 @@ It is visual emphasis only and does not change the child's semantics.
14
40
  <span class="aura aura-glow secondary">
15
41
  <span class="badge">New</span>
16
42
  </span>
43
+
44
+ <span class="aura aura-glow success">
45
+ <span class="badge success soft">Live</span>
46
+ </span>
47
+
48
+ <span class="aura danger">
49
+ <span class="badge danger solid" aria-label="12 unread notifications">12</span>
50
+ </span>
51
+ </div>
52
+ ```
53
+
54
+ Note the intent on the wrapper, not on the child: `.aura` is an intent
55
+ boundary, so the badge or alert inside keeps its own color and the frame keeps
56
+ the one you gave the wrapper. Matching them reads as one object; deliberately
57
+ mismatching them reads as two.
58
+
59
+ ```html demo
60
+ <div class="stack" style="max-inline-size: 28rem">
61
+ <span class="aura warning">
62
+ <div class="alert warning" role="alert">
63
+ <i class="ti ti-alert-triangle alert-icon" aria-hidden="true"></i>
64
+ <div>Your trial ends in three days.</div>
65
+ </div>
66
+ </span>
67
+
68
+ <span class="aura aura-glow danger">
69
+ <div class="alert danger" role="alert">
70
+ <i class="ti ti-credit-card-off alert-icon" aria-hidden="true"></i>
71
+ <div>Two payment methods failed. <a href="#">Review them</a>.</div>
72
+ </div>
73
+ </span>
17
74
  </div>
18
75
  ```
19
76
 
20
- The animated conic frame runs only when the user has not requested reduced
21
- motion. Add `.aura-glow` for a static, solid frame. The component uses an
22
- intent such as `.primary`, `.success`, or `.danger`; it does not introduce
23
- separate aura color variants.
77
+ Motion changes how the light lives, never whether the effect is there: when the
78
+ user has requested reduced motion, the travelling accent becomes two opposite
79
+ lit corners — a deliberate composition rather than a rotation frozen at an
80
+ arbitrary angle — and the glow stays a full, still halo.
81
+
82
+ The frame color comes from an intent such as `.primary`, `.success`, or
83
+ `.danger`; there are no separate aura color variants. The intent is sovereign:
84
+ a neutral intent produces a neutral aura. Use a chromatic intent when you want
85
+ a visibly luminous effect.
86
+
87
+ Two hooks tune how loud the effect is. `--aura-intensity` scales both halos at
88
+ once, resting and breathing alike, so the breath stays proportional at every
89
+ setting; `--aura-duration` sets one full rotation of the accent, or one breath
90
+ of the glow.
91
+
92
+ ```html demo
93
+ <div class="cluster" style="gap: 2rem">
94
+ <span class="aura aura-glow primary" style="--aura-intensity: 0.4">
95
+ <span class="card" style="inline-size: 8rem">Muted</span>
96
+ </span>
97
+
98
+ <span class="aura aura-glow primary">
99
+ <span class="card" style="inline-size: 8rem">Default</span>
100
+ </span>
101
+
102
+ <span class="aura aura-glow primary" style="--aura-intensity: 2">
103
+ <span class="card" style="inline-size: 8rem">Loud</span>
104
+ </span>
105
+
106
+ <span class="aura primary" style="--aura-duration: 2s">
107
+ <span class="card" style="inline-size: 8rem">Fast accent</span>
108
+ </span>
109
+ </div>
110
+ ```
24
111
 
25
112
  Keep exactly one direct child and set `--aura-radius` when its shape differs
26
113
  from the common button, card, alert, and badge shapes detected as a visual
27
114
  enhancement.
28
115
 
116
+ The child must be opaque. The frame paints across the whole box and relies on
117
+ the child to cover all but its edge, so a transparent child — an `.outline`
118
+ alert, a `.ghost` button — is filled edge to edge with the frame color instead
119
+ of framed by it. Give such a child a surface, or move the aura to an opaque
120
+ wrapper around it.
121
+
29
122
  ## CSS hooks
30
123
 
31
124
  - `--aura-color` — frame and halo color; follows the local intent.
32
125
  - `--aura-width` — frame thickness.
33
- - `--aura-blur` — halo blur radius.
126
+ - `--aura-blur` — near halo blur; the far halo derives its own blur and its
127
+ spread beyond the frame from this value, so one hook scales both.
128
+ - `--aura-intensity` — multiplies both halo opacities, resting and breathing.
129
+ It does not touch the frame itself, which stays a crisp line at any setting.
34
130
  - `--aura-radius` — child corner radius used to derive the outer radius.
35
- - `--aura-duration` — rotation duration when motion is allowed.
131
+ - `--aura-duration` — one rotation of the accent, or one breath of the glow.
@@ -12,12 +12,15 @@
12
12
 
13
13
  ## Class reference
14
14
 
15
- | Class | Kind | Description |
16
- |-----------------------------------|-------------|-------------------------------------------------------------------------|
17
- | `.avatar` | Component | Initials or image in a circular box; works on `div`, `a`, and `button`. |
18
- | `.avatar-stack` | Composition | Overlapping avatar group. |
19
- | `.badge:empty` (inside `.avatar`) | Composition | Status dot at the bottom-end corner; give it an `aria-label`. |
20
- | `.sm` / `.lg` | Size | Density; also rescales the whole `.avatar-stack`. |
15
+ | Class | Kind | Description |
16
+ | --------------- | ----------- | --------------------------------------- |
17
+ | `.avatar` | Component | Initials or image in a circular box. |
18
+ | `.avatar-stack` | Composition | Overlapping avatar group. |
19
+ | `.badge:empty` | Composition | Status dot, at the bottom-end corner. |
20
+ | `.sm` / `.lg` | Size | Density; also rescales `.avatar-stack`. |
21
+
22
+ `.avatar` works on `div`, `a` and `button`. The status dot is an empty
23
+ `.badge` inside an `.avatar`, and needs an `aria-label` of its own.
21
24
 
22
25
  ## Basic usage
23
26
 
@@ -20,13 +20,17 @@ full-width badge is intentional.
20
20
 
21
21
  ## Class reference
22
22
 
23
- | Class | Kind | Description |
24
- |-----------------|-------------|--------------------------------------------------------------|
25
- | `.badge` | Component | Compact label; soft by default. |
26
- | `.badge:empty` | Composition | Truly-empty badge renders as a solid dot. |
27
- | Shared intents | Intent | `.primary`, `.secondary`, `.success`, `.warning`, `.danger`. |
28
- | Shared variants | Variant | `.solid` (filled), `.outline` (bordered), `.surface` (untinted) emphasis. |
29
- | `.sm` / `.lg` | Size | Compact density. |
23
+ | Class | Kind | Description |
24
+ | --------------- | ----------- | ----------------------------------------- |
25
+ | `.badge` | Component | Compact label; soft by default. |
26
+ | `.badge:empty` | Composition | Truly-empty badge renders as a solid dot. |
27
+ | Shared intents | Intent | `.primary`, `.secondary`, `.success`, … |
28
+ | Shared variants | Variant | `.solid`, `.outline`, `.surface`. |
29
+ | `.sm` / `.lg` | Size | Compact density. |
30
+
31
+ The shared intents are `.primary`, `.secondary`, `.success`, `.warning` and
32
+ `.danger`. The variants set emphasis: `.solid` is filled, `.outline` bordered,
33
+ `.surface` untinted.
30
34
 
31
35
  ## Basic usage
32
36
 
@@ -4,23 +4,23 @@ Actions and navigation with shared intents, variants, sizes, and loading, presse
4
4
 
5
5
  ## Class reference
6
6
 
7
- | Class | Kind | Description |
8
- |---------------|-----------|--------------------------------------------------------------------------------------------|
9
- | `.btn` | Component | Base button. Solid by default; works on `<button>`, `<a href>`, and button-like `<input>`. |
10
- | `.primary` | Intent | Primary action. |
11
- | `.secondary` | Intent | Secondary action. |
12
- | `.success` | Intent | Positive outcome. |
13
- | `.warning` | Intent | Caution. |
14
- | `.danger` | Intent | Destructive action. |
15
- | `.neutral` | Intent | Neutral tone (the `.btn` default). |
16
- | `.soft` | Variant | Muted intent-tinted surface. |
17
- | `.outline` | Variant | Transparent with an intent-colored border. |
18
- | `.solid` | Variant | Filled intent background — the `.btn` default. |
19
- | `.surface` | Variant | Page surface with a neutral border; the intent colors the label only. |
20
- | `.ghost` | Variant | Borderless text button (button-only). |
21
- | `.link` | Variant | Underlined text button (button-only). |
22
- | `.sm` / `.lg` | Size | Compact or large control height. |
23
- | `.icon-only` | Variant | Square icon button sized to the control height. |
7
+ | Class | Kind | Description |
8
+ | ------------- | --------- | -------------------------------------------------- |
9
+ | `.btn` | Component | Base button, solid by default. |
10
+ | `.primary` | Intent | Primary action. |
11
+ | `.secondary` | Intent | Secondary action. |
12
+ | `.success` | Intent | Positive outcome. |
13
+ | `.warning` | Intent | Caution. |
14
+ | `.danger` | Intent | Destructive action. |
15
+ | `.neutral` | Intent | Neutral tone (the `.btn` default). |
16
+ | `.soft` | Variant | Muted intent-tinted surface. |
17
+ | `.outline` | Variant | Transparent with an intent-colored border. |
18
+ | `.solid` | Variant | Filled intent background — the `.btn` default. |
19
+ | `.surface` | Variant | Page surface, neutral border, intent on the label. |
20
+ | `.ghost` | Variant | Borderless text button (button-only). |
21
+ | `.link` | Variant | Underlined text button (button-only). |
22
+ | `.sm` / `.lg` | Size | Compact or large control height. |
23
+ | `.icon-only` | Variant | Square icon button sized to the control height. |
24
24
 
25
25
  ## Basic usage
26
26
 
@@ -4,14 +4,21 @@ Flexible content container with optional header, body, and footer regions, plus
4
4
 
5
5
  ## Class reference
6
6
 
7
- | Class | Kind | Description |
8
- |-------------|------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
9
- | `.card` | Component | Neutral raised surface for grouped content. |
10
- | `.raised` | Variant | Elevated surface with a soft shadow. |
11
- | `.subtle` | Variant | Lower contrast against the page surface. |
12
- | `.surface` | Variant | Shared variant: the page surface instead of `--surface-raised`, with the intent in the text only. |
13
- | `.inverted` | Surface modifier | Inverse surface treatment: paints `--surface-solid` and swaps the foreground to `--surface`, so it inverts in dark themes instead of always reading as dark. Apply as `class="card inverted"`, or on any block. |
14
- | `.compact` | Variant | Tighter padding for dense contexts. |
7
+ | Class | Kind | Description |
8
+ | ----------- | ---------------- | ------------------------------------------- |
9
+ | `.card` | Component | Neutral raised surface for grouped content. |
10
+ | `.raised` | Variant | Elevated surface with a soft shadow. |
11
+ | `.subtle` | Variant | Lower contrast against the page surface. |
12
+ | `.surface` | Variant | The page surface; intent in the text only. |
13
+ | `.inverted` | Surface modifier | Inverse surface treatment. |
14
+ | `.compact` | Variant | Tighter padding for dense contexts. |
15
+
16
+ `.surface` is a shared variant: it paints the page surface instead of
17
+ `--surface-raised` and leaves the intent to the text.
18
+
19
+ `.inverted` paints `--surface-solid` and swaps the foreground to `--surface`,
20
+ so it inverts in dark themes instead of always reading as dark. Write it as
21
+ `class="card inverted"`, or on any block.
15
22
 
16
23
  ## Basic usage
17
24
 
@@ -37,7 +37,7 @@ put `role="log"` on individual messages.
37
37
  ## Class reference
38
38
 
39
39
  | Class | Description |
40
- |---------------------------------|----------------------------------------------|
40
+ | ------------------------------- | -------------------------------------------- |
41
41
  | `.chat` | Message row grid. |
42
42
  | `.chat-start` / `.chat-end` | Logical inline placement. |
43
43
  | `.chat-avatar` | Avatar grid slot; compose it with `.avatar`. |
@@ -6,13 +6,16 @@
6
6
 
7
7
  ## Class reference
8
8
 
9
- | Class | Kind | Description |
10
- |------------------------|-----------|----------------------------------------------------------------------------------|
11
- | `.modal` | Component | Centered modal surface built on the native `<dialog>` element. |
12
- | `.scrollable` | Variant | Keeps the header and footer visible while the dialog body scrolls. |
13
- | `.dialog-confirmation` | Variant | Separates a confirmation message from its full-width action band. |
14
- | `.dialog-icon` | Component | Circular intent-aware icon well for confirmation messages. |
15
- | `.dialog-close` | Component | Icon-only close button anchored to the modal's top end, out of the content flow. |
9
+ | Class | Kind | Description |
10
+ | ---------------------- | --------- | ---------------------------------------------- |
11
+ | `.modal` | Component | Centered surface on the native `<dialog>`. |
12
+ | `.scrollable` | Variant | Header and footer stay while the body scrolls. |
13
+ | `.dialog-confirmation` | Variant | Message above a full-width action band. |
14
+ | `.dialog-icon` | Component | Circular intent-aware icon well. |
15
+ | `.dialog-close` | Component | Icon-only close button, at the top end. |
16
+
17
+ `.dialog-close` sits outside the content flow, so it does not take part in the
18
+ dialog's own layout.
16
19
 
17
20
  ## Usage
18
21
 
@@ -7,7 +7,7 @@
7
7
  ## Class reference
8
8
 
9
9
  | Class | Kind | Description |
10
- |-----------------|-----------|------------------------------------------------------------|
10
+ | --------------- | --------- | ---------------------------------------------------------- |
11
11
  | `.drawer` | Component | Modal side-sheet built on the native `<dialog>` element. |
12
12
  | `.drawer-close` | Component | Icon-only close button that sits inside the drawer header. |
13
13
 
@@ -12,10 +12,12 @@ the layout keeps it clear of adaptive application navigation and safe areas.
12
12
  ```html
13
13
  <div class="fab">
14
14
  <button class="btn circle primary lg icon-only" type="button" aria-label="Create">
15
- <span aria-hidden="true">+</span>
15
+ <i class="ti ti-plus" aria-hidden="true"></i>
16
16
  </button>
17
17
  </div>
18
18
  ```
19
+ The plain FAB button carries the application's own icon — it is a product
20
+ action, not a toggle.
19
21
 
20
22
  Use native `details` for a no-JavaScript speed dial. List actions in their
21
23
  visual top-to-bottom order so keyboard focus follows the same sequence.
@@ -26,9 +28,7 @@ visual top-to-bottom order so keyboard focus follows the same sequence.
26
28
  class="btn circle primary icon-only"
27
29
  style="--btn-min-size: 3.5rem"
28
30
  aria-label="Create"
29
- >
30
- <span aria-hidden="true">+</span>
31
- </summary>
31
+ ></summary>
32
32
 
33
33
  <div class="fab-actions">
34
34
  <div class="fab-action">
@@ -65,9 +65,7 @@ not the FAB's containing component. Production `.fab` remains viewport-fixed.
65
65
  class="btn circle primary icon-only"
66
66
  style="--btn-min-size: 3.5rem"
67
67
  aria-label="Create"
68
- >
69
- <span aria-hidden="true">+</span>
70
- </summary>
68
+ ></summary>
71
69
 
72
70
  <div class="fab-actions">
73
71
  <div class="fab-action">
@@ -101,13 +99,16 @@ Each `.fab-action` keeps its `.fab-label` and secondary button on one compact
101
99
  line. The example uses the button's existing `.lg` density for 44px secondary
102
100
  actions and its `--btn-min-size` hook for a dominant 56px trigger. Size still
103
101
  belongs to Button rather than introducing FAB-specific size modifiers.
104
- When the summary contains one decorative icon, FAB rotates it by 45 degrees in
105
- the open state so a plus naturally becomes a close symbol.
102
+ Unlike the plain FAB, the speed dial trigger takes no icon: FAB owns it as a
103
+ toggle and masks the shared `--icon-plus` glyph while closed and `--icon-close` —
104
+ the same close symbol used by alerts and dialogs — while open, both sized by
105
+ `--fab-icon-size`. No application icon and no rotated glyph is involved, so the
106
+ two states stay visually identical in size and stroke.
106
107
 
107
108
  ## Class reference
108
109
 
109
110
  | Class | Description |
110
- |----------------|-----------------------------------------------------------|
111
+ | -------------- | --------------------------------------------------------- |
111
112
  | `.fab` | Fixed viewport placement; use `details` for a speed dial. |
112
113
  | `.fab-actions` | Collapsible vertical action list. |
113
114
  | `.fab-action` | One horizontal label-and-button unit. |
@@ -117,6 +118,7 @@ the open state so a plus naturally becomes a close symbol.
117
118
 
118
119
  - `--fab-offset` — minimum viewport-edge offset.
119
120
  - `--fab-gap` — gap between actions and between the trigger and action list.
121
+ - `--fab-icon-size` — size of the speed dial toggle's open and close markers.
120
122
 
121
123
  FAB is hidden in print and uses `--z-menu`, leaving tooltips and status UI above
122
124
  it. Dialogs remain in the browser's top layer.
@@ -9,12 +9,12 @@ that owns its appearance.
9
9
 
10
10
  ## Class reference
11
11
 
12
- | Class | Kind | Description |
13
- |-------------------|-----------|--------------------------------------------------|
14
- | `.indicator` | Component | Positioning context for an overlaid child. |
15
- | `.indicator-item` | Part | Item attached to the top-end corner by default. |
16
- | `.start` | Placement | Moves the item to the inline-start corner. |
17
- | `.bottom` | Placement | Moves the item to the bottom corner. |
12
+ | Class | Kind | Description |
13
+ | ----------------- | --------- | ----------------------------------------------- |
14
+ | `.indicator` | Component | Positioning context for an overlaid child. |
15
+ | `.indicator-item` | Part | Item attached to the top-end corner by default. |
16
+ | `.start` | Placement | Moves the item to the inline-start corner. |
17
+ | `.bottom` | Placement | Moves the item to the bottom corner. |
18
18
 
19
19
  Combine `.bottom.start` for bottom-start. Logical inline placement follows the
20
20
  document direction automatically.