@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
|
-
|
|
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={
|
|
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={
|
|
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
|
-
|
|
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
|
|
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