diff --git a/.changeset/button-family-api.md b/.changeset/button-family-api.md new file mode 100644 index 00000000..24fe3144 --- /dev/null +++ b/.changeset/button-family-api.md @@ -0,0 +1,10 @@ +--- +"@makeplane/propel": minor +--- + +Rework the button-family API (`Button`, `IconButton`, `SplitButton`, `ButtonGroup`, +`AnchorButton`): merge `prominence` + `tone` into a single `variant` axis (`primary` / +`secondary` / `tertiary` / `ghost` / `danger` / `danger-outline`), rename `magnitude` to `size` +and `sizing` to `fillType`, replace `startIcon`/`endIcon` with one `icon` prop plus +`iconPosition` (`"start"` default, `"end"` to trail — the `loading` spinner takes the same +slot), and add explicit `disabled` and `type` (`"button"` default; `submit` / `reset`) props. diff --git a/apps/docs/src/components/react/MobileNav.tsx b/apps/docs/src/components/react/MobileNav.tsx index d035855f..cdca3ad1 100644 --- a/apps/docs/src/components/react/MobileNav.tsx +++ b/apps/docs/src/components/react/MobileNav.tsx @@ -31,9 +31,8 @@ export default function MobileNav({ groups, currentPath }: Props) { } /> @@ -47,9 +46,8 @@ export default function MobileNav({ groups, currentPath }: Props) { } /> diff --git a/apps/docs/src/components/react/ThemeToggle.tsx b/apps/docs/src/components/react/ThemeToggle.tsx index 870ef719..c65ddcbd 100644 --- a/apps/docs/src/components/react/ThemeToggle.tsx +++ b/apps/docs/src/components/react/ThemeToggle.tsx @@ -44,9 +44,8 @@ export default function ThemeToggle() { return ( } onClick={() => { diff --git a/apps/docs/src/demos/accordion/controlled.tsx b/apps/docs/src/demos/accordion/controlled.tsx index 3a29399e..6e222c6a 100644 --- a/apps/docs/src/demos/accordion/controlled.tsx +++ b/apps/docs/src/demos/accordion/controlled.tsx @@ -32,18 +32,16 @@ export default function ControlledDemo() {
); } diff --git a/apps/docs/src/demos/button/magnitudes.tsx b/apps/docs/src/demos/button/magnitudes.tsx deleted file mode 100644 index 132c6c68..00000000 --- a/apps/docs/src/demos/button/magnitudes.tsx +++ /dev/null @@ -1,12 +0,0 @@ -import { Button } from "@makeplane/propel/components/button"; - -export default function MagnitudesDemo() { - return ( -
-
- ); -} diff --git a/apps/docs/src/demos/button/prominences.tsx b/apps/docs/src/demos/button/prominences.tsx deleted file mode 100644 index 132a4c7a..00000000 --- a/apps/docs/src/demos/button/prominences.tsx +++ /dev/null @@ -1,12 +0,0 @@ -import { Button } from "@makeplane/propel/components/button"; - -export default function ProminencesDemo() { - return ( -
-
- ); -} diff --git a/apps/docs/src/demos/button/sizes.tsx b/apps/docs/src/demos/button/sizes.tsx new file mode 100644 index 00000000..d4110ad1 --- /dev/null +++ b/apps/docs/src/demos/button/sizes.tsx @@ -0,0 +1,12 @@ +import { Button } from "@makeplane/propel/components/button"; + +export default function SizesDemo() { + return ( +
+
+ ); +} diff --git a/apps/docs/src/demos/button/tones.tsx b/apps/docs/src/demos/button/tones.tsx deleted file mode 100644 index c14e3c7d..00000000 --- a/apps/docs/src/demos/button/tones.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import { Button } from "@makeplane/propel/components/button"; - -export default function TonesDemo() { - return ( -
-
- ); -} diff --git a/apps/docs/src/demos/button/variants.tsx b/apps/docs/src/demos/button/variants.tsx new file mode 100644 index 00000000..1a31aa68 --- /dev/null +++ b/apps/docs/src/demos/button/variants.tsx @@ -0,0 +1,14 @@ +import { Button } from "@makeplane/propel/components/button"; + +export default function VariantsDemo() { + return ( +
+
+ ); +} diff --git a/apps/docs/src/demos/button/with-icon.tsx b/apps/docs/src/demos/button/with-icon.tsx index 3dd6547e..ed8683aa 100644 --- a/apps/docs/src/demos/button/with-icon.tsx +++ b/apps/docs/src/demos/button/with-icon.tsx @@ -2,14 +2,5 @@ import { Button } from "@makeplane/propel/components/button"; import { Plus } from "lucide-react"; export default function WithIconDemo() { - return ( -
diff --git a/apps/docs/src/demos/home/invite-form.tsx b/apps/docs/src/demos/home/invite-form.tsx index a9ca79a7..d98acb2d 100644 --- a/apps/docs/src/demos/home/invite-form.tsx +++ b/apps/docs/src/demos/home/invite-form.tsx @@ -25,14 +25,7 @@ export default function InviteFormDemo() { description="They can be removed from a project later." defaultChecked /> - // ✅ corner close — IconButton for the icon-only ✕ -}> +}> ``` @@ -99,7 +99,7 @@ Not the reverse: ```tsx // ❌ wrong — renders a bare/ghost element, NOT a styled button -}> +}> Cancel ``` diff --git a/packages/propel/src/components/accordion/accordion.stories.tsx b/packages/propel/src/components/accordion/accordion.stories.tsx index 2cad8b3f..04234f46 100644 --- a/packages/propel/src/components/accordion/accordion.stories.tsx +++ b/packages/propel/src/components/accordion/accordion.stories.tsx @@ -199,18 +199,16 @@ export const Controlled: Story = {
), }; -/** - * Tone selects the palette: `neutral` (default) or `danger` (Figma "Error"). Danger is shown as a - * solid fill and a bordered outline. - */ -export const Tones: Story = { - parameters: { controls: { disable: true } }, - render: (args) => ( -
-
- ), -}; - /** All sizes (Figma S/Base/L/XL map to sm/md/lg/xl). */ -export const Magnitudes: Story = { - // Iterates `magnitude` and labels each button with the magnitude name, so disable +export const Sizes: Story = { + // Iterates `size` and labels each button with the size name, so disable // just those two controls; the rest stay live and update every button at once. - argTypes: { magnitude: { control: false }, label: { control: false } }, + argTypes: { size: { control: false }, label: { control: false } }, render: (args) => (
- {MAGNITUDES.map((magnitude) => ( -
), }; -/** Inline-start and inline-end nodes sit beside the label and are decorative. */ +/** The icon sits inline-start by default; `iconPosition="end"` moves it after the label. */ export const WithIcons: Story = { parameters: { controls: { disable: true } }, render: (args) => (
-
), }; -/** The loading state shows a trailing spinner, sets `aria-busy`, and blocks interaction. */ +/** + * The loading state swaps the icon slot for a spinner (at `iconPosition`, inline-start by default), + * sets `aria-busy`, and blocks interaction. + */ export const Loading: Story = { parameters: { controls: { disable: true } }, render: (args) => (
), }; @@ -146,13 +136,13 @@ export const AsyncSubmit: Story = { }, }; -/** `sizing="fill"` fills the container (e.g. a form row or mobile CTA). */ +/** `fillType="fill"` fills the container (e.g. a form row or mobile CTA). */ export const Stretch: Story = { parameters: { controls: { disable: true } }, render: (args) => (
-
), }; @@ -307,10 +297,9 @@ export const AsyncSubmitKeepsFocus: Story = { */ export const CustomTag: Story = { args: { - prominence: "secondary", - tone: "neutral", - magnitude: "md", - sizing: "hug", + variant: "secondary", + size: "md", + fillType: "hug", }, render: (args) => (
- @@ -149,10 +149,10 @@ export const States: Story = { - - diff --git a/packages/propel/src/elements/anchor-button/anchor-button.stories.tsx b/packages/propel/src/elements/anchor-button/anchor-button.stories.tsx index 55b0232c..a37dec5a 100644 --- a/packages/propel/src/elements/anchor-button/anchor-button.stories.tsx +++ b/packages/propel/src/elements/anchor-button/anchor-button.stories.tsx @@ -5,12 +5,12 @@ import { Spinner } from "../../internal/spinner"; import { AnchorButton, AnchorButtonLabel, - type AnchorButtonMagnitude, - type AnchorButtonProminence, + type AnchorButtonSize, + type AnchorButtonVariant, } from "./index"; -const PROMINENCES: AnchorButtonProminence[] = ["primary", "secondary"]; -const MAGNITUDES: AnchorButtonMagnitude[] = ["sm", "md", "lg", "xl"]; +const VARIANTS: AnchorButtonVariant[] = ["primary", "secondary"]; +const SIZES: AnchorButtonSize[] = ["sm", "md", "lg", "xl"]; // elements-tier story (rule 2b): a pure UI-configuration showcase of the styled ` - - - + diff --git a/packages/propel/src/elements/button-group/button-group-button.tsx b/packages/propel/src/elements/button-group/button-group-button.tsx index 4003a071..f68328b9 100644 --- a/packages/propel/src/elements/button-group/button-group-button.tsx +++ b/packages/propel/src/elements/button-group/button-group-button.tsx @@ -3,7 +3,7 @@ import { useRender } from "@base-ui/react/use-render"; import { type ButtonGroupButtonVariantProps, buttonGroupButtonVariants } from "./variants"; -export type { ButtonGroupButtonMagnitude } from "./variants"; +export type { ButtonGroupButtonSize } from "./variants"; export type ButtonGroupButtonProps = Omit< useRender.ComponentProps<"button">, @@ -14,13 +14,13 @@ export type ButtonGroupButtonProps = Omit< /** * The styled segment of a `ButtonGroup`: a transparent, borderless ` ), @@ -59,69 +45,43 @@ type Story = StoryObj; export const Default: Story = {}; -/** Every prominence (Figma "Type") side by side at the default magnitude. */ -export const Prominences: Story = { - argTypes: { prominence: { control: false }, children: { control: false } }, - render: ({ magnitude, sizing }) => ( +/** Every variant — the four Figma Types plus the two danger palettes — at the default size. */ +export const Variants: Story = { + argTypes: { variant: { control: false }, children: { control: false } }, + render: ({ size, fillType }) => (
- {PROMINENCES.map((prominence) => ( - ))}
), }; -/** - * Tone selects the palette: `neutral` (default) or `danger` (Figma "Error"). Danger shows as a - * solid fill and a bordered outline. - */ -export const Tones: Story = { - parameters: { controls: { disable: true } }, - render: ({ magnitude, sizing }) => ( -
- - - -
- ), -}; - /** All sizes (Figma S/Base/L/XL map to sm/md/lg/xl). */ -export const Magnitudes: Story = { - argTypes: { magnitude: { control: false }, children: { control: false } }, - render: ({ prominence, tone, sizing }) => ( +export const Sizes: Story = { + argTypes: { size: { control: false }, children: { control: false } }, + render: ({ variant, fillType }) => (
- {MAGNITUDES.map((magnitude) => ( - ))}
), }; -/** `sizing="fill"` fills the container (e.g. a form row or mobile CTA). */ +/** `fillType="fill"` fills the container (e.g. a form row or mobile CTA). */ export const Stretch: Story = { - argTypes: { sizing: { control: false }, children: { control: false } }, - render: ({ prominence, tone, magnitude }) => ( + argTypes: { fillType: { control: false }, children: { control: false } }, + render: ({ variant, size }) => (
- -
@@ -129,60 +89,45 @@ export const Stretch: Story = { }; /** - * Every visual state of every palette, pinned statically — one row per prominence×tone pairing the - * chrome defines. Hover / active / focus-visible are CSS pseudo-classes, forced by the - * pseudo-states addon; `disabled` is the native attribute the `disabled:` palette keys off; busy - * pins the `aria-busy` the ready-made Button sets while `loading` (loading mutes via the root - * chrome palette — label and spinner share the same weight). + * Every visual state of every variant, pinned statically — one row per variant the chrome defines. + * Hover / active / focus-visible are CSS pseudo-classes, forced by the pseudo-states addon; + * `disabled` is the native attribute the `disabled:` palette keys off; busy pins the + * `aria-busy`/`aria-disabled` the ready-made Button sets while `loading` (loading mutes via the + * root chrome palette — label and spinner share the same weight). */ export const States: Story = { parameters: { controls: { disable: true }, pseudo: { - hover: PALETTES.map(({ prominence, tone }) => `#button-${prominence}-${tone}-hover`), - active: PALETTES.map(({ prominence, tone }) => `#button-${prominence}-${tone}-active`), - focusVisible: PALETTES.map(({ prominence, tone }) => `#button-${prominence}-${tone}-focus`), + hover: VARIANTS.map((variant) => `#button-${variant}-hover`), + active: VARIANTS.map((variant) => `#button-${variant}-active`), + focusVisible: VARIANTS.map((variant) => `#button-${variant}-focus`), }, }, - render: ({ magnitude, sizing }) => ( + render: ({ size, fillType }) => (
- {PALETTES.map((palette) => ( -
- - - - - -
))} @@ -193,33 +138,27 @@ export const States: Story = { /** * The atomic button is composed from named parts: the internal `Icon` sizes a decorative * leading/trailing node to the button's `--node-size`, `ButtonLabel` holds the text, and the - * internal `Spinner` is the loading indicator (trailing, after the label). The busy state is pinned - * here via the `aria-busy`/`aria-disabled` the ready-made Button (Components/Button) sets while - * `loading` — that ready-made also lays these parts out for you and adds the soft-disabled - * behavior. + * internal `Spinner` is the loading indicator (it takes the icon slot — leading by default). The + * busy state is pinned here via the `aria-busy`/`aria-disabled` the ready-made Button + * (Components/Button) sets while `loading` — that ready-made also lays these parts out for you and + * adds the soft-disabled behavior. */ export const Anatomy: Story = { args: { children: undefined }, argTypes: { children: { control: false } }, - render: ({ prominence, tone, magnitude, sizing }) => ( + render: ({ variant, size, fillType }) => (
- -
), diff --git a/packages/propel/src/elements/button/button.tsx b/packages/propel/src/elements/button/button.tsx index 72dd56fb..1f7ecfbd 100644 --- a/packages/propel/src/elements/button/button.tsx +++ b/packages/propel/src/elements/button/button.tsx @@ -3,23 +3,23 @@ import { useRender } from "@base-ui/react/use-render"; import { type ButtonVariantProps, buttonVariants } from "./variants"; -export type { ButtonMagnitude, ButtonProminence, ButtonSizing, ButtonTone } from "./variants"; +export type { ButtonFillType, ButtonSize, ButtonVariant } from "./variants"; export type ButtonProps = Omit, "className" | "style"> & ButtonVariantProps; /** - * The styled action button element: a `
- @@ -105,7 +105,7 @@ export const Magnitudes: Story = { {magnitude} — {MAGNITUDE_WIDTHS[magnitude]} - + @@ -117,7 +117,7 @@ export const Magnitudes: Story = { - @@ -151,7 +151,7 @@ export const States: Story = { The open pose. - @@ -169,7 +169,7 @@ export const States: Story = { Pinned at opacity-0 scale-95. - @@ -187,7 +187,7 @@ export const States: Story = { Pinned at opacity-0 scale-95. - diff --git a/packages/propel/src/elements/drawer/drawer.stories.tsx b/packages/propel/src/elements/drawer/drawer.stories.tsx index 28ec4893..6eb208bb 100644 --- a/packages/propel/src/elements/drawer/drawer.stories.tsx +++ b/packages/propel/src/elements/drawer/drawer.stories.tsx @@ -81,10 +81,10 @@ export const Default: Story = { Panel body content goes here. - - diff --git a/packages/propel/src/elements/form/form.stories.tsx b/packages/propel/src/elements/form/form.stories.tsx index aee35c19..58969894 100644 --- a/packages/propel/src/elements/form/form.stories.tsx +++ b/packages/propel/src/elements/form/form.stories.tsx @@ -53,10 +53,10 @@ export const Default: Story = { - - @@ -105,7 +105,7 @@ export const MultiColumnBody: Story = { - @@ -135,10 +135,10 @@ export const StretchActions: Story = { - - diff --git a/packages/propel/src/elements/icon-button/icon-button.stories.tsx b/packages/propel/src/elements/icon-button/icon-button.stories.tsx index 2c298433..181efbf8 100644 --- a/packages/propel/src/elements/icon-button/icon-button.stories.tsx +++ b/packages/propel/src/elements/icon-button/icon-button.stories.tsx @@ -1,32 +1,21 @@ import type { Meta, StoryObj } from "@storybook/react-vite"; import { LoaderCircle, Plus } from "lucide-react"; -import type { ControlChromePair } from "../../internal/control-chrome"; import { Icon } from "../../internal/icon"; import { Spinner } from "../../internal/spinner"; import { iconControl } from "../../storybook/icon-control"; -import { - IconButton, - type IconButtonMagnitude, - type IconButtonProminence, - type IconButtonTone, -} from "./index"; +import { IconButton, type IconButtonSize, type IconButtonVariant } from "./index"; -const PROMINENCES: IconButtonProminence[] = ["primary", "secondary", "tertiary", "ghost"]; -const MAGNITUDES: IconButtonMagnitude[] = ["sm", "md", "lg", "xl"]; - -// Storybook ArgTypes flatten discriminant unions into independent controls — re-pair for the API. -const chrome = (prominence: IconButtonProminence, tone: IconButtonTone): ControlChromePair => - ({ prominence, tone }) as ControlChromePair; - -const PALETTES = [ - { prominence: "primary", tone: "neutral" }, - { prominence: "secondary", tone: "neutral" }, - { prominence: "tertiary", tone: "neutral" }, - { prominence: "ghost", tone: "neutral" }, - { prominence: "primary", tone: "danger" }, - { prominence: "secondary", tone: "danger" }, -] as const satisfies readonly ControlChromePair[]; +// Every variant (danger only exists as the filled `danger` and the bordered `danger-outline`). +const VARIANTS: IconButtonVariant[] = [ + "primary", + "secondary", + "tertiary", + "ghost", + "danger", + "danger-outline", +]; +const SIZES: IconButtonSize[] = ["sm", "md", "lg", "xl"]; // elements-tier story (rule 2b): a pure UI-configuration showcase. `IconButton` is a Base-UI-agnostic // styled square ` - + @@ -51,32 +51,27 @@ export const Default: Story = { }; /** - * Every prominence (Figma ships primary and secondary only — no tertiary/ghost, no danger tone). + * Every variant (Figma ships primary and secondary only — no tertiary/ghost, no danger palette). * Primary lays segments out as separate pills with a gap; secondary connects them — the leading * segment's own end border IS the divider. */ -export const Prominences: Story = { +export const Variants: Story = { render: () => (
- {PROMINENCES.map((prominence) => ( + {VARIANTS.map((variant) => ( - - + @@ -88,13 +83,13 @@ export const Prominences: Story = { }; /** - * CSS canary (rule 2b): asserts the frame's prominence-specific child selectors compiled — primary + * CSS canary (rule 2b): asserts the frame's variant-specific child selectors compiled — primary * keeps outer `rounded-md` but soft-squares the facing edges to 2px; secondary flattens the shared * edge (`rounded-e-none` / `rounded-s-none`) and collapses the trailing start border. Tagged out of * the sidebar/docs/manifest while still running under the default `test` tag. */ -export const ProminencesCanary: Story = { - ...Prominences, +export const VariantsCanary: Story = { + ...Variants, tags: ["!dev", "!autodocs", "!manifest"], play: async ({ canvas }) => { const [primaryMain, secondaryMain] = canvas.getAllByRole("button", { name: "Button" }); @@ -116,24 +111,19 @@ export const ProminencesCanary: Story = { }, }; -/** The four size steps — both segments of a split button always share one `magnitude`. */ -export const Magnitudes: Story = { +/** The four size steps — both segments of a split button always share one `size`. */ +export const Sizes: Story = { render: () => (
- {MAGNITUDES.map((magnitude) => ( - - - + @@ -150,20 +140,14 @@ export const Magnitudes: Story = { */ export const Disabled: Story = { render: () => ( - - - + diff --git a/packages/propel/src/elements/split-button/split-button.tsx b/packages/propel/src/elements/split-button/split-button.tsx index b2eecbc4..332fdc8f 100644 --- a/packages/propel/src/elements/split-button/split-button.tsx +++ b/packages/propel/src/elements/split-button/split-button.tsx @@ -3,7 +3,7 @@ import { useRender } from "@base-ui/react/use-render"; import { type SplitButtonVariantProps, splitButtonVariants } from "./variants"; -export type { SplitButtonMagnitude, SplitButtonProminence } from "./variants"; +export type { SplitButtonSize, SplitButtonVariant } from "./variants"; export type SplitButtonProps = Omit, "className" | "style"> & SplitButtonVariantProps; @@ -16,9 +16,9 @@ export type SplitButtonProps = Omit, "className" * own control chrome. Base-UI-agnostic (there is no Base UI split-button primitive); the * `components` ready-made composes `Button` + `MenuTrigger`-grafted `IconButton` inside it. */ -export function SplitButton({ prominence, magnitude, render, ...props }: SplitButtonProps) { +export function SplitButton({ variant, size, render, ...props }: SplitButtonProps) { const defaultProps: useRender.ElementProps<"div"> = { - className: splitButtonVariants({ prominence, magnitude }), + className: splitButtonVariants({ variant, size }), }; return useRender({ defaultTagName: "div", render, props: mergeProps(defaultProps, props) }); } diff --git a/packages/propel/src/elements/split-button/variants.ts b/packages/propel/src/elements/split-button/variants.ts index ab89ae7b..f0fabd12 100644 --- a/packages/propel/src/elements/split-button/variants.ts +++ b/packages/propel/src/elements/split-button/variants.ts @@ -3,9 +3,9 @@ import { cva, cx, type VariantProps } from "class-variance-authority"; import { type StrictVariantProps } from "../../internal/variant-props"; // SplitButton is the two-segment frame around a main action button and a menu-opening icon button -// (Figma "Split button"). No `tone` axis — Figma ships only neutral primary/secondary. The segments -// keep their own control chrome; the frame only owns how they sit together — and that layout -// differs by prominence: +// (Figma "Split button"). Figma ships only neutral primary/secondary — no danger split button. The +// segments keep their own control chrome; the frame only owns how they sit together — and that +// layout differs by variant: // // - **primary** — two discrete pills 1px apart (`gap-px`). Outer corners keep each segment's // `rounded-md`; the facing (split) edges are soft-squared to 2px via longhand @@ -23,14 +23,14 @@ import { type StrictVariantProps } from "../../internal/variant-props"; // reliably the last child (keying the flattening off `:not(:last-child)` visibly ate the open // trigger's end radius). // -// `prominence` is declared for the split-button Types only — primary and secondary; there is no -// tertiary/ghost split button. `magnitude` is color-less: it constrains the composed frame to the -// segments' shared size step (both segments of a split button are always the same magnitude). +// `variant` is declared for the split-button Types only — primary and secondary; there is no +// tertiary/ghost split button. `size` is color-less: it constrains the composed frame to the +// segments' shared size step (both segments of a split button are always the same size). export const splitButtonVariants = cva( cx("isolate inline-flex items-stretch", "[&>*:focus-visible]:z-10"), { variants: { - prominence: { + variant: { primary: cx("gap-px", "[&>*:first-child]:rounded-e-[2px]", "[&>*+*]:rounded-s-[2px]"), secondary: cx( "[&>*+*]:rounded-s-none [&>*:first-child]:rounded-e-none", @@ -38,14 +38,14 @@ export const splitButtonVariants = cva( "[&:has(:disabled)>*:first-child]:border-e-subtle", ), }, - magnitude: { sm: "", md: "", lg: "", xl: "" }, + size: { sm: "", md: "", lg: "", xl: "" }, }, }, ); type SplitButtonVariantConfig = VariantProps; -export type SplitButtonProminence = NonNullable; -export type SplitButtonMagnitude = NonNullable; +export type SplitButtonVariant = NonNullable; +export type SplitButtonSize = NonNullable; // No `defaultVariants` today, so every axis is required. export type SplitButtonVariantProps = StrictVariantProps; diff --git a/packages/propel/src/elements/toast/toast.stories.tsx b/packages/propel/src/elements/toast/toast.stories.tsx index 3455d3ba..74dde192 100644 --- a/packages/propel/src/elements/toast/toast.stories.tsx +++ b/packages/propel/src/elements/toast/toast.stories.tsx @@ -88,7 +88,7 @@ export const Default: Story = { - + @@ -106,7 +106,7 @@ export const Default: Story = { - + diff --git a/packages/propel/src/internal/button-geometry.ts b/packages/propel/src/internal/button-geometry.ts index c0c7caad..728a4eae 100644 --- a/packages/propel/src/internal/button-geometry.ts +++ b/packages/propel/src/internal/button-geometry.ts @@ -1,19 +1,19 @@ import { cva, cx, type VariantProps } from "class-variance-authority"; // The label-button geometry for surfaces that wear control chrome with a text label: `Button`. -// Owns the label row (gap/font), the per-magnitude height/min-width/padding/text + `--node-size` -// glyph scale (Figma "Buttons" Size), and full-width `sizing`. Compose with `controlChromeVariants` -// via `composeVariants`. (The inline text-link look is `linkChromeVariants` on `AnchorButton` — -// not this geometry.) +// Owns the label row (gap/font), the per-size height/min-width/padding/text + `--node-size` +// glyph scale (Figma "Buttons" Size), and full-width `fillType`. Compose with +// `controlChromeVariants` via `composeVariants`. (The inline text-link look is +// `linkChromeVariants` on `AnchorButton` — not this geometry.) export const buttonGeometryVariants = cva(cx("gap-1 font-medium whitespace-nowrap"), { variants: { - magnitude: { + size: { sm: "h-5 min-w-10 px-1.5 text-12 leading-none [--node-size:0.875rem]", md: "h-6 min-w-10 px-2 text-13 leading-none [--node-size:0.875rem]", lg: "h-7 min-w-12 px-2 text-13 leading-none [--node-size:1rem]", xl: "h-8 min-w-13 px-2 text-14 leading-none [--node-size:1rem]", }, - sizing: { hug: "", fill: "w-full" }, + fillType: { hug: "", fill: "w-full" }, }, }); diff --git a/packages/propel/src/internal/control-chrome.ts b/packages/propel/src/internal/control-chrome.ts index e1f86992..a0de6f15 100644 --- a/packages/propel/src/internal/control-chrome.ts +++ b/packages/propel/src/internal/control-chrome.ts @@ -4,10 +4,15 @@ import { cva, cx, type VariantProps } from "class-variance-authority"; * The control chrome shared by the button-look surfaces built on Figma's button tokens — `Button` * and `IconButton` (both default to `