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
@@ -4,13 +4,20 @@ Explicit overflow handling for content that may exceed its container.
4
4
 
5
5
  ## Class reference
6
6
 
7
- | Class | Kind | Description |
8
- |--------------------|---------|-----------------------------------------------------------------------|
9
- | `.overflow-auto` | Utility | Enables scrolling when content exceeds the container. |
10
- | `.overflow-hidden` | Utility | Clips overflowing content. Optional layer. |
11
- | `.overflow-clip` | Utility | Clips overflowing content without a scroll container. Optional layer. |
12
- | `.overflow-x-auto` | Utility | Horizontal scrolling only. Optional layer. |
13
- | `.overflow-y-auto` | Utility | Vertical scrolling only. Optional layer. |
7
+ Base utilities:
8
+
9
+ | Class | Description |
10
+ | ---------------- | ----------------------------------------------------- |
11
+ | `.overflow-auto` | Enables scrolling when content exceeds the container. |
12
+
13
+ Extra utilities:
14
+
15
+ | Class | Description |
16
+ | ------------------ | ----------------------------------------------------- |
17
+ | `.overflow-hidden` | Clips overflowing content. |
18
+ | `.overflow-clip` | Clips overflowing content without a scroll container. |
19
+ | `.overflow-x-auto` | Horizontal scrolling only. |
20
+ | `.overflow-y-auto` | Vertical scrolling only. |
14
21
 
15
22
  Use `.overflow-auto` when content may overflow its container, especially tables and code-like regions.
16
23
 
@@ -7,6 +7,28 @@ Utilities are a small escape hatch for common single-purpose needs. They should
7
7
  - Keep utility names stable, boring, and few.
8
8
  - Utilities should use logical properties where relevant.
9
9
 
10
+ ## Two layers
11
+
12
+ Utilities ship in two modules, and the split is about **naming and curation**,
13
+ not about opting in. Importing `actual-css/css/utilities` — or
14
+ `actual.full.css` — gives you both.
15
+
16
+ | Module | Import | Naming |
17
+ | --------- | -------------------------------- | ---------------------------------- |
18
+ | **Base** | `actual-css/css/utilities/base` | Compact names, frequent operations |
19
+ | **Extra** | `actual-css/css/utilities/extra` | Explicit property/value names |
20
+
21
+ Base is the curated set, and it is where a short name is allowed: `.px`, `.py`,
22
+ `.mbs`, `.gap-none`. Extra spells out the property and the value — `.gap-sm`,
23
+ `.overflow-x-auto`, `.justify-content-center` — and never gives a base utility
24
+ a second spelling.
25
+
26
+ Reach for base first. Extra exists so an explicit one-off does not have to
27
+ become a new short name, which is the pressure that grows a utility surface.
28
+
29
+ Extra utilities are concentrated in flex, overflow and spacing; those pages
30
+ list the two layers as separate tables. Every other utility page is base only.
31
+
10
32
  ## Non-Goals
11
33
 
12
34
  > Utilities deliberately excluded to keep the surface small and intentional.
@@ -5,7 +5,7 @@ Rare shape utilities for cases that must be perfectly round or square.
5
5
  ## Class reference
6
6
 
7
7
  | Class | Kind | Description |
8
- |-----------|---------|-----------------------------------------------------|
8
+ | --------- | ------- | --------------------------------------------------- |
9
9
  | `.circle` | Utility | Forces a perfectly round shape via `--radius-full`. |
10
10
 
11
11
  Shape utilities should be rare because shape is mostly theme-level.
@@ -4,13 +4,13 @@ Small layout corrections for controls, labels, and compact rails.
4
4
 
5
5
  ## Class reference
6
6
 
