@breathe-ds/ui 0.6.3 → 0.6.5

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.
@@ -7,8 +7,9 @@ import { Byline } from "./Byline";
7
7
  import { SidePanel } from "./SidePanel";
8
8
 
9
9
  type Labels = {
10
- /** sr-only text of the play button: "Watch: {description}". */
11
- watch: (description: string) => string;
10
+ /** sr-only text of the play button: "Watch: {description}". A function, or a
11
+ * string with `{description}` (a string can cross from a server component). */
12
+ watch: ((description: string) => string) | string;
12
13
  /** sr-only after the outbound link. */
13
14
  newTab: string;
14
15
  /** Button under a clamped quote that opens the whole testimonial. */
@@ -180,6 +181,7 @@ const CLAMP: Record<number, string> = { 3: "line-clamp-3", 4: "line-clamp-4", 5:
180
181
 
181
182
  export function TestimonialCard({ quote, author, authorPhoto, authorAvatar, authors, link, video, photo, mediaPlacement = "card", role, joiners, panel, excerpt, excerptMarks = true, clampLines, as: Root = "div", labels, className = "" }: Props) {
182
183
  const t = { ...EN, ...labels };
184
+ const watch = (d: string) => (typeof t.watch === "function" ? t.watch(d) : t.watch.replace("{description}", d));
183
185
  const people = authors ?? [{ name: author ?? "", photo: authorPhoto, avatar: authorAvatar }];
184
186
  const cardPhoto = mediaPlacement === "card" ? photo : undefined;
185
187
  const cardVideo = mediaPlacement === "card" ? video : undefined;
@@ -214,7 +216,7 @@ export function TestimonialCard({ quote, author, authorPhoto, authorAvatar, auth
214
216
  return (
215
217
  <Root className={`${opens ? "group/card relative" : ""} ${cardVideo || cardPhoto ? "grid grid-cols-1 sm:grid-cols-[minmax(0,2fr)_minmax(0,3fr)]" : "flex"} overflow-hidden rounded-[28px] bg-card ${className}`}>
216
218
  {cardVideo ? (
217
- <TapVideo {...cardVideo} label={t.watch(cardVideo.description)} frameClass="relative z-[1] aspect-video overflow-hidden bg-ink sm:aspect-auto sm:min-h-[16rem]" />
219
+ <TapVideo {...cardVideo} label={watch(cardVideo.description)} frameClass="relative z-[1] aspect-video overflow-hidden bg-ink sm:aspect-auto sm:min-h-[16rem]" />
218
220
  ) : cardPhoto ? (
219
221
  <div className="relative aspect-[4/3] overflow-hidden bg-tint sm:aspect-auto sm:min-h-[16rem]">
220
222
  {cardPhoto.media ?? (
@@ -260,7 +262,7 @@ export function TestimonialCard({ quote, author, authorPhoto, authorAvatar, auth
260
262
  <article className="flex flex-col gap-6 px-6 pt-2 pb-10 md:px-8">
261
263
  {panel && video ? (
262
264
  <div className="overflow-hidden rounded-2xl">
263
- <TapVideo {...video} label={t.watch(video.description)} frameClass="relative aspect-video overflow-hidden bg-ink" />
265
+ <TapVideo {...video} label={watch(video.description)} frameClass="relative aspect-video overflow-hidden bg-ink" />
264
266
  </div>
265
267
  ) : panel && photo ? (
266
268
  <div className="overflow-hidden rounded-2xl bg-tint">
@@ -65,8 +65,9 @@ export type VideoInviteCardProps = {
65
65
  video?: InviteVideo;
66
66
  /** `plain` (default): ink text straight over the video, for footage with a light
67
67
  * wall behind the title. `pill`: the title on a white pill, legible over any
68
- * frame (e.g. when the face reaches the top of the crop). */
69
- captionStyle?: "plain" | "pill";
68
+ * frame (e.g. when the face reaches the top of the crop). `blur`: no fill, only
69
+ * the frosted blur of the video behind, white text with a soft shadow. */
70
+ captionStyle?: "plain" | "pill" | "blur";
70
71
  /** Where the title sits on the idle loop: `top` (default) or `bottom` — when the
71
72
  * face fills the top of the frame. The play circle stays bottom-right. */
72
73
  captionAt?: "top" | "bottom";
@@ -99,6 +100,14 @@ function PlayGlyph({ size = 22 }: { size?: number }) {
99
100
  * (`rounded-2xl` in the DS scale); the consumer sets aspect/min-height via
100
101
  * `className`.
101
102
  */
103
+ const CAPTION = {
104
+ plain: "text-ink [text-shadow:0_1px_2px_rgba(255,255,255,0.5)]",
105
+ pill: "text-ink rounded-2xl bg-card/90 px-3 py-2 backdrop-blur-sm",
106
+ // a 20% ink veil under the blur: white measured 5.1–5.5:1 over ttcab's loop
107
+ // (4.2–4.7:1 with blur alone); the shadow holds it on the lightest frames
108
+ blur: "text-white rounded-lg bg-ink/20 px-3 py-2 backdrop-blur-md [text-shadow:0_1px_3px_rgba(0,0,0,0.55)]",
109
+ } as const;
110
+
102
111
  export function VideoInviteCard({ title, description, loop, video, captionStyle = "plain", captionAt = "top", onPlay, onEnded, labels, className = "" }: VideoInviteCardProps) {
103
112
  const [inviting, setInviting] = useState(false);
104
113
  const t = { ...EN, ...labels };
@@ -108,7 +117,7 @@ export function VideoInviteCard({ title, description, loop, video, captionStyle
108
117
  median on the loop, 5.86:1 on the invitation). The light text-shadow
109
118
  holds it on the darker frames. */
110
119
  const caption = (
111
- <p className={`inline-block text-sm font-medium leading-snug text-ink ${captionStyle === "pill" ? "rounded-2xl bg-card/90 px-3 py-2 backdrop-blur-sm" : "[text-shadow:0_1px_2px_rgba(255,255,255,0.5)]"}`}>
120
+ <p className={`inline-block text-sm font-medium leading-snug ${CAPTION[captionStyle]}`}>
112
121
  <span className="block">{title}</span>
113
122
  {description && <span className="block font-normal">{description}</span>}
114
123
  </p>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@breathe-ds/ui",
3
- "version": "0.6.3",
3
+ "version": "0.6.5",
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",