@junoput01/junoui 0.5.0 → 0.7.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 CHANGED
@@ -1,5 +1,193 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.7.0
4
+
5
+ ### Minor Changes
6
+
7
+ - aad6351: **`.juno-btn--sm` promotes to the tap target on touch; `.juno-btn--dense` is the opt-out.**
8
+
9
+ `--sm` names a **density**, and consumers reach for it as a **semantic**. Audited across one app: 40 call sites, nearly all `--sm --ghost` meaning "secondary", shipping a 24px target on a phone — and junoui's own showcase does it twice, in a navbar action slot.
10
+
11
+ A size modifier should not quietly become a tap-target decision. Under `(pointer: coarse)`, `.juno-btn--sm` now holds `--juno-size-tap-min` like every other control. Type and padding still shrink, so it stays a density modifier and stops being a touch-target one. **On a fine pointer nothing changes** — still 24px, the WCAG 2.2 AA floor (2.5.8) exactly.
12
+
13
+ `.juno-btn--dense` opts a `--sm` button back out, for a toolbar that is genuinely dense on touch (a scrubber, an editor rail). It is meaningful only in combination with `--sm`, on purpose: a dense touch target should be chosen by name, never inherited from a size.
14
+
15
+ **This is a visual change on touch devices.** Any `--sm` button in a phone layout grows to 44px unless you add `--dense`. Audit your `--sm` call sites: the ones that meant "secondary" want `--ghost` alone and are now correct for free; the ones that meant "dense" want `--dense` added.
16
+
17
+ The rule lives in `button.css`, not `base.css`'s coarse block — a media query adds no specificity, so a `.juno-btn--sm` there would lose to `button.css`'s own `.juno-btn--sm` later in the bundle.
18
+
19
+ - 40a5e43: **Class manifest + `junoui/testing` — conformance kit slice 1.**
20
+
21
+ A `juno-*` class name in a consumer's source is a string that has to match something in junoui's stylesheet, and nothing checked it. When it does not match, nothing fails: the file compiles, the tests pass, and the element renders as unstyled UA defaults. One consumer shipped eleven such names in a dialog; on a phone that put the confirm button off the bottom of the screen with no way to reach it. junoui had the same defect pointing the other way — `.juno-seg__option` sat in a `touch-action` list that never matched anything, because the shipped class is `.juno-seg__opt`.
22
+
23
+ **New: `junoui/classes.json`**, generated at build time from the bundle's own selectors — `all`, `public` (the documented subset), `roles`, `components` grouped BEM-wise, plus the other `juno-*` namespaces junoui ships and a consumer writes as bare strings: `tokens`, `keyframes`, `icons`.
24
+
25
+ **New: `junoui/testing`**, dependency-free and framework-agnostic:
26
+
27
+ ```js
28
+ import { assertJunoClasses } from 'junoui/testing';
29
+ assertJunoClasses(['src/**/*.tsx'], { allowed: ['my-own-juno-namespaced-thing'] });
30
+ ```
31
+
32
+ It throws with every offending `file: name`, and throws rather than passing when its globs match no files.
33
+
34
+ **What it answers:** "junoui ships nothing by this name." **What it does not:** whether the class still does what your component assumes.
35
+
36
+ Nothing existing changes; both entries are additive.
37
+
38
+ - 447c133: The floating bar's offset from the bottom edge is now one token,
39
+ `--juno-dock-edge-offset`, consumed by both `.juno-dock--pill`/`--float`'s margin
40
+ and `--juno-dock-clearance`. Plus `--juno-dock-clearance-breathing` (default
41
+ `space-8`) for the gap between the bar and the last row.
42
+
43
+ No default changes: the offset still resolves to `space-16 + env(safe-area-inset-bottom)`
44
+ and the clearances to the same 86px / 78px they did at a 44px bubble.
45
+
46
+ What it fixes: the inset FORM used to be written separately at each site, so a
47
+ consumer whose design puts the bar flush above the home indicator —
48
+ `max(8px, env(safe-area-inset-bottom))` — changed its margin and could not change
49
+ the reservation, which kept adding. Measured at 16px of dead band at inset 0 and
50
+ 24px at inset 34, with no value of `--juno-dock-h` able to reconcile them because
51
+ one side added the inset and the other maxed it. Both now follow the token, so
52
+ they agree by construction at every inset.
53
+
54
+ - e852323: **The dock publishes its horizontal item budget.**
55
+
56
+ `.juno-dock__item` is `flex: 1 1 0`, so the bar divides its inner width by however many items are present. A consumer deciding how many to render — and whether they still hold a tap target — had to re-derive that from the numbers in `dock.css`. Two did, in prose, twice, and both drifted the same way: they subtracted 12px of inline padding where the pill actually spends 8 (`--juno-space-4` a side), so every per-item width came out ~0.8px low.
57
+
58
+ New custom properties on `.juno-dock`:
59
+
60
+ | Property | What it is |
61
+ | --------------------------- | --------------------------------------------------------------------------------------------- |
62
+ | `--juno-dock-items` | The item budget. You set it to what you render (default `5`). |
63
+ | `--juno-dock-item-inline` | The width one item gets — a prediction of the flex layout, asserted against the measured box. |
64
+ | `--juno-dock-fit-inline` | The narrowest viewport at which every item still holds `--juno-size-tap-comfortable`. |
65
+ | `--juno-dock-chrome-inline` | The bar's total inline chrome. `0` full-bleed, `34px` on `--pill`/`--float`. |
66
+ | `--juno-dock-avail` | The width the budget divides (default `100vw`). |
67
+
68
+ The margin, padding and border terms are declared once and consumed by both the variant's own box and the sum, so the budget cannot disagree with the bar it describes — the same construction as `--juno-dock-edge-offset`. One consequence worth knowing: `--pill`/`--float` now paint their border from `--juno-dock-border-inline`, so overriding that term to `0` removes the hairline as well as widening the items. That is deliberate — the sum follows the paint.
69
+
70
+ **No scale floor is published.** `44px / --juno-dock-item-inline` is a ratio of two lengths and CSS cannot divide by a length. A consumer that must scale rather than drop an item compares the two values itself.
71
+
72
+ ### Patch Changes
73
+
74
+ - 770f331: **fold-slot: the fold now reaches zero when composed with a component class.**
75
+ `.juno-fold` promises its inline-size folds to zero, and the canonical use puts it on an element that already carries the capsule chrome — `.juno-pillbar__item`, `.juno-btn`, `.juno-chip`. Composed that way it could not: `min-inline-size` (the 44px tap floor), `padding-inline` and `border-inline-width` each hold a border-box inline size open, and the folded state released none of them. Measured against the built bundle at a 390px viewport, composed with `.juno-pillbar__item`: 44px folded, 20px with the floor released, 0px with all three. A consumer's pill carried one dead 44px slot whenever the folded action was absent.
76
+
77
+ The folded state now releases all three, and each is in the fold's transition list so nothing snaps as the fold opens or shuts.
78
+
79
+ Also fixed, and invisible from the source: `transition` is a shorthand, `.juno-fold` was one class of specificity, and `pillbar.css` sorts after `fold-slot.css` — so `.juno-pillbar__item`'s own `transition` replaced the fold's whole list and the slot jumped shut instead of folding. The fold's declarations now sit at attribute specificity, and its transition list carries the chrome properties (`color`, `background-color`) too, since owning the shorthand means owning all of it. A component of your own that composes with `.juno-fold` and needs a third property transitioned must state it above `(0,2,0)`.
80
+
81
+ - a1f3f53: New `docs/ios-pwa.md`: a bounded statement of what junoui gives you on iOS and in
82
+ a Home-Screen web app — what you get for free, what your app must supply, and
83
+ what junoui explicitly does not do. `docs/ios-conformance.md` gains the
84
+ standalone `<head>` contract and names the letterbox flag
85
+ (`data-juno-letterboxed`, app-set, documented rather than shipped).
86
+
87
+ Docs ship in the package, so a consumer installing this version receives both.
88
+ Two corrections travel with them: the letterbox flag is **not** an upstream-fix
89
+ detector (that test needs a document that cannot scroll, and the unlock makes it
90
+ scroll), and `.juno-pagination`'s items take the coarse-pointer promotion on the
91
+ inline axis only — 44 × 32 on touch, which clears WCAG 2.5.8 AA and not the
92
+ 44 px comfortable target the docs previously implied.
93
+
94
+ - 4568157: **Two mistyped class names in the touch-default lists, and a tap floor for the segmented pill.**
95
+
96
+ `base.css` carries two `:where()` lists of junoui's own tappable components — one dropping double-tap-to-zoom recognition (`touch-action: manipulation`), one killing the UA tap-highlight square under `(pointer: coarse)`. Two members named classes that do not exist, so `:where()` matched nothing, the rule still parsed, every other member kept working, and the named components silently kept the defaults they were listed to opt out of:
97
+
98
+ - `.juno-seg__option` → `.juno-seg__opt` (touch-action list)
99
+ - `.juno-list__item` → `.juno-list__row` (**both** lists)
100
+
101
+ Every segmented control and every grouped list row in every consumer has been carrying the ~300ms double-tap delay. No consumer change is needed — the fix lands in the shipped stylesheet.
102
+
103
+ `.juno-seg__opt` was also the only interactive primitive with no tap floor: it computed 25.39px from its padding, which meets WCAG 2.2 AA (2.5.8, 24px) by accident and misses the comfortable touch target entirely. It now holds `--juno-size-tap-min` on the painted box, like `.juno-btn`. Measured against the built bundle: **fine pointer unchanged at 25.39px, coarse 25.39 → 44.00**, width unchanged either way — so no showcase baseline moves.
104
+
105
+ Unlike `.juno-btn--sm`, `.juno-seg--sm` does **not** drop below that floor: it reduces type and padding only. A segmented row is routinely the only control on a whole settings section, so a sub-tap variant of it has no safe use on a phone.
106
+
107
+ ## 0.6.0
108
+
109
+ ### Minor Changes
110
+
111
+ - 71c069c: Two primitives stop encoding junoui's own dimensions and start exposing the
112
+ derivation, so a consumer that parameterizes them stays correct.
113
+
114
+ - **`junoui/icons/install`** — the sprite injector without the sprite (~1 kB).
115
+ A consumer that subsets with `junoui/subset` can now keep the same-document
116
+ injection Safari requires without pulling the 66-symbol payload that
117
+ `junoui/icons/inline` carries; both share one id-guarded holder, so mixing
118
+ them cannot produce two. `icons/inline` is unchanged for everyone else.
119
+ - **`--juno-dock-clearance` / `--juno-pillbar-clearance` are derived**, from
120
+ new published parts: `--juno-dock-h`, `--juno-pillbar-h` and
121
+ `--juno-dock-clearance-scale`. They were constants that promised to track the
122
+ dock's geometry and did not — past a 58px bubble they reserved less than the
123
+ pill's own height plus its margin, hiding content under the dock. Values at
124
+ the default 44px bubble change from 92px to 86px (dock) and 72px to 78px
125
+ (pillbar), both now equal to what the control actually measures plus its
126
+ margin and a breathing gap.
127
+
128
+ - 0ee41a8: Declare the browser-support baseline, and guard the one gap that is functional.
129
+
130
+ New `docs/browser-support.md`: the supported floor (Safari/iOS 17.5, Chrome/Edge
131
+ 117, Firefox 129), the hard floor below which things break (17.0 / 114 / 125),
132
+ and a per-feature audit of the built bundle with a degrade-vs-break verdict on
133
+ each. `package.json` now carries a matching `browserslist`; README and
134
+ getting-started state the floor.
135
+
136
+ `base.css` ships one `@supports not selector(:popover-open)` guard. Below Safari
137
+ 17.0 the Popover API is absent, and because the UA rule that hides a closed
138
+ popover is absent with it, `.juno-menu` and `.juno-popover` were rendering as
139
+ invisible fixed panels that swallowed taps. The guard hides them instead —
140
+ absent beats invisibly-present, and apps can branch on
141
+ `CSS.supports('selector(:popover-open)')`. Guards are for functional failures
142
+ only; cosmetic gaps (missing entry animations, unanchored placement) are
143
+ documented, not wrapped.
144
+
145
+ `docs/ios-conformance.md` gains the viewport-unit decision it was missing:
146
+ `dvh` stays at both `.juno-app-shell` and `.juno-drawer`, with the reasoning,
147
+ what each option costs at the moment browser chrome retracts, and a rule for
148
+ applying the choice to a new component. It also now records the iOS Home-Screen
149
+ standalone letterbox — iOS sizes the window from the document's resting
150
+ scrollability at launch — which is why `base.css` carries the standalone unlock.
151
+
152
+ Docs and defaults only; no token or component API changed.
153
+
154
+ ### Patch Changes
155
+
156
+ - cacdb21: Fix: the 16px `.juno-input` font floor on touch never applied.
157
+
158
+ The rule lived in `base.css`'s `@media (pointer: coarse)` block, but a media
159
+ query adds no specificity — so `components/input.css`'s own
160
+ `.juno-input { font-size: var(--juno-font-size-14) }`, same 0,1,0 selector and
161
+ later in the bundle, won every time. On a coarse pointer the field measured
162
+ 14px, i.e. exactly the condition the floor exists to avoid (iOS Safari zooming
163
+ the page onto a focused sub-16px field). The rule now lives in `input.css`,
164
+ after the declaration it has to beat.
165
+
166
+ Found by the new coarse-pointer visual-regression project (20260815-006): the
167
+ suite ran only `Desktop Chrome`, where `(pointer: coarse)` never matches, so
168
+ nothing had ever exercised the rule.
169
+
170
+ - da03666: Fix: the forced-colors (Windows High Contrast) border never applied.
171
+
172
+ `@media (forced-colors: active) { .juno-badge, .juno-btn, .juno-card,
173
+ .juno-readout { border: 1px solid CanvasText } }` lived in `base.css`, which the
174
+ bundler emits before `components/`. A media query adds no specificity, so each
175
+ component's own `border` declaration won on source order alone. Measured under
176
+ emulated forced-colors: `.juno-badge` computed a transparent border — it sets
177
+ `forced-color-adjust: none` to keep its status fill, and that opt-out also
178
+ disables the UA repaint that was silently rescuing `.juno-btn` and `.juno-card`.
179
+
180
+ New `src/css/overrides.css`, bundled last, is where cross-cutting `@media` /
181
+ `@supports` gates live now — after everything they guard. Same reasoning the
182
+ bundler already applied to `utilities.css`.
183
+
184
+ - 2ec7cff: Stop publishing junoui's internal release process to consumers: the pre-release
185
+ consumer gate document moves from `docs/release-gate.md` to `RELEASING.md` at
186
+ the repo root, beside CONTRIBUTING.md, which has never shipped. `files` carries
187
+ `docs` wholesale, so the rule is now positional — `docs/` IS the published
188
+ manual, and contributor or process documents live at the root. The published
189
+ surface loses one file (204 → 203) and no consumer-facing content changes.
190
+
3
191
  ## 0.5.0
