From dbeebf58e3b475930d36299aded048bfe4098c90 Mon Sep 17 00:00:00 2001 From: Aaron Reisman Date: Tue, 23 Jun 2026 16:09:48 +0700 Subject: [PATCH 1/2] Align alert-dialog anatomy to design-system spec Per issue #117, the designer marked spacing, right-aligned action placement, and max-width constraint as "always the same". These were previously left to the consumer via ad-hoc layout divs inside the stories. This commit bakes them in via two new anatomy parts and updates the popup's baked styles. --- .../alert-dialog/alert-dialog.stories.tsx | 42 +++++++------- .../src/components/alert-dialog/index.tsx | 4 ++ .../ui/alert-dialog/alert-dialog-actions.tsx | 13 +++++ .../ui/alert-dialog/alert-dialog-intro.tsx | 13 +++++ .../ui/alert-dialog/alert-dialog.stories.tsx | 55 ++++++++----------- packages/propel/src/ui/alert-dialog/index.tsx | 2 + .../propel/src/ui/alert-dialog/variants.ts | 11 ++++ 7 files changed, 85 insertions(+), 55 deletions(-) create mode 100644 packages/propel/src/ui/alert-dialog/alert-dialog-actions.tsx create mode 100644 packages/propel/src/ui/alert-dialog/alert-dialog-intro.tsx diff --git a/packages/propel/src/components/alert-dialog/alert-dialog.stories.tsx b/packages/propel/src/components/alert-dialog/alert-dialog.stories.tsx index bc5c9e67..99ca146c 100644 --- a/packages/propel/src/components/alert-dialog/alert-dialog.stories.tsx +++ b/packages/propel/src/components/alert-dialog/alert-dialog.stories.tsx @@ -4,9 +4,11 @@ import { expect, userEvent, waitFor, within } from "storybook/test"; import { Button } from "../../ui/button"; import { AlertDialog, + AlertDialogActions, AlertDialogClose, AlertDialogContent, AlertDialogDescription, + AlertDialogIntro, AlertDialogTitle, AlertDialogTrigger, } from "./index"; @@ -23,8 +25,10 @@ const meta = { subcomponents: { AlertDialogTrigger, AlertDialogContent, + AlertDialogIntro, AlertDialogTitle, AlertDialogDescription, + AlertDialogActions, AlertDialogClose, }, } satisfies Meta; @@ -40,29 +44,21 @@ export const Default: Story = { Delete project - {/* - * Two layout groups, separated by the parent's gap (never a margin on a - * child): an "intro" (title + description) and the "actions" row. These - * boundaries are the future anatomy surfaces — e.g. AlertDialogIntro and - * AlertDialogActions — so define them correctly here before hardening. - */} -
-
- Delete project? - - This permanently removes the project and all of its work items. This action can't - be undone. - -
-
- - -
-
+ + Delete project? + + This permanently removes the project and all of its work items. This action can't + be undone. + + + + + +
), diff --git a/packages/propel/src/components/alert-dialog/index.tsx b/packages/propel/src/components/alert-dialog/index.tsx index 4cfb2aa9..e9e52921 100644 --- a/packages/propel/src/components/alert-dialog/index.tsx +++ b/packages/propel/src/components/alert-dialog/index.tsx @@ -3,10 +3,14 @@ export { AlertDialogContent, type AlertDialogContentProps } from "./alert-dialog export { AlertDialog, type AlertDialogProps, + AlertDialogActions, + type AlertDialogActionsProps, AlertDialogClose, type AlertDialogCloseProps, AlertDialogDescription, type AlertDialogDescriptionProps, + AlertDialogIntro, + type AlertDialogIntroProps, AlertDialogTitle, type AlertDialogTitleProps, AlertDialogTrigger, diff --git a/packages/propel/src/ui/alert-dialog/alert-dialog-actions.tsx b/packages/propel/src/ui/alert-dialog/alert-dialog-actions.tsx new file mode 100644 index 00000000..64aebf1d --- /dev/null +++ b/packages/propel/src/ui/alert-dialog/alert-dialog-actions.tsx @@ -0,0 +1,13 @@ +import * as React from "react"; + +import { alertDialogActionsVariants } from "./variants"; + +export type AlertDialogActionsProps = React.HTMLAttributes; + +/** + * Right-aligns action buttons with consistent horizontal spacing. The placement and gap are always + * the same — baked in by the design system. + */ +export function AlertDialogActions({ ...props }: AlertDialogActionsProps) { + return
; +} diff --git a/packages/propel/src/ui/alert-dialog/alert-dialog-intro.tsx b/packages/propel/src/ui/alert-dialog/alert-dialog-intro.tsx new file mode 100644 index 00000000..0f96c134 --- /dev/null +++ b/packages/propel/src/ui/alert-dialog/alert-dialog-intro.tsx @@ -0,0 +1,13 @@ +import * as React from "react"; + +import { alertDialogIntroVariants } from "./variants"; + +export type AlertDialogIntroProps = React.HTMLAttributes; + +/** + * Groups the title and description with consistent vertical spacing. The gap between title and + * description is always the same — baked in by the design system. + */ +export function AlertDialogIntro({ ...props }: AlertDialogIntroProps) { + return
; +} diff --git a/packages/propel/src/ui/alert-dialog/alert-dialog.stories.tsx b/packages/propel/src/ui/alert-dialog/alert-dialog.stories.tsx index 9c48b16c..c4f445ca 100644 --- a/packages/propel/src/ui/alert-dialog/alert-dialog.stories.tsx +++ b/packages/propel/src/ui/alert-dialog/alert-dialog.stories.tsx @@ -4,9 +4,11 @@ import { expect, userEvent, waitFor, within } from "storybook/test"; import { Button } from "../button"; import { AlertDialog, + AlertDialogActions, AlertDialogBackdrop, AlertDialogClose, AlertDialogDescription, + AlertDialogIntro, AlertDialogPopup, AlertDialogPortal, AlertDialogTitle, @@ -30,8 +32,10 @@ const meta = { AlertDialogBackdrop, AlertDialogViewport, AlertDialogPopup, + AlertDialogIntro, AlertDialogTitle, AlertDialogDescription, + AlertDialogActions, AlertDialogClose, }, } satisfies Meta; @@ -50,38 +54,25 @@ export const Anatomy: Story = { - {/* - * Two layout groups, separated by the parent's gap (never a margin on a - * child): an "intro" (title + description) and the "actions" row. These - * boundaries are the future anatomy surfaces — e.g. AlertDialogIntro and - * AlertDialogActions — so define them correctly here before hardening. - */} -
-
- Delete account? - - This permanently deletes your account and cannot be undone. - -
-
- - -
-
+ + Delete account? + + This permanently deletes your account and cannot be undone. + + + + + +
diff --git a/packages/propel/src/ui/alert-dialog/index.tsx b/packages/propel/src/ui/alert-dialog/index.tsx index b94d221b..f57b90e6 100644 --- a/packages/propel/src/ui/alert-dialog/index.tsx +++ b/packages/propel/src/ui/alert-dialog/index.tsx @@ -1,10 +1,12 @@ export { AlertDialog, type AlertDialogProps } from "./alert-dialog"; +export { AlertDialogActions, type AlertDialogActionsProps } from "./alert-dialog-actions"; export { AlertDialogBackdrop, type AlertDialogBackdropProps } from "./alert-dialog-backdrop"; export { AlertDialogClose, type AlertDialogCloseProps } from "./alert-dialog-close"; export { AlertDialogDescription, type AlertDialogDescriptionProps, } from "./alert-dialog-description"; +export { AlertDialogIntro, type AlertDialogIntroProps } from "./alert-dialog-intro"; export { AlertDialogPopup, type AlertDialogPopupProps } from "./alert-dialog-popup"; export { AlertDialogPortal } from "./alert-dialog-portal"; export { AlertDialogTitle, type AlertDialogTitleProps } from "./alert-dialog-title"; diff --git a/packages/propel/src/ui/alert-dialog/variants.ts b/packages/propel/src/ui/alert-dialog/variants.ts index 25a2bb93..e278e0d9 100644 --- a/packages/propel/src/ui/alert-dialog/variants.ts +++ b/packages/propel/src/ui/alert-dialog/variants.ts @@ -20,6 +20,9 @@ export const alertDialogViewportVariants = cva( export const alertDialogPopupVariants = cva( cx( + // Fixed width and layout: the max-width constraint and internal spacing are + // always the same per spec — baked here, not left to the consumer. + "flex w-80 flex-col gap-4", "rounded-lg border-sm border-subtle bg-layer-1 p-4 shadow-overlay-100 outline-none", "origin-(--transform-origin) transition-[opacity,transform] duration-200", "data-starting-style:scale-95 data-starting-style:opacity-0", @@ -31,6 +34,14 @@ export const alertDialogTitleVariants = cva("text-16 font-semibold text-primary" export const alertDialogDescriptionVariants = cva("text-14 text-secondary"); +// Intro: groups the title and description with consistent vertical spacing. +// Always the same per spec (spacing between title and description). +export const alertDialogIntroVariants = cva("flex flex-col gap-2"); + +// Actions: right-aligns action buttons with consistent horizontal spacing. +// Always the same per spec (action button placement, right-aligned in footer). +export const alertDialogActionsVariants = cva("flex justify-end gap-2"); + export const alertDialogCloseVariants = cva( cx( "inline-flex items-center justify-center rounded-md text-icon-secondary outline-none", From 514d8a4ff081098c5c592d9f30cee8ef77f9ad8d Mon Sep 17 00:00:00 2001 From: Aaron Reisman Date: Tue, 23 Jun 2026 16:46:21 +0700 Subject: [PATCH 2/2] Extend alert-dialog anatomy: Header + Icon parts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The popup now exposes the leading-icon region the design spec calls out ("icon placement, left of title"; "icon type warning/error/info"). Two new ui parts: - AlertDialogIcon: a decorative single-element node slot for the leading glyph, with a required `tone` (danger/warning/info/success) — the destructive-vs-informational axis the spec marks adjustable, no default. - AlertDialogHeader: the row that places the icon at the inline-start of the intro so the icon sits left of the title. Styling stays in ui/.../variants.ts (cva); the components tier only composes parts. Stories compose the new parts and register them in subcomponents. --- .../alert-dialog/alert-dialog.stories.tsx | 24 ++++++++---- .../src/components/alert-dialog/index.tsx | 5 +++ .../ui/alert-dialog/alert-dialog-header.tsx | 17 +++++++++ .../src/ui/alert-dialog/alert-dialog-icon.tsx | 26 +++++++++++++ .../ui/alert-dialog/alert-dialog.stories.tsx | 22 ++++++++--- packages/propel/src/ui/alert-dialog/index.tsx | 6 +++ .../propel/src/ui/alert-dialog/variants.ts | 37 ++++++++++++++++--- 7 files changed, 118 insertions(+), 19 deletions(-) create mode 100644 packages/propel/src/ui/alert-dialog/alert-dialog-header.tsx create mode 100644 packages/propel/src/ui/alert-dialog/alert-dialog-icon.tsx diff --git a/packages/propel/src/components/alert-dialog/alert-dialog.stories.tsx b/packages/propel/src/components/alert-dialog/alert-dialog.stories.tsx index 99ca146c..c27bdb54 100644 --- a/packages/propel/src/components/alert-dialog/alert-dialog.stories.tsx +++ b/packages/propel/src/components/alert-dialog/alert-dialog.stories.tsx @@ -1,4 +1,5 @@ import type { Meta, StoryObj } from "@storybook/react-vite"; +import { TriangleAlert } from "lucide-react"; import { expect, userEvent, waitFor, within } from "storybook/test"; import { Button } from "../../ui/button"; @@ -8,6 +9,8 @@ import { AlertDialogClose, AlertDialogContent, AlertDialogDescription, + AlertDialogHeader, + AlertDialogIcon, AlertDialogIntro, AlertDialogTitle, AlertDialogTrigger, @@ -25,6 +28,8 @@ const meta = { subcomponents: { AlertDialogTrigger, AlertDialogContent, + AlertDialogHeader, + AlertDialogIcon, AlertDialogIntro, AlertDialogTitle, AlertDialogDescription, @@ -44,13 +49,18 @@ export const Default: Story = { Delete project - - Delete project? - - This permanently removes the project and all of its work items. This action can't - be undone. - - + + + + + + Delete project? + + This permanently removes the project and all of its work items. This action can't + be undone. + + +