@breathe-ds/ui 0.6.11 → 0.6.12

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
@@ -224,6 +224,11 @@ from color, not from extra markup.
224
224
  centred) + **FilterBar** (thin sticky bar of compact filters — `SelectField density="sm"` —
225
225
  with the count and "clear" on the right; it publishes its height as `--cg-filterbar-h`, so
226
226
  `StickyGroupHeading stickyTop="top-[calc(80px+var(--cg-filterbar-h,0px))]"` sticks right below).
227
+ - **Directory hero with people** (0.6.12): `PageHero photos={[…]}` — the photo wall from
228
+ metodorta's professionals page: real portraits (4:5) in three columns drifting up on the right
229
+ half, fading into the band; a strip of faces on phones. Decorative, still with reduced motion,
230
+ needs ≥ 6 photos, replaces the watermark. `tone="callout"` gives the panel the Callout's
231
+ colour ("info well"), as on metodorta.
227
232
  - **Callout** (a.k.a. info well): a secondary invitation under a page's main control
228
233
  (location invite under the professionals search, a tip under the articles search), at
229
234
  most one action. Never for errors or warnings. Default `tone="wash"`: no border, the
@@ -16,15 +16,54 @@ type Props = {
16
16
  * 6% in `ink`). Decorative: rendered aria-hidden. */
17
17
  watermark?: ReactNode;
18
18
  /** `alt` (default): the section band colour (`surface-alt`), light, ink
19
- * text at full contrast (8.85:1 on ttcab). `card`: white. */
20
- tone?: "alt" | "card";
19
+ * text at full contrast (8.85:1 on ttcab). `card`: white. `callout`: the
20
+ * colour of the Callout ("info well"), a step deeper (0.6.12). */
21
+ tone?: "alt" | "card" | "callout";
21
22
  /** `panel` (default): the rounded band with the tools inside. `centered`: the
22
23
  * Apple search page — no box, title and lead centred, one search below on the
23
24
  * page colour; refining filters live outside, after the results begin. */
24
25
  variant?: "panel" | "centered";
26
+ /** Photo wall (0.6.12, from metodorta's professionals page): real portraits
27
+ * (4:5) in three columns drifting slowly up on the right half of the panel,
28
+ * fading into the band on the left so the title sits on a clean background;
29
+ * on phones, one strip of faces moving sideways at the top. Decorative
30
+ * (aria-hidden, alt=""), still with prefers-reduced-motion. Needs ≥ 6 photos;
31
+ * replaces the watermark. `panel` variant only. */
32
+ photos?: string[];
25
33
  className?: string;
26
34
  };
27
35
 
36
+ /** The photo wall of `PageHero` (CSS animation, no JS: 70–95 s a lap, the
37
+ * middle column the other way). Content is doubled, so the loop has no jump. */
38
+ function PhotoWall({ photos }: { photos: string[] }) {
39
+ const columns = [0, 1, 2].map((c) => photos.filter((_, i) => i % 3 === c));
40
+ const speed = ["animate-[cg-wall-up_80s_linear_infinite]", "animate-[cg-wall-up_95s_linear_infinite_reverse]", "animate-[cg-wall-up_70s_linear_infinite]"];
41
+ return (
42
+ <div aria-hidden="true" className="pointer-events-none">
43
+ <div className="absolute inset-y-0 right-0 hidden w-[52%] grid-cols-3 gap-3 px-3 [mask-image:linear-gradient(to_right,transparent,black_38%),linear-gradient(to_bottom,transparent,black_12%,black_88%,transparent)] [mask-composite:intersect] lg:grid">
44
+ {columns.map((col, c) => (
45
+ <div key={c} className="overflow-hidden">
46
+ <div className={`flex flex-col [&>img]:mb-3 motion-reduce:!animate-none ${speed[c]}`}>
47
+ {[...col, ...col].map((src, i) => (
48
+ // eslint-disable-next-line @next/next/no-img-element
49
+ <img key={i} src={src} alt="" width={384} height={480} decoding="async" className="aspect-[4/5] w-full rounded-[20px] object-cover" />
50
+ ))}
51
+ </div>
52
+ </div>
53
+ ))}
54
+ </div>
55
+ <div className="relative overflow-hidden pt-4 [mask-image:linear-gradient(to_right,transparent,black_10%,black_90%,transparent)] lg:hidden">
56
+ <div className="flex w-max [&>img]:mr-2 motion-reduce:!animate-none animate-[cg-wall-side_60s_linear_infinite]">
57
+ {[...photos.slice(0, 12), ...photos.slice(0, 12)].map((src, i) => (
58
+ // eslint-disable-next-line @next/next/no-img-element
59
+ <img key={i} src={src} alt="" width={384} height={480} decoding="async" className="aspect-[4/5] w-24 shrink-0 rounded-2xl object-cover sm:w-28" />
60
+ ))}
61
+ </div>
62
+ </div>
63
+ </div>
64
+ );
65
+ }
66
+
28
67
  /**
29
68
  * PageHero — the opening panel of an inner page (a library, a directory).
30
69
  * Wide, in novo.creeta's proportion (up to 1600px, 16/24px from the screen
@@ -35,7 +74,8 @@ type Props = {
35
74
  * and the panel groups title, explanation and controls (common region)
36
75
  * without the weight of a dark panel.
37
76
  */
