@hanzo/ui 8.29.12 → 8.29.14

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.
@@ -6,27 +6,39 @@ const jsx_runtime_1 = require("react/jsx-runtime");
6
6
  /**
7
7
  * Consent — the one cookie banner every Hanzo, Lux and Zoo site mounts.
8
8
  *
9
- * It draws the choice; @hanzo/event stores it (`hz_consent`) and acts on it:
10
- * nothing from an ad platform, no Google, and no event on our own stream before
11
- * Accept, and Google Consent Mode v2 starts denied.
9
+ * It draws the choice; @hanzo/event stores it (`hz_consent`) and acts on it: no
10
+ * Google Analytics, no Meta pixel and no ad personalization before Accept, and
11
+ * Google Consent Mode v2 starts denied. The brand's own first-party analytics is
12
+ * strictly necessary and runs for every visitor; the choice does not govern it.
12
13
  *
13
- * - every visitor, wherever they are, is asked: a banner with Accept all,
14
- * Reject all and Choose, until they have chosen. Nothing optional loads first.
14
+ * - every visitor, wherever they are, is asked: a bar with Manage preferences,
15
+ * Accept all and a ×, until they have chosen. Nothing optional loads first.
16
+ * - the bar's × is a choice, not a dismissal: it stores essential cookies only
17
+ * and closes, so refusing is one press, exactly as accepting is.
18
+ * - Manage preferences opens the Cookie Preferences sheet: a modal with one
19
+ * section per category, Strictly Necessary always on, the rest switches that
20
+ * start off until chosen, and the policy links.
15
21
  * - Global Privacy Control or Do Not Track is the visitor's answer already: they
16
22
  * are not asked, nothing optional runs, and the switches are locked off.
17
- * - cloud serves the policy with the site's tag config; the banner redraws when
18
- * it arrives, and asks again when the policy version changes.
23
+ * - cloud serves the policy with the site's tag config; the bar redraws when it
24
+ * arrives, and asks again when the policy version changes.
19
25
  *
20
- * `Consent` mounts once near the root; `ConsentLink` goes wherever a footer
21
- * link goes and opens the panel from anywhere. `brand` names who is asking.
26
+ * `Consent` mounts once near the root. `openConsent()`, `ConsentLink` and any
27
+ * link to `#choices` (the footer's Privacy choices) open the sheet from anywhere.
28
+ * `brand` names who is asking; `cookies`, `privacy` and `terms` are the brand's
29
+ * own policies.
22
30
  */
23
31
  const gui_1 = require("@hanzo/gui");
32
+ const lucide_icons_2_1 = require("@hanzogui/lucide-icons-2");
24
33
  const event_1 = require("@hanzo/event");
25
34
  const react_1 = require("react");
26
35
  const button_1 = require("../backends/gui/button.cjs");
36
+ const dir_1 = require("../backends/gui/dir.cjs");
27
37
  const switch_1 = require("../backends/gui/switch.cjs");
28
38
  const slot_1 = require("../backends/gui/slot.cjs");
29
39
  const OPEN = 'hzconsent-open';
40
+ /** The address that opens the sheet, from any link on any page. */
41
+ const HASH = '#choices';
30
42
  /** Opens the choices from anywhere. */
