Skip to content

Commit 9d439c3

Browse files
committed
fix(ui): align action button roles across product surfaces
Use primary for submit, save, create, and confirm actions, and fill for cancel or dismiss actions. Correct shared confirmations, approval actions, notifications, recommendations, Canvas primary mapping, and Design Lab examples. Keep danger semantics and intentional selection/utility variants. Preserve dimensions, callbacks, disabled/loading conditions, and transport logic. Document variant roles and cover shared confirmation choices, notification actions, settings actions, Canvas mapping, and AskUser output. Validation: design-system:check; check:web; 20 shared action regression tests; 110 focused editor tests. Manual visual and live remote checks remain pending.
1 parent fd7ae48 commit 9d439c3

115 files changed

Lines changed: 340 additions & 258 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎design-system/apps/design-lab/src/pages/ComponentDetailPage.tsx‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -552,7 +552,7 @@ export function ComponentDetailPage({
552552
return `import { Icon, Menu, MenuItem, MenuSection, MenuSeparator } from "@openbitfun/ui";\n\n<Menu\n aria-label="${t("components.preview.menuLabel")}"\n scrollbarVisibility="${menuShowScrollbar ? "auto" : "hidden"}"\n>\n <MenuSection title="${t("components.preview.menuSectionTitle")}">\n <MenuItem leading={<Icon name="session" />}>${t("components.preview.menuItemOne")}</MenuItem>\n <MenuItem leading={<Icon name="session" />}>${t("components.preview.menuItemTwo")}</MenuItem>\n </MenuSection>\n <MenuSeparator />\n <MenuSection aria-label="${t("components.preview.menuMoreSection")}">\n <MenuItem disabled>${t("components.preview.menuDisabledItem")}</MenuItem>\n </MenuSection>\n</Menu>`;
553553
}
554554
if (component.name === "Dialog") {
555-
return `import { Button, Dialog, DialogBody, DialogClose, DialogFooter, DialogHeader, DialogHeading, DialogTitle } from "@openbitfun/ui";\n\n<Dialog onOpenChange={() => setOpen(false)} open={open} size="xl">\n <DialogHeader>\n <DialogHeading><DialogTitle>${t("components.preview.modalTitle")}</DialogTitle></DialogHeading>\n <DialogClose />\n </DialogHeader>\n <DialogBody><ProviderConfigurationFields /></DialogBody>\n <DialogFooter appearance="floating">\n <Button onClick={() => setOpen(false)} variant="secondary">${t("components.preview.modalCancel")}</Button>\n <Button onClick={() => setOpen(false)} variant="primary">${t("components.preview.modalSave")}</Button>\n </DialogFooter>\n</Dialog>`;
555+
return `import { Button, Dialog, DialogBody, DialogClose, DialogFooter, DialogHeader, DialogHeading, DialogTitle } from "@openbitfun/ui";\n\n<Dialog onOpenChange={() => setOpen(false)} open={open} size="xl">\n <DialogHeader>\n <DialogHeading><DialogTitle>${t("components.preview.modalTitle")}</DialogTitle></DialogHeading>\n <DialogClose />\n </DialogHeader>\n <DialogBody><ProviderConfigurationFields /></DialogBody>\n <DialogFooter appearance="floating">\n <Button onClick={() => setOpen(false)} variant="fill">${t("components.preview.modalCancel")}</Button>\n <Button onClick={() => setOpen(false)} variant="primary">${t("components.preview.modalSave")}</Button>\n </DialogFooter>\n</Dialog>`;
556556
}
557557
if (component.name === "Sheet") {
558558
return `import { Button, DialogBody, DialogClose, DialogFooter, DialogHeader, DialogHeading, DialogTitle, Sheet } from "@openbitfun/ui";\n\n<Sheet onOpenChange={() => setOpen(false)} open={open} placement="right" size="lg">\n <DialogHeader>\n <DialogHeading><DialogTitle>${t("components.preview.modalTitle")}</DialogTitle></DialogHeading>\n <DialogClose />\n </DialogHeader>\n <DialogBody><ProviderConfigurationFields /></DialogBody>\n <DialogFooter>\n <Button onClick={() => setOpen(false)} variant="fill">${t("components.preview.modalCancel")}</Button>\n <Button onClick={() => setOpen(false)} variant="primary">${t("components.preview.modalSave")}</Button>\n </DialogFooter>\n</Sheet>`;
@@ -788,7 +788,7 @@ export function ComponentDetailPage({
788788
</DialogHeader>
789789
<DialogBody className="component-dialog-example__body">{renderDialogConfigurationContent()}</DialogBody>
790790
<DialogFooter appearance="floating">
791-
<Button onClick={closePreview} variant="secondary">
791+
<Button onClick={closePreview} variant="fill">
792792
{t("components.preview.modalCancel")}
793793
</Button>
794794
<Button onClick={closePreview} variant="primary">

‎design-system/apps/design-lab/src/pages/ComponentsPage.tsx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,7 @@ function ComponentCardPreview({ component }: { component: ComponentMeta }) {
215215
case "Button":
216216
return (
217217
<Stack align="center" direction="horizontal" gap="2" wrap>
218-
<Button variant="fill">{t("components.preview.primary")}</Button>
218+
<Button variant="primary">{t("components.preview.primary")}</Button>
219219
<Button>{t("components.preview.button")}</Button>
220220
</Stack>
221221
);

‎design-system/apps/design-lab/src/pages/PatternsPage.tsx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@ export function PatternsPage({ colorScheme, contrast, density, tokenOverrides }:
172172
<Card appearance="subtle" className="pattern-device-card" data-openbitfun-pattern="device-card" gap="md" padding="md" radius="md">
173173
<CardHeader actions={<IconButton aria-label={t("patterns.device.refresh")} icon={<Icon name="refresh" />} size="sm" variant="quiet" />} description="macOS · 127.0.0.1" leading={<span className="pattern-device-icon"><Icon name="device-mac" size="lg" /></span>} title="MacBook Pro" />
174174
<CardBody><StatusPill leading={<Icon name="unselected" />} tone="success">{t("patterns.device.online")}</StatusPill></CardBody>
175-
<Button leadingIcon={<Icon name="link" />} size="sm" variant="fill">{t("patterns.device.connect")}</Button>
175+
<Button leadingIcon={<Icon name="link" />} size="sm" variant="primary">{t("patterns.device.connect")}</Button>
176176
</Card>
177177
</PatternSection>
178178
<PatternSection description={t("patterns.provider.description")} index="05" title={t("patterns.provider.title")}>

‎design-system/apps/design-lab/src/pages/ReferencePatterns.tsx‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,8 @@ export function ProviderConfigurationPattern() {
4141
const [revision, setRevision] = useState(0);
4242
const [saved, setSaved] = useState(false);
4343
const footer = (close: () => void) => <CardFooter align="center">
44-
<Button variant="outline" onClick={close}>{t("components.preview.modalCancel")}</Button>
45-
<Button variant="fill" onClick={() => { setSaved(true); setOpen(false); }}>{t("components.preview.modalSave")}</Button>
44+
<Button variant="fill" onClick={close}>{t("components.preview.modalCancel")}</Button>
45+
<Button variant="primary" onClick={() => { setSaved(true); setOpen(false); }}>{t("components.preview.modalSave")}</Button>
4646
</CardFooter>;
4747

4848
return <div className="pattern-provider" data-openbitfun-pattern="provider-configuration">

‎design-system/packages/ui/README.md‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,13 @@ The package owns component anatomy, behavior, accessibility, and stable variants
2020

2121
## Buttons
2222

23+
Choose variants by action role: use `primary` for the main save, submit, create,
24+
or confirm action, and `fill` for cancel, dismiss, or discard alongside it.
25+
Keep `outline` for ordinary toolbar utilities and secondary choices. A neutral
26+
`fill` button is a low-emphasis surface, not an alias for `primary`. Preserve
27+
`tone="danger"` for destructive actions. Disabled and loading states belong to
28+
the same variant; do not switch a primary action to outline when it is disabled.
29+
2330
Button outline and text variants have transparent resting surfaces. Fill and
2431
primary state colors come from the theme's `component.button.*` contract, with
2532
variant-specific disabled content. The secondary variant keeps its opaque

‎design-system/packages/ui/src/components/ConfirmDialog/ConfirmDialog.tsx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,7 @@ export const ConfirmDialog = forwardRef<HTMLDivElement, ConfirmDialogProps>(
206206
onClick={() => void runAction("confirm", onConfirm)}
207207
ref={confirmButtonRef}
208208
tone={confirmDanger || type === "error" ? "danger" : "neutral"}
209-
variant={confirmDanger || type === "error" ? "primary" : "fill"}
209+
variant="primary"
210210
>
211211
{resolvedConfirmText}
212212
</Button>

‎design-system/packages/ui/src/flow-chat/ask-user/AskUser.tsx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -453,7 +453,7 @@ export const AskUser = forwardRef<HTMLDivElement, AskUserProps>(function AskUser
453453
onClick={onSubmit}
454454
size="sm"
455455
title={submitTitle}
456-
variant="fill"
456+
variant="primary"
457457
>
458458
{state === "submitting" && submittingLabel !== undefined
459459
? submittingLabel

‎design-system/packages/ui/tests/ask-user.test.mjs‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ test("AskUser renders a controlled custom answer with an accessible text field",
7070
assert.match(markup, /aria-label="Custom version"/);
7171
assert.match(markup, /value="v0.2.17"/);
7272
assert.match(markup, /data-openbitfun-part="submit"/);
73+
assert.match(markup, /data-openbitfun-component="button"[^>]+data-openbitfun-variant="primary"/);
7374
});
7475

7576
test("AskUser styles use public semantic and component geometry tokens", async () => {

‎src/web-ui/src/app/components/AboutDialog/AboutDialog.tsx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -430,7 +430,7 @@ export const AboutDialog: React.FC<AboutDialogProps> = ({
430430
<Icon name="check-circle" size="sm" className="openbitfun-about-dialog__update-status-icon" aria-hidden="true" />
431431
<span>{t('update.readyVersion', { version: updateVersion ?? '' })}</span>
432432
</div>
433-
<Button variant="fill" size="sm" disabled={updateStatus === 'installing'} onClick={onRestart}>
433+
<Button variant="primary" size="sm" disabled={updateStatus === 'installing'} onClick={onRestart}>
434434
{t(updateStatus === 'installing' ? 'update.installing' : 'update.installAndRestart')}
435435
</Button>
436436
</div>

‎src/web-ui/src/app/components/AppErrorBoundary.tsx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ export class AppErrorBoundary extends Component<Props, State> {
7676
<p style={{ margin: '12px 0 0', opacity: 0.9 }}>{firstLine}</p>
7777
<div style={{ marginTop: 16 }}>
7878
<Button
79-
variant="fill"
79+
variant="primary"
8080
size="sm"
8181
onClick={this.handleReload}
8282
>

0 commit comments

Comments
 (0)