38
- export function PageHero({ title, lead, note, breadcrumb, children, watermark, tone = "alt", variant = "panel", className = "" }: Props) {
77
+ export function PageHero({ title, lead, note, breadcrumb, children, watermark, tone = "alt", variant = "panel", photos, className = "" }: Props) {
78
+ const wall = photos && photos.length >= 6 ? photos : null;
39
79
  if (variant === "centered") {
40
80
  return (
41
81
  <section className={`px-4 pt-[clamp(40px,7vw,96px)] pb-[clamp(28px,4vw,48px)] text-center md:px-6 ${className}`}>
@@ -53,20 +93,23 @@ export function PageHero({ title, lead, note, breadcrumb, children, watermark, t
53
93
  }
54
94
  return (
55
95
  <div className={`px-4 pt-2 md:px-6 md:pt-4 ${className}`}>
56
- <section className={`relative isolate mx-auto max-w-[1600px] overflow-hidden rounded-[28px] md:rounded-[32px] ${tone === "card" ? "bg-card" : "bg-surface-alt"}`}>
57
- {watermark && (
96
+ <section className={`relative isolate mx-auto max-w-[1600px] overflow-hidden rounded-[28px] md:rounded-[32px] ${tone === "card" ? "bg-card" : tone === "callout" ? "bg-callout" : "bg-surface-alt"}`}>
97
+ {wall && <PhotoWall photos={wall} />}
98
+ {watermark && !wall && (
58
99
  <span aria-hidden="true" className="pointer-events-none absolute -right-10 -bottom-16 -z-10 hidden w-[min(34vw,420px)] md:block">
59
100
  {watermark}
60
101
  </span>
61
102
  )}
62
- <div className="mx-auto w-full max-w-6xl px-5 py-[clamp(36px,6vw,72px)] sm:px-6 lg:px-8">
103
+ <div className={`relative mx-auto w-full max-w-6xl px-5 sm:px-6 lg:px-8 ${wall ? "pt-[clamp(28px,5vw,88px)] pb-[clamp(32px,5vw,88px)] lg:min-h-[30rem]" : "py-[clamp(36px,6vw,72px)]"}`}>
63
104
  {breadcrumb && <div className="mb-6">{breadcrumb}</div>}
64
- <Heading as="h1" size="display" className="text-balance">
65
- {title}
66
- </Heading>
67
- {lead && <p className="m-0 mt-4 max-w-xl text-lg leading-relaxed text-ink-body">{lead}</p>}
68
- {note && <p className="m-0 mt-3 max-w-xl text-sm leading-relaxed text-ink-soft">{note}</p>}
69
- {children && <div className="mt-[clamp(24px,4vw,40px)]">{children}</div>}
105
+ <div className={wall ? "max-w-xl lg:max-w-[46%]" : undefined}>
106
+ <Heading as="h1" size="display" className="text-balance">
107
+ {title}
108
+ </Heading>
109
+ {lead && <p className="m-0 mt-4 max-w-xl text-lg leading-relaxed text-ink-body">{lead}</p>}
110
+ {note && <p className="m-0 mt-3 max-w-xl text-sm leading-relaxed text-ink-soft">{note}</p>}
111
+ </div>
112
+ {children && <div className={`mt-[clamp(24px,4vw,40px)] ${wall ? "lg:max-w-[52%]" : ""}`}>{children}</div>}
70
113
  </div>
71
114
  </section>
72
115
  </div>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@breathe-ds/ui",
3
- "version": "0.6.11",
3
+ "version": "0.6.12",
4
4
  "description": "Breathe DS — the design system of the Cuidado Global family of sites (creeta, ttcab, metodorta). Themeable tokens + React components.",
5
5
  "license": "MIT",
6
6
  "type": "module",
package/theme.css CHANGED
@@ -321,3 +321,8 @@
321
321
  mix-blend-mode: lighten;
322
322
  }
323
323
 
324
+
325
+ /* PageHero photo wall (0.6.12): columns drift up, the phone strip drifts sideways.
326
+ Half the track, because the content is doubled: the loop has no jump. */
327
+ @keyframes cg-wall-up { to { transform: translateY(-50%); } }
328
+ @keyframes cg-wall-side { to { transform: translateX(-50%); } }