actual-css 0.4.2 → 0.6.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 (117) hide show
  1. package/CHANGELOG.md +66 -3
  2. package/README.md +2 -2
  3. package/dist/actual.css +54 -14
  4. package/dist/actual.full.css +744 -185
  5. package/dist/actual.full.js +1 -1
  6. package/dist/actual.full.min.css +1 -1
  7. package/dist/actual.min.css +1 -1
  8. package/docs/design-notes/browser-support.md +14 -14
  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 +9 -0
  14. package/docs/design-notes/references.md +4 -0
  15. package/docs/design-notes/spacing-units.md +286 -0
  16. package/docs/design-notes/steps.md +748 -0
  17. package/docs/design-notes/widget-primitives.md +32 -15
  18. package/docs/navigation.json +2 -0
  19. package/docs/pages/components/alert.md +14 -9
  20. package/docs/pages/components/app-navigation.md +2 -0
  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 +27 -9
  24. package/docs/pages/components/button.md +25 -18
  25. package/docs/pages/components/card.md +27 -7
  26. package/docs/pages/components/chat.md +1 -1
  27. package/docs/pages/components/data-list.md +72 -0
  28. package/docs/pages/components/dialog.md +12 -7
  29. package/docs/pages/components/drawer.md +3 -1
  30. package/docs/pages/components/fab.md +14 -10
  31. package/docs/pages/components/indicator.md +6 -6
  32. package/docs/pages/components/list.md +11 -2
  33. package/docs/pages/components/meter.md +56 -1
  34. package/docs/pages/components/navbar.md +1 -1
  35. package/docs/pages/components/pagination.md +1 -1
  36. package/docs/pages/components/progress.md +10 -0
  37. package/docs/pages/components/rating.md +4 -4
  38. package/docs/pages/components/skeleton.md +3 -1
  39. package/docs/pages/components/spinner.md +1 -1
  40. package/docs/pages/components/steps.md +379 -47
  41. package/docs/pages/components/table.md +6 -5
  42. package/docs/pages/enhancements/flyout.md +14 -10
  43. package/docs/pages/enhancements/overview.md +2 -2
  44. package/docs/pages/enhancements/scrollspy.md +1 -1
  45. package/docs/pages/enhancements/status-bar.md +10 -1
  46. package/docs/pages/enhancements/tabs.md +1 -1
  47. package/docs/pages/examples/overview.md +4 -1
  48. package/docs/pages/forms/choice-cards.md +4 -4
  49. package/docs/pages/forms/combobox.md +87 -0
  50. package/docs/pages/forms/floating-field.md +5 -3
  51. package/docs/pages/forms/joined-controls.md +6 -4
  52. package/docs/pages/forms/labels.md +8 -5
  53. package/docs/pages/forms/overview.md +26 -19
  54. package/docs/pages/forms/password.md +3 -2
  55. package/docs/pages/forms/range.md +15 -2
  56. package/docs/pages/forms/states-and-sizes.md +7 -5
  57. package/docs/pages/forms/validation.md +15 -9
  58. package/docs/pages/foundations/philosophy.md +11 -1
  59. package/docs/pages/foundations/tokens.md +16 -9
  60. package/docs/pages/foundations/typography.md +11 -3
  61. package/docs/pages/guides/bootstrap.md +3 -3
  62. package/docs/pages/guides/introduction.md +2 -2
  63. package/docs/pages/guides/modular-import.md +21 -10
  64. package/docs/pages/guides/progressive-enhancement.md +75 -54
  65. package/docs/pages/guides/tailwind.md +2 -2
  66. package/docs/pages/layout/app-shell.md +4 -4
  67. package/docs/pages/layout/choosing.md +10 -6
  68. package/docs/pages/layout/grid.md +11 -10
  69. package/docs/pages/layout/scroll-snap.md +2 -0
  70. package/docs/pages/layout/sidebar-layout.md +2 -0
  71. package/docs/pages/layout/stack.md +10 -6
  72. package/docs/pages/layout/topbar.md +2 -0
  73. package/docs/pages/patterns/structured-lists.md +7 -7
  74. package/docs/pages/utilities/accessibility.md +19 -1
  75. package/docs/pages/utilities/flex.md +20 -13
  76. package/docs/pages/utilities/links.md +1 -1
  77. package/docs/pages/utilities/list.md +1 -1
  78. package/docs/pages/utilities/overflow.md +14 -7
  79. package/docs/pages/utilities/overview.md +22 -0
  80. package/docs/pages/utilities/shape.md +1 -1
  81. package/docs/pages/utilities/sizing-wrapping.md +10 -8
  82. package/docs/pages/utilities/spacing.md +26 -19
  83. package/docs/pages/utilities/text.md +1 -1
  84. package/llms.txt +27 -4
  85. package/package.json +3 -2
  86. package/scripts/reserved-classes.json +8 -1
  87. package/src/css/components/data-list.css +27 -0
  88. package/src/css/components/drawer.css +30 -11
  89. package/src/css/components/fab.css +44 -8
  90. package/src/css/components/flyout.css +17 -12
  91. package/src/css/components/index.css +1 -0
  92. package/src/css/components/list.css +3 -1
  93. package/src/css/components/meter.css +28 -1
  94. package/src/css/components/modal.css +28 -11
  95. package/src/css/components/progress.css +5 -0
  96. package/src/css/components/status-bar.css +31 -15
  97. package/src/css/components/steps.css +289 -55
  98. package/src/css/components/surface.css +5 -3
  99. package/src/css/components/tooltip.css +5 -8
  100. package/src/css/core/base.css +7 -1
  101. package/src/css/core/intents.css +6 -0
  102. package/src/css/core/tokens.css +24 -9
  103. package/src/css/core/variants.css +17 -4
  104. package/src/css/effects/aura.css +101 -11
  105. package/src/css/forms/custom-select.css +1 -1
  106. package/src/css/forms/floating-field.css +8 -6
  107. package/src/css/forms/form.css +4 -4
  108. package/src/css/layout/container-query.css +37 -13
  109. package/src/css/layout/grid.css +7 -7
  110. package/src/css/layout/index.css +1 -1
  111. package/src/css/typography/prose.css +2 -3
  112. package/src/css/utilities/base.css +16 -0
  113. package/src/js/password.js +1 -1
  114. package/src/js/selectors.js +1 -0
  115. package/src/js/status.js +88 -7
  116. package/src/js/surface.js +2 -11
  117. package/src/js/transition.js +20 -0
