diff --git a/app/(app)/create/CreateFlowLayoutClient.tsx b/app/(app)/create/CreateFlowLayoutClient.tsx index 85e38c4..df73786 100644 --- a/app/(app)/create/CreateFlowLayoutClient.tsx +++ b/app/(app)/create/CreateFlowLayoutClient.tsx @@ -76,7 +76,13 @@ import { CREATE_FLOW_FOOTER_BUTTON_CLASS, CREATE_FLOW_FOOTER_BUTTON_ON_DARK_CLASS, } from "./utils/createFlowFooterClassNames"; -import { CREATE_FLOW_MD_CENTERED_MAIN_CLASS } from "./components/createFlowLayoutTokens"; +import { + CREATE_FLOW_MD_CENTERED_MAIN_CLASS, + CREATE_FLOW_PAGE_GUTTER_CLASS, + CREATE_FLOW_SCROLL_REGION_CLASS, + CREATE_FLOW_VIEWPORT_FRAME_CLASS, + getCreateFlowContentMaxClass, +} from "./components/createFlowLayoutTokens"; import { CUSTOM_RULE_CONFIRM_FOOTER_STEP_BY_STEP, methodCardFacetSectionForConfirmStep, @@ -668,10 +674,10 @@ function CreateFlowLayoutContent({ ].filter((b): b is NonNullable => b !== null); return ( -
+
{topBanners.length > 0 ? (
{topBanners.map((b) => ( @@ -798,13 +804,17 @@ function CreateFlowLayoutContent({ /> ) : null}
{children}
{!isCompletedStep && ( +
+
+
+ +
+
+ {children} +
+
+ + ); +} diff --git a/app/(app)/create/components/CreateFlowStepShell.tsx b/app/(app)/create/components/CreateFlowStepShell.tsx index bf5aab2..a9ca8a2 100644 --- a/app/(app)/create/components/CreateFlowStepShell.tsx +++ b/app/(app)/create/components/CreateFlowStepShell.tsx @@ -1,6 +1,7 @@ "use client"; import type { ReactNode } from "react"; +import { CREATE_FLOW_PAGE_GUTTER_CLASS } from "./createFlowLayoutTokens"; export type CreateFlowStepShellVariant = | "centeredNarrow" @@ -13,15 +14,12 @@ export type CreateFlowStepShellVariant = export type CreateFlowContentTopBelowMd = "none" | "space-1400" | "space-800"; const outerByVariant: Record = { - centeredNarrow: - "flex w-full min-w-0 flex-col items-center px-5 md:px-16", - centeredNarrowBottomPad: - "flex w-full min-w-0 flex-col items-center px-5 pb-28 md:px-[var(--measures-spacing-1800,64px)] md:pb-32", - /** Wide two-column steps; 1328px = two 640px columns + 48px gutter. */ - wideGrid: "w-full min-w-0 max-w-[1328px] shrink-0 px-5 md:px-12", - /** Create Community review + card grid (Figma Flow — Review `19706:12135`): max width 1440. */ - wideGridLoosePadding: - "w-full min-w-0 max-w-[1440px] shrink-0 px-5 md:px-16", + centeredNarrow: `flex w-full min-w-0 flex-col items-center ${CREATE_FLOW_PAGE_GUTTER_CLASS}`, + centeredNarrowBottomPad: `flex w-full min-w-0 flex-col items-center ${CREATE_FLOW_PAGE_GUTTER_CLASS} pb-28 md:pb-32`, + /** Wide two-column steps; inner content supplies the 1328px cap. */ + wideGrid: `flex w-full min-w-0 shrink-0 flex-col items-center ${CREATE_FLOW_PAGE_GUTTER_CLASS}`, + /** Create Community review + card grid; inner content supplies the 1440px cap. */ + wideGridLoosePadding: `flex w-full min-w-0 shrink-0 flex-col items-center ${CREATE_FLOW_PAGE_GUTTER_CLASS}`, bare: "w-full min-w-0", }; @@ -41,7 +39,8 @@ interface CreateFlowStepShellProps { /** * Shared horizontal padding and width constraints for create-flow step pages. - * Horizontal padding uses Tailwind `md:` so it tracks `--breakpoint-md` (640px in `app/tailwind.css`). + * Gutters come from {@link CREATE_FLOW_PAGE_GUTTER_CLASS} (`md` / `lg` track + * `--breakpoint-md` and `--breakpoint-lg` in `app/tailwind.css`). */ export function CreateFlowStepShell({ children, diff --git a/app/(app)/create/components/CreateFlowTwoColumnSelectShell.tsx b/app/(app)/create/components/CreateFlowTwoColumnSelectShell.tsx index c3f8363..b9d2c7b 100644 --- a/app/(app)/create/components/CreateFlowTwoColumnSelectShell.tsx +++ b/app/(app)/create/components/CreateFlowTwoColumnSelectShell.tsx @@ -5,7 +5,11 @@ import { CreateFlowStepShell, type CreateFlowContentTopBelowMd, } from "./CreateFlowStepShell"; -import { CREATE_FLOW_MD_UP_COLUMN_MAX_CLASS } from "./createFlowLayoutTokens"; +import { + CREATE_FLOW_MD_UP_COLUMN_MAX_CLASS, + CREATE_FLOW_SCROLL_REGION_CLASS, + CREATE_FLOW_TWO_COLUMN_MAX_WIDTH_CLASS, +} from "./createFlowLayoutTokens"; export type CreateFlowSelectShellLgVerticalAlign = "center" | "start"; @@ -26,10 +30,10 @@ interface CreateFlowTwoColumnSelectShellProps { } /** - * Two-column layout for create-flow select steps (community size/structure, core values) and - * {@link DecisionApproachesScreen} (decision approaches). Below `lg` (1024px), one column + main scrolls. - * At `lg+`, mirrors {@link CompletedScreen}: static header column + scrollable controls column - * (`min-h-0` + `overflow-y-auto` height chain; see completed page right rail). + * Question-and-options layout for create-flow select steps (community size/structure, + * core values, stakeholders) and {@link DecisionApproachesScreen}. + * Below `lg` (1024px), one column uses available width and main scrolls. + * At `lg+`, static header column + scrollable options column (`min-h-0` + overflow). */ export function CreateFlowTwoColumnSelectShell({ header, @@ -56,24 +60,24 @@ export function CreateFlowTwoColumnSelectShell({ >
{header}
{children} diff --git a/app/(app)/create/components/createFlowLayoutTokens.ts b/app/(app)/create/components/createFlowLayoutTokens.ts index ef372a7..0b0f3f6 100644 --- a/app/(app)/create/components/createFlowLayoutTokens.ts +++ b/app/(app)/create/components/createFlowLayoutTokens.ts @@ -1,13 +1,47 @@ -/** Single column/section: full width under `md`, max 640px from `--breakpoint-md` up. */ +import type { CreateFlowStep } from "../types"; +import { CREATE_FLOW_SCREEN_REGISTRY } from "../utils/createFlowScreenRegistry"; + +/** + * Shared page gutters for create-flow chrome and step shells. + * Six viewport bands collapse to three values: 20px below `md`, 48px from `md`, + * 64px from `lg` (320–639 / 640–1023 / 1024+). + */ +export const CREATE_FLOW_PAGE_GUTTER_CLASS = "px-5 md:px-12 lg:px-16"; + +/** + * Wizard viewport frame. `dvh` tracks mobile browser chrome so the action bar + * stays on-screen; `max-h-dvh` keeps the flex column from overflowing it. + */ +export const CREATE_FLOW_VIEWPORT_FRAME_CLASS = + "relative flex h-dvh max-h-dvh min-h-0 flex-col overflow-hidden"; + +/** Design-system scrollbar for create-flow scroll regions (not `scrollbar-hide`). */ +export const CREATE_FLOW_SCROLL_REGION_CLASS = "scrollbar-design"; + +/** + * Fade above the fixed action bar so content that continues below the fold is + * visible as a continuation cue (Figma Scrim / footer overlay). + */ +export const CREATE_FLOW_FOOTER_SCRIM_CLASS = + "pointer-events-none absolute inset-x-0 top-0 z-[1] h-8 -translate-y-full bg-gradient-to-t from-[var(--color-surface-default-primary,#000)] to-transparent"; + +/** + * Single column: full width below `lg` so tablet widths use available space; + * 640px from `--breakpoint-lg` up (two-column layouts start at `lg`). + */ export const CREATE_FLOW_MD_UP_COLUMN_MAX_CLASS = - "w-full min-w-0 md:max-w-[640px]"; + "w-full min-w-0 lg:max-w-[640px]"; /** Grid cell: same cap as column max, centered when the track is wider than 640px. */ export const CREATE_FLOW_MD_UP_GRID_CELL_CLASS = - "w-full min-w-0 md:mx-auto md:max-w-[640px]"; + "w-full min-w-0 lg:mx-auto lg:max-w-[640px]"; /** Two 640px columns + `--measures-spacing-1200` (48px) gutter. */ -export const CREATE_FLOW_TWO_COLUMN_MAX_WIDTH_CLASS = "md:max-w-[1328px]"; +export const CREATE_FLOW_TWO_COLUMN_MAX_WIDTH_CLASS = + "w-full min-w-0 lg:max-w-[1328px]"; + +/** Review / card-stack frames (Figma Flow — Review max 1440). */ +export const CREATE_FLOW_WIDE_MAX_CLASS = "w-full min-w-0 lg:max-w-[1440px]"; /** * Lockup+card and card-stack `
`: keep `items-start` so a tall card can @@ -29,3 +63,30 @@ export const CREATE_FLOW_MD_CENTERED_SHELL_CLASS = "md:my-auto md:pt-0"; */ export const CREATE_FLOW_CARD_STACK_AREA_MAX_CLASS = "w-full min-w-0 md:max-w-[min(100%,860px)]"; + +/** + * Inner max-width for step content and the action bar, so the primary button + * lines up with the nearest content column above 1440. + */ +export function getCreateFlowContentMaxClass(options: { + step: CreateFlowStep | null | undefined; + isTemplateReview?: boolean; +}): string { + if (options.isTemplateReview) { + return CREATE_FLOW_TWO_COLUMN_MAX_WIDTH_CLASS; + } + if (!options.step) { + return CREATE_FLOW_MD_UP_COLUMN_MAX_CLASS; + } + switch (CREATE_FLOW_SCREEN_REGISTRY[options.step].layoutKind) { + case "select": + case "right-rail": + case "completed": + case "review": + return CREATE_FLOW_TWO_COLUMN_MAX_WIDTH_CLASS; + case "card": + return CREATE_FLOW_WIDE_MAX_CLASS; + default: + return CREATE_FLOW_MD_UP_COLUMN_MAX_CLASS; + } +} diff --git a/app/(app)/create/loading.tsx b/app/(app)/create/loading.tsx index 0c0a982..739986d 100644 --- a/app/(app)/create/loading.tsx +++ b/app/(app)/create/loading.tsx @@ -6,7 +6,7 @@ export default function CreateFlowRouteLoading() { return (
diff --git a/app/(app)/create/screens/card/CommunicationMethodsScreen.tsx b/app/(app)/create/screens/card/CommunicationMethodsScreen.tsx index d0c46b2..22279ed 100644 --- a/app/(app)/create/screens/card/CommunicationMethodsScreen.tsx +++ b/app/(app)/create/screens/card/CommunicationMethodsScreen.tsx @@ -6,7 +6,7 @@ * * Lives under `screens/card/` (not `select/`): Figma **card stack** layout is a distinct shell from * two-column chip **select** frames. Future card-stack steps get their own `*Screen.tsx` here and - * reuse `CardStack` / `CreateFlowStepShell` as needed. + * reuse `CardStack` / `CreateFlowCardStackStepShell` as needed. * * Card click opens the Figma create modal (node `20246-15829`) with three * editable sections rendered by {@link CommunicationMethodEditFields}. The @@ -22,16 +22,10 @@ import { useCreateFlowMdUp } from "../../hooks/useCreateFlowMdUp"; import { useBeforeUnloadGuard } from "../../../../hooks/useBeforeUnloadGuard"; import { useDiscardCustomizeConfirm } from "../../hooks/useDiscardCustomizeConfirm"; import { useMethodCardDeckOrdering } from "../../hooks/useMethodCardDeckOrdering"; -import { CreateFlowHeaderLockup } from "../../components/CreateFlowHeaderLockup"; +import { CreateFlowCardStackStepShell } from "../../components/CreateFlowCardStackStepShell"; import CardStack from "../../../../components/cards/CardStack"; import Create from "../../../../components/modals/Create"; import InlineTextButton from "../../../../components/buttons/InlineTextButton"; -import { CreateFlowStepShell } from "../../components/CreateFlowStepShell"; -import { - CREATE_FLOW_CARD_STACK_AREA_MAX_CLASS, - CREATE_FLOW_MD_CENTERED_SHELL_CLASS, - CREATE_FLOW_MD_UP_COLUMN_MAX_CLASS, -} from "../../components/createFlowLayoutTokens"; import { CommunicationMethodEditFields } from "../../components/methodEditFields"; import CustomMethodCardWizard from "../../components/CustomMethodCardWizard"; import { uploadCreateFlowFile } from "../../../../../lib/create/uploadToServer"; @@ -700,44 +694,30 @@ export function CommunicationMethodsScreen() { return ( <> - -
-
- -
-
- {recommendationsReady && ( - { - markCreateFlowInteraction(); - setExpanded((prev) => !prev); - }} - hasMore={true} - toggleLabel={comm.page.seeAllLink} - compactRecommendedLimit={5} - compactCardIds={compactCardIds} - compactDesktopLayout="flexWrap" - headerLockupSize={mdUp ? "L" : "M"} - /> - )} -
-
- + {recommendationsReady && ( + { + markCreateFlowInteraction(); + setExpanded((prev) => !prev); + }} + hasMore={true} + toggleLabel={comm.page.seeAllLink} + compactRecommendedLimit={5} + compactCardIds={compactCardIds} + compactDesktopLayout="flexWrap" + headerLockupSize={mdUp ? "L" : "M"} + /> + )} + -
- -
-
- -
-
- {recommendationsReady && ( - { - markCreateFlowInteraction(); - setExpanded((prev) => !prev); - }} - hasMore={true} - toggleLabel={cm.page.seeAllLink} - compactRecommendedLimit={5} - compactCardIds={compactCardIds} - compactDesktopLayout="pyramidFive" - headerLockupSize={mdUp ? "L" : "M"} - /> - )} -
-
- + {recommendationsReady && ( + { + markCreateFlowInteraction(); + setExpanded((prev) => !prev); + }} + hasMore={true} + toggleLabel={cm.page.seeAllLink} + compactRecommendedLimit={5} + compactCardIds={compactCardIds} + compactDesktopLayout="pyramidFive" + headerLockupSize={mdUp ? "L" : "M"} + /> + )} + -
- -
-
- -
-
- {recommendationsReady && ( - { - markCreateFlowInteraction(); - setExpanded((prev) => !prev); - }} - hasMore={true} - toggleLabel={mem.page.seeAllLink} - compactRecommendedLimit={5} - compactCardIds={compactCardIds} - compactDesktopLayout="pyramidFive" - headerLockupSize={mdUp ? "L" : "M"} - /> - )} -
-
- + {recommendationsReady && ( + { + markCreateFlowInteraction(); + setExpanded((prev) => !prev); + }} + hasMore={true} + toggleLabel={mem.page.seeAllLink} + compactRecommendedLimit={5} + compactCardIds={compactCardIds} + compactDesktopLayout="pyramidFive" + headerLockupSize={mdUp ? "L" : "M"} + /> + )} + -
+ } > -
-
- -
- -
- + + ); } return ( <> - -
-
- -
- {chipError ? ( -

- {chipError} -

- ) : null} - -
-
+ } + > + {chipError ? ( +

+ {chipError} +

+ ) : null} + + {!toastDismissed && (
( proportionBarVariant, onBackClick, className = "", + contentMaxClass, footerAriaLabel, }) => { const t = useTranslation("controlsChrome"); @@ -29,6 +30,7 @@ const CreateFlowFooterContainer = memo( proportionBarVariant={proportionBarVariant} onBackClick={onBackClick} className={className} + contentMaxClass={contentMaxClass} footerAriaLabel={footerAriaLabel ?? t("createFlowFooterAriaLabel")} /> ); diff --git a/app/components/navigation/CreateFlowFooter/CreateFlowFooter.types.ts b/app/components/navigation/CreateFlowFooter/CreateFlowFooter.types.ts index c97b0ba..276a357 100644 --- a/app/components/navigation/CreateFlowFooter/CreateFlowFooter.types.ts +++ b/app/components/navigation/CreateFlowFooter/CreateFlowFooter.types.ts @@ -36,6 +36,11 @@ export interface CreateFlowFooterProps { * Additional CSS classes */ className?: string; + /** + * Inner max-width for the progress bar and actions, matching the step + * content column so the primary button does not drift at wide viewports. + */ + contentMaxClass?: string; /** * Accessible name for the footer landmark. */ diff --git a/app/components/navigation/CreateFlowFooter/CreateFlowFooter.view.tsx b/app/components/navigation/CreateFlowFooter/CreateFlowFooter.view.tsx index c71467d..83f4d33 100644 --- a/app/components/navigation/CreateFlowFooter/CreateFlowFooter.view.tsx +++ b/app/components/navigation/CreateFlowFooter/CreateFlowFooter.view.tsx @@ -1,6 +1,11 @@ import ProportionBar from "../../progress/ProportionBar"; import Button from "../../buttons/Button"; import type { CreateFlowFooterProps } from "./CreateFlowFooter.types"; +import { + CREATE_FLOW_FOOTER_SCRIM_CLASS, + CREATE_FLOW_PAGE_GUTTER_CLASS, + CREATE_FLOW_TWO_COLUMN_MAX_WIDTH_CLASS, +} from "../../../(app)/create/components/createFlowLayoutTokens"; export function CreateFlowFooterView({ secondButton, @@ -9,41 +14,43 @@ export function CreateFlowFooterView({ proportionBarVariant: proportionBarVariantProp, onBackClick, className = "", + contentMaxClass = CREATE_FLOW_TWO_COLUMN_MAX_WIDTH_CLASS, footerAriaLabel, }: CreateFlowFooterProps) { const proportionBarVariant = proportionBarVariantProp ?? "default"; return (
- {/* Progress Bar - Top */} - {progressBar && ( -
- +
+
+
+ {progressBar && ( +
+ +
+ )} + +
+ + + {secondButton &&
{secondButton}
} +
- )} - - {/* Buttons Container */} -
- {/* Back Button - Left */} - - - {/* Second Button - Right */} - {secondButton &&
{secondButton}
}
); diff --git a/app/components/navigation/CreateFlowTopNav/CreateFlowTopNav.view.tsx b/app/components/navigation/CreateFlowTopNav/CreateFlowTopNav.view.tsx index 593e187..e4cd6dd 100644 --- a/app/components/navigation/CreateFlowTopNav/CreateFlowTopNav.view.tsx +++ b/app/components/navigation/CreateFlowTopNav/CreateFlowTopNav.view.tsx @@ -5,6 +5,7 @@ import Button from "../../buttons/Button"; import ListItem from "../../layout/ListItem"; import Popover from "../../modals/Popover"; import type { CreateFlowTopNavViewProps } from "./CreateFlowTopNav.types"; +import { CREATE_FLOW_PAGE_GUTTER_CLASS } from "../../../(app)/create/components/createFlowLayoutTokens"; const outlineButtonClass = "md:!text-x-small-label !text-xx-small-label !px-[var(--spacing-scale-006,6px)] md:!px-[var(--spacing-scale-008,8px)] !py-[6px] md:!py-[8px] !border md:!border-[1.5px]"; @@ -229,7 +230,7 @@ export function CreateFlowTopNavView({ aria-label={bannerAriaLabel} >