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
9 changes: 9 additions & 0 deletions design-system/packages/design-tokens/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,15 @@ line travel and intrinsic-width changes. It has its own token so tuning content
replacement does not change the existing hover-feedback and larger-view
transition durations. It is zeroed in the reduced-motion projection.

## Overlay layers

Portaled tooltips must paint above the popovers containing their triggers.
Use `layer.popover` for menus and panels, with adjacent offsets for their
backdrops (-1) and nested menus (+1); keep those offsets below `layer.tooltip`.
Toast, notification, and context-menu layers remain above ordinary hover hints.
These values order siblings within a stacking context; a portal host still
needs its own layer above the app content it serves.

## Semantic typography roles

Text-bearing components consume a complete semantic role rather than assembling
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -989,7 +989,7 @@
"modalActive": { "$value": 250 },
"fullscreen": { "$value": 280 },
"overlayHost": { "$value": 300 },
"tooltip": { "$value": 350 },
"tooltip": { "$value": 370 },
"popover": { "$value": 360 },
"toast": { "$value": 400 },
"notification": { "$value": 400 },
Expand Down
14 changes: 14 additions & 0 deletions design-system/packages/design-tokens/tests/contract.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -436,6 +436,20 @@ test("shared system scales preserve the migrated Web UI foundation contract", ()
assert.equal(tokens["layer.contextMenu"], 500);
});

test("tooltips outrank popovers and nested menus without covering priority chrome", () => {
for (const mode of tokenModes) {
const modeTokens = Object.fromEntries(
tokenCatalog.filter(token => token.category === "layer")
.map(token => [token.name, Number(token.values[mode])]),
);
assert.ok(modeTokens["layer.modal"] < modeTokens["layer.popover"]);
assert.ok(modeTokens["layer.popover"] + 1 < modeTokens["layer.tooltip"]);
for (const layer of ["toast", "notification", "contextMenu"]) {
assert.ok(modeTokens["layer.tooltip"] < modeTokens[`layer.${layer}`]);
}
}
});

