Skip to content

Commit 64008cd

Browse files
authored
Merge pull request #2921 from kev1n77/fmy/ui-main
fix(ui): align dialog typography, spacing, and floating footer
2 parents 0b865ce + c40a391 commit 64008cd

12 files changed

Lines changed: 93 additions & 27 deletions

File tree

‎design-system/apps/design-lab/src/pages/ComponentDetailPage.tsx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -817,7 +817,7 @@ export function ComponentDetailPage({
817817
</DialogHeading>
818818
<DialogClose />
819819
</DialogHeader>
820-
<DialogBody className="component-dialog-example__body">{renderDialogConfigurationContent()}</DialogBody>
820+
<DialogBody>{renderDialogConfigurationContent()}</DialogBody>
821821
<DialogFooter appearance="floating">
822822
<Button onClick={closePreview} variant="fill">
823823
{t("components.preview.modalCancel")}

‎design-system/apps/design-lab/src/styles.css‎

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3744,11 +3744,6 @@ body,
37443744
min-width: 0;
37453745
}
37463746

3747-
.component-dialog-example__body {
3748-
padding-block-end: var(--openbitfun-overlay-dialog-footer-content-inset);
3749-
scroll-padding-block-end: var(--openbitfun-overlay-dialog-footer-content-inset);
3750-
}
3751-
37523747
.component-dialog-example__hint {
37533748
margin: 0;
37543749
color: var(--openbitfun-color-content-muted);

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -263,10 +263,10 @@
263263
},
264264
"dialog": {
265265
"fontFamily": { "$value": "{font.family.sans}" },
266-
"fontSize": { "$value": "{font.size.3xl}" },
267-
"fontWeight": { "$value": "{font.weight.semibold}" },
268-
"lineHeight": { "$value": "{lineHeight.tight}" },
269-
"letterSpacing": { "$value": "{letterSpacing.tight}" }
266+
"fontSize": { "$value": "{font.size.3xl-plus}" },
267+
"fontWeight": { "$value": "{font.weight.bold}" },
268+
"lineHeight": { "$value": 1.2083333333 },
269+
"letterSpacing": { "$value": "{letterSpacing.normal}" }
270270
},
271271
"display": {
272272
"fontFamily": { "$value": "{font.family.sans}" },

‎design-system/packages/ui/README.md‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -415,3 +415,5 @@ caption color directly, avoiding a second opacity multiplier. The nested-menu
415415
Pattern includes a scrolling toggle for keyboard and submenu verification.
416416

417417
Compact tabs use `size="sm"` (30px, 14px icons, 4px icon gap); standard tabs retain 40px and 16px icons. Tabs share the outline-button surface contract and keep selection separate from pointer press. `SegmentedControl size="md"` uses a borderless 36px bar with 30px segments, 3px inset, 4px gaps and 12px segment padding. The default `sm` bar keeps its 28px outer height; separate pills retain their existing heights. Mobile controls own their touch geometry independently.
418+
419+
Dialog titles use 24px bold type with their own 29px line box and normal tracking. `DialogHeader` and `DialogFooter` omit separators by default; pass `separator` for a deliberate divider. A direct `DialogBody` sibling of `DialogFooter appearance="floating"` owns the trailing scroll inset automatically. The floating footer provides the 68px centered action area and a masked blur/gradient using the current theme surface; reduced transparency and forced colors use an opaque fallback. Keep scrollable form content inside `DialogBody` instead of adding a second viewport with independent footer spacing.

‎design-system/packages/ui/src/components/Dialog/Dialog.meta.ts‎

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,19 @@ import type { ComponentMeta } from "../../registry.types";
22

33
export const dialogMeta = {
44
category: "feedback",
5-
description: "A compound dialog with attached or opaque floating footer anatomy on the shared overlay kernel.",
5+
description: "A compound dialog with attached or frosted floating footer anatomy on the shared overlay kernel.",
66
maturity: "stable",
77
name: "Dialog",
88
props: [
99
{ name: "open", type: "boolean" },
1010
{ name: "onOpenChange", type: "(open: false, reason: DialogCloseReason) => void" },
1111
{ defaultValue: "md", name: "size", type: "sm | md | lg | xl | 2xl" },
12+
{ defaultValue: "false", name: "DialogHeader.separator", type: "boolean" },
13+
{ defaultValue: "false", name: "DialogFooter.separator", type: "boolean" },
1214
{ defaultValue: "true", name: "closeOnEscape", type: "boolean" },
1315
{ defaultValue: "true", name: "closeOnPointerOutside", type: "boolean" },
1416
],
15-
states: ["default", "open", "alert", "scrolling", "opaque-floating-footer"],
17+
states: ["default", "open", "alert", "scrolling", "floating-footer"],
1618
tokens: [
1719
"color.overlay.scrim",
1820
"color.surface.raised",
@@ -27,12 +29,18 @@ export const dialogMeta = {
2729
"overlay.dialog.footerPaddingBlockStart",
2830
"overlay.dialog.footerPaddingBlockEnd",
2931
"overlay.dialog.footerActionMinWidth",
32+
"overlay.dialog.footerHeight",
33+
"overlay.dialog.footerFadeExtent",
34+
"overlay.dialog.footerContentInset",
35+
"overlay.dialog.footerBlur",
3036
"shadow.overlay",
3137
"motion.distance.sm",
3238
"type.support",
3339
"type.heading.dialog.fontFamily",
3440
"type.heading.dialog.fontSize",
3541
"type.heading.dialog.fontWeight",
42+
"type.heading.dialog.lineHeight",
43+
"type.heading.dialog.letterSpacing",
3644
],
3745
} as const satisfies ComponentMeta;
3846

‎design-system/packages/ui/src/components/Dialog/Dialog.module.css‎

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,9 @@
7676
gap: var(--openbitfun-space-3);
7777
padding-block: var(--openbitfun-overlay-dialog-header-padding-block-start) var(--openbitfun-overlay-dialog-header-padding-block-end);
7878
padding-inline: var(--openbitfun-overlay-dialog-header-padding-inline);
79+
}
80+
81+
.header[data-separator="true"] {
7982
border-block-end: var(--openbitfun-border-width-default) solid var(--openbitfun-color-border-subtle);
8083
}
8184

@@ -95,7 +98,7 @@
9598
font-family: var(--openbitfun-type-heading-dialog-font-family);
9699
font-size: var(--openbitfun-type-heading-dialog-font-size);
97100
font-weight: var(--openbitfun-type-heading-dialog-font-weight);
98-
line-height: var(--openbitfun-type-body-sm-line-height);
101+
line-height: var(--openbitfun-type-heading-dialog-line-height);
99102
letter-spacing: var(--openbitfun-type-heading-dialog-letter-spacing);
100103
white-space: nowrap;
101104
}
@@ -133,6 +136,15 @@
133136
padding: var(--openbitfun-overlay-dialog-content-padding-lg);
134137
}
135138

