actual-css 0.1.0 → 0.2.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 +53 -1
- package/README.md +2 -0
- package/dist/actual.css +356 -124
- package/dist/actual.js +2 -2
- package/dist/actual.js.map +13 -13
- package/dist/actual.min.css +1 -1
- package/docs/README.md +2 -0
- package/docs/components.md +2 -2
- package/docs/design-notes/browser-support.md +9 -0
- package/docs/design-notes/enhancement-contract.md +104 -0
- package/docs/design-notes/naming.md +1 -0
- package/docs/design-notes/widget-primitives.md +83 -0
- package/docs/forms.md +4 -4
- package/docs/javascript.md +104 -34
- package/docs/patterns.md +89 -0
- package/docs/ui.md +22 -30
- package/llms.txt +6 -0
- package/package.json +14 -8
- package/src/css/base.css +0 -4
- package/src/css/components/alert.css +88 -2
- package/src/css/components/badge.css +30 -7
- package/src/css/components/busy.css +1 -0
- package/src/css/components/button.css +6 -8
- package/src/css/components/flyout.css +0 -1
- package/src/css/components/join.css +1 -2
- package/src/css/components/scrollspy.css +2 -2
- package/src/css/components/spinner.css +4 -4
- package/src/css/components/status-bar.css +3 -4
- package/src/css/components/tab.css +1 -0
- package/src/css/focus.css +37 -0
- package/src/css/forms/choice-card.css +1 -2
- package/src/css/forms/choice.css +1 -2
- package/src/css/forms/control.css +1 -10
- package/src/css/forms/index.css +1 -0
- package/src/css/forms/native.css +8 -32
- package/src/css/forms/range.css +95 -0
- package/src/css/forms/switch.css +1 -2
- package/src/css/optional/scroller.css +6 -0
- package/src/css/theme.css +9 -9
- package/src/css/themes/edge.css +3 -27
- package/src/css/tokens.css +9 -3
- package/src/js/context-menu.js +21 -34
- package/src/js/enhance.js +26 -0
- package/src/js/flyout.js +12 -19
- package/src/js/menu.js +15 -14
- package/src/js/scrollspy.js +125 -62
- package/src/js/selectors.js +14 -24
- package/src/js/status.js +13 -13
- package/src/js/surface.js +57 -12
- package/src/js/tab.js +10 -13
- package/src/js/validation.js +11 -43
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# Widget primitives
|
|
2
|
+
|
|
3
|
+
The JS runtime doubles as a primitive kit for building custom widgets.
|
|
4
|
+
|
|
5
|
+
## Primitives
|
|
6
|
+
|
|
7
|
+
| Need | Primitive | Import |
|
|
8
|
+
|------|-----------|--------|
|
|
9
|
+
| Opt-in discovery + lifecycle + cleanup | `enhance`, `registerEnhancement` | `actual-css/js/enhance` |
|
|
10
|
+
| Positioning, flip/shift, `--available-height`, `data-placement` | `floating` (`track`, `reposition`, `repositionAt`) | `actual-css/js/floating` |
|
|
11
|
+
| Open/close, sheet on mobile, backdrop, outside-click, Escape, focus restore | `surface` | `actual-css/js/surface` |
|
|
12
|
+
| Roving focus over a list | `keys` (`firstItem`, `lastItem`, `nextItem`) | `actual-css/js/keys` |
|
|
13
|
+
| Menu-item vocabulary, usable-item filtering | `menu` | `actual-css/js/menu` |
|
|
14
|
+
| Focusable lookup, visibility | `focus` | `actual-css/js/focus` |
|
|
15
|
+
| Caret-safe value rewriting | `input` (`onTextInput`, `setCaret`, `dispatchInput`) | `actual-css/js/input` |
|
|
16
|
+
| Character policies | `filter` | `actual-css/js/filter` |
|
|
17
|
+
| Event namespace | `events` (`EVENTS`, `ACTUAL_EVENT_PREFIX`) | `actual-css/js/events` |
|
|
18
|
+
| Declarative triggers | `command` (`registerCommands`) | `actual-css/js/command` |
|
|
19
|
+
| State classes the runtime writes | `selectors` (`CLASSES`) | `actual-css/js/selectors` |
|
|
20
|
+
|
|
21
|
+
## Compositions
|
|
22
|
+
|
|
23
|
+
### Custom select — A. Owns its popup
|
|
24
|
+
|
|
25
|
+
```
|
|
26
|
+
enhance
|
|
27
|
+
floating
|
|
28
|
+
keys
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Full control. Everything re-implemented. Use when the widget must own dismissal (e.g.
|
|
32
|
+
an inline autocomplete that should survive an outside click).
|
|
33
|
+
|
|
34
|
+
### Custom select — B. Reuses the surface lifecycle
|
|
35
|
+
|
|
36
|
+
```
|
|
37
|
+
enhance
|
|
38
|
+
surface (Escape, outside click, focus restore, sheet mode)
|
|
39
|
+
floating
|
|
40
|
+
keys
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Less code. Inherits the runtime's dismissal semantics and mobile sheet. Use for
|
|
44
|
+
anything that behaves like a popup.
|
|
45
|
+
|
|
46
|
+
> A `[role="listbox"]` surface with `[role="option"]` children gets no key handling and
|
|
47
|
+
> no click-autoclose from `surface.js` — that is the extension seam. `[role="option"]`
|
|
48
|
+
> is deliberately not in the menu-item vocabulary.
|
|
49
|
+
|
|
50
|
+
### Tags input
|
|
51
|
+
|
|
52
|
+
```
|
|
53
|
+
enhance + input + keys + filter (optional character policy)
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
No floating, no surface.
|
|
57
|
+
|
|
58
|
+
### Date picker
|
|
59
|
+
|
|
60
|
+
```
|
|
61
|
+
enhance + surface + floating + keys + events
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
A calendar popup wants Escape/outside-click/sheet. `actual:surface-open` to veto or
|
|
65
|
+
decorate.
|
|
66
|
+
|
|
67
|
+
## What "no Actual class" actually promises
|
|
68
|
+
|
|
69
|
+
> No Actual presentation or component class is required in authored markup for behavior to
|
|
70
|
+
> work. Runtime state classes are still written by the primitives, and remapping them is
|
|
71
|
+
> what `selectors.js` is for.
|
|
72
|
+
|
|
73
|
+
Concretely: no `.flyout`, `.tabs`, `.field`, `.btn`, `.needs-validation`, `.status-bar`
|
|
74
|
+
anywhere in the author's HTML — while `.is-open` may appear in the author's *CSS*, as a
|
|
75
|
+
state to style or ignore.
|
|
76
|
+
|
|
77
|
+
## Honest gaps
|
|
78
|
+
|
|
79
|
+
- No typeahead helper — filter, debounce, render are author concerns.
|
|
80
|
+
- No `aria-activedescendant` helper — the author manages the composite-widget pattern.
|
|
81
|
+
- No virtual-list support.
|
|
82
|
+
- No `<select>` replacement in core — the deliverable is the *surface*, not the widget.
|
|
83
|
+
`<select data-enhance="select">` is the intended shape for an external package.
|
package/docs/forms.md
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
- Form controls are styled with explicit classes, not by parent scope.
|
|
8
8
|
- Control classes: `.input`, `.textarea`, `.select`, `.check`, `.radio`, `.switch`, `.range`, `.file`.
|
|
9
9
|
- Reusable layout classes: `.field`, `.field-label`, `.field-help`, `.field-error`, `.field-group`, `.choice`, `.form-actions`, `.form-actions.sticky`.
|
|
10
|
-
- No floating labels.
|
|
10
|
+
- No floating labels shipped — the blocks demo shows a userland recipe for teams that want them.
|
|
11
11
|
- Proper focus style that preserves keyboard navigation.
|
|
12
12
|
- `.field-group` and `.choice` provide layout without depending on parent scope.
|
|
13
13
|
- Customizable select is progressive enhancement only — the native select remains the baseline.
|
|
@@ -1085,7 +1085,7 @@ Actual CSS ships validation *styles*. Invalid fields are marked with `aria-inval
|
|
|
1085
1085
|
Opt in with the `.needs-validation` class. Importing the module registers the behavior; there is no init call.
|
|
1086
1086
|
|
|
1087
1087
|
```html
|
|
1088
|
-
<form class="needs-validation" data-validation-message="Please check the highlighted fields.">
|
|
1088
|
+
<form class="needs-validation" data-enhance="validation" data-validation-message="Please check the highlighted fields.">
|
|
1089
1089
|
<div class="stack">
|
|
1090
1090
|
<label class="field">
|
|
1091
1091
|
<span class="field-label">Password</span>
|
|
@@ -1116,7 +1116,7 @@ On blur, the enhancer sets `aria-invalid="true"` on the invalid field so visual
|
|
|
1116
1116
|
The status bar (`actual-css/js/status`) auto-wires to that event: import it and add one status element, and the form's `data-validation-message` appears automatically on invalid submit — no manual listener.
|
|
1117
1117
|
|
|
1118
1118
|
```html
|
|
1119
|
-
<form class="needs-validation" data-validation-message="Please check the highlighted fields.">
|
|
1119
|
+
<form class="needs-validation" data-enhance="validation" data-validation-message="Please check the highlighted fields.">
|
|
1120
1120
|
<div class="stack">
|
|
1121
1121
|
<label class="field">
|
|
1122
1122
|
<span class="field-label">Email</span>
|
|
@@ -1196,7 +1196,7 @@ Add your own with `FormValidator.registerRule(name, (value, el, ...opts) => bool
|
|
|
1196
1196
|
The `date` rule pairs naturally with `data-mask` — the mask structures input, the rule validates meaning. Same rule, different formats:
|
|
1197
1197
|
|
|
1198
1198
|
```html
|
|
1199
|
-
<form class="stack needs-validation" novalidate>
|
|
1199
|
+
<form class="stack needs-validation" data-enhance="validation" novalidate>
|
|
1200
1200
|
<label class="field">
|
|
1201
1201
|
<span class="field-label">Date (ISO)</span>
|
|
1202
1202
|
<input class="input" name="date-iso"
|
package/docs/javascript.md
CHANGED
|
@@ -67,42 +67,85 @@ handle; cleanup is per-element when an element leaves the DOM.
|
|
|
67
67
|
|
|
68
68
|
## Using The Runtime With Another CSS Framework
|
|
69
69
|
|
|
70
|
-
The JS is designed to be usable on its own, without Actual's stylesheet.
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
`data-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
70
|
+
The JS is designed to be usable on its own, without Actual's stylesheet.
|
|
71
|
+
|
|
72
|
+
- **data-enhance tokens** register root-controller behaviors. The runtime
|
|
73
|
+
discovers components through `data-enhance="tabs"`, `data-enhance="flyout"`,
|
|
74
|
+
`data-enhance="scrollspy"`, and `data-enhance="validation"` — no Actual
|
|
75
|
+
presentation class needed. Self-describing attributes (`data-mask`,
|
|
76
|
+
`data-tooltip`, `data-context-menu`) stay framework-neutral by construction.
|
|
77
|
+
- **`selectors.js`** lists state classes the runtime *writes* (`is-open`,
|
|
78
|
+
`was-validated`, `is-sheet`, …). Edit this file (or alias it in a bundler)
|
|
79
|
+
to match a different CSS framework's state vocabulary. Discovery selectors
|
|
80
|
+
were removed in 0.2 — the file no longer bridges CSS class names to JS
|
|
81
|
+
initialization.
|
|
82
|
+
- **One documented exception:** validation reads `.field-error` (or its alias
|
|
83
|
+
`[data-field-error]`) and the optional `.field` ancestor to connect error
|
|
84
|
+
slots and toggle danger state. Both are read-side presentation adapters that
|
|
85
|
+
degrade to no-op under foreign CSS — `aria-invalid`, focus management,
|
|
86
|
+
custom rules, and server errors all keep working without them.
|
|
87
|
+
- **status.js** uses `[data-status][role="status"]` — semantics, not a class.
|
|
88
|
+
Its `intent` option applies whatever class names the caller passes, so
|
|
89
|
+
`.danger`, `.success`, and the rest of intents.css are never hardcoded.
|
|
90
|
+
|
|
91
|
+
## The Runtime as a Primitive Kit
|
|
92
|
+
|
|
93
|
+
For building custom widgets (select, tags, date picker) on Actual's primitives,
|
|
94
|
+
see [Widget primitives](design-notes/widget-primitives.md). Every primitive
|
|
95
|
+
subpath is published in `package.json#exports`:
|
|
85
96
|
|
|
86
|
-
```js
|
|
87
|
-
// src/js/selectors.js
|
|
88
|
-
export const CLASSES = {
|
|
89
|
-
flyout: "flyout",
|
|
90
|
-
tabs: "tabs",
|
|
91
|
-
open: "is-open",
|
|
92
|
-
// …
|
|
93
|
-
};
|
|
94
97
|
```
|
|
98
|
+
actual-css/js/enhance actual-css/js/events actual-css/js/floating
|
|
99
|
+
actual-css/js/focus actual-css/js/keys actual-css/js/menu
|
|
100
|
+
actual-css/js/surface
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
The exports map is explicit — it *is* the statement of what is public.
|
|
104
|
+
|
|
105
|
+
## Floating Contract
|
|
106
|
+
|
|
107
|
+
`floating` is a positioning-only primitive with zero CSS dependency: no
|
|
108
|
+
`classList`, no `selectors.js` import, no Actual class anywhere. It computes and
|
|
109
|
+
exposes `left`/`top`, `data-placement`, `--available-height`,
|
|
110
|
+
`--arrow-x`/`--arrow-y`. The caller owns the floating element's presentation
|
|
111
|
+
entirely.
|
|
112
|
+
|
|
113
|
+
A third-party widget importing only `floating` with no Actual stylesheet must
|
|
114
|
+
be able to position a listbox under an input — that is the test.
|
|
115
|
+
|
|
116
|
+
## Surface Contract
|
|
117
|
+
|
|
118
|
+
`surface` has documented presentation coupling. A foreign consumer must supply
|
|
119
|
+
or account for:
|
|
120
|
+
|
|
121
|
+
```css
|
|
122
|
+
.my-panel { position: absolute; z-index: 100; }
|
|
123
|
+
.my-panel[hidden] { display: none; }
|
|
124
|
+
/* surface.js sets position:fixed + inline left/top on open —
|
|
125
|
+
do not override with !important */
|
|
126
|
+
/* .is-open / .is-sheet / .surface-backdrop are written by surface.js
|
|
127
|
+
— style or ignore them */
|
|
128
|
+
/* --flyout-trigger-width is set while an open surface is positioned */
|
|
129
|
+
/* data-actual-surface is written by surface.js for teardown
|
|
130
|
+
— never select on it */
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
Two non-obvious requirements:
|
|
134
|
+
1. `[hidden] { display: none }` must not be defeated by a higher-specificity
|
|
135
|
+
`display` rule — `surface.js` toggles `hidden`, not `display`.
|
|
136
|
+
2. The open state switches to `position: fixed` with measured inline
|
|
137
|
+
coordinates.
|
|
138
|
+
|
|
139
|
+
Lifecycle: `prepareSurface` → `openSurface` → `closeSurface` →
|
|
140
|
+
`disconnectSurface`. `openSurface` dispatches a cancelable
|
|
141
|
+
`actual:surface-open` event: widgets can veto or decorate opens, and context
|
|
142
|
+
menus inject their own source/restoreFocusTo through it.
|
|
95
143
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
passes straight to the target's class list — `.danger`, `.success`, and the
|
|
102
|
-
rest of Actual's intents.css vocabulary are never referenced by the module
|
|
103
|
-
itself. Each call resets the target to the class list it had the first time
|
|
104
|
-
`status()` ran, then adds the current call's intent classes, so nothing
|
|
105
|
-
accumulates and classes present before JavaScript ever ran are left alone.
|
|
144
|
+
Extension seam: a `[role="listbox"]` surface with `[role="option"]` children
|
|
145
|
+
gets no keyboard handling and no click-autoclose from `surface.js`.
|
|
146
|
+
`[role="option"]` is deliberately not in the menu-item vocabulary — a combobox
|
|
147
|
+
reuses the surface lifecycle while retaining full control of its own keyboard
|
|
148
|
+
and selection.
|
|
106
149
|
|
|
107
150
|
## Extending The Runtime
|
|
108
151
|
|
|
@@ -169,6 +212,33 @@ Prefer explicit app attributes such as `data-copy`, `data-autogrow`, or
|
|
|
169
212
|
`data-ajax`. Actual's built-in attributes are intentionally small contracts; app
|
|
170
213
|
behavior can be richer without overloading them.
|
|
171
214
|
|
|
215
|
+
### Named behavior registration
|
|
216
|
+
|
|
217
|
+
Use `registerEnhancement()` when author markup opts in with a `data-enhance`
|
|
218
|
+
token instead of an app-specific attribute. A third-party behavior registers
|
|
219
|
+
exactly like a built-in one:
|
|
220
|
+
|
|
221
|
+
```js
|
|
222
|
+
import { registerEnhancement } from "actual-css/js/enhance";
|
|
223
|
+
|
|
224
|
+
registerEnhancement("autosubmit", (form) => {
|
|
225
|
+
const controller = new AbortController();
|
|
226
|
+
|
|
227
|
+
form.addEventListener("change", () => form.requestSubmit(), {
|
|
228
|
+
signal: controller.signal,
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
return () => controller.abort();
|
|
232
|
+
});
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
```html
|
|
236
|
+
<form data-enhance="validation autosubmit">
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
No core module list is modified. The test case in `tests/enhance.test.js`
|
|
240
|
+
(*registers a third-party behavior without touching core*) proves the shape.
|
|
241
|
+
|
|
172
242
|
## Lifecycle Rules
|
|
173
243
|
|
|
174
244
|
- Keep selectors opt-in.
|
|
@@ -389,7 +459,7 @@ event with `{ form, firstInvalid, message }`. Valid fields are not marked
|
|
|
389
459
|
automatically.
|
|
390
460
|
|
|
391
461
|
```html
|
|
392
|
-
<form class="needs-validation" data-validation-message="Please check the fields.">
|
|
462
|
+
<form class="needs-validation" data-enhance="validation" data-validation-message="Please check the fields.">
|
|
393
463
|
<input class="input" name="email" type="email" required
|
|
394
464
|
aria-describedby="email-error" />
|
|
395
465
|
<span class="field-error" id="email-error">Enter a valid email.</span>
|
package/docs/patterns.md
CHANGED
|
@@ -413,3 +413,92 @@ If list chrome should be removed, use an explicit pattern.
|
|
|
413
413
|
<li><a href="/components">Components</a></li>
|
|
414
414
|
</ul>
|
|
415
415
|
```
|
|
416
|
+
|
|
417
|
+
## Hierarchical scrollable nav
|
|
418
|
+
|
|
419
|
+
App shell with a collapsible nav tree. Uses existing primitives only — no
|
|
420
|
+
treeview component.
|
|
421
|
+
|
|
422
|
+
```html
|
|
423
|
+
<aside class="sidebar">
|
|
424
|
+
<nav class="nav-list scroller" data-enhance="scrollspy" aria-label="Docs">
|
|
425
|
+
<details open>
|
|
426
|
+
<summary class="nav-heading">Getting started</summary>
|
|
427
|
+
<a class="nav-link" href="#intro">Introduction</a>
|
|
428
|
+
<a class="nav-link" href="#install">Installation</a>
|
|
429
|
+
</details>
|
|
430
|
+
<details>
|
|
431
|
+
<summary class="nav-heading">Components</summary>
|
|
432
|
+
<a class="nav-link" href="#alert">Alert</a>
|
|
433
|
+
<a class="nav-link" href="#badge">Badge</a>
|
|
434
|
+
</details>
|
|
435
|
+
</nav>
|
|
436
|
+
</aside>
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
- `.nav-list` + `.nav-link`: Actual's nav chrome
|
|
440
|
+
- Native `<details>`: exclusive accordion with `<details name>` (no JS engine)
|
|
441
|
+
- `data-enhance="scrollspy"`: scroll-driven `aria-current`
|
|
442
|
+
- `.scroller`: thin, theme-aware scrollbar
|
|
443
|
+
- No `.nav-heading` / `.nav-sublist` classes — the existing primitives are enough
|
|
444
|
+
|
|
445
|
+
## Code block with copy button
|
|
446
|
+
|
|
447
|
+
```html
|
|
448
|
+
<div class="code-block">
|
|
449
|
+
<pre><code>npm install actual-css</code></pre>
|
|
450
|
+
<button class="btn sm ghost" data-copy>Copy</button>
|
|
451
|
+
</div>
|
|
452
|
+
```
|
|
453
|
+
|
|
454
|
+
```css
|
|
455
|
+
.code-block { position: relative; }
|
|
456
|
+
.code-block > [data-copy] {
|
|
457
|
+
position: absolute;
|
|
458
|
+
inset-block-start: var(--space-2);
|
|
459
|
+
inset-inline-end: var(--space-2);
|
|
460
|
+
}
|
|
461
|
+
```
|
|
462
|
+
|
|
463
|
+
```js
|
|
464
|
+
import enhance from "actual-css/js/enhance";
|
|
465
|
+
|
|
466
|
+
enhance({
|
|
467
|
+
"[data-copy]": (button) => {
|
|
468
|
+
const controller = new AbortController();
|
|
469
|
+
button.addEventListener("click", async () => {
|
|
470
|
+
const code = button.closest(".code-block")?.querySelector("code");
|
|
471
|
+
if (code) await navigator.clipboard.writeText(code.textContent);
|
|
472
|
+
}, { signal: controller.signal });
|
|
473
|
+
return () => controller.abort();
|
|
474
|
+
},
|
|
475
|
+
});
|
|
476
|
+
```
|
|
477
|
+
|
|
478
|
+
No core JS, no syntax highlighter — the recipe is the documentation.
|
|
479
|
+
|
|
480
|
+
## `<details name>` exclusive accordion
|
|
481
|
+
|
|
482
|
+
Browsers that support the `name` attribute on `<details>` get native exclusive
|
|
483
|
+
accordions — no JS engine needed. Omit `name` to allow several open at once.
|
|
484
|
+
|
|
485
|
+
```html
|
|
486
|
+
<details name="faq" open>
|
|
487
|
+
<summary>What is this?</summary>
|
|
488
|
+
<p>A CSS framework.</p>
|
|
489
|
+
</details>
|
|
490
|
+
<details name="faq">
|
|
491
|
+
<summary>Does it need JS?</summary>
|
|
492
|
+
<p>Only the progressive enhancers you import.</p>
|
|
493
|
+
</details>
|
|
494
|
+
```
|
|
495
|
+
|
|
496
|
+
Not shipping an accordion JS engine is a design decision, not a gap.
|
|
497
|
+
|
|
498
|
+
## Intent nesting
|
|
499
|
+
|
|
500
|
+
Intent on an ancestor must not tint nested components, and a local intent inside
|
|
501
|
+
an intent-carrying ancestor must win. Both directions are verified in
|
|
502
|
+
`tests/css-audit.test.js` and the kitchen-sink demo template. Every
|
|
503
|
+
intent-consuming component must declare its `@sync intent-boundary` block —
|
|
504
|
+
that is what prevents inheritance from leaking across components.
|
package/docs/ui.md
CHANGED
|
@@ -65,13 +65,7 @@ Use this shape when the dialog interrupts the flow and asks for a decision. It h
|
|
|
65
65
|
</div>
|
|
66
66
|
|
|
67
67
|
<footer>
|
|
68
|
-
<button class="btn outline"
|
|
69
|
-
type="button"
|
|
70
|
-
commandfor="delete-dialog"
|
|
71
|
-
command="request-close"
|
|
72
|
-
aria-controls="delete-dialog">
|
|
73
|
-
Cancel
|
|
74
|
-
</button>
|
|
68
|
+
<button class="btn outline" value="cancel">Cancel</button>
|
|
75
69
|
|
|
76
70
|
<button class="btn danger"
|
|
77
71
|
value="delete">
|
|
@@ -161,13 +155,7 @@ Use `modal scrollable` when the header and footer should stay visible while the
|
|
|
161
155
|
</div>
|
|
162
156
|
|
|
163
157
|
<footer>
|
|
164
|
-
<button class="btn outline"
|
|
165
|
-
type="button"
|
|
166
|
-
commandfor="scroll-dialog"
|
|
167
|
-
command="request-close"
|
|
168
|
-
aria-controls="scroll-dialog">
|
|
169
|
-
Cancel
|
|
170
|
-
</button>
|
|
158
|
+
<button class="btn outline" value="cancel">Cancel</button>
|
|
171
159
|
|
|
172
160
|
<button class="btn primary" value="accept">
|
|
173
161
|
Accept
|
|
@@ -227,9 +215,7 @@ Flyouts and tooltips opened from inside a modal dialog are mounted inside the di
|
|
|
227
215
|
</button>
|
|
228
216
|
</div>
|
|
229
217
|
|
|
230
|
-
<menu class="flyout"
|
|
231
|
-
id="dialog-actions-menu"
|
|
232
|
-
hidden>
|
|
218
|
+
<menu class="flyout" data-enhance="flyout" id="dialog-actions-menu" hidden>
|
|
233
219
|
<li><button type="button">Archive</button></li>
|
|
234
220
|
<li><button type="button">Duplicate</button></li>
|
|
235
221
|
<li><button type="button">Share</button></li>
|
|
@@ -311,13 +297,7 @@ The effect is progressive: it only runs when the browser supports `document.star
|
|
|
311
297
|
</header>
|
|
312
298
|
|
|
313
299
|
<footer>
|
|
314
|
-
<button class="btn outline"
|
|
315
|
-
type="button"
|
|
316
|
-
commandfor="vt-dialog"
|
|
317
|
-
command="request-close"
|
|
318
|
-
aria-controls="vt-dialog">
|
|
319
|
-
Cancel
|
|
320
|
-
</button>
|
|
300
|
+
<button class="btn outline" value="cancel">Cancel</button>
|
|
321
301
|
<button class="btn primary" value="confirm">Confirm</button>
|
|
322
302
|
</footer>
|
|
323
303
|
</form>
|
|
@@ -478,6 +458,7 @@ Flyout covers two distinct patterns:
|
|
|
478
458
|
</button>
|
|
479
459
|
|
|
480
460
|
<menu class="flyout sm"
|
|
461
|
+
data-enhance="flyout"
|
|
481
462
|
id="account-actions"
|
|
482
463
|
aria-labelledby="account-flyout-trigger"
|
|
483
464
|
hidden>
|
|
@@ -501,7 +482,7 @@ Flyout covers two distinct patterns:
|
|
|
501
482
|
<i class="ti ti-chevron-down" aria-hidden="true"></i>
|
|
502
483
|
</button>
|
|
503
484
|
|
|
504
|
-
<div class="flyout"
|
|
485
|
+
<div class="flyout" data-enhance="flyout"
|
|
505
486
|
id="products-panel"
|
|
506
487
|
aria-label="Products"
|
|
507
488
|
data-flyout-mobile="auto"
|
|
@@ -551,6 +532,7 @@ Use `class="flyout grid-3"` when a nav panel needs multiple link groups. Keep li
|
|
|
551
532
|
</button>
|
|
552
533
|
|
|
553
534
|
<div class="flyout grid-3"
|
|
535
|
+
data-enhance="flyout"
|
|
554
536
|
id="product-mega-menu"
|
|
555
537
|
aria-label="Platform"
|
|
556
538
|
data-flyout-mobile="auto"
|
|
@@ -628,6 +610,7 @@ Long press is opt-in with `data-context-menu-long-press`. Empty uses the default
|
|
|
628
610
|
<output id="context-result" class="text-sm text-muted" aria-live="polite">No context selected.</output>
|
|
629
611
|
|
|
630
612
|
<menu class="flyout"
|
|
613
|
+
data-enhance="flyout"
|
|
631
614
|
id="file-actions"
|
|
632
615
|
aria-labelledby="file-actions-trigger"
|
|
633
616
|
hidden>
|
|
@@ -662,7 +645,7 @@ Long press is opt-in with `data-context-menu-long-press`. Empty uses the default
|
|
|
662
645
|
|
|
663
646
|
```html
|
|
664
647
|
<div class="tabset">
|
|
665
|
-
<div class="tabs" role="tablist" aria-label="Settings">
|
|
648
|
+
<div class="tabs" data-enhance="tabs" role="tablist" aria-label="Settings">
|
|
666
649
|
<button class="tab primary"
|
|
667
650
|
type="button"
|
|
668
651
|
role="tab"
|
|
@@ -832,12 +815,21 @@ or style `[aria-current]` yourself.
|
|
|
832
815
|
|
|
833
816
|
- `.scrollspy` marks the region for JS detection. It does not add visual styles.
|
|
834
817
|
- `.nav-list` and `.nav-link` provide the visible list and active link styling.
|
|
835
|
-
- The JS enhancer
|
|
836
|
-
|
|
818
|
+
- The JS enhancer toggles `aria-current="location"` on the active link. Without
|
|
819
|
+
JavaScript, the links remain regular anchor links.
|
|
820
|
+
- Detection is geometric and deterministic: on each scroll, the active section is
|
|
821
|
+
the last one whose top has crossed an activation line. Reaching the end of the
|
|
822
|
+
scroll always activates the last section, and sitting above the first activates
|
|
823
|
+
none.
|
|
824
|
+
- `data-scrollspy-root="#container"` measures a scroll container instead of the
|
|
825
|
+
viewport.
|
|
826
|
+
- `data-scrollspy-offset` moves the activation line: a bare number or `px` value
|
|
827
|
+
is pixels, a `%` value is a share of the root's visible height. Invalid values
|
|
828
|
+
fall back to `20%`, and negatives clamp to `0`.
|
|
837
829
|
- The same markup also works with the CSS-native scroll markers path (see below).
|
|
838
830
|
|
|
839
831
|
```html
|
|
840
|
-
<nav class="scrollspy" aria-label="Page sections">
|
|
832
|
+
<nav class="scrollspy" data-enhance="scrollspy" aria-label="Page sections">
|
|
841
833
|
<ol class="nav-list stack">
|
|
842
834
|
<li><a class="nav-link" href="#overview" aria-current="location">Overview</a></li>
|
|
843
835
|
<li><a class="nav-link" href="#tokens">Tokens</a></li>
|
|
@@ -892,7 +884,7 @@ portable fallback — the same markup serves both paths.
|
|
|
892
884
|
- Intents: `danger`, `success`, `warning`, `neutral`. The default (no intent) is a neutral dark pill.
|
|
893
885
|
|
|
894
886
|
```html
|
|
895
|
-
<form class="needs-validation" data-validation-message="Please check the highlighted fields.">
|
|
887
|
+
<form class="needs-validation" data-enhance="validation" data-validation-message="Please check the highlighted fields.">
|
|
896
888
|
<label class="field">
|
|
897
889
|
<span class="field-label">Email</span>
|
|
898
890
|
<input class="input" type="email" name="email" required
|
package/llms.txt
CHANGED
|
@@ -15,3 +15,9 @@ full CSS framework and `actual-css/js` for the complete runtime. The composition
|
|
|
15
15
|
guides in [README](README.md) and [JavaScript](docs/javascript.md) show supported
|
|
16
16
|
CSS modules and individual enhancer imports without duplicating the full export
|
|
17
17
|
list here.
|
|
18
|
+
|
|
19
|
+
In 0.2, presentation (`class`) and behavior (`data-enhance`) are separate layers.
|
|
20
|
+
Behaviour primitives (`enhance`, `floating`, `surface`, `keys`, `menu`, `focus`,
|
|
21
|
+
`events`) are independently importable for building custom widgets.
|
|
22
|
+
See [enhancement contract](docs/design-notes/enhancement-contract.md) and
|
|
23
|
+
[widget primitives](docs/design-notes/widget-primitives.md).
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "actual-css",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.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.3.14",
|
|
@@ -8,11 +8,11 @@
|
|
|
8
8
|
"author": "Thomas <thomas@lekoala.be>",
|
|
9
9
|
"repository": {
|
|
10
10
|
"type": "git",
|
|
11
|
-
"url": "https://
|
|
11
|
+
"url": "https://github.com/lekoala/actual-css.git"
|
|
12
12
|
},
|
|
13
|
-
"homepage": "https://
|
|
13
|
+
"homepage": "https://github.com/lekoala/actual-css",
|
|
14
14
|
"bugs": {
|
|
15
|
-
"url": "https://
|
|
15
|
+
"url": "https://github.com/lekoala/actual-css/issues"
|
|
16
16
|
},
|
|
17
17
|
"keywords": [
|
|
18
18
|
"css",
|
|
@@ -40,11 +40,15 @@
|
|
|
40
40
|
"./js/dialog": "./src/js/dialog.js",
|
|
41
41
|
"./js/dialog-fallback": "./src/js/dialog-fallback.js",
|
|
42
42
|
"./js/enhance": "./src/js/enhance.js",
|
|
43
|
+
"./js/events": "./src/js/events.js",
|
|
43
44
|
"./js/filter": "./src/js/filter.js",
|
|
44
45
|
"./js/floating": "./src/js/floating.js",
|
|
45
46
|
"./js/flyout": "./src/js/flyout.js",
|
|
47
|
+
"./js/focus": "./src/js/focus.js",
|
|
46
48
|
"./js/input": "./src/js/input.js",
|
|
49
|
+
"./js/keys": "./src/js/keys.js",
|
|
47
50
|
"./js/mask": "./src/js/mask.js",
|
|
51
|
+
"./js/menu": "./src/js/menu.js",
|
|
48
52
|
"./js/password": "./src/js/password.js",
|
|
49
53
|
"./js/scrollspy": "./src/js/scrollspy.js",
|
|
50
54
|
"./js/selectors": "./src/js/selectors.js",
|
|
@@ -52,6 +56,7 @@
|
|
|
52
56
|
"./js/tooltip": "./src/js/tooltip.js",
|
|
53
57
|
"./js/validation": "./src/js/validation.js",
|
|
54
58
|
"./js/status": "./src/js/status.js",
|
|
59
|
+
"./js/surface": "./src/js/surface.js",
|
|
55
60
|
"./package.json": "./package.json"
|
|
56
61
|
},
|
|
57
62
|
"main": "./dist/actual.js",
|
|
@@ -83,6 +88,7 @@
|
|
|
83
88
|
"build:dist": "bun scripts/build-dist.js",
|
|
84
89
|
"build:js": "bun scripts/build-js.js",
|
|
85
90
|
"build:size": "bun scripts/build-size.js",
|
|
91
|
+
"check:enhance": "bun run scripts/check-enhance.js",
|
|
86
92
|
"check:reserved": "bun scripts/check-reserved.js",
|
|
87
93
|
"check:links": "bun scripts/check-doc-links.js",
|
|
88
94
|
"check:templates": "bun scripts/check-templates.js",
|
|
@@ -90,12 +96,12 @@
|
|
|
90
96
|
"shot:page": "bun scripts/page-shot.js",
|
|
91
97
|
"check:sync": "bun scripts/check-sync.js",
|
|
92
98
|
"test": "bun test",
|
|
93
|
-
"build:all": "bun run build:demo && bun run build:dist && bun run check:reserved && bun run check:sync && bun run check:links && bun run check:templates && bun run test && bun run build:js && bun run build:size",
|
|
99
|
+
"build:all": "bun run build:demo && bun run build:dist && bun run check:enhance && bun run check:reserved && bun run check:sync && bun run check:links && bun run check:templates && bun run test && bun run build:js && bun run build:size",
|
|
94
100
|
"prepublishOnly": "bun run build:all"
|
|
95
101
|
},
|
|
96
102
|
"devDependencies": {
|
|
97
|
-
"@biomejs/biome": "^2.
|
|
98
|
-
"happy-dom": "^20.
|
|
99
|
-
"prismjs": "^1.30
|
|
103
|
+
"@biomejs/biome": "^2.5",
|
|
104
|
+
"happy-dom": "^20.11",
|
|
105
|
+
"prismjs": "^1.30"
|
|
100
106
|
}
|
|
101
107
|
}
|