Match the 3 Nov ramps and leftover launch QA #95
No files matched your search
@@ -5,6 +5,7 @@ import MonitorPageContent from "./MonitorPageContent";
|
||||
|
||||
export const metadata: Metadata = routeMetadata("/monitor", {
|
||||
title: messages.metadata.monitor.title,
|
||||
description: messages.metadata.monitor.description,
|
||||
});
|
||||
|
||||
export default function MonitorPage() {
|
||||
|
||||
@@ -4,6 +4,7 @@ import { NO_INDEX_ROBOTS, routeMetadata } from "../../../lib/siteMetadata";
|
||||
|
||||
export const metadata: Metadata = routeMetadata("/login", {
|
||||
title: messages.metadata.login.title,
|
||||
description: messages.metadata.login.description,
|
||||
robots: NO_INDEX_ROBOTS,
|
||||
});
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import ProfilePageClient from "./ProfilePageClient";
|
||||
|
||||
export const metadata: Metadata = routeMetadata("/profile", {
|
||||
title: messages.metadata.profile.title,
|
||||
description: messages.metadata.profile.description,
|
||||
robots: NO_INDEX_ROBOTS,
|
||||
});
|
||||
|
||||
|
||||
@@ -112,6 +112,14 @@ const Button = memo<ButtonProps>(
|
||||
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> = {
|
||||
xsmall: "text-xx-small-label",
|
||||
small: "text-x-small-label",
|
||||
@@ -122,21 +130,21 @@ const Button = memo<ButtonProps>(
|
||||
|
||||
const variantStyles: Record<string, string> = {
|
||||
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":
|
||||
"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:
|
||||
"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":
|
||||
"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:
|
||||
"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":
|
||||
"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:
|
||||
"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":
|
||||
"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
|
||||
@@ -144,7 +152,7 @@ const Button = memo<ButtonProps>(
|
||||
// and cards: color, opacity, or shadow on hover; scale only on press.
|
||||
// State prop is informational for Figma alignment; CSS pseudo-classes own
|
||||
// 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 sharedA11y = {
|
||||
|
||||
@@ -21,7 +21,7 @@ function StatView({
|
||||
|
||||
return (
|
||||
<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 items-center">
|
||||
@@ -29,16 +29,16 @@ function StatView({
|
||||
variant={shapeVariant}
|
||||
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}
|
||||
</p>
|
||||
</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}
|
||||
</p>
|
||||
</div>
|
||||
{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 ? (
|
||||
<>
|
||||
{asOfPrefix ? <span>{`${asOfPrefix} `}</span> : null}
|
||||
|
||||
@@ -33,10 +33,10 @@ const ContentContainerContainer = memo<ContentContainerProps>(
|
||||
const onLight = tone === "onLight";
|
||||
const titleColor = onLight
|
||||
? "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
|
||||
? "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 resolvedSlug =
|
||||
|
||||
@@ -56,7 +56,7 @@ const CheckboxContainer = memo<CheckboxProps>(
|
||||
// Default state: tertiary border, with hover and focus states via CSS
|
||||
// Hover changes border to brand primary color
|
||||
// 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
|
||||
@@ -64,7 +64,7 @@ const CheckboxContainer = memo<CheckboxProps>(
|
||||
// Inverse: transparent background, white border
|
||||
// Hover changes border to brand primary color
|
||||
// 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;
|
||||
@@ -75,8 +75,8 @@ const CheckboxContainer = memo<CheckboxProps>(
|
||||
// Checkmark color per Figma
|
||||
const checkGlyphColor = checked
|
||||
? isStandard
|
||||
? "var(--color-content-default-brand-primary, #fefcc9)" // Light yellow/cream for standard mode
|
||||
: "var(--color-content-invert-primary, #000000)" // Black for inverse mode
|
||||
? "var(--color-content-default-brand-primary)"
|
||||
: "var(--color-content-invert-primary)"
|
||||
: "transparent";
|
||||
|
||||
// Label color
|
||||
|
||||
@@ -8,7 +8,7 @@ function chipSurfaceStyle(
|
||||
palette: ChipViewProps["palette"],
|
||||
size: ChipViewProps["size"],
|
||||
): CSSProperties {
|
||||
const borderWidth = size === "s" ? "1.25px" : "2px";
|
||||
const borderWidth = size === "s" ? "1.25px" : "1.5px";
|
||||
const reset: CSSProperties = {
|
||||
appearance: "none",
|
||||
WebkitAppearance: "none",
|
||||
@@ -64,14 +64,14 @@ function chipSurfaceStyle(
|
||||
}
|
||||
if (state === "selected") {
|
||||
return paint(
|
||||
"var(--color-surface-invert-brand-primary, #fefcc9)",
|
||||
"var(--color-content-invert-primary, #000)",
|
||||
"var(--color-border-default-brand-primary, #fdfaa8)",
|
||||
"var(--color-surface-invert-brand-primary)",
|
||||
"var(--color-content-invert-primary)",
|
||||
"var(--color-border-default-brand-primary)",
|
||||
);
|
||||
}
|
||||
return paint(
|
||||
"transparent",
|
||||
"var(--color-content-default-brand-primary, #fefcc9)",
|
||||
"var(--color-content-default-brand-primary)",
|
||||
"var(--color-border-default-control)",
|
||||
);
|
||||
}
|
||||
@@ -185,7 +185,7 @@ function ChipView({
|
||||
{onCheck && (
|
||||
<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}
|
||||
disabled={!inputValue || !inputValue.trim()}
|
||||
onClick={(event) => {
|
||||
@@ -202,7 +202,7 @@ function ChipView({
|
||||
viewBox="0 0 12 12"
|
||||
fill="none"
|
||||
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
|
||||
d="M10 3L4.5 8.5L2 6"
|
||||
@@ -224,7 +224,7 @@ function ChipView({
|
||||
onChange={(e) => onInputChange?.(e.target.value)}
|
||||
onKeyDown={onInputKeyDown}
|
||||
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={{
|
||||
fontSize: isSmall
|
||||
? "var(--sizing-300)"
|
||||
@@ -240,7 +240,7 @@ function ChipView({
|
||||
{onClose && (
|
||||
<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}
|
||||
onClick={(event) => {
|
||||
event.stopPropagation();
|
||||
@@ -253,7 +253,7 @@ function ChipView({
|
||||
viewBox="0 0 12 12"
|
||||
fill="none"
|
||||
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
|
||||
d="M9 3L3 9M3 3L9 9"
|
||||
@@ -309,7 +309,7 @@ function ChipView({
|
||||
{onRemove && !isDisabled && (
|
||||
<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}`}
|
||||
onClick={(event) => {
|
||||
event.stopPropagation();
|
||||
|
||||
@@ -59,7 +59,7 @@ function MultiSelectView({
|
||||
|
||||
{!formHeader || !label ? (
|
||||
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}
|
||||
</p>
|
||||
) : null
|
||||
@@ -133,7 +133,7 @@ function MultiSelectView({
|
||||
className={
|
||||
!addButtonText
|
||||
? // 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)
|
||||
`${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
|
||||
@@ -151,8 +151,8 @@ function MultiSelectView({
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className={`shrink-0 ${
|
||||
!addButtonText && isInverse
|
||||
? "text-[var(--color-content-invert-primary,black)]"
|
||||
: "text-[var(--color-content-default-brand-primary,#fefcc9)]"
|
||||
? "text-[var(--color-content-invert-primary)]"
|
||||
: "text-[var(--color-content-default-brand-primary)]"
|
||||
}`}
|
||||
>
|
||||
<path
|
||||
@@ -167,8 +167,8 @@ function MultiSelectView({
|
||||
<span
|
||||
className={`font-medium ${isSmall ? "text-[length:var(--sizing-300)] leading-[14px]" : "text-medium-label"} ${
|
||||
isInverse
|
||||
? "text-[color:var(--color-content-invert-primary,black)]"
|
||||
: "text-[color:var(--color-content-default-primary,white)]"
|
||||
? "text-[color:var(--color-content-invert-primary)]"
|
||||
: "text-[color:var(--color-content-default-primary)]"
|
||||
}`}
|
||||
>
|
||||
{addButtonText}
|
||||
|
||||
@@ -53,15 +53,15 @@ const RadioButtonContainer = ({
|
||||
// Focus shows shadow (double ring: 2px white inner, 4px dark outer)
|
||||
// When checked, border is brand primary (but changes to invert tertiary on focus)
|
||||
const defaultBorder = checked
|
||||
? "border-[var(--color-border-default-brand-primary,#fdfaa8)]"
|
||||
? "border-[var(--color-border-default-brand-primary)]"
|
||||
: "border-[var(--color-border-default-control)]";
|
||||
|
||||
// When focused and checked, border should be invert tertiary (#2d2d2d) per Figma
|
||||
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)]";
|
||||
|
||||
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
|
||||
@@ -71,17 +71,17 @@ const RadioButtonContainer = ({
|
||||
// Focus shows shadow (double ring: 2px dark inner, 4px white outer)
|
||||
// When checked, border is brand primary (but changes to white on focus)
|
||||
const defaultBorder = checked
|
||||
? "border-[var(--color-border-default-brand-primary,#fdfaa8)]"
|
||||
: "border-[var(--color-border-invert-primary,white)]";
|
||||
? "border-[var(--color-border-default-brand-primary)]"
|
||||
: "border-[var(--color-border-invert-primary)]";
|
||||
|
||||
// Hover border: inverse brand primary for both selected and unselected per Figma
|
||||
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
|
||||
const focusBorder = checked
|
||||
? "focus:border-[var(--color-border-invert-primary,white)]"
|
||||
: "focus:border-[var(--color-border-invert-primary,white)]";
|
||||
? "focus:border-[var(--color-border-invert-primary)]"
|
||||
: "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`;
|
||||
}
|
||||
|
||||
@@ -39,9 +39,9 @@ export function RadioButtonView({
|
||||
<div
|
||||
className={`w-[16px] h-[16px] rounded-full transition-all duration-200 ${
|
||||
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"
|
||||
? "bg-[var(--color-content-default-primary,#000000)]"
|
||||
? "bg-[var(--color-content-default-primary)]"
|
||||
: "bg-transparent"
|
||||
}`}
|
||||
/>
|
||||
|
||||
@@ -52,8 +52,8 @@ export function RadioGroupView({
|
||||
<span
|
||||
className={`text-small-paragraph ${
|
||||
mode === "inverse"
|
||||
? "text-[var(--color-content-invert-secondary,#1f1f1f)]"
|
||||
: "text-[var(--color-content-default-secondary,#b4b4b4)]"
|
||||
? "text-[var(--color-content-invert-secondary)]"
|
||||
: "text-[var(--color-content-default-secondary)]"
|
||||
}`}
|
||||
>
|
||||
{option.subtext}
|
||||
|
||||
@@ -109,7 +109,7 @@ export function SelectInputView({
|
||||
.replace(/\s+/g, " ");
|
||||
|
||||
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) {
|
||||
@@ -135,7 +135,7 @@ export function SelectInputView({
|
||||
// Text color based on filled state
|
||||
const textColorClass = isFilled
|
||||
? "text-[var(--color-content-default-primary)]"
|
||||
: "text-[var(--color-content-default-tertiary,#b4b4b4)]";
|
||||
: "text-[var(--color-content-default-tertiary)]";
|
||||
|
||||
// Chevron icon
|
||||
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>
|
||||
{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>
|
||||
)}
|
||||
@@ -167,7 +167,7 @@ export function SelectInputView({
|
||||
)}
|
||||
</div>
|
||||
{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
|
||||
</span>
|
||||
)}
|
||||
|
||||
@@ -102,7 +102,7 @@ const TextInputContainer = forwardRef<HTMLInputElement, TextInputProps>(
|
||||
if (disabled) {
|
||||
return {
|
||||
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)]",
|
||||
inputWrapper: "relative",
|
||||
focusRing: "",
|
||||
@@ -171,7 +171,7 @@ const TextInputContainer = forwardRef<HTMLInputElement, TextInputProps>(
|
||||
const inputClasses = `
|
||||
w-full transition-all duration-200 ease-in-out
|
||||
focus:outline-none focus:ring-0
|
||||
placeholder:text-[var(--color-content-default-tertiary,#b4b4b4)]
|
||||
placeholder:text-[var(--color-content-default-tertiary)]
|
||||
${sizeStyles.input}
|
||||
${stateStyles.input}
|
||||
${className}
|
||||
@@ -180,7 +180,7 @@ const TextInputContainer = forwardRef<HTMLInputElement, TextInputProps>(
|
||||
// Text color for filled text (placeholder color is handled above)
|
||||
const textColorClass = isFilled
|
||||
? "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
|
||||
const { handleChange, handleBlur } = useFormField<HTMLInputElement>(
|
||||
|
||||
@@ -20,22 +20,22 @@ function UploadView({
|
||||
|
||||
// Button styles based on active state
|
||||
const buttonBgClass = isActive
|
||||
? "bg-[var(--color-surface-invert-primary,white)]"
|
||||
: "bg-[var(--color-surface-default-secondary,#141414)]";
|
||||
? "bg-[var(--color-surface-invert-primary)]"
|
||||
: "bg-[var(--color-surface-default-secondary)]";
|
||||
|
||||
const buttonTextColor = isActive
|
||||
? "text-[color:var(--color-content-invert-primary,black)]"
|
||||
: "text-[color:var(--color-content-invert-tertiary,#2d2d2d)]";
|
||||
? "text-[color:var(--color-content-invert-primary)]"
|
||||
: "text-[color:var(--color-content-invert-tertiary)]";
|
||||
|
||||
// Description text color based on active state
|
||||
const descriptionTextColor = isActive
|
||||
? "text-[color:var(--color-content-default-primary,white)]"
|
||||
: "text-[color:var(--color-content-default-tertiary,#b4b4b4)]";
|
||||
? "text-[color:var(--color-content-default-primary)]"
|
||||
: "text-[color:var(--color-content-default-tertiary)]";
|
||||
|
||||
// Icon color based on active state
|
||||
const iconColor = isActive
|
||||
? "text-[color:var(--color-content-invert-primary,black)]"
|
||||
: "text-[color:var(--color-content-invert-tertiary,#2d2d2d)]";
|
||||
? "text-[color:var(--color-content-invert-primary)]"
|
||||
: "text-[color:var(--color-content-invert-tertiary)]";
|
||||
|
||||
return (
|
||||
<div
|
||||
@@ -55,7 +55,7 @@ function UploadView({
|
||||
|
||||
{/* Upload container — max width per create-flow spec */}
|
||||
<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"
|
||||
? "border border-[var(--color-border-default-utility-negative)]"
|
||||
: ""
|
||||
|
||||
@@ -216,9 +216,6 @@ const Footer = memo(({ siteOrigin = PRODUCTION_SITE_ORIGIN }: { siteOrigin?: str
|
||||
<Link href={t("legal.termsOfServiceHref")} className={legalLinkClass}>
|
||||
{t("legal.termsOfService")}
|
||||
</Link>
|
||||
<Link href={t("legal.cookiesSettingsHref")} className={legalLinkClass}>
|
||||
{t("legal.cookiesSettings")}
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -63,22 +63,22 @@ const MenuItemContainer = memo<MenuItemProps>(
|
||||
const defaultModeStyles: Record<"default" | "hover" | "selected", string> =
|
||||
{
|
||||
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:
|
||||
"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:
|
||||
"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)
|
||||
const inverseModeStyles: Record<"default" | "hover" | "selected", string> =
|
||||
{
|
||||
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:
|
||||
"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:
|
||||
"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
|
||||
|
||||
@@ -33,6 +33,7 @@ const FeatureGridContainer = memo<FeatureGridProps>(
|
||||
),
|
||||
descriptionClassName: "text-[var(--color-content-invert-brand-lime)]",
|
||||
iconSrc: getAssetPath(featureIconPath("arrow-left-right")),
|
||||
href: "/how-it-works",
|
||||
},
|
||||
{
|
||||
backgroundColor: "bg-[var(--color-surface-invert-brand-rust)]",
|
||||
@@ -42,6 +43,7 @@ const FeatureGridContainer = memo<FeatureGridProps>(
|
||||
),
|
||||
descriptionClassName: "text-[var(--color-content-invert-brand-rust)]",
|
||||
iconSrc: getAssetPath(featureIconPath("door-open")),
|
||||
href: "/blog/integrating-new-members-without-dilution",
|
||||
},
|
||||
{
|
||||
backgroundColor: "bg-[var(--color-surface-invert-brand-teal)]",
|
||||
|
||||
@@ -10,6 +10,7 @@ export interface Feature {
|
||||
description: string;
|
||||
descriptionClassName: string;
|
||||
iconSrc: string;
|
||||
href?: string;
|
||||
}
|
||||
|
||||
export interface FeatureGridViewProps extends FeatureGridProps {
|
||||
|
||||
@@ -1,8 +1,35 @@
|
||||
"use client";
|
||||
|
||||
import Link from "next/link";
|
||||
import { useTranslation } from "../../../contexts/MessagesContext";
|
||||
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). */
|
||||
function FeatureGridView({
|
||||
@@ -41,34 +68,25 @@ function FeatureGridView({
|
||||
</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">
|
||||
{features.map((feature) => (
|
||||
<div
|
||||
{features.map((feature) => {
|
||||
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}
|
||||
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">
|
||||
{/* 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"
|
||||
/>
|
||||
<FeatureTileBody feature={feature} />
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<div key={feature.title} className={tileClassName}>
|
||||
<FeatureTileBody feature={feature} />
|
||||
</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>
|
||||
|
||||
@@ -75,7 +75,7 @@ function QuoteBlockView({
|
||||
role="region"
|
||||
>
|
||||
<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 />
|
||||
<div className="relative z-10 flex w-full min-w-0 shrink-0 flex-col items-center text-center">
|
||||
|
||||
+4
-3
@@ -2,16 +2,17 @@ import type { Metadata } from "next";
|
||||
import Link from "next/link";
|
||||
import messages from "../messages/en/index";
|
||||
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 Icon from "./components/asset/icon";
|
||||
import Button from "./components/buttons/Button";
|
||||
import HeroDecor from "./components/sections/HeroBanner/HeroDecor";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
export const metadata: Metadata = routeMetadata("/404", {
|
||||
title: messages.metadata.notFound.title,
|
||||
description: messages.metadata.notFound.description,
|
||||
robots: NO_INDEX_ROBOTS,
|
||||
};
|
||||
});
|
||||
|
||||
const NOT_FOUND_TEMPLATE_SLUGS = [
|
||||
"consensus",
|
||||
|
||||
+83
-80
@@ -202,14 +202,15 @@
|
||||
/* Grays & alpha */
|
||||
--color-gray-000: #ffffff;
|
||||
--color-gray-050: #f6f6f6;
|
||||
--color-gray-100: #e5e5e5;
|
||||
--color-gray-200: #d2d2d2;
|
||||
--color-gray-300: #b4b4b4;
|
||||
--color-gray-400: #8f8f8f;
|
||||
--color-gray-500: #666666;
|
||||
--color-gray-600: #464646;
|
||||
--color-gray-700: #2d2d2d;
|
||||
--color-gray-800: #1f1f1f;
|
||||
--color-gray-100: #e4e4e4;
|
||||
--color-gray-150: #d2d2d2;
|
||||
--color-gray-200: #c2c2c2;
|
||||
--color-gray-300: #a4a4a4;
|
||||
--color-gray-400: #8a8a8a;
|
||||
--color-gray-500: #717171;
|
||||
--color-gray-600: #5b5b5b;
|
||||
--color-gray-700: #464646;
|
||||
--color-gray-800: #303030;
|
||||
--color-gray-900: #141414;
|
||||
--color-gray-1000: #000000;
|
||||
|
||||
@@ -244,16 +245,16 @@
|
||||
/* Lavender */
|
||||
--color-lavender-lavender0: #f0ebff;
|
||||
--color-lavender-lavender50: #e8defe;
|
||||
--color-lavender-lavender100: #e0d1fd;
|
||||
--color-lavender-lavender150: #d7c3f9;
|
||||
--color-lavender-lavender200: #ceb5f6;
|
||||
--color-lavender-lavender300: #bb97ea;
|
||||
--color-lavender-lavender400: #a578d9;
|
||||
--color-lavender-lavender500: #8d5ac4;
|
||||
--color-lavender-lavender600: #743ea9;
|
||||
--color-lavender-lavender700: #5a278c;
|
||||
--color-lavender-lavender800: #41146c;
|
||||
--color-lavender-lavender900: #2b074d;
|
||||
--color-lavender-lavender100: #dccdf9;
|
||||
--color-lavender-lavender150: #d1bdf3;
|
||||
--color-lavender-lavender200: #c6adee;
|
||||
--color-lavender-lavender300: #b490e2;
|
||||
--color-lavender-lavender400: #a275d5;
|
||||
--color-lavender-lavender500: #8f5cc6;
|
||||
--color-lavender-lavender600: #7c47b2;
|
||||
--color-lavender-lavender700: #67359b;
|
||||
--color-lavender-lavender800: #50277e;
|
||||
--color-lavender-lavender900: #38185c;
|
||||
--color-lavender-lavender1000: #1a0033;
|
||||
|
||||
/* Lime */
|
||||
@@ -274,61 +275,61 @@
|
||||
/* Magenta */
|
||||
--color-magenta-magenta0: #ffebf0;
|
||||
--color-magenta-magenta50: #fedee7;
|
||||
--color-magenta-magenta100: #fdd1dd;
|
||||
--color-magenta-magenta150: #f9c3d2;
|
||||
--color-magenta-magenta200: #f6b5c7;
|
||||
--color-magenta-magenta300: #ea97ad;
|
||||
--color-magenta-magenta400: #d97892;
|
||||
--color-magenta-magenta500: #c45a76;
|
||||
--color-magenta-magenta600: #a93e5b;
|
||||
--color-magenta-magenta700: #8c2742;
|
||||
--color-magenta-magenta800: #6c142b;
|
||||
--color-magenta-magenta900: #4d071a;
|
||||
--color-magenta-magenta100: #f8ccd8;
|
||||
--color-magenta-magenta150: #f0bbca;
|
||||
--color-magenta-magenta200: #eaaabc;
|
||||
--color-magenta-magenta300: #dd8ba1;
|
||||
--color-magenta-magenta400: #ce6f89;
|
||||
--color-magenta-magenta500: #be5571;
|
||||
--color-magenta-magenta600: #aa3f5b;
|
||||
--color-magenta-magenta700: #922d47;
|
||||
--color-magenta-magenta800: #761f33;
|
||||
--color-magenta-magenta900: #571222;
|
||||
--color-magenta-magenta1000: #33000e;
|
||||
|
||||
/* Red */
|
||||
--color-red-red0: #ffebf1;
|
||||
--color-red-red50: #fec9d6;
|
||||
--color-red-red100: #fda8b7;
|
||||
--color-red-red150: #f98c98;
|
||||
--color-red-red200: #f66f78;
|
||||
--color-red-red300: #ea4845;
|
||||
--color-red-red400: #d93529;
|
||||
--color-red-red500: #c42916;
|
||||
--color-red-red600: #a9200b;
|
||||
--color-red-red700: #8c1905;
|
||||
--color-red-red800: #6c1301;
|
||||
--color-red-red900: #4d0d00;
|
||||
--color-red-red100: #ffb6c2;
|
||||
--color-red-red150: #ffa4ac;
|
||||
--color-red-red200: #ff9296;
|
||||
--color-red-red300: #ff6a62;
|
||||
--color-red-red400: #ef4c3d;
|
||||
--color-red-red500: #d33a26;
|
||||
--color-red-red600: #b62e1a;
|
||||
--color-red-red700: #972613;
|
||||
--color-red-red800: #771f0d;
|
||||
--color-red-red900: #571707;
|
||||
--color-red-red1000: #330800;
|
||||
|
||||
/* Royal Blue */
|
||||
--color-royal-blue-royal-blue0: #ebecff;
|
||||
--color-royal-blue-royal-blue50: #c9cdfe;
|
||||
--color-royal-blue-royal-blue100: #a8adfd;
|
||||
--color-royal-blue-royal-blue150: #8c91f9;
|
||||
--color-royal-blue-royal-blue200: #6f74f6;
|
||||
--color-royal-blue-royal-blue300: #4548ea;
|
||||
--color-royal-blue-royal-blue400: #2b29d9;
|
||||
--color-royal-blue-royal-blue500: #1c16c4;
|
||||
--color-royal-blue-royal-blue600: #140ba9;
|
||||
--color-royal-blue-royal-blue700: #0f058c;
|
||||
--color-royal-blue-royal-blue800: #0c016c;
|
||||
--color-royal-blue-royal-blue900: #0a004d;
|
||||
--color-royal-blue-royal-blue100: #b9bfff;
|
||||
--color-royal-blue-royal-blue150: #aab1ff;
|
||||
--color-royal-blue-royal-blue200: #99a4ff;
|
||||
--color-royal-blue-royal-blue300: #778cff;
|
||||
--color-royal-blue-royal-blue400: #5674ff;
|
||||
--color-royal-blue-royal-blue500: #3a5afe;
|
||||
--color-royal-blue-royal-blue600: #2d4adb;
|
||||
--color-royal-blue-royal-blue700: #233bb6;
|
||||
--color-royal-blue-royal-blue800: #1d2c8f;
|
||||
--color-royal-blue-royal-blue900: #171b65;
|
||||
--color-royal-blue-royal-blue1000: #080033;
|
||||
|
||||
/* Rust */
|
||||
--color-rust-rust0: #ffedeb;
|
||||
--color-rust-rust50: #fed8d4;
|
||||
--color-rust-rust100: #fdc4bd;
|
||||
--color-rust-rust150: #f9b0a6;
|
||||
--color-rust-rust200: #f69c8f;
|
||||
--color-rust-rust300: #ea7a65;
|
||||
--color-rust-rust400: #d96043;
|
||||
--color-rust-rust500: #c44e29;
|
||||
--color-rust-rust600: #a94417;
|
||||
--color-rust-rust700: #8c3d0b;
|
||||
--color-rust-rust800: #6c3604;
|
||||
--color-rust-rust900: #4d2c01;
|
||||
--color-rust-rust100: #ffc6bf;
|
||||
--color-rust-rust150: #feb4aa;
|
||||
--color-rust-rust200: #fda395;
|
||||
--color-rust-rust300: #f4836e;
|
||||
--color-rust-rust400: #e46a4c;
|
||||
--color-rust-rust500: #cd5632;
|
||||
--color-rust-rust600: #b04a1f;
|
||||
--color-rust-rust700: #904111;
|
||||
--color-rust-rust800: #703909;
|
||||
--color-rust-rust900: #512f05;
|
||||
--color-rust-rust1000: #332100;
|
||||
|
||||
/* Teal */
|
||||
@@ -392,25 +393,25 @@
|
||||
--color-border-invert-warning-primary: var(--color-yellow-yellow600);
|
||||
--color-border-invert-negative-primary: var(--color-red-red300);
|
||||
|
||||
/* Link — nearest palette step for each off-ramp hex */
|
||||
--color-link-primary: var(--color-gray-1000);
|
||||
--color-link-primary-hover: var(--color-gray-500);
|
||||
--color-link-primary-focus: var(--color-gray-500);
|
||||
--color-link-primary-active: var(--color-gray-500);
|
||||
--color-link-secondary: var(--color-gray-500);
|
||||
--color-link-secondary-hover: var(--color-gray-700);
|
||||
--color-link-secondary-focus: var(--color-gray-700);
|
||||
--color-link-secondary-active: var(--color-gray-700);
|
||||
--color-link-invert-primary: var(--color-gray-000);
|
||||
--color-link-invert-primary-hover: var(--color-gray-300);
|
||||
--color-link-invert-primary-focus: var(--color-gray-300);
|
||||
--color-link-invert-primary-active: var(--color-gray-300);
|
||||
--color-link-invert-secondary: var(--color-gray-300);
|
||||
--color-link-invert-secondary-hover: var(--color-gray-050);
|
||||
--color-link-invert-secondary-focus: var(--color-gray-050);
|
||||
--color-link-invert-secondary-active: var(--color-gray-050);
|
||||
--color-border-link-focus: var(--color-gray-1000);
|
||||
--color-border-link-invert-focus: var(--color-gray-000);
|
||||
/* Link — Navigation / Link binds content tokens */
|
||||
--color-link-primary: var(--color-content-invert-primary);
|
||||
--color-link-primary-hover: var(--color-content-invert-quaternary);
|
||||
--color-link-primary-focus: var(--color-content-invert-quaternary);
|
||||
--color-link-primary-active: var(--color-content-invert-quaternary);
|
||||
--color-link-secondary: var(--color-content-invert-quaternary);
|
||||
--color-link-secondary-hover: var(--color-content-invert-tertiary);
|
||||
--color-link-secondary-focus: var(--color-content-invert-tertiary);
|
||||
--color-link-secondary-active: var(--color-content-invert-tertiary);
|
||||
--color-link-invert-primary: var(--color-content-default-primary);
|
||||
--color-link-invert-primary-hover: var(--color-content-default-tertiary);
|
||||
--color-link-invert-primary-focus: var(--color-content-default-tertiary);
|
||||
--color-link-invert-primary-active: var(--color-content-default-tertiary);
|
||||
--color-link-invert-secondary: var(--color-content-default-tertiary);
|
||||
--color-link-invert-secondary-hover: var(--color-content-default-primary);
|
||||
--color-link-invert-secondary-focus: var(--color-content-default-primary);
|
||||
--color-link-invert-secondary-active: var(--color-content-default-primary);
|
||||
--color-border-link-focus: var(--color-border-default-primary);
|
||||
--color-border-link-invert-focus: var(--color-border-invert-primary);
|
||||
|
||||
/* Content */
|
||||
--color-content-default-brand-accent: var(--color-opacity-700);
|
||||
@@ -443,10 +444,12 @@
|
||||
--color-content-invert-primary: var(--color-gray-1000);
|
||||
--color-content-invert-secondary: var(--color-gray-800);
|
||||
--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-secondary: var(--color-yellow-yellow800);
|
||||
--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-rust: var(--color-rust-rust700);
|
||||
--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-positive-primary: var(--color-kiwi-kiwi800);
|
||||
--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 */
|
||||
--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-primary: var(--color-yellow-yellow1000);
|
||||
--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-teal: var(--color-teal-teal700);
|
||||
--color-surface-default-positive-secondary: var(--color-kiwi-kiwi1000);
|
||||
|
||||
@@ -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",
|
||||
"linkHref": "/learn",
|
||||
"linkHref": "/templates",
|
||||
"ariaLabel": "Feature tools and services",
|
||||
"features": {
|
||||
"decisionMaking": {
|
||||
|
||||
@@ -31,9 +31,7 @@
|
||||
"privacyPolicy": "Privacy Policy",
|
||||
"privacyPolicyHref": "/privacy",
|
||||
"termsOfService": "Terms of Service",
|
||||
"termsOfServiceHref": "/terms",
|
||||
"cookiesSettings": "Cookies Settings",
|
||||
"cookiesSettingsHref": "/cookies"
|
||||
"termsOfServiceHref": "/terms"
|
||||
},
|
||||
"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.",
|
||||
|
||||
@@ -7,8 +7,8 @@
|
||||
"templatesAlt": "CommunityRule governance templates"
|
||||
},
|
||||
"home": {
|
||||
"title": "Build structure before you need it — CommunityRule",
|
||||
"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.",
|
||||
"title": "CommunityRule | Write down how your community works",
|
||||
"description": "Write down how your community talks, who belongs, how it decides and what happens when it disagrees. Free for every community.",
|
||||
"keywords": [
|
||||
"community",
|
||||
"governance",
|
||||
@@ -47,16 +47,20 @@
|
||||
"fallbackDescription": "A blog post from our community."
|
||||
},
|
||||
"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": {
|
||||
"title": "Profile"
|
||||
"title": "Profile",
|
||||
"description": "Manage your CommunityRules, saved progress, and account."
|
||||
},
|
||||
"monitor": {
|
||||
"title": "Monitor"
|
||||
"title": "Monitor",
|
||||
"description": "Real-time monitoring of Core Web Vitals and performance metrics for Community Rule 3.0"
|
||||
},
|
||||
"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": {
|
||||
"title": "Use cases",
|
||||
|
||||
@@ -47,22 +47,32 @@ describe("FeatureGrid (behavioral tests)", () => {
|
||||
).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" />);
|
||||
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.queryByRole("link", { name: "Choose how you decide" }),
|
||||
screen.queryByRole("link", { name: /Choose how you decide/ }),
|
||||
).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" />);
|
||||
expect(
|
||||
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", () => {
|
||||
|
||||
@@ -110,7 +110,7 @@ describe("Footer (behavioral tests)", () => {
|
||||
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 />);
|
||||
expect(
|
||||
screen.getAllByRole("link", { name: "Privacy Policy" })[0],
|
||||
@@ -119,7 +119,7 @@ describe("Footer (behavioral tests)", () => {
|
||||
screen.getAllByRole("link", { name: "Terms of Service" })[0],
|
||||
).toHaveAttribute("href", "/terms");
|
||||
expect(
|
||||
screen.getAllByRole("link", { name: "Cookies Settings" })[0],
|
||||
).toHaveAttribute("href", "/cookies");
|
||||
screen.queryByRole("link", { name: "Cookies Settings" }),
|
||||
).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
@@ -37,7 +37,7 @@ describe("Upload (behavioral tests)", () => {
|
||||
render(<Upload label="Upload" />);
|
||||
const button = screen.getByRole("button", { name: /upload/i });
|
||||
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} />);
|
||||
const button = screen.getByRole("button", { name: /upload/i });
|
||||
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;
|
||||
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;
|
||||
expect(descriptionContainer).toHaveClass(
|
||||
"text-[color:var(--color-content-default-tertiary,#b4b4b4)]",
|
||||
"text-[color:var(--color-content-default-tertiary)]",
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -129,13 +129,24 @@ test.describe("Critical User Journeys", () => {
|
||||
featureSection.locator("text=Plan for conflict"),
|
||||
).toBeVisible();
|
||||
|
||||
// Feature tiles are presentational — only the lockup "Explore the patterns" is a link
|
||||
await expect(
|
||||
featureSection.locator('a[href="#decision-making"]'),
|
||||
).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(
|
||||
featureSection.getByRole("link", { name: "Explore the patterns" }),
|
||||
).toHaveAttribute("href", "/learn");
|
||||
).toHaveAttribute("href", "/templates");
|
||||
});
|
||||
|
||||
test("header navigation functionality", async ({ page }) => {
|
||||
|
||||
@@ -123,7 +123,7 @@ describe("Page", () => {
|
||||
const learnMore = screen.getAllByRole("link", {
|
||||
name: "Explore the patterns",
|
||||
});
|
||||
expect(learnMore[0]).toHaveAttribute("href", "/learn");
|
||||
expect(learnMore[0]).toHaveAttribute("href", "/templates");
|
||||
});
|
||||
|
||||
test("renders ask organizer section with correct data", () => {
|
||||
|
||||
@@ -12,11 +12,18 @@ function decl(name: string): string {
|
||||
describe("Figma tokens", () => {
|
||||
it("points theme colors, radius, and focus at Figma", () => {
|
||||
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-content-default-brand-secondary")).toContain("yellow200");
|
||||
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-negative-primary")).toContain(
|
||||
"utility-negative",
|
||||
|
||||
Reference in new issue
Block a user