139+
.header + .body[data-inset="standard"] {
140+
padding-block-start: 0;
141+
}
142+
143+
.surface:has(> .footer[data-appearance="floating"]) > .body {
144+
padding-block-end: var(--openbitfun-overlay-dialog-footer-content-inset);
145+
scroll-padding-block-end: var(--openbitfun-overlay-dialog-footer-content-inset);
146+
}
147+
136148
.footer {
137149
display: flex;
138150
flex: 0 0 auto;
@@ -141,6 +153,9 @@
141153
gap: var(--openbitfun-space-2);
142154
padding-block: var(--openbitfun-overlay-dialog-footer-padding-block-start) var(--openbitfun-overlay-dialog-footer-padding-block-end);
143155
padding-inline: var(--openbitfun-overlay-dialog-footer-padding-inline);
156+
}
157+
158+
.footer[data-separator="true"] {
144159
border-block-start: var(--openbitfun-border-width-default) solid var(--openbitfun-color-border-subtle);
145160
}
146161

@@ -149,11 +164,26 @@
149164
z-index: var(--openbitfun-layer-content);
150165
inset-inline: 0;
151166
inset-block-end: 0;
167+
min-block-size: var(--openbitfun-overlay-dialog-footer-height);
152168
justify-content: center;
153-
background: var(--openbitfun-color-surface-raised);
169+
isolation: isolate;
170+
background: transparent;
154171
pointer-events: auto;
155172
}
156173

174+
.footer[data-appearance="floating"]::before {
175+
position: absolute;
176+
z-index: -1;
177+
inset: 0;
178+
inset-block-start: calc(-1 * var(--openbitfun-overlay-dialog-footer-fade-extent));
179+
background: linear-gradient(to bottom, transparent, var(--openbitfun-color-surface-raised));
180+
-webkit-backdrop-filter: var(--openbitfun-overlay-dialog-footer-blur);
181+
backdrop-filter: var(--openbitfun-overlay-dialog-footer-blur);
182+
mask-image: linear-gradient(to bottom, transparent, var(--openbitfun-color-surface-raised) var(--openbitfun-overlay-dialog-footer-fade-extent));
183+
content: "";
184+
pointer-events: none;
185+
}
186+
157187
.footer[data-appearance="floating"] > * {
158188
min-inline-size: var(--openbitfun-overlay-dialog-footer-action-min-width);
159189
pointer-events: auto;
@@ -179,4 +209,14 @@
179209
animation-duration: 1ms;
180210
}
181211
}
212+
213+
@media (forced-colors: active), (prefers-reduced-transparency: reduce) {
214+
.footer[data-appearance="floating"]::before {
215+
inset-block-start: 0;
216+
background: var(--openbitfun-color-surface-raised);
217+
-webkit-backdrop-filter: none;
218+
backdrop-filter: none;
219+
mask-image: none;
220+
}
221+
}
182222
}

‎design-system/packages/ui/src/components/Dialog/Dialog.tsx‎

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -209,9 +209,13 @@ export const Sheet = forwardRef<HTMLDivElement, SheetProps>(function Sheet({
209209
);
210210
});
211211