test("semantic typography roles resolve to the canonical foundation", async () => {
const systemDocument = await readSource("system.tokens.json");

Expand Down
2 changes: 1 addition & 1 deletion src/apps/data-migrator/ui/generated/design-system.css
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@
--openbitfun-layer-popover: 360;
--openbitfun-layer-sticky: 15;
--openbitfun-layer-toast: 400;
--openbitfun-layer-tooltip: 350;
--openbitfun-layer-tooltip: 370;
--openbitfun-layout-card-footer-gap: var(--openbitfun-space-2);
--openbitfun-layout-card-gap-lg: 30px;
--openbitfun-layout-card-gap-md: var(--openbitfun-space-3);
Expand Down
12 changes: 7 additions & 5 deletions src/web-ui/src/app/components/NavPanel/NavPanel.scss
Original file line number Diff line number Diff line change
Expand Up @@ -981,7 +981,7 @@ $_section-header-height: 22px;
width: fit-content;
max-width: min(440px, calc(100vw - 32px));
max-height: min(560px, calc(100vh - 16px));
z-index: 9999;
z-index: var(--openbitfun-layer-popover);
transform-origin: top left;
animation: openbitfun-footer-menu-in var(--openbitfun-motion-duration-fast) var(--openbitfun-motion-easing-decelerate) forwards;

Expand Down Expand Up @@ -1028,6 +1028,8 @@ $_section-header-height: 22px;
}

&__workspace-menu-workspaces {
display: flex;
flex-direction: column;
max-height: 240px;
overscroll-behavior: contain;
}
Expand Down Expand Up @@ -1604,7 +1606,7 @@ $_section-header-height: 22px;
.openbitfun-nav-panel__footer-backdrop {
position: fixed;
inset: 0;
z-index: 9998;
z-index: calc(var(--openbitfun-layer-popover) - 1);
}

/* Positioning-only overrides; the design-system Menu owns the surface look. */
Expand All @@ -1613,7 +1615,7 @@ $_section-header-height: 22px;
min-width: 148px;
max-width: calc(100vw - 16px);
max-height: calc(100vh - 16px);
z-index: 9999;
z-index: var(--openbitfun-layer-popover);
transform-origin: bottom right;
animation: openbitfun-footer-menu-in var(--openbitfun-motion-duration-fast) var(--openbitfun-motion-easing-decelerate) forwards;

Expand All @@ -1640,7 +1642,7 @@ $_section-header-height: 22px;
position: fixed;
max-width: calc(100vw - 16px);
max-height: calc(100vh - 16px);
z-index: 10000;
z-index: calc(var(--openbitfun-layer-popover) + 1);
animation: openbitfun-nav-popover-in 150ms cubic-bezier(0.23, 1, 0.32, 1) both;

&[data-placement='right'] {
Expand All @@ -1658,7 +1660,7 @@ $_section-header-height: 22px;

.openbitfun-device-overview {
position: fixed;
z-index: 9999;
z-index: var(--openbitfun-layer-popover);
width: min(280px, calc(100vw - 16px));
max-height: calc(100vh - 16px);
display: flex;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -460,7 +460,7 @@

&__inline-item-menu-popover {
position: fixed;
z-index: 10000;
z-index: var(--openbitfun-layer-popover);
}

&__inline-item-edit {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -910,7 +910,7 @@
min-inline-size: 180px;
max-inline-size: min(320px, calc(100vw - 24px));
max-block-size: min(560px, calc(100vh - 16px));
z-index: 10000;
z-index: var(--openbitfun-layer-popover);
transform-origin: top left;
animation: openbitfun-footer-menu-in var(--openbitfun-motion-duration-fast) var(--openbitfun-motion-easing-decelerate) forwards;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@
--openbitfun-overlay-menu-inline-size: min(280px, calc(100vw - 16px));
--openbitfun-overlay-menu-max-block-size: min(360px, calc(100vh - 16px));
min-width: 200px;
z-index: 99999;
z-index: var(--openbitfun-layer-popover);
animation: menuSlideIn 0.12s ease-out;

@keyframes menuSlideIn {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -467,7 +467,7 @@

&__background-command-menu {
position: fixed;
z-index: 10000;
z-index: calc(var(--openbitfun-layer-popover) + 1);

&--portal {
top: auto;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,7 @@
position: fixed;
top: auto;
right: auto;
z-index: 10000;
z-index: calc(var(--openbitfun-layer-popover) + 1);
}

&__node-copy {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,26 @@ describe('appearance overlay host', () => {
expect(systemTokens.layer.overlayHost.$value).toBeLessThan(systemTokens.layer.contextMenu.$value);
});

it.each([
'src/app/components/NavPanel/NavPanel.scss',
'src/app/components/NavPanel/sections/sessions/SessionsSection.scss',
'src/app/components/NavPanel/sections/workspaces/WorkspaceListSection.scss',
'src/app/components/panels/content-canvas/tab-bar/TabOverflowMenu.scss',
'src/flow_chat/components/modern/FlowChatHeader.scss',
'src/flow_chat/components/modern/SessionTreePopover.scss',
'src/infrastructure/peer-device/DeviceSurfaceSwitcher.scss',
])('keeps %s below portaled tooltips', (path) => {
const styles = readSource(path);
// Legacy 9999/10000 menu layers bypassed the shared scale and covered
// Tooltip/OverflowText siblings even after the portal escaped clipping.
for (const [, value] of styles.matchAll(/z-index:\s*(\d+)\s*;/g)) {
expect(Number(value), `${path}: z-index ${value}`).toBeLessThan(
systemTokens.layer.tooltip.$value,
);
}
expect(systemTokens.layer.popover.$value + 1).toBeLessThan(systemTokens.layer.tooltip.$value);
});

// A containing block on the host would re-anchor every `position: fixed`
// overlay to the host box instead of the viewport, moving all of them.
it('never becomes a containing block for fixed-position overlays', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,12 +82,12 @@
&__backdrop {
position: fixed;
inset: 0;
z-index: 9998;
z-index: calc(var(--openbitfun-layer-popover) - 1);
}

&__menu {
position: fixed;
z-index: 9999;
z-index: var(--openbitfun-layer-popover);
}

&__item-metadata {
Expand Down
Loading