@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,46 @@
1
+ # Stepper
2
+
3
+ A sequence of steps with numbered markers and connectors that shows progress through a
4
+ multi-step flow. Each step carries `data-state` — `complete` · `current` · `upcoming`;
5
+ the active one gets `aria-current="step"`. Built on a real `<ol>`; the app sets the
6
+ states. Zero JS. Reads `--juno-role` (default `active`) for the accent.
7
+
8
+ ## Web
9
+
10
+ ```html
11
+ <ol class="juno-stepper">
12
+ <li class="juno-stepper__step" data-state="complete">
13
+ <span class="juno-stepper__marker">
14
+ <svg class="juno-icon juno-icon--sm" aria-hidden="true"><use href="…#juno-i-check" /></svg>
15
+ </span>
16
+ <span class="juno-stepper__label">Plan</span>
17
+ </li>
18
+ <li class="juno-stepper__step" data-state="current" aria-current="step">
19
+ <span class="juno-stepper__marker">2</span>
20
+ <span class="juno-stepper__label">Build</span>
21
+ </li>
22
+ <li class="juno-stepper__step" data-state="upcoming">
23
+ <span class="juno-stepper__marker">3</span>
24
+ <span class="juno-stepper__label">Ship</span>
25
+ </li>
26
+ </ol>
27
+ ```
28
+
29
+ | Part / attr | Effect |
30
+ | ------------------------- | ----------------------------------------------------------- |
31
+ | `.juno-stepper` | `<ol>` row; `--juno-stepper-marker` sizes the circle. |
32
+ | `.juno-stepper__step` | One step; set `data-state` + connector lights when reached. |
33
+ | `.juno-stepper__marker` | Numbered circle (or a `check` icon when complete). |
34
+ | `.juno-stepper__label` | Step caption. |
35
+ | `data-state="complete"` | Filled accent marker + lit connector. |
36
+ | `data-state="current"` | Ringed accent marker; pair with `aria-current="step"`. |
37
+ | `data-state="upcoming"` | Muted marker + connector. |
38
+ | `.juno-stepper--vertical` | Stack steps; connector runs down the marker column. |
39
+
40
+ ## Usage
41
+
42
+ - Set `aria-current="step"` on the current step so assistive tech announces position.
43
+ - The **state drives the color**, not vice-versa — `complete` and `current` both light
44
+ the accent + their incoming connector; `upcoming` stays muted.
45
+ - Use a `check` icon for completed markers, the number for current / upcoming.
46
+ - Change the accent with a role class on `.juno-stepper` (e.g. `.juno--nominal`).
@@ -0,0 +1,38 @@
1
+ # Switch
2
+
3
+ A labeled rocker: a squared track that lights green (`nominal`) when ON, with an
4
+ **ON / OFF legend** and a sliding knob. A real checkbox drives it; the app owns state.
5
+
6
+ ## Web
7
+
8
+ ```html
9
+ <label class="juno-switch">
10
+ <input class="juno-switch__input" type="checkbox" role="switch" checked />
11
+ <span class="juno-switch__track"></span>
12
+ <span>AUTO-SCALING</span>
13
+ </label>
14
+ ```
15
+
16
+ | Class | Effect |
17
+ | ------------------------ | ---------------------------------------------------------------------------------- |
18
+ | `.juno-switch` | Inline-flex label row; `gap-control`, `data` text. |
19
+ | `.juno-switch__input` | The checkbox — visually hidden, still focusable / tab-ordered. |
20
+ | `.juno-switch__track` | 70 × `space.28` track; OFF = `control-edge`, ON = role (`nominal`). Legend + knob. |
21
+ | `.juno-switch--sm` | Quiet 40 × `space.20` track: no printed legend, smaller knob. For per-row toggles. |
22
+ | `.juno--<role>` | On-color of the track (put on `__track`). |
23
+ | `:checked` / `:disabled` | Slides the knob to inline-end + legend ON / dims. |
24
+
25
+ ## Anatomy (any platform)
26
+
27
+ - Track 70 × `space.28`, radius `4`, inset shadow. Knob 26 × 22, radius `4`, gradient
28
+ `s3`→`s2`, three grip lines; printed ON / OFF legend (mono 10px, `muted` → role).
29
+ - Knob travels via `inset-inline-start` (mirrors under RTL), `motion.duration.quick`;
30
+ honors `prefers-reduced-motion`.
31
+
32
+ ## Usage
33
+
34
+ - Switch = immediate state change (a setting that takes effect now). For form values
35
+ submitted later, prefer a [checkbox](./checkbox.md).
36
+ - The default rocker is for settings panels; `--sm` for columns of row toggles
37
+ (plugin lists, table rows) where five rockers would shout.
38
+ - Add `role="switch"`; the app toggles `checked` + `aria-checked`.
@@ -0,0 +1,126 @@
1
+ # Table / data grid
2
+
3
+ Dense, scannable rows that read like instrument readouts — numbers in mono and
4
+ right-aligned, semantic color reserved for status, and the active-cyan rail marking the
5
+ row in focus. junoui dresses a **real `<table>`** and ships the **look + the ARIA
6
+ contract**; the app owns sort, selection, inline-edit, pagination, and filtering.
7
+
8
+ ## Web
9
+
10
+ ```html
11
+ <div class="juno-table-scroll">
12
+ <table class="juno-table juno-table--zebra juno-table--sticky">
13
+ <thead>
14
+ <tr>
15
+ <th aria-sort="ascending">SERVICE</th>
16
+ <th class="juno-table__num" aria-sort="none">CPU</th>
17
+ <th>STATUS</th>
18
+ </tr>
19
+ </thead>
20
+ <tbody>
21
+ <tr aria-selected="true">
22
+ <td class="juno-table__id">api-gateway</td>
23
+ <td class="juno-table__num">62</td>
24
+ <td><span class="juno-badge juno-badge--soft juno--nominal">HEALTHY</span></td>
25
+ </tr>
26
+ </tbody>
27
+ </table>
28
+ </div>
29
+ ```
30
+
31
+ | Class | Effect |
32
+ | -------------------------- | ----------------------------------------------------------------------- |
33
+ | `.juno-table-scroll` | Overflow-scrolling viewport (`max-block-size: 480px`) + thin scrollbar. |
34
+ | `.juno-table` | The `<table>`: header `s2`, mono numerics, `border` row rules. |
35
+ | `.juno-table--sticky` | Header holds (`position: sticky`) while the body scrolls. |
36
+ | `.juno-table--zebra` | Stripes even body rows (`s2` dark / `s1` light). |
37
+ | `.juno-table--compact` | Tighter row padding (per-table; independent of global density). |
38
+ | `.juno-table--stack` | Phone mode: rows become label/value cards below 480px (see below). |
39
+ | `th[aria-sort]` | Marks a sortable column; `ascending` / `descending` draw the arrow. |
40
+ | `tr[aria-selected="true"]` | Active rail (left) + cyan row wash. |
41
+
42
+ ### Cell flavors
43
+
44
+ | Class | Use |
45
+ | ----------------------- | -------------------------------------------------------------------------------------------------------------------------- |
46
+ | `.juno-table__id` | Identifier — mono, bold (codes, hostnames, IDs). |
47
+ | `.juno-table__num` | Numeric — mono, bold, end-aligned (measured values). |
48
+ | `.juno-table__mono` | Secondary mono (routes, sub-codes) — quieter `label` color. |
49
+ | `.juno-table__time` | Timestamp — mono, end-aligned, muted. |
50
+ | `.juno-table__trend` | Role-colored mono delta; author supplies the `▲` / `▼` + sign. |
51
+ | `.juno-table__meter` | Inline track + role fill + value; set `--juno-table-fill` (0–100). |
52
+ | `.juno-table__actions` | Icon buttons (`__action`), revealed on row hover / focus. |
53
+ | `.juno-badge--soft` | Status as a low-fill role chip (see [badge](./badge.md)). |
54
+ | `.juno-table__check` | Selection-column cell (44px, centered). Holds a `.juno-checkbox`. |
55
+ | `.juno-table__editable` | Editable cell: dashed `__mark` underline + hover ring. App swaps `__mark` for a `.juno-table__edit-input` on double-click. |
56
+
57
+ ### Overflow (set on a cell)
58
+
59
+ | Class | Behavior |
60
+ | ----------------------- | --------------------------------------------------------------------- |
61
+ | `.juno-table__truncate` | Single line + ellipsis (default). Pair with a `title` / tooltip. |
62
+ | `.juno-table__clamp` | Two lines, then ellipsis. Wrap the text in `.juno-table__clamp-text`. |
63
+ | `.juno-table__wrap` | Full value; row grows. Reserve for one descriptive column. |
64
+
65
+ All three cap at `--juno-cell-max` (default 240px).
66
+
67
+ ### Stacked mode (phone widths)
68
+
69
+ Wide tables don't shrink — they either scroll (the default: `.juno-table-scroll`
70
+ scrolls sideways too) or **stack**. Opt in with `.juno-table--stack`, give every
71
+ `td` a `data-label` echoing its column header, and make the wrapper a container:
72
+
73
+ ```html
74
+ <div class="juno-table-scroll" style="container-type: inline-size">
75
+ <table class="juno-table juno-table--stack">
76
+ <tbody>
77
+ <tr>
78
+ <td data-label="FLIGHT" class="juno-table__id">JU-204</td>
79
+ <td data-label="ALT" class="juno-table__num">37,000</td>
80
+ </tr>
81
+ </tbody>
82
+ </table>
83
+ </div>
84
+ ```
85
+
86
+ Below a 480px container the header row hides, each row becomes a bordered card,
87
+ and each cell renders as a label/value pair (the `data-label` becomes the label).
88
+ The selection rail moves to the card's edge, and row actions stay visible (no
89
+ hover on touch). Semantics stay a real `<table>` — only the display flips.
90
+
91
+ ### Framing rows
92
+
93
+ `.juno-table__toolbar` (top), `.juno-table__foot` (bottom), and `.juno-table__bulk`
94
+ (the selection bar — role `active`, with `.juno-table__bulk-count`) are flex rows you
95
+ stack around the scroll viewport. Frame the whole thing in a [`.juno-card`](./card.md)
96
+ for the border + radius + clipped corners.
97
+
98
+ ### States
99
+
100
+ - `.juno-table__skeleton` — shimmer bar for loading rows (honors `prefers-reduced-motion`).
101
+ - `.juno-table__empty` + `.juno-table__empty-icon` — the no-data block.
102
+
103
+ ## Anatomy (any platform)
104
+
105
+ - Header: `s2`, `font.size.10` uppercase `label`, `control-edge` underline, sticky.
106
+ - Body cell: `space.12`/`space.16` padding (`space.8` block when compact), `border` rule,
107
+ `data` text. Numerics mono + bold + end-aligned.
108
+ - Selected row: `active` left rail (`border.width.3`) + `color-mix(active 11%)` wash.
109
+ - Status is a soft badge per row; caution / warning tint **only the note**, never the
110
+ whole row, so the grid stays calm.
111
+
112
+ ## Usage (the app's half)
113
+
114
+ junoui can't sort or select in CSS — wire these and keep the ARIA in sync:
115
+
116
+ - **Sort:** click a `th`; set its `aria-sort` to `ascending` / `descending` (others to
117
+ `none`) and reorder the rows.
118
+ - **Select:** a `.juno-checkbox` per row (in a `.juno-table__check` cell) toggles
119
+ `aria-selected` on the `<tr>`; reflect the count in a `.juno-table__bulk` bar. The
120
+ header "select all" checkbox drives every visible row (use `indeterminate` for a
121
+ partial set).
122
+ - **Inline edit:** swap the cell's text for an [`.juno-input`](./input.md) on
123
+ double-click; Enter commits, Esc cancels.
124
+ - **Paginate / filter:** owned by the app; `.juno-table__foot` is just the frame.
125
+
126
+ `showcase/app.js` (`initTables`) is a reference driver for sort + selection.
@@ -0,0 +1,61 @@
1
+ # Tabs
2
+
3
+ A horizontal tablist with an active-cyan underline; the selected tab shows its panel.
4
+ junoui ships the look + the ARIA contract; the app owns switching, panel visibility, and
5
+ arrow-key roving focus.
6
+
7
+ ## Web
8
+
9
+ ```html
10
+ <div class="juno-tabs">
11
+ <div class="juno-tabs__list" role="tablist" aria-label="Service detail">
12
+ <button class="juno-tabs__tab" role="tab" id="t1" aria-controls="p1" aria-selected="true">
13
+ OVERVIEW
14
+ </button>
15
+ <button class="juno-tabs__tab" role="tab" id="t2" aria-controls="p2" aria-selected="false">
16
+ METRICS
17
+ </button>
18
+ <button
19
+ class="juno-tabs__tab"
20
+ role="tab"
21
+ id="t3"
22
+ aria-controls="p3"
23
+ aria-selected="false"
24
+ disabled
25
+ >
26
+ CONFIG
27
+ </button>
28
+ </div>
29
+ <div class="juno-tabs__panel" role="tabpanel" id="p1" aria-labelledby="t1">…</div>
30
+ <div class="juno-tabs__panel" role="tabpanel" id="p2" aria-labelledby="t2" hidden>…</div>
31
+ <div class="juno-tabs__panel" role="tabpanel" id="p3" aria-labelledby="t3" hidden>…</div>
32
+ </div>
33
+ ```
34
+
35
+ | Class | Effect |
36
+ | -------------------------------- | -------------------------------------------------------------- |
37
+ | `.juno-tabs__list` | The `role="tablist"` row; bottom rule the tabs sit on. |
38
+ | `.juno-tabs__tab` | A tab button. `label` at rest → `data` on hover. |
39
+ | `.juno-tabs__tab[aria-selected]` | Selected: `active` text + `active` underline indicator. |
40
+ | `.juno-tabs__tab:disabled` | Dimmed, not selectable. |
41
+ | `.juno-tabs__panel` | The `role="tabpanel"`; `[hidden]` when its tab isn't selected. |
42
+
43
+ ## Anatomy (any platform)
44
+
45
+ - Tabs sit on a 1px `border` rule; the selected tab paints a `border.width.2` `active`
46
+ underline that overlaps the rule. Text `label` → `active` when selected.
47
+ - Panel gets `space.20` of leading space above its content.
48
+
49
+ ## Usage (the app's half)
50
+
51
+ junoui can't switch panels in CSS — wire it and keep the ARIA in sync:
52
+
53
+ - On tab activation: set its `aria-selected="true"` (others `false`), unhide its panel
54
+ (`[hidden]` on the rest), and move the roving `tabindex` (selected = `0`, rest = `-1`).
55
+ - **Keyboard:** `←` / `→` move between tabs (wrapping), `Home` / `End` jump to the ends;
56
+ activation follows focus. `showcase/app.js` (`initTabs`) is a reference driver.
57
+ - For a stable layout, give panels equal-ish height or let content flow; don't animate
58
+ height unless you measure it.
59
+ - Tabs never wrap: when the strip runs out of room (phone widths) it scrolls
60
+ sideways, so every tab stays reachable. Keep labels short anyway — a scrolled
61
+ tab is a hidden tab until swiped to.
@@ -0,0 +1,81 @@
1
+ # Thumb
2
+
3
+ A media frame that never shows the browser's broken-image glyph. A muted
4
+ placeholder (icon on `s2`) sits under the media; the image covers it while
5
+ present. **Loading** is [skeleton](./skeleton.md)'s job — thumb handles media
6
+ that is **missing or failed**.
7
+
8
+ ## Web
9
+
10
+ ```html
11
+ <!-- normal: image covers the placeholder; aspect defaults to square -->
12
+ <figure class="juno-thumb">
13
+ <img src="…" alt="Sunset clip" onerror="this.remove()" />
14
+ </figure>
15
+
16
+ <!-- non-square: override the ratio, not the aspect-ratio property -->
17
+ <figure
18
+ class="juno-thumb juno-thumb--video"
19
+ style="--juno-thumb-ratio: 16/9"
20
+ role="img"
21
+ aria-label="Preview unavailable"
22
+ >
23
+ <figcaption class="juno-thumb__label">Unavailable</figcaption>
24
+ </figure>
25
+
26
+ <!-- selected, with corner overlays: a check top-left, a duration top-right -->
27
+ <figure class="juno-thumb juno-thumb--selected">
28
+ <img src="…" alt="Sunset clip" onerror="this.remove()" />
29
+ <span class="juno-thumb__corner juno-thumb__corner--top-start badge badge--sm">✓</span>
30
+ <span class="juno-thumb__corner juno-thumb__corner--top-end badge badge--sm">0:42</span>
31
+ </figure>
32
+ ```
33
+
34
+ | Class / attr | Effect |
35
+ | ------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
36
+ | `.juno-thumb` | Frame: `s2` fill, hairline border, `radius.3`, centered glyph, aspect locked to `--juno-thumb-ratio` (default `1`). |
37
+ | `.juno-thumb--flush` | Drops the border/radius — full-bleed tiles in a tight wall. |
38
+ | `.juno-thumb--selected` | Inset outline (`--juno-active`) — never a border, so nothing reflows on toggle. Apps own the state; junoui never adds/removes the class. |
39
+ | `.juno-thumb--video` | Play glyph instead of the image glyph. |
40
+ | `.juno-thumb__label` | Optional uppercase micro-caption under the glyph. |
41
+ | `.juno-thumb__corner` | Absolutely-positioned slot over the media, `space.4` inset from the frame edge, above the media (`z-index: 1`). Pair with one position modifier. |
42
+ | `.juno-thumb__corner--top-start` / `--top-right` / `--bottom-left` / `--bottom-right` | Anchors the slot to a corner using logical `inset-block-*` / `inset-inline-*` — start/end swap correctly under `dir="rtl"`. |
43
+ | `> img` / `> video` | Covers the frame (`object-fit: cover`). |
44
+ | `onerror="this.remove()"` | The whole JS contract — optional, stateless, one attribute. |
45
+
46
+ ## Anatomy (any platform)
47
+
48
+ - Frame: surface `s2`, 1px `border`, radius `3`; aspect locked by
49
+ `--juno-thumb-ratio` (default square) so a media wall's scroll height is
50
+ stable before anything loads — override per instance
51
+ (`style="--juno-thumb-ratio: 16/9"`) or via the [tiles grid](../layout.md).
52
+ - Glyph: 28% of the frame (capped `space.32`), `muted` color — a missing thumb
53
+ is **not** a warning state; the placeholder stays neutral.
54
+ - Media covers the full frame, center-cropped.
55
+ - Selection is an inset outline, not a border — the frame's box never
56
+ changes size, so a wall re-flowing selection doesn't jitter.
57
+ - Corner slots are presentational anchors only; what occupies them (check
58
+ icon, duration chip, storage-tier badge) is app vocabulary.
59
+
60
+ ## Failure contract
61
+
62
+ There is no portable CSS-only way to detect a broken image, so the contract is
63
+ explicit:
64
+
65
+ - **Failed to load** → `onerror="this.remove()"`: the broken element removes
66
+ itself, the placeholder underneath shows.
67
+ - **Known missing** (offline node, purged cache, no permission) → render no
68
+ media element at all.
69
+ - When no media renders, the alt text is gone too — put the accessible name on
70
+ the figure (`role="img"` + `aria-label`) or show a `__label`.
71
+
72
+ ## Usage
73
+
74
+ - Media walls (pairs with `.juno-grid-auto--tiles`), avatars' big cousin,
75
+ video poster slots, attachment previews.
76
+ - Keep the placeholder neutral; if failure _matters_ (broken pipeline), say it
77
+ with a [badge](./badge.md) or [alert](./alert.md) next to the thumb.
78
+ - Multi-select galleries: toggle `--selected` per tile and put a check in a
79
+ `__corner` slot; duration/storage-tier chips are `__corner` occupants too.
80
+ - Retry, lazy-load, LQIP/blur-up, and the selection/click behavior itself:
81
+ app or `junoui-<framework>` territory.
@@ -0,0 +1,48 @@
1
+ # Toast / snackbar
2
+
3
+ A transient, floating notification. Same role-rail language as the [alert](./alert.md),
4
+ but elevated and stacked in a fixed corner on the reserved **alert layer**
5
+ (`--juno-z-alert`, above modals). junoui ships the look + enter/exit motion; the app owns
6
+ creation, auto-dismiss timing, and the live-region announcement.
7
+
8
+ ## Web
9
+
10
+ ```html
11
+ <!-- one stack per corner; the app appends toasts into it -->
12
+ <div class="juno-toast-stack" id="toasts" aria-live="polite"></div>
13
+
14
+ <!-- a toast (app-created) -->
15
+ <div class="juno-toast juno--nominal" role="status">
16
+ <span class="juno-toast__icon" aria-hidden="true">✓</span>
17
+ <span class="juno-toast__text">Build promoted.</span>
18
+ <button class="juno-toast__close" aria-label="Dismiss">✕</button>
19
+ </div>
20
+ ```
21
+
22
+ | Class | Effect |
23
+ | ------------------------------------------ | ------------------------------------------------------------------- |
24
+ | `.juno-toast-stack` | Fixed container on `z.alert`; bottom-end, stacks its toasts. |
25
+ | `.juno-toast-stack--top` / `--start` | Move the stack to the top / inline-start. |
26
+ | `.juno-toast` | `s2` surface, `shadow.2`, role start-rail. Default `active`. |
27
+ | `.juno-toast--leaving` | Exit state (app adds it, then removes the node on `transitionend`). |
28
+ | `.juno-toast__icon` / `__text` / `__close` | Glyph · message · dismiss. |
29
+ | `.juno--<role>` | Sets the rail + icon color. |
30
+
31
+ ## Anatomy (any platform)
32
+
33
+ - Stack: `position: fixed`, `z.alert` (5000), `inline-size: min(360px, …)`, `pointer-events:
34
+ none` so only the toasts catch clicks.
35
+ - Toast: solid `s2` + `shadow.2` + role rail. Enters via `@starting-style` (fade + slide),
36
+ exits on `.juno-toast--leaving`; both honor `prefers-reduced-motion`.
37
+
38
+ ## Usage
39
+
40
+ - The app creates and destroys toasts: append to the stack, start a timer (~4s), then add
41
+ `.juno-toast--leaving` and remove on `transitionend`. `showcase/app.js` (`showToast`) is a
42
+ reference driver.
43
+ - Put `aria-live="polite"` on the stack (`assertive` for errors) so new toasts announce.
44
+ - Keep messages short and self-contained; one line. For anything needing a decision, use a
45
+ [modal](./modal.md) or inline [alert](./alert.md).
46
+ - **Phone widths (< `bp.sm`) are automatic:** the stack goes full-width along the
47
+ bottom edge (thumb reach), clear of the home indicator, and toasts slide up
48
+ instead of sideways. No extra class.
@@ -0,0 +1,40 @@
1
+ # Toggle button
2
+
3
+ An Airbus-style illuminated-legend pushbutton: the body stays dark; a top strip lights
4
+ (`nominal`) and the surround glows when engaged. A real `<button aria-pressed>`; the app
5
+ flips `aria-pressed`. Use for panel-style on/off controls where the lit legend matters.
6
+
7
+ ## Web
8
+
9
+ ```html
10
+ <button class="juno-toggle-btn" aria-pressed="true">
11
+ <span class="juno-toggle-btn__strip"></span>
12
+ <span class="juno-toggle-btn__row">
13
+ <span class="juno-toggle-btn__text">
14
+ <span class="juno-toggle-btn__legend">AUTO-SCALING</span>
15
+ <span class="juno-toggle-btn__desc">Scale workers with load</span>
16
+ </span>
17
+ <span class="juno-toggle-btn__status">ON</span>
18
+ </span>
19
+ </button>
20
+ ```
21
+
22
+ | Class | Effect |
23
+ | -------------------------------------------------- | --------------------------------------------------- |
24
+ | `.juno-toggle-btn` | Pushbutton tile on `control-surface`. |
25
+ | `[aria-pressed="true"]` | Border → role, surround glow, strip + status light. |
26
+ | `.juno-toggle-btn__strip` | Top legend strip; `s3` (off) → role (on) with glow. |
27
+ | `.juno-toggle-btn__legend` / `__desc` / `__status` | Mono legend, sub-label, ON/OFF status. |
28
+ | `.juno--<role>` | Lit color (default `nominal`). |
29
+ | `:disabled` | Dimmed, not-allowed. |
30
+
31
+ ## Anatomy (any platform)
32
+
33
+ - Tile border `control-edge` → role when pressed, plus a 20% role outer glow. Strip
34
+ `space.4` tall lights to role with an 8px glow. `motion.duration.quick` transitions.
35
+
36
+ ## Usage
37
+
38
+ - A latching control whose engaged state must read at a glance (system arm/enable).
39
+ - Use a real `<button>` and keep `aria-pressed` in sync; for inline on/off in a form,
40
+ prefer the [switch](./switch.md).
@@ -0,0 +1,63 @@
1
+ # Tooltip
2
+
3
+ A transient hover/focus label. Pure CSS reveal — no JS. Four placements with an arrow.
4
+
5
+ ## Web
6
+
7
+ ```html
8
+ <span class="juno-tooltip">
9
+ <button tabindex="0">SPD</button>
10
+ <span class="juno-tooltip__bubble" role="tooltip">
11
+ Requests / sec · 248
12
+ <span class="juno-tooltip__arrow"></span>
13
+ </span>
14
+ </span>
15
+
16
+ <!-- placements -->
17
+ <span class="juno-tooltip__bubble juno-tooltip__bubble--right">…</span>
18
+ <span class="juno-tooltip__bubble juno-tooltip__bubble--bottom">…</span>
19
+ <span class="juno-tooltip__bubble juno-tooltip__bubble--left">…</span>
20
+ ```
21
+
22
+ | Class | Effect |
23
+ | ----------------------------------------------- | -------------------------------------------------------- |
24
+ | `.juno-tooltip` | Relative wrapper around the trigger. |
25
+ | `.juno-tooltip__bubble` | `s3` bubble, mono 11px, shadow `2`, `z.anchored` (2000). |
26
+ | `--top` (default) `--right` `--bottom` `--left` | Placement of the bubble + arrow. |
27
+ | `.juno-tooltip__arrow` | Rotated-square pointer back to the trigger. |
28
+
29
+ Shows on `:hover` or `:focus-within` of the wrapper; `motion.duration.quick` (140ms)
30
+ / `ease.decel` rise.
31
+
32
+ ## Top-layer mode (escaping clipping)
33
+
34
+ The pure-CSS reveal is clipped if an ancestor sets `overflow`/`clip` (cards, scroll
35
+ areas). To lift it above everything, promote the bubble to a `popover="hint"` — it then
36
+ renders in the browser **top layer**, immune to ancestor clipping. The bubble already
37
+ carries `:popover-open` + anchor-positioning styles; a tiny stateless enhancer toggles it
38
+ and points `position-anchor` at the trigger:
39
+
40
+ ```js
41
+ const name = `--juno-tip-${i}`;
42
+ trigger.style.anchorName = name;
43
+ bubble.style.positionAnchor = name;
44
+ bubble.popover = 'hint';
45
+ trigger.addEventListener('pointerenter', () => bubble.showPopover());
46
+ trigger.addEventListener('pointerleave', () => bubble.hidePopover());
47
+ trigger.addEventListener('focus', () => bubble.showPopover());
48
+ trigger.addEventListener('blur', () => bubble.hidePopover());
49
+ ```
50
+
51
+ (`showcase/app.js` ships this as `initTooltips()`.) With no enhancer the component still
52
+ works as the CSS-only hover tooltip — just clippable by its container.
53
+
54
+ ## Anatomy (any platform)
55
+
56
+ - Bubble offset `space.8` from the trigger edge; small rise on reveal.
57
+ - Content is mono (codes/values); single line (`white-space: nowrap`).
58
+
59
+ ## Usage
60
+
61
+ - Reference text only — never put focusable controls in a tooltip (use a [popover](./popover.md)).
62
+ - Give the trigger a tabstop and the bubble `role="tooltip"` + `aria-describedby` so it
63
+ reveals on keyboard focus, not just hover.