actual-css 0.4.0 → 0.4.2
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 +84 -35
- package/dist/actual.css +1 -20
- package/dist/actual.full.css +692 -221
- package/dist/actual.full.js +1 -1
- package/dist/actual.full.min.css +1 -1
- package/dist/actual.min.css +1 -1
- package/docs/design-notes/app-ui.md +33 -0
- package/docs/design-notes/enhancement-contract.md +25 -0
- package/docs/design-notes/grid.md +177 -4
- package/docs/design-notes/references.md +1 -1
- package/docs/navigation.json +7 -1
- package/docs/pages/components/alert.md +5 -0
- package/docs/pages/components/app-navigation.md +34 -0
- package/docs/pages/components/avatar.md +10 -0
- package/docs/pages/components/badge.md +2 -0
- package/docs/pages/components/button.md +30 -0
- package/docs/pages/components/card.md +5 -1
- package/docs/pages/components/fab.md +2 -0
- package/docs/pages/components/indicator.md +52 -0
- package/docs/pages/components/list.md +62 -0
- package/docs/pages/components/navbar.md +6 -0
- package/docs/pages/components/rating.md +68 -0
- package/docs/pages/components/steps.md +57 -0
- package/docs/pages/enhancements/flyout.md +2 -2
- package/docs/pages/enhancements/tabs.md +1 -2
- package/docs/pages/examples/overview.md +8 -0
- package/docs/pages/forms/otp.md +32 -0
- package/docs/pages/forms/states-and-sizes.md +3 -3
- package/docs/pages/guides/integrating-actual-css.md +6 -0
- package/docs/pages/layout/app-layout.md +57 -0
- package/docs/pages/layout/app-shell.md +28 -7
- package/docs/pages/layout/center.md +12 -1
- package/docs/pages/layout/choosing.md +15 -0
- package/docs/pages/layout/grid.md +41 -8
- package/docs/pages/layout/sidebar-layout.md +19 -0
- package/docs/pages/layout/stack.md +17 -0
- package/docs/pages/patterns/icon-slot.md +61 -0
- package/docs/pages/patterns/structured-lists.md +38 -40
- package/package.json +3 -2
- package/scripts/reserved-classes.json +17 -0
- package/src/css/components/actions.css +1 -0
- package/src/css/components/app-nav.css +76 -0
- package/src/css/components/avatar.css +4 -0
- package/src/css/components/badge.css +1 -13
- package/src/css/components/breadcrumb.css +1 -0
- package/src/css/components/button.css +8 -38
- package/src/css/components/card.css +11 -11
- package/src/css/components/chat.css +0 -6
- package/src/css/components/drawer.css +0 -6
- package/src/css/components/fab.css +1 -7
- package/src/css/components/index.css +5 -0
- package/src/css/components/indicator.css +47 -0
- package/src/css/components/list.css +83 -0
- package/src/css/components/menu.css +0 -7
- package/src/css/components/modal.css +0 -17
- package/src/css/components/navbar.css +8 -15
- package/src/css/components/pagination.css +1 -0
- package/src/css/components/rating.css +101 -0
- package/src/css/components/steps.css +123 -0
- package/src/css/components/tab.css +21 -22
- package/src/css/components/tooltip.css +1 -10
- package/src/css/core/focus.css +0 -8
- package/src/css/core/theme.css +0 -10
- package/src/css/core/tokens.css +1 -2
- package/src/css/effects/aura.css +1 -10
- package/src/css/forms/choice-card.css +6 -15
- package/src/css/forms/form-actions.css +1 -0
- package/src/css/forms/otp.css +7 -2
- package/src/css/forms/range.css +14 -4
- package/src/css/layout/app-layout.css +102 -0
- package/src/css/layout/app-shell.css +1 -1
- package/src/css/layout/center.css +1 -0
- package/src/css/layout/cluster.css +1 -0
- package/src/css/layout/container-query.css +6 -4
- package/src/css/layout/grid.css +25 -11
- package/src/css/layout/index.css +4 -2
- package/src/css/layout/scroll-snap.css +1 -0
- package/src/css/layout/sidebar-layout.css +23 -1
- package/src/css/layout/switcher.css +1 -0
- package/src/css/layout/topbar.css +1 -0
- package/src/css/typography/prose.css +4 -0
- package/src/js/tooltip.js +2 -1
- package/docs/pages/layout/list.md +0 -41
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Application UI boundaries
|
|
2
|
+
|
|
3
|
+
Actual CSS provides application chrome and content composition without owning
|
|
4
|
+
the application lifecycle.
|
|
5
|
+
|
|
6
|
+
```text
|
|
7
|
+
app-shell full-height single-column document flow
|
|
8
|
+
topbar persistent top application chrome
|
|
9
|
+
app-layout specialized topbar + internal scroll + adaptive navigation recipe
|
|
10
|
+
app-nav primary application destinations
|
|
11
|
+
list repeated application rows with leading/content/trailing regions
|
|
12
|
+
status-bar passive transient feedback
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
`app-layout` is selected by that complete behavior, never by product category.
|
|
16
|
+
Public sites and applications with ordinary document scrolling stay on
|
|
17
|
+
semantic landmarks and may use `app-shell`; a site header uses `navbar`.
|
|
18
|
+
|
|
19
|
+
Routing, navigation history, application state, SPA page lifecycle, service
|
|
20
|
+
workers, manifests, offline policy, and install prompts belong to the
|
|
21
|
+
application. Actual does not prescribe Material breakpoints or device classes;
|
|
22
|
+
the application layout changes when its available viewport can accommodate
|
|
23
|
+
labelled side navigation.
|
|
24
|
+
|
|
25
|
+
Compact UI vocabulary remains semantic rather than shape-driven:
|
|
26
|
+
|
|
27
|
+
- use `.btn.sm` for compact actions and `aria-pressed` filters;
|
|
28
|
+
- use `.badge` for passive counts, statuses, and categories;
|
|
29
|
+
- use `.badge > button` when a displayed tag can be removed;
|
|
30
|
+
- use `.status-bar` for passive feedback, not an actionable toast.
|
|
31
|
+
|
|
32
|
+
There is no generic chip or snackbar component. Those names conflate different
|
|
33
|
+
HTML semantics that the existing components already keep explicit.
|
|
@@ -72,6 +72,31 @@ creates two independent records that both run.
|
|
|
72
72
|
The owner of a custom enhancement root must call `disconnect()` when disposing
|
|
73
73
|
that root; the default `document.documentElement` root is not affected.
|
|
74
74
|
|
|
75
|
+
`enhance(…, root)` owns the lifecycle boundary. It scans additions inside that
|
|
76
|
+
root, preserves instances moved within it, and requests cleanup once an instance
|
|
77
|
+
leaves it—even when the element remains connected elsewhere. An enhancer's
|
|
78
|
+
cleanup must therefore be authoritative and idempotent: it releases its state
|
|
79
|
+
when called and must not repeat the root-membership decision.
|
|
80
|
+
|
|
81
|
+
Core lifecycle primitives accept a `Document`, `Element`, or `DocumentFragment`
|
|
82
|
+
root, and command targets resolve in the trigger's document or shadow root.
|
|
83
|
+
Built-in auto-registration observes the document tree. It does not discover or
|
|
84
|
+
manage built-ins created inside shadow roots. Applications that own a custom
|
|
85
|
+
root can install their own enhancements with the core APIs and must dispose
|
|
86
|
+
those registrations explicitly.
|
|
87
|
+
|
|
88
|
+
## Choosing a JavaScript primitive
|
|
89
|
+
|
|
90
|
+
- Use a command for an immediate trigger-to-current-target action. Commands
|
|
91
|
+
resolve the DOM at event time and do not own element lifecycle.
|
|
92
|
+
- Use `registerEnhancement()` for an opted-in controller that owns listeners or
|
|
93
|
+
state for a subtree.
|
|
94
|
+
- Use `enhance()` for a self-describing leaf behavior that needs connection and
|
|
95
|
+
cleanup lifecycle without a named token.
|
|
96
|
+
- Prefer delegation when no persistent per-element state is required.
|
|
97
|
+
- Give instance-owned listeners an `AbortController`, and abort it from the
|
|
98
|
+
instance cleanup.
|
|
99
|
+
|
|
75
100
|
## CSS must never select on `data-enhance`
|
|
76
101
|
|
|
77
102
|
CSS rules must not select on `data-enhance` or the runtime marker
|
|
@@ -254,7 +254,7 @@ asks.
|
|
|
254
254
|
It is nevertheless the right **baseline**. Bounded at N, using `auto-fill`, it
|
|
255
255
|
is responsive, overflow-safe, and equal-width at every state, and it needs
|
|
256
256
|
nothing above Actual's Minimal floor. Adopting it as the unenhanced behavior of
|
|
257
|
-
`.grid-N` is what makes the
|
|
257
|
+
`.grid-N` is what makes the actual-grid container a precision enhancement rather than
|
|
258
258
|
a prerequisite for responsiveness. This is the single most important difference
|
|
259
259
|
from 0.3.1, where a preset used without a wrapper stayed frozen at N columns
|
|
260
260
|
and was a genuine trap.
|
|
@@ -380,7 +380,7 @@ Placing the query container on an ancestor preserves the intrinsic sizing behavi
|
|
|
380
380
|
The relationship is:
|
|
381
381
|
|
|
382
382
|
```text
|
|
383
|
-
.container-query
|
|
383
|
+
query container (any ancestor, e.g. .container-query)
|
|
384
384
|
↓ observes local available width
|
|
385
385
|
.grid-N
|
|
386
386
|
↓ changes structural subdivision
|
|
@@ -391,7 +391,7 @@ The extra ancestor is therefore not accidental ceremony.
|
|
|
391
391
|
|
|
392
392
|
It represents a real boundary imposed by the CSS containment model.
|
|
393
393
|
|
|
394
|
-
Actual should not hide that boundary if hiding it requires changing the sizing semantics of the grid.
|
|
394
|
+
Actual should not hide that boundary if hiding it requires changing the sizing semantics of the grid. Balanced subdivision keys off a container **named** `actual-grid` rather than any anonymous query container, so an incidental `container-type: inline-size` region never changes the grid by accident; `.container-query` is the convenience helper that establishes that named context.
|
|
395
395
|
|
|
396
396
|
## Out of scope: explicit per-region placement
|
|
397
397
|
|
|
@@ -496,6 +496,79 @@ Before changing structural-grid thresholds, probes should verify:
|
|
|
496
496
|
|
|
497
497
|
The viewport itself should not determine the result; the relevant local container should.
|
|
498
498
|
|
|
499
|
+
## Why there is no breakpoint scale
|
|
500
|
+
|
|
501
|
+
Actual exposes no `sm` / `md` / `lg` / `xl` scale, and the structural grid is the
|
|
502
|
+
clearest illustration of why. The whole framework declares two width media
|
|
503
|
+
queries:
|
|
504
|
+
|
|
505
|
+
```text
|
|
506
|
+
@media (max-width: 30rem) x2 modal padding on small screens
|
|
507
|
+
@media (min-width: 48rem) x1 .app-layout, bottom nav -> side nav
|
|
508
|
+
```
|
|
509
|
+
|
|
510
|
+
Everything else responsive is intrinsic or container-driven. The responsibility
|
|
511
|
+
splits cleanly:
|
|
512
|
+
|
|
513
|
+
```text
|
|
514
|
+
viewport
|
|
515
|
+
|
|
|
516
|
+
+-- the few genuinely viewport-level decisions
|
|
517
|
+
| .app-layout
|
|
518
|
+
|
|
|
519
|
+
+-- composition and chrome
|
|
520
|
+
|
|
|
521
|
+
named query container
|
|
522
|
+
|
|
|
523
|
+
actual-grid
|
|
524
|
+
|
|
|
525
|
+
2 / 3 / 4 / 6 stages
|
|
526
|
+
```
|
|
527
|
+
|
|
528
|
+
**Thresholds are local and content-driven.** `28rem`, `48rem` and `64rem`
|
|
529
|
+
describe how much room *this collection* needs for a given density, probed
|
|
530
|
+
against the content each preset targets. They are not device classes, and the
|
|
531
|
+
fact that two of them coincide with widely used framework breakpoints is a
|
|
532
|
+
coincidence, not the justification. A grid does not know what device it is on
|
|
533
|
+
and does not need to.
|
|
534
|
+
|
|
535
|
+
**The unit is `rem` so the thresholds follow typographic scale.** Container
|
|
536
|
+
query lengths in `rem` resolve against the root font size, so a reader who
|
|
537
|
+
scales their type up moves every threshold with it:
|
|
538
|
+
|
|
539
|
+
```text
|
|
540
|
+
root 16px 48rem = 768px 768px container -> .grid-3 = 3 columns
|
|
541
|
+
root 20px 48rem = 960px 768px container -> .grid-3 = 1 column
|
|
542
|
+
root 24px 48rem = 1152px 768px container -> .grid-3 = 1 column
|
|
543
|
+
```
|
|
544
|
+
|
|
545
|
+
That is the correct response, not a rounding artifact: larger type genuinely
|
|
546
|
+
needs more room per item, so the same container honestly supports fewer columns.
|
|
547
|
+
A `px` breakpoint cannot express this.
|
|
548
|
+
|
|
549
|
+
### Container width is not a monotonic function of viewport width
|
|
550
|
+
|
|
551
|
+
This is the empirical case against a global scale, and it is measurable inside
|
|
552
|
+
this repository. `demo/templates/dashboard.html` reveals its workspace sidebar
|
|
553
|
+
at `72rem`. Measuring the main region's content box across that boundary:
|
|
554
|
+
|
|
555
|
+
| Viewport | Container content box | `.grid-4` | `.grid-6` |
|
|
556
|
+
| -------- | --------------------- | --------- | --------- |
|
|
557
|
+
| 1100px | 65.8rem | 4 | 6 |
|
|
558
|
+
| 1160px | 52.6rem | 2 | 3 |
|
|
559
|
+
|
|
560
|
+
The viewport grew by 60px and the grid lost half its columns, because the
|
|
561
|
+
composition spent that growth on chrome rather than on content. A viewport-keyed
|
|
562
|
+
system reasons about this backwards by construction: it sees a wider screen and
|
|
563
|
+
infers more room, at the exact moment there is less. No amount of breakpoint
|
|
564
|
+
tuning fixes that, because there is no stable function from viewport width to
|
|
565
|
+
container width — the composition sits in between and is free to change.
|
|
566
|
+
|
|
567
|
+
A named query container asks the only question with a reliable answer: *how much
|
|
568
|
+
inline space does this collection actually have?* Everything the ladder does
|
|
569
|
+
follows from that measurement, which is why the thresholds can stay content-
|
|
570
|
+
derived instead of device-derived.
|
|
571
|
+
|
|
499
572
|
## API guidance
|
|
500
573
|
|
|
501
574
|
Use `.grid` when the number of columns should emerge naturally from available space.
|
|
@@ -547,7 +620,7 @@ inside a query container.
|
|
|
547
620
|
.grid space-driven, auto-fit, --grid-min
|
|
548
621
|
.grid-N count-driven baseline, auto-fill bounded at N,
|
|
549
622
|
may pass through non-divisor column counts
|
|
550
|
-
|
|
623
|
+
actual-grid .grid-N balanced subdivision, N -> divisors of N -> 1
|
|
551
624
|
```
|
|
552
625
|
|
|
553
626
|
The whole enhancement lives inside `@supports (container-type: inline-size)`.
|
|
@@ -603,6 +676,42 @@ The framework does not guess appropriate item size. The author states it by
|
|
|
603
676
|
choosing a density; the framework then guarantees a clean structural collapse
|
|
604
677
|
of that density.
|
|
605
678
|
|
|
679
|
+
### Reference container sizes
|
|
680
|
+
|
|
681
|
+
The thresholds describe the **query container's content box**, not the viewport
|
|
682
|
+
and not the grid's own border box. Padding and borders on the container come off
|
|
683
|
+
the top before the query resolves. Verified states:
|
|
684
|
+
|
|
685
|
+
| Container content box | `.grid-2` | `.grid-3` | `.grid-4` | `.grid-6` |
|
|
686
|
+
| --------------------- | --------- | --------- | --------- | --------- |
|
|
687
|
+
| `< 28rem` | 1 | 1 | 1 | 1 |
|
|
688
|
+
| `28rem` – `< 48rem` | 2 | 1 | 2 | 2 |
|
|
689
|
+
| `48rem` – `< 64rem` | 2 | 3 | 2 | 3 |
|
|
690
|
+
| `>= 64rem` | 2 | 3 | 4 | 6 |
|
|
691
|
+
|
|
692
|
+
Two consequences for anyone sizing a region that hosts a structural grid.
|
|
693
|
+
|
|
694
|
+
**Size the region to land on a threshold, not just under one.** A region whose
|
|
695
|
+
content box settles at 47rem gives `.grid-3` one column — the same result as
|
|
696
|
+
20rem. The step is a cliff, not a ramp, and `.grid-3` has no intermediate state
|
|
697
|
+
to soften it; that is the invariant, not a defect. If a region is meant to show
|
|
698
|
+
three columns, give it 48rem of content box with room to spare.
|
|
699
|
+
|
|
700
|
+
**Budget backwards from the content box.** A region that must reach a threshold
|
|
701
|
+
`T` needs `T + its own padding-inline + its own border-inline` of outer width.
|
|
702
|
+
The convenient reference sizes are therefore the thresholds plus the padding the
|
|
703
|
+
region actually carries:
|
|
704
|
+
|
|
705
|
+
```text
|
|
706
|
+
target 28rem content box + 2 x 1rem padding + 2 x 1px border ~= 30.2rem outer
|
|
707
|
+
target 48rem content box + 2 x 1rem padding + 2 x 1px border ~= 50.2rem outer
|
|
708
|
+
target 64rem content box + 2 x 1rem padding + 2 x 1px border ~= 66.2rem outer
|
|
709
|
+
```
|
|
710
|
+
|
|
711
|
+
Rounding **up** past that figure is deliberate. Landing on the threshold to the
|
|
712
|
+
pixel means a later padding change, measured in fractions of a rem, silently
|
|
713
|
+
drops the region a state.
|
|
714
|
+
|
|
606
715
|
### Why there is no `.grid-5`
|
|
607
716
|
|
|
608
717
|
The divisors of five are five and one, so the chain would be `5 -> 1` with no
|
|
@@ -610,3 +719,67 @@ intermediate state. The system exposes densities that subdivide well, not every
|
|
|
610
719
|
integer. Proposals to give `.grid-5` a hand-picked `5 -> 2 -> 1` chain are
|
|
611
720
|
covered by the rejected `smart-grid` section above: those steps encode an
|
|
612
721
|
assumption about item count that the class cannot verify.
|
|
722
|
+
|
|
723
|
+
## Demonstrability is part of the contract
|
|
724
|
+
|
|
725
|
+
A threshold nobody can reach is indistinguishable from a threshold that does not
|
|
726
|
+
work. This was not hypothetical: the documentation site shipped for some time
|
|
727
|
+
with an article column capped at 48rem, and the live demo previews inside it are
|
|
728
|
+
the query containers. Their content box therefore topped out at **45.9rem at
|
|
729
|
+
every viewport width** — 1280px, 1920px, identical. Every `.grid-3` demo on the
|
|
730
|
+
site rendered one column, `.grid-4` and `.grid-6` rendered two, and the page
|
|
731
|
+
teaching `6 -> 3 -> 2 -> 1` showed the reader its 2-column state as though that
|
|
732
|
+
were the top of the chain.
|
|
733
|
+
|
|
734
|
+
The bug was reported as a `.grid-3` calibration complaint. It was not one. No
|
|
735
|
+
text-based check could see it, because every class name, every threshold, and
|
|
736
|
+
every rule in the framework was correct.
|
|
737
|
+
|
|
738
|
+
Two durable rules came out of it.
|
|
739
|
+
|
|
740
|
+
**The query container must be the element whose content box is the grid's actual
|
|
741
|
+
available space.** Putting `container` on a padded box is correct; putting it on
|
|
742
|
+
an outer wrapper and letting an inner box add the padding makes the query
|
|
743
|
+
resolve against a width the grid does not have, firing every threshold early.
|
|
744
|
+
The demo would then claim a state it cannot honor — a lie in the geometry rather
|
|
745
|
+
than in the CSS.
|
|
746
|
+
|
|
747
|
+
**A surface that documents the thresholds has to be wide enough to reach them.**
|
|
748
|
+
The docs shell is sized backwards from 64rem plus the preview's own padding, and
|
|
749
|
+
that arithmetic is asserted in `tests/docs-geometry.test.js` against the
|
|
750
|
+
thresholds parsed out of `grid.css`, so raising a threshold without giving the
|
|
751
|
+
documentation room to reach it fails a test instead of quietly degrading every
|
|
752
|
+
demo. The prose keeps its own reading measure inside the wider track: demo
|
|
753
|
+
measure and prose measure are different contracts and should not be forced to
|
|
754
|
+
share a number.
|
|
755
|
+
|
|
756
|
+
### A probe that correctly changed nothing
|
|
757
|
+
|
|
758
|
+
The same investigation measured `.choice-card` in a forced 3-up, to ask whether
|
|
759
|
+
`.grid-3`'s 48rem step was simply too conservative:
|
|
760
|
+
|
|
761
|
+
| Container | px / card | Result |
|
|
762
|
+
| --------- | --------- | ------------------ |
|
|
763
|
+
| 44rem | 224 | one line, at ease |
|
|
764
|
+
| 40rem | 203 | one line, at limit |
|
|
765
|
+
| 38rem | 192 | **wraps** |
|
|
766
|
+
| 32rem | 160 | wraps badly |
|
|
767
|
+
|
|
768
|
+
So those particular cards do fit three-up at roughly 40rem, well below the
|
|
769
|
+
preset's step. That was not a reason to move the step or to add a density
|
|
770
|
+
modifier, and the temptation is worth recording.
|
|
771
|
+
|
|
772
|
+
`.grid-3` is a generic preset built around ~15.5rem per item, in agreement with
|
|
773
|
+
`.grid-4`'s ~15.4rem. Choice cards are simply smaller than the preset's target
|
|
774
|
+
content. Lowering the step to fit them would have put `.grid-3` at ~14.2rem and
|
|
775
|
+
broken the only clean agreement in the ladder — to fix a symptom whose actual
|
|
776
|
+
cause was 2rem of documentation padding.
|
|
777
|
+
|
|
778
|
+
A modifier introducing a second density ladder becomes defensible only when
|
|
779
|
+
*several unrelated* content families independently show the standard steps to be
|
|
780
|
+
too conservative while wanting identical balanced-collapse behavior. One
|
|
781
|
+
component in one miscalibrated container is not that evidence. A per-component
|
|
782
|
+
threshold would also collide with the established meaning of `.compact` in
|
|
783
|
+
Actual, which is padding density on a component (`.card.compact`,
|
|
784
|
+
`.table.compact`) and never a layout threshold.
|
|
785
|
+
|
|
@@ -221,7 +221,7 @@ Prior Actual CSS libraries:
|
|
|
221
221
|
- https://smolcss.dev/#smol-container
|
|
222
222
|
- https://every-layout.dev/layouts/center/
|
|
223
223
|
|
|
224
|
-
###
|
|
224
|
+
### Component: List
|
|
225
225
|
|
|
226
226
|
- https://daisyui.com/components/list/
|
|
227
227
|
- https://html.spec.whatwg.org/multipage/grouping-content.html#the-li-element
|
package/docs/navigation.json
CHANGED
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
"scroll-target",
|
|
37
37
|
"frame",
|
|
38
38
|
"app-shell",
|
|
39
|
-
"
|
|
39
|
+
"app-layout"
|
|
40
40
|
]
|
|
41
41
|
},
|
|
42
42
|
{
|
|
@@ -45,6 +45,7 @@
|
|
|
45
45
|
"pages": [
|
|
46
46
|
"accordion",
|
|
47
47
|
"alert",
|
|
48
|
+
"app-navigation",
|
|
48
49
|
"aura",
|
|
49
50
|
"avatar",
|
|
50
51
|
"badge",
|
|
@@ -55,13 +56,17 @@
|
|
|
55
56
|
"dialog",
|
|
56
57
|
"drawer",
|
|
57
58
|
"fab",
|
|
59
|
+
"indicator",
|
|
58
60
|
"key",
|
|
61
|
+
"list",
|
|
59
62
|
"meter",
|
|
60
63
|
"navbar",
|
|
61
64
|
"pagination",
|
|
62
65
|
"progress",
|
|
66
|
+
"rating",
|
|
63
67
|
"skeleton",
|
|
64
68
|
"spinner",
|
|
69
|
+
"steps",
|
|
65
70
|
"table"
|
|
66
71
|
]
|
|
67
72
|
},
|
|
@@ -95,6 +100,7 @@
|
|
|
95
100
|
"actions",
|
|
96
101
|
"navigation-list",
|
|
97
102
|
"icon-input",
|
|
103
|
+
"icon-slot",
|
|
98
104
|
"structured-lists",
|
|
99
105
|
"hierarchical-nav",
|
|
100
106
|
"code-block",
|
|
@@ -14,6 +14,11 @@
|
|
|
14
14
|
- Use `.sm` or `.lg` for density changes. The inline padding stays stable.
|
|
15
15
|
- Use `.alert-dismiss` for a compact dismiss button. It is a direct trailing child in standard alerts and lives inside `.alert-title` in admonitions. It uses the shared `--icon-close` mask and the `--dismiss` runtime command — no icon font or custom JS.
|
|
16
16
|
|
|
17
|
+
Because alerts are soft by default, adding `.soft` to an intent (`.alert.soft.primary`,
|
|
18
|
+
`.alert.soft.danger`, …) is a no-op: it already resolves to a soft version of that
|
|
19
|
+
intent. Reach for `.solid` (stronger) or `.outline` (quieter) when the emphasis
|
|
20
|
+
itself needs to change, not `.soft`.
|
|
21
|
+
|
|
17
22
|
## Class reference
|
|
18
23
|
|
|
19
24
|
| Class | Kind | Description |
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# App Navigation
|
|
2
|
+
|
|
3
|
+
> Primary application destinations in a persistent bottom bar or labelled side navigation.
|
|
4
|
+
|
|
5
|
+
Use one `.app-nav` landmark for the application's primary destinations. Links
|
|
6
|
+
remain links, and `aria-current="page"` is the only current-page state.
|
|
7
|
+
|
|
8
|
+
```html demo
|
|
9
|
+
<nav class="app-nav" aria-label="Primary">
|
|
10
|
+
<a href="#today" aria-current="page">
|
|
11
|
+
<span aria-hidden="true">☀</span>
|
|
12
|
+
<span>Today</span>
|
|
13
|
+
</a>
|
|
14
|
+
<a href="#tasks">
|
|
15
|
+
<span aria-hidden="true">✓</span>
|
|
16
|
+
<span>Tasks</span>
|
|
17
|
+
</a>
|
|
18
|
+
<a href="#settings">
|
|
19
|
+
<span aria-hidden="true">⚙</span>
|
|
20
|
+
<span>Settings</span>
|
|
21
|
+
</a>
|
|
22
|
+
</nav>
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
On its own, `.app-nav` is a sticky bottom bar. Compose it as a direct child of
|
|
26
|
+
`.app-layout` to let the layout turn the same landmark into side navigation when space
|
|
27
|
+
permits. The component contains no JavaScript and does not manage routing.
|
|
28
|
+
The icon slot accepts SVGs, images, and decorative elements such as icon-font
|
|
29
|
+
`i` elements marked with `aria-hidden="true"`.
|
|
30
|
+
|
|
31
|
+
### Hooks
|
|
32
|
+
|
|
33
|
+
- `--app-nav-size` controls the minimum bottom-bar size.
|
|
34
|
+
- `--app-nav-gap` controls spacing between destinations.
|
|
@@ -41,6 +41,16 @@
|
|
|
41
41
|
The avatar keeps the same visual box across semantics — as a link or button it
|
|
42
42
|
gains hover and pressed feedback.
|
|
43
43
|
|
|
44
|
+
For an accessible initials label, prefer `<abbr title="Full name">` so the
|
|
45
|
+
expansion is available to assistive tech; the UA dotted underline it would draw
|
|
46
|
+
is suppressed inside the avatar.
|
|
47
|
+
|
|
48
|
+
```html demo
|
|
49
|
+
<div class="avatar" role="img">
|
|
50
|
+
<abbr title="Ada Meridian">AM</abbr>
|
|
51
|
+
</div>
|
|
52
|
+
```
|
|
53
|
+
|
|
44
54
|
```html demo
|
|
45
55
|
<a href="#" class="avatar">
|
|
46
56
|
<img src="https://mockmind-api.uifaces.co/content/human/219.jpg" alt="Jane Doe" />
|
|
@@ -77,6 +77,8 @@ The dot conveys no text, so an `aria-label` is required for it to mean anything.
|
|
|
77
77
|
## Removable tag pattern
|
|
78
78
|
|
|
79
79
|
Use `.badge soft` for tag visuals. Add a direct dismiss button only when the tag can actually be removed. There is no separate chip component.
|
|
80
|
+
For a compact action or filter, use `.btn.sm` instead; badges describe content
|
|
81
|
+
and must not be turned into toggle controls merely to obtain a compact shape.
|
|
80
82
|
|
|
81
83
|
```html demo
|
|
82
84
|
<span class="badge primary soft">
|
|
@@ -61,6 +61,18 @@ foreground by default, including on a contrasting `.inverted` surface. A local
|
|
|
61
61
|
intent class still wins: `.danger.outline` uses the danger color in either
|
|
62
62
|
context. Filled buttons own their surface and keep their normal intent recipe.
|
|
63
63
|
|
|
64
|
+
`.btn.link` is a link-style action: it keeps button behavior and states, but
|
|
65
|
+
remains intrinsically sized like text instead of stretching with its layout
|
|
66
|
+
container. For a plain text link, use a bare `<a>` with no class.
|
|
67
|
+
|
|
68
|
+
```html demo
|
|
69
|
+
<div class="cluster">
|
|
70
|
+
<a href="#">Normal link</a>
|
|
71
|
+
<a class="btn link" href="#">Button-like link</a>
|
|
72
|
+
<button class="btn link" type="button">Button with link appearance</button>
|
|
73
|
+
</div>
|
|
74
|
+
```
|
|
75
|
+
|
|
64
76
|
```html demo
|
|
65
77
|
<div class="cluster">
|
|
66
78
|
<button class="btn outline" type="button">Outline</button>
|
|
@@ -117,6 +129,24 @@ change typography.
|
|
|
117
129
|
|
|
118
130
|
## States
|
|
119
131
|
|
|
132
|
+
### Compact filters and actions
|
|
133
|
+
|
|
134
|
+
A compact filter is still a button, not a separate chip semantic. Compose the
|
|
135
|
+
small size with `aria-pressed`; application code owns the state change.
|
|
136
|
+
|
|
137
|
+
```html demo
|
|
138
|
+
<div class="cluster" aria-label="Task filters">
|
|
139
|
+
<button class="btn sm" type="button" aria-pressed="true">All</button>
|
|
140
|
+
<button class="btn sm outline" type="button" aria-pressed="false">Open</button>
|
|
141
|
+
<button class="btn sm outline" type="button" aria-pressed="false">Done</button>
|
|
142
|
+
</div>
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
Use `.badge` for a passive category or count and the removable-tag composition
|
|
146
|
+
when a displayed value needs its own remove action. Actual intentionally has no
|
|
147
|
+
generic `.chip` component: action, selection, and information retain their HTML
|
|
148
|
+
semantics.
|
|
149
|
+
|
|
120
150
|
### Disabled
|
|
121
151
|
|
|
122
152
|
Disable a `<button>` or `<input>` with the native `disabled` attribute. Anchors
|
|
@@ -18,7 +18,10 @@ Use semantic elements inside the card. A direct `<header>` or `<footer>` is the
|
|
|
18
18
|
card's structural slot. A direct `<footer>` is anchored to the bottom of the
|
|
19
19
|
card when extra block space is available, so prices and actions line up across
|
|
20
20
|
equal-height cards. Cards do not own page spacing — compose them with layout
|
|
21
|
-
primitives like `.grid`.
|
|
21
|
+
primitives like `.grid`. A bare card owns the rhythm between its direct
|
|
22
|
+
children through `--card-gap`; applying a layout primitive such as `.stack` or
|
|
23
|
+
`.media` to the same element gives that primitive control of the layout and
|
|
24
|
+
gap.
|
|
22
25
|
|
|
23
26
|
```html demo
|
|
24
27
|
<div style="max-inline-size: 32rem">
|
|
@@ -197,3 +200,4 @@ variants.
|
|
|
197
200
|
- `--card-radius` — corner radius.
|
|
198
201
|
- `--card-max-inline-size` — maximum width.
|
|
199
202
|
- `--card-pad` — inner padding; also drives the negative offsets that let a `.bleed` child reach the card edge. `.compact` lowers it.
|
|
203
|
+
- `--card-gap` — space between direct children of a bare card. A composed layout primitive owns its own gap instead. `.compact` does not change this hook.
|
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
|
|
5
5
|
FAB fixes a primary action near the viewport's block-end and inline-end edges.
|
|
6
6
|
The existing button component owns its size, shape, intent, and interaction.
|
|
7
|
+
As a direct child of `.app-layout`, it instead overlays the main grid area so
|
|
8
|
+
the layout keeps it clear of adaptive application navigation and safe areas.
|
|
7
9
|
|
|
8
10
|
```html
|
|
9
11
|
<div class="fab">
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# Indicator
|
|
2
|
+
|
|
3
|
+
> Attach any component to one of a container's four corners.
|
|
4
|
+
|
|
5
|
+
`.indicator` creates the positioning context and `.indicator-item` positions
|
|
6
|
+
its child at the top-end corner. The primitive does not add a background,
|
|
7
|
+
size, border, or contrast ring: compose it with `.badge` or another component
|
|
8
|
+
that owns its appearance.
|
|
9
|
+
|
|
10
|
+
## Class reference
|
|
11
|
+
|
|
12
|
+
| Class | Kind | Description |
|
|
13
|
+
|-------------------|-----------|--------------------------------------------------|
|
|
14
|
+
| `.indicator` | Component | Positioning context for an overlaid child. |
|
|
15
|
+
| `.indicator-item` | Part | Item attached to the top-end corner by default. |
|
|
16
|
+
| `.start` | Placement | Moves the item to the inline-start corner. |
|
|
17
|
+
| `.bottom` | Placement | Moves the item to the bottom corner. |
|
|
18
|
+
|
|
19
|
+
Combine `.bottom.start` for bottom-start. Logical inline placement follows the
|
|
20
|
+
document direction automatically.
|
|
21
|
+
|
|
22
|
+
```html demo
|
|
23
|
+
<div class="cluster" style="padding: var(--space-40);">
|
|
24
|
+
<button class="btn indicator" type="button">
|
|
25
|
+
Inbox
|
|
26
|
+
<span class="indicator-item badge danger solid">3</span>
|
|
27
|
+
</button>
|
|
28
|
+
|
|
29
|
+
<article class="card indicator" style="max-inline-size: 18rem;">
|
|
30
|
+
<span class="indicator-item start badge success">New</span>
|
|
31
|
+
<h2>Quarterly report</h2>
|
|
32
|
+
<p>The latest figures are ready to review.</p>
|
|
33
|
+
</article>
|
|
34
|
+
</div>
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Four corners
|
|
38
|
+
|
|
39
|
+
```html demo
|
|
40
|
+
<div class="indicator card" style="inline-size: 12rem; min-block-size: 7rem; margin: var(--space-40);">
|
|
41
|
+
<span class="indicator-item badge solid" aria-label="Top end">TE</span>
|
|
42
|
+
<span class="indicator-item start badge solid" aria-label="Top start">TS</span>
|
|
43
|
+
<span class="indicator-item bottom badge solid" aria-label="Bottom end">BE</span>
|
|
44
|
+
<span class="indicator-item bottom start badge solid" aria-label="Bottom start">BS</span>
|
|
45
|
+
<p class="muted">Four logical corners</p>
|
|
46
|
+
</div>
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
The item extends beyond the container edge. An ancestor with clipped overflow
|
|
50
|
+
can cut it off; leave overflow visible or provide enough inset space in that
|
|
51
|
+
composition. Avatar status dots keep their dedicated circle-aware placement
|
|
52
|
+
and contrast ring; continue to use `.avatar > .badge:empty` for that pattern.
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# List
|
|
2
|
+
|
|
3
|
+
> Repeated application rows with optional leading and trailing regions.
|
|
4
|
+
|
|
5
|
+
Use `.list` when a collection repeatedly needs leading content, flexible text,
|
|
6
|
+
and trailing metadata or controls. Use `.media` for a single two-part
|
|
7
|
+
media/content relationship, `.nav-list` for navigation structure, and native
|
|
8
|
+
list markers when order or bullets carry meaning.
|
|
9
|
+
|
|
10
|
+
```html demo
|
|
11
|
+
<ul class="list">
|
|
12
|
+
<li class="list-item">
|
|
13
|
+
<span class="avatar sm"><abbr>AM</abbr></span>
|
|
14
|
+
<span class="list-item-content">
|
|
15
|
+
<strong class="list-item-title">Ada Morgan</strong>
|
|
16
|
+
<span class="list-item-text">Design systems · Brussels</span>
|
|
17
|
+
</span>
|
|
18
|
+
<span class="badge success">Online</span>
|
|
19
|
+
</li>
|
|
20
|
+
<li class="list-item">
|
|
21
|
+
<input class="check" type="checkbox" aria-label="Select quarterly review">
|
|
22
|
+
<span class="list-item-content">
|
|
23
|
+
<strong class="list-item-title">Quarterly review</strong>
|
|
24
|
+
<span class="list-item-text">Due tomorrow</span>
|
|
25
|
+
</span>
|
|
26
|
+
<time class="list-item-trailing" datetime="09:30">09:30</time>
|
|
27
|
+
</li>
|
|
28
|
+
</ul>
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Leading and trailing content are unconstrained: avatars, native choices,
|
|
32
|
+
badges, text, switches, and buttons retain their own component sizing. The
|
|
33
|
+
middle region has no minimum content floor, so supporting text and long tokens
|
|
34
|
+
wrap instead of widening the page.
|
|
35
|
+
|
|
36
|
+
For a navigable row, keep the list semantics and put `.list-item` on the link:
|
|
37
|
+
|
|
38
|
+
```html demo
|
|
39
|
+
<ul class="list">
|
|
40
|
+
<li>
|
|
41
|
+
<a class="list-item" href="#profile">
|
|
42
|
+
<span aria-hidden="true">👤</span>
|
|
43
|
+
<span class="list-item-content">
|
|
44
|
+
<strong class="list-item-title">Profile</strong>
|
|
45
|
+
<span class="list-item-text">Name, photo, and contact details</span>
|
|
46
|
+
</span>
|
|
47
|
+
<span class="list-item-trailing" aria-hidden="true">›</span>
|
|
48
|
+
</a>
|
|
49
|
+
</li>
|
|
50
|
+
</ul>
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Do not attach click JavaScript to the entire row. Use a link for navigation and
|
|
54
|
+
a button or native control for actions.
|
|
55
|
+
|
|
56
|
+
### Hooks
|
|
57
|
+
|
|
58
|
+
- `--list-item-min-size` controls the row's minimum touch height.
|
|
59
|
+
- `--list-item-pad-block` and `--list-item-pad-inline` control row padding.
|
|
60
|
+
- `--list-item-gap` controls spacing between the three regions.
|
|
61
|
+
- `--list-divider` controls the leading and inter-row dividers. The final row
|
|
62
|
+
deliberately has no trailing divider.
|
|
@@ -8,6 +8,10 @@
|
|
|
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
10
|
|
|
11
|
+
For a public or normally scrolling page, `.navbar` belongs inside the semantic
|
|
12
|
+
site header. Do not use `.topbar` or `.app-layout` unless the page actually has
|
|
13
|
+
the specialized persistent application-shell behavior those APIs describe.
|
|
14
|
+
|
|
11
15
|
`.navbar-nav` and `.nav-list` are mutually exclusive: `.navbar-nav` is a horizontal
|
|
12
16
|
flex list, `.nav-list` a vertical grid. Putting both on one element resolves to
|
|
13
17
|
`.nav-list` (vertical) — pick one per container.
|
|
@@ -36,6 +40,8 @@ flex list, `.nav-list` a vertical grid. Putting both on one element resolves to
|
|
|
36
40
|
```
|
|
37
41
|
|
|
38
42
|
For a vertical sidebar nav, use `.nav-list` inside the drawer or sidebar.
|
|
43
|
+
Links inside `.nav-list` stretch to the available inline size, providing a
|
|
44
|
+
full-row hit target for vertical navigation.
|
|
39
45
|
|
|
40
46
|
## Responsive: navbar + drawer
|
|
41
47
|
|