vireuikit 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/README.md CHANGED
@@ -12,7 +12,7 @@ capsule-to-target growth morph.
12
12
  npm install vireuikit vireglass
13
13
  ```
14
14
 
15
- `vireglass` is a peer dependency — this package draws on its material model rather than
15
+ `vireglass` (3.x) is a peer dependency — this package draws on its material model rather than
16
16
  shipping its own.
17
17
 
18
18
  ## Atom state and morph
@@ -43,6 +43,116 @@ const frame = growthFrame(sourceShape, targetShape, anchorPoint, growthPhase(sta
43
43
  // frame.body: the shape to draw as glass this frame.
44
44
  ```
45
45
 
46
+ The default timing is fitted to Apple's reference of a menu growing out of its toolbar. The menu itself is a model
47
+ too, so every target shares its keyboard, where it sits and what its surface is on each frame:
48
+
49
+ ```ts
50
+ import { MENU_CLOSED, menuPlacement, menuSurfaceFrame, stepMenu } from 'vireuikit';
51
+
52
+ const step = stepMenu(MENU_CLOSED, { type: 'open', focus: 'first' }, items); // items: { label, disabled, focusable? }[]
53
+ // step.focus says where focus goes; a choice always closes the menu.
54
+ const { rect, anchor } = menuPlacement(triggerRect, menuSize, viewport);
55
+ const surface = menuSurfaceFrame(triggerBox, { ...rect, radius }, anchor, growthPhase(state));
56
+ ```
57
+
58
+ ## Web elements
59
+
60
+ `vireuikit/web` upgrades native controls with custom elements. The control inside works before the
61
+ upgrade; afterwards the element drives its states from the model. Glass is opt-in: put `glass` on pieces
62
+ of the navigation layer, and leave it off for content.
63
+
64
+ ```html
65
+ <vire-button><button>Play</button></vire-button>
66
+ <vire-icon-button glass><button aria-label="Share">…</button></vire-icon-button>
67
+ ```
68
+
69
+ ```ts
70
+ import { defineVireElements, setA11yOverride } from 'vireuikit/web';
71
+
72
+ defineVireElements({ nonce: cspNonce });
73
+ setA11yOverride({ reduceTransparency: true });
74
+ ```
75
+
76
+ - `<vire-button>` is an action: it sinks and grows slightly under press, and nothing opens out of it unless it is the
77
+ trigger of a `<vire-menu>`.
78
+ - `<vire-icon-button>` lifts into the glass under the finger; the lens response is visible while the
79
+ press is held.
80
+ - Attributes: `glass` (empty or `present`: a lens at rest; `touch`: a lens only under the finger; absent: no
81
+ glass), `size` (`touch`, `small`, `medium`, `large`, `xlarge`; by default `touch` on a coarse pointer and
82
+ `medium` otherwise), `disabled`.
83
+ - On a coarse pointer every size keeps a hit area of at least 44 dp without changing layout.
84
+ - The lens reads the backdrop from declared colours and cannot see an image, a video or a canvas. A host that knows
85
+ what lies under the control sets the element's `sample` (a `BackdropSample` from `vireglass`), at any time; `null`
86
+ hands it back to the probe.
87
+
88
+ ```ts
89
+ import { statsFromSamples } from 'vireglass/dom';
90
+
91
+ playButton.sample = statsFromSamples(coverColours); // [{ r, g, b }, …] in 0–255
92
+ ```
93
+
94
+ - The element dispatches `vire-glass-ready` once its lens is attached. If the lens cannot be loaded or attached,
95
+ the control stays a complete one on its plain (or, under reduced transparency, opaque frosted) surface, and the next
96
+ setting change tries again.
97
+ - Set `disabled` on the host element: it is mirrored onto the inner `<button>` and wins over it. Clearing it on
98
+ the host re-enables the button.
99
+ - Under `forced-colors` the elements keep the system colours and drop their own tints and blur.
100
+ - Accessibility follows the system `prefers-*` settings; `setA11yOverride` or the element's `a11y`
101
+ property overrides them. Reduced transparency keeps the lens and frosts it: a blurrier lens and a denser body, with the
102
+ ink following the body. Text takes the page's colour; the frosted fallback and the high-contrast bodies assume
103
+ the page's `color-scheme` matches it.
104
+ - The elements inject one stylesheet. Under a strict CSP pass `nonce` to `defineVireElements`, or ship the
105
+ exported `STYLES` string yourself.
106
+ - Importing the module touches no DOM, so it is safe on a server; `defineVireElements()` is the only call
107
+ that registers anything. An element defined before its markup is parsed waits for its button.
108
+
109
+ ### Menu
110
+
111
+ `<vire-menu>` wraps a platform popover menu: a trigger button with `popovertarget` (bare, or inside `<vire-button>` or
112
+ `<vire-icon-button>`) and a `popover` list of item buttons.
113
+
114
+ ```html
115
+ <vire-menu glass>
116
+ <vire-icon-button glass>
117
+ <button popovertarget="more" aria-label="More">…</button>
118
+ </vire-icon-button>
119
+ <div id="more" popover>
120
+ <button popovertarget="more" popovertargetaction="hide"><svg>…</svg>Copy link</button>
121
+ <button popovertarget="more" popovertargetaction="hide">Rename</button>
122
+ <button popovertarget="more" popovertargetaction="hide" disabled>Delete</button>
123
+ </div>
124
+ </vire-menu>
125
+ ```
126
+
127
+ - Before the element is defined the browser runs it: the trigger toggles the list, a choice clicks the item and closes
128
+ it, Escape and a press outside dismiss it, and focus comes back to the trigger. Write the markup without menu roles.
129
+ - After the upgrade the element adds the menu-button roles (`aria-haspopup`, `aria-expanded`, `menu`, `menuitem`)
130
+ together with their keyboard: Enter, Space or ArrowDown open on the first item and ArrowUp on the last; the arrows,
131
+ Home, End and typeahead move over the items; Escape and a choice return focus to the trigger; Tab closes and moves
132
+ on. A pointer opens it with focus on the menu. Removing the element restores the markup.
133
+ - Disabled items are yours to set and clear at any time; the element never rewrites them. A `disabled` item cannot take
134
+ focus, so the keyboard passes over it; an `aria-disabled="true"` item stays focusable, the WAI-ARIA APG way. Neither can
135
+ be chosen.
136
+ - The press that opened the menu chooses nothing: a pointer click on an item counts once the menu has arrived, a key
137
+ once its content shows, and neither the second click of a double click nor a held key ever counts.
138
+ - The list's own `beforetoggle` and `toggle` events fire when it leaves the top layer, after the growth has run back;
139
+ `aria-expanded` turns false as the closing begins.
140
+ - The menu grows out of its trigger and covers it: the trigger's body contracts into a drop, the drop grows into the
141
+ menu with a small overshoot, and the content comes into focus near size. A choice closes it, and closing runs the
142
+ same path back into the trigger. Reduced motion opens and closes it in one frame. A menu that has to stay open for
143
+ several picks is a popover, which is another atom.
144
+ - While the menu is out, the trigger hands its body to the menu's surface (`setCarried` on `<vire-button>` and
145
+ `<vire-icon-button>`), so there is never glass on glass.
146
+ - Attributes: `glass` (empty, `present` or `touch`: the surface is a lens; absent or anything else: an opaque surface), `size` (as for the buttons; rows are as tall
147
+ as the tier), `corner` (`top-left`, `top-right`, `bottom-left`, `bottom-right`: the trigger corner the menu pins to;
148
+ by default the one facing the nearest viewport corner). `sample` works as on the buttons; without one, the backdrop
149
+ under the menu is read once as it opens and held while it is out.
150
+
151
+ Sizes, behaviour state machines and accessibility modes are exported from `vireuikit` and shared by every
152
+ target. How an atom is accepted:
153
+ [design](https://github.com/pruffit/vireuikit/blob/main/docs/design.md),
154
+ [acceptance](https://github.com/pruffit/vireuikit/blob/main/docs/acceptance.md).
155
+
46
156
  ## License
47
157
 
48
158
  Apache-2.0. See [LICENSE](./LICENSE) and [NOTICE](./NOTICE).
@@ -0,0 +1,25 @@
1
+ type A11yMode = {
2
+ reduceTransparency: boolean;
3
+ increaseContrast: boolean;
4
+ reduceMotion: boolean;
5
+ };
6
+ declare const A11Y_NONE: A11yMode;
7
+ /** `undefined` follows the system; a boolean overrides it. */
8
+ type A11yOverride = {
9
+ [K in keyof A11yMode]?: boolean | undefined;
10
+ };
11
+ declare function resolveA11y(system: A11yMode, override?: A11yOverride): A11yMode;
12
+ /** `plain`: no glass was asked for. `frosted`: the stand-in when glass under reduced transparency cannot attach. */
13
+ type Surface = 'plain' | 'frosted' | 'glass';
14
+ type Presentation = {
15
+ glass: boolean;
16
+ /** Reduced transparency: the glass stays and frosts. */
17
+ frost: boolean;
18
+ surface: Surface;
19
+ contrastBorder: boolean;
20
+ instantSprings: boolean;
21
+ };
22
+ declare function presentationFor(mode: A11yMode, wantsGlass: boolean): Presentation;
23
+ declare function sameA11y(a: A11yMode, b: A11yMode): boolean;
24
+
25
+ export { A11Y_NONE as A, type Presentation as P, type Surface as S, type A11yMode as a, type A11yOverride as b, presentationFor as p, resolveA11y as r, sameA11y as s };
@@ -0,0 +1,25 @@
1
+ type A11yMode = {
2
+ reduceTransparency: boolean;
3
+ increaseContrast: boolean;
4
+ reduceMotion: boolean;
5
+ };
6
+ declare const A11Y_NONE: A11yMode;
7
+ /** `undefined` follows the system; a boolean overrides it. */
8
+ type A11yOverride = {
9
+ [K in keyof A11yMode]?: boolean | undefined;
10
+ };
11
+ declare function resolveA11y(system: A11yMode, override?: A11yOverride): A11yMode;
12
+ /** `plain`: no glass was asked for. `frosted`: the stand-in when glass under reduced transparency cannot attach. */
13
+ type Surface = 'plain' | 'frosted' | 'glass';
14
+ type Presentation = {
15
+ glass: boolean;
16
+ /** Reduced transparency: the glass stays and frosts. */
17
+ frost: boolean;
18
+ surface: Surface;
19
+ contrastBorder: boolean;
20
+ instantSprings: boolean;
21
+ };
22
+ declare function presentationFor(mode: A11yMode, wantsGlass: boolean): Presentation;
23
+ declare function sameA11y(a: A11yMode, b: A11yMode): boolean;
24
+
25
+ export { A11Y_NONE as A, type Presentation as P, type Surface as S, type A11yMode as a, type A11yOverride as b, presentationFor as p, resolveA11y as r, sameA11y as s };