actual-css 0.7.0 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +217 -0
- package/README.md +13 -11
- package/components.json +668 -0
- package/dist/actual.css +166 -37
- package/dist/actual.full.css +1087 -643
- package/dist/actual.full.js +1 -1
- package/dist/actual.full.min.css +1 -1
- package/dist/actual.js +1 -1
- package/dist/actual.min.css +1 -1
- package/docs/design-notes/accessibility-decisions.md +33 -0
- package/docs/design-notes/browser-support.md +62 -30
- package/docs/design-notes/column-layout.md +3 -3
- package/docs/design-notes/css-anchor.md +75 -21
- package/docs/design-notes/enhancement-contract.md +14 -6
- package/docs/design-notes/grid.md +4 -4
- package/docs/design-notes/naming.md +6 -1
- package/docs/design-notes/platform-alignment.md +53 -7
- package/docs/design-notes/popover-manual-poc.md +105 -25
- package/docs/design-notes/surface-reparenting.md +56 -47
- package/docs/design-notes/tooltip-coordinate-space.md +132 -0
- package/docs/design-notes/widget-primitives.md +7 -4
- package/docs/pages/components/accordion.md +41 -2
- package/docs/pages/components/alert.md +85 -30
- package/docs/pages/components/app-navigation.md +6 -0
- package/docs/pages/components/avatar.md +6 -6
- package/docs/pages/components/badge.md +101 -16
- package/docs/pages/components/button.md +16 -8
- package/docs/pages/components/card.md +10 -10
- package/docs/pages/components/dialog.md +21 -6
- package/docs/pages/components/drawer.md +13 -0
- package/docs/pages/components/fab.md +4 -3
- package/docs/pages/components/list.md +42 -4
- package/docs/pages/components/navbar.md +17 -1
- package/docs/pages/components/pagination.md +2 -2
- package/docs/pages/components/skeleton.md +3 -1
- package/docs/pages/components/steps.md +19 -0
- package/docs/pages/components/table.md +3 -2
- package/docs/pages/enhancements/flyout.md +82 -38
- package/docs/pages/enhancements/overview.md +25 -0
- package/docs/pages/enhancements/status-bar.md +1 -1
- package/docs/pages/enhancements/tabs.md +45 -37
- package/docs/pages/enhancements/tooltip.md +36 -10
- package/docs/pages/examples/overview.md +38 -5
- package/docs/pages/forms/choice-cards.md +11 -6
- package/docs/pages/forms/combobox.md +1 -1
- package/docs/pages/forms/floating-field.md +4 -3
- package/docs/pages/forms/joined-controls.md +22 -0
- package/docs/pages/forms/otp.md +2 -1
- package/docs/pages/forms/overview.md +1 -1
- package/docs/pages/forms/states-and-sizes.md +16 -18
- package/docs/pages/forms/sticky-actions.md +2 -2
- package/docs/pages/forms/switch.md +1 -1
- package/docs/pages/foundations/philosophy.md +57 -26
- package/docs/pages/foundations/theming.md +2 -2
- package/docs/pages/foundations/tokens.md +118 -21
- package/docs/pages/foundations/typography.md +35 -14
- package/docs/pages/guides/introduction.md +11 -9
- package/docs/pages/guides/progressive-enhancement.md +115 -30
- package/docs/pages/guides/tailwind.md +3 -2
- package/docs/pages/layout/media.md +1 -1
- package/docs/pages/layout/overview.md +11 -1
- package/docs/pages/layout/scroll-snap.md +3 -3
- package/docs/pages/layout/stack.md +17 -9
- package/docs/pages/patterns/icon-input.md +4 -2
- package/docs/pages/patterns/icon-slot.md +2 -0
- package/docs/pages/patterns/intent-nesting.md +1 -1
- package/docs/pages/utilities/overview.md +23 -2
- package/docs/pages/utilities/sizing-wrapping.md +1 -1
- package/docs/pages/utilities/spacing.md +10 -8
- package/llms.txt +2 -1
- package/package.json +159 -149
- package/{scripts/reserved-classes.json → reserved-classes.json} +4 -6
- package/src/css/components/accordion.css +42 -3
- package/src/css/components/alert.css +25 -13
- package/src/css/components/app-nav.css +18 -8
- package/src/css/components/avatar.css +13 -1
- package/src/css/components/badge.css +78 -12
- package/src/css/components/breadcrumb.css +2 -3
- package/src/css/components/button.css +9 -16
- package/src/css/components/card.css +9 -2
- package/src/css/components/chat.css +1 -0
- package/src/css/components/data-list.css +1 -1
- package/src/css/components/drawer.css +101 -32
- package/src/css/components/fab.css +0 -6
- package/src/css/components/flyout.css +35 -98
- package/src/css/components/index.css +0 -1
- package/src/css/components/indicator.css +21 -0
- package/src/css/components/key.css +10 -0
- package/src/css/components/list.css +40 -4
- package/src/css/components/menu.css +2 -1
- package/src/css/components/modal.css +48 -33
- package/src/css/components/navbar.css +37 -4
- package/src/css/components/pagination.css +1 -0
- package/src/css/components/skeleton.css +15 -4
- package/src/css/components/spinner.css +6 -6
- package/src/css/components/status-bar.css +0 -5
- package/src/css/components/steps.css +45 -59
- package/src/css/components/tab.css +29 -9
- package/src/css/components/table.css +13 -5
- package/src/css/components/tooltip.css +36 -40
- package/src/css/core/base.css +23 -1
- package/src/css/core/intents.css +17 -6
- package/src/css/core/theme.css +27 -0
- package/src/css/core/tokens.css +35 -15
- package/src/css/core/variants.css +64 -15
- package/src/css/forms/choice-card.css +4 -4
- package/src/css/forms/choice.css +76 -29
- package/src/css/forms/control.css +2 -1
- package/src/css/forms/floating-field.css +3 -3
- package/src/css/forms/form.css +4 -3
- package/src/css/forms/input-icon.css +20 -14
- package/src/css/forms/native.css +6 -1
- package/src/css/forms/otp.css +9 -1
- package/src/css/forms/range.css +2 -0
- package/src/css/forms/select.css +29 -9
- package/src/css/forms/switch.css +28 -8
- package/src/css/layout/app-layout.css +1 -1
- package/src/css/layout/grid.css +50 -76
- package/src/css/layout/scroll-snap.css +11 -7
- package/src/css/layout/scroller.css +3 -4
- package/src/css/layout/stack.css +2 -2
- package/src/css/typography/fluid.css +4 -3
- package/src/css/typography/prose.css +6 -5
- package/src/css/utilities/base.css +2 -1
- package/src/css/utilities/extra.css +20 -17
- package/src/js/context-menu.js +27 -26
- package/src/js/dialog.js +2 -1
- package/src/js/dismiss.js +5 -0
- package/src/js/enhance.js +34 -2
- package/src/js/enhancement-loader.js +36 -43
- package/src/js/events.js +0 -3
- package/src/js/filter.js +0 -1
- package/src/js/floating-space.js +42 -0
- package/src/js/flyout.js +9 -44
- package/src/js/focus-group.js +7 -0
- package/src/js/focus.js +6 -0
- package/src/js/menu.js +10 -1
- package/src/js/selectors.js +5 -9
- package/src/js/status.js +6 -5
- package/src/js/surface.js +97 -139
- package/src/js/tab.js +6 -1
- package/src/js/tooltip.js +150 -51
- package/src/js/validation.js +1 -1
- package/src/css/components/surface.css +0 -49
|
@@ -7,6 +7,9 @@
|
|
|
7
7
|
- `.navbar-nav` is the horizontal link list; its items are `.nav-link`.
|
|
8
8
|
- `.nav-list` is the shared vertical navigation list, reused by `.drawer` for stacked links.
|
|
9
9
|
- Mark the current page with `aria-current="page"` on the active `.nav-link`.
|
|
10
|
+
- Presence is the contract: set `aria-current` on the current link and remove
|
|
11
|
+
it elsewhere, never `aria-current="false"`. Scrollspy follows the same rule
|
|
12
|
+
with `aria-current="location"`.
|
|
10
13
|
|
|
11
14
|
For a public or normally scrolling page, `.navbar` belongs inside the semantic
|
|
12
15
|
site header. Do not use `.topbar` or `.app-layout` unless the page actually has
|
|
@@ -16,6 +19,13 @@ the specialized persistent application-shell behavior those APIs describe.
|
|
|
16
19
|
flex list, `.nav-list` a vertical grid. Putting both on one element resolves to
|
|
17
20
|
`.nav-list` (vertical) — pick one per container.
|
|
18
21
|
|
|
22
|
+
`.navbar` stays the horizontal bar for a site or webapp header with a brand and
|
|
23
|
+
normal document scrolling. `.app-nav` is not merely a stacked-icon flavour of it:
|
|
24
|
+
reach for that component only when the destinations are part of a persistent
|
|
25
|
+
application shell — bottom navigation or labelled side navigation via
|
|
26
|
+
`.app-layout`. The choice follows the shell contract, not a preference for
|
|
27
|
+
icons. See [App Navigation](app-navigation.md).
|
|
28
|
+
|
|
19
29
|
## Class reference
|
|
20
30
|
|
|
21
31
|
| Class | Kind | Description |
|
|
@@ -41,7 +51,9 @@ flex list, `.nav-list` a vertical grid. Putting both on one element resolves to
|
|
|
41
51
|
|
|
42
52
|
For a vertical sidebar nav, use `.nav-list` inside the drawer or sidebar.
|
|
43
53
|
Links inside `.nav-list` stretch to the available inline size, providing a
|
|
44
|
-
full-row hit target for vertical navigation.
|
|
54
|
+
full-row hit target for vertical navigation. The [Workspaces example](../examples/overview.md)
|
|
55
|
+
shows the sectioned-sidebar composition: grouped `.nav-list` blocks with
|
|
56
|
+
section titles, a pinned account footer, and the native current state.
|
|
45
57
|
|
|
46
58
|
## Responsive: navbar + drawer
|
|
47
59
|
|
|
@@ -51,6 +63,10 @@ a `.drawer` with a vertical `.nav-list`, opened by a `command="show-modal"` /
|
|
|
51
63
|
`commandfor` trigger. Keep that trigger outside the `<nav>` subtree you hide at
|
|
52
64
|
narrow widths, or hiding the nav hides the control that opens it.
|
|
53
65
|
|
|
66
|
+
When the compact layout drops labels, keep that text in the DOM and hide it
|
|
67
|
+
visually instead of replacing it with `aria-label` strings: one source for the
|
|
68
|
+
accessible name.
|
|
69
|
+
|
|
54
70
|
```html demo
|
|
55
71
|
<nav class="navbar" aria-label="Main">
|
|
56
72
|
<a class="navbar-brand" href="/">Actual CSS</a>
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
- Use a semantic `<nav>` landmark.
|
|
6
6
|
- Put `.pagination` on the ordered list.
|
|
7
7
|
- Use `aria-current="page"` for the current page.
|
|
8
|
-
- Put `.sm` or `.lg` on `.pagination` to
|
|
8
|
+
- Put `.sm` or `.lg` on `.pagination` to scale its controls.
|
|
9
9
|
- Numeric links may use `aria-label="Page N"` for clearer screen reader output.
|
|
10
10
|
- Prefer text labels for Previous and Next. Icon-only controls need an accessible name.
|
|
11
11
|
- Page links can compose with `.btn` for button-like hit targets.
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
| Class | Kind | Description |
|
|
18
18
|
| ------------- | --------- | ------------------------------ |
|
|
19
19
|
| `.pagination` | Component | Ordered list of page controls. |
|
|
20
|
-
| `.sm` / `.lg` | Size | Control
|
|
20
|
+
| `.sm` / `.lg` | Size | Control geometry and type. |
|
|
21
21
|
|
|
22
22
|
## Basic usage
|
|
23
23
|
|
|
@@ -37,4 +37,6 @@
|
|
|
37
37
|
- `--skeleton-highlight` — sweeping highlight color.
|
|
38
38
|
- `--skeleton-radius` — corner radius; `data-shape` presets override it.
|
|
39
39
|
- `--skeleton-size` — block size; `data-shape` presets override it.
|
|
40
|
-
- `--skeleton-width` — inline size
|
|
40
|
+
- `--skeleton-width` — maximum inline size. A placeholder fills its container
|
|
41
|
+
and stops here, so it never gives an ancestor a width floor. `data-shape`
|
|
42
|
+
presets set it; `avatar` ignores it and stays square.
|
|
@@ -65,6 +65,25 @@ smaller than body text, and neutral in weight. Only the current step's label is
|
|
|
65
65
|
emphasised — it is where you are now. A completed step needs no bold text; its
|
|
66
66
|
filled marker already says as much.
|
|
67
67
|
|
|
68
|
+
## Theming the states
|
|
69
|
+
|
|
70
|
+
The complete and current states consume the shared
|
|
71
|
+
`--state-selected` / `--state-selected-fg` tokens, which default to
|
|
72
|
+
`--primary` / `--primary-fg`. A `--primary` recolor therefore flows through
|
|
73
|
+
without any stepper-specific rule. To theme only this sequence with a local
|
|
74
|
+
accent, override the tokens on the component:
|
|
75
|
+
|
|
76
|
+
```css
|
|
77
|
+
.checkout-steps {
|
|
78
|
+
--state-selected: var(--success);
|
|
79
|
+
--state-selected-fg: var(--success-fg);
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
`--state-selected` drives the completed fill, connector and the current ring;
|
|
84
|
+
`--state-selected-fg` corrects the number inside a completed marker when the
|
|
85
|
+
accent needs a lighter ink.
|
|
86
|
+
|
|
68
87
|
## Horizontal steps
|
|
69
88
|
|
|
70
89
|
`.steps-horizontal` is designed for **2 to 5 stages**.
|
|
@@ -100,8 +100,9 @@ table width before the wrapper scrolls horizontally.
|
|
|
100
100
|
|
|
101
101
|
## Compact tables
|
|
102
102
|
|
|
103
|
-
`.compact` lowers
|
|
104
|
-
|
|
103
|
+
`.compact` lowers cell padding and establishes compact density for controls or
|
|
104
|
+
spacing helpers inside cells. Typography and icons stay unchanged. It is the
|
|
105
|
+
same vocabulary as `.card.compact`.
|
|
105
106
|
|
|
106
107
|
```html demo
|
|
107
108
|
<div class="table-wrap">
|
|
@@ -19,8 +19,8 @@
|
|
|
19
19
|
|
|
20
20
|
`.menu` has a strict `.menu > li > .menu-item` anatomy.
|
|
21
21
|
| `.menu-label` | Variant | Muted section heading inside a menu; not interactive. |
|
|
22
|
-
| `.menu-separator` | Variant | Divider between menu groups (`<hr
|
|
23
|
-
| `.sm` / `.lg` |
|
|
22
|
+
| `.menu-separator` | Variant | Divider between menu groups (`<hr>`). |
|
|
23
|
+
| `.sm` / `.lg` | Size | Smaller or larger menu rows and type. |
|
|
24
24
|
|
|
25
25
|
## Usage
|
|
26
26
|
|
|
@@ -28,7 +28,12 @@ Flyout and context menu share one action-surface runtime:
|
|
|
28
28
|
|
|
29
29
|
- Flyout = a visible trigger opens a surface.
|
|
30
30
|
- Context menu = right click or a keyboard context action opens that same surface.
|
|
31
|
-
-
|
|
31
|
+
- Flyouts and context menus remain anchored, non-modal popovers at every viewport size.
|
|
32
|
+
|
|
33
|
+
One surface is open at a time. Opening any of them closes the others, so a
|
|
34
|
+
trigger placed inside an open panel replaces that panel instead of opening a
|
|
35
|
+
second level. Submenus and other nested panels are outside the current
|
|
36
|
+
contract.
|
|
32
37
|
|
|
33
38
|
### Trigger wiring
|
|
34
39
|
|
|
@@ -44,7 +49,8 @@ contract:
|
|
|
44
49
|
context for the no-JS fallback: without it the panel lays out against the
|
|
45
50
|
nearest positioned ancestor (or the page edge) instead of under the trigger.
|
|
46
51
|
- Start the panel with `hidden` so it does not flash open before the runtime
|
|
47
|
-
|
|
52
|
+
takes it over. The runtime then removes the attribute: a closed panel is
|
|
53
|
+
hidden by the popover transport, and `[hidden]` would outrank it.
|
|
48
54
|
|
|
49
55
|
```html
|
|
50
56
|
<div class="flyout-trigger">
|
|
@@ -57,16 +63,9 @@ contract:
|
|
|
57
63
|
</div>
|
|
58
64
|
```
|
|
59
65
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
- `sheet` always uses the sheet presentation.
|
|
64
|
-
- `anchored` always uses floating positioning.
|
|
65
|
-
- `none` disables the mobile transformation.
|
|
66
|
-
|
|
67
|
-
`data-flyout-breakpoint` only matters for `auto`. Prefer the built-in tokens
|
|
68
|
-
(`sm`, `md`, `lg`) and use raw pixel values only as an escape hatch, for example
|
|
69
|
-
`data-flyout-breakpoint="640"`.
|
|
66
|
+
For a modal bottom sheet or action sheet, use `dialog.drawer`; use
|
|
67
|
+
`dialog.modal` for a centered modal. Those components own modal focus,
|
|
68
|
+
dismissal, scroll locking, and backdrop behavior explicitly.
|
|
70
69
|
|
|
71
70
|
Use `data-flyout-placement` for the preferred anchored placement. It accepts the
|
|
72
71
|
placement strings supported by the floating runtime, such as `bottom-start`,
|
|
@@ -83,20 +82,21 @@ another library may therefore own opening, dismissal, and focus without
|
|
|
83
82
|
requiring `.is-open` or `[hidden]`.
|
|
84
83
|
|
|
85
84
|
Actual CSS does not position that independently managed popover. Its positioner
|
|
86
|
-
must
|
|
87
|
-
|
|
85
|
+
must couple fixed positioning with viewport coordinates or absolute positioning
|
|
86
|
+
with document coordinates (and may write `--available-height` and
|
|
87
|
+
`--surface-anchor-width`). The built-in `surface.js` runtime remains the full
|
|
88
88
|
fallback when Actual owns the lifecycle too. A bare `[popover]` without
|
|
89
89
|
`.flyout` or `.tooltip` keeps its platform appearance.
|
|
90
90
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
or promotes a `popover` element, so nothing dismisses the panel either.
|
|
91
|
+
The Popover lifecycle is guaranteed across Minimal, so choosing it is a
|
|
92
|
+
behavior decision rather than a support one: native light dismiss closes on
|
|
93
|
+
outside clicks only, which cannot express `data-flyout-auto-close`. The two
|
|
94
|
+
lifecycles are alternatives, not layers.
|
|
96
95
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
96
|
+
`surface.js` itself uses `popover="manual"` as its transport — the panel is
|
|
97
|
+
promoted to the top layer where it stands, so a scoped theme, density, or
|
|
98
|
+
custom property still reaches it. That transport supplies no behavior, so the
|
|
99
|
+
runtime keeps owning dismissal, Escape ordering, focus and placement.
|
|
100
100
|
|
|
101
101
|
#### Choose one lifecycle owner
|
|
102
102
|
|
|
@@ -125,10 +125,12 @@ The native path, where the browser owns the lifecycle and the top layer:
|
|
|
125
125
|
</ul>
|
|
126
126
|
```
|
|
127
127
|
|
|
128
|
-
Do not combine them. `surface.js`
|
|
129
|
-
|
|
130
|
-
to the runtime has
|
|
131
|
-
|
|
128
|
+
Do not combine them. `surface.js` writes `popover="manual"` on every panel it
|
|
129
|
+
manages and drives `showPopover()` / `hidePopover()` itself, so a
|
|
130
|
+
`popover="auto"` panel handed to the runtime has its mode overwritten — the
|
|
131
|
+
runtime keeps one lifecycle owner rather than letting native light dismiss
|
|
132
|
+
compete with `data-flyout-auto-close`. Keep `popovertarget` for panels the
|
|
133
|
+
runtime does not enhance.
|
|
132
134
|
|
|
133
135
|
**The native path still needs a positioner.** `popovertarget` gives the panel an
|
|
134
136
|
implicit anchor reference to its invoker, but it does not place it: Actual's
|
|
@@ -198,9 +200,9 @@ A list of *actions* the user can take: sign out, copy, delete.
|
|
|
198
200
|
- Flyout triggers are opt-in: add `data-enhance="flyout"` to the trigger.
|
|
199
201
|
- Wrap the trigger and flyout in `.flyout-trigger` when the flyout should have a local absolute-position fallback before JavaScript positions it. Add `.stretch` when the trigger must span its container, such as the last row of a full-width sidebar nav list.
|
|
200
202
|
- Use `<menu class="flyout menu">` with strict anatomy: `.menu > li > .menu-item`. Items must carry the `.menu-item` class to participate in directional keyboard navigation. ArrowUp/Down and Home/End move focus without rewriting their normal tab stops.
|
|
201
|
-
- Use `.menu-label` on a `li` for a muted section heading inside a menu (e.g. a group title before its items). It is non-interactive and does not participate in roving focus. Use `.menu-separator` (`<hr
|
|
203
|
+
- Use `.menu-label` on a `li` for a muted section heading inside a menu (e.g. a group title before its items). It is non-interactive and does not participate in roving focus. Use `.menu-separator` (a plain `<hr>`) between groups.
|
|
202
204
|
- Items are regular `<button>` or `<a>` elements.
|
|
203
|
-
- Use `.sm` or `.lg`
|
|
205
|
+
- Use `.sm` or `.lg` to scale one flyout; use a density context for surrounding UI.
|
|
204
206
|
- Add `role="menu"` / `role="menuitem"` only when you intentionally need the ARIA menu pattern described below.
|
|
205
207
|
|
|
206
208
|
```html demo
|
|
@@ -221,7 +223,7 @@ A list of *actions* the user can take: sign out, copy, delete.
|
|
|
221
223
|
hidden>
|
|
222
224
|
<li><button class="menu-item" type="button">Profile</button></li>
|
|
223
225
|
<li><button class="menu-item" type="button">Settings</button></li>
|
|
224
|
-
<hr class="menu-separator"
|
|
226
|
+
<hr class="menu-separator">
|
|
225
227
|
<li><button class="menu-item danger" type="button">Sign out</button></li>
|
|
226
228
|
</menu>
|
|
227
229
|
</div>
|
|
@@ -239,6 +241,36 @@ tab stop instead.
|
|
|
239
241
|
The runtime owns only focus movement and `tabindex`. The application still owns
|
|
240
242
|
command state and updates attributes such as `aria-checked`.
|
|
241
243
|
|
|
244
|
+
### Linting `role="menu"`
|
|
245
|
+
|
|
246
|
+
Biome's `a11y/noNoninteractiveElementToInteractiveRole` reports
|
|
247
|
+
`<menu role="menu">`, and the fix it offers — remove the role — dismantles the
|
|
248
|
+
pattern. It is a port of
|
|
249
|
+
`jsx-a11y/no-noninteractive-element-to-interactive-role` that lost the source
|
|
250
|
+
rule's default allowlist, and that allowlist exists to permit exactly this
|
|
251
|
+
markup: the source documents `<ul role="menu">` as *valid*, matching the
|
|
252
|
+
WAI-ARIA APG menu button pattern. Every ARIA composite built on a list is
|
|
253
|
+
affected, not only menus — `listbox`, `tablist`, `menubar`, `tree`, `grid`.
|
|
254
|
+
|
|
255
|
+
Until it is fixed upstream, turn the rule off:
|
|
256
|
+
|
|
257
|
+
```json
|
|
258
|
+
{
|
|
259
|
+
"linter": {
|
|
260
|
+
"rules": {
|
|
261
|
+
"a11y": { "noNoninteractiveElementToInteractiveRole": "off" }
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
One line, and no `biome-ignore` comment above every menu in your markup. The
|
|
268
|
+
rest of the `a11y` group stays on and is worth keeping — with just this rule
|
|
269
|
+
off, the menu anatomy above lints clean.
|
|
270
|
+
|
|
271
|
+
[Accessibility decisions](https://github.com/lekoala/actual-css/blob/master/docs/design-notes/accessibility-decisions.md)
|
|
272
|
+
records which patterns Biome rejects, and which of its findings were right.
|
|
273
|
+
|
|
242
274
|
### Rich items and selection state
|
|
243
275
|
|
|
244
276
|
Wrap rich item content in the three optional slots when a menu needs aligned
|
|
@@ -304,7 +336,6 @@ can be multi-column with `<section>` / `<ul>` groups.
|
|
|
304
336
|
<div class="flyout"
|
|
305
337
|
id="products-panel"
|
|
306
338
|
aria-label="Products"
|
|
307
|
-
data-flyout-mobile="auto"
|
|
308
339
|
hidden>
|
|
309
340
|
<section aria-labelledby="products-design">
|
|
310
341
|
<h3 id="products-design">Design</h3>
|
|
@@ -337,8 +368,8 @@ can be multi-column with `<section>` / `<ul>` groups.
|
|
|
337
368
|
## Mega menu
|
|
338
369
|
|
|
339
370
|
Use `class="flyout grid-3"` when a nav panel needs multiple link groups. Keep
|
|
340
|
-
links regular anchors
|
|
341
|
-
|
|
371
|
+
links as regular anchors. The panel remains an anchored popover on mobile; use
|
|
372
|
+
a separate `dialog.drawer` when the content needs a modal mobile presentation.
|
|
342
373
|
|
|
343
374
|
```html demo
|
|
344
375
|
<nav aria-label="Product navigation">
|
|
@@ -356,7 +387,6 @@ with `data-flyout-mobile="auto"`.
|
|
|
356
387
|
<div class="flyout grid-3"
|
|
357
388
|
id="product-mega-menu"
|
|
358
389
|
aria-label="Platform"
|
|
359
|
-
data-flyout-mobile="auto"
|
|
360
390
|
style="--flyout-inline-size: 42rem; --flyout-max-inline-size: 42rem"
|
|
361
391
|
hidden>
|
|
362
392
|
<section aria-labelledby="mega-design">
|
|
@@ -457,7 +487,7 @@ delay; a number sets the delay in milliseconds.
|
|
|
457
487
|
hidden>
|
|
458
488
|
<li><button class="menu-item" type="button">Open</button></li>
|
|
459
489
|
<li><button class="menu-item" type="button">Rename</button></li>
|
|
460
|
-
<hr class="menu-separator"
|
|
490
|
+
<hr class="menu-separator">
|
|
461
491
|
<li><button class="menu-item danger" type="button">Delete</button></li>
|
|
462
492
|
</menu>
|
|
463
493
|
</div>
|
|
@@ -476,8 +506,22 @@ delay; a number sets the delay in milliseconds.
|
|
|
476
506
|
|
|
477
507
|
## CSS hooks
|
|
478
508
|
|
|
479
|
-
- `--flyout-inline-size` —
|
|
480
|
-
|
|
509
|
+
- `--flyout-inline-size` — preferred width. Takes intrinsic values such as
|
|
510
|
+
`max-content`, its default.
|
|
511
|
+
- `--flyout-max-inline-size` — width cap, a `<length-percentage>` the runtime
|
|
512
|
+
also clamps to the viewport. Defaults to `20rem`, independently of the
|
|
513
|
+
preferred width — the same way `width: 42rem; max-width: 20rem` renders at
|
|
514
|
+
20rem. A panel wider than the cap therefore sets both:
|
|
515
|
+
|
|
516
|
+
```css
|
|
517
|
+
.mega-panel {
|
|
518
|
+
--flyout-inline-size: 42rem;
|
|
519
|
+
--flyout-max-inline-size: 42rem;
|
|
520
|
+
}
|
|
521
|
+
```
|
|
522
|
+
|
|
523
|
+
The repetition is the point: the two hooks are different contracts, and only
|
|
524
|
+
the cap may enter the runtime's `min()`.
|
|
481
525
|
- `--menu-item-size` — minimum row height of `.menu-item`.
|
|
482
526
|
- `--menu-item-icon-size` — shared leading-column width for icons and checked-state indicators.
|
|
483
527
|
|
|
@@ -13,6 +13,31 @@ class API, shared tokens, and progressive enhancement.
|
|
|
13
13
|
- Use shared button classes and variants for triggers.
|
|
14
14
|
- Do not add toasts. Use alerts, status regions, dialogs, or inline validation instead.
|
|
15
15
|
|
|
16
|
+
## Popover ownership
|
|
17
|
+
|
|
18
|
+
Actual drives the top layer itself. On every surface and tooltip its runtime
|
|
19
|
+
manages, `surface.js` and `tooltip.js` own the `popover` attribute and both
|
|
20
|
+
`showPopover()` and `hidePopover()`.
|
|
21
|
+
|
|
22
|
+
On a managed element, an application must not:
|
|
23
|
+
|
|
24
|
+
- set or change `popover`. The runtime writes `manual` over any other value, because `popover="auto"` hands the browser a dismissal policy of its own that competes with `data-flyout-auto-close`.
|
|
25
|
+
- call `showPopover()`, `hidePopover()`, or `togglePopover()`.
|
|
26
|
+
- cancel its `beforetoggle`. Preventing an open leaves Actual's state saying open while the element is not in the top layer, and nothing repairs that — Actual owns the lifecycle rather than mediating it.
|
|
27
|
+
|
|
28
|
+
To stop a surface from opening, cancel Actual's own `actual:surface-open`
|
|
29
|
+
instead. It is dispatched before anything is promoted, so cancelling it leaves
|
|
30
|
+
no state behind.
|
|
31
|
+
|
|
32
|
+
**What counts as managed is the runtime's decision, not the attribute's.** A
|
|
33
|
+
panel is managed when a `data-enhance="flyout"` trigger points at it through
|
|
34
|
+
`aria-controls`, or when a `data-context-menu` target names it; a tooltip is
|
|
35
|
+
managed when a `data-tooltip` trigger points at it. A `.flyout[popover]` or
|
|
36
|
+
`.tooltip[popover]` that no such trigger reaches is never touched — Actual
|
|
37
|
+
supplies its presentation and the application owns its lifecycle. That is the
|
|
38
|
+
supported way to drive either class with `popovertarget` or a third-party
|
|
39
|
+
lifecycle. Do not do both to the same element.
|
|
40
|
+
|
|
16
41
|
## Modules
|
|
17
42
|
|
|
18
43
|
The full runtime is `actual-css/js/full`. Each enhancer is also importable on its own
|
|
@@ -15,7 +15,7 @@ runtime targets, empty by default.
|
|
|
15
15
|
- Use it for transient status (`Saved.`, `Reconnected.`, `Copied.`). Critical, persistent, or actionable information belongs in `.alert`, inline messages, or dialogs.
|
|
16
16
|
- Intents: `danger`, `success`, `warning`, `neutral`. The default (no intent) is a neutral dark pill.
|
|
17
17
|
- On mobile, the runtime keeps the bar above the software keyboard by tracking `visualViewport` while a message is showing.
|
|
18
|
-
- A message originating inside an open modal dialog temporarily moves the singleton bar into that dialog.
|
|
18
|
+
- A message originating inside an open modal dialog temporarily moves the singleton bar into that dialog. A modal inerts the rest of the document, and being painted above it would not undo that — inertness is computed on the DOM — so the bar has to be in the dialog's subtree to be announced and seen. The bar returns to its original DOM position after its exit.
|
|
19
19
|
|
|
20
20
|
Clearing is deferred, not immediate: the bar closes first and only empties its
|
|
21
21
|
text and intent once the exit transition has finished, so it leaves at the size
|
|
@@ -14,51 +14,59 @@
|
|
|
14
14
|
|
|
15
15
|
- Use real tab semantics when panels switch in place.
|
|
16
16
|
- Use normal links and `aria-current="page"` for page navigation that only looks like tabs.
|
|
17
|
+
- On that link variant, `aria-current="page"` on the current link gets the same
|
|
18
|
+
color and underline treatment as `aria-selected="true"` on a widget tab;
|
|
19
|
+
`primary` tints it.
|
|
20
|
+
- Tab selection never changes text metrics: the active tab keeps the shared
|
|
21
|
+
weight and is marked by color and the indicator line.
|
|
17
22
|
- JavaScript owns roving `tabindex`, `aria-selected`, `hidden`, and keyboard behavior.
|
|
18
23
|
- Left/Right select tabs and wrap at the ends. Home/End jump to first/last. Down moves focus into the selected panel.
|
|
19
24
|
- A tab list needs both `.tabs` and `role="tablist"`; `.tab` styles each trigger.
|
|
20
25
|
- A `.tab` with an icon uses `--tab-gap` (default `0.375em`) for the space between icon and label.
|
|
26
|
+
- A tab that is `hidden`, `disabled`, `aria-disabled`, or has no panel is skipped
|
|
27
|
+
by the arrow keys. An application filtering a tab strip only has to set
|
|
28
|
+
`hidden`; deciding what to show when the selected tab is filtered out is the
|
|
29
|
+
application's call.
|
|
21
30
|
|
|
22
31
|
```html demo
|
|
23
32
|
<div class="tabs" data-enhance="tabs" role="tablist" aria-label="Settings">
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
</div>
|
|
51
|
-
|
|
52
|
-
<section role="tabpanel" id="panel-general" aria-labelledby="tab-general" tabindex="-1" class="py">
|
|
53
|
-
General content
|
|
54
|
-
</section>
|
|
55
|
-
<section role="tabpanel" id="panel-security" aria-labelledby="tab-security" tabindex="-1" hidden class="py">
|
|
56
|
-
Security content
|
|
57
|
-
</section>
|
|
58
|
-
<section role="tabpanel" id="panel-billing" aria-labelledby="tab-billing" tabindex="-1" hidden class="py">
|
|
59
|
-
Billing content
|
|
60
|
-
</section>
|
|
33
|
+
<button class="tab primary"
|
|
34
|
+
type="button"
|
|
35
|
+
role="tab"
|
|
36
|
+
aria-selected="true"
|
|
37
|
+
aria-controls="panel-general"
|
|
38
|
+
id="tab-general">
|
|
39
|
+
General
|
|
40
|
+
</button>
|
|
41
|
+
<button class="tab"
|
|
42
|
+
type="button"
|
|
43
|
+
role="tab"
|
|
44
|
+
aria-selected="false"
|
|
45
|
+
aria-controls="panel-security"
|
|
46
|
+
id="tab-security"
|
|
47
|
+
tabindex="-1">
|
|
48
|
+
Security
|
|
49
|
+
</button>
|
|
50
|
+
<button class="tab"
|
|
51
|
+
type="button"
|
|
52
|
+
role="tab"
|
|
53
|
+
aria-selected="false"
|
|
54
|
+
aria-controls="panel-billing"
|
|
55
|
+
id="tab-billing"
|
|
56
|
+
tabindex="-1">
|
|
57
|
+
Billing
|
|
58
|
+
</button>
|
|
61
59
|
</div>
|
|
60
|
+
|
|
61
|
+
<section role="tabpanel" id="panel-general" aria-labelledby="tab-general" tabindex="-1" class="py">
|
|
62
|
+
General content
|
|
63
|
+
</section>
|
|
64
|
+
<section role="tabpanel" id="panel-security" aria-labelledby="tab-security" tabindex="-1" hidden class="py">
|
|
65
|
+
Security content
|
|
66
|
+
</section>
|
|
67
|
+
<section role="tabpanel" id="panel-billing" aria-labelledby="tab-billing" tabindex="-1" hidden class="py">
|
|
68
|
+
Billing content
|
|
69
|
+
</section>
|
|
62
70
|
```
|
|
63
71
|
|
|
64
72
|
For navigation that only looks like tabs, keep it a plain link list — no
|
|
@@ -5,11 +5,14 @@
|
|
|
5
5
|
Tooltips are supplemental: `data-tooltip` on the trigger wires a `.tooltip`
|
|
6
6
|
element (`role="tooltip"`) that appears near the trigger. Position, placement,
|
|
7
7
|
and arrow position are written by JavaScript; the element is shown and hidden
|
|
8
|
-
|
|
8
|
+
by promoting it to the top layer with `popover="manual"`.
|
|
9
9
|
|
|
10
10
|
- Use `data-tooltip` on the trigger. With text (`data-tooltip="Help"`), the tooltip element is generated. Empty (`data-tooltip`) marks an explicit tooltip connected via `aria-describedby`.
|
|
11
11
|
- Tooltips are supplemental. Do not put required information or interactive controls inside them.
|
|
12
|
-
- Show on hover and focus. Hide on Escape, blur, pointer leave
|
|
12
|
+
- Show on hover and focus. Hide on Escape, blur, or pointer leave.
|
|
13
|
+
- A trigger scrolled out of view takes its tooltip down and brings it back when it returns; only a dismissal ends the tooltip.
|
|
14
|
+
- Escape outlasts the hover and focus that justified the tooltip (WCAG 2.1 SC 1.4.13); showing it again takes a fresh hover or focus, not a second click on a trigger that already has focus.
|
|
15
|
+
- Author an explicit tooltip with `hidden`. The runtime removes it when it takes the lifecycle over; until then it is what keeps the tooltip off screen.
|
|
13
16
|
- JavaScript can generate tooltip elements from `data-tooltip`.
|
|
14
17
|
- Add `data-tooltip-click` to toggle it on click instead of hover/focus.
|
|
15
18
|
- Add `data-tooltip-visible` to show it immediately and keep it visible while its trigger is in view.
|
|
@@ -20,14 +23,28 @@ with the `hidden` attribute.
|
|
|
20
23
|
A `.tooltip[popover]` may use a native or third-party-owned popover lifecycle.
|
|
21
24
|
Actual removes the native inset, margin, and overflow that conflict with
|
|
22
25
|
positioning and with the projecting arrow, while leaving bare `[popover]`
|
|
23
|
-
elements untouched. The lifecycle owner
|
|
24
|
-
coordinates and arrow position; Actual only supplies the presentation.
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
26
|
+
elements untouched. The lifecycle owner still writes `position`, the
|
|
27
|
+
coordinates, and the arrow position; Actual only supplies the presentation.
|
|
28
|
+
|
|
29
|
+
Do not combine them. `tooltip.js` writes `popover="manual"` on every tooltip it
|
|
30
|
+
manages and calls `showPopover()` / `hidePopover()` itself, so a tooltip handed
|
|
31
|
+
to the runtime has its mode overwritten. The runtime only ever reaches a
|
|
32
|
+
tooltip that a `data-tooltip` trigger points at, so a `.tooltip[popover]` with
|
|
33
|
+
no such trigger stays yours to drive. Use `data-tooltip` and the built-in
|
|
34
|
+
runtime when Actual should own showing and hiding.
|
|
35
|
+
|
|
36
|
+
**Where the element lives is part of the contract.** An explicit tooltip stays
|
|
37
|
+
exactly where you wrote it, so it keeps every scope that reaches it by
|
|
38
|
+
inheritance — a theme island, a density scope, `.inverted`, your own scoped
|
|
39
|
+
custom properties. It also means placement is yours to get right: a tooltip
|
|
40
|
+
used from inside a modal dialog must be authored inside that dialog, because
|
|
41
|
+
the top layer does not lift an element out of a modal's inertness.
|
|
42
|
+
|
|
43
|
+
A generated tooltip (`data-tooltip="Help"`) is Actual's to place, and it goes
|
|
44
|
+
at document level rather than beside the trigger, so it cannot disturb the
|
|
45
|
+
trigger's structural position — a generated sibling would take
|
|
46
|
+
`.join > :last-child` from a trigger that ends a group. It therefore makes no
|
|
47
|
+
inheritance promise. Use the explicit form when the local CSS context matters.
|
|
31
48
|
|
|
32
49
|
```html demo
|
|
33
50
|
<button class="btn ghost"
|
|
@@ -98,6 +115,15 @@ viewport-aware maximum width.
|
|
|
98
115
|
Set `data-tooltip-placement` to control where the tooltip appears relative to
|
|
99
116
|
its trigger. The arrow follows the placement automatically.
|
|
100
117
|
|
|
118
|
+
The runtime also picks the coordinate space, and writes `position` to match. A
|
|
119
|
+
tooltip whose trigger scrolls with the page is placed in document coordinates
|
|
120
|
+
and `position: absolute`, so the browser scrolls it with the page instead of
|
|
121
|
+
the positioner correcting it a frame later — the lag visible while scrolling on
|
|
122
|
+
a touch device. A trigger that is viewport-anchored — inside a fixed or sticky
|
|
123
|
+
ancestor, an open popover, or a modal dialog — keeps `position: fixed`. Do not
|
|
124
|
+
override `position` on a tooltip the runtime manages. See
|
|
125
|
+
[Tooltip coordinate space](https://github.com/lekoala/actual-css/blob/master/docs/design-notes/tooltip-coordinate-space.md).
|
|
126
|
+
|
|
101
127
|
```html demo
|
|
102
128
|
<p>
|
|
103
129
|
<button class="btn outline" type="button" data-tooltip="Above the button" data-tooltip-placement="top">Top</button>
|
|
@@ -9,19 +9,52 @@ and `actual.full.js` from `dist/`, plus the theme palettes bundle from
|
|
|
9
9
|
|
|
10
10
|
## Templates
|
|
11
11
|
|
|
12
|
-
`demo/templates/` are standalone pages
|
|
12
|
+
`demo/templates/` are standalone pages. The first three explain the system; the
|
|
13
|
+
rest show it composed, isolated, or under stress.
|
|
14
|
+
|
|
15
|
+
### Start here
|
|
16
|
+
|
|
17
|
+
- [Visual guide](../../demo/templates/visual-guide.html) - seventeen illustrated figures on the spacing scale, the layout primitives, and density versus local size
|
|
18
|
+
- [Color guide](../../demo/templates/color-guide.html) - theming principles measured live on `ocean` and `sunset`, in light and dark, as `data-theme` islands
|
|
19
|
+
- [Kitchen sink](../../demo/templates/kitchen-sink.html) - one-screen test bed for themes, scopes, variants and interactions
|
|
20
|
+
|
|
21
|
+
### Page shapes
|
|
22
|
+
|
|
23
|
+
Whole pages, composed the way a real product would.
|
|
13
24
|
|
|
14
|
-
- [Kitchen sink](../../demo/templates/kitchen-sink.html) - every component and variant on one page
|
|
15
|
-
- [Keyboard controls](../../demo/templates/keyboard-controls.html) - hands-on keyboard support matrix and focus test bench
|
|
16
|
-
- [Surface contracts](../../demo/templates/surfaces.html) - .inverted, cascade precedence, and the intent boundary
|
|
17
25
|
- [Dashboard](../../demo/templates/dashboard.html) - data-heavy overview page
|
|
18
26
|
- [App](../../demo/templates/app.html) - application shell with sidebar
|
|
27
|
+
- [Workspaces](../../demo/templates/workspaces.html) - sectioned sidebar, master/detail list with native current state, and workspace cards
|
|
19
28
|
- [Marketing](../../demo/templates/marketing.html) - landing page with typography and fluid type
|
|
20
29
|
- [Blog](../../demo/templates/blog.html) - article page with prose
|
|
21
|
-
- [
|
|
30
|
+
- [Settings modal](../../demo/templates/settings-modal.html) - two-pane preferences dialog, with the local CSS it needs audited rule by rule
|
|
31
|
+
- [Workspace settings](../../demo/templates/settings-pricing.html) - plans with visible native choices, preferences and a local segmented tab treatment
|
|
32
|
+
- [Card compositions](../../demo/templates/card-compositions.html) - editorial, team, sign-in and horizontal cards composed from existing primitives
|
|
33
|
+
|
|
34
|
+
### Layout references
|
|
35
|
+
|
|
36
|
+
One mechanism at a time, with its contract and its pitfalls.
|
|
37
|
+
|
|
22
38
|
- [Layout reference](../../demo/templates/layouts.html) - canonical compositions built only with the layout primitives
|
|
39
|
+
- [Blocks](../../demo/templates/blocks.html) - layout building blocks
|
|
23
40
|
- [Grid density](../../demo/templates/grid.html) - the grid contracts on resizable stages, with the pitfalls called out
|
|
41
|
+
- [Column layout](../../demo/templates/column-layout.html) - the twelve-column canvas: spans, starts, and the narrow-container recomposition the framework leaves to the application
|
|
24
42
|
- [Density](../../demo/templates/density.html) - compact control density
|
|
43
|
+
|
|
44
|
+
### System benches
|
|
45
|
+
|
|
46
|
+
Contracts you cannot read off a static screenshot: state, motion, focus, and
|
|
47
|
+
extension points.
|
|
48
|
+
|
|
49
|
+
- [Surface contracts](../../demo/templates/surfaces.html) - `.inverted`, cascade precedence, and the intent boundary
|
|
50
|
+
- [Presence motion](../../demo/templates/motion.html) - status bar, tooltip, flyout, modal and drawer replayed side by side, with easing swaps and a reduced-motion simulation
|
|
51
|
+
- [Keyboard controls](../../demo/templates/keyboard-controls.html) - hands-on keyboard support matrix and focus test bench
|
|
52
|
+
- [Adaptive filter surface](../../demo/templates/adaptive-surface.html) - app-level flyout/drawer composition with one synchronized filter state
|
|
53
|
+
- [Select intents](../../demo/templates/select-intents.html) - extending the enhancer with a project's own `data-enhance` behavior, injected markup included, with no framework change
|
|
54
|
+
- [Popover transport probe](../../demo/templates/popover-transport.html) - self-scoring bench for the native popover transport go/no-go
|
|
55
|
+
|
|
56
|
+
### Third-party components on Actual tokens
|
|
57
|
+
|
|
25
58
|
- [Data grid](../../demo/templates/data-grid.html) - an external data grid component themed with Actual tokens (sort, filter, search, selection)
|
|
26
59
|
- [Combobox](../../demo/templates/combobox.html) - an external combobox themed with Actual tokens (search, tags, autocomplete)
|
|
27
60
|
|
|
@@ -88,7 +88,10 @@ Validation follows the shared pattern: `aria-invalid="true"` on the input (set m
|
|
|
88
88
|
</fieldset>
|
|
89
89
|
```
|
|
90
90
|
|
|
91
|
-
Sizes follow the shared `.sm` and `.lg`
|
|
91
|
+
Sizes follow the shared `.sm` and `.lg` typography scale; the check indicator
|
|
92
|
+
follows through its `em` sizing. Padding is density, so use a `.compact` or
|
|
93
|
+
`.spacious` context to change it. Intent classes (`.primary`, `.secondary`) are
|
|
94
|
+
supported on the `.choice-card` element.
|
|
92
95
|
|
|
93
96
|
## CSS hooks
|
|
94
97
|
|
|
@@ -100,8 +103,10 @@ Plain `.check` / `.radio` controls expose two hooks:
|
|
|
100
103
|
|
|
101
104
|
- `--choice-control-size` — the control's own size, in `em` off the surrounding
|
|
102
105
|
type. The switch track derives from it too, so a checkbox and a switch on the
|
|
103
|
-
same line stay proportionate. `.sm`
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
106
|
+
same line stay proportionate. A `.choice.sm` or `.choice.lg` scales it through
|
|
107
|
+
the surrounding typography.
|
|
108
|
+
- `--choice-control-nudge` — block correction added to the derived first-line
|
|
109
|
+
offset, for every control in the row. Left unset, each control centres itself
|
|
110
|
+
on the first line's cap height, measured from its own block size — so a
|
|
111
|
+
checkbox and a taller switch stay level with each other, and with the text,
|
|
112
|
+
whatever the font, line-height or control size.
|