7
- | Class | Kind | Description |
8
- |---------------------------|---------|---------------------------------------------------------------------------|
9
- | `.fit` | Utility | Shrinks a control or element to its content width. |
10
- | `.text-nowrap` | Utility | Keeps text on one line. |
11
- | `.truncate` | Utility | Ellipsizes overflowing single-line text. |
12
- | `.scroller` | Utility | Theme-aware scrollbar density and colour for scroll containers. Optional layer. |
13
- | `.scroller.stable-gutter` | Variant | Reserves scrollbar gutter space so layout does not shift. Optional layer. |
7
+ | Class | Kind | Description |
8
+ | ------------------------- | ------- | ----------------------------------------- |
9
+ | `.fit` | Utility | Shrinks an element to its content width. |
10
+ | `.text-nowrap` | Utility | Keeps text on one line. |
11
+ | `.truncate` | Utility | Ellipsizes overflowing single-line text. |
12
+ | `.scroller` | Layout | Theme-aware scrollbar density and colour. |
13
+ | `.scroller.stable-gutter` | Variant | Reserves inline-axis scrollbar gutter. |
14
14
 
15
15
  Use `.fit` when a control or element should shrink to its content instead of filling the available inline space.
16
16
 
@@ -78,7 +78,9 @@ above the capability floor. An engine that knows neither ignores both
78
78
  declarations and draws its native scrollbar — the intended fallback, not a
79
79
  broken state.
80
80
 
81
- `scroller` is not imported by `actual.css`. Import it explicitly when a project wants Actual CSS scrollbar chrome instead of the native OS default:
81
+ `.scroller` is a layout primitive, so it comes with `actual.full.css` and with
82
+ `actual-css/css/layout` like `.stack` or `.cluster`. A project importing module
83
+ by module reaches it directly:
82
84
 
