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
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 { NestedMenuPattern, ProviderConfigurationPattern, SceneToolbarPattern } from "./ReferencePatterns";
import { FormTypographyPattern, NestedMenuPattern, ProviderConfigurationPattern, SceneToolbarPattern } from "./ReferencePatterns";

interface PatternsPageProps {
colorScheme: ColorScheme;
Expand Down Expand Up @@ -116,6 +116,8 @@ export function PatternsPage({ colorScheme, contrast, density, tokenOverrides }:
</Card>
</PatternSection>

<FormTypographyPattern />

<PatternSection description={t("patterns.navigation.description")} index="02" title={t("patterns.navigation.title")}>
<div className="pattern-navigation-stage" data-openbitfun-pattern="navigation-panel">
<NavigationPanel
Expand Down
21 changes: 21 additions & 0 deletions design-system/apps/design-lab/src/pages/ReferencePatterns.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
Field,
FieldGroup,
FieldRow,
FormSection,
Icon,
IconButton,
Input,
Expand Down Expand Up @@ -35,6 +36,26 @@ import {
} from "@openbitfun/ui";
import { useI18n } from "../i18n";

export function FormTypographyPattern() {
const { t } = useI18n();
return <Card appearance="subtle" padding="md" data-openbitfun-pattern="form-typography">
<FormSection title={t("patterns.settings.title")} description={t("patterns.settings.description")}>
<FieldGroup>
<FieldRow>
<Field label={t("patterns.settings.language")} description={t("patterns.settings.languageDescription")} orientation="horizontal" controlWidth="fill" labelWidth="md">
<Input defaultValue="English / 简体中文 / 繁體中文" />
</Field>
</FieldRow>
<FieldRow>
<Field label={t("patterns.provider.headers")} description={t("components.preview.fieldDescription")} orientation="vertical" controlWidth="fill">
<Input defaultValue="X-OpenBitFun-Workspace-Display-Name-With-A-Long-Value" />
</Field>
</FieldRow>
</FieldGroup>
</FormSection>
</Card>;
}

export function ProviderConfigurationPattern() {
const { t } = useI18n();
const [open, setOpen] = useState(false);
Expand Down
8 changes: 8 additions & 0 deletions design-system/packages/design-tokens/src/system.tokens.json
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,14 @@
"letterSpacing": { "$value": "{letterSpacing.normal}" }
}
},
"fieldHelper": {
"$description": "Field helper leading: 16px at the default 11px type size, scaling with user typography.",
"lineHeight": { "$type": "number", "$value": 1.4545454545454546 }
},
"formDescription": {
"$description": "Form-section description leading: 16px at the default 13px type size, scaling with user typography.",
"lineHeight": { "$type": "number", "$value": 1.2307692307692308 }
},
"support": {
"fontFamily": { "$value": "{font.family.control}" },
"fontSize": { "$value": "{font.size.meta}" },
Expand Down
3 changes: 2 additions & 1 deletion design-system/packages/theme-openbitfun/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,8 @@ 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.
- `color.surface.tertiary` is an opaque low-emphasis fill for persistent containers such as cards.
- `color.field.groupBackground` owns grouped form surfaces: light mode uses a 3% black tint so the underlying surface remains visible; dark and high-contrast modes retain their tertiary fill. Opaque tertiary containers and transient `surface.subtle` feedback cannot express this form-specific contract. Imported appearances inherit an explicitly supplied legacy tertiary color unless they supply the new field token.
- `color.surface.subtle` is a translucent local tint for transient feedback and small inset details. It must not define a persistent application plane.
- `color.selection.surface` owns persistent neutral selection. Hover and pressed colors remain action feedback and are not substitutes for selection.
- `color.codeChange.added` (`#1aa73e`) and `color.codeChange.removed` (`#ec221f`) also anchor success and danger emphasis. Warning emphasis uses `#ff8c00`; information uses the existing creative-action blue (`#2e7eff`). These clear hues share light tints instead of separate per-component palettes.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,7 @@
}
},
"field": {
"groupBackground": { "$description": "Grouped form tint; independent of opaque tertiary containers and transient subtle feedback.", "$type": "color", "$value": "{color.surface.tertiary}" },
"borderActive": { "$type": "color", "$value": "{color.field.borderFocus}" },
"placeholder": { "$type": "color", "$value": "{color.content.muted}" },
"background": { "$type": "color", "$value": "{ref.color.neutral.900}" },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
}
},
"field": {
"groupBackground": { "$description": "Grouped form tint; independent of opaque tertiary containers and transient subtle feedback.", "$type": "color", "$value": "{color.surface.tertiary}" },
"borderActive": { "$type": "color", "$value": "{color.field.borderFocus}" },
"placeholder": { "$type": "color", "$value": "{color.content.muted}" },
"border": { "$type": "color", "$value": "{ref.color.gray.250}" },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
}
},
"field": {
"groupBackground": { "$description": "Grouped form tint; independent of opaque tertiary containers and transient subtle feedback.", "$type": "color", "$value": "{color.surface.tertiary}" },
"borderActive": { "$type": "color", "$value": "{color.field.borderFocus}" },
"placeholder": { "$type": "color", "$value": "{color.content.muted}" },
"border": { "$type": "color", "$value": "{ref.color.gray.650}" },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,7 @@
}
},
"field": {
"groupBackground": { "$description": "Grouped form tint; independent of opaque tertiary containers and transient subtle feedback.", "$type": "color", "$value": "rgba(0, 0, 0, 0.03)" },
"borderActive": { "$description": "Input and SearchField editing border, distinct from the stronger generic focus indicator used by other controls.", "$type": "color", "$value": "{color.field.borderHover}" },
"placeholder": { "$description": "Empty field hints and decorative adornments; distinct from general secondary prose.", "$type": "color", "$value": "rgba(0, 0, 0, 0.40)" },
"background": { "$type": "color", "$value": "{ref.color.neutral.0}" },
Expand Down
8 changes: 8 additions & 0 deletions design-system/packages/ui/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -397,3 +397,11 @@ Tool-specific data shaping, localization, host actions, stores, and heavy
renderers remain in the consuming product and enter through semantic props,
callbacks, and slots. Bespoke product workflows remain product-owned rather
than being forced into a standard package view.

Field labels follow their orientation: horizontal labels use the 13px semibold
label role, while vertical labels use the 11px regular meta role. Field helpers
use secondary content with 16px leading at the default 11px font size;
FormSection descriptions use primary content with 16px leading at 13px. Both
leading roles scale with user typography. FieldGroup uses the form group tint,
retaining its existing row padding, dividers, and radius. The Patterns form
specimen shows both orientations and long values over a tinted container.
6 changes: 4 additions & 2 deletions design-system/packages/ui/src/components/Field/Field.meta.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export const fieldMeta = {
tokens: [
"color.content.requiredIndicator",
"color.content.primary",
"color.content.muted",
"color.content.secondary",
"color.status.danger.content",
"layout.field.rootGap",
"layout.field.horizontalGap",
Expand All @@ -35,7 +35,9 @@ export const fieldMeta = {
"layout.field.labelGap",
"layout.field.labelActionGap",
"layout.field.controlGap",
"type.label.md.fontSize",
"type.label.selected.fontSize",
"type.meta.fontSize",
"type.fieldHelper.lineHeight",
"type.support.fontSize",
],
} as const satisfies ComponentMeta;
22 changes: 15 additions & 7 deletions design-system/packages/ui/src/components/Field/Field.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -66,11 +66,19 @@
align-items: center;
gap: var(--openbitfun-layout-field-label-gap);
color: var(--openbitfun-color-content-primary);
font-family: var(--openbitfun-type-label-md-font-family);
font-size: var(--openbitfun-type-label-md-font-size);
font-weight: var(--openbitfun-type-label-md-font-weight);
line-height: var(--openbitfun-type-label-md-line-height);
letter-spacing: var(--openbitfun-type-label-md-letter-spacing);
font-family: var(--openbitfun-type-meta-font-family);
font-size: var(--openbitfun-type-meta-font-size);
font-weight: var(--openbitfun-type-meta-font-weight);
line-height: var(--openbitfun-type-meta-line-height);
letter-spacing: var(--openbitfun-type-meta-letter-spacing);
}

.root[data-orientation="horizontal"] .label {
font-family: var(--openbitfun-type-label-selected-font-family);
font-size: var(--openbitfun-type-label-selected-font-size);
font-weight: var(--openbitfun-type-label-selected-font-weight);
line-height: var(--openbitfun-type-label-selected-line-height);
letter-spacing: var(--openbitfun-type-label-selected-letter-spacing);
}

.required {
Expand All @@ -85,11 +93,11 @@
}

.description {
color: var(--openbitfun-color-content-muted);
color: var(--openbitfun-color-content-secondary);
font-family: var(--openbitfun-type-support-font-family);
font-size: var(--openbitfun-type-support-font-size);
font-weight: var(--openbitfun-type-support-font-weight);
line-height: var(--openbitfun-type-support-line-height);
line-height: var(--openbitfun-type-field-helper-line-height);
letter-spacing: var(--openbitfun-type-support-letter-spacing);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,8 @@ export const fieldGroupMeta = {
],
states: ["subtle", "plain", "divided"],
tokens: [
"color.surface.tertiary",
"color.field.groupBackground",
"color.content.primary",
"color.content.muted",
"color.border.subtle",
"color.field.background",
"color.field.backgroundHover",
Expand All @@ -30,6 +29,7 @@ export const fieldGroupMeta = {
"layout.formSection.titleDescriptionGap",
"type.heading.section.fontSize",
"type.body.sm.fontSize",
"type.formDescription.lineHeight",
"layout.fieldGroup.radius",
"layout.fieldGroup.rowPaddingBlock",
"layout.fieldGroup.rowPaddingInline",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,11 +63,11 @@
}

.description {
color: var(--openbitfun-color-content-muted);
color: var(--openbitfun-color-content-primary);
font-family: var(--openbitfun-type-body-sm-font-family);
font-size: var(--openbitfun-type-body-sm-font-size);
font-weight: var(--openbitfun-type-body-sm-font-weight);
line-height: var(--openbitfun-type-body-sm-line-height);
line-height: var(--openbitfun-type-form-description-line-height);
letter-spacing: var(--openbitfun-type-body-sm-letter-spacing);
}

Expand All @@ -86,7 +86,7 @@
}

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

.group[data-appearance="plain"] {
Expand Down
2 changes: 1 addition & 1 deletion design-system/packages/ui/tests/field-group.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ test("form grouping styles consume only shared public composition tokens", async
assert.match(styles, /--openbitfun-layout-form-section-gap/);
assert.match(styles, /--openbitfun-layout-field-group-radius/);
assert.match(styles, /--openbitfun-layout-field-group-row-padding-block/);
assert.match(styles, /--openbitfun-color-surface-tertiary/);
assert.match(styles, /--openbitfun-color-field-group-background/);
assert.match(styles, /--openbitfun-color-border-subtle/);
});

Expand Down
4 changes: 2 additions & 2 deletions design-system/packages/ui/tests/field.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -98,10 +98,10 @@ test("Field styles consume shared content and typography tokens", async () => {
const styles = await readFile(new URL("../src/components/Field/Field.module.css", import.meta.url), "utf8");

assert.match(styles, /--openbitfun-color-content-primary/);
assert.match(styles, /--openbitfun-color-content-muted/);
assert.match(styles, /--openbitfun-color-content-secondary/);
assert.match(styles, /--openbitfun-color-content-required-indicator/);
assert.doesNotMatch(styles, /--openbitfun-color-status-danger-content[^\n]*required/);
assert.match(styles, /--openbitfun-type-label-md-font-size/);
assert.match(styles, /--openbitfun-type-label-selected-font-size/);
assert.match(styles, /--openbitfun-type-support-font-size/);
assert.match(styles, /--openbitfun-layout-field-root-gap/);
assert.match(styles, /--openbitfun-layout-field-label-action-gap/);
Expand Down
6 changes: 6 additions & 0 deletions src/apps/data-migrator/ui/generated/design-system.css
Original file line number Diff line number Diff line change
Expand Up @@ -463,6 +463,7 @@
--openbitfun-type-display-xxl-font-weight: var(--openbitfun-font-weight-semibold);
--openbitfun-type-display-xxl-letter-spacing: var(--openbitfun-letter-spacing-tight);
--openbitfun-type-display-xxl-line-height: var(--openbitfun-line-height-display);
--openbitfun-type-field-helper-line-height: 1.4545454545454546;
--openbitfun-type-flow-body-font-family: var(--openbitfun-font-family-sans);
--openbitfun-type-flow-body-font-size: var(--openbitfun-font-size-base);
--openbitfun-type-flow-body-font-weight: var(--openbitfun-font-weight-regular);
Expand Down Expand Up @@ -513,6 +514,7 @@
--openbitfun-type-flow-title-font-weight: var(--openbitfun-font-weight-semibold);
--openbitfun-type-flow-title-letter-spacing: var(--openbitfun-letter-spacing-snug);
--openbitfun-type-flow-title-line-height: var(--openbitfun-line-height-tight);
--openbitfun-type-form-description-line-height: 1.2307692307692308;
--openbitfun-type-heading-card-font-family: var(--openbitfun-font-family-control);
--openbitfun-type-heading-card-font-size: var(--openbitfun-font-size-sm);
--openbitfun-type-heading-card-font-weight: var(--openbitfun-font-weight-semibold);
Expand Down Expand Up @@ -728,6 +730,7 @@
--openbitfun-color-field-border-active: rgba(0, 0, 0, 0.20);
--openbitfun-color-field-border-focus: #858585;
--openbitfun-color-field-border-hover: rgba(0, 0, 0, 0.20);
--openbitfun-color-field-group-background: rgba(0, 0, 0, 0.03);
--openbitfun-color-field-placeholder: rgba(0, 0, 0, 0.40);
--openbitfun-color-focus-ring: #6a6a6a;
--openbitfun-color-identity-assistant-border: color-mix(in srgb, #db2777 15%, transparent);
Expand Down Expand Up @@ -875,6 +878,7 @@
--openbitfun-color-field-border-active: #858585;
--openbitfun-color-field-border-focus: #858585;
--openbitfun-color-field-border-hover: rgba(255, 255, 255, 0.24);
--openbitfun-color-field-group-background: #0e0e10;
--openbitfun-color-field-placeholder: #858585;
--openbitfun-color-focus-ring: #60a5fa;
--openbitfun-color-identity-assistant-border: color-mix(in srgb, #ec4899 15%, transparent);
Expand Down Expand Up @@ -993,6 +997,7 @@
--openbitfun-color-field-border-active: #005fcc;
--openbitfun-color-field-border-focus: #005fcc;
--openbitfun-color-field-border-hover: #000000;
--openbitfun-color-field-group-background: #f7f7f7;
--openbitfun-color-field-placeholder: #343434;
--openbitfun-color-focus-ring: #005fcc;
--openbitfun-color-identity-global-search-open-terminal: #000000;
Expand Down Expand Up @@ -1054,6 +1059,7 @@
--openbitfun-color-field-border-active: #ffcc00;
--openbitfun-color-field-border-focus: #ffcc00;
--openbitfun-color-field-border-hover: #ffffff;
--openbitfun-color-field-group-background: #000000;
--openbitfun-color-field-placeholder: #d0d0d0;
--openbitfun-color-focus-ring: #ffcc00;
--openbitfun-color-identity-global-search-open-terminal: #ffffff;
Expand Down
1 change: 1 addition & 0 deletions src/apps/desktop/src/generated/bootstrap_theme.css
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@
--openbitfun-color-field-border-active: #858585;
--openbitfun-color-field-border-focus: #858585;
--openbitfun-color-field-border-hover: rgba(255, 255, 255, 0.24);
--openbitfun-color-field-group-background: #0e0e10;
--openbitfun-color-field-placeholder: #858585;
--openbitfun-color-focus-ring: #60a5fa;
--openbitfun-color-identity-assistant-border: color-mix(in srgb, #ec4899 15%, transparent);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

&__content {
overflow: hidden;
background: var(--openbitfun-color-surface-tertiary);
background: var(--openbitfun-color-field-group-background);
border-radius: var(--openbitfun-radius-lg);
}

Expand All @@ -29,10 +29,10 @@

&__preview-label {
color: var(--openbitfun-color-content-primary);
font-family: var(--openbitfun-type-label-md-font-family);
font-size: var(--openbitfun-type-label-md-font-size);
font-weight: var(--openbitfun-type-label-md-font-weight);
line-height: var(--openbitfun-type-label-md-line-height);
font-family: var(--openbitfun-type-meta-font-family);
font-size: var(--openbitfun-type-meta-font-size);
font-weight: var(--openbitfun-type-meta-font-weight);
line-height: var(--openbitfun-type-meta-line-height);
}

&__preview-path {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,13 +56,15 @@ describe('builtin appearance preset output', () => {
expect(settings?.tokens['--openbitfun-color-field-border-hover']).toBe('rgba(0, 0, 0, 0.20)');
expect(settings?.tokens['--openbitfun-color-field-border-active']).toBe('rgba(0, 0, 0, 0.20)');
expect(settings?.tokens['--openbitfun-color-field-border-focus']).toBe(themes.light['color.field.borderFocus']);
expect(settings?.tokens['--openbitfun-color-field-group-background']).toBe('rgba(0, 0, 0, 0.03)');
expect(settings?.tokens['--openbitfun-color-field-placeholder']).toBe('rgba(0, 0, 0, 0.40)');
for (const palette of builtinAppearancePalettes) {
if (palette.id === 'openbitfun-light') continue;
const tokens = getBuiltinAppearanceThemeTokens(palette.id);
expect(tokens['--openbitfun-color-field-border']).toBe(palette.colors.border.base);
expect(tokens['--openbitfun-color-field-border-focus']).toBe(palette.colors.accent[500]);
expect(tokens['--openbitfun-color-field-border-active']).toBe(palette.colors.accent[500]);
expect(tokens['--openbitfun-color-field-group-background']).toBe(palette.colors.background.tertiary);
expect(tokens['--openbitfun-color-field-placeholder']).toBe(palette.colors.text.muted);
}
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ function createThemeTokenValues(palette: AppearancePalette): Record<ThemeTokenNa
'color.action.quiet.pressed': colors.element.base,
'color.action.quiet.content': colors.text.secondary,
'color.selection.surface': colors.element.medium,
'color.field.groupBackground': colors.background.tertiary,
'color.field.background': colors.background.secondary,
'color.field.backgroundHover': colors.element.subtle,
'color.field.border': colors.border.base,
Expand Down Expand Up @@ -242,6 +243,7 @@ function createChromeThemeTokens(
'color.action.quiet.pressed': chrome.element.base,
'color.action.quiet.content': chrome.text.secondary,
'color.selection.surface': chrome.element.medium,
'color.field.groupBackground': chrome.background.tertiary,
'color.field.background': chrome.background.secondary,
'color.field.backgroundHover': chrome.element.subtle,
'color.field.border': chrome.border.base,
Expand Down
Loading
Loading