Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion design-system/apps/design-lab/src/pages/ColorsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ interface ColorsPageProps {
}

const semanticColorTokens = themeTokenCatalog.filter(
(token): token is ThemeTokenCatalogEntry => token.category === "color",
(token): token is ThemeTokenCatalogEntry => token.type === "color",
);

const semanticGroupOrder: readonly SemanticColorGroup[] = [
Expand Down Expand Up @@ -126,6 +126,7 @@ const referenceNameByValue = new Map(
);

function getSemanticGroup(token: ThemeTokenCatalogEntry): SemanticColorGroup {
if (token.name.startsWith("component.button.")) return "action";
const segment = token.name.split(".")[1] as SemanticColorGroup | undefined;
return segment && semanticGroupOrder.includes(segment) ? segment : "accent";
}
Expand Down
67 changes: 36 additions & 31 deletions design-system/apps/design-lab/src/pages/ComponentDetailPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -552,7 +552,7 @@ export function ComponentDetailPage({
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>`;
}
if (component.name === "Dialog") {
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>`;
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>`;
}
if (component.name === "Sheet") {
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>`;
Expand Down Expand Up @@ -788,7 +788,7 @@ export function ComponentDetailPage({
</DialogHeader>
<DialogBody className="component-dialog-example__body">{renderDialogConfigurationContent()}</DialogBody>
<DialogFooter appearance="floating">
<Button onClick={closePreview} variant="secondary">
<Button onClick={closePreview} variant="fill">
{t("components.preview.modalCancel")}
</Button>
<Button onClick={closePreview} variant="primary">
Expand Down Expand Up @@ -2106,38 +2106,43 @@ export function ComponentDetailPage({
))}
</div>
) : component.name === "Button" ? (
<div
className="component-preview-matrix"
data-component="button"
data-state-count={states.length}
>
<span className="component-preview-matrix__corner" />
{states.map((state, index) => (
<span
className="component-preview-matrix__column-label"
data-last={index === states.length - 1 || undefined}
key={state}
(["plain", "subtle"] as const).map((surface) => (
<section className="button-state-surface" data-surface={surface} key={surface}>
<h3>{t(surface === "plain" ? "detail.option.plain" : "detail.option.subtle")}</h3>
<div
className="component-preview-matrix"
data-component="button"
data-state-count={states.length}
>
{stateLabel(state)}
</span>
))}
{buttonVariants.map((matrixVariant) => (
<Fragment key={matrixVariant}>
<span className="component-preview-matrix__row-label">
{stateLabel(matrixVariant)}
</span>
{states.map((state) => (
<div
className="component-preview-matrix__cell"
data-active={matrixVariant === variant && state === previewState || undefined}
key={`${matrixVariant}-${state}`}
<span className="component-preview-matrix__corner" />
{states.map((state, index) => (
<span
className="component-preview-matrix__column-label"
data-last={index === states.length - 1 || undefined}
key={state}
>
{renderPreview(state, matrixVariant)}
</div>
{stateLabel(state)}
</span>
))}
</Fragment>
))}
</div>
{buttonVariants.map((matrixVariant) => (
<Fragment key={matrixVariant}>
<span className="component-preview-matrix__row-label">
{stateLabel(matrixVariant)}
</span>
{states.map((state) => (
<div
className="component-preview-matrix__cell"
data-active={matrixVariant === variant && state === previewState || undefined}
key={`${matrixVariant}-${state}`}
>
{renderPreview(state, matrixVariant)}
</div>
))}
</Fragment>
))}
</div>
</section>
))
) : component.name === "Icon" ? (
<>
<IconCompositionPreview />
Expand Down
2 changes: 1 addition & 1 deletion design-system/apps/design-lab/src/pages/ComponentsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ function ComponentCardPreview({ component }: { component: ComponentMeta }) {
case "Button":
return (
<Stack align="center" direction="horizontal" gap="2" wrap>
<Button variant="fill">{t("components.preview.primary")}</Button>
<Button variant="primary">{t("components.preview.primary")}</Button>
<Button>{t("components.preview.button")}</Button>
</Stack>
);
Expand Down
2 changes: 1 addition & 1 deletion design-system/apps/design-lab/src/pages/PatternsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ export function PatternsPage({ colorScheme, contrast, density, tokenOverrides }:
<Card appearance="subtle" className="pattern-device-card" data-openbitfun-pattern="device-card" gap="md" padding="md" radius="md">
<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" />
<CardBody><StatusPill leading={<Icon name="unselected" />} tone="success">{t("patterns.device.online")}</StatusPill></CardBody>
<Button leadingIcon={<Icon name="link" />} size="sm" variant="fill">{t("patterns.device.connect")}</Button>
<Button leadingIcon={<Icon name="link" />} size="sm" variant="primary">{t("patterns.device.connect")}</Button>
</Card>
</PatternSection>
<PatternSection description={t("patterns.provider.description")} index="05" title={t("patterns.provider.title")}>
Expand Down
4 changes: 2 additions & 2 deletions design-system/apps/design-lab/src/pages/ReferencePatterns.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ export function ProviderConfigurationPattern() {
const [revision, setRevision] = useState(0);
const [saved, setSaved] = useState(false);
const footer = (close: () => void) => <CardFooter align="center">
<Button variant="outline" onClick={close}>{t("components.preview.modalCancel")}</Button>
<Button variant="fill" onClick={() => { setSaved(true); setOpen(false); }}>{t("components.preview.modalSave")}</Button>
<Button variant="fill" onClick={close}>{t("components.preview.modalCancel")}</Button>
<Button variant="primary" onClick={() => { setSaved(true); setOpen(false); }}>{t("components.preview.modalSave")}</Button>
</CardFooter>;

return <div className="pattern-provider" data-openbitfun-pattern="provider-configuration">
Expand Down
16 changes: 16 additions & 0 deletions design-system/apps/design-lab/src/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -4135,6 +4135,22 @@ input.lab-force-focus {
min-width: 112px;
}

.button-state-surface {
width: 100%;
background: var(--openbitfun-color-surface-panel);
}

.button-state-surface[data-surface="subtle"] {
background: var(--openbitfun-color-surface-tertiary);
}

.button-state-surface > h3 {
margin: 0;
padding: var(--openbitfun-space-4);
color: var(--openbitfun-color-content-primary);
font-size: var(--openbitfun-type-body-sm-font-size);
}

.component-code-panel--standalone {
border-top: 1px solid var(--openbitfun-color-border-subtle);
background: var(--openbitfun-color-surface-panel);
Expand Down
4 changes: 2 additions & 2 deletions design-system/apps/design-lab/src/token-editor/catalog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,11 @@ export const editableTokenCatalog: readonly EditableToken[] = [
];

export const colorTokenCatalog: readonly EditableToken[] = editableTokenCatalog.filter(
(token) => token.category === "color",
(token) => token.type === "color",
);

export const nonColorTokenCatalog: readonly EditableToken[] = editableTokenCatalog.filter(
(token) => token.category !== "color",
(token) => token.type !== "color",
);

const categoryLabels: Readonly<Record<string, string>> = {
Expand Down
5 changes: 3 additions & 2 deletions design-system/apps/design-lab/vite/token-authoring-plugin.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,9 @@ async function applySourceChanges(designSystemDirectory, changes) {
if (!target) {
throw new Error(`Unsupported token target "${targetKey}".`);
}
if (change.collection === "theme" && !change.name.startsWith("color.")) {
if (change.collection === "theme"
&& !change.name.startsWith("color.")
&& !change.name.startsWith("component.button.")) {
throw new Error("Only public semantic color tokens can be written to theme sources.");
}
if (change.collection === "system" && change.name.startsWith("color.")) {
Expand Down Expand Up @@ -348,4 +350,3 @@ export function createTokenAuthoringPlugin({ designSystemDirectory }) {
},
};
}

14 changes: 14 additions & 0 deletions design-system/packages/theme-openbitfun/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,20 @@ The same data is available as `@openbitfun/theme-openbitfun/reference-colors.jso

## Surface and state roles

`component.button.*` owns Button's state palette. Its light fill stays at black
8% while the shared neutral actions retain their 5/8/10% feedback; its primary
background uses black 80/60/90% and disabled content 20%. Outline and text variants
composite directly over the caller's surface. These differences cannot be
represented by changing the shared action palette without changing menus,
IconButton, and other controls. Dark and high-contrast mappings retain their
mode-specific feedback and outline contrast. Color entries remain editable in
Design Lab's Colors catalog; Button geometry is independent of this palette.

The default Web UI appearances consume these published component values.
Branded presets and imported appearances may still supply the existing action
tokens: the Web UI inherits explicit old values only when the corresponding
component token is absent, and keeps explicit component overrides intact.

- `color.surface.scene`, `panel`, and `raised` own primary content and elevated planes.
- `color.surface.chrome` owns persistent application structure such as navigation and window-control regions.
- `color.surface.tertiary` is an opaque low-emphasis fill for persistent grouped content such as cards and field groups.
Expand Down
2 changes: 1 addition & 1 deletion design-system/packages/theme-openbitfun/scripts/build.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ const highContrastDarkTokens = resolveTokens(
mergeTokenDocuments(reference, dark, highContrastDark),
);

const PUBLIC_THEME_TOKEN_PREFIXES = ["color.", "effect.", "opacity.", "shadow."];
const PUBLIC_THEME_TOKEN_PREFIXES = ["color.", "component.button.", "effect.", "opacity.", "shadow."];
const REFERENCE_COLOR_TOKEN_PATTERN = /^ref\.color\.([a-z][a-z0-9-]*)\.(\d+)$/;

function createReferenceColorArtifacts(document, tokens) {
Expand Down
18 changes: 18 additions & 0 deletions design-system/packages/theme-openbitfun/src/dark.tokens.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,23 @@
{
"$description": "OpenBitFun dark semantic theme migrated from the built-in dark Appearance.",
"component": {
"button": {
"$description": "Button-specific state colors. Keep shared action colors stable for other controls and preserve contrast-mode feedback.",
"content": { "$type": "color", "$value": "{color.action.neutral.content}" },
"textContent": { "$type": "color", "$value": "{color.accent.default}" },
"textContentHover": { "$type": "color", "$value": "{color.accent.hover}" },
"textContentDisabled": { "$type": "color", "$value": "{color.accent.disabled}" },
"outlineBorder": { "$type": "color", "$value": "{color.action.neutral.border}" },
"outlineBorderInteractive": { "$type": "color", "$value": "{color.action.neutral.border}" },
"fillBackground": { "$type": "color", "$value": "{color.action.neutral.surface}" },
"fillBackgroundHover": { "$type": "color", "$value": "{color.action.neutral.surfaceHover}" },
"fillBackgroundPressed": { "$type": "color", "$value": "{color.action.neutral.surfacePressed}" },
"primaryBackground": { "$type": "color", "$value": "{color.action.primary.background}" },
"primaryBackgroundHover": { "$type": "color", "$value": "{color.action.primary.hover}" },
"primaryBackgroundPressed": { "$type": "color", "$value": "{color.action.primary.pressed}" },
"primaryContentDisabled": { "$type": "color", "$value": "{color.action.neutral.contentDisabled}" }
}
},
"color": {
"surface": {
"canvas": { "$type": "color", "$value": "{ref.color.neutral.950}" },
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,23 @@
{
"$description": "High-contrast overrides for the dark scheme.",
"component": {
"button": {
"$description": "Button-specific state colors. Keep shared action colors stable for other controls and preserve contrast-mode feedback.",
"content": { "$type": "color", "$value": "{color.action.neutral.content}" },
"textContent": { "$type": "color", "$value": "{color.accent.default}" },
"textContentHover": { "$type": "color", "$value": "{color.accent.hover}" },
"textContentDisabled": { "$type": "color", "$value": "{color.accent.disabled}" },
"outlineBorder": { "$type": "color", "$value": "{color.action.neutral.border}" },
"outlineBorderInteractive": { "$type": "color", "$value": "{color.action.neutral.border}" },
"fillBackground": { "$type": "color", "$value": "{color.action.neutral.surface}" },
"fillBackgroundHover": { "$type": "color", "$value": "{color.action.neutral.surfaceHover}" },
"fillBackgroundPressed": { "$type": "color", "$value": "{color.action.neutral.surfacePressed}" },
"primaryBackground": { "$type": "color", "$value": "{color.action.primary.background}" },
"primaryBackgroundHover": { "$type": "color", "$value": "{color.action.primary.hover}" },
"primaryBackgroundPressed": { "$type": "color", "$value": "{color.action.primary.pressed}" },
"primaryContentDisabled": { "$type": "color", "$value": "{color.action.neutral.contentDisabled}" }
}
},
"color": {
"surface": {
"canvas": { "$type": "color", "$value": "{ref.color.gray.1000}" },
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,23 @@
{
"$description": "High-contrast overrides for the light scheme.",
"component": {
"button": {
"$description": "Button-specific state colors. Keep shared action colors stable for other controls and preserve contrast-mode feedback.",
"content": { "$type": "color", "$value": "{color.action.neutral.content}" },
"textContent": { "$type": "color", "$value": "{color.accent.default}" },
"textContentHover": { "$type": "color", "$value": "{color.accent.hover}" },
"textContentDisabled": { "$type": "color", "$value": "{color.accent.disabled}" },
"outlineBorder": { "$type": "color", "$value": "{color.action.neutral.border}" },
"outlineBorderInteractive": { "$type": "color", "$value": "{color.action.neutral.border}" },
"fillBackground": { "$type": "color", "$value": "{color.action.neutral.surface}" },
"fillBackgroundHover": { "$type": "color", "$value": "{color.action.neutral.surfaceHover}" },
"fillBackgroundPressed": { "$type": "color", "$value": "{color.action.neutral.surfacePressed}" },
"primaryBackground": { "$type": "color", "$value": "{color.action.primary.background}" },
"primaryBackgroundHover": { "$type": "color", "$value": "{color.action.primary.hover}" },
"primaryBackgroundPressed": { "$type": "color", "$value": "{color.action.primary.pressed}" },
"primaryContentDisabled": { "$type": "color", "$value": "{color.action.neutral.contentDisabled}" }
}
},
"color": {
"surface": {
"canvas": { "$type": "color", "$value": "{ref.color.gray.0}" },
Expand Down
18 changes: 18 additions & 0 deletions design-system/packages/theme-openbitfun/src/light.tokens.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,23 @@
{
"$description": "OpenBitFun light semantic theme migrated from the built-in light Appearance.",
"component": {
"button": {
"$description": "Button-specific state colors. Keep shared action colors stable for other controls and preserve contrast-mode feedback.",
"content": { "$type": "color", "$value": "{color.action.neutral.content}" },
"textContent": { "$type": "color", "$value": "{color.accent.default}" },
"textContentHover": { "$type": "color", "$value": "{color.accent.hover}" },
"textContentDisabled": { "$type": "color", "$value": "{color.accent.disabled}" },
"outlineBorder": { "$type": "color", "$value": "{color.action.neutral.surfaceHover}" },
"outlineBorderInteractive": { "$type": "color", "$value": "transparent" },
"fillBackground": { "$type": "color", "$value": "{color.action.neutral.surfaceHover}" },
"fillBackgroundHover": { "$type": "color", "$value": "{color.action.neutral.surfaceHover}" },
"fillBackgroundPressed": { "$type": "color", "$value": "{color.action.neutral.surfaceHover}" },
"primaryBackground": { "$type": "color", "$value": "rgba(0, 0, 0, 0.80)" },
"primaryBackgroundHover": { "$type": "color", "$value": "rgba(0, 0, 0, 0.60)" },
"primaryBackgroundPressed": { "$type": "color", "$value": "rgba(0, 0, 0, 0.90)" },
"primaryContentDisabled": { "$type": "color", "$value": "rgba(0, 0, 0, 0.20)" }
}
},
"color": {
"surface": {
"canvas": { "$type": "color", "$value": "{ref.color.neutral.50}" },
Expand Down
Loading
Loading