212-
export const DialogHeader = forwardRef<HTMLDivElement, HTMLAttributes<HTMLDivElement>>(
213-
function DialogHeader({ className, ...props }, ref) {
214-
return <header {...props} className={classNames(styles.header, className)} data-openbitfun-part="header" ref={ref} />;
212+
export interface DialogHeaderProps extends HTMLAttributes<HTMLDivElement> {
213+
separator?: boolean;
214+
}
215+
216+
export const DialogHeader = forwardRef<HTMLDivElement, DialogHeaderProps>(
217+
function DialogHeader({ className, separator = false, ...props }, ref) {
218+
return <header {...props} className={classNames(styles.header, className)} data-openbitfun-part="header" data-separator={separator} ref={ref} />;
215219
},
216220
);
217221

@@ -291,15 +295,17 @@ export const DialogBody = forwardRef<HTMLDivElement, DialogBodyProps>(
291295

292296
export interface DialogFooterProps extends HTMLAttributes<HTMLElement> {
293297
appearance?: DialogFooterAppearance;
298+
separator?: boolean;
294299
}
295300

296301
export const DialogFooter = forwardRef<HTMLElement, DialogFooterProps>(
297-
function DialogFooter({ appearance = "attached", className, ...props }, ref) {
302+
function DialogFooter({ appearance = "attached", className, separator = false, ...props }, ref) {
298303
return (
299304
<footer
300305
{...props}
301306
className={classNames(styles.footer, className)}
302307
data-appearance={appearance}
308+
data-separator={separator}
303309
data-openbitfun-part="footer"
304310
ref={ref}
305311
/>

‎design-system/packages/ui/src/components/Dialog/index.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ export {
1414
type DialogCloseReason,
1515
type DialogFooterAppearance,
1616
type DialogFooterProps,
17+
type DialogHeaderProps,
1718
type DialogProps,
1819
type DialogSize,
1920
type SheetPlacement,

‎design-system/packages/ui/src/index.ts‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,9 @@ export {
171171
type DialogBodyProps,
172172
type DialogCloseProps,
173173
type DialogCloseReason,
174+
type DialogFooterAppearance,
175+
type DialogFooterProps,
176+
type DialogHeaderProps,
174177
type DialogProps,
175178
type DialogSize,
176179
type SheetPlacement,

‎design-system/packages/ui/tests/dialog.test.mjs‎

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { createElement } from "react";
33
import { renderToStaticMarkup } from "react-dom/server";
44
import { readFile } from "node:fs/promises";
55
import test from "node:test";
6-
import { DialogFooter } from "../dist/index.js";
6+
import { DialogFooter, DialogHeader } from "../dist/index.js";
77

88
test("Dialog and Sheet compose the shared overlay kernel and compound anatomy", async () => {
99
const source = await readFile(
@@ -69,7 +69,7 @@ test("Dialog geometry and typography use public design tokens", async () => {
6969
assert.doesNotMatch(styles, /#[0-9a-f]{3,8}/i);
7070
});
7171

72-
test("DialogFooter exposes a centered opaque floating action layer", async () => {
72+
test("DialogFooter exposes a centered frosted floating action layer", async () => {
7373
const markup = renderToStaticMarkup(
7474
createElement(
7575
DialogFooter,
@@ -83,8 +83,17 @@ test("DialogFooter exposes a centered opaque floating action layer", async () =>
8383
assert.match(markup, /data-appearance="floating"/);
8484
assert.match(styles, /\[data-appearance=floating\]\{[^}]*position:absolute/);
8585
assert.match(styles, /\[data-appearance=floating\]\{[^}]*justify-content:center/);
86-
assert.match(styles, /\[data-appearance=floating\]\{[^}]*background:var\(--openbitfun-color-surface-raised\)/);
87-
assert.doesNotMatch(styles, /\[data-appearance=floating\]\{[^}]*background:transparent/);
86+
assert.match(styles, /\[data-appearance=floating\]:before\{[^}]*background:linear-gradient/);
87+
assert.match(styles, /\[data-appearance=floating\]:before\{[^}]*backdrop-filter:var\(--openbitfun-overlay-dialog-footer-blur\)/);
8888
assert.match(styles, /\[data-appearance=floating\]\{[^}]*pointer-events:auto/);
8989
assert.match(styles, /--openbitfun-overlay-dialog-footer-action-min-width/);
9090
});
91+
92+
test("dialog separators are explicit and do not leak native attributes", () => {
93+
for (const component of [DialogHeader, DialogFooter]) {
94+
assert.match(renderToStaticMarkup(createElement(component)), /data-separator="false"/);
95+
const markup = renderToStaticMarkup(createElement(component, { separator: true }));
96+
assert.match(markup, /data-separator="true"/);
97+
assert.doesNotMatch(markup, / separator=/);
98+
}
99+
});

0 commit comments

Comments
 (0)