83
85
  ```css
84
86
  @import "actual-css/css/layout/scroller";
@@ -4,25 +4,32 @@ Semantic step helpers for gap, padding, and margin — the most common inline-st
4
4
 
5
5
  ## Class reference
6
6
 
7
- | Class | Kind | Description |
8
- |-----------------------------|---------|---------------------------------------------------------------|
9
- | `.gap-none` | Utility | `gap: 0` on the element itself. |
10
- | `.py` | Utility | Block (vertical) padding at the current density step. |
11
- | `.px` | Utility | Inline (horizontal) padding at the current density step. |
12
- | `.mbs` | Utility | Block-start margin at the current density step. |
13
- | `.mbe` | Utility | Block-end margin at the current density step. |
14
- | `.gap-sm` | Utility | `gap: var(--space-20)` — denser row rhythm. Optional layer. |
15
- | `.gap-md` | Utility | `gap: var(--space-40)` — default row rhythm. Optional layer. |
16
- | `.gap-lg` | Utility | `gap: var(--space-50)` — roomier row rhythm. Optional layer. |
17
- | `.padding-context` | Utility | All-side padding at the current density step. Optional layer. |
18
- | `.gap-context` | Utility | `gap: var(--gap)`. Optional layer. |
19
- | `.row-gap-context` | Utility | `row-gap: var(--gap)`. Optional layer. |
20
- | `.column-gap-context` | Utility | `column-gap: var(--gap)`. Optional layer. |
21
- | `.margin-inline-auto` | Utility | Horizontally centers a block. Optional layer. |
22
- | `.margin-inline-start-auto` | Utility | Pushes a block toward the inline end. Optional layer. |
23
- | `.margin-inline-end-auto` | Utility | Pushes a block toward the inline start. Optional layer. |
24
- | `.margin-block-start-auto` | Utility | Pushes content toward the block end. Optional layer. |
25
- | `.margin-block-end-auto` | Utility | Pushes content toward the block start. Optional layer. |
7
+ Base utilities:
8
+
9
+ | Class | Description |
10
+ | ----------- | -------------------------------------------------------- |
11
+ | `.gap-none` | `gap: 0` on the element itself. |
12
+ | `.py` | Block (vertical) padding at the current density step. |
13
+ | `.px` | Inline (horizontal) padding at the current density step. |
14
+ | `.mbs` | Block-start margin at the current density step. |
15
+ | `.mbe` | Block-end margin at the current density step. |
16
+
17
+ Extra utilities:
18
+
19
+ | Class | Description |
20
+ | --------------------------- | --------------------------------------------- |
21
+ | `.gap-sm` | `gap: var(--space-20)` — denser row rhythm. |
22
+ | `.gap-md` | `gap: var(--space-40)` — default row rhythm. |
23
+ | `.gap-lg` | `gap: var(--space-50)` — roomier row rhythm. |
24
+ | `.padding-context` | All-side padding at the current density step. |
25
+ | `.gap-context` | `gap: var(--gap)`. |
26
+ | `.row-gap-context` | `row-gap: var(--gap)`. |
27
+ | `.column-gap-context` | `column-gap: var(--gap)`. |
28
+ | `.margin-inline-auto` | Horizontally centers a block. |
29
+ | `.margin-inline-start-auto` | Pushes a block toward the inline end. |
30
+ | `.margin-inline-end-auto` | Pushes a block toward the inline start. |
31
+ | `.margin-block-start-auto` | Pushes content toward the block end. |
32
+ | `.margin-block-end-auto` | Pushes content toward the block start. |
26
33
 
27
34
  Spacing helpers follow the density context. The default step is
28
35
  `--space-40` (1rem); the `.sm` context tightens it to `--space-20` (0.5rem)
@@ -5,7 +5,7 @@ Lightweight helpers for muted or secondary text without a full color scale.
5
5
  ## Class reference
6
6
 
7
7
  | Class | Kind | Description |
8
- |-----------------|---------|----------------------------------------------------------|
8
+ | --------------- | ------- | -------------------------------------------------------- |
9
9
  | `.muted` | Utility | Secondary text color. |
10
10
  | `.text-start` | Utility | Logical start-aligned text; sets `--text-align`. |
11
11
  | `.text-center` | Utility | Centered text; sets `--text-align`. |
package/llms.txt CHANGED
@@ -16,19 +16,37 @@ Actual CSS uses a small public grammar: `.component [intent] [variant] [size] [m
16
16
  - `actual-css/full` — the complete framework: core plus every component, form, layout, typography, effect, and utility family.
17
17
  - `actual-css/js` — the enhancement loader only; it discovers `data-enhance` markers and loads enhancers on demand.
18
18
  - `actual-css/js/full` — the complete runtime with all built-in enhancers pre-registered.
19
- - `actual-css/reserved-classes.json` — machine-readable list of every class name the framework can generate. Use it to validate class usage in HTML (an "unknown class" checker) instead of guessing names from other frameworks.
19
+ - `actual-css/reserved-classes.json` — machine-readable list of the framework-owned class namespace. Use it to check whether a class belongs to Actual CSS and to validate claimed framework classes; application-specific classes are expected to exist outside this manifest.
20
20
  - Individual families and modules are importable separately (`actual-css/css/components`, `actual-css/css/layout/grid`, `actual-css/js/dialog`, …); see [README](README.md) and [Modular imports](docs/pages/guides/modular-import.md).
21
21
 
22
+ Forms are the one family with two aggregate entrypoints:
23
+ `actual-css/css/forms` is the native-control baseline; use
24
+ `actual-css/css/forms/all` for the complete forms family.
25
+
22
26
  The composition guides in [README](README.md) and [Progressive Enhancements](docs/pages/enhancements/overview.md)
23
27
  show supported CSS modules and individual enhancer imports without duplicating
24
28
  the full export list here.
25
29
 
26
30
  Presentation (`class`) and behavior (`data-enhance`) are separate layers.
27
- Behaviour primitives (`enhance`, `floating`, `surface`, `keys`, `menu`, `focus`,
28
- `events`) are independently importable for building custom widgets.
29
- See [enhancement contract](docs/design-notes/enhancement-contract.md) and
31
+ Behaviour primitives are independently importable for building custom widgets,
32
+ including `enhance`, `surface`, `floating`, `focus-group`, `keys`, `input`,
33
+ `command`, `menu`, `focus`, `escape`, and `events`. See
34
+ [enhancement contract](docs/design-notes/enhancement-contract.md) and
30
35
  [widget primitives](docs/design-notes/widget-primitives.md).
31
36
 
37
+ ## Choosing a page shell
38
+
39
+ Before choosing a content layout, choose the page shell:
40
+
41
+ - Normal scrolling page, marketing/editorial site, or ordinary application page
42
+ → semantic landmarks; optionally `.app-shell` and `.navbar`.
43
+ - Persistent topbar + independently scrolling main + adaptive bottom/side
44
+ primary navigation → `.app-layout`.
45
+
46
+ Do not choose `.app-layout` merely because the product is an application. Its
47
+ viewport-sized shell, internal scrolling, and adaptive navigation are one
48
+ combined contract. See [layout choices](docs/pages/layout/choosing.md).
49
+
32
50
  ## Choosing a layout primitive
33
51
 
34
52
  Pick from the relationship between children, not the number of children in one mockup:
@@ -41,6 +59,11 @@ Pick from the relationship between children, not the number of children in one m
41
59
  - Explicit region placement on a shared 12-column canvas → `.column-layout`
42
60
  - Vertical flow → `.stack`; wrapping inline items → `.cluster`
43
61
 
62
+ `.container-query` establishes the framework's shared
63
+ `actual-container / inline-size` container for components that explicitly
64
+ respond to allocated space; it is not a general prerequisite for responsive
65
+ layouts.
66
+
44
67
  Use `--grid-columns` on `.grid` for exact track templates that do not match a
45
68
  built-in layout contract: arbitrary structural counts (seven calendar days),
46
69
  asymmetric fractional tracks, or mixed intrinsic/flexible tracks. See
package/package.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "name": "actual-css",
3
- "version": "0.5.0",
3
+ "version": "0.7.0",
4
4
  "description": "Plain CSS component framework with semantic classes, universal variants, small tokens, theme hooks, and progressive enhancements.",
5
5
  "type": "module",
6
- "packageManager": "bun@1.4.0",
6
+ "packageManager": "bun@1.4.1",
7
7
  "license": "MIT",
8
8
  "author": "Thomas <thomas@lekoala.be>",
9
9
  "repository": {
@@ -58,7 +58,6 @@
58
58
  "./js/parse-config": "./src/js/parse-config.js",
59
59
  "./js/events": "./src/js/events.js",
60
60
  "./js/filter": "./src/js/filter.js",
61
- "./js/floating": "./src/js/floating.js",
62
61
  "./js/flyout": "./src/js/flyout.js",
63
62
  "./js/focus": "./src/js/focus.js",
64
63
  "./js/focus-group": "./src/js/focus-group.js",
@@ -115,6 +114,7 @@
115
114
  "build:docs": "bun run build:themes && bun scripts/build-docs.js",
116
115
  "watch:docs": "bun run build:themes && bun scripts/build-docs.js --watch",
117
116
  "check:docs": "bun scripts/check-docs.js",
117
+ "format:docs": "bun scripts/format-doc-tables.js",
118
118
  "check:doc-classes": "bun scripts/check-doc-classes.js",
119
119
  "check:compat": "bun scripts/check-compat.js",
120
120
  "check:css-api": "bun scripts/check-css-api.js",
@@ -136,11 +136,14 @@
136
136
  "check:sync": "bun scripts/check-sync.js",
137
137
  "test": "bun test",
138
138
  "test:changed": "bun test --changed=master",
139
- "build:all": "bun run lint && bun run build:dist && bun run build:js && bun run build:docs && bun run check:docs && bun run check:doc-classes && bun run check:compat && bun run check:css-api && bun run check:color-space && bun run check:architecture && bun run check:enhance && bun run generate:reserved && bun run check:reserved && bun run check:sync && bun run check:links && bun run check:templates && bun run test && bun run build:size",
139
+ "build:all": "bun run lint && bun run build:dist && bun run build:js && bun run format:docs && bun run build:docs && bun run check:docs && bun run check:doc-classes && bun run check:compat && bun run check:css-api && bun run check:color-space && bun run check:architecture && bun run check:enhance && bun run generate:reserved && bun run check:reserved && bun run check:sync && bun run check:links && bun run check:templates && bun run test && bun run build:size",
140
140
  "prepublishOnly": "bun run build:all"
141
141
  },
142
142
  "devDependencies": {
143
143
  "@biomejs/biome": "^2.5",
144
144
  "happy-dom": "^20.11"
145
+ },
146
+ "dependencies": {
147
+ "@lekoala/floating": "^0.1.0"
145
148
  }
146
149
  }
