@caqophony/ui 0.5.0 → 0.5.1

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.
@@ -346,7 +346,7 @@ function NavbarMenu({
346
346
  defaultOpen = false,
347
347
  triggerClassName,
348
348
  folded = false,
349
- surface
349
+ night = false
350
350
  }) {
351
351
  return /* @__PURE__ */ jsx5("div", { className: folded ? "ml-auto" : "ml-auto md:hidden", children: /* @__PURE__ */ jsxs4(
352
352
  NavDrawer,
@@ -356,7 +356,7 @@ function NavbarMenu({
356
356
  closedFrom: folded ? null : "md",
357
357
  defaultOpen,
358
358
  triggerClassName,
359
- surface,
359
+ surface: night ? "night" : void 0,
360
360
  children: [
361
361
  search && /* @__PURE__ */ jsx5("div", { className: "px-4 pb-3", children: search }),
362
362
  links && /* @__PURE__ */ jsx5("nav", { className: "flex flex-col px-4 pb-3 [&>a]:whitespace-normal [&>a]:py-2.5 [&>a]:text-base", children: links }),
package/dist/index.js CHANGED
@@ -11,7 +11,7 @@ import {
11
11
  buttonVariants,
12
12
  useModalSurface,
13
13
  usePanelRef
14
- } from "./chunk-I5DQCG6D.js";
14
+ } from "./chunk-WGWKCFG2.js";
15
15
  import {
16
16
  Alert,
17
17
  EMPTY_VALUE_GLYPH,
@@ -41,8 +41,10 @@ interface MarketingHeaderProps {
41
41
  * through the bar; its drawer stands on the same scope.
42
42
  *
43
43
  * #5948: below `md` the links and actions fold into the library's one phone
44
- * drawer (`NavbarMenu`), in both variants. The drawer stands on the card
45
- * surface whichever the variant, so its links are drawn for that surface.
44
+ * drawer (`NavbarMenu`), in both variants. A `light` bar's drawer stands on
45
+ * the card surface. #5997: a `dark` bar's stands on the night scope, as a
46
+ * `night` bar's does (#5980): its actions are `MarketingCta ground="dark"`,
47
+ * which read near-white on a day card.
46
48
  *
47
49
  * #5952: the row has a limit, and past it the bar folds into the same drawer at
48
50
  * any width (`useFold`), so a brand is never squeezed to make room for links.
package/dist/marketing.js CHANGED
@@ -3,7 +3,7 @@ import {
3
3
  Button,
4
4
  Container,
5
5
  NavbarMenu
6
- } from "./chunk-I5DQCG6D.js";
6
+ } from "./chunk-WGWKCFG2.js";
7
7
  import {
8
8
  ScrollHintXMark
9
9
  } from "./chunk-ID7VBGJA.js";
@@ -196,7 +196,7 @@ function MarketingHeader({
196
196
  {
197
197
  folded,
198
198
  triggerClassName: isDark ? DARK_MENU_BUTTON : isNight ? NIGHT_MENU_BUTTON : void 0,
199
- surface: isNight ? variant : void 0,
199
+ night: isNight || isDark,
200
200
  links: hasLinks ? navItems.map((item) => /* @__PURE__ */ jsx(
201
201
  "a",
202
202
  {
@@ -198,14 +198,13 @@ interface FieldSize {
198
198
  height: number;
199
199
  }
200
200
  /**
201
- * #5985 round 2 — which way a field's horizon pieces run on past it. On a
202
- * `close` side a piece ends at the frame's edge with a natural end (a bank's
203
- * last bump, a ridge's slope, down to the field's bottom); `down` runs every
204
- * fill on below the field's bottom.
201
+ * #5985 round 2 — how far a field's horizon pieces run on past it: every piece
202
+ * bleeds both ways (`BLEED_WIDTHS`), and `down` runs every fill on below the
203
+ * field's bottom. #5997 retired the copy-side end (`close`): the approved port
204
+ * runs its ground the hero's full width, under the copy, and an end inside the
205
+ * scene showed as a tongue on every generated hero (generation check 7, 4 of 4).
205
206
  */
206
207
  interface FieldReach {
207
- left: 'bleed' | 'close';
208
- right: 'bleed' | 'close';
209
208
  down: boolean;
210
209
  }
211
210
  /** A field outside a `HeroScene`, `Band`'s edge and the stored art: the bleed both ways, filled to the field's bottom. */
@@ -253,10 +252,7 @@ interface CloudBankProps {
253
252
  * `contain`, and no start edge lands in a zone.
254
253
  *
255
254
  * #5985 round 2 — `reach` (a `HeroScene`'s field): `down` fills every path on
256
- * `DOWN_SHARE` of a field-height below the field's bottom, and a `close` side
257
- * has no bleed: the run ends with a last bump, a quarter ellipse from the run's
258
- * top down to the frame's edge at the foot of that fill, at least a `max` wide,
259
- * so it never stands as a straight edge.
255
+ * `DOWN_SHARE` of a field-height below the field's bottom.
260
256
  */
261
257
  declare function cloudBankPaths(field: FieldSize, bank: Omit<CloudBankProps, 'tone'>, reach: FieldReach): readonly string[];
262
258
  /**
@@ -283,9 +279,7 @@ interface RidgeProps {
283
279
  * pixels from before to keep.
284
280
  *
285
281
  * #5985 round 2 — `down` fills on `DOWN_SHARE` of a field-height below the
286
- * field's bottom; on a `close` side (a `HeroScene`'s field, toward its copy)
287
- * there is no bleed: the swell at the edge slopes down to the frame's edge at
288
- * the foot of that fill.
282
+ * field's bottom.
289
283
  */
290
284
  declare function ridgePath(field: FieldSize, ridge: Omit<RidgeProps, 'tone'>, reach: FieldReach): string;
291
285
  /** A smooth ridge line across its field, filled to the field's bottom. */
@@ -309,7 +303,11 @@ interface RoutePoint {
309
303
  x: number;
310
304
  y: number;
311
305
  label?: string;
312
- /** Which side of the point its label sits. */
306
+ /**
307
+ * Which side of the point its label sits, first: the route keeps it unless
308
+ * the label would cross another or a mark, or leave the field, and then
309
+ * places it on the first side clear of them (#5997).
310
+ */
313
311
  side?: 'above' | 'below' | 'left' | 'right';
314
312
  /** `passed`: filled; `here`: ringed; `ahead`: hollow. */
315
313
  state: 'passed' | 'here' | 'ahead';
package/dist/material.js CHANGED
@@ -263,11 +263,8 @@ function sized(piece, field) {
263
263
  );
264
264
  }
265
265
  var MAX_BUMPS = 2e3;
266
- var BLEED_BOTH_SIDES = {
267
- left: "bleed",
268
- right: "bleed",
269
- down: false
270
- };
266
+ var BLEED_BOTH_SIDES = { down: false };
267
+ var IN_SCENE = { down: true };
271
268
  var SceneCopyContext = createContext(null);
272
269
  var GroundContext = createContext(null);
273
270
  var FieldContext = createContext(null);
@@ -280,11 +277,8 @@ function useField(piece) {
280
277
  );
281
278
  return field;
282
279
  }
283
- function reachOf(copy, only) {
284
- if (copy === null) return BLEED_BOTH_SIDES;
285
- if (only === "stack" || only === "phone")
286
- return { left: "bleed", right: "bleed", down: true };
287
- return copy === "start" ? { left: "close", right: "bleed", down: true } : { left: "bleed", right: "close", down: true };
280
+ function reachOf(copy) {
281
+ return copy === null ? BLEED_BOTH_SIDES : IN_SCENE;
288
282
  }
289
283
  function SkyField({
290
284
  width,
@@ -304,7 +298,7 @@ function SkyField({
304
298
  const field = {
305
299
  width,
306
300
  height,
307
- reach: reachOf(copy, only),
301
+ reach: reachOf(copy),
308
302
  ground: ground === "none" ? under : ground,
309
303
  routes: new Set(
310
304
  routes.map(
@@ -380,7 +374,7 @@ function cloudBankPaths(field, bank, reach) {
380
374
  const rounded = (n) => Number(n.toFixed(1));
381
375
  const step = (r, draw) => (draw() - 0.5) * r * 0.5 - climb * r * 2 / width;
382
376
  const next = rng(seed);
383
- const run = [];
377
+ let frame = `M${-max} ${bottom}L${-max} ${y}`;
384
378
  let x = -max;
385
379
  let endX = x;
386
380
  let endY = y;
@@ -393,63 +387,35 @@ function cloudBankPaths(field, bank, reach) {
393
387
  x += r * 2;
394
388
  endX += rounded(r * 2);
395
389
  endY += rounded(lift);
396
- run.push({ d: arc(r, lift), x, endX, endY });
397
- }
398
- const end = (from, edge, up) => {
399
- const rx = Math.abs(edge - from.x).toFixed(1);
400
- const ry = bottom - from.y;
401
- if (ry <= 0)
402
- throw new Error(
403
- `CloudBank: its end at the frame's edge has no depth to fall (top ${from.y.toFixed(1)}, fill to ${bottom}); a closed side fills on below the field`
404
- );
405
- return up ? `A${rx} ${ry.toFixed(1)} 0 0 1 ${from.x.toFixed(1)} ${from.y.toFixed(1)}` : `A${rx} ${ry.toFixed(1)} 0 0 1 ${edge} ${bottom}`;
406
- };
407
- const first = reach.left === "close" ? run.findIndex((b) => b.endX >= max) : -1;
408
- let last = run.length - 1;
409
- if (reach.right === "close") {
410
- last = -1;
411
- run.forEach((b, i) => {
412
- if (b.endX <= width - max && i > first) last = i;
413
- });
390
+ frame += arc(r, lift);
414
391
  }
415
- const startPoint = first === -1 ? { x: -max, y } : { x: run[first].endX, y: run[first].endY };
416
- let frame = reach.left === "close" ? `M0 ${bottom}${end(startPoint, 0, true)}` : `M${-max} ${bottom}L${-max} ${y}`;
417
- for (let i = first + 1; i <= last; i++) frame += run[i].d;
418
- if (reach.right === "close") {
419
- const at = last === -1 ? startPoint : { x: run[last].endX, y: run[last].endY };
420
- frame += `${end(at, width, false)}Z`;
421
- } else frame += `L${x} ${bottom}Z`;
422
- const paths = [];
423
- if (reach.left === "bleed") {
424
- const back = rng(seed + BLEED_STREAM);
425
- const arcs = [];
426
- let lx = -max;
427
- let offset = 0;
428
- while (lx > -BLEED_WIDTHS * width - max) {
429
- const r = min + back() * (max - min);
430
- const s = step(r, back);
431
- const lift = level ? offset - s : s;
432
- offset -= rounded(lift);
433
- arcs.unshift(arc(r, lift));
434
- lx -= rounded(r * 2);
435
- }
436
- paths.push(
437
- `M${lx.toFixed(1)} ${bottom}L${lx.toFixed(1)} ${(y + offset).toFixed(1)}${arcs.join("")}L${-max} ${y}L${-max + under} ${y + under}L${-max + under} ${bottom}Z`
438
- );
392
+ frame += `L${x} ${bottom}Z`;
393
+ const back = rng(seed + BLEED_STREAM);
394
+ const arcs = [];
395
+ let lx = -max;
396
+ let offset = 0;
397
+ while (lx > -BLEED_WIDTHS * width - max) {
398
+ const r = min + back() * (max - min);
399
+ const s = step(r, back);
400
+ const lift = level ? offset - s : s;
401
+ offset -= rounded(lift);
402
+ arcs.unshift(arc(r, lift));
403
+ lx -= rounded(r * 2);
439
404
  }
440
- if (reach.right === "bleed") {
441
- let right = `M${(endX - under).toFixed(1)} ${bottom}L${(endX - under).toFixed(1)} ${(endY + under).toFixed(1)}L${endX.toFixed(1)} ${endY.toFixed(1)}`;
442
- let rx = x;
443
- while (rx < (1 + BLEED_WIDTHS) * width + max) {
444
- const r = min + next() * (max - min);
445
- const s = step(r, next);
446
- const lift = level ? s - drift : s;
447
- drift += lift;
448
- right += arc(r, lift);
449
- rx += r * 2;
450
- }
451
- paths.push(`${right}L${rx} ${bottom}Z`);
405
+ const paths = [
406
+ `M${lx.toFixed(1)} ${bottom}L${lx.toFixed(1)} ${(y + offset).toFixed(1)}${arcs.join("")}L${-max} ${y}L${-max + under} ${y + under}L${-max + under} ${bottom}Z`
407
+ ];
408
+ let right = `M${(endX - under).toFixed(1)} ${bottom}L${(endX - under).toFixed(1)} ${(endY + under).toFixed(1)}L${endX.toFixed(1)} ${endY.toFixed(1)}`;
409
+ let rx = x;
410
+ while (rx < (1 + BLEED_WIDTHS) * width + max) {
411
+ const r = min + next() * (max - min);
412
+ const s = step(r, next);
413
+ const lift = level ? s - drift : s;
414
+ drift += lift;
415
+ right += arc(r, lift);
416
+ rx += r * 2;
452
417
  }
418
+ paths.push(`${right}L${rx} ${bottom}Z`);
453
419
  paths.push(frame);
454
420
  return paths;
455
421
  }
@@ -465,27 +431,20 @@ function ridgePath(field, ridge, reach) {
465
431
  measuredAll("Ridge", field, { y, rise, swells, seed });
466
432
  const bottom = reach.down ? height * (1 + DOWN_SHARE) : height;
467
433
  const step = width / swells;
468
- let d;
469
- if (reach.left === "close") d = `M0 ${bottom}`;
470
- else {
471
- const back = rng(seed + BLEED_STREAM);
472
- const from = -BLEED_WIDTHS * width;
473
- d = `M${from} ${bottom}L${from} ${(y - back() * rise * 0.3).toFixed(1)}`;
474
- for (let i = -BLEED_WIDTHS * swells; i < 0; i++) {
475
- const cy = y - back() * rise;
476
- const end = i === -1 ? y : y - back() * rise * 0.3;
477
- d += `Q${(i + 0.5) * step} ${cy.toFixed(1)} ${(i + 1) * step} ${end.toFixed(1)}`;
478
- }
434
+ const back = rng(seed + BLEED_STREAM);
435
+ const from = -BLEED_WIDTHS * width;
436
+ let d = `M${from} ${bottom}L${from} ${(y - back() * rise * 0.3).toFixed(1)}`;
437
+ for (let i = -BLEED_WIDTHS * swells; i < 0; i++) {
438
+ const cy = y - back() * rise;
439
+ const end = i === -1 ? y : y - back() * rise * 0.3;
440
+ d += `Q${(i + 0.5) * step} ${cy.toFixed(1)} ${(i + 1) * step} ${end.toFixed(1)}`;
479
441
  }
480
442
  const next = rng(seed);
481
- const count = reach.right === "close" ? swells : (1 + BLEED_WIDTHS) * swells;
482
- for (let i = 0; i < count; i++) {
443
+ for (let i = 0; i < (1 + BLEED_WIDTHS) * swells; i++) {
483
444
  const cy = y - next() * rise;
484
445
  const tail = (y - next() * rise * 0.3).toFixed(1);
485
- const end = reach.right === "close" && i === count - 1 ? `${bottom}` : tail;
486
- d += `Q${(i + 0.5) * step} ${cy.toFixed(1)} ${(i + 1) * step} ${end}`;
446
+ d += `Q${(i + 0.5) * step} ${cy.toFixed(1)} ${(i + 1) * step} ${tail}`;
487
447
  }
488
- if (reach.right === "close") return `${d}Z`;
489
448
  return `${d}L${(1 + BLEED_WIDTHS) * width} ${bottom}Z`;
490
449
  }
491
450
  function Ridge({ tone, ...ridge }) {
@@ -509,7 +468,71 @@ function Stars({ tone, ...stars }) {
509
468
  const dots = starDots(field, stars);
510
469
  return /* @__PURE__ */ jsx2("g", { className: FILL[tone], children: dots.map((dot, i) => /* @__PURE__ */ jsx2("circle", { cx: dot.cx, cy: dot.cy, r: dot.r }, i)) });
511
470
  }
512
- var LABEL_HALO = 0.4;
471
+ var LABEL_HALO = 0.12;
472
+ var LABEL_ADVANCE_EM = 0.62;
473
+ function routeLabelPlaces(points, labelSize, field) {
474
+ const gap = labelSize * 1.1;
475
+ const pad = labelSize * LABEL_HALO / 2;
476
+ const marks = points.map((p) => {
477
+ const r = labelSize * (p.state === "here" ? 0.85 : 0.34);
478
+ return {
479
+ what: `the mark at ${p.x},${p.y}`,
480
+ box: { left: p.x - r, top: p.y - r, right: p.x + r, bottom: p.y + r }
481
+ };
482
+ });
483
+ const placed = [];
484
+ return points.map((p, i) => {
485
+ if (!p.label) return null;
486
+ const words = p.label;
487
+ const width = words.length * LABEL_ADVANCE_EM * labelSize;
488
+ const before = points[Math.max(0, i - 1)];
489
+ const after = points[Math.min(points.length - 1, i + 1)];
490
+ const across = Math.abs(after.x - before.x) >= Math.abs(after.y - before.y);
491
+ const order = across ? ["above", "below", "right", "left"] : ["right", "left", "above", "below"];
492
+ const sides = p.side === void 0 ? order : [p.side, ...order.filter((side) => side !== p.side)];
493
+ const candidates = sides.map((side) => {
494
+ const x = side === "left" ? p.x - gap : side === "right" ? p.x + gap : p.x;
495
+ const y = side === "above" ? p.y - gap : side === "below" ? p.y + gap * 1.5 : p.y + labelSize * 0.35;
496
+ const anchor = side === "left" ? "end" : side === "right" ? "start" : "middle";
497
+ const left = anchor === "start" ? x : anchor === "end" ? x - width : x - width / 2;
498
+ return {
499
+ x,
500
+ y,
501
+ anchor,
502
+ side,
503
+ box: {
504
+ left: left - pad,
505
+ right: left + width + pad,
506
+ top: y - labelSize * 0.8 - pad,
507
+ bottom: y + labelSize * 0.25 + pad
508
+ }
509
+ };
510
+ });
511
+ const others = [...placed, ...marks.filter((_, k) => k !== i)];
512
+ const blocks = (c) => [
513
+ ...others.map((o) => ({
514
+ what: o.what,
515
+ across: Math.min(c.box.right, o.box.right) - Math.max(c.box.left, o.box.left),
516
+ down: Math.min(c.box.bottom, o.box.bottom) - Math.max(c.box.top, o.box.top)
517
+ })).filter((h) => h.across > 0 && h.down > 0).map(
518
+ (h) => `crosses ${h.what} by ${h.across.toFixed(1)} by ${h.down.toFixed(1)}`
519
+ ),
520
+ ...[
521
+ { edge: "left", by: -c.box.left },
522
+ { edge: "top", by: -c.box.top },
523
+ { edge: "right", by: c.box.right - field.width },
524
+ { edge: "bottom", by: c.box.bottom - field.height }
525
+ ].filter((e) => e.by > 0).map((e) => `runs ${e.by.toFixed(1)} past the field's ${e.edge}`)
526
+ ];
527
+ const clear = candidates.find((c) => blocks(c).length === 0);
528
+ if (clear === void 0)
529
+ throw new Error(
530
+ `RouteLine: the label "${words}" has no clear side, in field units: ${candidates.map((c) => `${c.side} ${blocks(c).join(" and ")}`).join("; ")}. Move its point, or what it meets, apart`
531
+ );
532
+ placed.push({ what: `the label "${words}"`, box: clear.box });
533
+ return clear;
534
+ });
535
+ }
513
536
  function RouteLine({
514
537
  points,
515
538
  tone,
@@ -535,19 +558,16 @@ function RouteLine({
535
558
  if (layer === "labels") {
536
559
  if (!labelled || field.ground === null) return null;
537
560
  const halo = STROKE[field.ground];
538
- const gap = labelSize * 1.1;
561
+ const places = routeLabelPlaces(points, labelSize, field);
539
562
  return /* @__PURE__ */ jsx2(Fragment2, { children: points.map((p, i) => {
540
- if (!p.label) return null;
541
- const side = p.side ?? "above";
542
- const lx = side === "left" ? p.x - gap : side === "right" ? p.x + gap : p.x;
543
- const ly = side === "above" ? p.y - gap : side === "below" ? p.y + gap * 1.5 : p.y + labelSize * 0.35;
544
- const anchor = side === "left" ? "end" : side === "right" ? "start" : "middle";
563
+ const place = places[i];
564
+ if (!p.label || !place) return null;
545
565
  return /* @__PURE__ */ jsx2(
546
566
  "text",
547
567
  {
548
- x: lx,
549
- y: ly,
550
- textAnchor: anchor,
568
+ x: place.x,
569
+ y: place.y,
570
+ textAnchor: place.anchor,
551
571
  fontSize: labelSize,
552
572
  fontWeight: 600,
553
573
  strokeWidth: labelSize * LABEL_HALO,
@@ -759,13 +779,14 @@ var HEIGHT2 = {
759
779
  fold: "lg:min-h-[min(calc(100svh-4rem),56rem)]",
760
780
  tall: "lg:min-h-[46rem]"
761
781
  };
782
+ var CLIP_ABOVE = "[clip-path:inset(0_0_-100vh_0)]";
762
783
  function HeroScene({
763
784
  height,
764
785
  subject,
765
786
  copySide = "start",
766
787
  children
767
788
  }) {
768
- return /* @__PURE__ */ jsx5("div", { "data-hero-scene": "", className: "overflow-x-clip", children: /* @__PURE__ */ jsxs3(
789
+ return /* @__PURE__ */ jsx5("div", { "data-hero-scene": "", className: cn("overflow-x-clip", CLIP_ABOVE), children: /* @__PURE__ */ jsxs3(
769
790
  "div",
770
791
  {
771
792
  className: cn(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@caqophony/ui",
3
- "version": "0.5.0",
3
+ "version": "0.5.1",
4
4
  "description": "Generic, optional React UI reuse library (Gossamer tokens). Centrally maintained by Caqophony.",
5
5
  "license": "MIT",
6
6
  "author": "Caqophony",