@salesmind-ai/design-system 1.4.0 → 1.6.0
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/dist/{chunk-RY257SWH.js → chunk-PW56UU3S.js} +92 -9
- package/dist/chunk-PW56UU3S.js.map +1 -0
- package/dist/{chunk-YL4FGGHF.cjs → chunk-SR4QRXRR.cjs} +92 -9
- package/dist/chunk-SR4QRXRR.cjs.map +1 -0
- package/dist/index.cjs +9 -9
- package/dist/index.css +91 -0
- package/dist/index.css.map +1 -1
- package/dist/index.js +1 -1
- package/dist/social-proof/index.cjs +9 -9
- package/dist/social-proof/index.css +91 -0
- package/dist/social-proof/index.css.map +1 -1
- package/dist/social-proof/index.d.cts +12 -0
- package/dist/social-proof/index.d.ts +12 -0
- package/dist/social-proof/index.js +1 -1
- package/dist/styles/styles.css +29 -0
- package/package.json +2 -2
- package/dist/chunk-RY257SWH.js.map +0 -1
- package/dist/chunk-YL4FGGHF.cjs.map +0 -1
|
@@ -207,6 +207,15 @@ var PlatformBadge = forwardRef(
|
|
|
207
207
|
}
|
|
208
208
|
);
|
|
209
209
|
PlatformBadge.displayName = "PlatformBadge";
|
|
210
|
+
function getInitials(name) {
|
|
211
|
+
return name.split(/\s+/).map((word) => word[0]).filter(Boolean).slice(0, 2).join("").toUpperCase();
|
|
212
|
+
}
|
|
213
|
+
var platformLabels = {
|
|
214
|
+
trustpilot: "Trustpilot",
|
|
215
|
+
g2: "G2",
|
|
216
|
+
google: "Google",
|
|
217
|
+
"chrome-web-store": "Chrome Web Store"
|
|
218
|
+
};
|
|
210
219
|
var SocialProofCard = forwardRef(
|
|
211
220
|
({
|
|
212
221
|
variant = "standard",
|
|
@@ -222,8 +231,13 @@ var SocialProofCard = forwardRef(
|
|
|
222
231
|
industry,
|
|
223
232
|
ctaText,
|
|
224
233
|
className,
|
|
234
|
+
verified,
|
|
235
|
+
reviewUrl,
|
|
236
|
+
reviewDate,
|
|
237
|
+
workspaceProof,
|
|
225
238
|
...props
|
|
226
239
|
}, ref) => {
|
|
240
|
+
const hasTrustHeader = platform || verified || starRating !== void 0 || industry;
|
|
227
241
|
return /* @__PURE__ */ jsxs(
|
|
228
242
|
"article",
|
|
229
243
|
{
|
|
@@ -235,6 +249,7 @@ var SocialProofCard = forwardRef(
|
|
|
235
249
|
"ds-social-proof-card",
|
|
236
250
|
`ds-social-proof-card--${variant}`,
|
|
237
251
|
href && "ds-social-proof-card--linked",
|
|
252
|
+
verified && "ds-social-proof-card--verified",
|
|
238
253
|
className
|
|
239
254
|
),
|
|
240
255
|
...props,
|
|
@@ -250,20 +265,63 @@ var SocialProofCard = forwardRef(
|
|
|
250
265
|
}
|
|
251
266
|
) }),
|
|
252
267
|
/* @__PURE__ */ jsxs("div", { className: "ds-social-proof-card__body", children: [
|
|
253
|
-
/* @__PURE__ */ jsxs("div", { className: "ds-social-proof-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
268
|
+
hasTrustHeader && /* @__PURE__ */ jsxs("div", { className: "ds-social-proof-card__trust-header", children: [
|
|
269
|
+
/* @__PURE__ */ jsxs("div", { className: "ds-social-proof-card__trust-platforms", children: [
|
|
270
|
+
platform && /* @__PURE__ */ jsx(PlatformBadge, { platform }),
|
|
271
|
+
verified && /* @__PURE__ */ jsxs("span", { className: "ds-social-proof-card__verified", "aria-label": "Verified review", children: [
|
|
272
|
+
/* @__PURE__ */ jsx(
|
|
273
|
+
"svg",
|
|
274
|
+
{
|
|
275
|
+
className: "ds-social-proof-card__verified-icon",
|
|
276
|
+
viewBox: "0 0 16 16",
|
|
277
|
+
fill: "none",
|
|
278
|
+
"aria-hidden": "true",
|
|
279
|
+
width: "14",
|
|
280
|
+
height: "14",
|
|
281
|
+
children: /* @__PURE__ */ jsx(
|
|
282
|
+
"path",
|
|
283
|
+
{
|
|
284
|
+
d: "M8 1L10.05 5.16L14.63 5.84L11.32 9.08L12.1 13.67L8 11.5L3.9 13.67L4.68 9.08L1.37 5.84L5.95 5.16L8 1Z",
|
|
285
|
+
fill: "currentColor"
|
|
286
|
+
}
|
|
287
|
+
)
|
|
288
|
+
}
|
|
289
|
+
),
|
|
290
|
+
"Verified"
|
|
291
|
+
] })
|
|
292
|
+
] }),
|
|
293
|
+
/* @__PURE__ */ jsxs("div", { className: "ds-social-proof-card__trust-stars", children: [
|
|
294
|
+
starRating !== void 0 && /* @__PURE__ */ jsx(StarRating, { value: starRating, size: "sm" }),
|
|
295
|
+
industry && /* @__PURE__ */ jsx("span", { className: "ds-social-proof-card__industry", children: industry })
|
|
296
|
+
] })
|
|
257
297
|
] }),
|
|
258
298
|
headline && /* @__PURE__ */ jsx("h3", { className: "ds-social-proof-card__headline", children: headline }),
|
|
259
299
|
/* @__PURE__ */ jsx("blockquote", { className: "ds-social-proof-card__quote", children: /* @__PURE__ */ jsx("p", { children: content }) }),
|
|
300
|
+
workspaceProof && /* @__PURE__ */ jsxs("div", { className: "ds-social-proof-card__workspace-proof", "aria-label": "Verified workspace proof", children: [
|
|
301
|
+
/* @__PURE__ */ jsxs(
|
|
302
|
+
"svg",
|
|
303
|
+
{
|
|
304
|
+
className: "ds-social-proof-card__workspace-icon",
|
|
305
|
+
viewBox: "0 0 16 16",
|
|
306
|
+
fill: "none",
|
|
307
|
+
"aria-hidden": "true",
|
|
308
|
+
width: "12",
|
|
309
|
+
height: "12",
|
|
310
|
+
children: [
|
|
311
|
+
/* @__PURE__ */ jsx("rect", { x: "1", y: "2", width: "14", height: "11", rx: "2", stroke: "currentColor", strokeWidth: "1.5" }),
|
|
312
|
+
/* @__PURE__ */ jsx("path", { d: "M5 8H11M8 5V11", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round" })
|
|
313
|
+
]
|
|
314
|
+
}
|
|
315
|
+
),
|
|
316
|
+
/* @__PURE__ */ jsx("span", { children: workspaceProof })
|
|
317
|
+
] }),
|
|
260
318
|
metrics && metrics.length > 0 && /* @__PURE__ */ jsx("div", { className: "ds-social-proof-card__metrics", children: metrics.map((metric, index) => /* @__PURE__ */ jsxs("div", { className: "ds-social-proof-card__metric", children: [
|
|
261
319
|
/* @__PURE__ */ jsx("span", { className: "ds-social-proof-card__metric-value", children: metric.value }),
|
|
262
320
|
/* @__PURE__ */ jsx("span", { className: "ds-social-proof-card__metric-label", children: metric.label })
|
|
263
321
|
] }, index)) }),
|
|
264
322
|
badges && badges.length > 0 && /* @__PURE__ */ jsx("div", { className: "ds-social-proof-card__badges", children: badges.map((badge, index) => /* @__PURE__ */ jsx("span", { className: "ds-social-proof-card__badge", children: badge }, index)) }),
|
|
265
323
|
/* @__PURE__ */ jsxs("footer", { className: "ds-social-proof-card__author", children: [
|
|
266
|
-
author.avatar
|
|
324
|
+
author.avatar ? /* @__PURE__ */ jsx(
|
|
267
325
|
"img",
|
|
268
326
|
{
|
|
269
327
|
src: author.avatar,
|
|
@@ -272,12 +330,37 @@ var SocialProofCard = forwardRef(
|
|
|
272
330
|
loading: "lazy",
|
|
273
331
|
decoding: "async"
|
|
274
332
|
}
|
|
275
|
-
),
|
|
333
|
+
) : /* @__PURE__ */ jsx("span", { className: "ds-social-proof-card__avatar ds-social-proof-card__avatar--initials", "aria-hidden": "true", children: getInitials(author.name) }),
|
|
276
334
|
/* @__PURE__ */ jsxs("div", { className: "ds-social-proof-card__author-copy", children: [
|
|
277
335
|
/* @__PURE__ */ jsx("cite", { className: "ds-social-proof-card__name", children: author.name }),
|
|
278
|
-
(author.title || author.company) && /* @__PURE__ */ jsx("span", { className: "ds-social-proof-card__role", children: [author.title, author.company].filter(Boolean).join(" \xB7 ") })
|
|
279
|
-
|
|
336
|
+
(author.title || author.company) && /* @__PURE__ */ jsx("span", { className: "ds-social-proof-card__role", children: [author.title, author.company].filter(Boolean).join(" \xB7 ") }),
|
|
337
|
+
(author.location || reviewDate) && /* @__PURE__ */ jsx("span", { className: "ds-social-proof-card__micrometa", children: [author.location, reviewDate].filter(Boolean).join(" \xB7 ") })
|
|
338
|
+
] }),
|
|
339
|
+
author.companyLogo && /* @__PURE__ */ jsx(
|
|
340
|
+
"img",
|
|
341
|
+
{
|
|
342
|
+
src: author.companyLogo,
|
|
343
|
+
alt: author.company ?? "",
|
|
344
|
+
className: "ds-social-proof-card__company-logo",
|
|
345
|
+
loading: "lazy",
|
|
346
|
+
decoding: "async"
|
|
347
|
+
}
|
|
348
|
+
)
|
|
280
349
|
] }),
|
|
350
|
+
reviewUrl && platform && /* @__PURE__ */ jsxs(
|
|
351
|
+
OutboundLink,
|
|
352
|
+
{
|
|
353
|
+
href: reviewUrl,
|
|
354
|
+
className: "ds-social-proof-card__review-link",
|
|
355
|
+
context: "social-proof-card",
|
|
356
|
+
openInNewTab: true,
|
|
357
|
+
children: [
|
|
358
|
+
"Read on ",
|
|
359
|
+
platformLabels[platform],
|
|
360
|
+
" \u2192"
|
|
361
|
+
]
|
|
362
|
+
}
|
|
363
|
+
),
|
|
281
364
|
href && ctaText && /* @__PURE__ */ jsx("span", { className: "ds-social-proof-card__cta", children: ctaText })
|
|
282
365
|
] })
|
|
283
366
|
]
|
|
@@ -489,4 +572,4 @@ VideoLightbox.displayName = "VideoLightbox";
|
|
|
489
572
|
|
|
490
573
|
export { PlatformBadge, SocialProofCard, SocialProofCarousel, SocialProofFeatured, SocialProofGrid, SocialProofLogos, StarRating, VideoLightbox };
|
|
491
574
|
//# sourceMappingURL=out.js.map
|
|
492
|
-
//# sourceMappingURL=chunk-
|
|
575
|
+
//# sourceMappingURL=chunk-PW56UU3S.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/StarRating/StarRating.tsx","../src/components/PlatformBadge/PlatformBadge.tsx","../src/components/SocialProofCard/SocialProofCard.tsx","../src/components/SocialProofCarousel/SocialProofCarousel.tsx","../src/components/SocialProofFeatured/SocialProofFeatured.tsx","../src/components/SocialProofGrid/SocialProofGrid.tsx","../src/components/SocialProofLogos/SocialProofLogos.tsx","../src/components/VideoLightbox/VideoLightbox.tsx"],"names":["forwardRef","clsx","jsx","jsxs","useState"],"mappings":"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","sourcesContent":["import React, { forwardRef, useEffect, useRef, useState } from 'react';\nimport clsx from 'clsx';\nimport './StarRating.css';\n\n/* ============================================================================\n STAR RATING\n ============================================================================\n Animated star display with half-star support, configurable sizes, and\n optional viewport-triggered fill animation. Replaces text-character\n star displays across all social proof surfaces.\n\n Strategic objective: Instant visual trust signal\n ============================================================================ */\n\nexport interface StarRatingProps extends React.HTMLAttributes<HTMLDivElement> {\n /** Rating value (0-5, supports decimals for half-stars) */\n value: number;\n /** Maximum number of stars */\n max?: number;\n /** Star size variant */\n size?: 'sm' | 'md' | 'lg';\n /** Animate stars filling on viewport entry */\n animate?: boolean;\n /** Show numeric value next to stars */\n showValue?: boolean;\n /** Label shown after value (e.g., \"/ 5\") */\n valueLabel?: string;\n}\n\n/**\n * Star rating display with gold-fill, half-star support, and optional\n * entrance animation. Uses inline SVGs with clip-path for precise\n * partial fills.\n *\n * @example\n * ```tsx\n * <StarRating value={4.5} size=\"lg\" animate />\n * <StarRating value={5} size=\"sm\" showValue />\n * ```\n */\nexport const StarRating = forwardRef<HTMLDivElement, StarRatingProps>(\n (\n {\n value,\n max = 5,\n size = 'md',\n animate = false,\n showValue = false,\n valueLabel,\n className,\n ...props\n },\n ref,\n ) => {\n const [visible, setVisible] = useState(!animate);\n const containerRef = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n if (!animate) return;\n\n // Respect reduced motion\n if (typeof window !== 'undefined') {\n const mq = window.matchMedia('(prefers-reduced-motion: reduce)');\n if (mq.matches) {\n setVisible(true);\n return;\n }\n }\n\n const target = containerRef.current;\n if (!target) return;\n\n const observer = new IntersectionObserver(\n ([entry]) => {\n if (entry.isIntersecting) {\n setVisible(true);\n observer.disconnect();\n }\n },\n { threshold: 0.5 },\n );\n\n observer.observe(target);\n return () => observer.disconnect();\n }, [animate]);\n\n const clampedValue = Math.min(Math.max(value, 0), max);\n\n const stars = Array.from({ length: max }, (_, i) => {\n const starIndex = i + 1;\n const fillPercent =\n clampedValue >= starIndex\n ? 100\n : clampedValue > starIndex - 1\n ? (clampedValue - (starIndex - 1)) * 100\n : 0;\n\n return (\n <span\n key={i}\n className={clsx(\n 'ds-star-rating__star',\n visible && 'ds-star-rating__star--visible',\n )}\n style={{\n '--star-fill': `${fillPercent}%`,\n '--star-delay': animate ? `${i * 100}ms` : '0ms',\n } as React.CSSProperties}\n >\n {/* Background (empty) star */}\n <svg\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n className=\"ds-star-rating__svg\"\n aria-hidden=\"true\"\n >\n <path\n d=\"M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z\"\n className=\"ds-star-rating__empty\"\n />\n </svg>\n {/* Foreground (filled) star with clip */}\n <svg\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n className=\"ds-star-rating__svg ds-star-rating__svg--filled\"\n aria-hidden=\"true\"\n style={{\n clipPath: `inset(0 ${100 - fillPercent}% 0 0)`,\n }}\n >\n <path\n d=\"M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z\"\n className=\"ds-star-rating__filled\"\n />\n </svg>\n </span>\n );\n });\n\n return (\n <div\n ref={(node) => {\n (containerRef as React.MutableRefObject<HTMLDivElement | null>).current = node;\n if (typeof ref === 'function') ref(node);\n else if (ref) (ref as React.MutableRefObject<HTMLDivElement | null>).current = node;\n }}\n className={clsx('ds-star-rating', `ds-star-rating--${size}`, className)}\n role=\"img\"\n aria-label={`${clampedValue} out of ${max} stars`}\n {...props}\n >\n <span className=\"ds-star-rating__stars\">{stars}</span>\n {showValue && (\n <span className=\"ds-star-rating__value\">\n {clampedValue.toLocaleString(undefined, {\n minimumFractionDigits: clampedValue % 1 !== 0 ? 1 : 0,\n maximumFractionDigits: 1,\n })}\n {valueLabel && (\n <span className=\"ds-star-rating__value-label\">{valueLabel}</span>\n )}\n </span>\n )}\n </div>\n );\n },\n);\n\nStarRating.displayName = 'StarRating';\n","import React, { forwardRef } from 'react';\nimport clsx from 'clsx';\nimport { StarRating } from '../StarRating/StarRating';\nimport './PlatformBadge.css';\n\n/* ============================================================================\n PLATFORM BADGE\n ============================================================================\n Recognizable review platform icons (Trustpilot, G2, Google, Chrome Web\n Store) with optional rating display. Builds instant trust through visual\n brand recognition.\n\n Strategic objective: Third-party authority signal\n ============================================================================ */\n\nexport type Platform = 'trustpilot' | 'g2' | 'google' | 'chrome-web-store';\n\nexport interface PlatformBadgeProps extends React.HTMLAttributes<HTMLDivElement> {\n /** Review platform */\n platform: Platform;\n /** Display mode */\n variant?: 'icon-only' | 'icon-label' | 'full';\n /** Platform rating (used in 'full' variant) */\n rating?: number;\n /** Number of reviews (used in 'full' variant) */\n count?: number;\n /** Size variant */\n size?: 'sm' | 'md';\n}\n\n/* --------------------------------------------------------------------------\n Platform SVG Icons — inline for zero-latency rendering\n -------------------------------------------------------------------------- */\n\nconst TrustpilotIcon = ({ size }: { size: number }) => (\n <svg width={size} height={size} viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">\n <path d=\"M12 2l2.9 8.9H24l-7.35 5.34 2.81 8.64L12 19.56l-7.46 5.32 2.81-8.64L0 10.9h9.1L12 2z\" fill=\"#00B67A\" />\n </svg>\n);\n\nconst G2Icon = ({ size }: { size: number }) => (\n <svg width={size} height={size} viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">\n <path d=\"M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2z\" fill=\"#FF492C\" />\n <text x=\"50%\" y=\"54%\" dominantBaseline=\"middle\" textAnchor=\"middle\" fill=\"white\" fontWeight=\"bold\" fontSize=\"11\" fontFamily=\"Arial, sans-serif\">G2</text>\n </svg>\n);\n\nconst GoogleIcon = ({ size }: { size: number }) => (\n <svg width={size} height={size} viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">\n <path d=\"M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92a5.06 5.06 0 01-2.2 3.32v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.1z\" fill=\"#4285F4\"/>\n <path d=\"M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z\" fill=\"#34A853\"/>\n <path d=\"M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18A10.96 10.96 0 001 12c0 1.77.42 3.45 1.18 4.93l3.66-2.84z\" fill=\"#FBBC05\"/>\n <path d=\"M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z\" fill=\"#EA4335\"/>\n </svg>\n);\n\nconst ChromeWebStoreIcon = ({ size }: { size: number }) => (\n <svg width={size} height={size} viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">\n <circle cx=\"12\" cy=\"12\" r=\"10\" fill=\"#4285F4\" opacity=\"0.1\" stroke=\"#4285F4\" strokeWidth=\"1.5\"/>\n <path d=\"M12 7c-2.76 0-5 2.24-5 5s2.24 5 5 5 5-2.24 5-5-2.24-5-5-5zm0 8.5c-1.93 0-3.5-1.57-3.5-3.5S10.07 8.5 12 8.5s3.5 1.57 3.5 3.5-1.57 3.5-3.5 3.5z\" fill=\"#4285F4\"/>\n <path d=\"M7.5 10.5L4 5.5\" stroke=\"#EA4335\" strokeWidth=\"1.5\" strokeLinecap=\"round\"/>\n <path d=\"M16.5 10.5L20 5.5\" stroke=\"#FBBC05\" strokeWidth=\"1.5\" strokeLinecap=\"round\"/>\n <path d=\"M12 17l-3.5 5\" stroke=\"#34A853\" strokeWidth=\"1.5\" strokeLinecap=\"round\"/>\n </svg>\n);\n\nconst PLATFORM_ICONS: Record<Platform, React.FC<{ size: number }>> = {\n trustpilot: TrustpilotIcon,\n g2: G2Icon,\n google: GoogleIcon,\n 'chrome-web-store': ChromeWebStoreIcon,\n};\n\nconst PLATFORM_LABELS: Record<Platform, string> = {\n trustpilot: 'Trustpilot',\n g2: 'G2',\n google: 'Google',\n 'chrome-web-store': 'Chrome Web Store',\n};\n\nconst PLATFORM_COLORS: Record<Platform, string> = {\n trustpilot: '#00B67A',\n g2: '#FF492C',\n google: '#4285F4',\n 'chrome-web-store': '#4285F4',\n};\n\n/**\n * Review platform badge with recognizable brand icon, optional label,\n * and optional rating display.\n *\n * @example\n * ```tsx\n * <PlatformBadge platform=\"trustpilot\" variant=\"icon-only\" />\n * <PlatformBadge platform=\"g2\" variant=\"icon-label\" />\n * <PlatformBadge platform=\"google\" variant=\"full\" rating={4.9} count={42} />\n * ```\n */\nexport const PlatformBadge = forwardRef<HTMLDivElement, PlatformBadgeProps>(\n (\n {\n platform,\n variant = 'icon-label',\n rating,\n count,\n size = 'sm',\n className,\n ...props\n },\n ref,\n ) => {\n const Icon = PLATFORM_ICONS[platform];\n const label = PLATFORM_LABELS[platform];\n const accentColor = PLATFORM_COLORS[platform];\n const iconSize = size === 'sm' ? 18 : 22;\n\n return (\n <div\n ref={ref}\n role=\"img\"\n className={clsx(\n 'ds-platform-badge',\n `ds-platform-badge--${variant}`,\n `ds-platform-badge--${size}`,\n className,\n )}\n style={{ '--platform-accent': accentColor } as React.CSSProperties}\n aria-label={\n variant === 'full' && rating\n ? `${label}: ${rating} stars${count ? ` from ${count} reviews` : ''}`\n : label\n }\n {...props}\n >\n <span className=\"ds-platform-badge__icon\">\n <Icon size={iconSize} />\n </span>\n\n {variant !== 'icon-only' && (\n <span className=\"ds-platform-badge__label\">{label}</span>\n )}\n\n {variant === 'full' && rating != null && (\n <span className=\"ds-platform-badge__rating\">\n <StarRating value={rating} size=\"sm\" />\n {count != null && (\n <span className=\"ds-platform-badge__count\">({count})</span>\n )}\n </span>\n )}\n </div>\n );\n },\n);\n\nPlatformBadge.displayName = 'PlatformBadge';\n","import React, { forwardRef } from 'react';\nimport clsx from 'clsx';\nimport { PlatformBadge, type Platform } from '../PlatformBadge/PlatformBadge';\nimport { StarRating } from '../StarRating/StarRating';\nimport { OutboundLink } from '../OutboundLink/OutboundLink';\nimport './SocialProofCard.css';\nimport './SocialProofCard-v2.css';\n\nexport interface SocialProofAuthor {\n name: string;\n title?: string;\n company?: string;\n avatar?: string;\n /** Secondary brand mark shown after the author name. */\n companyLogo?: string;\n /** Location shown as a micro-label, for example country or city. */\n location?: string;\n}\n\nexport interface SocialProofMetric {\n value: React.ReactNode;\n label: React.ReactNode;\n}\n\nexport interface SocialProofMedia {\n src: string;\n type: 'image';\n alt?: string;\n}\n\nexport interface SocialProofCardProps extends Omit<React.HTMLAttributes<HTMLElement>, 'content'> {\n variant?: 'social' | 'compact' | 'standard' | 'detailed' | 'narrative';\n content: React.ReactNode;\n author: SocialProofAuthor;\n starRating?: number;\n platform?: Platform;\n badges?: React.ReactNode[];\n href?: string;\n media?: SocialProofMedia;\n metrics?: SocialProofMetric[];\n headline?: React.ReactNode;\n industry?: React.ReactNode;\n ctaText?: React.ReactNode;\n /** Renders a verified-review pill when true. */\n verified?: boolean;\n /** Outbound link to the original review. Requires platform to render. */\n reviewUrl?: string;\n /** Review date label, for example \"Jan 2026\". */\n reviewDate?: string;\n /** Provenance line sourced from real workspace data. */\n workspaceProof?: string;\n}\n\nfunction getInitials(name: string): string {\n return name\n .split(/\\s+/)\n .map((word) => word[0])\n .filter(Boolean)\n .slice(0, 2)\n .join('')\n .toUpperCase();\n}\n\nconst platformLabels: Record<Platform, string> = {\n trustpilot: 'Trustpilot',\n g2: 'G2',\n google: 'Google',\n 'chrome-web-store': 'Chrome Web Store',\n};\n\nexport const SocialProofCard = forwardRef<HTMLElement, SocialProofCardProps>(\n (\n {\n variant = 'standard',\n content,\n author,\n starRating,\n platform,\n badges,\n href,\n media,\n metrics,\n headline,\n industry,\n ctaText,\n className,\n verified,\n reviewUrl,\n reviewDate,\n workspaceProof,\n ...props\n },\n ref,\n ) => {\n const hasTrustHeader = platform || verified || starRating !== undefined || industry;\n\n return (\n <article\n ref={ref}\n className={clsx(\n 'ds-card',\n variant === 'compact' ? 'ds-card--tier-1' : 'ds-card--tier-2',\n variant === 'compact' ? 'glass--tier-1' : 'glass--tier-2',\n 'ds-social-proof-card',\n `ds-social-proof-card--${variant}`,\n href && 'ds-social-proof-card--linked',\n verified && 'ds-social-proof-card--verified',\n className,\n )}\n {...props}\n >\n {media?.src && (\n <div className=\"ds-social-proof-card__media\">\n <img\n src={media.src}\n alt={media.alt ?? ''}\n className=\"ds-social-proof-card__image\"\n loading=\"lazy\"\n decoding=\"async\"\n />\n </div>\n )}\n\n <div className=\"ds-social-proof-card__body\">\n {hasTrustHeader && (\n <div className=\"ds-social-proof-card__trust-header\">\n <div className=\"ds-social-proof-card__trust-platforms\">\n {platform && <PlatformBadge platform={platform} />}\n {verified && (\n <span className=\"ds-social-proof-card__verified\" aria-label=\"Verified review\">\n <svg\n className=\"ds-social-proof-card__verified-icon\"\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n aria-hidden=\"true\"\n width=\"14\"\n height=\"14\"\n >\n <path\n d=\"M8 1L10.05 5.16L14.63 5.84L11.32 9.08L12.1 13.67L8 11.5L3.9 13.67L4.68 9.08L1.37 5.84L5.95 5.16L8 1Z\"\n fill=\"currentColor\"\n />\n </svg>\n Verified\n </span>\n )}\n </div>\n <div className=\"ds-social-proof-card__trust-stars\">\n {starRating !== undefined && <StarRating value={starRating} size=\"sm\" />}\n {industry && <span className=\"ds-social-proof-card__industry\">{industry}</span>}\n </div>\n </div>\n )}\n\n {headline && <h3 className=\"ds-social-proof-card__headline\">{headline}</h3>}\n\n <blockquote className=\"ds-social-proof-card__quote\">\n <p>{content}</p>\n </blockquote>\n\n {workspaceProof && (\n <div className=\"ds-social-proof-card__workspace-proof\" aria-label=\"Verified workspace proof\">\n <svg\n className=\"ds-social-proof-card__workspace-icon\"\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n aria-hidden=\"true\"\n width=\"12\"\n height=\"12\"\n >\n <rect x=\"1\" y=\"2\" width=\"14\" height=\"11\" rx=\"2\" stroke=\"currentColor\" strokeWidth=\"1.5\" />\n <path d=\"M5 8H11M8 5V11\" stroke=\"currentColor\" strokeWidth=\"1.5\" strokeLinecap=\"round\" />\n </svg>\n <span>{workspaceProof}</span>\n </div>\n )}\n\n {metrics && metrics.length > 0 && (\n <div className=\"ds-social-proof-card__metrics\">\n {metrics.map((metric, index) => (\n <div className=\"ds-social-proof-card__metric\" key={index}>\n <span className=\"ds-social-proof-card__metric-value\">{metric.value}</span>\n <span className=\"ds-social-proof-card__metric-label\">{metric.label}</span>\n </div>\n ))}\n </div>\n )}\n\n {badges && badges.length > 0 && (\n <div className=\"ds-social-proof-card__badges\">\n {badges.map((badge, index) => (\n <span className=\"ds-social-proof-card__badge\" key={index}>{badge}</span>\n ))}\n </div>\n )}\n\n <footer className=\"ds-social-proof-card__author\">\n {author.avatar ? (\n <img\n src={author.avatar}\n alt=\"\"\n className=\"ds-social-proof-card__avatar\"\n loading=\"lazy\"\n decoding=\"async\"\n />\n ) : (\n <span className=\"ds-social-proof-card__avatar ds-social-proof-card__avatar--initials\" aria-hidden=\"true\">\n {getInitials(author.name)}\n </span>\n )}\n <div className=\"ds-social-proof-card__author-copy\">\n <cite className=\"ds-social-proof-card__name\">{author.name}</cite>\n {(author.title || author.company) && (\n <span className=\"ds-social-proof-card__role\">\n {[author.title, author.company].filter(Boolean).join(' · ')}\n </span>\n )}\n {(author.location || reviewDate) && (\n <span className=\"ds-social-proof-card__micrometa\">\n {[author.location, reviewDate].filter(Boolean).join(' · ')}\n </span>\n )}\n </div>\n {author.companyLogo && (\n <img\n src={author.companyLogo}\n alt={author.company ?? ''}\n className=\"ds-social-proof-card__company-logo\"\n loading=\"lazy\"\n decoding=\"async\"\n />\n )}\n </footer>\n\n {reviewUrl && platform && (\n <OutboundLink\n href={reviewUrl}\n className=\"ds-social-proof-card__review-link\"\n context=\"social-proof-card\"\n openInNewTab\n >\n Read on {platformLabels[platform]} →\n </OutboundLink>\n )}\n\n {href && ctaText && <span className=\"ds-social-proof-card__cta\">{ctaText}</span>}\n </div>\n </article>\n );\n },\n);\n\nSocialProofCard.displayName = 'SocialProofCard';\n","import React, { forwardRef } from 'react';\nimport clsx from 'clsx';\nimport './SocialProofCarousel.css';\n\nexport interface SocialProofCarouselProps extends React.HTMLAttributes<HTMLDivElement> {\n background?: 'default' | 'muted' | 'dark' | 'brand' | 'transparent';\n}\n\nexport const SocialProofCarousel = forwardRef<HTMLDivElement, SocialProofCarouselProps>(\n ({ background = 'transparent', className, children, ...props }, ref) => (\n <div\n ref={ref}\n className={clsx(\n 'ds-social-proof-carousel',\n `ds-social-proof-carousel--background-${background}`,\n className,\n )}\n {...props}\n >\n <div className=\"ds-social-proof-carousel__track\">{children}</div>\n </div>\n ),\n);\n\nSocialProofCarousel.displayName = 'SocialProofCarousel';\n","import React, { forwardRef } from 'react';\nimport clsx from 'clsx';\nimport { Card } from '../Card/Card';\nimport { PlatformBadge, type Platform } from '../PlatformBadge/PlatformBadge';\nimport { StarRating } from '../StarRating/StarRating';\nimport type { SocialProofAuthor } from '../SocialProofCard/SocialProofCard';\nimport './SocialProofFeatured.css';\n\nexport interface SocialProofFeaturedProps extends React.HTMLAttributes<HTMLDivElement> {\n quote: React.ReactNode;\n author: SocialProofAuthor;\n featuredVariant?: 'standard' | 'cinematic' | 'spotlight';\n starRating?: number;\n platform?: Platform;\n mediaSrc?: string;\n background?: 'default' | 'muted' | 'brand' | 'transparent';\n padding?: 'sm' | 'md' | 'lg';\n}\n\nexport const SocialProofFeatured = forwardRef<HTMLDivElement, SocialProofFeaturedProps>(\n (\n {\n quote,\n author,\n featuredVariant = 'standard',\n starRating,\n platform,\n mediaSrc,\n background = 'transparent',\n padding = 'md',\n className,\n ...props\n },\n ref,\n ) => (\n <Card\n ref={ref}\n tier={featuredVariant === 'spotlight' ? 3 : 2}\n radius=\"panel\"\n aurora={featuredVariant === 'spotlight'}\n className={clsx(\n 'ds-social-proof-featured',\n `ds-social-proof-featured--${featuredVariant}`,\n `ds-social-proof-featured--background-${background}`,\n `ds-social-proof-featured--padding-${padding}`,\n className,\n )}\n {...props}\n >\n {mediaSrc && (\n <div className=\"ds-social-proof-featured__media\">\n <img src={mediaSrc} alt=\"\" className=\"ds-social-proof-featured__image\" loading=\"lazy\" decoding=\"async\" />\n </div>\n )}\n\n <div className=\"ds-social-proof-featured__content\">\n <div className=\"ds-social-proof-featured__meta\">\n {starRating !== undefined && <StarRating value={starRating} size=\"md\" />}\n {platform && <PlatformBadge platform={platform} />}\n </div>\n\n <blockquote className=\"ds-social-proof-featured__quote\">\n <p>{quote}</p>\n </blockquote>\n\n <footer className=\"ds-social-proof-featured__author\">\n {author.avatar && (\n <img\n src={author.avatar}\n alt=\"\"\n className=\"ds-social-proof-featured__avatar\"\n loading=\"lazy\"\n decoding=\"async\"\n />\n )}\n <div className=\"ds-social-proof-featured__author-copy\">\n <cite className=\"ds-social-proof-featured__name\">{author.name}</cite>\n {(author.title || author.company) && (\n <span className=\"ds-social-proof-featured__role\">\n {[author.title, author.company].filter(Boolean).join(' · ')}\n </span>\n )}\n </div>\n </footer>\n </div>\n </Card>\n ),\n);\n\nSocialProofFeatured.displayName = 'SocialProofFeatured';\n","import React, { forwardRef } from 'react';\nimport clsx from 'clsx';\nimport './SocialProofGrid.css';\n\nexport interface SocialProofGridProps extends React.HTMLAttributes<HTMLDivElement> {\n columns?: 1 | 2 | 3 | 4;\n staggered?: boolean;\n background?: 'default' | 'muted' | 'dark' | 'brand' | 'transparent';\n}\n\nexport const SocialProofGrid = forwardRef<HTMLDivElement, SocialProofGridProps>(\n ({ columns = 3, staggered = false, background = 'transparent', className, children, ...props }, ref) => (\n <div\n ref={ref}\n className={clsx(\n 'ds-social-proof-grid',\n `ds-social-proof-grid--columns-${columns}`,\n `ds-social-proof-grid--background-${background}`,\n staggered && 'ds-social-proof-grid--staggered',\n className,\n )}\n {...props}\n >\n {children}\n </div>\n ),\n);\n\nSocialProofGrid.displayName = 'SocialProofGrid';\n","import React, { forwardRef } from 'react';\nimport clsx from 'clsx';\nimport { OutboundLink } from '../OutboundLink/OutboundLink';\nimport './SocialProofLogos.css';\n\nexport interface SocialProofLogoItem {\n id: string;\n alt: string;\n src?: string;\n logo?: React.ReactNode;\n href?: string;\n badge?: React.ReactNode;\n}\n\nexport interface SocialProofLogosProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'title'> {\n logos: SocialProofLogoItem[];\n title?: React.ReactNode;\n variant?: 'grid' | 'marquee';\n speed?: 'slow' | 'normal' | 'fast';\n background?: 'default' | 'muted' | 'dark' | 'brand' | 'transparent';\n}\n\nexport const SocialProofLogos = forwardRef<HTMLDivElement, SocialProofLogosProps>(\n ({ logos, title, variant = 'grid', speed = 'normal', background = 'transparent', className, ...props }, ref) => {\n const renderedLogos = logos.map((item) => {\n const content = (\n <span className=\"ds-social-proof-logos__item-content\">\n {item.src ? <img src={item.src} alt={item.alt} className=\"ds-social-proof-logos__image\" loading=\"lazy\" decoding=\"async\" /> : item.logo}\n {item.badge && <span className=\"ds-social-proof-logos__badge\">{item.badge}</span>}\n </span>\n );\n\n return item.href ? (\n <OutboundLink key={item.id} href={item.href} context=\"social-proof-logo\" className=\"ds-social-proof-logos__item\">\n {content}\n </OutboundLink>\n ) : (\n <span key={item.id} className=\"ds-social-proof-logos__item\">\n {content}\n </span>\n );\n });\n\n return (\n <div\n ref={ref}\n className={clsx(\n 'ds-social-proof-logos',\n `ds-social-proof-logos--${variant}`,\n `ds-social-proof-logos--speed-${speed}`,\n `ds-social-proof-logos--background-${background}`,\n className,\n )}\n {...props}\n >\n {title && <p className=\"ds-social-proof-logos__title\">{title}</p>}\n <div className=\"ds-social-proof-logos__viewport\">\n <div className=\"ds-social-proof-logos__track\">\n {renderedLogos}\n {variant === 'marquee' && renderedLogos}\n </div>\n </div>\n </div>\n );\n },\n);\n\nSocialProofLogos.displayName = 'SocialProofLogos';\n","import React, { forwardRef, useState, useCallback } from 'react';\nimport { Dialog as BaseDialog } from '@base-ui/react/dialog';\nimport clsx from 'clsx';\nimport './VideoLightbox.css';\n\n/* ============================================================================\n VIDEO LIGHTBOX\n ============================================================================\n Modal overlay video player built on @base-ui/react Dialog. Opens a lazy\n iframe embed (YouTube/Vimeo/Loom) with backdrop blur, keyboard navigation,\n and focus trap. Designed for inline playback from thumbnail cards without\n navigating away from the current page.\n\n Strategic objective: Engagement — keep users on the Wall of Love\n ============================================================================ */\n\nexport interface VideoLightboxProps {\n /** Video embed URL (YouTube, Vimeo, Loom) */\n videoUrl: string;\n /** Poster image shown as thumbnail trigger */\n thumbnailSrc?: string;\n /** Alt text for thumbnail */\n thumbnailAlt?: string;\n /** Video title for accessibility */\n title?: string;\n /** Custom trigger element (overrides default thumbnail + play button) */\n trigger?: React.ReactNode;\n /** Additional class name for the trigger wrapper */\n className?: string;\n /** Callback when video starts playing */\n onPlay?: () => void;\n}\n\n/* --------------------------------------------------------------------------\n Helpers\n -------------------------------------------------------------------------- */\n\nfunction getEmbedUrl(src: string): string {\n // YouTube\n const ytMatch = src.match(\n /(?:youtube\\.com\\/watch\\?v=|youtu\\.be\\/|youtube\\.com\\/embed\\/)([a-zA-Z0-9_-]+)/,\n );\n if (ytMatch) return `https://www.youtube-nocookie.com/embed/${ytMatch[1]}?autoplay=1&rel=0`;\n\n // Vimeo\n const vimeoMatch = src.match(/(?:vimeo\\.com\\/)(\\d+)/);\n if (vimeoMatch) return `https://player.vimeo.com/video/${vimeoMatch[1]}?autoplay=1`;\n\n // Loom\n const loomMatch = src.match(/(?:loom\\.com\\/share\\/)([a-zA-Z0-9]+)/);\n if (loomMatch) return `https://www.loom.com/embed/${loomMatch[1]}?autoplay=1`;\n\n return src;\n}\n\nconst PlayIcon = () => (\n <svg width=\"64\" height=\"64\" viewBox=\"0 0 64 64\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">\n <circle cx=\"32\" cy=\"32\" r=\"32\" className=\"ds-video-lightbox__play-bg\" />\n <path d=\"M26 20v24l20-12L26 20z\" fill=\"white\" />\n </svg>\n);\n\nconst CloseIcon = () => (\n <svg width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\">\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\" />\n <line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\" />\n </svg>\n);\n\n/**\n * Modal video player triggered by a thumbnail or custom element.\n * Opens a full-screen overlay with lazy iframe injection.\n *\n * @example\n * ```tsx\n * <VideoLightbox\n * videoUrl=\"https://www.youtube.com/watch?v=dQw4w9WgXcQ\"\n * thumbnailSrc=\"/thumbnails/demo.jpg\"\n * title=\"Customer testimonial\"\n * />\n *\n * <VideoLightbox\n * videoUrl=\"https://vimeo.com/123456789\"\n * trigger={<Button>Watch Video</Button>}\n * />\n * ```\n */\nexport const VideoLightbox = forwardRef<HTMLDivElement, VideoLightboxProps>(\n (\n {\n videoUrl,\n thumbnailSrc,\n thumbnailAlt,\n title = 'Video',\n trigger,\n className,\n onPlay,\n },\n ref,\n ) => {\n const [open, setOpen] = useState(false);\n\n const handleOpen = useCallback(() => {\n setOpen(true);\n onPlay?.();\n }, [onPlay]);\n\n return (\n <div ref={ref} className={clsx('ds-video-lightbox', className)}>\n <BaseDialog.Root open={open} onOpenChange={setOpen}>\n <BaseDialog.Trigger\n className=\"ds-video-lightbox__trigger\"\n onClick={handleOpen}\n >\n {trigger || (\n <div className=\"ds-video-lightbox__thumbnail-wrap\">\n {thumbnailSrc ? (\n <img\n src={thumbnailSrc}\n alt={thumbnailAlt || `Play: ${title}`}\n className=\"ds-video-lightbox__thumbnail\"\n loading=\"lazy\"\n decoding=\"async\"\n />\n ) : (\n <div className=\"ds-video-lightbox__placeholder\" />\n )}\n <div className=\"ds-video-lightbox__overlay\">\n <div className=\"ds-video-lightbox__play-btn\">\n <PlayIcon />\n </div>\n </div>\n </div>\n )}\n </BaseDialog.Trigger>\n\n <BaseDialog.Portal>\n <BaseDialog.Backdrop className=\"ds-video-lightbox__backdrop\" />\n <BaseDialog.Popup className=\"ds-video-lightbox__popup\">\n <BaseDialog.Close className=\"ds-video-lightbox__close\" aria-label=\"Close video\">\n <CloseIcon />\n </BaseDialog.Close>\n <BaseDialog.Title className=\"ds-sr-only\">{title}</BaseDialog.Title>\n <div className=\"ds-video-lightbox__iframe-wrap\">\n {open && (\n <iframe\n src={getEmbedUrl(videoUrl)}\n title={title}\n className=\"ds-video-lightbox__iframe\"\n allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture\"\n allowFullScreen\n />\n )}\n </div>\n </BaseDialog.Popup>\n </BaseDialog.Portal>\n </BaseDialog.Root>\n </div>\n );\n },\n);\n\nVideoLightbox.displayName = 'VideoLightbox';\n"]}
|
|
@@ -213,6 +213,15 @@ var PlatformBadge = react.forwardRef(
|
|
|
213
213
|
}
|
|
214
214
|
);
|
|
215
215
|
PlatformBadge.displayName = "PlatformBadge";
|
|
216
|
+
function getInitials(name) {
|
|
217
|
+
return name.split(/\s+/).map((word) => word[0]).filter(Boolean).slice(0, 2).join("").toUpperCase();
|
|
218
|
+
}
|
|
219
|
+
var platformLabels = {
|
|
220
|
+
trustpilot: "Trustpilot",
|
|
221
|
+
g2: "G2",
|
|
222
|
+
google: "Google",
|
|
223
|
+
"chrome-web-store": "Chrome Web Store"
|
|
224
|
+
};
|
|
216
225
|
var SocialProofCard = react.forwardRef(
|
|
217
226
|
({
|
|
218
227
|
variant = "standard",
|
|
@@ -228,8 +237,13 @@ var SocialProofCard = react.forwardRef(
|
|
|
228
237
|
industry,
|
|
229
238
|
ctaText,
|
|
230
239
|
className,
|
|
240
|
+
verified,
|
|
241
|
+
reviewUrl,
|
|
242
|
+
reviewDate,
|
|
243
|
+
workspaceProof,
|
|
231
244
|
...props
|
|
232
245
|
}, ref) => {
|
|
246
|
+
const hasTrustHeader = platform || verified || starRating !== void 0 || industry;
|
|
233
247
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
234
248
|
"article",
|
|
235
249
|
{
|
|
@@ -241,6 +255,7 @@ var SocialProofCard = react.forwardRef(
|
|
|
241
255
|
"ds-social-proof-card",
|
|
242
256
|
`ds-social-proof-card--${variant}`,
|
|
243
257
|
href && "ds-social-proof-card--linked",
|
|
258
|
+
verified && "ds-social-proof-card--verified",
|
|
244
259
|
className
|
|
245
260
|
),
|
|
246
261
|
...props,
|
|
@@ -256,20 +271,63 @@ var SocialProofCard = react.forwardRef(
|
|
|
256
271
|
}
|
|
257
272
|
) }),
|
|
258
273
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "ds-social-proof-card__body", children: [
|
|
259
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "ds-social-proof-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
274
|
+
hasTrustHeader && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "ds-social-proof-card__trust-header", children: [
|
|
275
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "ds-social-proof-card__trust-platforms", children: [
|
|
276
|
+
platform && /* @__PURE__ */ jsxRuntime.jsx(PlatformBadge, { platform }),
|
|
277
|
+
verified && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "ds-social-proof-card__verified", "aria-label": "Verified review", children: [
|
|
278
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
279
|
+
"svg",
|
|
280
|
+
{
|
|
281
|
+
className: "ds-social-proof-card__verified-icon",
|
|
282
|
+
viewBox: "0 0 16 16",
|
|
283
|
+
fill: "none",
|
|
284
|
+
"aria-hidden": "true",
|
|
285
|
+
width: "14",
|
|
286
|
+
height: "14",
|
|
287
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
288
|
+
"path",
|
|
289
|
+
{
|
|
290
|
+
d: "M8 1L10.05 5.16L14.63 5.84L11.32 9.08L12.1 13.67L8 11.5L3.9 13.67L4.68 9.08L1.37 5.84L5.95 5.16L8 1Z",
|
|
291
|
+
fill: "currentColor"
|
|
292
|
+
}
|
|
293
|
+
)
|
|
294
|
+
}
|
|
295
|
+
),
|
|
296
|
+
"Verified"
|
|
297
|
+
] })
|
|
298
|
+
] }),
|
|
299
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "ds-social-proof-card__trust-stars", children: [
|
|
300
|
+
starRating !== void 0 && /* @__PURE__ */ jsxRuntime.jsx(StarRating, { value: starRating, size: "sm" }),
|
|
301
|
+
industry && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "ds-social-proof-card__industry", children: industry })
|
|
302
|
+
] })
|
|
263
303
|
] }),
|
|
264
304
|
headline && /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "ds-social-proof-card__headline", children: headline }),
|
|
265
305
|
/* @__PURE__ */ jsxRuntime.jsx("blockquote", { className: "ds-social-proof-card__quote", children: /* @__PURE__ */ jsxRuntime.jsx("p", { children: content }) }),
|
|
306
|
+
workspaceProof && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "ds-social-proof-card__workspace-proof", "aria-label": "Verified workspace proof", children: [
|
|
307
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
308
|
+
"svg",
|
|
309
|
+
{
|
|
310
|
+
className: "ds-social-proof-card__workspace-icon",
|
|
311
|
+
viewBox: "0 0 16 16",
|
|
312
|
+
fill: "none",
|
|
313
|
+
"aria-hidden": "true",
|
|
314
|
+
width: "12",
|
|
315
|
+
height: "12",
|
|
316
|
+
children: [
|
|
317
|
+
/* @__PURE__ */ jsxRuntime.jsx("rect", { x: "1", y: "2", width: "14", height: "11", rx: "2", stroke: "currentColor", strokeWidth: "1.5" }),
|
|
318
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M5 8H11M8 5V11", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round" })
|
|
319
|
+
]
|
|
320
|
+
}
|
|
321
|
+
),
|
|
322
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: workspaceProof })
|
|
323
|
+
] }),
|
|
266
324
|
metrics && metrics.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "ds-social-proof-card__metrics", children: metrics.map((metric, index) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "ds-social-proof-card__metric", children: [
|
|
267
325
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "ds-social-proof-card__metric-value", children: metric.value }),
|
|
268
326
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "ds-social-proof-card__metric-label", children: metric.label })
|
|
269
327
|
] }, index)) }),
|
|
270
328
|
badges && badges.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "ds-social-proof-card__badges", children: badges.map((badge, index) => /* @__PURE__ */ jsxRuntime.jsx("span", { className: "ds-social-proof-card__badge", children: badge }, index)) }),
|
|
271
329
|
/* @__PURE__ */ jsxRuntime.jsxs("footer", { className: "ds-social-proof-card__author", children: [
|
|
272
|
-
author.avatar
|
|
330
|
+
author.avatar ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
273
331
|
"img",
|
|
274
332
|
{
|
|
275
333
|
src: author.avatar,
|
|
@@ -278,12 +336,37 @@ var SocialProofCard = react.forwardRef(
|
|
|
278
336
|
loading: "lazy",
|
|
279
337
|
decoding: "async"
|
|
280
338
|
}
|
|
281
|
-
),
|
|
339
|
+
) : /* @__PURE__ */ jsxRuntime.jsx("span", { className: "ds-social-proof-card__avatar ds-social-proof-card__avatar--initials", "aria-hidden": "true", children: getInitials(author.name) }),
|
|
282
340
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "ds-social-proof-card__author-copy", children: [
|
|
283
341
|
/* @__PURE__ */ jsxRuntime.jsx("cite", { className: "ds-social-proof-card__name", children: author.name }),
|
|
284
|
-
(author.title || author.company) && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "ds-social-proof-card__role", children: [author.title, author.company].filter(Boolean).join(" \xB7 ") })
|
|
285
|
-
|
|
342
|
+
(author.title || author.company) && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "ds-social-proof-card__role", children: [author.title, author.company].filter(Boolean).join(" \xB7 ") }),
|
|
343
|
+
(author.location || reviewDate) && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "ds-social-proof-card__micrometa", children: [author.location, reviewDate].filter(Boolean).join(" \xB7 ") })
|
|
344
|
+
] }),
|
|
345
|
+
author.companyLogo && /* @__PURE__ */ jsxRuntime.jsx(
|
|
346
|
+
"img",
|
|
347
|
+
{
|
|
348
|
+
src: author.companyLogo,
|
|
349
|
+
alt: author.company ?? "",
|
|
350
|
+
className: "ds-social-proof-card__company-logo",
|
|
351
|
+
loading: "lazy",
|
|
352
|
+
decoding: "async"
|
|
353
|
+
}
|
|
354
|
+
)
|
|
286
355
|
] }),
|
|
356
|
+
reviewUrl && platform && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
357
|
+
chunkLJADZITX_cjs.OutboundLink,
|
|
358
|
+
{
|
|
359
|
+
href: reviewUrl,
|
|
360
|
+
className: "ds-social-proof-card__review-link",
|
|
361
|
+
context: "social-proof-card",
|
|
362
|
+
openInNewTab: true,
|
|
363
|
+
children: [
|
|
364
|
+
"Read on ",
|
|
365
|
+
platformLabels[platform],
|
|
366
|
+
" \u2192"
|
|
367
|
+
]
|
|
368
|
+
}
|
|
369
|
+
),
|
|
287
370
|
href && ctaText && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "ds-social-proof-card__cta", children: ctaText })
|
|
288
371
|
] })
|
|
289
372
|
]
|
|
@@ -502,4 +585,4 @@ exports.SocialProofLogos = SocialProofLogos;
|
|
|
502
585
|
exports.StarRating = StarRating;
|
|
503
586
|
exports.VideoLightbox = VideoLightbox;
|
|
504
587
|
//# sourceMappingURL=out.js.map
|
|
505
|
-
//# sourceMappingURL=chunk-
|
|
588
|
+
//# sourceMappingURL=chunk-SR4QRXRR.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/StarRating/StarRating.tsx","../src/components/PlatformBadge/PlatformBadge.tsx","../src/components/SocialProofCard/SocialProofCard.tsx","../src/components/SocialProofCarousel/SocialProofCarousel.tsx","../src/components/SocialProofFeatured/SocialProofFeatured.tsx","../src/components/SocialProofGrid/SocialProofGrid.tsx","../src/components/SocialProofLogos/SocialProofLogos.tsx","../src/components/VideoLightbox/VideoLightbox.tsx"],"names":["forwardRef","clsx","jsx","jsxs","useState"],"mappings":"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","sourcesContent":["import React, { forwardRef, useEffect, useRef, useState } from 'react';\nimport clsx from 'clsx';\nimport './StarRating.css';\n\n/* ============================================================================\n STAR RATING\n ============================================================================\n Animated star display with half-star support, configurable sizes, and\n optional viewport-triggered fill animation. Replaces text-character\n star displays across all social proof surfaces.\n\n Strategic objective: Instant visual trust signal\n ============================================================================ */\n\nexport interface StarRatingProps extends React.HTMLAttributes<HTMLDivElement> {\n /** Rating value (0-5, supports decimals for half-stars) */\n value: number;\n /** Maximum number of stars */\n max?: number;\n /** Star size variant */\n size?: 'sm' | 'md' | 'lg';\n /** Animate stars filling on viewport entry */\n animate?: boolean;\n /** Show numeric value next to stars */\n showValue?: boolean;\n /** Label shown after value (e.g., \"/ 5\") */\n valueLabel?: string;\n}\n\n/**\n * Star rating display with gold-fill, half-star support, and optional\n * entrance animation. Uses inline SVGs with clip-path for precise\n * partial fills.\n *\n * @example\n * ```tsx\n * <StarRating value={4.5} size=\"lg\" animate />\n * <StarRating value={5} size=\"sm\" showValue />\n * ```\n */\nexport const StarRating = forwardRef<HTMLDivElement, StarRatingProps>(\n (\n {\n value,\n max = 5,\n size = 'md',\n animate = false,\n showValue = false,\n valueLabel,\n className,\n ...props\n },\n ref,\n ) => {\n const [visible, setVisible] = useState(!animate);\n const containerRef = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n if (!animate) return;\n\n // Respect reduced motion\n if (typeof window !== 'undefined') {\n const mq = window.matchMedia('(prefers-reduced-motion: reduce)');\n if (mq.matches) {\n setVisible(true);\n return;\n }\n }\n\n const target = containerRef.current;\n if (!target) return;\n\n const observer = new IntersectionObserver(\n ([entry]) => {\n if (entry.isIntersecting) {\n setVisible(true);\n observer.disconnect();\n }\n },\n { threshold: 0.5 },\n );\n\n observer.observe(target);\n return () => observer.disconnect();\n }, [animate]);\n\n const clampedValue = Math.min(Math.max(value, 0), max);\n\n const stars = Array.from({ length: max }, (_, i) => {\n const starIndex = i + 1;\n const fillPercent =\n clampedValue >= starIndex\n ? 100\n : clampedValue > starIndex - 1\n ? (clampedValue - (starIndex - 1)) * 100\n : 0;\n\n return (\n <span\n key={i}\n className={clsx(\n 'ds-star-rating__star',\n visible && 'ds-star-rating__star--visible',\n )}\n style={{\n '--star-fill': `${fillPercent}%`,\n '--star-delay': animate ? `${i * 100}ms` : '0ms',\n } as React.CSSProperties}\n >\n {/* Background (empty) star */}\n <svg\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n className=\"ds-star-rating__svg\"\n aria-hidden=\"true\"\n >\n <path\n d=\"M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z\"\n className=\"ds-star-rating__empty\"\n />\n </svg>\n {/* Foreground (filled) star with clip */}\n <svg\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n className=\"ds-star-rating__svg ds-star-rating__svg--filled\"\n aria-hidden=\"true\"\n style={{\n clipPath: `inset(0 ${100 - fillPercent}% 0 0)`,\n }}\n >\n <path\n d=\"M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z\"\n className=\"ds-star-rating__filled\"\n />\n </svg>\n </span>\n );\n });\n\n return (\n <div\n ref={(node) => {\n (containerRef as React.MutableRefObject<HTMLDivElement | null>).current = node;\n if (typeof ref === 'function') ref(node);\n else if (ref) (ref as React.MutableRefObject<HTMLDivElement | null>).current = node;\n }}\n className={clsx('ds-star-rating', `ds-star-rating--${size}`, className)}\n role=\"img\"\n aria-label={`${clampedValue} out of ${max} stars`}\n {...props}\n >\n <span className=\"ds-star-rating__stars\">{stars}</span>\n {showValue && (\n <span className=\"ds-star-rating__value\">\n {clampedValue.toLocaleString(undefined, {\n minimumFractionDigits: clampedValue % 1 !== 0 ? 1 : 0,\n maximumFractionDigits: 1,\n })}\n {valueLabel && (\n <span className=\"ds-star-rating__value-label\">{valueLabel}</span>\n )}\n </span>\n )}\n </div>\n );\n },\n);\n\nStarRating.displayName = 'StarRating';\n","import React, { forwardRef } from 'react';\nimport clsx from 'clsx';\nimport { StarRating } from '../StarRating/StarRating';\nimport './PlatformBadge.css';\n\n/* ============================================================================\n PLATFORM BADGE\n ============================================================================\n Recognizable review platform icons (Trustpilot, G2, Google, Chrome Web\n Store) with optional rating display. Builds instant trust through visual\n brand recognition.\n\n Strategic objective: Third-party authority signal\n ============================================================================ */\n\nexport type Platform = 'trustpilot' | 'g2' | 'google' | 'chrome-web-store';\n\nexport interface PlatformBadgeProps extends React.HTMLAttributes<HTMLDivElement> {\n /** Review platform */\n platform: Platform;\n /** Display mode */\n variant?: 'icon-only' | 'icon-label' | 'full';\n /** Platform rating (used in 'full' variant) */\n rating?: number;\n /** Number of reviews (used in 'full' variant) */\n count?: number;\n /** Size variant */\n size?: 'sm' | 'md';\n}\n\n/* --------------------------------------------------------------------------\n Platform SVG Icons — inline for zero-latency rendering\n -------------------------------------------------------------------------- */\n\nconst TrustpilotIcon = ({ size }: { size: number }) => (\n <svg width={size} height={size} viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">\n <path d=\"M12 2l2.9 8.9H24l-7.35 5.34 2.81 8.64L12 19.56l-7.46 5.32 2.81-8.64L0 10.9h9.1L12 2z\" fill=\"#00B67A\" />\n </svg>\n);\n\nconst G2Icon = ({ size }: { size: number }) => (\n <svg width={size} height={size} viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">\n <path d=\"M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2z\" fill=\"#FF492C\" />\n <text x=\"50%\" y=\"54%\" dominantBaseline=\"middle\" textAnchor=\"middle\" fill=\"white\" fontWeight=\"bold\" fontSize=\"11\" fontFamily=\"Arial, sans-serif\">G2</text>\n </svg>\n);\n\nconst GoogleIcon = ({ size }: { size: number }) => (\n <svg width={size} height={size} viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">\n <path d=\"M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92a5.06 5.06 0 01-2.2 3.32v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.1z\" fill=\"#4285F4\"/>\n <path d=\"M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z\" fill=\"#34A853\"/>\n <path d=\"M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18A10.96 10.96 0 001 12c0 1.77.42 3.45 1.18 4.93l3.66-2.84z\" fill=\"#FBBC05\"/>\n <path d=\"M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z\" fill=\"#EA4335\"/>\n </svg>\n);\n\nconst ChromeWebStoreIcon = ({ size }: { size: number }) => (\n <svg width={size} height={size} viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">\n <circle cx=\"12\" cy=\"12\" r=\"10\" fill=\"#4285F4\" opacity=\"0.1\" stroke=\"#4285F4\" strokeWidth=\"1.5\"/>\n <path d=\"M12 7c-2.76 0-5 2.24-5 5s2.24 5 5 5 5-2.24 5-5-2.24-5-5-5zm0 8.5c-1.93 0-3.5-1.57-3.5-3.5S10.07 8.5 12 8.5s3.5 1.57 3.5 3.5-1.57 3.5-3.5 3.5z\" fill=\"#4285F4\"/>\n <path d=\"M7.5 10.5L4 5.5\" stroke=\"#EA4335\" strokeWidth=\"1.5\" strokeLinecap=\"round\"/>\n <path d=\"M16.5 10.5L20 5.5\" stroke=\"#FBBC05\" strokeWidth=\"1.5\" strokeLinecap=\"round\"/>\n <path d=\"M12 17l-3.5 5\" stroke=\"#34A853\" strokeWidth=\"1.5\" strokeLinecap=\"round\"/>\n </svg>\n);\n\nconst PLATFORM_ICONS: Record<Platform, React.FC<{ size: number }>> = {\n trustpilot: TrustpilotIcon,\n g2: G2Icon,\n google: GoogleIcon,\n 'chrome-web-store': ChromeWebStoreIcon,\n};\n\nconst PLATFORM_LABELS: Record<Platform, string> = {\n trustpilot: 'Trustpilot',\n g2: 'G2',\n google: 'Google',\n 'chrome-web-store': 'Chrome Web Store',\n};\n\nconst PLATFORM_COLORS: Record<Platform, string> = {\n trustpilot: '#00B67A',\n g2: '#FF492C',\n google: '#4285F4',\n 'chrome-web-store': '#4285F4',\n};\n\n/**\n * Review platform badge with recognizable brand icon, optional label,\n * and optional rating display.\n *\n * @example\n * ```tsx\n * <PlatformBadge platform=\"trustpilot\" variant=\"icon-only\" />\n * <PlatformBadge platform=\"g2\" variant=\"icon-label\" />\n * <PlatformBadge platform=\"google\" variant=\"full\" rating={4.9} count={42} />\n * ```\n */\nexport const PlatformBadge = forwardRef<HTMLDivElement, PlatformBadgeProps>(\n (\n {\n platform,\n variant = 'icon-label',\n rating,\n count,\n size = 'sm',\n className,\n ...props\n },\n ref,\n ) => {\n const Icon = PLATFORM_ICONS[platform];\n const label = PLATFORM_LABELS[platform];\n const accentColor = PLATFORM_COLORS[platform];\n const iconSize = size === 'sm' ? 18 : 22;\n\n return (\n <div\n ref={ref}\n role=\"img\"\n className={clsx(\n 'ds-platform-badge',\n `ds-platform-badge--${variant}`,\n `ds-platform-badge--${size}`,\n className,\n )}\n style={{ '--platform-accent': accentColor } as React.CSSProperties}\n aria-label={\n variant === 'full' && rating\n ? `${label}: ${rating} stars${count ? ` from ${count} reviews` : ''}`\n : label\n }\n {...props}\n >\n <span className=\"ds-platform-badge__icon\">\n <Icon size={iconSize} />\n </span>\n\n {variant !== 'icon-only' && (\n <span className=\"ds-platform-badge__label\">{label}</span>\n )}\n\n {variant === 'full' && rating != null && (\n <span className=\"ds-platform-badge__rating\">\n <StarRating value={rating} size=\"sm\" />\n {count != null && (\n <span className=\"ds-platform-badge__count\">({count})</span>\n )}\n </span>\n )}\n </div>\n );\n },\n);\n\nPlatformBadge.displayName = 'PlatformBadge';\n","import React, { forwardRef } from 'react';\nimport clsx from 'clsx';\nimport { PlatformBadge, type Platform } from '../PlatformBadge/PlatformBadge';\nimport { StarRating } from '../StarRating/StarRating';\nimport { OutboundLink } from '../OutboundLink/OutboundLink';\nimport './SocialProofCard.css';\nimport './SocialProofCard-v2.css';\n\nexport interface SocialProofAuthor {\n name: string;\n title?: string;\n company?: string;\n avatar?: string;\n /** Secondary brand mark shown after the author name. */\n companyLogo?: string;\n /** Location shown as a micro-label, for example country or city. */\n location?: string;\n}\n\nexport interface SocialProofMetric {\n value: React.ReactNode;\n label: React.ReactNode;\n}\n\nexport interface SocialProofMedia {\n src: string;\n type: 'image';\n alt?: string;\n}\n\nexport interface SocialProofCardProps extends Omit<React.HTMLAttributes<HTMLElement>, 'content'> {\n variant?: 'social' | 'compact' | 'standard' | 'detailed' | 'narrative';\n content: React.ReactNode;\n author: SocialProofAuthor;\n starRating?: number;\n platform?: Platform;\n badges?: React.ReactNode[];\n href?: string;\n media?: SocialProofMedia;\n metrics?: SocialProofMetric[];\n headline?: React.ReactNode;\n industry?: React.ReactNode;\n ctaText?: React.ReactNode;\n /** Renders a verified-review pill when true. */\n verified?: boolean;\n /** Outbound link to the original review. Requires platform to render. */\n reviewUrl?: string;\n /** Review date label, for example \"Jan 2026\". */\n reviewDate?: string;\n /** Provenance line sourced from real workspace data. */\n workspaceProof?: string;\n}\n\nfunction getInitials(name: string): string {\n return name\n .split(/\\s+/)\n .map((word) => word[0])\n .filter(Boolean)\n .slice(0, 2)\n .join('')\n .toUpperCase();\n}\n\nconst platformLabels: Record<Platform, string> = {\n trustpilot: 'Trustpilot',\n g2: 'G2',\n google: 'Google',\n 'chrome-web-store': 'Chrome Web Store',\n};\n\nexport const SocialProofCard = forwardRef<HTMLElement, SocialProofCardProps>(\n (\n {\n variant = 'standard',\n content,\n author,\n starRating,\n platform,\n badges,\n href,\n media,\n metrics,\n headline,\n industry,\n ctaText,\n className,\n verified,\n reviewUrl,\n reviewDate,\n workspaceProof,\n ...props\n },\n ref,\n ) => {\n const hasTrustHeader = platform || verified || starRating !== undefined || industry;\n\n return (\n <article\n ref={ref}\n className={clsx(\n 'ds-card',\n variant === 'compact' ? 'ds-card--tier-1' : 'ds-card--tier-2',\n variant === 'compact' ? 'glass--tier-1' : 'glass--tier-2',\n 'ds-social-proof-card',\n `ds-social-proof-card--${variant}`,\n href && 'ds-social-proof-card--linked',\n verified && 'ds-social-proof-card--verified',\n className,\n )}\n {...props}\n >\n {media?.src && (\n <div className=\"ds-social-proof-card__media\">\n <img\n src={media.src}\n alt={media.alt ?? ''}\n className=\"ds-social-proof-card__image\"\n loading=\"lazy\"\n decoding=\"async\"\n />\n </div>\n )}\n\n <div className=\"ds-social-proof-card__body\">\n {hasTrustHeader && (\n <div className=\"ds-social-proof-card__trust-header\">\n <div className=\"ds-social-proof-card__trust-platforms\">\n {platform && <PlatformBadge platform={platform} />}\n {verified && (\n <span className=\"ds-social-proof-card__verified\" aria-label=\"Verified review\">\n <svg\n className=\"ds-social-proof-card__verified-icon\"\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n aria-hidden=\"true\"\n width=\"14\"\n height=\"14\"\n >\n <path\n d=\"M8 1L10.05 5.16L14.63 5.84L11.32 9.08L12.1 13.67L8 11.5L3.9 13.67L4.68 9.08L1.37 5.84L5.95 5.16L8 1Z\"\n fill=\"currentColor\"\n />\n </svg>\n Verified\n </span>\n )}\n </div>\n <div className=\"ds-social-proof-card__trust-stars\">\n {starRating !== undefined && <StarRating value={starRating} size=\"sm\" />}\n {industry && <span className=\"ds-social-proof-card__industry\">{industry}</span>}\n </div>\n </div>\n )}\n\n {headline && <h3 className=\"ds-social-proof-card__headline\">{headline}</h3>}\n\n <blockquote className=\"ds-social-proof-card__quote\">\n <p>{content}</p>\n </blockquote>\n\n {workspaceProof && (\n <div className=\"ds-social-proof-card__workspace-proof\" aria-label=\"Verified workspace proof\">\n <svg\n className=\"ds-social-proof-card__workspace-icon\"\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n aria-hidden=\"true\"\n width=\"12\"\n height=\"12\"\n >\n <rect x=\"1\" y=\"2\" width=\"14\" height=\"11\" rx=\"2\" stroke=\"currentColor\" strokeWidth=\"1.5\" />\n <path d=\"M5 8H11M8 5V11\" stroke=\"currentColor\" strokeWidth=\"1.5\" strokeLinecap=\"round\" />\n </svg>\n <span>{workspaceProof}</span>\n </div>\n )}\n\n {metrics && metrics.length > 0 && (\n <div className=\"ds-social-proof-card__metrics\">\n {metrics.map((metric, index) => (\n <div className=\"ds-social-proof-card__metric\" key={index}>\n <span className=\"ds-social-proof-card__metric-value\">{metric.value}</span>\n <span className=\"ds-social-proof-card__metric-label\">{metric.label}</span>\n </div>\n ))}\n </div>\n )}\n\n {badges && badges.length > 0 && (\n <div className=\"ds-social-proof-card__badges\">\n {badges.map((badge, index) => (\n <span className=\"ds-social-proof-card__badge\" key={index}>{badge}</span>\n ))}\n </div>\n )}\n\n <footer className=\"ds-social-proof-card__author\">\n {author.avatar ? (\n <img\n src={author.avatar}\n alt=\"\"\n className=\"ds-social-proof-card__avatar\"\n loading=\"lazy\"\n decoding=\"async\"\n />\n ) : (\n <span className=\"ds-social-proof-card__avatar ds-social-proof-card__avatar--initials\" aria-hidden=\"true\">\n {getInitials(author.name)}\n </span>\n )}\n <div className=\"ds-social-proof-card__author-copy\">\n <cite className=\"ds-social-proof-card__name\">{author.name}</cite>\n {(author.title || author.company) && (\n <span className=\"ds-social-proof-card__role\">\n {[author.title, author.company].filter(Boolean).join(' · ')}\n </span>\n )}\n {(author.location || reviewDate) && (\n <span className=\"ds-social-proof-card__micrometa\">\n {[author.location, reviewDate].filter(Boolean).join(' · ')}\n </span>\n )}\n </div>\n {author.companyLogo && (\n <img\n src={author.companyLogo}\n alt={author.company ?? ''}\n className=\"ds-social-proof-card__company-logo\"\n loading=\"lazy\"\n decoding=\"async\"\n />\n )}\n </footer>\n\n {reviewUrl && platform && (\n <OutboundLink\n href={reviewUrl}\n className=\"ds-social-proof-card__review-link\"\n context=\"social-proof-card\"\n openInNewTab\n >\n Read on {platformLabels[platform]} →\n </OutboundLink>\n )}\n\n {href && ctaText && <span className=\"ds-social-proof-card__cta\">{ctaText}</span>}\n </div>\n </article>\n );\n },\n);\n\nSocialProofCard.displayName = 'SocialProofCard';\n","import React, { forwardRef } from 'react';\nimport clsx from 'clsx';\nimport './SocialProofCarousel.css';\n\nexport interface SocialProofCarouselProps extends React.HTMLAttributes<HTMLDivElement> {\n background?: 'default' | 'muted' | 'dark' | 'brand' | 'transparent';\n}\n\nexport const SocialProofCarousel = forwardRef<HTMLDivElement, SocialProofCarouselProps>(\n ({ background = 'transparent', className, children, ...props }, ref) => (\n <div\n ref={ref}\n className={clsx(\n 'ds-social-proof-carousel',\n `ds-social-proof-carousel--background-${background}`,\n className,\n )}\n {...props}\n >\n <div className=\"ds-social-proof-carousel__track\">{children}</div>\n </div>\n ),\n);\n\nSocialProofCarousel.displayName = 'SocialProofCarousel';\n","import React, { forwardRef } from 'react';\nimport clsx from 'clsx';\nimport { Card } from '../Card/Card';\nimport { PlatformBadge, type Platform } from '../PlatformBadge/PlatformBadge';\nimport { StarRating } from '../StarRating/StarRating';\nimport type { SocialProofAuthor } from '../SocialProofCard/SocialProofCard';\nimport './SocialProofFeatured.css';\n\nexport interface SocialProofFeaturedProps extends React.HTMLAttributes<HTMLDivElement> {\n quote: React.ReactNode;\n author: SocialProofAuthor;\n featuredVariant?: 'standard' | 'cinematic' | 'spotlight';\n starRating?: number;\n platform?: Platform;\n mediaSrc?: string;\n background?: 'default' | 'muted' | 'brand' | 'transparent';\n padding?: 'sm' | 'md' | 'lg';\n}\n\nexport const SocialProofFeatured = forwardRef<HTMLDivElement, SocialProofFeaturedProps>(\n (\n {\n quote,\n author,\n featuredVariant = 'standard',\n starRating,\n platform,\n mediaSrc,\n background = 'transparent',\n padding = 'md',\n className,\n ...props\n },\n ref,\n ) => (\n <Card\n ref={ref}\n tier={featuredVariant === 'spotlight' ? 3 : 2}\n radius=\"panel\"\n aurora={featuredVariant === 'spotlight'}\n className={clsx(\n 'ds-social-proof-featured',\n `ds-social-proof-featured--${featuredVariant}`,\n `ds-social-proof-featured--background-${background}`,\n `ds-social-proof-featured--padding-${padding}`,\n className,\n )}\n {...props}\n >\n {mediaSrc && (\n <div className=\"ds-social-proof-featured__media\">\n <img src={mediaSrc} alt=\"\" className=\"ds-social-proof-featured__image\" loading=\"lazy\" decoding=\"async\" />\n </div>\n )}\n\n <div className=\"ds-social-proof-featured__content\">\n <div className=\"ds-social-proof-featured__meta\">\n {starRating !== undefined && <StarRating value={starRating} size=\"md\" />}\n {platform && <PlatformBadge platform={platform} />}\n </div>\n\n <blockquote className=\"ds-social-proof-featured__quote\">\n <p>{quote}</p>\n </blockquote>\n\n <footer className=\"ds-social-proof-featured__author\">\n {author.avatar && (\n <img\n src={author.avatar}\n alt=\"\"\n className=\"ds-social-proof-featured__avatar\"\n loading=\"lazy\"\n decoding=\"async\"\n />\n )}\n <div className=\"ds-social-proof-featured__author-copy\">\n <cite className=\"ds-social-proof-featured__name\">{author.name}</cite>\n {(author.title || author.company) && (\n <span className=\"ds-social-proof-featured__role\">\n {[author.title, author.company].filter(Boolean).join(' · ')}\n </span>\n )}\n </div>\n </footer>\n </div>\n </Card>\n ),\n);\n\nSocialProofFeatured.displayName = 'SocialProofFeatured';\n","import React, { forwardRef } from 'react';\nimport clsx from 'clsx';\nimport './SocialProofGrid.css';\n\nexport interface SocialProofGridProps extends React.HTMLAttributes<HTMLDivElement> {\n columns?: 1 | 2 | 3 | 4;\n staggered?: boolean;\n background?: 'default' | 'muted' | 'dark' | 'brand' | 'transparent';\n}\n\nexport const SocialProofGrid = forwardRef<HTMLDivElement, SocialProofGridProps>(\n ({ columns = 3, staggered = false, background = 'transparent', className, children, ...props }, ref) => (\n <div\n ref={ref}\n className={clsx(\n 'ds-social-proof-grid',\n `ds-social-proof-grid--columns-${columns}`,\n `ds-social-proof-grid--background-${background}`,\n staggered && 'ds-social-proof-grid--staggered',\n className,\n )}\n {...props}\n >\n {children}\n </div>\n ),\n);\n\nSocialProofGrid.displayName = 'SocialProofGrid';\n","import React, { forwardRef } from 'react';\nimport clsx from 'clsx';\nimport { OutboundLink } from '../OutboundLink/OutboundLink';\nimport './SocialProofLogos.css';\n\nexport interface SocialProofLogoItem {\n id: string;\n alt: string;\n src?: string;\n logo?: React.ReactNode;\n href?: string;\n badge?: React.ReactNode;\n}\n\nexport interface SocialProofLogosProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'title'> {\n logos: SocialProofLogoItem[];\n title?: React.ReactNode;\n variant?: 'grid' | 'marquee';\n speed?: 'slow' | 'normal' | 'fast';\n background?: 'default' | 'muted' | 'dark' | 'brand' | 'transparent';\n}\n\nexport const SocialProofLogos = forwardRef<HTMLDivElement, SocialProofLogosProps>(\n ({ logos, title, variant = 'grid', speed = 'normal', background = 'transparent', className, ...props }, ref) => {\n const renderedLogos = logos.map((item) => {\n const content = (\n <span className=\"ds-social-proof-logos__item-content\">\n {item.src ? <img src={item.src} alt={item.alt} className=\"ds-social-proof-logos__image\" loading=\"lazy\" decoding=\"async\" /> : item.logo}\n {item.badge && <span className=\"ds-social-proof-logos__badge\">{item.badge}</span>}\n </span>\n );\n\n return item.href ? (\n <OutboundLink key={item.id} href={item.href} context=\"social-proof-logo\" className=\"ds-social-proof-logos__item\">\n {content}\n </OutboundLink>\n ) : (\n <span key={item.id} className=\"ds-social-proof-logos__item\">\n {content}\n </span>\n );\n });\n\n return (\n <div\n ref={ref}\n className={clsx(\n 'ds-social-proof-logos',\n `ds-social-proof-logos--${variant}`,\n `ds-social-proof-logos--speed-${speed}`,\n `ds-social-proof-logos--background-${background}`,\n className,\n )}\n {...props}\n >\n {title && <p className=\"ds-social-proof-logos__title\">{title}</p>}\n <div className=\"ds-social-proof-logos__viewport\">\n <div className=\"ds-social-proof-logos__track\">\n {renderedLogos}\n {variant === 'marquee' && renderedLogos}\n </div>\n </div>\n </div>\n );\n },\n);\n\nSocialProofLogos.displayName = 'SocialProofLogos';\n","import React, { forwardRef, useState, useCallback } from 'react';\nimport { Dialog as BaseDialog } from '@base-ui/react/dialog';\nimport clsx from 'clsx';\nimport './VideoLightbox.css';\n\n/* ============================================================================\n VIDEO LIGHTBOX\n ============================================================================\n Modal overlay video player built on @base-ui/react Dialog. Opens a lazy\n iframe embed (YouTube/Vimeo/Loom) with backdrop blur, keyboard navigation,\n and focus trap. Designed for inline playback from thumbnail cards without\n navigating away from the current page.\n\n Strategic objective: Engagement — keep users on the Wall of Love\n ============================================================================ */\n\nexport interface VideoLightboxProps {\n /** Video embed URL (YouTube, Vimeo, Loom) */\n videoUrl: string;\n /** Poster image shown as thumbnail trigger */\n thumbnailSrc?: string;\n /** Alt text for thumbnail */\n thumbnailAlt?: string;\n /** Video title for accessibility */\n title?: string;\n /** Custom trigger element (overrides default thumbnail + play button) */\n trigger?: React.ReactNode;\n /** Additional class name for the trigger wrapper */\n className?: string;\n /** Callback when video starts playing */\n onPlay?: () => void;\n}\n\n/* --------------------------------------------------------------------------\n Helpers\n -------------------------------------------------------------------------- */\n\nfunction getEmbedUrl(src: string): string {\n // YouTube\n const ytMatch = src.match(\n /(?:youtube\\.com\\/watch\\?v=|youtu\\.be\\/|youtube\\.com\\/embed\\/)([a-zA-Z0-9_-]+)/,\n );\n if (ytMatch) return `https://www.youtube-nocookie.com/embed/${ytMatch[1]}?autoplay=1&rel=0`;\n\n // Vimeo\n const vimeoMatch = src.match(/(?:vimeo\\.com\\/)(\\d+)/);\n if (vimeoMatch) return `https://player.vimeo.com/video/${vimeoMatch[1]}?autoplay=1`;\n\n // Loom\n const loomMatch = src.match(/(?:loom\\.com\\/share\\/)([a-zA-Z0-9]+)/);\n if (loomMatch) return `https://www.loom.com/embed/${loomMatch[1]}?autoplay=1`;\n\n return src;\n}\n\nconst PlayIcon = () => (\n <svg width=\"64\" height=\"64\" viewBox=\"0 0 64 64\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">\n <circle cx=\"32\" cy=\"32\" r=\"32\" className=\"ds-video-lightbox__play-bg\" />\n <path d=\"M26 20v24l20-12L26 20z\" fill=\"white\" />\n </svg>\n);\n\nconst CloseIcon = () => (\n <svg width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\">\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\" />\n <line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\" />\n </svg>\n);\n\n/**\n * Modal video player triggered by a thumbnail or custom element.\n * Opens a full-screen overlay with lazy iframe injection.\n *\n * @example\n * ```tsx\n * <VideoLightbox\n * videoUrl=\"https://www.youtube.com/watch?v=dQw4w9WgXcQ\"\n * thumbnailSrc=\"/thumbnails/demo.jpg\"\n * title=\"Customer testimonial\"\n * />\n *\n * <VideoLightbox\n * videoUrl=\"https://vimeo.com/123456789\"\n * trigger={<Button>Watch Video</Button>}\n * />\n * ```\n */\nexport const VideoLightbox = forwardRef<HTMLDivElement, VideoLightboxProps>(\n (\n {\n videoUrl,\n thumbnailSrc,\n thumbnailAlt,\n title = 'Video',\n trigger,\n className,\n onPlay,\n },\n ref,\n ) => {\n const [open, setOpen] = useState(false);\n\n const handleOpen = useCallback(() => {\n setOpen(true);\n onPlay?.();\n }, [onPlay]);\n\n return (\n <div ref={ref} className={clsx('ds-video-lightbox', className)}>\n <BaseDialog.Root open={open} onOpenChange={setOpen}>\n <BaseDialog.Trigger\n className=\"ds-video-lightbox__trigger\"\n onClick={handleOpen}\n >\n {trigger || (\n <div className=\"ds-video-lightbox__thumbnail-wrap\">\n {thumbnailSrc ? (\n <img\n src={thumbnailSrc}\n alt={thumbnailAlt || `Play: ${title}`}\n className=\"ds-video-lightbox__thumbnail\"\n loading=\"lazy\"\n decoding=\"async\"\n />\n ) : (\n <div className=\"ds-video-lightbox__placeholder\" />\n )}\n <div className=\"ds-video-lightbox__overlay\">\n <div className=\"ds-video-lightbox__play-btn\">\n <PlayIcon />\n </div>\n </div>\n </div>\n )}\n </BaseDialog.Trigger>\n\n <BaseDialog.Portal>\n <BaseDialog.Backdrop className=\"ds-video-lightbox__backdrop\" />\n <BaseDialog.Popup className=\"ds-video-lightbox__popup\">\n <BaseDialog.Close className=\"ds-video-lightbox__close\" aria-label=\"Close video\">\n <CloseIcon />\n </BaseDialog.Close>\n <BaseDialog.Title className=\"ds-sr-only\">{title}</BaseDialog.Title>\n <div className=\"ds-video-lightbox__iframe-wrap\">\n {open && (\n <iframe\n src={getEmbedUrl(videoUrl)}\n title={title}\n className=\"ds-video-lightbox__iframe\"\n allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture\"\n allowFullScreen\n />\n )}\n </div>\n </BaseDialog.Popup>\n </BaseDialog.Portal>\n </BaseDialog.Root>\n </div>\n );\n },\n);\n\nVideoLightbox.displayName = 'VideoLightbox';\n"]}
|
package/dist/index.cjs
CHANGED
|
@@ -9,7 +9,7 @@ var chunkCVLD5RQK_cjs = require('./chunk-CVLD5RQK.cjs');
|
|
|
9
9
|
var chunk3NS6X2R4_cjs = require('./chunk-3NS6X2R4.cjs');
|
|
10
10
|
var chunkNT4LBP7D_cjs = require('./chunk-NT4LBP7D.cjs');
|
|
11
11
|
var chunkFXYOSA4E_cjs = require('./chunk-FXYOSA4E.cjs');
|
|
12
|
-
var
|
|
12
|
+
var chunkSR4QRXRR_cjs = require('./chunk-SR4QRXRR.cjs');
|
|
13
13
|
var chunkUCWMJD4Y_cjs = require('./chunk-UCWMJD4Y.cjs');
|
|
14
14
|
var chunk6WQKRQEE_cjs = require('./chunk-6WQKRQEE.cjs');
|
|
15
15
|
var chunk7EUR3AKV_cjs = require('./chunk-7EUR3AKV.cjs');
|
|
@@ -727,35 +727,35 @@ Object.defineProperty(exports, "SpotlightCard", {
|
|
|
727
727
|
});
|
|
728
728
|
Object.defineProperty(exports, "PlatformBadge", {
|
|
729
729
|
enumerable: true,
|
|
730
|
-
get: function () { return
|
|
730
|
+
get: function () { return chunkSR4QRXRR_cjs.PlatformBadge; }
|
|
731
731
|
});
|
|
732
732
|
Object.defineProperty(exports, "SocialProofCard", {
|
|
733
733
|
enumerable: true,
|
|
734
|
-
get: function () { return
|
|
734
|
+
get: function () { return chunkSR4QRXRR_cjs.SocialProofCard; }
|
|
735
735
|
});
|
|
736
736
|
Object.defineProperty(exports, "SocialProofCarousel", {
|
|
737
737
|
enumerable: true,
|
|
738
|
-
get: function () { return
|
|
738
|
+
get: function () { return chunkSR4QRXRR_cjs.SocialProofCarousel; }
|
|
739
739
|
});
|
|
740
740
|
Object.defineProperty(exports, "SocialProofFeatured", {
|
|
741
741
|
enumerable: true,
|
|
742
|
-
get: function () { return
|
|
742
|
+
get: function () { return chunkSR4QRXRR_cjs.SocialProofFeatured; }
|
|
743
743
|
});
|
|
744
744
|
Object.defineProperty(exports, "SocialProofGrid", {
|
|
745
745
|
enumerable: true,
|
|
746
|
-
get: function () { return
|
|
746
|
+
get: function () { return chunkSR4QRXRR_cjs.SocialProofGrid; }
|
|
747
747
|
});
|
|
748
748
|
Object.defineProperty(exports, "SocialProofLogos", {
|
|
749
749
|
enumerable: true,
|
|
750
|
-
get: function () { return
|
|
750
|
+
get: function () { return chunkSR4QRXRR_cjs.SocialProofLogos; }
|
|
751
751
|
});
|
|
752
752
|
Object.defineProperty(exports, "StarRating", {
|
|
753
753
|
enumerable: true,
|
|
754
|
-
get: function () { return
|
|
754
|
+
get: function () { return chunkSR4QRXRR_cjs.StarRating; }
|
|
755
755
|
});
|
|
756
756
|
Object.defineProperty(exports, "VideoLightbox", {
|
|
757
757
|
enumerable: true,
|
|
758
|
-
get: function () { return
|
|
758
|
+
get: function () { return chunkSR4QRXRR_cjs.VideoLightbox; }
|
|
759
759
|
});
|
|
760
760
|
Object.defineProperty(exports, "ArchitectureDiagram", {
|
|
761
761
|
enumerable: true,
|