@@ -75,7 +75,6 @@
75
75
  "column-start-8",
76
76
  "column-start-9",
77
77
  "compact",
78
- "complete",
79
78
  "container-query",
80
79
  "cursor-pointer",
81
80
  "danger",
@@ -243,7 +242,11 @@
243
242
  "stack",
244
243
  "start",
245
244
  "status-bar",
245
+ "step-complete",
246
+ "step-label",
246
247
  "steps",
248
+ "steps-horizontal",
249
+ "steps-vertical",
247
250
  "sticky",
248
251
  "stretch",
249
252
  "subtle",
@@ -1,14 +1,17 @@
1
1
  /*
2
2
  * Fixed floating action placement and a native-details speed dial.
3
- * Button owns the action's size, shape, and intent; FAB only owns placement.
3
+ * Button owns the trigger's size, shape, and intent; FAB owns placement and
4
+ * the speed dial's structural open/close markers (--icon-plus / --icon-close).
4
5
  *
5
6
  * Public hooks:
6
7
  * --fab-offset
7
8
  * --fab-gap
9
+ * --fab-icon-size
8
10
  */
9
11
  .fab {
10
12
  --fab-offset: var(--space-40);
11
13
  --fab-gap: var(--space-20);
14
+ --fab-icon-size: 1.5rem;
12
15
 
13
16
  position: fixed;
14
17
  z-index: var(--z-menu);
@@ -17,6 +20,9 @@
17
20
  }