31
43
  const openConsent = () => {
32
44
  if (typeof window !== 'undefined')
@@ -34,24 +46,56 @@ const openConsent = () => {
34
46
  };
35
47
  exports.openConsent = openConsent;
36
48
  const kinds = (brand) => [
37
- { key: 'analytics', title: 'Analytics', body: `Counts visits and finds what is broken, so ${brand} can get better.` },
38
- { key: 'marketing', title: 'Marketing', body: 'Lets ad platforms measure the sign-ups and orders their ads led to.' },
39
- { key: 'ads', title: 'Personalized ads', body: 'Lets those platforms use your visit to show you ads elsewhere.' },
49
+ {
50
+ id: 'necessary',
51
+ title: 'Strictly Necessary Cookies',
52
+ body: `These are needed for the site to work, so they are always on. They keep you signed in and your session secure, protect forms against cross-site request forgery, spread traffic across servers, remember the choice you make here, and keep what you have typed into a form. They include ${brand}'s own first-party analytics, which counts visits and finds what is broken for ${brand} alone and is never sold or shared.`,
53
+ },
54
+ {
55
+ id: 'analytics',
56
+ key: 'analytics',
57
+ title: 'Performance Cookies',
58
+ body: `Google Analytics: lets Google measure visits to ${brand} with its own cookies.`,
59
+ },
60
+ {
61
+ id: 'marketing',
62
+ key: 'marketing',
63
+ title: 'Marketing Cookies',
64
+ body: `The Meta pixel: lets Meta measure the sign-ups and orders ${brand}'s ads led to.`,
65
+ },
66
+ {
67
+ id: 'ads',
68
+ key: 'ads',
69
+ title: 'Sell, Share, or Use Your Data for Targeted Advertising',
70
+ body: 'Lets Google and Meta use your visit to choose the ads they show you on other sites and apps. Some US state privacy laws call this a “sale” or “sharing” of your personal information, or “targeted advertising”, and give you the right to opt out: it stays off unless you turn it on.',
71
+ },
40
72
  ];
41
- /** The banner's inset from the window's foot and sides: 16px, or the device's inset and 8px where that is wider. */
73
+ /** The bar's and the sheet's inset from the window's edges: 16px, or the device's inset and 8px where that is wider. */
42
74
  const EDGE = {
75
+ top: 'max(16px, calc(var(--safe-top, env(safe-area-inset-top, 0px)) + 8px))',
43
76
  bottom: 'max(16px, calc(var(--safe-bottom, env(safe-area-inset-bottom, 0px)) + 8px))',
44
77
  left: 'max(16px, calc(var(--safe-left, env(safe-area-inset-left, 0px)) + 8px))',
45
78
  right: 'max(16px, calc(var(--safe-right, env(safe-area-inset-right, 0px)) + 8px))',
46
79
  };
80
+ /** The panel both wear: the card's ground, edge, corner (`$6`, the sign-in card's) and floating shadow. */
81
+ const PANEL = {
82
+ bg: '$background',
83
+ borderWidth: 1,
84
+ borderColor: '$borderColor',
85
+ rounded: '$6',
86
+ shadowColor: 'rgba(0,0,0,0.25)',
87
+ shadowRadius: 24,
88
+ shadowOffset: { width: 0, height: 8 },
89
+ };
47
90
  /**
48
- * Where the banner sits: fixed to the foot of the window, clear of the home
91
+ * Where the bar sits: fixed to the foot of the window, clear of the home
49
92
  * indicator and the notch, centred. It stands on design's banner rung
50
93
  * (`--z-banner`): over the page and its dock, under every overlay — a dialog, a
51
94
  * menu, a phone drawer — and is answered once they close.
52
95
  */
53
96
  const frame = {
54
97
  ...(0, slot_1.slot)('consent'),
98
+ ...PANEL,
55
99
  role: 'dialog',
56
100
  'aria-label': 'Cookie preferences',
57
101
  position: 'fixed',
@@ -60,46 +104,166 @@ const frame = {
60
104
  r: EDGE.right,
61
105
  mx: 'auto',
62
106
  z: 'var(--z-banner, 50)',
63
- py: '$2.5',
64
- px: '$3.5',
65
- rounded: '$5',
66
- borderWidth: 1,
67
- borderColor: '$borderColor',
68
- bg: '$background',
69
- shadowColor: 'rgba(0,0,0,0.25)',
70
- shadowRadius: 24,
71
- shadowOffset: { width: 0, height: 8 },
107
+ py: '$3',
108
+ paddingInlineStart: '$4',
109
+ // Clear of the × in the corner: its 32px, and 12px either side.
110
+ paddingInlineEnd: 56,
72
111
  };
73
112
  /** The bar is as wide as its line and buttons, up to the window less its inset. */
74
113
  const FIT = { width: 'fit-content' };
75
- /** A link in the banner's line: the line's own ink, underlined, never broken across lines. */
114
+ /** The one filled control in the bar and in the sheet: the ink, as the sign-in card's Continue. */
115
+ const ACT = { bg: '$ink', color: '$background', borderColor: '$ink', hoverStyle: { opacity: 0.92 } };
116
+ /** A link in the sheet's intro: the line's own ink, underlined, never broken across lines. */
76
117
  const INLINE = { color: 'inherit', textDecoration: 'underline', whiteSpace: 'nowrap' };
77
- const Consent = ({ privacy = 'https://hanzo.ai/privacy', brand = 'Hanzo' }) => {
118
+ /** The sheet covers the window, on design's modal rung, over a scrim of its own. */
119
+ const COVER = { position: 'fixed', t: 0, r: 0, b: 0, l: 0 };
120
+ /** What Tab may land on inside the sheet. */
121
+ const FOCUSABLE = 'a[href], button:not([disabled]), [tabindex]:not([tabindex="-1"])';
122
+ /** A section's button never submits a form the sheet sits in. gui's stack props do not name `type`. */
123
+ const BUTTON = { type: 'button' };
124
+ const Consent = ({ cookies = 'https://hanzo.ai/cookies', privacy = 'https://hanzo.ai/privacy', terms = 'https://hanzo.ai/terms', brand = 'Hanzo', }) => {
78
125
  const [shown, setShown] = (0, react_1.useState)(false);
79
126
  const [choosing, setChoosing] = (0, react_1.useState)(false);
80
127
  const [draft, setDraft] = (0, react_1.useState)({ analytics: false, marketing: false, ads: false });
128
+ const [expanded, setExpanded] = (0, react_1.useState)({});
81
129
  // The choices are open because the visitor asked for them. A policy that
82
130
  // arrives after (cloud answers ~150ms into a page opened at #choices) redraws
83
- // the banner's question, and must not close what was asked for.
131
+ // the bar's question, and must not close what was asked for.
84
132
  const asked = (0, react_1.useRef)(false);
85
- // The browser refused, as the page sees it or as cloud saw the request.
86
- const locked = typeof window !== 'undefined' && (0, event_1.declined)();
133
+ // What had focus when the sheet opened, given it back when the sheet closes.
134
+ const back = (0, react_1.useRef)(null);
135
+ const sheet = (0, react_1.useRef)(null);
136
+ const manage = (0, react_1.useRef)(null);
137
+ const opened = (0, react_1.useRef)(false);
138
+ // The switches were filled from a stored choice, not left off.
139
+ const seeded = (0, react_1.useRef)(false);
140
+ const id = (0, react_1.useId)();
141
+ // The browser refused, as the page sees it or as cloud saw the request. State,
142
+ // so cloud's answer redraws an open sheet.
143
+ const [locked, setLocked] = (0, react_1.useState)(false);
144
+ const choose = () => {
145
+ // Already open: the visitor's unsaved switches and the focus to return stay as they are.
146
+ if (asked.current)
147
+ return;
148
+ const at = document.activeElement;
149
+ back.current = at instanceof HTMLElement && at !== document.body ? at : null;
150
+ asked.current = true;
151
+ setLocked((0, event_1.declined)());
152
+ seeded.current = (0, event_1.storedConsent)();
153
+ setDraft((0, event_1.readConsent)());
154
+ setChoosing(true);
155
+ setShown(true);
156
+ };
157
+ // Closing without a choice: a visitor who has not chosen is asked again by the bar.
158
+ const close = () => {
159
+ asked.current = false;
160
+ setChoosing(false);
161
+ setShown((0, event_1.asks)());
162
+ };
87
163
  (0, react_1.useEffect)(() => {
88
- const sync = () => setShown((0, event_1.asks)() || asked.current);
164
+ const sync = () => {
165
+ setShown((0, event_1.asks)() || asked.current);
166
+ setLocked((0, event_1.declined)());
167
+ // A policy version the stored choice was not made under: the switches the
168
+ // sheet opened with were an old grant, and are off until chosen again.
169
+ if (asked.current && seeded.current && !(0, event_1.storedConsent)()) {
170
+ seeded.current = false;
171
+ setDraft((0, event_1.readConsent)());
172
+ }
173
+ };
89
174
  sync();
90
- const open = () => {
91
- asked.current = true;
92
- setDraft((0, event_1.readConsent)());
93
- setChoosing(true);
94
- setShown(true);
175
+ // `#choices` opens the sheet: on arrival, on a hash change, and on a press of
176
+ // any link on this site to it, so the footer's Privacy choices and a link in
177
+ // a page are the same control.
178
+ const hash = () => {
179
+ if (window.location.hash !== HASH)
180
+ return;
181
+ // The router's own entry state is kept: only the address loses its hash. A
182
+ // microtask, so a router that wraps replaceState in its own mount effect
183
+ // (Next's, which runs after this child's) records the address without it.
184
+ queueMicrotask(() => history.replaceState(history.state, '', window.location.pathname + window.location.search));
185
+ choose();
186
+ };
187
+ // A plain press only: a modified one (new tab, new window) keeps the browser's
188
+ // meaning, and the page it opens at #choices opens the sheet on arrival.
189
+ const press = (e) => {
190
+ if (e.defaultPrevented || e.button !== 0 || e.metaKey || e.ctrlKey || e.shiftKey || e.altKey)
191
+ return;
192
+ // The attribute, not `.href`: an SVG link's `.href` is not a string.
193
+ const a = e.target?.closest?.('a[href]');
194
+ const href = a?.getAttribute('href');
195
+ const target = a?.getAttribute('target');
196
+ if (!href || a?.hasAttribute('download') || (target && target !== '_self'))
197
+ return;
198
+ let to;
199
+ try {
200
+ to = new URL(href, document.baseURI);
201
+ }
202
+ catch {
203
+ return;
204
+ }
205
+ if (to.hash !== HASH || to.origin !== window.location.origin)
206
+ return;
207
+ e.preventDefault();
208
+ choose();
95
209
  };
96
- window.addEventListener(OPEN, open);
210
+ hash();
211
+ window.addEventListener(OPEN, choose);
97
212
  window.addEventListener(event_1.POLICY_EVENT, sync);
213
+ window.addEventListener('hashchange', hash);
214
+ document.addEventListener('click', press, { capture: true });
98
215
  return () => {
99
- window.removeEventListener(OPEN, open);
216
+ window.removeEventListener(OPEN, choose);
100
217
  window.removeEventListener(event_1.POLICY_EVENT, sync);
218
+ window.removeEventListener('hashchange', hash);
219
+ document.removeEventListener('click', press, { capture: true });
101
220
  };
102
221
  }, []);
222
+ // Focus moves into the sheet when it opens, stays inside it while it is open,
223
+ // and returns to what opened it (or the bar's Manage preferences) when it closes.
224
+ (0, react_1.useEffect)(() => {
225
+ if (!choosing) {
226
+ if (!opened.current)
227
+ return;
228
+ opened.current = false;
229
+ const el = back.current;
230
+ back.current = null;
231
+ if (el?.isConnected)
232
+ el.focus();
233
+ else
234
+ manage.current?.focus();
235
+ return;
236
+ }
237
+ opened.current = true;
238
+ const all = () => [...(sheet.current?.querySelectorAll(FOCUSABLE) ?? [])];
239
+ all()[0]?.focus();
240
+ const keys = (e) => {
241
+ if (e.key === 'Escape') {
242
+ e.preventDefault();
243
+ close();
244
+ return;
245
+ }
246
+ if (e.key !== 'Tab')
247
+ return;
248
+ const list = all();
249
+ const first = list[0];
250
+ const last = list[list.length - 1];
251
+ if (!first || !last)
252
+ return;
253
+ const at = document.activeElement;
254
+ const inside = !!at && !!sheet.current?.contains(at);
255
+ if (e.shiftKey && (at === first || !inside)) {
256
+ e.preventDefault();
257
+ last.focus();
258
+ }
259
+ else if (!e.shiftKey && (at === last || !inside)) {
260
+ e.preventDefault();
261
+ first.focus();
262
+ }
263
+ };
264
+ document.addEventListener('keydown', keys);
265
+ return () => document.removeEventListener('keydown', keys);
266
+ }, [choosing]);
103
267
  if (!shown)
104
268
  return null;
105
269
  const done = (fn) => () => {
@@ -108,21 +272,18 @@ const Consent = ({ privacy = 'https://hanzo.ai/privacy', brand = 'Hanzo' }) => {
108
272
  setShown(false);
109
273
  setChoosing(false);
110
274
  };
111
- const choose = () => {
112
- asked.current = true;
113
- setDraft((0, event_1.readConsent)());
114
- setChoosing(true);
115
- setShown(true);
116
- };
117
275
  if (choosing)
118
- return ((0, jsx_runtime_1.jsxs)(gui_1.YStack, { ...frame, maxW: 440, gap: "$3", maxH: `calc(100dvh - var(--safe-top, env(safe-area-inset-top, 0px)) - var(--header, 0px) - ${EDGE.bottom} - 16px)`, overflowY: "auto", children: [(0, jsx_runtime_1.jsxs)(gui_1.Paragraph, { size: "$2", color: "$ink", m: 0, children: [`Choose what ${brand} may measure and share. You can change this at any time. `, (0, jsx_runtime_1.jsx)("a", { href: privacy, style: INLINE, children: "Privacy policy" })] }), locked ? ((0, jsx_runtime_1.jsx)(gui_1.Paragraph, { size: "$2", color: "$soft", m: 0, children: "Your browser asks sites not to track you, so all of these stay off." })) : null, kinds(brand).map((k) => {
119
- const off = locked;
120
- return ((0, jsx_runtime_1.jsxs)(gui_1.XStack, { gap: "$3", items: "center", justify: "space-between", children: [(0, jsx_runtime_1.jsxs)(gui_1.YStack, { flex: 1, children: [(0, jsx_runtime_1.jsx)(gui_1.Paragraph, { size: "$2", color: "$ink", fontWeight: "500", m: 0, children: k.title }), (0, jsx_runtime_1.jsx)(gui_1.Paragraph, { size: "$1", color: "$soft", m: 0, children: k.body })] }), (0, jsx_runtime_1.jsx)(switch_1.Switch, { "aria-label": k.title, disabled: off, checked: off ? false : draft[k.key], onCheckedChange: (v) => setDraft({ ...draft, [k.key]: v === true }) })] }, k.key));
121
- }), (0, jsx_runtime_1.jsxs)(gui_1.XStack, { gap: "$2", justify: "flex-end", children: [(0, jsx_runtime_1.jsx)(button_1.Button, { type: "button", size: "sm", variant: "secondary", onClick: done(event_1.rejectAll), children: "Reject all" }), (0, jsx_runtime_1.jsx)(button_1.Button, { type: "button", size: "sm", onClick: done(() => (0, event_1.saveConsent)(draft)), children: "Save choices" })] })] }));
122
- // One bar: the line and its link, then the buttons. They share a row where
123
- // the line fits beside them and the buttons drop under it where it does not,
124
- // so a phone shows the line over one row of buttons and nothing breaks mid-control.
125
- return ((0, jsx_runtime_1.jsxs)(gui_1.XStack, { ...frame, style: FIT, maxW: 960, flexWrap: "wrap", items: "center", columnGap: "$4", rowGap: "$2", children: [(0, jsx_runtime_1.jsx)(gui_1.YStack, { shrink: 1, minW: 0, children: (0, jsx_runtime_1.jsxs)(gui_1.Paragraph, { size: "$2", color: "$ink", m: 0, children: ['Cookies measure visits and ads once you accept. ', (0, jsx_runtime_1.jsx)("a", { href: privacy, style: INLINE, children: "Privacy policy" })] }) }), (0, jsx_runtime_1.jsxs)(gui_1.XStack, { gap: "$2", marginInlineStart: "auto", items: "center", children: [(0, jsx_runtime_1.jsx)(button_1.Button, { type: "button", size: "sm", variant: "link", onClick: choose, children: "Choose" }), (0, jsx_runtime_1.jsx)(button_1.Button, { type: "button", size: "sm", variant: "secondary", onClick: done(event_1.rejectAll), children: "Reject all" }), (0, jsx_runtime_1.jsx)(button_1.Button, { type: "button", size: "sm", onClick: done(event_1.acceptAll), children: "Accept all" })] })] }));
276
+ return ((0, jsx_runtime_1.jsxs)(gui_1.YStack, { ...(0, slot_1.slot)('consent-sheet'), ...COVER, z: "var(--z-modal, 600)", items: "center", justify: "center",
277
+ // The device's insets are physical sides, so the shorthand names them in order.
278
+ style: { padding: `${EDGE.top} ${EDGE.right} ${EDGE.bottom} ${EDGE.left}` }, children: [(0, jsx_runtime_1.jsx)(gui_1.YStack, { ...COVER, position: "absolute", z: 0, bg: "rgba(0,0,0,0.6)", "aria-hidden": true, onPress: close }), (0, jsx_runtime_1.jsxs)(gui_1.YStack, { ...(0, slot_1.slot)('consent'), ...PANEL, ref: sheet, role: "dialog", "aria-modal": true, "aria-labelledby": `${id}-title`, position: "relative", width: "100%", maxW: 560, maxH: "100%", overflow: "hidden", children: [(0, jsx_runtime_1.jsxs)(gui_1.XStack, { items: "center", justify: "space-between", gap: "$3", pt: "$5", pb: "$3", paddingInlineStart: "$5", paddingInlineEnd: "$4", children: [(0, jsx_runtime_1.jsx)(gui_1.Text, { render: "h2", id: `${id}-title`, m: 0, fontSize: "$6", lineHeight: "$6", fontWeight: "600", color: "$ink", children: "Cookie Preferences" }), (0, jsx_runtime_1.jsx)(button_1.Button, { type: "button", size: "icon-sm", variant: "ghost", "aria-label": "Close", onClick: close, children: (0, jsx_runtime_1.jsx)(lucide_icons_2_1.X, { size: 16 }) })] }), (0, jsx_runtime_1.jsxs)(gui_1.YStack, { shrink: 1, minH: 0, overflowY: "auto", px: "$5", pb: "$2", style: { overscrollBehavior: 'contain' }, children: [(0, jsx_runtime_1.jsxs)(gui_1.Paragraph, { size: "$2", color: "$aside", m: 0, pb: "$3", children: ['We use cookies and similar technologies to provide our services, measure performance, and support advertising. Essential cookies are required for the site to work and cannot be turned off. You can allow or reject optional cookies below. Learn more in our ', (0, jsx_runtime_1.jsx)("a", { href: cookies, style: INLINE, children: "Cookie Policy" }), ', ', (0, jsx_runtime_1.jsx)("a", { href: privacy, style: INLINE, children: "Privacy Policy" }), ', and ', (0, jsx_runtime_1.jsx)("a", { href: terms, style: INLINE, children: "Terms of Service" }), "."] }), locked ? ((0, jsx_runtime_1.jsx)(gui_1.Paragraph, { size: "$2", color: "$ink", m: 0, pb: "$3", children: "Your browser asks sites not to track you, so all of these stay off." })) : null, kinds(brand).map((k) => {
279
+ const open = expanded[k.id] === true;
280
+ const body = `${id}-${k.id}`;
281
+ return ((0, jsx_runtime_1.jsxs)(gui_1.YStack, { ...(0, slot_1.slot)('consent-section'), borderTopWidth: 1, borderColor: "$borderColor", children: [(0, jsx_runtime_1.jsxs)(gui_1.XStack, { items: "center", gap: "$3", minH: 52, py: "$2", children: [(0, jsx_runtime_1.jsx)(gui_1.XStack, { render: "h3", flex: 1, minW: 0, m: 0, children: (0, jsx_runtime_1.jsxs)(gui_1.XStack, { render: "button", ...BUTTON, "aria-expanded": open, "aria-controls": body, onPress: () => setExpanded({ ...expanded, [k.id]: !open }), flex: 1, minW: 0, items: "center", gap: "$2", p: 0, bg: "transparent", borderWidth: 0, cursor: "pointer", children: [(0, jsx_runtime_1.jsx)(gui_1.XStack, { shrink: 0, rotate: open ? '180deg' : '0deg', opacity: 0.7, children: (0, jsx_runtime_1.jsx)(lucide_icons_2_1.ChevronDown, { size: 16 }) }), (0, jsx_runtime_1.jsx)(gui_1.Text, { fontSize: "$3", lineHeight: "$3", fontWeight: "600", color: "$ink", ...(0, dir_1.align)('start'), children: k.title })] }) }), k.key ? ((0, jsx_runtime_1.jsx)(switch_1.Switch, { "aria-label": k.title, "aria-describedby": body, disabled: locked, checked: locked ? false : draft[k.key], onCheckedChange: (v) => setDraft({ ...draft, [k.key]: v === true }) })) : ((0, jsx_runtime_1.jsx)(gui_1.Text, { fontSize: "$2", fontWeight: "600", color: "$ink", whiteSpace: "nowrap", children: "Always Active" }))] }), (0, jsx_runtime_1.jsx)(gui_1.YStack, { id: body, display: open ? 'flex' : 'none', pb: "$3", paddingInlineStart: 24, children: (0, jsx_runtime_1.jsx)(gui_1.Paragraph, { size: "$2", color: "$aside", m: 0, children: k.body }) })] }, k.id));
282
+ })] }), (0, jsx_runtime_1.jsxs)(gui_1.XStack, { flexWrap: "wrap", gap: "$2", px: "$5", py: "$4", borderTopWidth: 1, borderColor: "$borderColor", children: [locked ? null : ((0, jsx_runtime_1.jsx)(button_1.Button, { type: "button", variant: "secondary", grow: 1, onClick: done(event_1.acceptAll), children: "Accept all" })), (0, jsx_runtime_1.jsx)(button_1.Button, { type: "button", ...ACT, grow: 1, onClick: done(() => (0, event_1.saveConsent)(draft)), children: "Save preferences" })] })] })] }));
283
+ // One bar: the line, then the buttons. They share a row where the line fits
284
+ // beside them and the buttons drop under it where it does not, so a phone shows
285
+ // the line over one row of buttons and nothing breaks mid-control.
286
+ return ((0, jsx_runtime_1.jsxs)(gui_1.XStack, { ...frame, style: FIT, maxW: 1200, flexWrap: "wrap", items: "center", columnGap: "$4", rowGap: "$3", children: [(0, jsx_runtime_1.jsx)(gui_1.YStack, { shrink: 1, minW: 0, children: (0, jsx_runtime_1.jsx)(gui_1.Paragraph, { size: "$2", color: "$ink", m: 0, children: `Essential cookies keep ${brand} running. With your OK, optional cookies help us measure performance and ads.` }) }), (0, jsx_runtime_1.jsxs)(gui_1.XStack, { gap: "$2", marginInlineStart: "auto", items: "center", flexWrap: "wrap", children: [(0, jsx_runtime_1.jsx)(button_1.Button, { ref: manage, type: "button", size: "sm", variant: "secondary", onClick: choose, children: "Manage preferences" }), (0, jsx_runtime_1.jsx)(button_1.Button, { type: "button", size: "sm", ...ACT, onClick: done(event_1.acceptAll), children: "Accept all" })] }), (0, jsx_runtime_1.jsx)(button_1.Button, { type: "button", size: "icon-sm", variant: "ghost", "aria-label": "Close and use essential cookies only", position: "absolute", t: "$3", insetInlineEnd: "$3", onClick: done(event_1.rejectAll), children: (0, jsx_runtime_1.jsx)(lucide_icons_2_1.X, { size: 16 }) })] }));
126
287
  };
127
288
  exports.Consent = Consent;
128
289
  /** The footer link that reopens the choices, in every footer and every region. */
@@ -1,12 +1,16 @@
1
1
  /** Opens the choices from anywhere. */
2
2
  export declare const openConsent: () => void;
3
3
  export type ConsentProps = {
4
+ /** Where the cookie policy lives. */
5
+ cookies?: string;
4
6
  /** Where the privacy policy lives. */
5
7
  privacy?: string;
8
+ /** Where the terms of service live. */
9
+ terms?: string;
6
10
  /** Who is asking: the brand the visitor is on. */
7
11
  brand?: string;
8
12
  };
9
- declare const Consent: ({ privacy, brand }: ConsentProps) => import("react").JSX.Element | null;
13
+ declare const Consent: ({ cookies, privacy, terms, brand, }: ConsentProps) => import("react").JSX.Element | null;
10
14
  /** The footer link that reopens the choices, in every footer and every region. */
11
15
  declare const ConsentLink: ({ label }: {
12
16
  label?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"Consent.d.ts","sourceRoot":"","sources":["../../src/consent/Consent.tsx"],"names":[],"mappings":"AA6BA,uCAAuC;AACvC,eAAO,MAAM,WAAW,QAAO,IAE9B,CAAA;AAiDD,MAAM,MAAM,YAAY,GAAG;IACzB,sCAAsC;IACtC,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,kDAAkD;IAClD,KAAK,CAAC,EAAE,MAAM,CAAA;CACf,CAAA;AAED,QAAA,MAAM,OAAO,uBAA+D,YAAY,uCA0HvF,CAAA;AAED,kFAAkF;AAClF,QAAA,MAAM,WAAW,cAAmC;IAAE,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,gCAIrE,CAAA;AAED,OAAO,EAAE,OAAO,EAAE,WAAW,EAAE,CAAA"}
1
+ {"version":3,"file":"Consent.d.ts","sourceRoot":"","sources":["../../src/consent/Consent.tsx"],"names":[],"mappings":"AA0CA,uCAAuC;AACvC,eAAO,MAAM,WAAW,QAAO,IAE9B,CAAA;AA0FD,MAAM,MAAM,YAAY,GAAG;IACzB,qCAAqC;IACrC,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,sCAAsC;IACtC,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,uCAAuC;IACvC,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,kDAAkD;IAClD,KAAK,CAAC,EAAE,MAAM,CAAA;CACf,CAAA;AAED,QAAA,MAAM,OAAO,wCAKV,YAAY,uCA4Sd,CAAA;AAED,kFAAkF;AAClF,QAAA,MAAM,WAAW,cAAmC;IAAE,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,gCAIrE,CAAA;AAED,OAAO,EAAE,OAAO,EAAE,WAAW,EAAE,CAAA"}
@@ -3,51 +3,95 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  /**
4
4
  * Consent — the one cookie banner every Hanzo, Lux and Zoo site mounts.
5
5
  *
6
- * It draws the choice; @hanzo/event stores it (`hz_consent`) and acts on it:
7
- * nothing from an ad platform, no Google, and no event on our own stream before
8
- * Accept, and Google Consent Mode v2 starts denied.
6
+ * It draws the choice; @hanzo/event stores it (`hz_consent`) and acts on it: no
7
+ * Google Analytics, no Meta pixel and no ad personalization before Accept, and
8
+ * Google Consent Mode v2 starts denied. The brand's own first-party analytics is
9
+ * strictly necessary and runs for every visitor; the choice does not govern it.
9
10
  *
10
- * - every visitor, wherever they are, is asked: a banner with Accept all,
11
- * Reject all and Choose, until they have chosen. Nothing optional loads first.
11
+ * - every visitor, wherever they are, is asked: a bar with Manage preferences,
12
+ * Accept all and a ×, until they have chosen. Nothing optional loads first.
13
+ * - the bar's × is a choice, not a dismissal: it stores essential cookies only
14
+ * and closes, so refusing is one press, exactly as accepting is.
15
+ * - Manage preferences opens the Cookie Preferences sheet: a modal with one
16
+ * section per category, Strictly Necessary always on, the rest switches that
17
+ * start off until chosen, and the policy links.
12
18
  * - Global Privacy Control or Do Not Track is the visitor's answer already: they
13
19
  * are not asked, nothing optional runs, and the switches are locked off.
14
- * - cloud serves the policy with the site's tag config; the banner redraws when
15
- * it arrives, and asks again when the policy version changes.
20
+ * - cloud serves the policy with the site's tag config; the bar redraws when it
21
+ * arrives, and asks again when the policy version changes.
16
22
  *
17
- * `Consent` mounts once near the root; `ConsentLink` goes wherever a footer
18
- * link goes and opens the panel from anywhere. `brand` names who is asking.
23
+ * `Consent` mounts once near the root. `openConsent()`, `ConsentLink` and any
24
+ * link to `#choices` (the footer's Privacy choices) open the sheet from anywhere.
25
+ * `brand` names who is asking; `cookies`, `privacy` and `terms` are the brand's
26
+ * own policies.
19
27
  */
20
- import { Paragraph, XStack, YStack } from '@hanzo/gui';
21
- import { acceptAll, asks, declined, POLICY_EVENT, readConsent, rejectAll, saveConsent } from '@hanzo/event';
22
- import { useEffect, useRef, useState } from 'react';
28
+ import { Paragraph, Text, XStack, YStack } from '@hanzo/gui';
29
+ import { ChevronDown, X } from '@hanzogui/lucide-icons-2';
30
+ import { acceptAll, asks, declined, POLICY_EVENT, readConsent, rejectAll, saveConsent, storedConsent } from '@hanzo/event';
31
+ import { useEffect, useId, useRef, useState } from 'react';
23
32
  import { Button } from '../backends/gui/button.js';
33
+ import { align } from '../backends/gui/dir.js';
24
34
  import { Switch } from '../backends/gui/switch.js';
25
35
  import { slot } from '../backends/gui/slot.js';
26
36
  const OPEN = 'hzconsent-open';
37
+ /** The address that opens the sheet, from any link on any page. */
38
+ const HASH = '#choices';
27
39
  /** Opens the choices from anywhere. */
28
40
  export const openConsent = () => {
29
41
  if (typeof window !== 'undefined')
30
42
  window.dispatchEvent(new Event(OPEN));
31
43
  };
32
44
  const kinds = (brand) => [
33
- { key: 'analytics', title: 'Analytics', body: `Counts visits and finds what is broken, so ${brand} can get better.` },
34
- { key: 'marketing', title: 'Marketing', body: 'Lets ad platforms measure the sign-ups and orders their ads led to.' },
35
- { key: 'ads', title: 'Personalized ads', body: 'Lets those platforms use your visit to show you ads elsewhere.' },
45
+ {
46
+ id: 'necessary',
47
+ title: 'Strictly Necessary Cookies',
48
+ body: `These are needed for the site to work, so they are always on. They keep you signed in and your session secure, protect forms against cross-site request forgery, spread traffic across servers, remember the choice you make here, and keep what you have typed into a form. They include ${brand}'s own first-party analytics, which counts visits and finds what is broken for ${brand} alone and is never sold or shared.`,
49
+ },
50
+ {
51
+ id: 'analytics',
52
+ key: 'analytics',
53
+ title: 'Performance Cookies',
54
+ body: `Google Analytics: lets Google measure visits to ${brand} with its own cookies.`,
55
+ },
56
+ {
57
+ id: 'marketing',
58
+ key: 'marketing',
59
+ title: 'Marketing Cookies',
60
+ body: `The Meta pixel: lets Meta measure the sign-ups and orders ${brand}'s ads led to.`,
61
+ },
62
+ {
63
+ id: 'ads',
64
+ key: 'ads',
65
+ title: 'Sell, Share, or Use Your Data for Targeted Advertising',
66
+ body: 'Lets Google and Meta use your visit to choose the ads they show you on other sites and apps. Some US state privacy laws call this a “sale” or “sharing” of your personal information, or “targeted advertising”, and give you the right to opt out: it stays off unless you turn it on.',
67
+ },
36
68
  ];
37
- /** The banner's inset from the window's foot and sides: 16px, or the device's inset and 8px where that is wider. */
69
+ /** The bar's and the sheet's inset from the window's edges: 16px, or the device's inset and 8px where that is wider. */
38
70
  const EDGE = {
71
+ top: 'max(16px, calc(var(--safe-top, env(safe-area-inset-top, 0px)) + 8px))',
39
72
  bottom: 'max(16px, calc(var(--safe-bottom, env(safe-area-inset-bottom, 0px)) + 8px))',
40
73
  left: 'max(16px, calc(var(--safe-left, env(safe-area-inset-left, 0px)) + 8px))',
41
74
  right: 'max(16px, calc(var(--safe-right, env(safe-area-inset-right, 0px)) + 8px))',
42
75
  };
76
+ /** The panel both wear: the card's ground, edge, corner (`$6`, the sign-in card's) and floating shadow. */
77
+ const PANEL = {
78
+ bg: '$background',
79
+ borderWidth: 1,
80
+ borderColor: '$borderColor',
81
+ rounded: '$6',
82
+ shadowColor: 'rgba(0,0,0,0.25)',
83
+ shadowRadius: 24,
84
+ shadowOffset: { width: 0, height: 8 },
85
+ };
43
86
  /**
44
- * Where the banner sits: fixed to the foot of the window, clear of the home
87
+ * Where the bar sits: fixed to the foot of the window, clear of the home
45
88
  * indicator and the notch, centred. It stands on design's banner rung
46
89
  * (`--z-banner`): over the page and its dock, under every overlay — a dialog, a
47
90
  * menu, a phone drawer — and is answered once they close.
48
91
  */
49
92
  const frame = {
50
93
  ...slot('consent'),
94
+ ...PANEL,
51
95
  role: 'dialog',
52
96
  'aria-label': 'Cookie preferences',
53
97
  position: 'fixed',
@@ -56,46 +100,166 @@ const frame = {
56
100
  r: EDGE.right,
57
101
  mx: 'auto',
58
102
  z: 'var(--z-banner, 50)',
59
- py: '$2.5',
60
- px: '$3.5',
61
- rounded: '$5',
62
- borderWidth: 1,
63
- borderColor: '$borderColor',
64
- bg: '$background',
65
- shadowColor: 'rgba(0,0,0,0.25)',
66
- shadowRadius: 24,
67
- shadowOffset: { width: 0, height: 8 },
103
+ py: '$3',
104
+ paddingInlineStart: '$4',
105
+ // Clear of the × in the corner: its 32px, and 12px either side.
106
+ paddingInlineEnd: 56,
68
107
  };
69
108
  /** The bar is as wide as its line and buttons, up to the window less its inset. */
70
109
  const FIT = { width: 'fit-content' };
71
- /** A link in the banner's line: the line's own ink, underlined, never broken across lines. */
110
+ /** The one filled control in the bar and in the sheet: the ink, as the sign-in card's Continue. */
111
+ const ACT = { bg: '$ink', color: '$background', borderColor: '$ink', hoverStyle: { opacity: 0.92 } };
112
+ /** A link in the sheet's intro: the line's own ink, underlined, never broken across lines. */
72
113
  const INLINE = { color: 'inherit', textDecoration: 'underline', whiteSpace: 'nowrap' };
73
- const Consent = ({ privacy = 'https://hanzo.ai/privacy', brand = 'Hanzo' }) => {
114
+ /** The sheet covers the window, on design's modal rung, over a scrim of its own. */
115
+ const COVER = { position: 'fixed', t: 0, r: 0, b: 0, l: 0 };
116
+ /** What Tab may land on inside the sheet. */
117
+ const FOCUSABLE = 'a[href], button:not([disabled]), [tabindex]:not([tabindex="-1"])';
118
+ /** A section's button never submits a form the sheet sits in. gui's stack props do not name `type`. */
119
+ const BUTTON = { type: 'button' };
120
+ const Consent = ({ cookies = 'https://hanzo.ai/cookies', privacy = 'https://hanzo.ai/privacy', terms = 'https://hanzo.ai/terms', brand = 'Hanzo', }) => {
74
121
  const [shown, setShown] = useState(false);
75
122
  const [choosing, setChoosing] = useState(false);
76
123
  const [draft, setDraft] = useState({ analytics: false, marketing: false, ads: false });
124
+ const [expanded, setExpanded] = useState({});
77
125
  // The choices are open because the visitor asked for them. A policy that
78
126
  // arrives after (cloud answers ~150ms into a page opened at #choices) redraws
79
- // the banner's question, and must not close what was asked for.
127
+ // the bar's question, and must not close what was asked for.
80
128
  const asked = useRef(false);
81
- // The browser refused, as the page sees it or as cloud saw the request.
82
- const locked = typeof window !== 'undefined' && declined();
129
+ // What had focus when the sheet opened, given it back when the sheet closes.
130
+ const back = useRef(null);
131
+ const sheet = useRef(null);
132
+ const manage = useRef(null);
133
+ const opened = useRef(false);
134
+ // The switches were filled from a stored choice, not left off.
135
+ const seeded = useRef(false);
136
+ const id = useId();
137
+ // The browser refused, as the page sees it or as cloud saw the request. State,
138
+ // so cloud's answer redraws an open sheet.
139
+ const [locked, setLocked] = useState(false);
140
+ const choose = () => {
141
+ // Already open: the visitor's unsaved switches and the focus to return stay as they are.
142
+ if (asked.current)
143
+ return;
144
+ const at = document.activeElement;
145
+ back.current = at instanceof HTMLElement && at !== document.body ? at : null;
146
+ asked.current = true;
147
+ setLocked(declined());
148
+ seeded.current = storedConsent();
149
+ setDraft(readConsent());
150
+ setChoosing(true);
151
+ setShown(true);
152
+ };
153
+ // Closing without a choice: a visitor who has not chosen is asked again by the bar.
154
+ const close = () => {
155
+ asked.current = false;
156
+ setChoosing(false);
157
+ setShown(asks());
158
+ };
83
159
  useEffect(() => {
84
- const sync = () => setShown(asks() || asked.current);
160
+ const sync = () => {
161
+ setShown(asks() || asked.current);
162
+ setLocked(declined());
163
+ // A policy version the stored choice was not made under: the switches the
164
+ // sheet opened with were an old grant, and are off until chosen again.
165
+ if (asked.current && seeded.current && !storedConsent()) {
166
+ seeded.current = false;
167
+ setDraft(readConsent());
168
+ }
169
+ };
85
170
  sync();
86
- const open = () => {
87
- asked.current = true;
88
- setDraft(readConsent());
89
- setChoosing(true);
90
- setShown(true);
171
+ // `#choices` opens the sheet: on arrival, on a hash change, and on a press of
172
+ // any link on this site to it, so the footer's Privacy choices and a link in
173
+ // a page are the same control.
174
+ const hash = () => {
175
+ if (window.location.hash !== HASH)
176
+ return;
177
+ // The router's own entry state is kept: only the address loses its hash. A
178
+ // microtask, so a router that wraps replaceState in its own mount effect
179
+ // (Next's, which runs after this child's) records the address without it.
180
+ queueMicrotask(() => history.replaceState(history.state, '', window.location.pathname + window.location.search));
181
+ choose();
182
+ };
183
+ // A plain press only: a modified one (new tab, new window) keeps the browser's
184
+ // meaning, and the page it opens at #choices opens the sheet on arrival.
185
+ const press = (e) => {
186
+ if (e.defaultPrevented || e.button !== 0 || e.metaKey || e.ctrlKey || e.shiftKey || e.altKey)
187
+ return;
188
+ // The attribute, not `.href`: an SVG link's `.href` is not a string.
189
+ const a = e.target?.closest?.('a[href]');
190
+ const href = a?.getAttribute('href');
191
+ const target = a?.getAttribute('target');
192
+ if (!href || a?.hasAttribute('download') || (target && target !== '_self'))
193
+ return;
194
+ let to;
195
+ try {
196
+ to = new URL(href, document.baseURI);
197
+ }
198
+ catch {
199
+ return;
200
+ }
201
+ if (to.hash !== HASH || to.origin !== window.location.origin)
202
+ return;
203
+ e.preventDefault();
204
+ choose();
91
205
  };
92
- window.addEventListener(OPEN, open);
206
+ hash();
207
+ window.addEventListener(OPEN, choose);
93
208
  window.addEventListener(POLICY_EVENT, sync);
209
+ window.addEventListener('hashchange', hash);
210
+ document.addEventListener('click', press, { capture: true });
94
211
  return () => {
95
- window.removeEventListener(OPEN, open);
212
+ window.removeEventListener(OPEN, choose);
96
213
  window.removeEventListener(POLICY_EVENT, sync);
214
+ window.removeEventListener('hashchange', hash);
215
+ document.removeEventListener('click', press, { capture: true });
97
216
  };
98
217
  }, []);
218
+ // Focus moves into the sheet when it opens, stays inside it while it is open,
219
+ // and returns to what opened it (or the bar's Manage preferences) when it closes.
220
+ useEffect(() => {
221
+ if (!choosing) {
222
+ if (!opened.current)
223
+ return;
224
+ opened.current = false;
225
+ const el = back.current;
226
+ back.current = null;
227
+ if (el?.isConnected)
228
+ el.focus();
229
+ else
230
+ manage.current?.focus();
231
+ return;
232
+ }
233
+ opened.current = true;
234
+ const all = () => [...(sheet.current?.querySelectorAll(FOCUSABLE) ?? [])];
235
+ all()[0]?.focus();
236
+ const keys = (e) => {
237
+ if (e.key === 'Escape') {
238
+ e.preventDefault();
239
+ close();
240
+ return;
241
+ }
242
+ if (e.key !== 'Tab')
243
+ return;
244
+ const list = all();
245
+ const first = list[0];
246
+ const last = list[list.length - 1];
247
+ if (!first || !last)
248
+ return;
249
+ const at = document.activeElement;
250
+ const inside = !!at && !!sheet.current?.contains(at);
251
+ if (e.shiftKey && (at === first || !inside)) {
252
+ e.preventDefault();
253
+ last.focus();
254
+ }
255
+ else if (!e.shiftKey && (at === last || !inside)) {
256
+ e.preventDefault();
257
+ first.focus();
258
+ }
259
+ };
260
+ document.addEventListener('keydown', keys);
261
+ return () => document.removeEventListener('keydown', keys);
262
+ }, [choosing]);
99
263
  if (!shown)
100
264
  return null;
101
265
  const done = (fn) => () => {
@@ -104,21 +268,18 @@ const Consent = ({ privacy = 'https://hanzo.ai/privacy', brand = 'Hanzo' }) => {
104
268
  setShown(false);
105
269
  setChoosing(false);
106
270
  };
107
- const choose = () => {
108
- asked.current = true;
109
- setDraft(readConsent());
110
- setChoosing(true);
111
- setShown(true);
112
- };
113
271
  if (choosing)
114
- return (_jsxs(YStack, { ...frame, maxW: 440, gap: "$3", maxH: `calc(100dvh - var(--safe-top, env(safe-area-inset-top, 0px)) - var(--header, 0px) - ${EDGE.bottom} - 16px)`, overflowY: "auto", children: [_jsxs(Paragraph, { size: "$2", color: "$ink", m: 0, children: [`Choose what ${brand} may measure and share. You can change this at any time. `, _jsx("a", { href: privacy, style: INLINE, children: "Privacy policy" })] }), locked ? (_jsx(Paragraph, { size: "$2", color: "$soft", m: 0, children: "Your browser asks sites not to track you, so all of these stay off." })) : null, kinds(brand).map((k) => {
115
- const off = locked;
116
- return (_jsxs(XStack, { gap: "$3", items: "center", justify: "space-between", children: [_jsxs(YStack, { flex: 1, children: [_jsx(Paragraph, { size: "$2", color: "$ink", fontWeight: "500", m: 0, children: k.title }), _jsx(Paragraph, { size: "$1", color: "$soft", m: 0, children: k.body })] }), _jsx(Switch, { "aria-label": k.title, disabled: off, checked: off ? false : draft[k.key], onCheckedChange: (v) => setDraft({ ...draft, [k.key]: v === true }) })] }, k.key));
117
- }), _jsxs(XStack, { gap: "$2", justify: "flex-end", children: [_jsx(Button, { type: "button", size: "sm", variant: "secondary", onClick: done(rejectAll), children: "Reject all" }), _jsx(Button, { type: "button", size: "sm", onClick: done(() => saveConsent(draft)), children: "Save choices" })] })] }));
118
- // One bar: the line and its link, then the buttons. They share a row where
119
- // the line fits beside them and the buttons drop under it where it does not,
120
- // so a phone shows the line over one row of buttons and nothing breaks mid-control.
121
- return (_jsxs(XStack, { ...frame, style: FIT, maxW: 960, flexWrap: "wrap", items: "center", columnGap: "$4", rowGap: "$2", children: [_jsx(YStack, { shrink: 1, minW: 0, children: _jsxs(Paragraph, { size: "$2", color: "$ink", m: 0, children: ['Cookies measure visits and ads once you accept. ', _jsx("a", { href: privacy, style: INLINE, children: "Privacy policy" })] }) }), _jsxs(XStack, { gap: "$2", marginInlineStart: "auto", items: "center", children: [_jsx(Button, { type: "button", size: "sm", variant: "link", onClick: choose, children: "Choose" }), _jsx(Button, { type: "button", size: "sm", variant: "secondary", onClick: done(rejectAll), children: "Reject all" }), _jsx(Button, { type: "button", size: "sm", onClick: done(acceptAll), children: "Accept all" })] })] }));
272
+ return (_jsxs(YStack, { ...slot('consent-sheet'), ...COVER, z: "var(--z-modal, 600)", items: "center", justify: "center",
273
+ // The device's insets are physical sides, so the shorthand names them in order.
274
+ style: { padding: `${EDGE.top} ${EDGE.right} ${EDGE.bottom} ${EDGE.left}` }, children: [_jsx(YStack, { ...COVER, position: "absolute", z: 0, bg: "rgba(0,0,0,0.6)", "aria-hidden": true, onPress: close }), _jsxs(YStack, { ...slot('consent'), ...PANEL, ref: sheet, role: "dialog", "aria-modal": true, "aria-labelledby": `${id}-title`, position: "relative", width: "100%", maxW: 560, maxH: "100%", overflow: "hidden", children: [_jsxs(XStack, { items: "center", justify: "space-between", gap: "$3", pt: "$5", pb: "$3", paddingInlineStart: "$5", paddingInlineEnd: "$4", children: [_jsx(Text, { render: "h2", id: `${id}-title`, m: 0, fontSize: "$6", lineHeight: "$6", fontWeight: "600", color: "$ink", children: "Cookie Preferences" }), _jsx(Button, { type: "button", size: "icon-sm", variant: "ghost", "aria-label": "Close", onClick: close, children: _jsx(X, { size: 16 }) })] }), _jsxs(YStack, { shrink: 1, minH: 0, overflowY: "auto", px: "$5", pb: "$2", style: { overscrollBehavior: 'contain' }, children: [_jsxs(Paragraph, { size: "$2", color: "$aside", m: 0, pb: "$3", children: ['We use cookies and similar technologies to provide our services, measure performance, and support advertising. Essential cookies are required for the site to work and cannot be turned off. You can allow or reject optional cookies below. Learn more in our ', _jsx("a", { href: cookies, style: INLINE, children: "Cookie Policy" }), ', ', _jsx("a", { href: privacy, style: INLINE, children: "Privacy Policy" }), ', and ', _jsx("a", { href: terms, style: INLINE, children: "Terms of Service" }), "."] }), locked ? (_jsx(Paragraph, { size: "$2", color: "$ink", m: 0, pb: "$3", children: "Your browser asks sites not to track you, so all of these stay off." })) : null, kinds(brand).map((k) => {
275
+ const open = expanded[k.id] === true;
276
+ const body = `${id}-${k.id}`;
277
+ return (_jsxs(YStack, { ...slot('consent-section'), borderTopWidth: 1, borderColor: "$borderColor", children: [_jsxs(XStack, { items: "center", gap: "$3", minH: 52, py: "$2", children: [_jsx(XStack, { render: "h3", flex: 1, minW: 0, m: 0, children: _jsxs(XStack, { render: "button", ...BUTTON, "aria-expanded": open, "aria-controls": body, onPress: () => setExpanded({ ...expanded, [k.id]: !open }), flex: 1, minW: 0, items: "center", gap: "$2", p: 0, bg: "transparent", borderWidth: 0, cursor: "pointer", children: [_jsx(XStack, { shrink: 0, rotate: open ? '180deg' : '0deg', opacity: 0.7, children: _jsx(ChevronDown, { size: 16 }) }), _jsx(Text, { fontSize: "$3", lineHeight: "$3", fontWeight: "600", color: "$ink", ...align('start'), children: k.title })] }) }), k.key ? (_jsx(Switch, { "aria-label": k.title, "aria-describedby": body, disabled: locked, checked: locked ? false : draft[k.key], onCheckedChange: (v) => setDraft({ ...draft, [k.key]: v === true }) })) : (_jsx(Text, { fontSize: "$2", fontWeight: "600", color: "$ink", whiteSpace: "nowrap", children: "Always Active" }))] }), _jsx(YStack, { id: body, display: open ? 'flex' : 'none', pb: "$3", paddingInlineStart: 24, children: _jsx(Paragraph, { size: "$2", color: "$aside", m: 0, children: k.body }) })] }, k.id));
278
+ })] }), _jsxs(XStack, { flexWrap: "wrap", gap: "$2", px: "$5", py: "$4", borderTopWidth: 1, borderColor: "$borderColor", children: [locked ? null : (_jsx(Button, { type: "button", variant: "secondary", grow: 1, onClick: done(acceptAll), children: "Accept all" })), _jsx(Button, { type: "button", ...ACT, grow: 1, onClick: done(() => saveConsent(draft)), children: "Save preferences" })] })] })] }));
279
+ // One bar: the line, then the buttons. They share a row where the line fits
280
+ // beside them and the buttons drop under it where it does not, so a phone shows
281
+ // the line over one row of buttons and nothing breaks mid-control.
282
+ return (_jsxs(XStack, { ...frame, style: FIT, maxW: 1200, flexWrap: "wrap", items: "center", columnGap: "$4", rowGap: "$3", children: [_jsx(YStack, { shrink: 1, minW: 0, children: _jsx(Paragraph, { size: "$2", color: "$ink", m: 0, children: `Essential cookies keep ${brand} running. With your OK, optional cookies help us measure performance and ads.` }) }), _jsxs(XStack, { gap: "$2", marginInlineStart: "auto", items: "center", flexWrap: "wrap", children: [_jsx(Button, { ref: manage, type: "button", size: "sm", variant: "secondary", onClick: choose, children: "Manage preferences" }), _jsx(Button, { type: "button", size: "sm", ...ACT, onClick: done(acceptAll), children: "Accept all" })] }), _jsx(Button, { type: "button", size: "icon-sm", variant: "ghost", "aria-label": "Close and use essential cookies only", position: "absolute", t: "$3", insetInlineEnd: "$3", onClick: done(rejectAll), children: _jsx(X, { size: 16 }) })] }));
122
283
  };
123
284
  /** The footer link that reopens the choices, in every footer and every region. */
124
285
  const ConsentLink = ({ label = 'Cookie settings' }) => (_jsx(Button, { ...slot('consent-link'), type: "button", variant: "link", size: "sm", px: 0, minH: 0, onClick: openConsent, children: label }));
@@ -1 +1 @@
1
- {"version":3,"file":"Consent.js","sourceRoot":"","sources":["../../src/consent/Consent.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"Consent.js","sourceRoot":"","sources":["../../src/consent/Consent.tsx"],"names":[],"mappings":"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"}
package/dist/gallery.html CHANGED
@@ -1,4 +1,4 @@
1
- <!doctype html><html lang="en" class="t_dark"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>@hanzo/ui 8.29.12</title><style>/* @hanzo/ui — generated by scripts/gen-css.mjs. Do not edit. */
1
+ <!doctype html><html lang="en" class="t_dark"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>@hanzo/ui 8.29.14</title><style>/* @hanzo/ui — generated by scripts/gen-css.mjs. Do not edit. */
2
2
  :root { --hanzo-ui-styles: 1; }
3
3
  /* Hanzo Design System — the entry point. Import THIS one file.
4
4
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hanzo/ui",
3
- "version": "8.29.12",
3
+ "version": "8.29.14",
4
4
  "type": "module",
5
5
  "description": "Hanzo UI — the one canonical Hanzo component library, on @hanzo/gui + @hanzo/design. ONE substrate: every component renders through @hanzo/gui primitives, so the same import works on web, native (expo) and desktop (Tauri). Self-contained (theme.css), presentational, host-agnostic, clean-room.",
6
6
  "exports": {
@@ -331,7 +331,7 @@
331
331
  "@hanzo/canvas": ">=0.2.3",
332
332
  "@hanzo/cd": ">=0.1.2",
333
333
  "@hanzo/dashboard": ">=0.2.0",
334
- "@hanzo/event": ">=0.3.58",
334
+ "@hanzo/event": ">=0.3.61",
335
335
  "@hanzo/data": ">=1.2.4",
336
336
  "@hanzo/gui": "^8.3.5",
337
337
  "@hanzo/iam": ">=0.21.26",
@@ -388,7 +388,7 @@
388
388
  },
389
389
  "devDependencies": {
390
390
  "@hanzo/cd": "0.1.2",
391
- "@hanzo/event": "0.3.58",
391
+ "@hanzo/event": "0.3.61",
392
392
  "@hanzo/dashboard": "0.2.0",
393
393
  "@hanzo/data": "1.2.4",
394
394
  "@hanzo/gui": "8.3.5",