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: 4 additions & 0 deletions design-system/packages/ui/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -678,6 +678,10 @@ the SearchField over that anchor.
letting their shells reuse the grouped surface. The default field surface stays
theme-owned, and portalled menus remain on the opaque panel surface.

`FieldGroup fieldSurface="filled"` uses the theme's grouped field background with
the standard resting, hover, focus, invalid, and forced-color borders. It applies to Input, Select, Combobox, Textarea, and NumberInput; nested
groups can explicitly restore the default surface.

`Menu` remains composable inline anatomy. `MenuPopover` composes it into a
controlled anchored or coordinate popup. Pass `items`, `open`, `onClose` and
either `anchorRef` or `position`. Entries can include `submenu`, `shortcut`,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -424,4 +424,13 @@
from { opacity: 0; }
to { opacity: 1; }
}

/* Filled fields keep standard borders over the grouped-field background. */
.root[data-field-surface="filled"] .trigger {
background: var(--openbitfun-color-field-group-background);
}

.root[data-field-surface="filled"] .trigger:hover:not(:disabled) {
background: var(--openbitfun-color-field-background-hover);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export const fieldGroupMeta = {
{ name: "FormSection.actions", type: "ReactNode" },
{ defaultValue: "h2", name: "FormSection.headingAs", type: "h2 | h3 | h4" },
{ defaultValue: "subtle", name: "appearance", type: "subtle | plain" },
{ defaultValue: "default", name: "fieldSurface", type: "default | ambient" },
{ defaultValue: "default", name: "fieldSurface", type: "default | ambient | filled" },
{ defaultValue: "true", name: "dividers", type: "boolean" },
{ defaultValue: "center", name: "FieldRow.align", type: "center | start" },
{ defaultValue: "md", name: "FieldRow.padding", type: "none | md" },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export interface FormSectionProps
export interface FieldGroupProps extends HTMLAttributes<HTMLDivElement> {
appearance?: FieldGroupAppearance;
dividers?: boolean;
/** Lets text and picker field shells reuse the surrounding grouped surface. */
/** Ambient reuses the group surface; filled keeps standard borders over a grouped-field background. */
fieldSurface?: FieldGroupFieldSurface;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -145,4 +145,13 @@
border-color: Highlight;
}
}

/* Filled fields keep standard borders over the grouped-field background. */
.field[data-field-surface="filled"] {
background: var(--openbitfun-color-field-group-background);
}

.field[data-field-surface="filled"]:hover:not([data-disabled="true"]) {
background: var(--openbitfun-color-field-background-hover);
}
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -326,4 +326,13 @@
color: Highlight;
}
}

/* Filled fields keep standard borders over the grouped-field background. */
.root[data-field-surface="filled"] .trigger {
background: var(--openbitfun-color-field-group-background);
}

.root[data-field-surface="filled"] .trigger:hover:not(:disabled) {
background: var(--openbitfun-color-field-background-hover);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,13 @@
.count { margin-inline-start: auto; white-space: nowrap; }
@media (prefers-reduced-motion: reduce) { .textarea { transition: none; } }
@media (forced-colors: active) { .textarea { border-color: ButtonText; } .root .textarea:focus { border-color: Highlight; } }

/* Filled fields keep standard borders over the grouped-field background. */
.root[data-field-surface="filled"] .textarea {
background: var(--openbitfun-color-field-group-background);
}

.root[data-field-surface="filled"] .textarea:hover:not(:disabled) {
background: var(--openbitfun-color-field-background-hover);
}
}
2 changes: 1 addition & 1 deletion design-system/packages/ui/src/internal/fieldSurface.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { createContext, useContext } from "react";

export type FieldSurface = "ambient" | "default";
export type FieldSurface = "ambient" | "default" | "filled";

export const FieldSurfaceContext = createContext<FieldSurface>("default");

Expand Down
26 changes: 14 additions & 12 deletions design-system/packages/ui/tests/field-group.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -95,19 +95,21 @@ test("field surface context reaches canonical field shells and resets for nested
["number-input", createElement(NumberInput, { onValueChange() {}, value: 1 })],
];

controls.forEach(([component, control]) => {
const markup = renderToStaticMarkup(
createElement(FieldGroup, { fieldSurface: "ambient" }, control),
);
assert.match(markup, new RegExp(`data-openbitfun-component="${component}"[^>]+data-field-surface="ambient"`));
});
for (const fieldSurface of ["ambient", "filled"]) {
controls.forEach(([component, control]) => {
const markup = renderToStaticMarkup(
createElement(FieldGroup, { fieldSurface }, control),
);
assert.match(markup, new RegExp(`data-openbitfun-component="${component}"[^>]+data-field-surface="${fieldSurface}"`));
});

const nestedMarkup = renderToStaticMarkup(
createElement(FieldGroup, { fieldSurface: "ambient" },
createElement(FieldGroup, { fieldSurface: "default" }, createElement(Input)),
),
);
assert.match(nestedMarkup, /data-openbitfun-component="input"[^>]+data-field-surface="default"/);
const nestedMarkup = renderToStaticMarkup(
createElement(FieldGroup, { fieldSurface },
createElement(FieldGroup, { fieldSurface: "default" }, createElement(Input)),
),
);
assert.match(nestedMarkup, /data-openbitfun-component="input"[^>]+data-field-surface="default"/);
}
});

test("ambient field groups preserve opaque overlays while field shells reuse the group surface", async () => {
Expand Down
Loading
Loading