@junoput01/junoui 0.2.0 → 0.4.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 +251 -0
- package/README.md +23 -19
- package/dist/css/juno-custom-media.css +32 -0
- package/dist/css/juno.css +1355 -40
- package/dist/icons/inline.js +22 -0
- package/docs/accessibility.md +188 -0
- package/docs/boot-shell.md +295 -0
- package/docs/components/README.md +71 -0
- package/docs/components/accordion.md +49 -0
- package/docs/components/alert.md +46 -0
- package/docs/components/avatar.md +45 -0
- package/docs/components/badge.md +35 -0
- package/docs/components/breadcrumb.md +31 -0
- package/docs/components/button.md +32 -0
- package/docs/components/card.md +41 -0
- package/docs/components/checkbox.md +41 -0
- package/docs/components/chip.md +44 -0
- package/docs/components/divider.md +32 -0
- package/docs/components/dock.md +215 -0
- package/docs/components/drawer.md +100 -0
- package/docs/components/field.md +44 -0
- package/docs/components/fold-slot.md +31 -0
- package/docs/components/gauge.md +48 -0
- package/docs/components/icon-loader.md +122 -0
- package/docs/components/icon.md +70 -0
- package/docs/components/input.md +45 -0
- package/docs/components/list.md +76 -0
- package/docs/components/load-state.md +131 -0
- package/docs/components/loader.md +88 -0
- package/docs/components/menu.md +54 -0
- package/docs/components/modal.md +52 -0
- package/docs/components/navbar.md +53 -0
- package/docs/components/pagination.md +42 -0
- package/docs/components/pillbar.md +211 -0
- package/docs/components/popover.md +43 -0
- package/docs/components/rail.md +54 -0
- package/docs/components/readout.md +39 -0
- package/docs/components/reload.md +41 -0
- package/docs/components/segmented.md +46 -0
- package/docs/components/select.md +33 -0
- package/docs/components/skeleton.md +45 -0
- package/docs/components/slider.md +48 -0
- package/docs/components/spark.md +51 -0
- package/docs/components/status.md +30 -0
- package/docs/components/stepper.md +46 -0
- package/docs/components/switch.md +38 -0
- package/docs/components/table.md +126 -0
- package/docs/components/tabs.md +61 -0
- package/docs/components/thumb.md +81 -0
- package/docs/components/toast.md +48 -0
- package/docs/components/toggle-button.md +40 -0
- package/docs/components/tooltip.md +63 -0
- package/docs/design-guidelines.md +173 -0
- package/docs/flutter.md +42 -0
- package/docs/getting-started.md +93 -0
- package/docs/integration.md +95 -0
- package/docs/ios-conformance.md +224 -0
- package/docs/layout.md +271 -0
- package/docs/native.md +52 -0
- package/docs/roadmap.md +106 -0
- package/docs/tokens-reference.md +223 -0
- package/docs/web.md +95 -0
- package/package.json +7 -3
- package/src/css/base.css +171 -1
- package/src/css/components/dock.css +335 -0
- package/src/css/components/drawer.css +40 -3
- package/src/css/components/fold-slot.css +44 -0
- package/src/css/components/icon-loader.css +67 -0
- package/src/css/components/icon.css +6 -4
- package/src/css/components/load-state.css +136 -0
- package/src/css/components/loader.css +6 -0
- package/src/css/components/menu.css +4 -0
- package/src/css/components/modal.css +24 -3
- package/src/css/components/navbar.css +5 -1
- package/src/css/components/pillbar.css +222 -7
- package/src/css/components/rail.css +8 -0
- package/src/css/components/reload.css +48 -0
- package/src/css/components/skeleton.css +41 -13
- package/src/css/components/tabs.css +5 -0
- package/src/css/components/thumb.css +63 -1
- package/src/css/components/toast.css +5 -1
- package/src/css/density.css +22 -0
- package/src/css/layout.css +95 -5
- package/src/css/utilities.css +4 -1
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# Input / textarea
|
|
2
|
+
|
|
3
|
+
Text entry — inputs are precise. **Mono by default** (codes, IDs, tokens, values);
|
|
4
|
+
add `.juno-input--sans` for free text. One class for `<input>` and `<textarea>`.
|
|
5
|
+
|
|
6
|
+
## Web
|
|
7
|
+
|
|
8
|
+
```html
|
|
9
|
+
<input class="juno-input" type="text" value="UAL 519" />
|
|
10
|
+
<!-- mono code -->
|
|
11
|
+
<input class="juno-input juno-input--sans" placeholder="Type to edit…" />
|
|
12
|
+
<!-- free text -->
|
|
13
|
+
<input class="juno-input juno--target" value="38000" />
|
|
14
|
+
<!-- managed -->
|
|
15
|
+
<input class="juno-input" aria-invalid="true" value="7700" />
|
|
16
|
+
<!-- error -->
|
|
17
|
+
<input class="juno-input" disabled value="LOCKED" />
|
|
18
|
+
<textarea class="juno-input juno-input--sans" rows="4"></textarea>
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
| Class / attr | Effect |
|
|
22
|
+
| ------------------- | ------------------------------------------------------------------------------------ |
|
|
23
|
+
| `.juno-input` | Box: `control-surface` bg, `control-edge` border, radius `4`, density padding. Mono. |
|
|
24
|
+
| `.juno-input--sans` | Sans font for free-text fields. |
|
|
25
|
+
| `.juno--<role>` | Re-colors the border (e.g. `target` for managed values). |
|
|
26
|
+
| `[aria-invalid]` | `warning` border + text, tied to the a11y attribute. |
|
|
27
|
+
| `:disabled` | `s1` bg, `muted` text, not-allowed. |
|
|
28
|
+
|
|
29
|
+
`--juno-role` defaults to `control-edge`, so the resting border is quiet; a role class
|
|
30
|
+
or `aria-invalid` overrides it. **Focus** = `active` border + a 1px ring (replaces the
|
|
31
|
+
base outline on inputs).
|
|
32
|
+
|
|
33
|
+
## Anatomy (any platform)
|
|
34
|
+
|
|
35
|
+
- **Control surface:** on dark panels the hairline border vanishes, so the edge lifts
|
|
36
|
+
to `muted` on an `s2` field; light mode keeps the true `border` on `s0`
|
|
37
|
+
(`--juno-control-surface` / `--juno-control-edge`).
|
|
38
|
+
- Padding `--juno-pad-control-block` × `space.12` (density-aware). Radius `4`. Min
|
|
39
|
+
height `size.tap.min`. Font `font.size.14`, `data`; placeholder `muted`.
|
|
40
|
+
|
|
41
|
+
## Usage
|
|
42
|
+
|
|
43
|
+
- Mono is the default because most technical entry is codes/values; opt into `--sans`
|
|
44
|
+
for names and remarks. Textarea is usually `--sans`.
|
|
45
|
+
- Wrap in [`.juno-field`](./field.md) for label + help/error.
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# List
|
|
2
|
+
|
|
3
|
+
Grouped rows for vertical data organization — the settings-screen pattern.
|
|
4
|
+
Labeled groups of full-width rows; each row carries a leading icon, a label
|
|
5
|
+
(with optional support line), and a trailing value, control, or drill-in
|
|
6
|
+
chevron. The phone-first sibling of the [table](./table.md): use a list when
|
|
7
|
+
each record is a _destination or setting_, a table when you compare columns.
|
|
8
|
+
|
|
9
|
+
## Web
|
|
10
|
+
|
|
11
|
+
```html
|
|
12
|
+
<section class="juno-list">
|
|
13
|
+
<h3 class="juno-list__header">Playback</h3>
|
|
14
|
+
<ul class="juno-list__group">
|
|
15
|
+
<li>
|
|
16
|
+
<a class="juno-list__row" href="/settings/quality">
|
|
17
|
+
<svg class="juno-icon juno-list__icon" aria-hidden="true">
|
|
18
|
+
<use href="…#juno-i-sliders" />
|
|
19
|
+
</svg>
|
|
20
|
+
<span class="juno-list__main">
|
|
21
|
+
<span class="juno-list__label">Quality</span>
|
|
22
|
+
<span class="juno-list__support">Streaming resolution</span>
|
|
23
|
+
</span>
|
|
24
|
+
<span class="juno-list__value">1080p</span>
|
|
25
|
+
<svg class="juno-icon juno-list__chevron" aria-hidden="true">
|
|
26
|
+
<use href="…#juno-i-caret-right" />
|
|
27
|
+
</svg>
|
|
28
|
+
</a>
|
|
29
|
+
</li>
|
|
30
|
+
<li>
|
|
31
|
+
<div class="juno-list__row">
|
|
32
|
+
<span class="juno-list__main"><span class="juno-list__label">Autoplay</span></span>
|
|
33
|
+
<label class="juno-switch juno-switch--sm"
|
|
34
|
+
><input class="juno-switch__input" type="checkbox" role="switch" checked /><span
|
|
35
|
+
class="juno-switch__track"
|
|
36
|
+
></span
|
|
37
|
+
></label>
|
|
38
|
+
</div>
|
|
39
|
+
</li>
|
|
40
|
+
</ul>
|
|
41
|
+
</section>
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
| Class | Effect |
|
|
45
|
+
| --------------------- | ------------------------------------------------------------------- |
|
|
46
|
+
| `.juno-list` | Stack of header + group(s). |
|
|
47
|
+
| `.juno-list__header` | Uppercase group label above the group. |
|
|
48
|
+
| `.juno-list__group` | `s1` card frame; rows divided by hairlines, corners rounded. |
|
|
49
|
+
| `.juno-list__row` | Row ≥ `size.tap.comfortable`; `<a>`/`<button>` rows get hover. |
|
|
50
|
+
| `.juno-list__icon` | Leading glyph, label-toned. |
|
|
51
|
+
| `.juno-list__main` | Label + optional support line; truncates, takes the flexible space. |
|
|
52
|
+
| `.juno-list__label` | Primary text. |
|
|
53
|
+
| `.juno-list__support` | Secondary line under the label. |
|
|
54
|
+
| `.juno-list__value` | Trailing mono value. |
|
|
55
|
+
| `.juno-list__chevron` | Drill-in caret (`caret-right`), muted. |
|
|
56
|
+
|
|
57
|
+
## Anatomy (any platform)
|
|
58
|
+
|
|
59
|
+
- Group = `s1` surface, hairline `border`, `radius.8`; rows split by hairlines
|
|
60
|
+
drawn between rows only (no doubled edges).
|
|
61
|
+
- Row ≥ `size.tap.comfortable` tall, `space.16` inline padding, `space.12`
|
|
62
|
+
gap between slots. Label `font.size.13` data-toned; support `font.size.11`
|
|
63
|
+
label-toned; value mono `font.size.12`.
|
|
64
|
+
- Only interactive rows respond: hover/press fills `s2`.
|
|
65
|
+
|
|
66
|
+
## Usage
|
|
67
|
+
|
|
68
|
+
- Trailing slot takes whatever fits the row's job: a mono value, a
|
|
69
|
+
[badge](./badge.md), a [switch](./switch.md), or the chevron when the row
|
|
70
|
+
drills into a detail view (pair the pushed view with a
|
|
71
|
+
[navbar](./navbar.md) back control).
|
|
72
|
+
- A chevron row must be an `<a>` (or `<button>`) — the whole row is the tap
|
|
73
|
+
target, never just the caret.
|
|
74
|
+
- Static key→value screens can skip `<ul>`/`<li>` and stack `div.juno-list__row`
|
|
75
|
+
directly inside `.juno-list__group`; keep list semantics when rows navigate.
|
|
76
|
+
- Don't put two controls in one row — a row is one target. Split it.
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
# Load state
|
|
2
|
+
|
|
3
|
+
A vocabulary for every way a load can go, so nothing spins forever. junoui already had
|
|
4
|
+
"resolving" (arc/beacon/bar, both indeterminate and determinate) and "layout known, not
|
|
5
|
+
loaded yet" ([skeleton](./skeleton.md)). This adds the two treatments those don't cover —
|
|
6
|
+
**working with no ETA** and **failed** — plus a table-agnostic **empty** state and an
|
|
7
|
+
optional CSS-only switch to show exactly one at a time. Zero JS: junoui ships the look and
|
|
8
|
+
the ARIA contract per state; deciding _when_ a load becomes a fault or an empty result is
|
|
9
|
+
the app's job.
|
|
10
|
+
|
|
11
|
+
## The decision table
|
|
12
|
+
|
|
13
|
+
| State | Treatment | Why |
|
|
14
|
+
| ------------------------------- | ------------------------- | ------------------------------------------------------------ |
|
|
15
|
+
| No data yet, about to fetch | `.juno-beacon` | "no bytes yet" — a pulse, not a promise of progress. |
|
|
16
|
+
| Fetching, will resolve | `.juno-arc` / `.juno-bar` | Real progress or a bounded wait — see [loader](./loader.md). |
|
|
17
|
+
| Layout known, content pending | `.juno-skeleton` | Placeholder mirrors the shape of what's coming. |
|
|
18
|
+
| Server working, **no ETA** | `.juno-shimmer` | Motion _without_ a completion promise. |
|
|
19
|
+
| Failed — **terminal** | `.juno-fault` | Static. A spinner on a 404 spins forever. |
|
|
20
|
+
| Legitimately nothing — terminal | `.juno-empty` | Static. Not a failure — don't tint it like one. |
|
|
21
|
+
|
|
22
|
+
`.juno-shimmer` and `.juno-fault` are the two treatments this ticket adds; `.juno-empty` is
|
|
23
|
+
the generalized, table-agnostic form of the existing `.juno-table__empty` (same anatomy,
|
|
24
|
+
usable outside a table).
|
|
25
|
+
|
|
26
|
+
## Web
|
|
27
|
+
|
|
28
|
+
### Shimmer — work in progress, no completion promise
|
|
29
|
+
|
|
30
|
+
```html
|
|
31
|
+
<div class="juno-shimmer" style="block-size: 120px;" aria-busy="true" aria-live="polite"></div>
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Shares `.juno-skeleton`'s compositor-only band (same `@keyframes juno-skeleton-shimmer`,
|
|
35
|
+
defined once in `skeleton.css`) — one shimmer implementation, applied to a solid fill
|
|
36
|
+
instead of a placeholder shape. Override cadence with `--juno-shimmer-dur` (default
|
|
37
|
+
`1.4s`).
|
|
38
|
+
|
|
39
|
+
### Fault — terminal, never animates
|
|
40
|
+
|
|
41
|
+
```html
|
|
42
|
+
<div class="juno-fault" role="status">
|
|
43
|
+
<span class="juno-fault__icon" aria-hidden="true">!</span>
|
|
44
|
+
<p>Couldn't load this image.</p>
|
|
45
|
+
</div>
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Role defaults to `caution`; add a `.juno--<role>` class to recolor (`.juno--warning` for a
|
|
49
|
+
harder failure) — never hardcode a color. `role="status"` on the fault itself is a
|
|
50
|
+
per-region, polite announcement (this fired once, doesn't need to interrupt); for a
|
|
51
|
+
page-level urgent message use [`.juno-alert`](./alert.md) with `role="alert"` instead.
|
|
52
|
+
|
|
53
|
+
### Empty — legitimately nothing, terminal
|
|
54
|
+
|
|
55
|
+
```html
|
|
56
|
+
<div class="juno-empty">
|
|
57
|
+
<span class="juno-empty__icon" aria-hidden="true">∅</span>
|
|
58
|
+
<p>Nothing here yet.</p>
|
|
59
|
+
</div>
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### The switch (optional)
|
|
63
|
+
|
|
64
|
+
CSS-only, one visible child at a time. The app sets `data-juno-state` on the parent as
|
|
65
|
+
its fetch/rendition state changes; junoui does no state inference.
|
|
66
|
+
|
|
67
|
+
```html
|
|
68
|
+
<div class="juno-state" data-juno-state="processing">
|
|
69
|
+
<div data-juno-when="loading">
|
|
70
|
+
<div class="juno-arc juno-arc--indeterminate" role="status" aria-label="Loading"></div>
|
|
71
|
+
</div>
|
|
72
|
+
<div data-juno-when="processing" aria-busy="true" aria-live="polite">
|
|
73
|
+
<div class="juno-shimmer" style="block-size: 120px;"></div>
|
|
74
|
+
</div>
|
|
75
|
+
<div data-juno-when="error">
|
|
76
|
+
<div class="juno-fault" role="status">
|
|
77
|
+
<span class="juno-fault__icon" aria-hidden="true">!</span>
|
|
78
|
+
<p>Couldn't load this.</p>
|
|
79
|
+
</div>
|
|
80
|
+
</div>
|
|
81
|
+
<div data-juno-when="empty">
|
|
82
|
+
<div class="juno-empty">
|
|
83
|
+
<span class="juno-empty__icon" aria-hidden="true">∅</span>
|
|
84
|
+
<p>Nothing here yet.</p>
|
|
85
|
+
</div>
|
|
86
|
+
</div>
|
|
87
|
+
</div>
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
| Class / attribute | Effect |
|
|
91
|
+
| ----------------------------------- | ------------------------------------------------------------------------------------------------------------- |
|
|
92
|
+
| `.juno-state` | Switch container. |
|
|
93
|
+
| `[data-juno-when]` | A wrapper hidden by default; shown when its (space-separated) token list contains the parent's current state. |
|
|
94
|
+
| `.juno-state[data-juno-state='…']` | Set by the app: `loading` \| `processing` \| `error` \| `empty`. |
|
|
95
|
+
| `.juno-shimmer` | Compositor-only shimmer band on a solid fill (shares skeleton's `@keyframes`). |
|
|
96
|
+
| `--juno-shimmer-dur` | Shimmer cycle length (default `1.4s`). |
|
|
97
|
+
| `.juno-fault` / `.juno-fault__icon` | Terminal failure card. Role `caution` by default. |
|
|
98
|
+
| `.juno-empty` / `.juno-empty__icon` | Terminal empty-result card. Neutral (`label`/`muted`), same anatomy as `.juno-table__empty`. |
|
|
99
|
+
|
|
100
|
+
## Anatomy (any platform)
|
|
101
|
+
|
|
102
|
+
- Shimmer: solid `s2` fill, `s3` highlight band at 70% opacity sliding on `transform`
|
|
103
|
+
only, 1.4s ease-in-out loop — identical mechanism to skeleton, different semantic use.
|
|
104
|
+
- Fault: column, centered, `space.8` gap, `space.24` padding, role-colored text and icon
|
|
105
|
+
outline (`radius.8` box, `border.width.1`). Never animates.
|
|
106
|
+
- Empty: column, centered, `space.12` gap, `space.56`/`space.24` padding, `label`/`muted`
|
|
107
|
+
text and icon outline (`radius.8` box). Never animates.
|
|
108
|
+
|
|
109
|
+
## Usage
|
|
110
|
+
|
|
111
|
+
- **Shimmer vs. skeleton:** skeleton implies a known layout waiting for content (first
|
|
112
|
+
paint); shimmer implies ongoing server-side work with no bound on when it finishes
|
|
113
|
+
(transcoding, indexing, re-encoding). Don't use skeleton for the latter — the moment a
|
|
114
|
+
skeleton is on screen, the layout it promises should be about to land.
|
|
115
|
+
Reference: `web/src/media/loadview.ts` (nexora) — spinner only while nothing has
|
|
116
|
+
started, shimmer for `processing`, static fault for `error`; "a spinner on a 404 spins
|
|
117
|
+
forever."
|
|
118
|
+
- **Fault vs. alert:** a fault is a _region_ replacing content that failed to load
|
|
119
|
+
(an image tile, a card). An [alert](./alert.md) is a page-level message. Don't reach for
|
|
120
|
+
`.juno-alert` inside a grid cell.
|
|
121
|
+
- **Empty is not a failure:** legitimately-zero-results (an empty search, a fresh account)
|
|
122
|
+
is a distinct terminal state from an error — keep it neutral, offer a next action
|
|
123
|
+
(a button/link) rather than an apology.
|
|
124
|
+
- **The switch is optional.** Nothing stops rendering `.juno-shimmer` / `.juno-fault` /
|
|
125
|
+
`.juno-empty` directly and swapping them with app-side conditional rendering; `.juno-state`
|
|
126
|
+
exists for markup that's easier to keep all-present (e.g. server-rendered) and toggle
|
|
127
|
+
by attribute.
|
|
128
|
+
- **a11y, per state:** `loading`/`processing` → `aria-busy="true"` (+ `aria-live="polite"`
|
|
129
|
+
if the region isn't otherwise announced); `.juno-fault` → `role="status"` on itself;
|
|
130
|
+
`.juno-empty` → no extra role, it's static content. See
|
|
131
|
+
[accessibility.md](../accessibility.md) for the full per-component ARIA contract.
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# Loaders (arc · beacon · linear)
|
|
2
|
+
|
|
3
|
+
Three progress indicators, each in an **indeterminate** form (loops on its own,
|
|
4
|
+
CSS only) and a **determinate** form (tracks real progress).
|
|
5
|
+
|
|
6
|
+
## Progress API
|
|
7
|
+
|
|
8
|
+
Determinate variants read `--juno-progress` (0–100). Set it inline or from JS:
|
|
9
|
+
|
|
10
|
+
```js
|
|
11
|
+
el.style.setProperty('--juno-progress', 63);
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
Color comes from `--juno-role` (default `active`); add a `.juno--<role>` class to recolor.
|
|
15
|
+
|
|
16
|
+
## Arc — circular ring
|
|
17
|
+
|
|
18
|
+
```html
|
|
19
|
+
<!-- indeterminate -->
|
|
20
|
+
<div class="juno-arc juno-arc--indeterminate"></div>
|
|
21
|
+
|
|
22
|
+
<!-- determinate (label as a sibling so the ring mask doesn't clip it) -->
|
|
23
|
+
<div style="position:relative;width:76px;height:76px;">
|
|
24
|
+
<div class="juno-arc" style="--juno-progress:63;"></div>
|
|
25
|
+
<span class="juno-arc__label">63%</span>
|
|
26
|
+
</div>
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
| Class / var | Effect |
|
|
30
|
+
| --------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
|
|
31
|
+
| `.juno-arc` | Ring; determinate sweep = progress (conic gradient + ring mask). |
|
|
32
|
+
| `.juno-arc--indeterminate` | 12-step mechanical rotation. |
|
|
33
|
+
| `.juno-arc--smooth` | With `--indeterminate`: continuous (linear) rotation instead of the 12-step sweep — use under ~24px, where the steps read as jitter. |
|
|
34
|
+
| `--juno-arc-size` (76px) · `--juno-arc-width` (4px) | Diameter / stroke. |
|
|
35
|
+
|
|
36
|
+
> The ring uses a CSS mask, which clips children. Put `.juno-arc__label` as a
|
|
37
|
+
> **sibling** over a positioned wrapper, not inside `.juno-arc`.
|
|
38
|
+
|
|
39
|
+
> To ring an _arbitrary control_ (a button, a badge, an avatar) without
|
|
40
|
+
> changing its own box, don't reposition `.juno-arc` by hand — compose
|
|
41
|
+
> [`.juno-icon-loader`](./icon-loader.md), junoui's one ring-a-thing
|
|
42
|
+
> primitive. It stacks the arc and the control on a single grid cell sized
|
|
43
|
+
> off two custom props, so nothing you wrap ever resizes when the ring
|
|
44
|
+
> appears.
|
|
45
|
+
|
|
46
|
+
## Beacon — radiating pulse
|
|
47
|
+
|
|
48
|
+
```html
|
|
49
|
+
<!-- indeterminate -->
|
|
50
|
+
<div class="juno-beacon"><span class="juno-beacon__core"></span></div>
|
|
51
|
+
|
|
52
|
+
<!-- determinate: fill grows centre → rim -->
|
|
53
|
+
<div class="juno-beacon juno-beacon--determinate" style="--juno-progress:63;">
|
|
54
|
+
<span class="juno-beacon__track"></span>
|
|
55
|
+
<span class="juno-beacon__fill"></span>
|
|
56
|
+
<span class="juno-beacon__hub"></span>
|
|
57
|
+
<span class="juno-beacon__label">63%</span>
|
|
58
|
+
</div>
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
| Part | Role |
|
|
62
|
+
| ------------------------------------------ | ----------------------------------------------- |
|
|
63
|
+
| `.juno-beacon` | Container; emits two staggered pulse rings. |
|
|
64
|
+
| `.juno-beacon__core` | Centre dot (indeterminate). |
|
|
65
|
+
| `.juno-beacon--determinate` | Switches to a centre→rim fill mask by progress. |
|
|
66
|
+
| `__track` / `__fill` / `__hub` / `__label` | Ring track, growing fill, centre hub, readout. |
|
|
67
|
+
|
|
68
|
+
## Linear — bar
|
|
69
|
+
|
|
70
|
+
```html
|
|
71
|
+
<!-- indeterminate -->
|
|
72
|
+
<div class="juno-bar juno-bar--indeterminate"><span class="juno-bar__fill"></span></div>
|
|
73
|
+
|
|
74
|
+
<!-- determinate -->
|
|
75
|
+
<div class="juno-bar" style="--juno-progress:63;"><span class="juno-bar__fill"></span></div>
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
| Class | Effect |
|
|
79
|
+
| -------------------------- | ------------------------------------- |
|
|
80
|
+
| `.juno-bar` | Track (`s2`, 6px, radius `radius.3`). |
|
|
81
|
+
| `.juno-bar__fill` | Fill; width = progress, color = role. |
|
|
82
|
+
| `.juno-bar--indeterminate` | A 40% segment slides across. |
|
|
83
|
+
|
|
84
|
+
## Notes
|
|
85
|
+
|
|
86
|
+
- Indeterminate forms need no JS. All animations respect `prefers-reduced-motion`
|
|
87
|
+
(slowed to near-static by the base layer).
|
|
88
|
+
- Use determinate whenever real progress is known — it reads as more trustworthy.
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# Menu / Dropdown
|
|
2
|
+
|
|
3
|
+
A list of immediate actions anchored to its trigger — with section dividers, keyboard
|
|
4
|
+
shortcuts, and a destructive item set apart by `warning`. Built on the **native Popover
|
|
5
|
+
API** (top layer — never clipped, never z-index-trapped): open/close, light-dismiss, and
|
|
6
|
+
ESC are free. junoui ships the look; **the app owns** roving focus (arrow keys).
|
|
7
|
+
|
|
8
|
+
## Web
|
|
9
|
+
|
|
10
|
+
```html
|
|
11
|
+
<button popovertarget="acts" aria-haspopup="menu">ACTIONS ▾</button>
|
|
12
|
+
<ul class="juno-menu" id="acts" popover role="menu">
|
|
13
|
+
<li>
|
|
14
|
+
<button class="juno-menu__item" role="menuitem" popovertarget="acts" popovertargetaction="hide">
|
|
15
|
+
<span class="juno-menu__icon">⤓</span>Import data
|
|
16
|
+
<span class="juno-menu__kbd">⌘O</span>
|
|
17
|
+
</button>
|
|
18
|
+
</li>
|
|
19
|
+
<li class="juno-menu__sep" role="separator"></li>
|
|
20
|
+
<li>
|
|
21
|
+
<button
|
|
22
|
+
class="juno-menu__item juno--warning"
|
|
23
|
+
role="menuitem"
|
|
24
|
+
popovertarget="acts"
|
|
25
|
+
popovertargetaction="hide"
|
|
26
|
+
>
|
|
27
|
+
<span class="juno-menu__icon">⨯</span>Delete project
|
|
28
|
+
</button>
|
|
29
|
+
</li>
|
|
30
|
+
</ul>
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
| Class | Effect |
|
|
34
|
+
| -------------------------------- | ------------------------------------------------------------ |
|
|
35
|
+
| `.juno-menu` | 256px `s2` panel, radius `5`, shadow `2`. Top-layer, pinned. |
|
|
36
|
+
| `.juno-menu__item` | Action row; hover/focus → `s3`. Icon + label + shortcut. |
|
|
37
|
+
| `.juno-menu__item.juno--warning` | Destructive item; text + icon `warning`, hover tinted. |
|
|
38
|
+
| `.juno-menu__icon` / `__kbd` | Leading glyph (`label`) / trailing shortcut (`muted`). |
|
|
39
|
+
| `.juno-menu__sep` | Section divider. |
|
|
40
|
+
|
|
41
|
+
## Anatomy (any platform)
|
|
42
|
+
|
|
43
|
+
- Top-layer surface `s2` + `shadow.2`, pinned below the invoker with a `space.8` gap;
|
|
44
|
+
flips to stay on-screen. Rise + fade, `motion.duration.quick` (140ms) / `ease.decel`,
|
|
45
|
+
animated with `@starting-style` + `allow-discrete`.
|
|
46
|
+
|
|
47
|
+
## Usage
|
|
48
|
+
|
|
49
|
+
- Real `<button>` items inside `role="menu"` / `role="menuitem"`. Each item carries
|
|
50
|
+
`popovertarget="<id>" popovertargetaction="hide"` so activating it closes the menu with
|
|
51
|
+
no JS; the app still wires arrow-key roving focus + Enter.
|
|
52
|
+
- `popovertarget` on the trigger handles open / ESC / outside-click and exposes
|
|
53
|
+
`aria-expanded` implicitly. Keep `aria-haspopup="menu"` for intent.
|
|
54
|
+
- One destructive item max, separated by a divider and marked `.juno--warning`.
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# Modal / Dialog
|
|
2
|
+
|
|
3
|
+
A centered, scrim-backed `<dialog>` for decisions that must block the deck. junoui
|
|
4
|
+
ships the look + entry motion; **the app owns** `showModal()`/`close()`, focus-trapping,
|
|
5
|
+
and ESC/scrim-click dismiss (native `<dialog>` gives you most of this free).
|
|
6
|
+
|
|
7
|
+
## Web
|
|
8
|
+
|
|
9
|
+
```html
|
|
10
|
+
<dialog class="juno-modal">
|
|
11
|
+
<div class="juno-modal__stripe"></div>
|
|
12
|
+
<div class="juno-modal__head">
|
|
13
|
+
<span class="juno-modal__tag">DEPLOYMENT</span>
|
|
14
|
+
<button class="juno-modal__close" aria-label="Close">✕</button>
|
|
15
|
+
</div>
|
|
16
|
+
<div class="juno-modal__body">
|
|
17
|
+
<h2 class="juno-modal__title">Promote build to production?</h2>
|
|
18
|
+
<p class="juno-modal__text">This routes production traffic to the new version…</p>
|
|
19
|
+
<div class="juno-modal__foot">
|
|
20
|
+
<button class="juno-btn juno-btn--ghost">CANCEL</button>
|
|
21
|
+
<button class="juno-btn">PROMOTE BUILD</button>
|
|
22
|
+
</div>
|
|
23
|
+
</div>
|
|
24
|
+
</dialog>
|
|
25
|
+
|
|
26
|
+
<!-- destructive -->
|
|
27
|
+
<dialog class="juno-modal juno--warning">…</dialog>
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
| Class | Effect |
|
|
31
|
+
| ------------------------------------------ | ------------------------------------------------------------------- |
|
|
32
|
+
| `.juno-modal` | 440px panel, `s1`, radius `8`, shadow `3` (e3), `z.overlay` (4000). |
|
|
33
|
+
| `.juno-modal__stripe` | Top accent bar in `--juno-role`. |
|
|
34
|
+
| `.juno-modal__head` / `__tag` / `__close` | Tag row + close button (`size.tap.min`). |
|
|
35
|
+
| `.juno-modal__title` / `__text` / `__foot` | Title (20px light), body, right-aligned actions. |
|
|
36
|
+
| `.juno--warning` | Destructive: stripe + tag turn `warning`. |
|
|
37
|
+
| `::backdrop` | Dark scrim at `opacity.scrim` + 1.5px blur. |
|
|
38
|
+
|
|
39
|
+
## Anatomy (any platform)
|
|
40
|
+
|
|
41
|
+
- Enter: scrim fades, panel scales 0.97→1 + rises, `motion.duration.deliberate`
|
|
42
|
+
(300ms) / `ease.decel`. Exit accelerates. `prefers-reduced-motion` collapses it.
|
|
43
|
+
- Elevation = `z.overlay` + `shadow.3`, paired. One scrim at a time — never stack modals.
|
|
44
|
+
|
|
45
|
+
## Usage
|
|
46
|
+
|
|
47
|
+
- Use a real `<dialog>` + `showModal()` so focus-trap, ESC, and inert background come free.
|
|
48
|
+
- Destructive actions: `.juno--warning` + a deliberate confirm (`.juno-btn juno--warning`).
|
|
49
|
+
- For edge-anchored panels that don't block, use the [drawer](./drawer.md).
|
|
50
|
+
- **Phone widths (< `bp.sm`) are automatic:** the centered dialog becomes a
|
|
51
|
+
bottom sheet — full-width, top corners rounded, sliding up, footer buttons
|
|
52
|
+
stretched, content padded past the home indicator. No extra class.
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# Navbar
|
|
2
|
+
|
|
3
|
+
The top bar of a pushed view on phones: a back control on the start edge —
|
|
4
|
+
**always present**, so any screen can unwind — a centered truncating title,
|
|
5
|
+
and trailing actions. junoui ships the look; the app owns the navigation
|
|
6
|
+
stack (what "back" does and where it goes).
|
|
7
|
+
|
|
8
|
+
## Web
|
|
9
|
+
|
|
10
|
+
```html
|
|
11
|
+
<header class="juno-navbar">
|
|
12
|
+
<a class="juno-navbar__back" href="/library">
|
|
13
|
+
<svg class="juno-icon" aria-hidden="true"><use href="…#juno-i-caret-left" /></svg>
|
|
14
|
+
<span class="juno-navbar__back-label">Library</span>
|
|
15
|
+
</a>
|
|
16
|
+
<h1 class="juno-navbar__title">Album</h1>
|
|
17
|
+
<div class="juno-navbar__actions">
|
|
18
|
+
<button class="juno-btn juno-btn--sm juno-btn--ghost">EDIT</button>
|
|
19
|
+
</div>
|
|
20
|
+
</header>
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
| Class | Effect |
|
|
24
|
+
| -------------------------- | ------------------------------------------------------------------ |
|
|
25
|
+
| `.juno-navbar` | Sticky top `s1` bar, hairline seam below, safe-area padded on top. |
|
|
26
|
+
| `.juno-navbar__back` | Start-edge back control, active-toned; ≥ `size.tap.comfortable`. |
|
|
27
|
+
| `.juno-navbar__back-label` | Parent-view name next to the caret — truncates at 14ch. |
|
|
28
|
+
| `.juno-navbar__title` | Centered `font.size.14` title; truncates, never wraps. |
|
|
29
|
+
| `.juno-navbar__actions` | Trailing cluster for small buttons / icons. |
|
|
30
|
+
|
|
31
|
+
## Anatomy (any platform)
|
|
32
|
+
|
|
33
|
+
- Three-column grid: equal flexible sides keep the title optically centered;
|
|
34
|
+
when space runs out the title truncates first, never sliding under the back
|
|
35
|
+
control (back/actions keep their own truncated width).
|
|
36
|
+
- Back = `caret.left` + the **parent view's name** in the active color; the
|
|
37
|
+
caret flips under RTL. Title `font.size.14` semibold, data-toned.
|
|
38
|
+
- Bar ≥ `size.tap.comfortable` tall; top padding extends into
|
|
39
|
+
`env(safe-area-inset-top)` so the notch never covers it.
|
|
40
|
+
|
|
41
|
+
## Usage
|
|
42
|
+
|
|
43
|
+
- **Tab + stack recipe:** the [dock](./dock.md)/[pillbar](./pillbar.md)
|
|
44
|
+
switches sections; inside a section, drilling into a
|
|
45
|
+
[list](./list.md) row pushes a view whose navbar backs out one level.
|
|
46
|
+
Full shell recipe: [layout.md](../layout.md#app-shell).
|
|
47
|
+
- The back control is a real `<a>` to the parent route (deep-linkable), or a
|
|
48
|
+
`<button>` when the app drives history. Label it with the parent view's
|
|
49
|
+
name; keep `aria-label="Back"` when only the caret fits.
|
|
50
|
+
- Root views (tab landing screens) have no parent — there, swap the back slot
|
|
51
|
+
for a brand/title block or leave it empty; every _pushed_ view keeps it.
|
|
52
|
+
- One `<h1>` per document — pushed views' navbar titles are usually the
|
|
53
|
+
screen's `<h1>`.
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# Pagination
|
|
2
|
+
|
|
3
|
+
A row of page controls — prev / next plus numbered pages, with the current page marked
|
|
4
|
+
`aria-current="page"`. Built on a real `<nav>`; the app owns which page is active and
|
|
5
|
+
renders the list. Zero JS.
|
|
6
|
+
|
|
7
|
+
## Web
|
|
8
|
+
|
|
9
|
+
```html
|
|
10
|
+
<nav class="juno-pagination" aria-label="Pagination">
|
|
11
|
+
<button class="juno-pagination__item" aria-label="Previous page" disabled>
|
|
12
|
+
<svg class="juno-icon juno-icon--sm" aria-hidden="true"><use href="…#juno-i-caret-left" /></svg>
|
|
13
|
+
</button>
|
|
14
|
+
<button class="juno-pagination__item" aria-current="page">1</button>
|
|
15
|
+
<button class="juno-pagination__item">2</button>
|
|
16
|
+
<button class="juno-pagination__item">3</button>
|
|
17
|
+
<span class="juno-pagination__gap">…</span>
|
|
18
|
+
<button class="juno-pagination__item">9</button>
|
|
19
|
+
<button class="juno-pagination__item" aria-label="Next page">
|
|
20
|
+
<svg class="juno-icon juno-icon--sm" aria-hidden="true">
|
|
21
|
+
<use href="…#juno-i-caret-right" />
|
|
22
|
+
</svg>
|
|
23
|
+
</button>
|
|
24
|
+
</nav>
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
| Part / attr | Effect |
|
|
28
|
+
| ------------------------ | ------------------------------------------------------ |
|
|
29
|
+
| `.juno-pagination` | `<nav>` flex row; wraps on narrow widths. |
|
|
30
|
+
| `.juno-pagination__item` | Page / prev / next button (44px tap target, mono). |
|
|
31
|
+
| `[aria-current="page"]` | Current page — filled `active` accent, non-actionable. |
|
|
32
|
+
| `:disabled` | Dim prev / next at the ends. |
|
|
33
|
+
| `.juno-pagination__gap` | The `…` between page ranges (not a button). |
|
|
34
|
+
|
|
35
|
+
## Usage
|
|
36
|
+
|
|
37
|
+
- `aria-label="Pagination"` on the `<nav>`; `aria-current="page"` on the active page;
|
|
38
|
+
`aria-label` on the icon-only prev / next ("Previous page").
|
|
39
|
+
- The app computes the window of page numbers + where `…` falls, and disables prev / next
|
|
40
|
+
at the ends.
|
|
41
|
+
- For a simple "‹ Prev / Next ›" pair (as in the [table](./table.md) footer), use two
|
|
42
|
+
`__item` buttons without the numbered list.
|