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
Original file line number Diff line number Diff line change
Expand Up @@ -618,7 +618,7 @@ export function ComponentDetailPage({
return `import { Icon, SegmentedControl } from "@openbitfun/ui";\n\n<SegmentedControl\n size="md"\n aria-label="${t("components.preview.segmentedLabel")}"\n defaultValue="${defaultMode}"\n onValueChange={setMode}\n options={[\n { icon: <Icon name="session" />, label: "${t("components.preview.segmentedChat")}", value: "chat" },\n { label: "${t("components.preview.segmentedAgent")}", value: "agent" },\n ]}\n/>`;
}
if (component.name === "StatusPill") {
return `import { Icon, StatusPill } from "@openbitfun/ui";\n\n<StatusPill leading={<Icon name="unselected" />} tone="${previewState}">\n Ask\n</StatusPill>`;
return `import { Icon, StatusPill } from "@openbitfun/ui";\n\n<StatusPill emphasis leading={<Icon name="unselected" />} tone="${previewState}">\n Ask\n</StatusPill>`;
}
if (component.name === "Disclosure") {
const stateProps = previewState === "open" ? " defaultOpen" : previewState === "disabled" ? " disabled" : "";
Expand Down Expand Up @@ -1149,6 +1149,7 @@ export function ComponentDetailPage({
if (component.name === "StatusPill") {
return (
<StatusPill
emphasis
leading={<Icon name="unselected" />}
tone={state as StatusPillTone}
>
Expand Down
4 changes: 3 additions & 1 deletion design-system/apps/design-lab/src/pages/PatternsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ import {
type TokenOverrides,
} from "@openbitfun/ui";
import { useI18n, type MessageKey } from "../i18n";
import { FormTypographyPattern, NestedMenuPattern, ProviderConfigurationPattern, SceneToolbarPattern, WorkspaceConfigurationPattern } from "./ReferencePatterns";
import { IndicatorsPattern, FormTypographyPattern, NestedMenuPattern, ProviderConfigurationPattern, SceneToolbarPattern, WorkspaceConfigurationPattern } from "./ReferencePatterns";

interface PatternsPageProps {
colorScheme: ColorScheme;
Expand Down Expand Up @@ -80,6 +80,8 @@ export function PatternsPage({ colorScheme, contrast, density, tokenOverrides }:
<p>{t("patterns.description")}</p>
</header>

<IndicatorsPattern />

<PatternSection description={t("patterns.settings.description")} index="01" title={t("patterns.settings.title")}>
<Card appearance="raised" className="pattern-settings" data-openbitfun-pattern="settings-form" padding="md" radius="md">
<PageHeader description={t("components.preview.appearanceDescription")} level={3} size="md" title={t("components.preview.appearance")} />
Expand Down
13 changes: 13 additions & 0 deletions design-system/apps/design-lab/src/pages/ReferencePatterns.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ import {
MenuPopover,
MultiSelect,
NumberInput,
NumberBadge,
LauncherButton,
PageHeader,
SearchField,
Select,
Expand All @@ -36,6 +38,17 @@ import {
} from "@openbitfun/ui";
import { useI18n } from "../i18n";

export function IndicatorsPattern() {
const { t } = useI18n();
const [enabled, setEnabled] = useState(false);
return <Toolbar bordered={false} leadingOverflow="scroll" data-openbitfun-pattern="inline-indicators" leading={<>
<ToolbarGroup><NumberBadge value={0} /><NumberBadge value={18} /><NumberBadge value="1234" /></ToolbarGroup>
<ToolbarGroup><KeyHint icon="Ctrl">K</KeyHint><KeyHint icon={<Icon name="command-mac" />}>K</KeyHint></ToolbarGroup>
<ToolbarGroup><StatusPill emphasis leading={<Icon name="unselected" />}>Ask</StatusPill><StatusPill tone="success">{t("patterns.device.online")}</StatusPill></ToolbarGroup>
<ToolbarGroup><Switch aria-label={t("patterns.settings.pointer")} checked={enabled} onCheckedChange={setEnabled} /><LauncherButton leadingIcon={<Icon name="session" />}>Hello</LauncherButton></ToolbarGroup>
</>} />;
}

export function FormTypographyPattern() {
const { t } = useI18n();
return <Card appearance="subtle" padding="md" data-openbitfun-pattern="form-typography">
Expand Down
21 changes: 16 additions & 5 deletions design-system/packages/design-tokens/src/system.tokens.json
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,10 @@
"lineHeight": { "$value": "{lineHeight.tight}" },
"letterSpacing": { "$value": "{letterSpacing.normal}" }
},
"statusPill": {
"$description": "Short status leading: 14px at 11px, scaling with typography.",
"lineHeight": { "$type": "number", "$value": 1.2727272727272727 }
},
"fieldHelper": {
"$description": "Field helper leading: 16px at the default 11px type size, scaling with user typography.",
"lineHeight": { "$type": "number", "$value": 1.4545454545454546 }
Expand Down Expand Up @@ -291,6 +295,13 @@
}
},
"code": {
"meta": {
"fontFamily": { "$value": "{font.family.mono}" },
"fontSize": { "$value": "{font.size.meta}" },
"fontWeight": { "$value": "{font.weight.regular}" },
"lineHeight": { "$value": "{lineHeight.tight}" },
"letterSpacing": { "$value": "{letterSpacing.normal}" }
},
"sm": {
"fontFamily": { "$value": "{font.family.mono}" },
"fontSize": { "$value": "{font.size.xs}" },
Expand Down Expand Up @@ -618,11 +629,11 @@
"launcherButton": {
"$description": "Geometry for a shell-edge launcher with an icon and short label.",
"$type": "dimension",
"minInlineSize": { "$value": "104px" },
"minInlineSize": { "$value": "72px" },
"blockSize": { "$value": "{control.height.md}" },
"paddingInline": { "$value": "14px" },
"gap": { "$value": "{space.2}" },
"iconSize": { "$value": "16px" },
"paddingInline": { "$value": "10px" },
"gap": { "$value": "{space.1}" },
"iconSize": { "$value": "12px" },
"radius": { "$value": "{radius.lg}" }
},
"changeCount": {
Expand All @@ -635,7 +646,7 @@
"statusPill": {
"$type": "dimension",
"gap": { "$value": "2px" },
"paddingBlock": { "$value": "3px" },
"paddingBlock": { "$value": "2px" },
"paddingInline": { "$value": "6px" },
"radius": { "$value": "{radius.pill}" },
"iconSize": { "$value": "14px" }
Expand Down
10 changes: 5 additions & 5 deletions design-system/packages/design-tokens/tests/contract.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ test("StatusPill geometry preserves compact semantic status anatomy", async () =
const systemDocument = await readSource("system.tokens.json");

assert.equal(tokens["control.statusPill.gap"], "2px");
assert.equal(tokens["control.statusPill.paddingBlock"], "3px");
assert.equal(tokens["control.statusPill.paddingBlock"], "2px");
assert.equal(tokens["control.statusPill.paddingInline"], "6px");
assert.equal(tokens["control.statusPill.iconSize"], "14px");
assert.equal(systemDocument.control.statusPill.radius.$value, "{radius.pill}");
Expand Down Expand Up @@ -145,11 +145,11 @@ test("ActionCard geometry preserves compact and descriptive entry compositions",
test("LauncherButton geometry preserves the shell-edge action contract", async () => {
const systemDocument = await readSource("system.tokens.json");

assert.equal(tokens["control.launcherButton.minInlineSize"], "104px");
assert.equal(tokens["control.launcherButton.minInlineSize"], "72px");
assert.equal(tokens["control.launcherButton.blockSize"], "40px");
assert.equal(tokens["control.launcherButton.paddingInline"], "14px");
assert.equal(tokens["control.launcherButton.gap"], "8px");
assert.equal(tokens["control.launcherButton.iconSize"], "16px");
assert.equal(tokens["control.launcherButton.paddingInline"], "10px");
assert.equal(tokens["control.launcherButton.gap"], "4px");
assert.equal(tokens["control.launcherButton.iconSize"], "12px");
assert.equal(
systemDocument.control.launcherButton.radius.$value,
"{radius.lg}",
Expand Down
2 changes: 2 additions & 0 deletions design-system/packages/theme-openbitfun/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,3 +79,5 @@ in both root and chrome; generic palette projection previously reduced product
menu labels to secondary text (60%). Explicit imported action colors still win.

Action cards own `color.actionCard.background`: the light entry surface is black at 3% opacity. `surface.subtle` is a transient navy tint and `field.groupBackground` belongs to form groups, so neither represents this persistent action surface. Other modes retain their neutral action surface. Product Appearance preserves explicit legacy neutral-surface overrides in root and chrome.

Compact indicators own `color.numberBadge.background` and `color.keyHint.content` so reference light values (8% fill and 60% text) can coexist with existing dark/contrast values and explicit legacy Appearance overrides. The light Switch off track and resting scrollbar thumb use 10% black; KeyHint and Launcher resting fills use 8%. Long status labels keep content colors; StatusPill emphasis is opt-in.
4 changes: 4 additions & 0 deletions design-system/packages/theme-openbitfun/src/dark.tokens.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@
}
},
"color": {
"numberBadge": {
"background": { "$type": "color", "$value": "{color.action.neutral.surface}" }
},
"actionCard": {
"background": { "$type": "color", "$value": "{color.action.neutral.surface}" }
},
Expand Down Expand Up @@ -60,6 +63,7 @@
}
},
"keyHint": {
"content": { "$type": "color", "$value": "{color.content.muted}" },
"background": { "$type": "color", "$value": "{color.action.neutral.surface}" }
},
"scrollbar": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,12 @@
}
},
"color": {
"keyHint": {
"content": { "$type": "color", "$value": "{color.content.muted}" }
},
"numberBadge": {
"background": { "$type": "color", "$value": "{color.action.neutral.surface}" }
},
"actionCard": {
"background": { "$type": "color", "$value": "{color.action.neutral.surface}" }
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,12 @@
}
},
"color": {
"keyHint": {
"content": { "$type": "color", "$value": "{color.content.muted}" }
},
"numberBadge": {
"background": { "$type": "color", "$value": "{color.action.neutral.surface}" }
},
"actionCard": {
"background": { "$type": "color", "$value": "{color.action.neutral.surface}" }
},
Expand Down
12 changes: 8 additions & 4 deletions design-system/packages/theme-openbitfun/src/light.tokens.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@
}
},
"color": {
"numberBadge": {
"background": { "$type": "color", "$value": "{color.action.neutral.surfaceHover}" }
},
"actionCard": {
"background": { "$type": "color", "$value": "rgba(0, 0, 0, 0.03)" }
},
Expand Down Expand Up @@ -60,10 +63,11 @@
}
},
"keyHint": {
"background": { "$type": "color", "$value": "{color.action.neutral.surface}" }
"content": { "$type": "color", "$value": "{color.content.secondary}" },
"background": { "$type": "color", "$value": "{color.action.neutral.surfaceHover}" }
},
"scrollbar": {
"thumb": { "$type": "color", "$value": "{color.action.neutral.surfaceHover}" },
"thumb": { "$type": "color", "$value": "{color.action.neutral.surfacePressed}" },
"thumbHover": { "$type": "color", "$value": "{color.action.neutral.surfacePressed}" }
},
"content": {
Expand Down Expand Up @@ -196,7 +200,7 @@
},
"launcher": {
"$description": "Shell-edge launcher states: neutral at rest, then cyan-tinted with cyan content on hover and press.",
"background": { "$type": "color", "$value": "{color.action.neutral.surfacePressed}" },
"background": { "$type": "color", "$value": "{color.action.neutral.surfaceHover}" },
"backgroundHover": { "$type": "color", "$value": "color-mix(in srgb, {color.control.highlight.background} 20%, transparent)" },
"backgroundPressed": { "$type": "color", "$value": "color-mix(in srgb, {color.control.highlight.background} 30%, transparent)" },
"content": { "$type": "color", "$value": "{color.action.neutral.content}" },
Expand All @@ -205,7 +209,7 @@
"contentDisabled": { "$type": "color", "$value": "{color.action.neutral.contentDisabled}" }
},
"switch": {
"track": { "$type": "color", "$value": "{ref.color.neutral.250}" },
"track": { "$type": "color", "$value": "{color.action.neutral.surfacePressed}" },
"trackChecked": { "$type": "color", "$value": "{color.control.highlight.background}" },
"thumb": { "$type": "color", "$value": "{ref.color.neutral.0}" }
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -295,13 +295,13 @@ test("default modes preserve the built-in Appearance anchor values", () => {
assert.equal(themes.light["color.selection.surface"], "rgba(0, 0, 0, 0.08)");
assert.equal(themes.light["color.surface.chrome"], "#f8f8f9");
assert.equal(themes.light["color.surface.tertiary"], "#f7f7f7");
assert.equal(themes.light["color.scrollbar.thumb"], "rgba(0, 0, 0, 0.08)");
assert.equal(themes.light["color.scrollbar.thumb"], "rgba(0, 0, 0, 0.10)");
assert.equal(themes.light["color.scrollbar.thumbHover"], "rgba(0, 0, 0, 0.10)");
assert.equal(themes.light["color.keyHint.background"], "rgba(0, 0, 0, 0.05)");
assert.equal(themes.light["color.keyHint.background"], "rgba(0, 0, 0, 0.08)");
assert.equal(themes.light["color.control.highlight.background"], "#059cb0");
assert.equal(themes.light["color.control.highlight.content"], "#000000");
assert.equal(themes.light["color.content.requiredIndicator"], "#059cb0");
assert.equal(themes.light["color.control.launcher.background"], "rgba(0, 0, 0, 0.10)");
assert.equal(themes.light["color.control.launcher.background"], "rgba(0, 0, 0, 0.08)");
assert.equal(
themes.light["color.control.launcher.backgroundHover"],
"color-mix(in srgb, #059cb0 20%, transparent)",
Expand All @@ -313,7 +313,7 @@ test("default modes preserve the built-in Appearance anchor values", () => {
assert.equal(themes.light["color.control.launcher.content"], "rgba(0, 0, 0, 0.80)");
assert.equal(themes.light["color.control.launcher.contentHover"], "#059cb0");
assert.equal(themes.light["color.control.launcher.contentPressed"], "#059cb0");
assert.equal(themes.light["color.control.switch.track"], "#dddddd");
assert.equal(themes.light["color.control.switch.track"], "rgba(0, 0, 0, 0.10)");
assert.equal(themes.light["color.control.switch.trackChecked"], "#059cb0");
assert.equal(themes.light["color.control.switch.thumb"], "#ffffff");
assert.equal(themes.light["color.identity.harness.minimal"], "#b434ef");
Expand Down
4 changes: 3 additions & 1 deletion design-system/packages/ui/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,7 @@ Use `canonicalIconNames` for galleries and pickers. `iconNames` also keeps the
legacy `download`, `circle` and `turn` entries for compatibility; prefer
`arrow-down`, `unselected` and `<NumberBadge value={18} />` respectively.
`turn` is only the old empty background, not a complete numbered marker.
`NumberBadge` owns a 24px slot, a 20px surface and 11px medium text; longer
`NumberBadge` owns a 24px filled surface and 11px regular text; longer
values grow horizontally. Callers supply formatted values and contextual
accessible labels. `ToolbarBadge` delegates to the same anatomy.

Expand Down Expand Up @@ -421,3 +421,5 @@ Dialog titles use 24px bold type with their own 29px line box and normal trackin
Extra-large (`xl`) dialogs have an 800px maximum width and continue shrinking within the viewport gutter. Provider editing uses the floating footer; small workspace creation retains its attached footer and existing button/input sizes. The Lab workspace pattern uses local sample paths and callbacks only.

PageHeader `md` uses the settings title with a primary 15px description; `display` uses the welcome heading and medium 17px introduction with a 12px gap. ActionCard uses 12px padding, section-heading typography (15px semibold), and a primary 13px single-line action description. Its inset outline does not inflate the 62px medium minimum height; longer content keeps the independent sibling actions and OverflowText behavior.

KeyHint uses 10px text on a 10px line and 2px block padding. StatusPill uses a 14px line with 2px block padding; opt into `emphasis` for short mode labels such as Ask, while ordinary status descriptions retain readable content colors. LauncherButton owns a 72px minimum width, 40px height, 10px side padding, 4px gap, 12px icon and 11px monospace text. Product shells may retain a deliberate compact greeting that expands into this geometry.
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,10 @@ export const keyHintMeta = {
],
states: ["default"],
tokens: [
"color.content.muted",
"color.keyHint.content",
"color.keyHint.background",
"type.micro.fontSize",
"type.modifier.leading.none.lineHeight",
"radius.xs",
],
} as const satisfies ComponentMeta;
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,22 @@
padding-inline: var(--openbitfun-space-1);
border: 0;
border-radius: var(--openbitfun-radius-xs);
color: var(--openbitfun-color-content-muted);
color: var(--openbitfun-color-key-hint-content);
background: var(--openbitfun-color-key-hint-background);
font-family: var(--openbitfun-type-micro-font-family);
font-size: var(--openbitfun-type-micro-font-size);
font-weight: var(--openbitfun-type-micro-font-weight);
letter-spacing: var(--openbitfun-type-micro-letter-spacing);
line-height: var(--openbitfun-type-micro-line-height);
line-height: var(--openbitfun-type-modifier-leading-none-line-height);
text-decoration: none;
vertical-align: middle;
white-space: nowrap;
}

.icon {
--openbitfun-opacity-icon-artwork: 1;
}

.icon,
.label {
display: inline-flex;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,6 @@ export const launcherButtonMeta = {
"control.launcherButton.gap",
"control.launcherButton.iconSize",
"control.launcherButton.radius",
"type.code.md.fontSize",
"type.code.meta.fontSize",
],
} as const satisfies ComponentMeta;
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,12 @@
border-radius: var(--openbitfun-control-launcher-button-radius) 0 0 0;
color: var(--openbitfun-color-control-launcher-content);
background: var(--openbitfun-color-control-launcher-background);
font-family: var(--openbitfun-type-code-md-font-family);
font-size: var(--openbitfun-type-code-md-font-size);
font-weight: var(--openbitfun-type-code-md-font-weight);
font-family: var(--openbitfun-type-code-meta-font-family);
font-size: var(--openbitfun-type-code-meta-font-size);
font-weight: var(--openbitfun-type-code-meta-font-weight);
font-synthesis: none;
letter-spacing: var(--openbitfun-type-code-md-letter-spacing);
line-height: var(--openbitfun-type-code-md-line-height);
letter-spacing: var(--openbitfun-type-code-meta-letter-spacing);
line-height: var(--openbitfun-type-code-meta-line-height);
white-space: nowrap;
cursor: pointer;
transition:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import type { ComponentMeta } from "../../registry.types";

export const numberBadgeMeta = {
category: "primitive",
description: "A compact numeric marker with a 24px slot, 20px surface and caller-owned value. Long values grow without clipping.",
description: "A compact numeric marker with a 24px filled surface and caller-owned value. Long values grow without clipping.",
maturity: "stable",
name: "NumberBadge",
props: [
Expand All @@ -11,7 +11,7 @@ export const numberBadgeMeta = {
],
states: ["default"],
tokens: [
"space.1", "space.5", "space.6", "type.meta.fontSize", "radius.pill",
"color.action.neutral.content", "color.action.neutral.surface",
"space.1", "space.6", "type.meta.fontSize", "radius.pill",
"color.action.neutral.content", "color.numberBadge.background",
],
} as const satisfies ComponentMeta;
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
justify-content: center;
min-inline-size: var(--openbitfun-space-6);
min-block-size: var(--openbitfun-space-6);
padding: calc(var(--openbitfun-space-1) / 2);
padding: 0;
vertical-align: middle;
color: var(--openbitfun-color-action-neutral-content);
font-family: var(--openbitfun-type-meta-font-family);
Expand All @@ -24,11 +24,11 @@
display: inline-flex;
align-items: center;
justify-content: center;
min-inline-size: var(--openbitfun-space-5);
min-block-size: var(--openbitfun-space-5);
min-inline-size: var(--openbitfun-space-6);
min-block-size: var(--openbitfun-space-6);
padding-inline: calc(var(--openbitfun-space-1) / 2);
border-radius: var(--openbitfun-radius-pill);
background: var(--openbitfun-color-action-neutral-surface);
background: var(--openbitfun-color-number-badge-background);
}

@media (forced-colors: active) {
Expand Down
Loading
Loading