4
192
 
5
193
  ### Minor Changes
package/README.md CHANGED
@@ -61,22 +61,40 @@ build-time sRGB hex (identical rendering).
61
61
  | ---------------- | -------------------------- | ------------------------------- |
62
62
  | CSS, SCSS, JS/TS | Android (xml), iOS (Swift) | Flutter (Dart), JSON (W3C DTCG) |
63
63
 
64
+ ## Browser support
65
+
66
+ **Supported floor: Safari / iOS 17.5, Chrome / Edge 117, Firefox 129.**
67
+ Below **17.0 / 114 / 125** (the `browserslist` in `package.json`) the Popover
68
+ API is absent and the overlay surfaces — menu, popover, tooltip, and the
69
+ pillbar's overflow slot — stop working; junoui ships an `@supports` guard so
70
+ they are absent rather than invisibly blocking taps. Between the two floors
71
+ everything functions and entry animations are missing. Full fidelity, including
72
+ anchored placement for those surfaces, wants Safari 26.
73
+
74
+ CSS failures are silent — an unsupported at-rule is dropped, never reported —
75
+ so the degrade-vs-break audit, the version table and the guard rule live in
76
+ **[browser-support.md](./docs/browser-support.md)**. Read it before adding a
77
+ feature newer than the floor.
78
+
64
79
  ## Documentation
