@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.
Files changed (84) hide show
  1. package/CHANGELOG.md +251 -0
  2. package/README.md +23 -19
  3. package/dist/css/juno-custom-media.css +32 -0
  4. package/dist/css/juno.css +1355 -40
  5. package/dist/icons/inline.js +22 -0
  6. package/docs/accessibility.md +188 -0
  7. package/docs/boot-shell.md +295 -0
  8. package/docs/components/README.md +71 -0
  9. package/docs/components/accordion.md +49 -0
  10. package/docs/components/alert.md +46 -0
  11. package/docs/components/avatar.md +45 -0
  12. package/docs/components/badge.md +35 -0
  13. package/docs/components/breadcrumb.md +31 -0
  14. package/docs/components/button.md +32 -0
  15. package/docs/components/card.md +41 -0
  16. package/docs/components/checkbox.md +41 -0
  17. package/docs/components/chip.md +44 -0
  18. package/docs/components/divider.md +32 -0
  19. package/docs/components/dock.md +215 -0
  20. package/docs/components/drawer.md +100 -0
  21. package/docs/components/field.md +44 -0
  22. package/docs/components/fold-slot.md +31 -0
  23. package/docs/components/gauge.md +48 -0
  24. package/docs/components/icon-loader.md +122 -0
  25. package/docs/components/icon.md +70 -0
  26. package/docs/components/input.md +45 -0
  27. package/docs/components/list.md +76 -0
  28. package/docs/components/load-state.md +131 -0
  29. package/docs/components/loader.md +88 -0
  30. package/docs/components/menu.md +54 -0
  31. package/docs/components/modal.md +52 -0
  32. package/docs/components/navbar.md +53 -0
  33. package/docs/components/pagination.md +42 -0
  34. package/docs/components/pillbar.md +211 -0
  35. package/docs/components/popover.md +43 -0
  36. package/docs/components/rail.md +54 -0
  37. package/docs/components/readout.md +39 -0
  38. package/docs/components/reload.md +41 -0
  39. package/docs/components/segmented.md +46 -0
  40. package/docs/components/select.md +33 -0
  41. package/docs/components/skeleton.md +45 -0
  42. package/docs/components/slider.md +48 -0
  43. package/docs/components/spark.md +51 -0
  44. package/docs/components/status.md +30 -0
  45. package/docs/components/stepper.md +46 -0
  46. package/docs/components/switch.md +38 -0
  47. package/docs/components/table.md +126 -0
  48. package/docs/components/tabs.md +61 -0
  49. package/docs/components/thumb.md +81 -0
  50. package/docs/components/toast.md +48 -0
  51. package/docs/components/toggle-button.md +40 -0
  52. package/docs/components/tooltip.md +63 -0
  53. package/docs/design-guidelines.md +173 -0
  54. package/docs/flutter.md +42 -0
  55. package/docs/getting-started.md +93 -0
  56. package/docs/integration.md +95 -0
  57. package/docs/ios-conformance.md +224 -0
  58. package/docs/layout.md +271 -0
  59. package/docs/native.md +52 -0
  60. package/docs/roadmap.md +106 -0
  61. package/docs/tokens-reference.md +223 -0
  62. package/docs/web.md +95 -0
  63. package/package.json +7 -3
  64. package/src/css/base.css +171 -1
  65. package/src/css/components/dock.css +335 -0
  66. package/src/css/components/drawer.css +40 -3
  67. package/src/css/components/fold-slot.css +44 -0
  68. package/src/css/components/icon-loader.css +67 -0
  69. package/src/css/components/icon.css +6 -4
  70. package/src/css/components/load-state.css +136 -0
  71. package/src/css/components/loader.css +6 -0
  72. package/src/css/components/menu.css +4 -0
  73. package/src/css/components/modal.css +24 -3
  74. package/src/css/components/navbar.css +5 -1
  75. package/src/css/components/pillbar.css +222 -7
  76. package/src/css/components/rail.css +8 -0
  77. package/src/css/components/reload.css +48 -0
  78. package/src/css/components/skeleton.css +41 -13
  79. package/src/css/components/tabs.css +5 -0
  80. package/src/css/components/thumb.css +63 -1
  81. package/src/css/components/toast.css +5 -1
  82. package/src/css/density.css +22 -0
  83. package/src/css/layout.css +95 -5
  84. 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.