Skip to content

Commit efefeeb

Browse files
authored
Merge pull request #2915 from kev1n77/fmy/ui-main
fix(ui): align menu spacing and restore intended text contrast
2 parents b4b7901 + 8fadc35 commit efefeeb

30 files changed

Lines changed: 151 additions & 44 deletions

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,9 +171,13 @@ export function NestedMenuPattern() {
171171
const [position, setPosition] = useState<{ x: number; y: number } | undefined>();
172172
const [lastAction, setLastAction] = useState("");
173173
const [pinned, setPinned] = useState(false);
174+
const [scrolling, setScrolling] = useState(false);
174175
return <Card appearance="subtle" padding="md" gap="md" data-openbitfun-pattern="nested-menu" onContextMenu={event => { event.preventDefault(); setPosition({ x: event.clientX, y: event.clientY }); setOpen(true); }}>
175176
<PageHeader level={3} size="sm" title={t("patterns.menu.title")} description={t("patterns.menu.hint")} />
176177
<Button ref={anchorRef} aria-haspopup="menu" aria-expanded={open} leadingIcon={<Icon name="more" />} onClick={() => { setPosition(undefined); setOpen(!open); }}>{t("patterns.menu.open")}</Button>
178+
<Field label={t("detail.option.scrolling")} orientation="horizontal">
179+
<Switch checked={scrolling} onCheckedChange={setScrolling} />
180+
</Field>
177181
<MenuPopover aria-label={t("patterns.menu.title")} open={open} onClose={() => setOpen(false)} anchorRef={position ? undefined : anchorRef} position={position} items={[
178182
{ id: "open", label: t("patterns.actions.openFiles"), icon: <Icon name="files" />, shortcut: <KeyHint>Ctrl O</KeyHint>, onSelect: () => setLastAction(t("patterns.actions.openFiles")) },
179183
{ id: "tools", label: t("patterns.navigation.tools"), icon: <Icon name="extension" />, submenu: [
@@ -185,6 +189,7 @@ export function NestedMenuPattern() {
185189
] },
186190
{ id: "separator", label: "", separator: true },
187191
{ id: "pin", label: t("patterns.menu.pin"), role: "menuitemcheckbox", checked: pinned, icon: <Icon name={pinned ? "check-line" : "pin"} />, onSelect: () => { setPinned(!pinned); setLastAction(t("patterns.menu.pin")); } },
192+
...(scrolling ? Array.from({ length: 18 }, (_, index) => ({ id: `file-${index}`, label: `${t("patterns.actions.openFiles")} — workspace-${index + 1}`, icon: <Icon name="files" />, onSelect: () => setLastAction(`workspace-${index + 1}`) })) : []),
188193
]} />
189194
<p className="pattern-feedback" role="status">{lastAction ? t("patterns.menu.lastAction", { action: lastAction }) : t("patterns.menu.keyboard")}</p>
190195
</Card>;

design-system/packages/design-tokens/src/system.tokens.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,10 @@
169169
"letterSpacing": { "$value": "{letterSpacing.normal}" }
170170
}
171171
},
172+
"actionRow": {
173+
"$description": "Single-line action leading: 16px at the default 13px type size, scaling with user typography.",
174+
"lineHeight": { "$type": "number", "$value": 1.2307692307692308 }
175+
},
172176
"fieldHelper": {
173177
"$description": "Field helper leading: 16px at the default 11px type size, scaling with user typography.",
174178
"lineHeight": { "$type": "number", "$value": 1.4545454545454546 }
@@ -724,7 +728,7 @@
724728
"surfacePadding": { "$value": "{space.2}" },
725729
"contentGap": { "$value": "{space.4}" },
726730
"sectionGap": { "$value": "{space.1}" },
727-
"headingHeight": { "$value": "22px" },
731+
"headingHeight": { "$value": "24px" },
728732
"headingPaddingInline": { "$value": "{space.2}" },
729733
"headingGap": { "$value": "{space.5}" },
730734
"headingActionSize": { "$value": "22px" },

design-system/packages/design-tokens/tests/contract.test.mjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,7 @@ test("NavigationPanel tokens preserve the grouped sidebar composition contract",
227227
const systemDocument = await readSource("system.tokens.json");
228228

229229
assert.equal(tokens["layout.navigationPanel.inlineSize"], "216px");
230-
assert.equal(tokens["layout.navigationPanel.headingHeight"], "22px");
230+
assert.equal(tokens["layout.navigationPanel.headingHeight"], "24px");
231231
assert.equal(tokens["layout.navigationPanel.itemHeight"], "30px");
232232
assert.equal(tokens["layout.navigationPanel.itemIconSize"], "14px");
233233
assert.equal(tokens["layout.navigationPanel.footerHeight"], "40px");

design-system/packages/theme-openbitfun/README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,3 +69,11 @@ component token is absent, and keeps explicit component overrides intact.
6969
- `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.
7070
- `color.status.*.emphasis` colors icons and short emphasis. `content` derives a readable shade from that anchor for text; `surface` and `border` derive 10% and 30% tints. High-contrast themes may strengthen text contrast without changing the emphasis anchors.
7171
- Status source tokens retain their `color-mix()` references. The theme build resolves these mixes to concrete hex/RGBA values so CSS, plugins, and renderer payloads consume the same palette without relying on renderer-specific CSS color support.
72+
73+
`color.content.caption` distinguishes low-emphasis menu/navigation group headings
74+
from body descriptions and input placeholders. Light mode supplies final black
75+
40%; dark and high-contrast modes reuse readable muted content. An imported
76+
appearance's explicit old muted color is retained when the caption token is absent.
77+
The built-in light Appearance preserves the public neutral action content (80%)
78+
in both root and chrome; generic palette projection previously reduced product
79+
menu labels to secondary text (60%). Explicit imported action colors still win.

design-system/packages/theme-openbitfun/src/dark.tokens.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@
6464
"thumbHover": { "$type": "color", "$value": "{color.action.neutral.surfacePressed}" }
6565
},
6666
"content": {
67+
"caption": { "$description": "Menu and navigation group captions; carries final opacity without attenuating primary text again.", "$type": "color", "$value": "{color.content.muted}" },
6768
"primary": { "$type": "color", "$value": "{ref.color.neutral.200}" },
6869
"secondary": { "$type": "color", "$value": "{ref.color.neutral.350}" },
6970
"muted": { "$type": "color", "$value": "{ref.color.neutral.550}" },

design-system/packages/theme-openbitfun/src/high-contrast-dark.tokens.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
"panel": { "$type": "color", "$value": "{ref.color.gray.900}" }
2525
},
2626
"content": {
27+
"caption": { "$description": "Menu and navigation group captions; carries final opacity without attenuating primary text again.", "$type": "color", "$value": "{color.content.muted}" },
2728
"primary": { "$type": "color", "$value": "{ref.color.gray.0}" },
2829
"secondary": { "$type": "color", "$value": "{ref.color.gray.50}" },
2930
"muted": { "$type": "color", "$value": "{ref.color.gray.150}" }

design-system/packages/theme-openbitfun/src/high-contrast-light.tokens.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
"panel": { "$type": "color", "$value": "{ref.color.gray.0}" }
2525
},
2626
"content": {
27+
"caption": { "$description": "Menu and navigation group captions; carries final opacity without attenuating primary text again.", "$type": "color", "$value": "{color.content.muted}" },
2728
"primary": { "$type": "color", "$value": "{ref.color.gray.1000}" },
2829
"secondary": { "$type": "color", "$value": "{ref.color.gray.800}" },
2930
"muted": { "$type": "color", "$value": "{ref.color.gray.700}" }

design-system/packages/theme-openbitfun/src/light.tokens.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@
6464
"thumbHover": { "$type": "color", "$value": "{color.action.neutral.surfacePressed}" }
6565
},
6666
"content": {
67+
"caption": { "$description": "Menu and navigation group captions; carries final opacity without attenuating primary text again.", "$type": "color", "$value": "rgba(0, 0, 0, 0.40)" },
6768
"primary": { "$type": "color", "$value": "rgba(0, 0, 0, 0.80)" },
6869
"secondary": { "$type": "color", "$value": "rgba(0, 0, 0, 0.60)" },
6970
"muted": { "$type": "color", "$value": "{ref.color.neutral.650}" },
@@ -254,7 +255,7 @@
254255
"xl": { "$value": "0 12px 24px rgba(16, 26, 39, 0.11)" },
255256
"raised": { "$value": "{shadow.sm}" },
256257
"composer": { "$value": "0 2px 6px rgba(0, 0, 0, 0.08)" },
257-
"menu": { "$value": "0 4px 10px rgba(0, 0, 0, 0.12)" },
258+
"menu": { "$value": "0 4px 20px rgba(0, 0, 0, 0.12)" },
258259
"overlay": { "$value": "0 4px 20px rgba(0, 0, 0, 0.12)" },
259260
"accentGlow": {
260261
"$description": "Layered emphasis glow derived from the active accent.",

design-system/packages/theme-openbitfun/tests/theme-contract.test.mjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -329,7 +329,7 @@ test("default modes preserve the built-in Appearance anchor values", () => {
329329
assert.equal(themes.light["color.status.warning.surface"], "rgba(255, 140, 0, 0.1)");
330330
assert.equal(themes.light["shadow.base"], "0 4px 8px rgba(16, 26, 39, 0.07)");
331331
assert.equal(themes.light["shadow.composer"], "0 2px 6px rgba(0, 0, 0, 0.08)");
332-
assert.equal(themes.light["shadow.menu"], "0 4px 10px rgba(0, 0, 0, 0.12)");
332+
assert.equal(themes.light["shadow.menu"], "0 4px 20px rgba(0, 0, 0, 0.12)");
333333
assert.equal(themes.light["shadow.overlay"], "0 4px 20px rgba(0, 0, 0, 0.12)");
334334
assert.equal(themes.light["opacity.disabled"], 0.55);
335335
assert.equal(themes.dark["color.surface.canvas"], "#0e0e10");

design-system/packages/ui/README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -405,3 +405,11 @@ FormSection descriptions use primary content with 16px leading at 13px. Both
405405
leading roles scale with user typography. FieldGroup uses the form group tint,
406406
retaining its existing row padding, dividers, and radius. The Patterns form
407407
specimen shows both orientations and long values over a tinted container.
408+
409+
Menus keep contiguous 30px rows with no additional list or heading-to-item gap;
410+
separators own their 8px vertical margins. Their keyboard focus indicator is
411+
inset so scrolling does not clip it or require extra permanent padding.
412+
ActionItem hover and pressed surfaces use the semantic neutral hover fill;
413+
pressed text remains semibold. Menu and navigation captions consume the final
414+
caption color directly, avoiding a second opacity multiplier. The nested-menu
415+
Pattern includes a scrolling toggle for keyboard and submenu verification.

0 commit comments

Comments
 (0)