18
21
 
19
22
  .fab > summary {
23
+ /* Centers the toggle markers: the summary is the markers' positioned
24
+ context, not just the app-button class. */
25
+ position: relative;
20
26
  list-style: none;
21
27
  cursor: pointer;
22
28
  }
@@ -25,11 +31,35 @@
25
31
  display: none;
26
32
  }
27
33
 
28
- /* A lone decorative plus can communicate the open/close state without
29
- requiring a second icon or JavaScript-managed content. */
30
- .fab > summary > [aria-hidden="true"]:only-child {
31
- display: inline-block;
32
- transition: transform var(--duration);
34
+ /* The speed dial trigger is an icon-only toggle. FAB owns both structural
35
+ markers: masked pseudo-elements swap the shared plus (closed) and close
36
+ (open) glyphs at the same size, so the two states never drift apart. */
37
+ .fab > summary::before,
38
+ .fab > summary::after {
39
+ content: "";
40
+ position: absolute;
41
+ inset: 0;
42
+ margin: auto;
43
+ inline-size: var(--fab-icon-size);
44
+ block-size: var(--fab-icon-size);
45
+ background: currentColor;
46
+ opacity: 0;
47
+ transform: scale(0.8);
48
+ transition:
49
+ opacity var(--duration),
50
+ transform var(--duration);
51
+ }
52
+
53
+ .fab > summary::before {
54
+ -webkit-mask: var(--icon-plus) center / contain no-repeat;
55
+ mask: var(--icon-plus) center / contain no-repeat;
56
+ opacity: 1;
57
+ transform: none;
58
+ }
59
+
60
+ .fab > summary::after {
61
+ -webkit-mask: var(--icon-close) center / contain no-repeat;
62
+ mask: var(--icon-close) center / contain no-repeat;
33
63
  }
34
64
 
35
65
  .fab-actions {
@@ -87,8 +117,14 @@
87
117
  transition-delay: 0s;
88
118
  }
89
119
 
90
- .fab[open] > summary > [aria-hidden="true"]:only-child {
91
- transform: rotate(45deg);
120
+ .fab[open] > summary::before {
121
+ opacity: 0;
122
+ transform: scale(0.8);
123
+ }
124
+
125
+ .fab[open] > summary::after {
126
+ opacity: 1;
127
+ transform: none;
92
128
  }
93
129
 
94
130
  @media print {
@@ -79,12 +79,50 @@
79
79
  transition: opacity var(--duration-fast) ease;
80
80
  }
81
81
 
82
- .flyout.is-open:not(.is-sheet) {
82
+ /* Native popovers enter the top layer with UA geometry that conflicts with
83
+ anchored positioning. The reset is gated on real Popover support because an
84
+ attribute selector matches whether or not the engine implements Popover:
85
+ without it the UA neither hides nor promotes the element, so an ungated
86
+ reset would strand a permanently visible panel at inset: auto. Popover is
87
+ not part of Actual's Minimal contract — engines inside the Minimal range
88
+ may lack it — so the gate is about the capability, not the floor. Kept
89
+ scoped to Actual surfaces so bare [popover] elements retain their platform
90
+ presentation. */
91
+ @supports selector(:popover-open) {
92
+ .flyout[popover] {
93
+ position: fixed;
94
+ inset: auto;
95
+ margin: 0;
96
+ }
97
+
98
+ /* A presentation class may own its *visible* display type, but must not
99
+ override the platform's hidden state. The UA closes a popover with
100
+ display: none, and an author declaration beats the UA origin whatever the
101
+ specificity — so .flyout's own display: grid would leave a closed popover
102
+ painted at its static position. Restoring both properties here also gives
103
+ the native path the exit target the runtime path already has in
104
+ .flyout[hidden]: without an opacity to animate, the overlay/display
105
+ allow-discrete transition below would hold a fully opaque panel in the top
106
+ layer and then snap it away. */
107
+ .flyout[popover]:not(:popover-open) {
108
+ display: none;
109
+ opacity: 0;
110
+ }
111
+ }
112
+
113
+ /* compat-ok: :popover-open rides in a forgiving :is(), where an engine without
114
+ Popover discards just that member and .is-open alone keeps driving the open
115
+ state. This is a property of the forgiving list, not of the pseudo-class: in
116
+ a plain selector list an unsupported member would invalidate the whole rule.
117
+ Keep it inside :is()/:where() or gate it. */
118
+ .flyout:is(.is-open, :popover-open):not(.is-sheet) {
83
119
  inset: auto;
84
120
  }
85
121
 
122
+ /* compat-ok: forgiving :is() again, and @starting-style is itself unsupported
123
+ across part of the Minimal range, so the rule simply does not exist there. */
86
124
  @starting-style {
87
- .flyout.is-open {
125
+ .flyout:is(.is-open, :popover-open) {
88
126
  opacity: 0;
89
127
  }
90
128
  }
@@ -123,16 +161,23 @@
123
161
  translate: 0 var(--space-30);
124
162
  }
125
163
 
164
+ /* compat-ok: forgiving :is() inside @starting-style, as above. */
126
165
  @starting-style {
127
- .flyout.is-sheet.is-open {
166
+ .flyout.is-sheet:is(.is-open, :popover-open) {
128
167
  translate: 0 var(--space-30);
129
168
  }
130
169
  }
131
170
 
171
+ /* display keeps the exit transition alive for the [hidden] path; overlay does
172
+ the same for the native popover path, where hidePopover() would otherwise
173
+ drop the panel out of the top layer on the first frame and let a parent
174
+ overflow or a dialog clip the fade. An engine that cannot transition either
175
+ property ignores it and the exit is instant. */
132
176
  @supports (transition-behavior: allow-discrete) {
133
177
  .flyout {
134
178
  transition:
135
179
  opacity var(--duration-fast) ease,
180
+ overlay var(--duration-fast) allow-discrete,
136
181
  display var(--duration-fast) allow-discrete;
137
182
  }
138
183
 
@@ -140,6 +185,7 @@
140
185
  transition:
141
186
  opacity var(--duration-fast) ease,
142
187
  translate var(--duration-fast) ease,
188
+ overlay var(--duration-fast) allow-discrete,
143
189
  display var(--duration-fast) allow-discrete;
144
190
  }
145
191
  }
@@ -63,6 +63,13 @@
63
63
  border-end-start-radius: var(--join-es);
64
64
  }
65
65
 
66
+ /* A square icon button has an explicit block size, which otherwise prevents
67
+ align-items: stretch from matching a taller labelled sibling. The button's
68
+ existing min-block-size keeps the standalone square-size invariant intact. */
69
+ .join > .btn.icon-only {
70
+ block-size: auto;
71
+ }
72
+
66
73
  .join > :first-child {
67
74
  --join-ss: var(--join-radius);
68
75
  --join-es: var(--join-radius);
@@ -33,7 +33,9 @@
33
33
  .list > li > .list-item {
34
34
  display: grid;
35
35
  grid-template-columns: auto minmax(0, 1fr) auto;
36
- align-items: center;
36
+ /* Regions align to the first line, so leading, title, and trailing controls
37
+ share one horizontal line while supporting text wraps beneath. */
38
+ align-items: start;
37
39
  gap: var(--list-item-gap);
38
40
  min-block-size: var(--list-item-min-size);
39
41
  padding-block: var(--list-item-pad-block);
@@ -75,30 +75,36 @@
75
75
  /* Checkable menu items are one command with one accessible state. Their
76
76
  indicator is generated so authors do not add a second interactive input. */
77
77
  .menu-item:is([role="menuitemcheckbox"], [role="menuitemradio"])::before {
78
- display: grid;
78
+ content: "";
79
+ display: block;
79
80
  flex: 0 0 var(--menu-item-icon-size);
80
81
  inline-size: var(--menu-item-icon-size);
81
- place-items: center;
82
- }
83
-
84
- .menu-item[role="menuitemcheckbox"]::before {
85
- content: "";
82
+ block-size: var(--menu-item-icon-size);
86
83
  }
87
84
 
88
85
  .menu-item[role="menuitemcheckbox"][aria-checked="true"]::before {
89
- content: "✓";
86
+ background: currentColor;
87
+ -webkit-mask: var(--icon-check) center / contain no-repeat;
88
+ mask: var(--icon-check) center / contain no-repeat;
90
89
  }
91
90
 
92
91
  .menu-item[role="menuitemcheckbox"][aria-checked="mixed"]::before {
93
- content: "−";
92
+ background:
93
+ linear-gradient(currentColor 0 0) center / 60% max(var(--border-width), 0.125em) no-repeat;
94
94
  }
95
95
 
96
96
  .menu-item[role="menuitemradio"]::before {
97
- content: "○";
97
+ background: radial-gradient(circle, transparent 0 46%, currentColor 48% 58%, transparent 60%);
98
98
  }
99
99
 
100
100
  .menu-item[role="menuitemradio"][aria-checked="true"]::before {
101
- content: "●";
101
+ background: radial-gradient(
102
+ circle,
103
+ currentColor 0 25%,
104
+ transparent 27% 46%,
105
+ currentColor 48% 58%,
106
+ transparent 60%
107
+ );
102
108
  }
103
109
 
104
110
  .menu-item:focus-visible {