- Nested Flex components automatically increment their neutral background
- level. No explicit bg prop is needed on inner Flex components.
+ Flex is a layout primitive and is transparent to the bg system by
+ default. Only an explicit bg prop establishes a new bg level. Nested
+ Flex components without a bg prop inherit the parent context unchanged.
Neutral 1 (explicit)
-
- Auto (becomes neutral-2)
-
- Auto (becomes neutral-3)
-
- Auto (becomes neutral-4)
-
- Auto (stays neutral-4 - capped)
-
-
+
+ Neutral 2 (explicit)
+
+ Neutral 3 (explicit, capped)
diff --git a/packages/ui/src/components/Flex/Flex.tsx b/packages/ui/src/components/Flex/Flex.tsx
index a1bffa2a13..c4b8f9744c 100644
--- a/packages/ui/src/components/Flex/Flex.tsx
+++ b/packages/ui/src/components/Flex/Flex.tsx
@@ -24,8 +24,11 @@ import { BgProvider, useBg } from '../../hooks/useBg';
/** @public */
export const Flex = forwardRef((props, ref) => {
- // Resolve the bg this Flex creates for its children
- const { bg: resolvedBg } = useBg({ mode: 'container', bg: props.bg });
+ // Only establish bg context when an explicit bg prop is provided.
+ // Flex is a layout primitive — it should be transparent to the bg system by default.
+ const { bg: resolvedBg } = useBg(
+ props.bg !== undefined ? { mode: 'container', bg: props.bg } : undefined,
+ );
const { classNames, dataAttributes, utilityClasses, style, cleanedProps } =
useStyles(FlexDefinition, {
diff --git a/packages/ui/src/components/Grid/Grid.stories.tsx b/packages/ui/src/components/Grid/Grid.stories.tsx
index fe5b96dfe1..187a97dbbd 100644
--- a/packages/ui/src/components/Grid/Grid.stories.tsx
+++ b/packages/ui/src/components/Grid/Grid.stories.tsx
@@ -182,15 +182,16 @@ export const BgAutoIncrement = meta.story({
render: args => (
- Nested Grid components automatically increment their neutral background.
- Each nested Grid.Item inherits and increments from its parent's bg.
+ Grid is a layout primitive and is transparent to the bg system by
+ default. Only an explicit bg prop establishes a new bg level. Nested
+ grids without a bg prop inherit the parent context unchanged.
Neutral 1 (Grid.Root)
-
- Nested: Auto (becomes neutral-2)
- Nested: Auto (becomes neutral-2)
+
+ Nested: neutral-2 (explicit)
+ Nested: neutral-2 (explicit)
diff --git a/packages/ui/src/components/Grid/Grid.tsx b/packages/ui/src/components/Grid/Grid.tsx
index ddea92ce19..3e3ad7f300 100644
--- a/packages/ui/src/components/Grid/Grid.tsx
+++ b/packages/ui/src/components/Grid/Grid.tsx
@@ -23,8 +23,11 @@ import styles from './Grid.module.css';
import { BgProvider, useBg } from '../../hooks/useBg';
const GridRoot = forwardRef((props, ref) => {
- // Resolve the bg this Grid creates for its children
- const { bg: resolvedBg } = useBg({ mode: 'container', bg: props.bg });
+ // Only establish bg context when an explicit bg prop is provided.
+ // Grid is a layout primitive — it should be transparent to the bg system by default.
+ const { bg: resolvedBg } = useBg(
+ props.bg !== undefined ? { mode: 'container', bg: props.bg } : undefined,
+ );
const { classNames, dataAttributes, utilityClasses, style, cleanedProps } =
useStyles(GridDefinition, {
@@ -59,8 +62,11 @@ const GridRoot = forwardRef((props, ref) => {
});
const GridItem = forwardRef((props, ref) => {
- // Resolve the bg this GridItem creates for its children
- const { bg: resolvedBg } = useBg({ mode: 'container', bg: props.bg });
+ // Only establish bg context when an explicit bg prop is provided.
+ // GridItem is a layout slot — it should be transparent to the bg system by default.
+ const { bg: resolvedBg } = useBg(
+ props.bg !== undefined ? { mode: 'container', bg: props.bg } : undefined,
+ );
const { classNames, dataAttributes, utilityClasses, style, cleanedProps } =
useStyles(GridItemDefinition, {
diff --git a/packages/ui/src/components/ToggleButton/ToggleButton.tsx b/packages/ui/src/components/ToggleButton/ToggleButton.tsx
index c9956b4f22..d783681778 100644
--- a/packages/ui/src/components/ToggleButton/ToggleButton.tsx
+++ b/packages/ui/src/components/ToggleButton/ToggleButton.tsx
@@ -21,7 +21,6 @@ import type { ToggleButtonProps } from './types';
import { useStyles } from '../../hooks/useStyles';
import { ToggleButtonDefinition } from './definition';
import styles from './ToggleButton.module.css';
-import { useBg } from '../../hooks/useBg';
/** @public */
export const ToggleButton = forwardRef(
@@ -36,14 +35,11 @@ export const ToggleButton = forwardRef(
const { children, className, iconStart, iconEnd, ...rest } = cleanedProps;
- const { bg } = useBg({ mode: 'leaf' });
-
return (
{renderProps => {
diff --git a/packages/ui/src/hooks/useBg.tsx b/packages/ui/src/hooks/useBg.tsx
index fd3d4319e5..3cec51302b 100644
--- a/packages/ui/src/hooks/useBg.tsx
+++ b/packages/ui/src/hooks/useBg.tsx
@@ -62,11 +62,9 @@ const BgContext = createVersionedContext<{
* Increments a neutral bg level by one, capping at 'neutral-3'.
* Intent backgrounds (danger, warning, success) pass through unchanged.
*
- * Only used by container components for auto-increment. The 'neutral-4'
- * level is reserved for leaf components and is never set on containers.
+ * The 'neutral-4' level is reserved for leaf component CSS and is never
+ * set on containers.
*
- * @param bg - The current bg value
- * @returns The incremented bg value
* @internal
*/
function incrementNeutralBg(
@@ -81,44 +79,29 @@ function incrementNeutralBg(
}
/**
- * Resolves the bg value for a container component.
+ * Resolves the bg for a container component.
*
- * - If an explicit bg prop is provided, use that.
- * - If no bg prop is provided but there is a parent context, auto-increment from parent.
- * - If no bg prop and no context, return undefined (no bg).
+ * Uses the explicit `bg` prop if provided. Otherwise auto-increments from
+ * the parent context, capping at `neutral-3`. Returns undefined when there
+ * is no prop and no parent context.
*
* @internal
*/
-function resolveBgForContainer(
- contextBg: ContainerBg | undefined,
+function resolveContainerBg(
+ context: BgContextValue,
propBg: ContainerBg | undefined,
-): ContainerBg | undefined {
+): BgContextValue {
// Explicit bg prop takes priority
if (propBg !== undefined) {
- return propBg;
+ return { bg: propBg };
}
// No explicit bg: auto-increment from context if available
- if (contextBg === undefined) {
- return undefined;
+ if (context.bg === undefined) {
+ return { bg: undefined };
}
- return incrementNeutralBg(contextBg);
-}
-
-/**
- * Resolves the bg value for a leaf component.
- *
- * Returns the parent context bg unchanged. The leaf component's CSS
- * handles the visual step-up (e.g. on neutral-1 surface → use neutral-2 tokens).
- * If no context, returns undefined.
- *
- * @internal
- */
-function resolveBgForLeaf(
- contextBg: ContainerBg | undefined,
-): ContainerBg | undefined {
- return contextBg;
+ return { bg: incrementNeutralBg(context.bg) };
}
/**
@@ -137,9 +120,6 @@ export const BgProvider = ({ bg, children }: BgProviderProps) => {
/**
* Hook to access and resolve the current bg context.
*
- * All bg resolution logic lives here — callers only need to specify the mode
- * and (for containers) the explicit bg prop value.
- *
* - **Container mode** — uses explicit `bg` if provided, otherwise auto-increments
* from parent context. Caps at `neutral-3`.
* - **Leaf mode** — returns the parent context bg unchanged. No prop needed.
@@ -157,15 +137,17 @@ export const useBg = (options?: UseBgOptions): BgContextValue => {
return context;
}
+ // Leaf mode: return the parent context bg unchanged.
+ // The leaf component's CSS handles the visual step-up.
+ if (options.mode === 'leaf') {
+ return context;
+ }
+
// Resolve responsive prop value to a scalar for the current breakpoint
- const resolvedPropBg =
+ const propBg =
options.bg !== undefined
? resolveResponsiveValue(options.bg, breakpoint)
: undefined;
- if (options.mode === 'leaf') {
- return { bg: resolveBgForLeaf(context.bg) };
- }
-
- return { bg: resolveBgForContainer(context.bg, resolvedPropBg) };
+ return resolveContainerBg(context, propBg);
};