65
80
 
66
- | | |
67
- | -------------------------------------------------------------------------------- | ------------------------------------------------------------- |
68
- | [Getting started](./docs/getting-started.md) | Install, model, what ships |
69
- | [Integration](./docs/integration.md) | Consuming in an app: import order, fonts, token bridge |
70
- | [Web](./docs/web.md) · [Native](./docs/native.md) · [Flutter](./docs/flutter.md) | Per-platform usage |
71
- | [Design guidelines](./docs/design-guidelines.md) | Color semantics, a11y — for any tool |
72
- | [Layout](./docs/layout.md) | Responsive primitives + container queries (how blocks adapt) |
73
- | [Boot shell](./docs/boot-shell.md) | Fast first paint: pre-bundle shell, lazy screens, warming |
74
- | [Accessibility](./docs/accessibility.md) | WCAG 2.2 + WAI-ARIA references, focus, motion, targets, RTL |
75
- | [iOS conformance](./docs/ios-conformance.md) | Sourced iOS metrics safe areas, tap targets, viewport units |
76
- | [Token reference](./docs/tokens-reference.md) | Every token + value + platform name (generated) |
77
- | [Components](./docs/components/README.md) | 30+ forms, overlays, table, alerts, tabs, icons, nav, more |
78
- | [Contributing](./CONTRIBUTING.md) | Add tokens/components, lint, test, release |
79
- | [Roadmap](./docs/roadmap.md) | Missing capabilities, prioritised |
81
+ | | |
82
+ | -------------------------------------------------------------------------------- | ----------------------------------------------------------------- |
83
+ | [Getting started](./docs/getting-started.md) | Install, model, what ships |
84
+ | [Integration](./docs/integration.md) | Consuming in an app: import order, fonts, token bridge |
85
+ | [Web](./docs/web.md) · [Native](./docs/native.md) · [Flutter](./docs/flutter.md) | Per-platform usage |
86
+ | [Design guidelines](./docs/design-guidelines.md) | Color semantics, a11y — for any tool |
87
+ | [Layout](./docs/layout.md) | Responsive primitives + container queries (how blocks adapt) |
88
+ | [Boot shell](./docs/boot-shell.md) | Fast first paint: pre-bundle shell, lazy screens, warming |
89
+ | [Accessibility](./docs/accessibility.md) | WCAG 2.2 + WAI-ARIA references, focus, motion, targets, RTL |
90
+ | [iOS & PWA](./docs/ios-pwa.md) | The bounded claim: what's free, what you supply, what it won't do |
91
+ | [iOS conformance](./docs/ios-conformance.md) | Sourced iOS metrics safe areas, tap targets, viewport units |
92
+ | [Browser support](./docs/browser-support.md) | The supported floor, what degrades below it, what breaks |
93
+ | [Token reference](./docs/tokens-reference.md) | Every token + value + platform name (generated) |
94
+ | [Components](./docs/components/README.md) | 30+ — forms, overlays, table, alerts, tabs, icons, nav, more |
95
+ | [Release gate](./RELEASING.md) | The consumer build every release candidate must pass |
96
+ | [Contributing](./CONTRIBUTING.md) | Add tokens/components, lint, test, release |
97
+ | [Roadmap](./docs/roadmap.md) | Missing capabilities, prioritised |
80
98
 
81
99
  ## Repository
82
100
 
@@ -116,6 +134,18 @@ remain — publishes `@junoput01/junoui` to npm automatically. No manual `npm ru
116
134
  version` step. Bump by the token contract: remove/rename a token or class → major,
117
135
  additive → minor, fix → patch. See [CONTRIBUTING](./CONTRIBUTING.md#releasing).
118
136
 
137
+ One step is **not** automated, on purpose. Before merging the version PR, run
138
+
139
+ ```sh
140
+ npm run gate:consumer
141
+ ```
142
+
143
+ which packs the candidate, installs that tarball into a throwaway checkout of a real
144
+ consumer, and runs the consumer's typecheck, tests and production build against it. A
145
+ red gate blocks the release. It is a local step because the consumer lives on the dev
146
+ box, not in this repo's CI — [RELEASING.md](./RELEASING.md) explains
147
+ what it asserts and what it would take to move it into CI.
148
+
119
149
  ## License
120
150
 
121
151
  MIT