Match the 3 Nov ramps and leftover launch QA #95

Merged
an.di merged 1 commits from adilallo/fix/CR-267-open-qa into main 2026-11-05 22:46:04 +00:00
32 changed files with 275 additions and 212 deletions

No files matched your search

+1
View File
@@ -5,6 +5,7 @@ import MonitorPageContent from "./MonitorPageContent";
export const metadata: Metadata = routeMetadata("/monitor", { export const metadata: Metadata = routeMetadata("/monitor", {
title: messages.metadata.monitor.title, title: messages.metadata.monitor.title,
description: messages.metadata.monitor.description,
}); });
export default function MonitorPage() { export default function MonitorPage() {
+1
View File
@@ -4,6 +4,7 @@ import { NO_INDEX_ROBOTS, routeMetadata } from "../../../lib/siteMetadata";
export const metadata: Metadata = routeMetadata("/login", { export const metadata: Metadata = routeMetadata("/login", {
title: messages.metadata.login.title, title: messages.metadata.login.title,
description: messages.metadata.login.description,
robots: NO_INDEX_ROBOTS, robots: NO_INDEX_ROBOTS,
}); });
+1
View File
@@ -5,6 +5,7 @@ import ProfilePageClient from "./ProfilePageClient";
export const metadata: Metadata = routeMetadata("/profile", { export const metadata: Metadata = routeMetadata("/profile", {
title: messages.metadata.profile.title, title: messages.metadata.profile.title,
description: messages.metadata.profile.description,
robots: NO_INDEX_ROBOTS, robots: NO_INDEX_ROBOTS,
}); });
+17 -9
View File
@@ -112,6 +112,14 @@ const Button = memo<ButtonProps>(
xlarge: "p-[var(--space-400)] gap-[var(--space-200)]", xlarge: "p-[var(--space-400)] gap-[var(--space-200)]",
}; };
const strokeStyles: Record<string, string> = {
xsmall: "border-[1px] active:border-[1.25px]",
small: "border-[1.25px] active:border-[1.5px]",
medium: "border-[1.5px] active:border-[1.75px]",
large: "border-[1.75px]",
xlarge: "border-[2px]",
};
const fontStyles: Record<string, string> = { const fontStyles: Record<string, string> = {
xsmall: "text-xx-small-label", xsmall: "text-xx-small-label",
small: "text-x-small-label", small: "text-x-small-label",
@@ -122,21 +130,21 @@ const Button = memo<ButtonProps>(
const variantStyles: Record<string, string> = { const variantStyles: Record<string, string> = {
filled: filled:
"bg-[var(--color-surface-invert-primary)] text-[var(--color-content-invert-primary)] border-[1.5px] border-transparent hover:bg-[var(--color-surface-invert-primary)] hover:text-[var(--color-content-invert-brand-primary)] hover:border-[var(--color-border-invert-brand-primary)] focus-visible:bg-[var(--color-surface-invert-primary)] focus-visible:text-[var(--color-content-invert-brand-primary)] focus-visible:outline-none focus-visible:border-transparent focus-visible:shadow-[var(--shadow-focus-default)] active:bg-[var(--color-surface-invert-brand-primary)] active:text-[var(--color-content-invert-primary)] active:border-[var(--color-border-invert-brand-primary)] active:shadow-none active:scale-[0.98] disabled:bg-[var(--color-surface-default-secondary)] disabled:text-[var(--color-content-invert-tertiary)] disabled:border-transparent disabled:cursor-not-allowed disabled:opacity-50 disabled:active:scale-100", "bg-[var(--color-surface-invert-primary)] text-[var(--color-content-invert-primary)] border-transparent hover:bg-[var(--color-surface-invert-primary)] hover:text-[var(--color-content-invert-brand-primary)] hover:border-[var(--color-border-invert-brand-primary)] focus-visible:bg-[var(--color-surface-invert-primary)] focus-visible:text-[var(--color-content-invert-brand-primary)] focus-visible:outline-none focus-visible:border-transparent focus-visible:shadow-[var(--shadow-focus-default)] active:bg-[var(--color-surface-invert-brand-primary)] active:text-[var(--color-content-invert-primary)] active:border-[var(--color-border-invert-brand-primary)] active:shadow-none active:scale-[0.98] disabled:bg-[var(--color-surface-default-secondary)] disabled:text-[var(--color-content-invert-tertiary)] disabled:border-transparent disabled:cursor-not-allowed disabled:opacity-50 disabled:active:scale-100",
"filled-inverse": "filled-inverse":
"bg-[var(--color-surface-default-primary)] text-[var(--color-content-default-primary)] border-[1.5px] border-transparent hover:bg-[var(--color-surface-default-primary)] hover:text-[var(--color-content-default-brand-primary)] hover:border-[var(--color-border-default-brand-primary)] focus-visible:bg-[var(--color-surface-default-primary)] focus-visible:text-[var(--color-content-default-brand-primary)] focus-visible:outline-none focus-visible:border-transparent focus-visible:shadow-[var(--shadow-focus-invert)] active:bg-[var(--color-surface-default-brand-primary)] active:text-[var(--color-content-default-primary)] active:border-[var(--color-border-default-brand-primary)] active:shadow-none active:scale-[0.98] disabled:bg-[var(--color-surface-invert-secondary)] disabled:text-[var(--color-content-default-tertiary)] disabled:border-transparent disabled:cursor-not-allowed disabled:opacity-50 disabled:active:scale-100", "bg-[var(--color-surface-default-primary)] text-[var(--color-content-default-primary)] border-transparent hover:bg-[var(--color-surface-default-primary)] hover:text-[var(--color-content-default-brand-primary)] hover:border-[var(--color-border-default-brand-primary)] focus-visible:bg-[var(--color-surface-default-primary)] focus-visible:text-[var(--color-content-default-brand-primary)] focus-visible:outline-none focus-visible:border-transparent focus-visible:shadow-[var(--shadow-focus-invert)] active:bg-[var(--color-surface-default-brand-primary)] active:text-[var(--color-content-default-primary)] active:border-[var(--color-border-default-brand-primary)] active:shadow-none active:scale-[0.98] disabled:bg-[var(--color-surface-invert-secondary)] disabled:text-[var(--color-content-default-tertiary)] disabled:border-transparent disabled:cursor-not-allowed disabled:opacity-50 disabled:active:scale-100",
outline: outline:
"bg-transparent text-[var(--color-content-default-primary)] border-[1.5px] border-[var(--color-border-invert-primary)] hover:bg-transparent hover:text-[var(--color-content-default-brand-primary)] hover:border-[1.5px] hover:border-[var(--color-border-default-brand-primary)] focus-visible:bg-transparent focus-visible:text-[var(--color-content-default-primary)] focus-visible:outline-none focus-visible:border-[1.5px] focus-visible:border-[var(--color-border-invert-primary)] focus-visible:shadow-[var(--shadow-focus-default)] active:bg-transparent active:text-[var(--color-content-default-primary)] active:border-[1.5px] active:border-[var(--color-border-default-brand-primary)] active:shadow-none active:scale-[0.98] disabled:bg-[var(--color-surface-default-secondary)] disabled:text-[var(--color-content-invert-tertiary)] disabled:border-[1.5px] disabled:border-transparent disabled:cursor-not-allowed disabled:opacity-50 disabled:active:scale-100", "bg-transparent text-[var(--color-content-default-primary)] border-[var(--color-border-invert-primary)] hover:bg-transparent hover:text-[var(--color-content-default-brand-primary)] hover:border-[var(--color-border-default-brand-primary)] focus-visible:bg-transparent focus-visible:text-[var(--color-content-default-primary)] focus-visible:outline-none focus-visible:border-[var(--color-border-invert-primary)] focus-visible:shadow-[var(--shadow-focus-default)] active:bg-transparent active:text-[var(--color-content-default-primary)] active:border-[var(--color-border-default-brand-primary)] active:shadow-none active:scale-[0.98] disabled:bg-[var(--color-surface-default-secondary)] disabled:text-[var(--color-content-invert-tertiary)] disabled:border-transparent disabled:cursor-not-allowed disabled:opacity-50 disabled:active:scale-100",
"outline-inverse": "outline-inverse":
"bg-transparent text-[var(--color-content-invert-primary)] border-[1.5px] border-[var(--color-border-default-primary)] hover:bg-transparent hover:text-[var(--color-content-invert-brand-primary)] hover:border-[1.5px] hover:border-[var(--color-border-invert-brand-primary)] focus-visible:bg-transparent focus-visible:text-[var(--color-content-invert-primary)] focus-visible:outline-none focus-visible:border-[1.5px] focus-visible:border-[var(--color-border-default-primary)] focus-visible:shadow-[var(--shadow-focus-invert)] active:bg-transparent active:text-[var(--color-content-invert-primary)] active:border-[1.5px] active:border-[var(--color-border-invert-brand-primary)] active:shadow-none active:scale-[0.98] disabled:bg-[var(--color-surface-invert-secondary)] disabled:text-[var(--color-content-default-tertiary)] disabled:border-[1.5px] disabled:border-transparent disabled:cursor-not-allowed disabled:opacity-50 disabled:active:scale-100", "bg-transparent text-[var(--color-content-invert-primary)] border-[var(--color-border-default-primary)] hover:bg-transparent hover:text-[var(--color-content-invert-brand-primary)] hover:border-[var(--color-border-invert-brand-primary)] focus-visible:bg-transparent focus-visible:text-[var(--color-content-invert-primary)] focus-visible:outline-none focus-visible:border-[var(--color-border-default-primary)] focus-visible:shadow-[var(--shadow-focus-invert)] active:bg-transparent active:text-[var(--color-content-invert-primary)] active:border-[var(--color-border-invert-brand-primary)] active:shadow-none active:scale-[0.98] disabled:bg-[var(--color-surface-invert-secondary)] disabled:text-[var(--color-content-default-tertiary)] disabled:border-transparent disabled:cursor-not-allowed disabled:opacity-50 disabled:active:scale-100",
ghost: ghost:
"bg-transparent text-[var(--color-content-default-brand-primary)] border-[1.5px] border-transparent hover:bg-transparent hover:text-[var(--color-content-default-primary)] hover:border-transparent focus-visible:bg-transparent focus-visible:text-[var(--color-content-default-brand-primary)] focus-visible:outline-none focus-visible:border-transparent focus-visible:shadow-[var(--shadow-focus-default)] active:bg-transparent active:text-[var(--color-content-default-primary)] active:border-[var(--color-border-default-brand-primary)] active:shadow-none active:scale-[0.98] disabled:bg-[var(--color-surface-default-secondary)] disabled:text-[var(--color-content-invert-tertiary)] disabled:border-transparent disabled:cursor-not-allowed disabled:opacity-50 disabled:active:scale-100", "bg-transparent text-[var(--color-content-default-brand-primary)] border-transparent hover:bg-transparent hover:text-[var(--color-content-default-primary)] hover:border-transparent focus-visible:bg-transparent focus-visible:text-[var(--color-content-default-brand-primary)] focus-visible:outline-none focus-visible:border-transparent focus-visible:shadow-[var(--shadow-focus-default)] active:bg-transparent active:text-[var(--color-content-default-primary)] active:border-[var(--color-border-default-brand-primary)] active:shadow-none active:scale-[0.98] disabled:bg-[var(--color-surface-default-secondary)] disabled:text-[var(--color-content-invert-tertiary)] disabled:border-transparent disabled:cursor-not-allowed disabled:opacity-50 disabled:active:scale-100",
"ghost-inverse": "ghost-inverse":
"bg-transparent text-[var(--color-content-invert-primary)] border-[1.5px] border-transparent hover:bg-transparent hover:text-[var(--color-content-invert-primary)] hover:border-transparent focus-visible:bg-transparent focus-visible:text-[var(--color-content-invert-brand-primary)] focus-visible:outline-none focus-visible:border-transparent focus-visible:shadow-[var(--shadow-focus-invert)] active:bg-[var(--color-surface-invert-brand-primary)] active:text-[var(--color-content-invert-primary)] active:border-[var(--color-border-invert-brand-primary)] active:shadow-none active:scale-[0.98] disabled:bg-[var(--color-surface-invert-secondary)] disabled:text-[var(--color-content-default-tertiary)] disabled:border-transparent disabled:cursor-not-allowed disabled:opacity-50 disabled:active:scale-100", "bg-transparent text-[var(--color-content-invert-primary)] border-transparent hover:bg-transparent hover:text-[var(--color-content-invert-primary)] hover:border-transparent focus-visible:bg-transparent focus-visible:text-[var(--color-content-invert-brand-primary)] focus-visible:outline-none focus-visible:border-transparent focus-visible:shadow-[var(--shadow-focus-invert)] active:bg-[var(--color-surface-invert-brand-primary)] active:text-[var(--color-content-invert-primary)] active:border-[var(--color-border-invert-brand-primary)] active:shadow-none active:scale-[0.98] disabled:bg-[var(--color-surface-invert-secondary)] disabled:text-[var(--color-content-default-tertiary)] disabled:border-transparent disabled:cursor-not-allowed disabled:opacity-50 disabled:active:scale-100",
danger: danger:
"bg-transparent text-[var(--color-content-default-negative-primary)] border border-[var(--color-border-default-negative-primary)] hover:bg-[var(--color-surface-invert-negative-secondary)] hover:text-[var(--color-content-invert-negative-primary)] hover:border-[var(--color-border-default-negative-primary)] focus-visible:bg-transparent focus-visible:text-[var(--color-content-default-negative-primary)] focus-visible:outline-none focus-visible:border-[var(--color-border-default-negative-primary)] focus-visible:shadow-[var(--shadow-focus-default)] active:bg-[var(--color-surface-invert-negative-primary)] active:text-[var(--color-content-invert-primary)] active:border-[1.5px] active:border-[var(--color-border-default-negative-primary)] active:shadow-none active:scale-[0.98] disabled:bg-[var(--color-surface-default-secondary)] disabled:text-[var(--color-content-invert-tertiary)] disabled:border-transparent disabled:cursor-not-allowed disabled:opacity-50 disabled:active:scale-100", "bg-transparent text-[var(--color-content-default-negative-primary)] border-[var(--color-border-default-negative-primary)] hover:bg-[var(--color-surface-invert-negative-secondary)] hover:text-[var(--color-content-invert-negative-primary)] hover:border-[var(--color-border-default-negative-primary)] focus-visible:bg-transparent focus-visible:text-[var(--color-content-default-negative-primary)] focus-visible:outline-none focus-visible:border-[var(--color-border-default-negative-primary)] focus-visible:shadow-[var(--shadow-focus-default)] active:bg-[var(--color-surface-invert-negative-primary)] active:text-[var(--color-content-invert-primary)] active:border-[var(--color-border-default-negative-primary)] active:shadow-none active:scale-[0.98] disabled:bg-[var(--color-surface-default-secondary)] disabled:text-[var(--color-content-invert-tertiary)] disabled:border-transparent disabled:cursor-not-allowed disabled:opacity-50 disabled:active:scale-100",
"danger-inverse": "danger-inverse":
"bg-transparent text-[var(--color-content-invert-negative-primary)] border border-[var(--color-border-invert-negative-primary)] hover:bg-transparent hover:text-[var(--color-content-invert-negative-primary)] hover:border-[var(--color-border-invert-negative-primary)] focus-visible:bg-transparent focus-visible:text-[var(--color-content-invert-negative-primary)] focus-visible:outline-none focus-visible:border-[var(--color-border-invert-negative-primary)] focus-visible:shadow-[var(--shadow-focus-invert)] active:bg-[var(--color-surface-default-negative-primary)] active:text-[var(--color-content-default-primary)] active:border-[1.5px] active:border-[var(--color-border-default-negative-primary)] active:shadow-none active:scale-[0.98] disabled:bg-[var(--color-surface-invert-secondary)] disabled:text-[var(--color-content-default-tertiary)] disabled:border-transparent disabled:cursor-not-allowed disabled:opacity-50 disabled:active:scale-100", "bg-transparent text-[var(--color-content-invert-negative-primary)] border-[var(--color-border-invert-negative-primary)] hover:bg-transparent hover:text-[var(--color-content-invert-negative-primary)] hover:border-[var(--color-border-invert-negative-primary)] focus-visible:bg-transparent focus-visible:text-[var(--color-content-invert-negative-primary)] focus-visible:outline-none focus-visible:border-[var(--color-border-invert-negative-primary)] focus-visible:shadow-[var(--shadow-focus-invert)] active:bg-[var(--color-surface-default-negative-primary)] active:text-[var(--color-content-default-primary)] active:border-[var(--color-border-default-negative-primary)] active:shadow-none active:scale-[0.98] disabled:bg-[var(--color-surface-invert-secondary)] disabled:text-[var(--color-content-default-tertiary)] disabled:border-transparent disabled:cursor-not-allowed disabled:opacity-50 disabled:active:scale-100",
}; };
// Hover/focus: color + border only — no scale and no extra outline (those // Hover/focus: color + border only — no scale and no extra outline (those
@@ -144,7 +152,7 @@ const Button = memo<ButtonProps>(
// and cards: color, opacity, or shadow on hover; scale only on press. // and cards: color, opacity, or shadow on hover; scale only on press.
// State prop is informational for Figma alignment; CSS pseudo-classes own // State prop is informational for Figma alignment; CSS pseudo-classes own
// the actual interaction states. // the actual interaction states.
const baseStyles = `inline-flex items-center justify-start box-border whitespace-nowrap shrink-0 touch-manipulation [-webkit-tap-highlight-color:transparent] ${sizeStyles[size]} rounded-[var(--radius-full)] ${fontStyles[size]} transition-[transform,color,background-color,border-color,box-shadow] duration-150 ease-out cursor-pointer ${variantStyles[variant]}`; const baseStyles = `inline-flex items-center justify-start box-border whitespace-nowrap shrink-0 touch-manipulation [-webkit-tap-highlight-color:transparent] ${sizeStyles[size]} rounded-[var(--radius-full)] ${fontStyles[size]} transition-[transform,color,background-color,border-color,box-shadow] duration-150 ease-out cursor-pointer ${variantStyles[variant]} ${strokeStyles[size]}`;
const combinedStyles = `${baseStyles} ${className}`; const combinedStyles = `${baseStyles} ${className}`;
const sharedA11y = { const sharedA11y = {
+4 -4
View File
@@ -21,7 +21,7 @@ function StatView({
return ( return (
<article <article
className={`relative flex h-auto min-h-[182px] w-full flex-col items-start justify-between rounded-[var(--radius-500)] bg-[var(--color-surface-invert-primary,white)] px-[var(--space-600)] py-[var(--space-800)] sm:h-[170px] sm:min-h-0 sm:p-[var(--space-600)] ${className}`.trim()} className={`relative flex h-auto min-h-[182px] w-full flex-col items-start justify-between rounded-[var(--radius-500)] bg-[var(--color-surface-invert-primary)] px-[var(--space-600)] py-[var(--space-800)] sm:h-[170px] sm:min-h-0 sm:p-[var(--space-600)] ${className}`.trim()}
> >
<div className="relative flex w-full flex-col items-start"> <div className="relative flex w-full flex-col items-start">
<div className="relative flex items-center"> <div className="relative flex items-center">
@@ -29,16 +29,16 @@ function StatView({
variant={shapeVariant} variant={shapeVariant}
className="absolute -left-[11px] -top-[21px] size-[80px] rotate-[15deg] opacity-90" className="absolute -left-[11px] -top-[21px] size-[80px] rotate-[15deg] opacity-90"
/> />
<p className="relative font-bricolage-grotesque text-xx-large-heading text-[var(--color-content-invert-primary,black)]"> <p className="relative font-bricolage-grotesque text-xx-large-heading text-[var(--color-content-invert-primary)]">
{value} {value}
</p> </p>
</div> </div>
<p className="text-small-paragraph text-[var(--color-content-invert-primary,black)]"> <p className="text-small-paragraph text-[var(--color-content-invert-primary)]">
{label} {label}
</p> </p>
</div> </div>
{asOf ? ( {asOf ? (
<p className="w-full text-xx-small-paragraph text-[var(--color-content-invert-tertiary,#2d2d2d)]"> <p className="w-full text-xx-small-paragraph text-[var(--color-content-invert-tertiary)]">
{sourcedYear ? ( {sourcedYear ? (
<> <>
{asOfPrefix ? <span>{`${asOfPrefix} `}</span> : null} {asOfPrefix ? <span>{`${asOfPrefix} `}</span> : null}
@@ -33,10 +33,10 @@ const ContentContainerContainer = memo<ContentContainerProps>(
const onLight = tone === "onLight"; const onLight = tone === "onLight";
const titleColor = onLight const titleColor = onLight
? "text-[var(--color-content-default-primary)] group-hover:text-[var(--color-content-default-brand-primary)]" ? "text-[var(--color-content-default-primary)] group-hover:text-[var(--color-content-default-brand-primary)]"
: "text-[var(--color-content-invert-brand-royal)] group-hover:text-blue-200"; : "text-[var(--color-content-invert-brand-navy)] group-hover:text-[var(--color-content-invert-brand-royal)]";
const bodyColor = onLight const bodyColor = onLight
? "text-[var(--color-content-default-secondary)]" ? "text-[var(--color-content-default-secondary)]"
: "text-[var(--color-content-invert-brand-royal)]"; : "text-[var(--color-content-invert-brand-navy)]";
const getIconImage = (slug: string): string => { const getIconImage = (slug: string): string => {
const resolvedSlug = const resolvedSlug =
@@ -56,7 +56,7 @@ const CheckboxContainer = memo<CheckboxProps>(
// Default state: tertiary border, with hover and focus states via CSS // Default state: tertiary border, with hover and focus states via CSS
// Hover changes border to brand primary color // Hover changes border to brand primary color
// Focus removes border and shows shadow (double ring: 2px white inner, 4px dark outer) // Focus removes border and shows shadow (double ring: 2px white inner, 4px dark outer)
return `${baseBox} bg-[var(--color-surface-default-primary)] border border-solid border-[var(--color-border-default-control)] hover:border-[var(--color-border-default-brand-primary,#fdfaa8)] focus:border-transparent focus:shadow-[var(--shadow-focus-invert)] focus:outline-none`; return `${baseBox} bg-[var(--color-surface-default-primary)] border border-solid border-[var(--color-border-default-control)] hover:border-[var(--color-border-default-brand-primary)] focus:border-transparent focus:shadow-[var(--shadow-focus-invert)] focus:outline-none`;
} }
// Inverse mode styles per Figma // Inverse mode styles per Figma
@@ -64,7 +64,7 @@ const CheckboxContainer = memo<CheckboxProps>(
// Inverse: transparent background, white border // Inverse: transparent background, white border
// Hover changes border to brand primary color // Hover changes border to brand primary color
// Focus shows shadow (2px dark inner, 4px white outer) - note: reversed from standard // Focus shows shadow (2px dark inner, 4px white outer) - note: reversed from standard
return `${baseBox} bg-transparent border border-solid border-[var(--color-border-invert-primary,white)] hover:border-[var(--color-border-default-brand-primary,#fdfaa8)] focus:shadow-[var(--shadow-focus-default)] focus:outline-none`; return `${baseBox} bg-transparent border border-solid border-[var(--color-border-invert-primary)] hover:border-[var(--color-border-default-brand-primary)] focus:shadow-[var(--shadow-focus-default)] focus:outline-none`;
} }
return baseBox; return baseBox;
@@ -75,8 +75,8 @@ const CheckboxContainer = memo<CheckboxProps>(
// Checkmark color per Figma // Checkmark color per Figma
const checkGlyphColor = checked const checkGlyphColor = checked
? isStandard ? isStandard
? "var(--color-content-default-brand-primary, #fefcc9)" // Light yellow/cream for standard mode ? "var(--color-content-default-brand-primary)"
: "var(--color-content-invert-primary, #000000)" // Black for inverse mode : "var(--color-content-invert-primary)"
: "transparent"; : "transparent";
// Label color // Label color
+11 -11
View File
@@ -8,7 +8,7 @@ function chipSurfaceStyle(
palette: ChipViewProps["palette"], palette: ChipViewProps["palette"],
size: ChipViewProps["size"], size: ChipViewProps["size"],
): CSSProperties { ): CSSProperties {
const borderWidth = size === "s" ? "1.25px" : "2px"; const borderWidth = size === "s" ? "1.25px" : "1.5px";
const reset: CSSProperties = { const reset: CSSProperties = {
appearance: "none", appearance: "none",
WebkitAppearance: "none", WebkitAppearance: "none",
@@ -64,14 +64,14 @@ function chipSurfaceStyle(
} }
if (state === "selected") { if (state === "selected") {
return paint( return paint(
"var(--color-surface-invert-brand-primary, #fefcc9)", "var(--color-surface-invert-brand-primary)",
"var(--color-content-invert-primary, #000)", "var(--color-content-invert-primary)",
"var(--color-border-default-brand-primary, #fdfaa8)", "var(--color-border-default-brand-primary)",
); );
} }
return paint( return paint(
"transparent", "transparent",
"var(--color-content-default-brand-primary, #fefcc9)", "var(--color-content-default-brand-primary)",
"var(--color-border-default-control)", "var(--color-border-default-control)",
); );
} }
@@ -185,7 +185,7 @@ function ChipView({
{onCheck && ( {onCheck && (
<button <button
type="button" type="button"
className="flex items-center justify-center p-[var(--space-150)] rounded-full hover:bg-[var(--color-surface-default-semi-opaque,rgba(0,0,0,0.1))] transition-colors disabled:opacity-50 disabled:cursor-not-allowed" className="flex items-center justify-center p-[var(--space-150)] rounded-full hover:bg-[var(--color-surface-default-semi-opaque)] transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
aria-label={confirmAriaLabel} aria-label={confirmAriaLabel}
disabled={!inputValue || !inputValue.trim()} disabled={!inputValue || !inputValue.trim()}
onClick={(event) => { onClick={(event) => {
@@ -202,7 +202,7 @@ function ChipView({
viewBox="0 0 12 12" viewBox="0 0 12 12"
fill="none" fill="none"
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
className="text-[var(--color-content-default-brand-primary,#fefcc9)]" className="text-[var(--color-content-default-brand-primary)]"
> >
<path <path
d="M10 3L4.5 8.5L2 6" d="M10 3L4.5 8.5L2 6"
@@ -224,7 +224,7 @@ function ChipView({
onChange={(e) => onInputChange?.(e.target.value)} onChange={(e) => onInputChange?.(e.target.value)}
onKeyDown={onInputKeyDown} onKeyDown={onInputKeyDown}
placeholder={typeToAddPlaceholder} placeholder={typeToAddPlaceholder}
className="bg-transparent border-none outline-none flex-1 min-w-0 font-normal text-[color:var(--color-content-default-tertiary,#b4b4b4)] placeholder:text-[color:var(--color-content-default-tertiary,#b4b4b4)]" className="bg-transparent border-none outline-none flex-1 min-w-0 font-normal text-[color:var(--color-content-default-tertiary)] placeholder:text-[color:var(--color-content-default-tertiary)]"
style={{ style={{
fontSize: isSmall fontSize: isSmall
? "var(--sizing-300)" ? "var(--sizing-300)"
@@ -240,7 +240,7 @@ function ChipView({
{onClose && ( {onClose && (
<button <button
type="button" type="button"
className="flex items-center justify-center p-[var(--space-150)] rounded-full hover:bg-[var(--color-surface-default-semi-opaque,rgba(0,0,0,0.1))] transition-colors" className="flex items-center justify-center p-[var(--space-150)] rounded-full hover:bg-[var(--color-surface-default-semi-opaque)] transition-colors"
aria-label={closeAriaLabel} aria-label={closeAriaLabel}
onClick={(event) => { onClick={(event) => {
event.stopPropagation(); event.stopPropagation();
@@ -253,7 +253,7 @@ function ChipView({
viewBox="0 0 12 12" viewBox="0 0 12 12"
fill="none" fill="none"
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
className="text-[var(--color-content-default-brand-primary,#fefcc9)]" className="text-[var(--color-content-default-brand-primary)]"
> >
<path <path
d="M9 3L3 9M3 3L9 9" d="M9 3L3 9M3 3L9 9"
@@ -309,7 +309,7 @@ function ChipView({
{onRemove && !isDisabled && ( {onRemove && !isDisabled && (
<button <button
type="button" type="button"
className="ml-[var(--space-050)] p-[var(--space-050)] rounded-full hover:bg-[var(--color-surface-default-semi-opaque,rgba(0,0,0,0.05))]" className="ml-[var(--space-050)] p-[var(--space-050)] rounded-full hover:bg-[var(--color-surface-default-semi-opaque)]"
aria-label={`Remove ${label}`} aria-label={`Remove ${label}`}
onClick={(event) => { onClick={(event) => {
event.stopPropagation(); event.stopPropagation();
@@ -59,7 +59,7 @@ function MultiSelectView({
{!formHeader || !label ? ( {!formHeader || !label ? (
countCaption.length > 0 ? ( countCaption.length > 0 ? (
<p className="w-full text-x-small-paragraph text-[color:var(--color-content-default-tertiary,#b4b4b4)]"> <p className="w-full text-x-small-paragraph text-[color:var(--color-content-default-tertiary)]">
{countCaption} {countCaption}
</p> </p>
) : null ) : null
@@ -133,7 +133,7 @@ function MultiSelectView({
className={ className={
!addButtonText !addButtonText
? // Circular button with border (Rule style) ? // Circular button with border (Rule style)
`${atLimit ? "cursor-not-allowed opacity-60" : "cursor-pointer hover:opacity-80"} bg-[var(--color-surface-default-transparent,rgba(0,0,0,0))] border-[1.25px] ${isInverse ? "border-[var(--color-border-default-primary,#141414)]" : "border-[var(--color-border-default-control)]"} border-solid flex items-center justify-center ${isSmall ? "size-[30px]" : "size-[40px]"} rounded-[var(--radius-full)] shrink-0 transition-opacity` `${atLimit ? "cursor-not-allowed opacity-60" : "cursor-pointer hover:opacity-80"} bg-[var(--color-surface-default-transparent)] border-[1.25px] ${isInverse ? "border-[var(--color-border-default-primary)]" : "border-[var(--color-border-default-control)]"} border-solid flex items-center justify-center ${isSmall ? "size-[30px]" : "size-[40px]"} rounded-[var(--radius-full)] shrink-0 transition-opacity`
: // Text add control (default palette: white label + brand “+”; inverse: inverse primary for both) : // Text add control (default palette: white label + brand “+”; inverse: inverse primary for both)
`${atLimit ? "cursor-not-allowed opacity-60" : "cursor-pointer hover:opacity-80"} flex items-center justify-center overflow-hidden rounded-[var(--radius-full)] shrink-0 transition-opacity ${ `${atLimit ? "cursor-not-allowed opacity-60" : "cursor-pointer hover:opacity-80"} flex items-center justify-center overflow-hidden rounded-[var(--radius-full)] shrink-0 transition-opacity ${
isSmall isSmall
@@ -151,8 +151,8 @@ function MultiSelectView({
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
className={`shrink-0 ${ className={`shrink-0 ${
!addButtonText && isInverse !addButtonText && isInverse
? "text-[var(--color-content-invert-primary,black)]" ? "text-[var(--color-content-invert-primary)]"
: "text-[var(--color-content-default-brand-primary,#fefcc9)]" : "text-[var(--color-content-default-brand-primary)]"
}`} }`}
> >
<path <path
@@ -167,8 +167,8 @@ function MultiSelectView({
<span <span
className={`font-medium ${isSmall ? "text-[length:var(--sizing-300)] leading-[14px]" : "text-medium-label"} ${ className={`font-medium ${isSmall ? "text-[length:var(--sizing-300)] leading-[14px]" : "text-medium-label"} ${
isInverse isInverse
? "text-[color:var(--color-content-invert-primary,black)]" ? "text-[color:var(--color-content-invert-primary)]"
: "text-[color:var(--color-content-default-primary,white)]" : "text-[color:var(--color-content-default-primary)]"
}`} }`}
> >
{addButtonText} {addButtonText}
@@ -53,15 +53,15 @@ const RadioButtonContainer = ({
// Focus shows shadow (double ring: 2px white inner, 4px dark outer) // Focus shows shadow (double ring: 2px white inner, 4px dark outer)
// When checked, border is brand primary (but changes to invert tertiary on focus) // When checked, border is brand primary (but changes to invert tertiary on focus)
const defaultBorder = checked const defaultBorder = checked
? "border-[var(--color-border-default-brand-primary,#fdfaa8)]" ? "border-[var(--color-border-default-brand-primary)]"
: "border-[var(--color-border-default-control)]"; : "border-[var(--color-border-default-control)]";
// When focused and checked, border should be invert tertiary (#2d2d2d) per Figma // When focused and checked, border should be invert tertiary (#2d2d2d) per Figma
const focusBorder = checked const focusBorder = checked
? "focus:border-[var(--color-content-invert-tertiary,#2d2d2d)]" ? "focus:border-[var(--color-content-invert-tertiary)]"
: "focus:border-[var(--color-border-default-control)]"; : "focus:border-[var(--color-border-default-control)]";
return `${baseBox} bg-[var(--color-surface-default-primary)] border border-solid ${defaultBorder} hover:border-[var(--color-border-default-brand-primary,#fdfaa8)] ${focusBorder} focus:shadow-[var(--shadow-focus-invert)] focus:outline-none`; return `${baseBox} bg-[var(--color-surface-default-primary)] border border-solid ${defaultBorder} hover:border-[var(--color-border-default-brand-primary)] ${focusBorder} focus:shadow-[var(--shadow-focus-invert)] focus:outline-none`;
} }
// Inverse mode styles // Inverse mode styles
@@ -71,17 +71,17 @@ const RadioButtonContainer = ({
// Focus shows shadow (double ring: 2px dark inner, 4px white outer) // Focus shows shadow (double ring: 2px dark inner, 4px white outer)
// When checked, border is brand primary (but changes to white on focus) // When checked, border is brand primary (but changes to white on focus)
const defaultBorder = checked const defaultBorder = checked
? "border-[var(--color-border-default-brand-primary,#fdfaa8)]" ? "border-[var(--color-border-default-brand-primary)]"
: "border-[var(--color-border-invert-primary,white)]"; : "border-[var(--color-border-invert-primary)]";
// Hover border: inverse brand primary for both selected and unselected per Figma // Hover border: inverse brand primary for both selected and unselected per Figma
const hoverBorder = const hoverBorder =
"hover:border-[var(--color-border-invert-brand-primary,#6c6701)]"; "hover:border-[var(--color-border-invert-brand-primary)]";
// Focus border: when focused and checked, border should be white per Figma // Focus border: when focused and checked, border should be white per Figma
const focusBorder = checked const focusBorder = checked
? "focus:border-[var(--color-border-invert-primary,white)]" ? "focus:border-[var(--color-border-invert-primary)]"
: "focus:border-[var(--color-border-invert-primary,white)]"; : "focus:border-[var(--color-border-invert-primary)]";
return `${baseBox} bg-transparent border border-solid ${defaultBorder} ${hoverBorder} ${focusBorder} focus:shadow-[var(--shadow-focus-default)] focus:outline-none`; return `${baseBox} bg-transparent border border-solid ${defaultBorder} ${hoverBorder} ${focusBorder} focus:shadow-[var(--shadow-focus-default)] focus:outline-none`;
} }
@@ -39,9 +39,9 @@ export function RadioButtonView({
<div <div
className={`w-[16px] h-[16px] rounded-full transition-all duration-200 ${ className={`w-[16px] h-[16px] rounded-full transition-all duration-200 ${
checked && mode === "standard" checked && mode === "standard"
? "bg-[var(--color-content-default-brand-primary,#fefcc9)] group-hover:!bg-[var(--color-surface-default-brand-primary)]" ? "bg-[var(--color-content-default-brand-primary)] group-hover:!bg-[var(--color-surface-default-brand-primary)]"
: checked && mode === "inverse" : checked && mode === "inverse"
? "bg-[var(--color-content-default-primary,#000000)]" ? "bg-[var(--color-content-default-primary)]"
: "bg-transparent" : "bg-transparent"
}`} }`}
/> />
@@ -52,8 +52,8 @@ export function RadioGroupView({
<span <span
className={`text-small-paragraph ${ className={`text-small-paragraph ${
mode === "inverse" mode === "inverse"
? "text-[var(--color-content-invert-secondary,#1f1f1f)]" ? "text-[var(--color-content-invert-secondary)]"
: "text-[var(--color-content-default-secondary,#b4b4b4)]" : "text-[var(--color-content-default-secondary)]"
}`} }`}
> >
{option.subtext} {option.subtext}
@@ -109,7 +109,7 @@ export function SelectInputView({
.replace(/\s+/g, " "); .replace(/\s+/g, " ");
if (disabled) { if (disabled) {
return `${baseClasses} bg-[var(--color-surface-default-secondary)] text-[var(--color-content-invert-tertiary,#2d2d2d)] border-[var(--color-border-default-primary)] cursor-not-allowed opacity-40`; return `${baseClasses} bg-[var(--color-surface-default-secondary)] text-[var(--color-content-invert-tertiary)] border-[var(--color-border-default-primary)] cursor-not-allowed opacity-40`;
} }
if (error) { if (error) {
@@ -135,7 +135,7 @@ export function SelectInputView({
// Text color based on filled state // Text color based on filled state
const textColorClass = isFilled const textColorClass = isFilled
? "text-[var(--color-content-default-primary)]" ? "text-[var(--color-content-default-primary)]"
: "text-[var(--color-content-default-tertiary,#b4b4b4)]"; : "text-[var(--color-content-default-tertiary)]";
// Chevron icon // Chevron icon
const chevronClasses = `w-5 h-5 text-[var(--color-content-default-primary)] transition-transform duration-200 ${ const chevronClasses = `w-5 h-5 text-[var(--color-content-default-primary)] transition-transform duration-200 ${
@@ -151,7 +151,7 @@ export function SelectInputView({
{label} {label}
</label> </label>
{asterisk && ( {asterisk && (
<span className="text-[var(--color-content-default-negative-primary,#ea4845)] text-xx-small-label"> <span className="text-[var(--color-content-default-negative-primary)] text-xx-small-label">
* *
</span> </span>
)} )}
@@ -167,7 +167,7 @@ export function SelectInputView({
)} )}
</div> </div>
{textOptional && ( {textOptional && (
<span className="text-[var(--color-content-default-tertiary,#b4b4b4)] text-xx-small-paragraph"> <span className="text-[var(--color-content-default-tertiary)] text-xx-small-paragraph">
Optional text Optional text
</span> </span>
)} )}
@@ -102,7 +102,7 @@ const TextInputContainer = forwardRef<HTMLInputElement, TextInputProps>(
if (disabled) { if (disabled) {
return { return {
input: input:
"bg-[var(--color-surface-default-secondary)] text-[var(--color-content-invert-tertiary,#2d2d2d)] border border-solid border-[var(--color-border-default-primary)] cursor-not-allowed", "bg-[var(--color-surface-default-secondary)] text-[var(--color-content-invert-tertiary)] border border-solid border-[var(--color-border-default-primary)] cursor-not-allowed",
label: "text-[var(--color-content-default-secondary)]", label: "text-[var(--color-content-default-secondary)]",
inputWrapper: "relative", inputWrapper: "relative",
focusRing: "", focusRing: "",
@@ -171,7 +171,7 @@ const TextInputContainer = forwardRef<HTMLInputElement, TextInputProps>(
const inputClasses = ` const inputClasses = `
w-full transition-all duration-200 ease-in-out w-full transition-all duration-200 ease-in-out
focus:outline-none focus:ring-0 focus:outline-none focus:ring-0
placeholder:text-[var(--color-content-default-tertiary,#b4b4b4)] placeholder:text-[var(--color-content-default-tertiary)]
${sizeStyles.input} ${sizeStyles.input}
${stateStyles.input} ${stateStyles.input}
${className} ${className}
@@ -180,7 +180,7 @@ const TextInputContainer = forwardRef<HTMLInputElement, TextInputProps>(
// Text color for filled text (placeholder color is handled above) // Text color for filled text (placeholder color is handled above)
const textColorClass = isFilled const textColorClass = isFilled
? "text-[var(--color-content-default-primary)]" ? "text-[var(--color-content-default-primary)]"
: "text-[var(--color-content-default-tertiary,#b4b4b4)]"; : "text-[var(--color-content-default-tertiary)]";
// Form field handlers with disabled state handling // Form field handlers with disabled state handling
const { handleChange, handleBlur } = useFormField<HTMLInputElement>( const { handleChange, handleBlur } = useFormField<HTMLInputElement>(
@@ -20,22 +20,22 @@ function UploadView({
// Button styles based on active state // Button styles based on active state
const buttonBgClass = isActive const buttonBgClass = isActive
? "bg-[var(--color-surface-invert-primary,white)]" ? "bg-[var(--color-surface-invert-primary)]"
: "bg-[var(--color-surface-default-secondary,#141414)]"; : "bg-[var(--color-surface-default-secondary)]";
const buttonTextColor = isActive const buttonTextColor = isActive
? "text-[color:var(--color-content-invert-primary,black)]" ? "text-[color:var(--color-content-invert-primary)]"
: "text-[color:var(--color-content-invert-tertiary,#2d2d2d)]"; : "text-[color:var(--color-content-invert-tertiary)]";
// Description text color based on active state // Description text color based on active state
const descriptionTextColor = isActive const descriptionTextColor = isActive
? "text-[color:var(--color-content-default-primary,white)]" ? "text-[color:var(--color-content-default-primary)]"
: "text-[color:var(--color-content-default-tertiary,#b4b4b4)]"; : "text-[color:var(--color-content-default-tertiary)]";
// Icon color based on active state // Icon color based on active state
const iconColor = isActive const iconColor = isActive
? "text-[color:var(--color-content-invert-primary,black)]" ? "text-[color:var(--color-content-invert-primary)]"
: "text-[color:var(--color-content-invert-tertiary,#2d2d2d)]"; : "text-[color:var(--color-content-invert-tertiary)]";
return ( return (
<div <div
@@ -55,7 +55,7 @@ function UploadView({
{/* Upload container — max width per create-flow spec */} {/* Upload container — max width per create-flow spec */}
<div <div
className={`bg-[var(--color-surface-default-secondary,#141414)] mx-auto flex w-full max-w-[474px] shrink-0 items-center justify-center gap-[24px] rounded-[var(--radius-200)] px-[var(--space-600)] py-[var(--space-1200)] ${ className={`bg-[var(--color-surface-default-secondary)] mx-auto flex w-full max-w-[474px] shrink-0 items-center justify-center gap-[24px] rounded-[var(--radius-200)] px-[var(--space-600)] py-[var(--space-1200)] ${
state === "error" state === "error"
? "border border-[var(--color-border-default-utility-negative)]" ? "border border-[var(--color-border-default-utility-negative)]"
: "" : ""
-3
View File
@@ -216,9 +216,6 @@ const Footer = memo(({ siteOrigin = PRODUCTION_SITE_ORIGIN }: { siteOrigin?: str
<Link href={t("legal.termsOfServiceHref")} className={legalLinkClass}> <Link href={t("legal.termsOfServiceHref")} className={legalLinkClass}>
{t("legal.termsOfService")} {t("legal.termsOfService")}
</Link> </Link>
<Link href={t("legal.cookiesSettingsHref")} className={legalLinkClass}>
{t("legal.cookiesSettings")}
</Link>
</div> </div>
</div> </div>
</div> </div>
@@ -63,22 +63,22 @@ const MenuItemContainer = memo<MenuItemProps>(
const defaultModeStyles: Record<"default" | "hover" | "selected", string> = const defaultModeStyles: Record<"default" | "hover" | "selected", string> =
{ {
default: default:
"bg-transparent text-[var(--color-content-default-brand-primary,#fefcc9)] hover:bg-[var(--color-gray-800)] hover:text-[var(--color-content-default-brand-primary,#fefcc9)]", "bg-transparent text-[var(--color-content-default-brand-primary)] hover:bg-[var(--color-gray-800)] hover:text-[var(--color-content-default-brand-primary)]",
hover: hover:
"bg-[var(--color-gray-800)] text-[var(--color-content-default-brand-primary,#fefcc9)]", "bg-[var(--color-gray-800)] text-[var(--color-content-default-brand-primary)]",
selected: selected:
"border border-[var(--color-border-default-brand-primary,#fdfaa8)] text-[var(--color-content-default-brand-primary,#fefcc9)] bg-transparent hover:bg-[var(--color-gray-800)]", "border border-[var(--color-border-default-brand-primary)] text-[var(--color-content-default-brand-primary)] bg-transparent hover:bg-[var(--color-gray-800)]",
}; };
// State styles for Inverse mode (black text on yellow HeaderTab / inverse surfaces) // State styles for Inverse mode (black text on yellow HeaderTab / inverse surfaces)
const inverseModeStyles: Record<"default" | "hover" | "selected", string> = const inverseModeStyles: Record<"default" | "hover" | "selected", string> =
{ {
default: default:
"bg-transparent text-[var(--color-content-invert-primary,black)] hover:bg-[var(--color-surface-invert-brand-secondary)] hover:text-[var(--color-content-invert-primary,black)]", "bg-transparent text-[var(--color-content-invert-primary)] hover:bg-[var(--color-surface-invert-brand-secondary)] hover:text-[var(--color-content-invert-primary)]",
hover: hover:
"bg-[var(--color-surface-invert-brand-secondary)] text-[var(--color-content-invert-primary,black)]", "bg-[var(--color-surface-invert-brand-secondary)] text-[var(--color-content-invert-primary)]",
selected: selected:
"border border-[var(--color-border-default-primary,#141414)] text-[var(--color-content-invert-primary,black)] bg-transparent hover:bg-[var(--color-surface-invert-brand-secondary)]", "border border-[var(--color-border-default-primary)] text-[var(--color-content-invert-primary)] bg-transparent hover:bg-[var(--color-surface-invert-brand-secondary)]",
}; };
// Get state styles based on mode // Get state styles based on mode
@@ -33,6 +33,7 @@ const FeatureGridContainer = memo<FeatureGridProps>(
), ),
descriptionClassName: "text-[var(--color-content-invert-brand-lime)]", descriptionClassName: "text-[var(--color-content-invert-brand-lime)]",
iconSrc: getAssetPath(featureIconPath("arrow-left-right")), iconSrc: getAssetPath(featureIconPath("arrow-left-right")),
href: "/how-it-works",
}, },
{ {
backgroundColor: "bg-[var(--color-surface-invert-brand-rust)]", backgroundColor: "bg-[var(--color-surface-invert-brand-rust)]",
@@ -42,6 +43,7 @@ const FeatureGridContainer = memo<FeatureGridProps>(
), ),
descriptionClassName: "text-[var(--color-content-invert-brand-rust)]", descriptionClassName: "text-[var(--color-content-invert-brand-rust)]",
iconSrc: getAssetPath(featureIconPath("door-open")), iconSrc: getAssetPath(featureIconPath("door-open")),
href: "/blog/integrating-new-members-without-dilution",
}, },
{ {
backgroundColor: "bg-[var(--color-surface-invert-brand-teal)]", backgroundColor: "bg-[var(--color-surface-invert-brand-teal)]",
@@ -10,6 +10,7 @@ export interface Feature {
description: string; description: string;
descriptionClassName: string; descriptionClassName: string;
iconSrc: string; iconSrc: string;
href?: string;
} }
export interface FeatureGridViewProps extends FeatureGridProps { export interface FeatureGridViewProps extends FeatureGridProps {
@@ -1,8 +1,35 @@
"use client"; "use client";
import Link from "next/link";
import { useTranslation } from "../../../contexts/MessagesContext"; import { useTranslation } from "../../../contexts/MessagesContext";
import ContentLockup from "../../type/ContentLockup"; import ContentLockup from "../../type/ContentLockup";
import type { FeatureGridViewProps } from "./FeatureGrid.types"; import type { Feature, FeatureGridViewProps } from "./FeatureGrid.types";
function FeatureTileBody({ feature }: { feature: Feature }) {
return (
<>
<div className="size-[40px] shrink-0 overflow-clip">
{/* eslint-disable-next-line @next/next/no-img-element -- decorative Figma glyph */}
<img
src={feature.iconSrc}
alt=""
width={40}
height={40}
className="size-full object-contain"
role="presentation"
/>
</div>
<div className="flex w-full flex-col gap-[var(--space-200)]">
<p className="font-semibold text-[17px] leading-[22px] text-[var(--color-content-invert-primary)]">
{feature.title}
</p>
<p className={`text-small-paragraph ${feature.descriptionClassName}`}>
{feature.description}
</p>
</div>
</>
);
}
/** Figma **Section / Feature-Grid** [18632:10668](https://www.figma.com/design/agv0VBLiBlcnSAaiAORgPR/Community-Rule-System?node-id=18632-10668&m=dev). */ /** Figma **Section / Feature-Grid** [18632:10668](https://www.figma.com/design/agv0VBLiBlcnSAaiAORgPR/Community-Rule-System?node-id=18632-10668&m=dev). */
function FeatureGridView({ function FeatureGridView({
@@ -41,34 +68,25 @@ function FeatureGridView({
</div> </div>
<div className="grid w-full grid-cols-1 gap-[var(--space-200)] md:grid-cols-2 md:gap-[var(--space-400)] lg:flex-1"> <div className="grid w-full grid-cols-1 gap-[var(--space-200)] md:grid-cols-2 md:gap-[var(--space-400)] lg:flex-1">
{features.map((feature) => ( {features.map((feature) => {
<div const tileClassName = `flex flex-col gap-[var(--space-400)] p-[var(--space-600)] ${feature.backgroundColor} rounded-[var(--radius-200)] md:rounded-[var(--radius-400)] lg:rounded-[var(--radius-500)]`;
if (feature.href) {
return (
<Link
key={feature.title} key={feature.title}
className={`flex flex-col gap-[var(--space-400)] p-[var(--space-600)] ${feature.backgroundColor} rounded-[var(--radius-200)] md:rounded-[var(--radius-400)] lg:rounded-[var(--radius-500)]`} href={feature.href}
className={`${tileClassName} no-underline touch-manipulation [-webkit-tap-highlight-color:transparent] hover:opacity-90 focus-visible:outline-none focus-visible:shadow-[var(--shadow-focus-invert)]`}
> >
<div className="size-[40px] shrink-0 overflow-clip"> <FeatureTileBody feature={feature} />
{/* eslint-disable-next-line @next/next/no-img-element -- decorative Figma glyph */} </Link>
<img );
src={feature.iconSrc} }
alt="" return (
width={40} <div key={feature.title} className={tileClassName}>
height={40} <FeatureTileBody feature={feature} />
className="size-full object-contain"
role="presentation"
/>
</div> </div>
<div className="flex w-full flex-col gap-[var(--space-200)]"> );
<p className="font-semibold text-[17px] leading-[22px] text-[var(--color-content-invert-primary)]"> })}
{feature.title}
</p>
<p
className={`text-small-paragraph ${feature.descriptionClassName}`}
>
{feature.description}
</p>
</div>
</div>
))}
</div> </div>
</div> </div>
</div> </div>
@@ -75,7 +75,7 @@ function QuoteBlockView({
role="region" role="region"
> >
<div <div
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(--space-500)] bg-[var(--color-surface-invert-brand-primary,#fefcc9)] px-[var(--space-800)] py-[var(--space-1200)] md:px-[var(--space-1800)] md:py-[var(--space-2400)]" 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(--space-500)] bg-[var(--color-surface-invert-brand-primary)] px-[var(--space-800)] py-[var(--space-1200)] md:px-[var(--space-1800)] md:py-[var(--space-2400)]"
> >
<QuoteStatementDecor /> <QuoteStatementDecor />
<div className="relative z-10 flex w-full min-w-0 shrink-0 flex-col items-center text-center"> <div className="relative z-10 flex w-full min-w-0 shrink-0 flex-col items-center text-center">
+4 -3
View File
@@ -2,16 +2,17 @@ import type { Metadata } from "next";
import Link from "next/link"; import Link from "next/link";
import messages from "../messages/en/index"; import messages from "../messages/en/index";
import { getTranslation } from "../lib/i18n/getTranslation"; import { getTranslation } from "../lib/i18n/getTranslation";
import { NO_INDEX_ROBOTS } from "../lib/siteMetadata"; import { NO_INDEX_ROBOTS, routeMetadata } from "../lib/siteMetadata";
import { getGovernanceTemplateCatalogEntry } from "../lib/templates/governanceTemplateCatalog"; import { getGovernanceTemplateCatalogEntry } from "../lib/templates/governanceTemplateCatalog";
import Icon from "./components/asset/icon"; import Icon from "./components/asset/icon";
import Button from "./components/buttons/Button"; import Button from "./components/buttons/Button";
import HeroDecor from "./components/sections/HeroBanner/HeroDecor"; import HeroDecor from "./components/sections/HeroBanner/HeroDecor";
export const metadata: Metadata = { export const metadata: Metadata = routeMetadata("/404", {
title: messages.metadata.notFound.title, title: messages.metadata.notFound.title,
description: messages.metadata.notFound.description,
robots: NO_INDEX_ROBOTS, robots: NO_INDEX_ROBOTS,
}; });
const NOT_FOUND_TEMPLATE_SLUGS = [ const NOT_FOUND_TEMPLATE_SLUGS = [
"consensus", "consensus",
+83 -80
View File
@@ -202,14 +202,15 @@
/* Grays & alpha */ /* Grays & alpha */
--color-gray-000: #ffffff; --color-gray-000: #ffffff;
--color-gray-050: #f6f6f6; --color-gray-050: #f6f6f6;
--color-gray-100: #e5e5e5; --color-gray-100: #e4e4e4;
--color-gray-200: #d2d2d2; --color-gray-150: #d2d2d2;
--color-gray-300: #b4b4b4; --color-gray-200: #c2c2c2;
--color-gray-400: #8f8f8f; --color-gray-300: #a4a4a4;
--color-gray-500: #666666; --color-gray-400: #8a8a8a;
--color-gray-600: #464646; --color-gray-500: #717171;
--color-gray-700: #2d2d2d; --color-gray-600: #5b5b5b;
--color-gray-800: #1f1f1f; --color-gray-700: #464646;
--color-gray-800: #303030;
--color-gray-900: #141414; --color-gray-900: #141414;
--color-gray-1000: #000000; --color-gray-1000: #000000;
@@ -244,16 +245,16 @@
/* Lavender */ /* Lavender */
--color-lavender-lavender0: #f0ebff; --color-lavender-lavender0: #f0ebff;
--color-lavender-lavender50: #e8defe; --color-lavender-lavender50: #e8defe;
--color-lavender-lavender100: #e0d1fd; --color-lavender-lavender100: #dccdf9;
--color-lavender-lavender150: #d7c3f9; --color-lavender-lavender150: #d1bdf3;
--color-lavender-lavender200: #ceb5f6; --color-lavender-lavender200: #c6adee;
--color-lavender-lavender300: #bb97ea; --color-lavender-lavender300: #b490e2;
--color-lavender-lavender400: #a578d9; --color-lavender-lavender400: #a275d5;
--color-lavender-lavender500: #8d5ac4; --color-lavender-lavender500: #8f5cc6;
--color-lavender-lavender600: #743ea9; --color-lavender-lavender600: #7c47b2;
--color-lavender-lavender700: #5a278c; --color-lavender-lavender700: #67359b;
--color-lavender-lavender800: #41146c; --color-lavender-lavender800: #50277e;
--color-lavender-lavender900: #2b074d; --color-lavender-lavender900: #38185c;
--color-lavender-lavender1000: #1a0033; --color-lavender-lavender1000: #1a0033;
/* Lime */ /* Lime */
@@ -274,61 +275,61 @@
/* Magenta */ /* Magenta */
--color-magenta-magenta0: #ffebf0; --color-magenta-magenta0: #ffebf0;
--color-magenta-magenta50: #fedee7; --color-magenta-magenta50: #fedee7;
--color-magenta-magenta100: #fdd1dd; --color-magenta-magenta100: #f8ccd8;
--color-magenta-magenta150: #f9c3d2; --color-magenta-magenta150: #f0bbca;
--color-magenta-magenta200: #f6b5c7; --color-magenta-magenta200: #eaaabc;
--color-magenta-magenta300: #ea97ad; --color-magenta-magenta300: #dd8ba1;
--color-magenta-magenta400: #d97892; --color-magenta-magenta400: #ce6f89;
--color-magenta-magenta500: #c45a76; --color-magenta-magenta500: #be5571;
--color-magenta-magenta600: #a93e5b; --color-magenta-magenta600: #aa3f5b;
--color-magenta-magenta700: #8c2742; --color-magenta-magenta700: #922d47;
--color-magenta-magenta800: #6c142b; --color-magenta-magenta800: #761f33;
--color-magenta-magenta900: #4d071a; --color-magenta-magenta900: #571222;
--color-magenta-magenta1000: #33000e; --color-magenta-magenta1000: #33000e;
/* Red */ /* Red */
--color-red-red0: #ffebf1; --color-red-red0: #ffebf1;
--color-red-red50: #fec9d6; --color-red-red50: #fec9d6;
--color-red-red100: #fda8b7; --color-red-red100: #ffb6c2;
--color-red-red150: #f98c98; --color-red-red150: #ffa4ac;
--color-red-red200: #f66f78; --color-red-red200: #ff9296;
--color-red-red300: #ea4845; --color-red-red300: #ff6a62;
--color-red-red400: #d93529; --color-red-red400: #ef4c3d;
--color-red-red500: #c42916; --color-red-red500: #d33a26;
--color-red-red600: #a9200b; --color-red-red600: #b62e1a;
--color-red-red700: #8c1905; --color-red-red700: #972613;
--color-red-red800: #6c1301; --color-red-red800: #771f0d;
--color-red-red900: #4d0d00; --color-red-red900: #571707;
--color-red-red1000: #330800; --color-red-red1000: #330800;
/* Royal Blue */ /* Royal Blue */
--color-royal-blue-royal-blue0: #ebecff; --color-royal-blue-royal-blue0: #ebecff;
--color-royal-blue-royal-blue50: #c9cdfe; --color-royal-blue-royal-blue50: #c9cdfe;
--color-royal-blue-royal-blue100: #a8adfd; --color-royal-blue-royal-blue100: #b9bfff;
--color-royal-blue-royal-blue150: #8c91f9; --color-royal-blue-royal-blue150: #aab1ff;
--color-royal-blue-royal-blue200: #6f74f6; --color-royal-blue-royal-blue200: #99a4ff;
--color-royal-blue-royal-blue300: #4548ea; --color-royal-blue-royal-blue300: #778cff;
--color-royal-blue-royal-blue400: #2b29d9; --color-royal-blue-royal-blue400: #5674ff;
--color-royal-blue-royal-blue500: #1c16c4; --color-royal-blue-royal-blue500: #3a5afe;
--color-royal-blue-royal-blue600: #140ba9; --color-royal-blue-royal-blue600: #2d4adb;
--color-royal-blue-royal-blue700: #0f058c; --color-royal-blue-royal-blue700: #233bb6;
--color-royal-blue-royal-blue800: #0c016c; --color-royal-blue-royal-blue800: #1d2c8f;
--color-royal-blue-royal-blue900: #0a004d; --color-royal-blue-royal-blue900: #171b65;
--color-royal-blue-royal-blue1000: #080033; --color-royal-blue-royal-blue1000: #080033;
/* Rust */ /* Rust */
--color-rust-rust0: #ffedeb; --color-rust-rust0: #ffedeb;
--color-rust-rust50: #fed8d4; --color-rust-rust50: #fed8d4;
--color-rust-rust100: #fdc4bd; --color-rust-rust100: #ffc6bf;
--color-rust-rust150: #f9b0a6; --color-rust-rust150: #feb4aa;
--color-rust-rust200: #f69c8f; --color-rust-rust200: #fda395;
--color-rust-rust300: #ea7a65; --color-rust-rust300: #f4836e;
--color-rust-rust400: #d96043; --color-rust-rust400: #e46a4c;
--color-rust-rust500: #c44e29; --color-rust-rust500: #cd5632;
--color-rust-rust600: #a94417; --color-rust-rust600: #b04a1f;
--color-rust-rust700: #8c3d0b; --color-rust-rust700: #904111;
--color-rust-rust800: #6c3604; --color-rust-rust800: #703909;
--color-rust-rust900: #4d2c01; --color-rust-rust900: #512f05;
--color-rust-rust1000: #332100; --color-rust-rust1000: #332100;
/* Teal */ /* Teal */
@@ -392,25 +393,25 @@
--color-border-invert-warning-primary: var(--color-yellow-yellow600); --color-border-invert-warning-primary: var(--color-yellow-yellow600);
--color-border-invert-negative-primary: var(--color-red-red300); --color-border-invert-negative-primary: var(--color-red-red300);
/* Link — nearest palette step for each off-ramp hex */ /* Link — Navigation / Link binds content tokens */
--color-link-primary: var(--color-gray-1000); --color-link-primary: var(--color-content-invert-primary);
--color-link-primary-hover: var(--color-gray-500); --color-link-primary-hover: var(--color-content-invert-quaternary);
--color-link-primary-focus: var(--color-gray-500); --color-link-primary-focus: var(--color-content-invert-quaternary);
--color-link-primary-active: var(--color-gray-500); --color-link-primary-active: var(--color-content-invert-quaternary);
--color-link-secondary: var(--color-gray-500); --color-link-secondary: var(--color-content-invert-quaternary);
--color-link-secondary-hover: var(--color-gray-700); --color-link-secondary-hover: var(--color-content-invert-tertiary);
--color-link-secondary-focus: var(--color-gray-700); --color-link-secondary-focus: var(--color-content-invert-tertiary);
--color-link-secondary-active: var(--color-gray-700); --color-link-secondary-active: var(--color-content-invert-tertiary);
--color-link-invert-primary: var(--color-gray-000); --color-link-invert-primary: var(--color-content-default-primary);
--color-link-invert-primary-hover: var(--color-gray-300); --color-link-invert-primary-hover: var(--color-content-default-tertiary);
--color-link-invert-primary-focus: var(--color-gray-300); --color-link-invert-primary-focus: var(--color-content-default-tertiary);
--color-link-invert-primary-active: var(--color-gray-300); --color-link-invert-primary-active: var(--color-content-default-tertiary);
--color-link-invert-secondary: var(--color-gray-300); --color-link-invert-secondary: var(--color-content-default-tertiary);
--color-link-invert-secondary-hover: var(--color-gray-050); --color-link-invert-secondary-hover: var(--color-content-default-primary);
--color-link-invert-secondary-focus: var(--color-gray-050); --color-link-invert-secondary-focus: var(--color-content-default-primary);
--color-link-invert-secondary-active: var(--color-gray-050); --color-link-invert-secondary-active: var(--color-content-default-primary);
--color-border-link-focus: var(--color-gray-1000); --color-border-link-focus: var(--color-border-default-primary);
--color-border-link-invert-focus: var(--color-gray-000); --color-border-link-invert-focus: var(--color-border-invert-primary);
/* Content */ /* Content */
--color-content-default-brand-accent: var(--color-opacity-700); --color-content-default-brand-accent: var(--color-opacity-700);
@@ -443,10 +444,12 @@
--color-content-invert-primary: var(--color-gray-1000); --color-content-invert-primary: var(--color-gray-1000);
--color-content-invert-secondary: var(--color-gray-800); --color-content-invert-secondary: var(--color-gray-800);
--color-content-invert-tertiary: var(--color-gray-700); --color-content-invert-tertiary: var(--color-gray-700);
--color-content-invert-quaternary: var(--color-gray-500);
--color-content-invert-brand-primary: var(--color-yellow-yellow900); --color-content-invert-brand-primary: var(--color-yellow-yellow900);
--color-content-invert-brand-secondary: var(--color-yellow-yellow800); --color-content-invert-brand-secondary: var(--color-yellow-yellow800);
--color-content-invert-brand-lime: var(--color-lime-lime800); --color-content-invert-brand-lime: var(--color-lime-lime800);
--color-content-invert-brand-royal: var(--color-royal-blue-royal-blue400); --color-content-invert-brand-royal: var(--color-royal-blue-royal-blue700);
--color-content-invert-brand-navy: var(--color-royal-blue-royal-blue1000);
--color-content-invert-brand-teal: var(--color-teal-teal700); --color-content-invert-brand-teal: var(--color-teal-teal700);
--color-content-invert-brand-rust: var(--color-rust-rust700); --color-content-invert-brand-rust: var(--color-rust-rust700);
--color-content-invert-brand-lavender: var(--color-lavender-lavender700); --color-content-invert-brand-lavender: var(--color-lavender-lavender700);
@@ -454,7 +457,7 @@
--color-content-invert-brand-red: var(--color-red-red700); --color-content-invert-brand-red: var(--color-red-red700);
--color-content-invert-positive-primary: var(--color-kiwi-kiwi800); --color-content-invert-positive-primary: var(--color-kiwi-kiwi800);
--color-content-invert-warning-primary: var(--color-yellow-yellow800); --color-content-invert-warning-primary: var(--color-yellow-yellow800);
--color-content-invert-negative-primary: var(--color-red-red500); --color-content-invert-negative-primary: var(--color-red-red600);
/* Surface */ /* Surface */
--color-surface-default-brand-red: var(--color-red-red700); --color-surface-default-brand-red: var(--color-red-red700);
@@ -463,7 +466,7 @@
--color-surface-default-brand-lime: var(--color-lime-lime800); --color-surface-default-brand-lime: var(--color-lime-lime800);
--color-surface-default-brand-primary: var(--color-yellow-yellow1000); --color-surface-default-brand-primary: var(--color-yellow-yellow1000);
--color-surface-default-brand-secondary: var(--color-yellow-yellow800); --color-surface-default-brand-secondary: var(--color-yellow-yellow800);
--color-surface-default-brand-royal: var(--color-royal-blue-royal-blue600); --color-surface-default-brand-royal: var(--color-royal-blue-royal-blue700);
--color-surface-default-brand-rust: var(--color-rust-rust700); --color-surface-default-brand-rust: var(--color-rust-rust700);
--color-surface-default-brand-teal: var(--color-teal-teal700); --color-surface-default-brand-teal: var(--color-teal-teal700);
--color-surface-default-positive-secondary: var(--color-kiwi-kiwi1000); --color-surface-default-positive-secondary: var(--color-kiwi-kiwi1000);
+2 -2
View File
@@ -1,7 +1,7 @@
{ {
"_comment": "FeatureGrid component defaults (shared across pages). Explore the patterns goes to /learn; feature cards stay non-interactive until destinations are decided.", "_comment": "FeatureGrid component defaults (shared across pages). Explore the patterns goes to /templates. Feature cards link only when a matching page exists.",
"linkText": "Explore the patterns", "linkText": "Explore the patterns",
"linkHref": "/learn", "linkHref": "/templates",
"ariaLabel": "Feature tools and services", "ariaLabel": "Feature tools and services",
"features": { "features": {
"decisionMaking": { "decisionMaking": {
+1 -3
View File
@@ -31,9 +31,7 @@
"privacyPolicy": "Privacy Policy", "privacyPolicy": "Privacy Policy",
"privacyPolicyHref": "/privacy", "privacyPolicyHref": "/privacy",
"termsOfService": "Terms of Service", "termsOfService": "Terms of Service",
"termsOfServiceHref": "/terms", "termsOfServiceHref": "/terms"
"cookiesSettings": "Cookies Settings",
"cookiesSettingsHref": "/cookies"
}, },
"license": { "license": {
"_comment": "Replaces Figma's '© All right reserved'. Covers user-facing content (guides, template copy, marketing) per README — not GPL-3.0 application source. Published user-authored rules are a separate grant.", "_comment": "Replaces Figma's '© All right reserved'. Covers user-facing content (guides, template copy, marketing) per README — not GPL-3.0 application source. Published user-authored rules are a separate grant.",
+10 -6
View File
@@ -7,8 +7,8 @@
"templatesAlt": "CommunityRule governance templates" "templatesAlt": "CommunityRule governance templates"
}, },
"home": { "home": {
"title": "Build structure before you need it — CommunityRule", "title": "CommunityRule | Write down how your community works",
"description": "Strong communities agree on four things early. They settle how they talk, who belongs, how they decide and what happens when they disagree. Write yours down together.", "description": "Write down how your community talks, who belongs, how it decides and what happens when it disagrees. Free for every community.",
"keywords": [ "keywords": [
"community", "community",
"governance", "governance",
@@ -47,16 +47,20 @@
"fallbackDescription": "A blog post from our community." "fallbackDescription": "A blog post from our community."
}, },
"login": { "login": {
"title": "Log in" "title": "Log in",
"description": "Enter your email and we'll send you a magic link to sign in. No password needed!"
}, },
"profile": { "profile": {
"title": "Profile" "title": "Profile",
"description": "Manage your CommunityRules, saved progress, and account."
}, },
"monitor": { "monitor": {
"title": "Monitor" "title": "Monitor",
"description": "Real-time monitoring of Core Web Vitals and performance metrics for Community Rule 3.0"
}, },
"notFound": { "notFound": {
"title": "Page not found" "title": "Page not found",
"description": "Looks like this page didn't make it to a consensus. It may have moved, been removed, or never existed."
}, },
"useCases": { "useCases": {
"title": "Use cases", "title": "Use cases",
+16 -6
View File
@@ -47,22 +47,32 @@ describe("FeatureGrid (behavioral tests)", () => {
).not.toBeInTheDocument(); ).not.toBeInTheDocument();
}); });
it("renders all four feature cards as static tiles", () => { it("links the two features that have pages and leaves the others as tiles", () => {
render(<FeatureGrid title="Test" subtitle="Test" />); render(<FeatureGrid title="Test" subtitle="Test" />);
expect(screen.getByText("Choose how you decide")).toBeInTheDocument(); expect(screen.getByText("Choose how you decide")).toBeInTheDocument();
expect(screen.getByText("Name your values")).toBeInTheDocument();
expect(screen.getByText("Define membership")).toBeInTheDocument();
expect(screen.getByText("Plan for conflict")).toBeInTheDocument(); expect(screen.getByText("Plan for conflict")).toBeInTheDocument();
expect( expect(
screen.queryByRole("link", { name: "Choose how you decide" }), screen.queryByRole("link", { name: /Choose how you decide/ }),
).not.toBeInTheDocument(); ).not.toBeInTheDocument();
expect(
screen.queryByRole("link", { name: /Plan for conflict/ }),
).not.toBeInTheDocument();
expect(
screen.getByRole("link", { name: /Name your values/ }),
).toHaveAttribute("href", "/how-it-works");
expect(
screen.getByRole("link", { name: /Define membership/ }),
).toHaveAttribute(
"href",
"/blog/integrating-new-members-without-dilution",
);
}); });
it("sends Explore the patterns to the Learn page", () => { it("sends Explore the patterns to the templates page", () => {
render(<FeatureGrid title="Test" subtitle="Test" />); render(<FeatureGrid title="Test" subtitle="Test" />);
expect( expect(
screen.getByRole("link", { name: "Explore the patterns" }), screen.getByRole("link", { name: "Explore the patterns" }),
).toHaveAttribute("href", "/learn"); ).toHaveAttribute("href", "/templates");
}); });
it("does not apply a focus ring to the entire grid shell", () => { it("does not apply a focus ring to the entire grid shell", () => {
+3 -3
View File
@@ -110,7 +110,7 @@ describe("Footer (behavioral tests)", () => {
expect(screen.queryByText(/all right reserved/i)).not.toBeInTheDocument(); expect(screen.queryByText(/all right reserved/i)).not.toBeInTheDocument();
}); });
it("renders legal links to privacy, terms, and cookies pages", () => { it("renders legal links to privacy and terms pages", () => {
render(<Footer />); render(<Footer />);
expect( expect(
screen.getAllByRole("link", { name: "Privacy Policy" })[0], screen.getAllByRole("link", { name: "Privacy Policy" })[0],
@@ -119,7 +119,7 @@ describe("Footer (behavioral tests)", () => {
screen.getAllByRole("link", { name: "Terms of Service" })[0], screen.getAllByRole("link", { name: "Terms of Service" })[0],
).toHaveAttribute("href", "/terms"); ).toHaveAttribute("href", "/terms");
expect( expect(
screen.getAllByRole("link", { name: "Cookies Settings" })[0], screen.queryByRole("link", { name: "Cookies Settings" }),
).toHaveAttribute("href", "/cookies"); ).not.toBeInTheDocument();
}); });
}); });
+4 -4
View File
@@ -37,7 +37,7 @@ describe("Upload (behavioral tests)", () => {
render(<Upload label="Upload" />); render(<Upload label="Upload" />);
const button = screen.getByRole("button", { name: /upload/i }); const button = screen.getByRole("button", { name: /upload/i });
expect(button).toHaveClass( expect(button).toHaveClass(
"bg-[var(--color-surface-invert-primary,white)]", "bg-[var(--color-surface-invert-primary)]",
); );
}); });
@@ -45,7 +45,7 @@ describe("Upload (behavioral tests)", () => {
render(<Upload label="Upload" active={false} />); render(<Upload label="Upload" active={false} />);
const button = screen.getByRole("button", { name: /upload/i }); const button = screen.getByRole("button", { name: /upload/i });
expect(button).toHaveClass( expect(button).toHaveClass(
"bg-[var(--color-surface-default-secondary,#141414)]", "bg-[var(--color-surface-default-secondary)]",
); );
}); });
@@ -95,7 +95,7 @@ describe("Upload (behavioral tests)", () => {
); );
const descriptionContainer = descriptionText.parentElement; const descriptionContainer = descriptionText.parentElement;
expect(descriptionContainer).toHaveClass( expect(descriptionContainer).toHaveClass(
"text-[color:var(--color-content-default-primary,white)]", "text-[color:var(--color-content-default-primary)]",
); );
}); });
@@ -106,7 +106,7 @@ describe("Upload (behavioral tests)", () => {
); );
const descriptionContainer = descriptionText.parentElement; const descriptionContainer = descriptionText.parentElement;
expect(descriptionContainer).toHaveClass( expect(descriptionContainer).toHaveClass(
"text-[color:var(--color-content-default-tertiary,#b4b4b4)]", "text-[color:var(--color-content-default-tertiary)]",
); );
}); });
}); });
+13 -2
View File
@@ -129,13 +129,24 @@ test.describe("Critical User Journeys", () => {
featureSection.locator("text=Plan for conflict"), featureSection.locator("text=Plan for conflict"),
).toBeVisible(); ).toBeVisible();
// Feature tiles are presentational — only the lockup "Explore the patterns" is a link
await expect( await expect(
featureSection.locator('a[href="#decision-making"]'), featureSection.locator('a[href="#decision-making"]'),
).toHaveCount(0); ).toHaveCount(0);
await expect(
featureSection.getByRole("link", { name: "Choose how you decide" }),
).toHaveCount(0);
await expect(
featureSection.getByRole("link", { name: "Plan for conflict" }),
).toHaveCount(0);
await expect(
featureSection.getByRole("link", { name: "Name your values" }),
).toHaveAttribute("href", "/how-it-works");
await expect(
featureSection.getByRole("link", { name: "Define membership" }),
).toHaveAttribute("href", "/blog/integrating-new-members-without-dilution");
await expect( await expect(
featureSection.getByRole("link", { name: "Explore the patterns" }), featureSection.getByRole("link", { name: "Explore the patterns" }),
).toHaveAttribute("href", "/learn"); ).toHaveAttribute("href", "/templates");
}); });
test("header navigation functionality", async ({ page }) => { test("header navigation functionality", async ({ page }) => {
+1 -1
View File
@@ -123,7 +123,7 @@ describe("Page", () => {
const learnMore = screen.getAllByRole("link", { const learnMore = screen.getAllByRole("link", {
name: "Explore the patterns", name: "Explore the patterns",
}); });
expect(learnMore[0]).toHaveAttribute("href", "/learn"); expect(learnMore[0]).toHaveAttribute("href", "/templates");
}); });
test("renders ask organizer section with correct data", () => { test("renders ask organizer section with correct data", () => {
+9 -2
View File
@@ -12,11 +12,18 @@ function decl(name: string): string {
describe("Figma tokens", () => { describe("Figma tokens", () => {
it("points theme colors, radius, and focus at Figma", () => { it("points theme colors, radius, and focus at Figma", () => {
expect(decl("--radius-500")).toBe("24px"); expect(decl("--radius-500")).toBe("24px");
expect(decl("--color-surface-default-brand-royal")).toContain("royal-blue600"); expect(decl("--color-surface-default-brand-royal")).toContain("royal-blue700");
expect(decl("--color-surface-default-brand-kiwi")).toContain("kiwi800"); expect(decl("--color-surface-default-brand-kiwi")).toContain("kiwi800");
expect(decl("--color-content-default-brand-secondary")).toContain("yellow200"); expect(decl("--color-content-default-brand-secondary")).toContain("yellow200");
expect(decl("--color-content-invert-brand-secondary")).toContain("yellow800"); expect(decl("--color-content-invert-brand-secondary")).toContain("yellow800");
expect(decl("--color-content-invert-brand-royal")).toContain("royal-blue400"); expect(decl("--color-content-invert-brand-royal")).toContain("royal-blue700");
expect(decl("--color-content-invert-brand-navy")).toContain("royal-blue1000");
expect(decl("--color-content-invert-quaternary")).toContain("gray-500");
expect(decl("--color-content-invert-negative-primary")).toContain("red600");
expect(decl("--color-gray-150")).toBe("#d2d2d2");
expect(decl("--color-gray-500")).toBe("#717171");
expect(decl("--color-royal-blue-royal-blue700")).toBe("#233bb6");
expect(decl("--color-red-red600")).toBe("#b62e1a");
expect(decl("--color-content-default-utility-negative")).toContain("red300"); expect(decl("--color-content-default-utility-negative")).toContain("red300");
expect(decl("--color-content-default-negative-primary")).toContain( expect(decl("--color-content-default-negative-primary")).toContain(
"utility-negative", "utility-negative",