Implement use cases page

This commit is contained in:
adilallo
2026-05-17 21:41:54 -06:00
parent b6b9b63608
commit 450da4d8ab
78 changed files with 1870 additions and 118 deletions
@@ -5,7 +5,7 @@ import { logger } from "../../../../lib/logger";
import QuoteBlockView from "./QuoteBlock.view";
import type { QuoteBlockProps, VariantConfig } from "./QuoteBlock.types";
/** Figma: portrait variants standard | compact | extended; statement = Section/Quote (22137:890679, copy scale 22135:889716 from md). */
/** Figma: portrait variants standard | compact | extended; **`statement`** = Section/Quote (22137890679; **`lg`** single paragraph **2196724638** — About + use cases). */
const QuoteBlockContainer = memo<QuoteBlockProps>(
({
variant: variantProp = "standard",
@@ -19,7 +19,6 @@ const QuoteBlockContainer = memo<QuoteBlockProps>(
fallbackAvatarSrc = "/assets/Quote_Avatar.svg",
onError,
}) => {
const variant = variantProp;
const [imageError, setImageError] = useState(false);
const [imageLoading, setImageLoading] = useState(true);
@@ -86,12 +85,12 @@ const QuoteBlockContainer = memo<QuoteBlockProps>(
},
};
const config = variants[variant] || variants.standard;
const config = variants[variantProp] || variants.standard;
// Use provided ID or generate a stable one based on content
const baseId =
id ||
(variant === "statement"
(variantProp === "statement"
? "statement-quote"
: `quote-${author.toLowerCase().replace(/\s+/g, "-")}`);
const quoteId = `${baseId}-content`;
@@ -124,7 +123,7 @@ const QuoteBlockContainer = memo<QuoteBlockProps>(
};
// Validate required props
if (variant === "statement") {
if (variantProp === "statement") {
if (!quote?.trim() || !quoteSecondary?.trim()) {
logger.error(
"QuoteBlock: statement variant requires non-empty quote and quoteSecondary",
@@ -134,7 +133,7 @@ const QuoteBlockContainer = memo<QuoteBlockProps>(
type: "missing_props",
message:
"QuoteBlock statement variant requires quote and quoteSecondary",
quote: !!quote?.trim() && !!quoteSecondary?.trim(),
quote: !!(quote?.trim() && quoteSecondary?.trim()),
});
}
return null;
@@ -5,13 +5,11 @@ export type QuoteBlockVariantValue =
| "statement";
export interface QuoteBlockProps {
/** Default `standard` (home portrait quote). `statement` is About-only dual-paragraph layout; isolated branch in QuoteBlock.view. */
/** Default `standard` (home portrait quote). **`statement`** = yellow Section / Quote (**About** + **`/use-cases`** — two paragraphs below **`lg`**, one paragraph from **`lg`**; [21967-24638](https://www.figma.com/design/agv0VBLiBlcnSAaiAORgPR/Community-Rule-System?node-id=21967-24638&m=dev)). */
variant?: QuoteBlockVariantValue;
className?: string;
quote?: string;
/**
* Second paragraph for **`statement`** variant (Figma Section/Quote 22137:890679).
*/
/** Second paragraph for **`statement`** (Section/Quote); merged into one `<p>` from **`lg`**. */
quoteSecondary?: string;
author?: string;
source?: string;
@@ -39,7 +37,7 @@ export interface VariantConfig {
source: string;
showDecor: boolean;
/**
* When true, render Figma **Section/Quote** layout (yellow surface, dual paragraphs, no attribution).
* When true, render Figma **Section/Quote** layout (yellow surface; stacked copy below **`lg`**, single paragraph from **`lg`**; no attribution).
*/
statementLayout?: boolean;
}
@@ -26,15 +26,12 @@ function QuoteBlockView({
const avatarAlt = t("avatarAlt").replace("{author}", author);
if (config.statementLayout) {
if (!quoteSecondary?.trim()) {
return null;
}
const statementTextClass =
"font-bricolage-grotesque text-[28px] font-bold leading-9 tracking-[var(--text-xx-large-heading--letter-spacing)] text-[var(--color-surface-default-tertiary)] md:text-[length:var(--text-xx-large-heading)] md:leading-[length:var(--text-xx-large-heading--line-height)]";
return (
<section
data-figma-node="21967-24638"
className={`${config.container} ${className}`.trim()}
aria-labelledby={quoteId}
role="region"
@@ -43,12 +40,18 @@ function QuoteBlockView({
className="relative box-border flex w-full max-w-[1440px] shrink-0 flex-col items-center justify-center gap-[var(--space-800)] overflow-hidden rounded-[var(--spacing-scale-020)] bg-[var(--color-surface-invert-brand-primary,#fefcc9)] px-[var(--spacing-scale-032)] py-[var(--spacing-scale-048)] md:px-[var(--space-1800)] md:py-[var(--space-2400)]"
>
<QuoteStatementDecor />
<div className="relative z-10 flex w-full min-w-0 shrink-0 flex-col items-center gap-9 text-center md:gap-[length:var(--text-xx-large-heading--line-height)]">
<p id={quoteId} className={`${statementTextClass} mb-0 w-full min-w-0`}>
{quote}
</p>
<p className={`${statementTextClass} mb-0 w-full min-w-0`}>
{quoteSecondary}
<div className="relative z-10 flex w-full min-w-0 shrink-0 flex-col items-center text-center">
<p
id={quoteId}
className={`${statementTextClass} mb-0 flex w-full min-w-0 flex-col gap-9 text-center md:gap-[length:var(--text-xx-large-heading--line-height)] lg:block lg:gap-0`}
>
<span className="block lg:inline">{quote}</span>
{quoteSecondary ? (
<>
<span className="hidden lg:inline">{" "}</span>
<span className="block lg:inline">{quoteSecondary}</span>
</>
) : null}
</p>
</div>
</div>
@@ -3,7 +3,7 @@
import { memo } from "react";
import { getAssetPath, quoteStatementShapePath } from "../../../../lib/assetUtils";
/** Figma: Section / Quote — Shapes (22137:890679). Radial asset + horizontal gradient mask (side lobes only); grain matches QuoteBlock/HeroDecor. Background `cover` so wide banners still fill lateral mask stripes (square sized by panel height misses them when centered). */
/** Figma: Section / Quote — **`shape-qoute.svg`** (22137:890679). */
const EDGE_MASK =
"linear-gradient(to right, #fff 0%, #fff 14%, rgba(255,255,255,0) 30%, rgba(255,255,255,0) 70%, #fff 86%, #fff 100%)";