@@ -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,6 +1,6 @@
1
1
  {
2
2
  "name": "actual-css",
3
- "version": "0.4.2",
3
+ "version": "0.6.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
6
  "packageManager": "bun@1.4.0",
@@ -115,6 +115,7 @@
115
115
  "build:docs": "bun run build:themes && bun scripts/build-docs.js",
116
116
  "watch:docs": "bun run build:themes && bun scripts/build-docs.js --watch",
117
117
  "check:docs": "bun scripts/check-docs.js",
118
+ "format:docs": "bun scripts/format-doc-tables.js",
118
119
  "check:doc-classes": "bun scripts/check-doc-classes.js",
119
120
  "check:compat": "bun scripts/check-compat.js",
120
121
  "check:css-api": "bun scripts/check-css-api.js",
@@ -136,7 +137,7 @@
136
137
  "check:sync": "bun scripts/check-sync.js",
137
138
  "test": "bun test",
138
139
  "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",
140
+ "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
141
  "prepublishOnly": "bun run build:all"
141
142
  },
142
143
  "devDependencies": {
@@ -75,10 +75,10 @@
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",
81
+ "data-list",
82
82
  "dialog-close",
83
83
  "dialog-confirmation",
84
84
  "dialog-icon",
@@ -134,6 +134,7 @@
134
134
  "inline-size-full",
135
135
  "input",
136
136
  "input-icon",
137
+ "intent-color",
137
138
  "inverted",
138
139
  "is-open",
139
140
  "is-sheet",
@@ -241,11 +242,16 @@
241
242
  "stack",
242
243
  "start",
243
244
  "status-bar",
245
+ "step-complete",
246
+ "step-label",
244
247
  "steps",
248
+ "steps-horizontal",
249
+ "steps-vertical",
245
250
  "sticky",
246
251
  "stretch",
247
252
  "subtle",
248
253
  "success",
254
+ "surface",
249
255
  "surface-backdrop",
250
256
  "switch",
251
257
  "switcher",
@@ -269,6 +275,7 @@
269
275
  "textarea",
270
276
  "tooltip",
271
277
  "topbar",
278
+ "touch-target",
272
279
  "truncate",
273
280
  "user-select-none",
274
281
  "visibility-hidden",
@@ -0,0 +1,27 @@
1
+ /*
2
+ * Data list — term/value pairs for the properties of a single object.
3
+ *
4
+ * Expects alternating <dt>/<dd> children: one term, one value per property.
5
+ * A plain <dl> handles richer relationships (several terms or descriptions)
6
+ * and stays untouched by this component.
7
+ *
8
+ * Why margin resets: neutralizes .prose list margins if .data-list appears
9
+ * inside an authored rich-text scope; the grid owns row/column rhythm.
10
+ */
11
+ .data-list {
12
+ display: grid;
13
+ grid-template-columns: max-content minmax(0, 1fr);
14
+ align-items: baseline;
15
+ gap: var(--space-20) var(--space-40);
16
+ }
17
+
18
+ .data-list > dt {
19
+ min-inline-size: 0;
20
+ margin-block-start: 0;
21
+ font-weight: var(--font-weight-strong);
22
+ }
23
+
24
+ .data-list > dd {
25
+ min-inline-size: 0;
26
+ margin: 0;
27
+ }
@@ -13,11 +13,16 @@
13
13
  *
14
14
  * Internal:
15
15
  * --drawer-closed-translate (side/RTL positioning state)
16
+ * --drawer-motion-duration (shared by panel and ::backdrop so exit duration
17
+ * changes in one place; entry state redefines it longer)
16
18
  */
17
19
  dialog.drawer {
18
20
  --drawer-size: 20rem;
19
21
  --drawer-pad: var(--space-40);
20
22
  --drawer-closed-translate: -100%;
23
+ /* Exit is shorter than entry so the panel clears quickly; the scrim reads
24
+ the same variable through inheritance and never lags the panel. */
25
+ --drawer-motion-duration: var(--duration);
21
26
 
22
27
  /* Keep the surface anchored to the viewport in the top layer. This also
23
28
  provides the containing block for the absolutely positioned close button.
@@ -41,16 +46,20 @@ dialog.drawer {
41
46
  opacity: 0;
42
47
  transform: translateX(var(--drawer-closed-translate));
43
48
  transition:
44
- opacity var(--duration-slow),
45
- transform var(--duration-slow);
49
+ opacity var(--drawer-motion-duration) var(--ease-exit),
50
+ transform var(--drawer-motion-duration) var(--ease-exit);
46
51
  }
47
52
 
48
53
  dialog.drawer[open] {
54
+ --drawer-motion-duration: var(--duration-slow);
49
55
  display: grid;
50
56
  grid-template-rows: auto minmax(0, 1fr) auto;
51
57
  gap: var(--space-40);
52
58
  opacity: 1;
53
59
  transform: translateX(0);
60
+ transition:
61
+ opacity var(--drawer-motion-duration) var(--ease-enter),
62
+ transform var(--drawer-motion-duration) var(--ease-enter);
54
63
  }
55
64
 
56
65
  /* Backdrop color sourced from components/surface.css so it matches .modal
@@ -58,7 +67,9 @@ dialog.drawer[open] {
58
67
  dialog.drawer::backdrop {
59
68
  background: var(--backdrop-fill);
60
69
  opacity: 0;
61
- transition: opacity var(--duration-slow);
70
+ /* Scrim curve stays neutral `ease`; only the panel's character curves
71
+ apply. Duration follows --drawer-motion-duration through inheritance. */
72
+ transition: opacity var(--drawer-motion-duration) ease;
62
73
  }
63
74
 
64
75
  dialog.drawer[data-side="end"] {
@@ -94,17 +105,25 @@ dialog.drawer nav {
94
105
  @supports (transition-behavior: allow-discrete) {
95
106
  dialog.drawer {
96
107
  transition:
97
- opacity var(--duration-slow),
98
- transform var(--duration-slow),
99
- overlay var(--duration-slow) allow-discrete,
100
- display var(--duration-slow) allow-discrete;
108
+ opacity var(--drawer-motion-duration) var(--ease-exit),
109
+ transform var(--drawer-motion-duration) var(--ease-exit),
110
+ overlay var(--drawer-motion-duration) allow-discrete,
111
+ display var(--drawer-motion-duration) allow-discrete;
112
+ }
113
+
114
+ dialog.drawer[open] {
115
+ transition:
116
+ opacity var(--drawer-motion-duration) var(--ease-enter),
117
+ transform var(--drawer-motion-duration) var(--ease-enter),
118
+ overlay var(--drawer-motion-duration) allow-discrete,
119
+ display var(--drawer-motion-duration) allow-discrete;
101
120
  }
102
121
 
103
122
  dialog.drawer::backdrop {
104
123
  transition:
105
- opacity var(--duration-slow),
106
- overlay var(--duration-slow) allow-discrete,
107
- display var(--duration-slow) allow-discrete;
124
+ opacity var(--drawer-motion-duration) ease,
125
+ overlay var(--drawer-motion-duration) allow-discrete,
126
+ display var(--drawer-motion-duration) allow-discrete;
108
127
  }
109
128
  }
110
129
 
@@ -140,7 +159,7 @@ dialog.drawer nav {
140
159
 
141
160
  @media (prefers-reduced-motion: no-preference) {
142
161
  dialog.drawer.is-static {
143
- animation: actual-dialog-static var(--duration-slow) var(--ease);
162
+ animation: actual-dialog-static var(--duration-slow) ease;
144
163
  }
145
164
  }
146
165
 
@@ -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 {
@@ -73,11 +73,10 @@
73
73
  box-shadow: var(--shadow-popout);
74
74
  list-style: none;
75
75
  opacity: 1;
76
- scale: 1;
77
- transition:
78
- opacity var(--duration-fast) var(--ease),
79
- scale var(--duration-fast) var(--ease),
80
- translate var(--duration-fast) var(--ease);
76
+ /* Anchored flyouts fade in and out; scaling around the center would detach
77
+ the anchored edge from its trigger on wide panels. The sheet presentation
78
+ below adds its own translate motion. */
79
+ transition: opacity var(--duration-fast) ease;
81
80
  }
82
81
 
83
82
  .flyout.is-open:not(.is-sheet) {
@@ -87,7 +86,6 @@
87
86
  @starting-style {
88
87
  .flyout.is-open {
89
88
  opacity: 0;
90
- scale: 0.98;
91
89
  }
92
90
  }
93
91
 
@@ -109,22 +107,24 @@
109
107
  max(var(--space-30), env(safe-area-inset-bottom));
110
108
  border-end-start-radius: 0;
111
109
  border-end-end-radius: 0;
110
+ /* Sheet motion is a directional slide from the viewport edge; it replaces
111
+ the base fade-only recipe. */
112
+ transition:
113
+ opacity var(--duration-fast) ease,
114
+ translate var(--duration-fast) ease;
112
115
  }
113
116
 
114
117
  .flyout[hidden] {
115
118
  display: none;
116
119
  opacity: 0;
117
- scale: 0.98;
118
120
  }
119
121
 
120
122
  .flyout.is-sheet[hidden] {
121
- scale: 1;
122
123
  translate: 0 var(--space-30);
123
124
  }
124
125
 
125
126
  @starting-style {
126
127
  .flyout.is-sheet.is-open {
127
- scale: 1;
128
128
  translate: 0 var(--space-30);
129
129
  }
130
130
  }
@@ -132,9 +132,14 @@
132
132
  @supports (transition-behavior: allow-discrete) {
133
133
  .flyout {
134
134
  transition:
135
- opacity var(--duration-fast) var(--ease),
136
- scale var(--duration-fast) var(--ease),
137
- translate var(--duration-fast) var(--ease),
135
+ opacity var(--duration-fast) ease,
136
+ display var(--duration-fast) allow-discrete;
137
+ }
138
+
139
+ .flyout.is-sheet {
140
+ transition:
141
+ opacity var(--duration-fast) ease,
142
+ translate var(--duration-fast) ease,
138
143
  display var(--duration-fast) allow-discrete;
139
144
  }
140
145
  }
@@ -43,6 +43,7 @@
43
43
  @import "./scrollspy.css";
44
44
 
45
45
  /* Data */
46
+ @import "./data-list.css";
46
47
  @import "./list.css";
47
48
  @import "./table.css";
48
49
  @import "./meter.css";
@@ -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);
@@ -1,8 +1,16 @@
1
1
  /*
2
- * Meter — native gauge for known-range values.
2
+ * Meter — native and segmented gauges for known-range values.
3
3
  *
4
4
  * Maps the three standard meter zones (optimum / suboptimum / even-less-good)
5
5
  * to the shared success / warning / danger intent colors.
6
+ *
7
+ * Thickness is a dimension, not a density step: the bar always spans its
8
+ * container and only gets thinner or thicker, so --bar-height is the API and
9
+ * .sm/.lg are deliberately not supported.
10
+ *
11
+ * Public hooks:
12
+ * --meter-value
13
+ * --bar-height
6
14
  */
7
15
 
8
16
  .meter {
@@ -17,6 +25,25 @@
17
25
  background: var(--surface-subtle);
18
26
  }
19
27
 
28
+ /* A non-native meter is a weighted flex container. The element type keeps the
29
+ native <meter> API and the composed segmented API distinct without another
30
+ public variant class. */
31
+ .meter:not(meter) {
32
+ --intent: initial;
33
+
34
+ display: flex;
35
+ }
36
+
37
+ .meter:not(meter) > * {
38
+ flex: var(--meter-value, 1) 1 0;
39
+ min-inline-size: 0;
40
+ background: var(--intent);
41
+ }
42
+
43
+ .meter:not(meter) > * + * {
44
+ border-inline-start: var(--border-width) solid var(--surface);
45
+ }
46
+
20
47
  .meter::-webkit-meter-bar {
21
48
  -webkit-appearance: none;
22
49
  block-size: var(--bar-height);
@@ -39,12 +39,14 @@ dialog.modal {
39
39
  * Keep the open dialog root untransformed. Fixed overlays mounted inside a
40
40
  * modal dialog use viewport coordinates; transform/translate/scale on this
41
41
  * element would make them position against the dialog instead. The closed
42
- * state can still carry a small transform for enter/exit motion.
42
+ * state can still carry a small transform for enter/exit motion. The lift
43
+ * is a pure vertical offset: scaling a wide panel around its center drags
44
+ * both edges, which reads as horizontal movement.
43
45
  */
44
- transform: translateY(0.35rem) scale(0.98);
46
+ transform: translateY(0.35rem);
45
47
  transition:
46
- opacity var(--duration),
47
- transform var(--duration);
48
+ opacity var(--duration) var(--ease-exit),
49
+ transform var(--duration) var(--ease-exit);
48
50
  }
49
51
 
50
52
  dialog.modal > form,
@@ -163,33 +165,46 @@ dialog.modal.dialog-confirmation > form > footer {
163
165
  dialog.modal[open] {
164
166
  opacity: 1;
165
167
  transform: none;
168
+ transition:
169
+ opacity var(--duration) var(--ease-enter),
170
+ transform var(--duration) var(--ease-enter);
166
171
  }
167
172
 
168
173
  @starting-style {
169
174
  dialog.modal[open] {
170
175
  opacity: 0;
171
- transform: translateY(0.35rem) scale(0.98);
176
+ transform: translateY(0.35rem);
172
177
  }
173
178
  }
174
179
 
175
180
  dialog.modal::backdrop {
176
181
  background: var(--backdrop-fill);
177
182
  opacity: 0;
178
- transition: opacity var(--duration);
183
+ /* Scrim curve stays neutral `ease`; only the panel carries the character
184
+ curves. */
185
+ transition: opacity var(--duration) ease;
179
186
  }
180
187
 
181
188
  @supports (transition-behavior: allow-discrete) {
182
189
  dialog.modal {
183
190
  transition:
184
- opacity var(--duration),
185
- transform var(--duration),
191
+ opacity var(--duration) var(--ease-exit),
192
+ transform var(--duration) var(--ease-exit),
193
+ overlay var(--duration) allow-discrete,
194
+ display var(--duration) allow-discrete;
195
+ }
196
+
197
+ dialog.modal[open] {
198
+ transition:
199
+ opacity var(--duration) var(--ease-enter),
200
+ transform var(--duration) var(--ease-enter),
186
201
  overlay var(--duration) allow-discrete,
187
202
  display var(--duration) allow-discrete;
188
203
  }
189
204
 
190
205
  dialog.modal::backdrop {
191
206
  transition:
192
- opacity var(--duration),
207
+ opacity var(--duration) ease,
193
208
  overlay var(--duration) allow-discrete,
194
209
  display var(--duration) allow-discrete;
195
210
  }
@@ -214,13 +229,15 @@ html.has-modal-open.had-scrollbar {
214
229
  }
215
230
 
216
231
  @media (prefers-reduced-motion: no-preference) {
232
+ /* Wrong-close feedback and view-transition transport are motion quirks, not
233
+ presence enter/exit, so they stay on the plain CSS default easing. */
217
234
  dialog.modal.is-static {
218
- animation: actual-dialog-static var(--duration-slow) var(--ease);
235
+ animation: actual-dialog-static var(--duration-slow) ease;
219
236
  }
220
237
 
221
238
  ::view-transition-group(actual-dialog) {
222
239
  animation-duration: var(--duration);
223
- animation-timing-function: var(--ease);
240
+ animation-timing-function: ease;
224
241
  }
225
242
  }
226
243
 
@@ -4,9 +4,14 @@
4
4
  * Consumes the shared --intent/--primary for the value bar and --bar-height
5
5
  * for thickness. Indeterminate state animates a shimmer using --duration-shimmer.
6
6
  *
7
+ * Thickness is a dimension, not a density step: the bar always spans its
8
+ * container and only gets thinner or thicker, so --bar-height is the API and
9
+ * .sm/.lg are deliberately not supported.
10
+ *
7
11
  * Public hooks:
8
12
  * --progress-track
9
13
  * --progress-value
14
+ * --bar-height
10
15
  */
11
16
  /* Intent boundary — inherited intents do not tint this component.
12
17
  Local intent classes still win because :where() has zero specificity. */