- Using bg="neutral-auto" on Flex auto-increments from the parent context.
- The first Flex defaults to neutral-1 (no parent), then each nested Flex
+ Using bg="neutral" on Flex auto-increments from the parent context. The
+ first Flex defaults to neutral-1 (no parent), then each nested Flex
increments by one, capping at neutral-3.
-
-
Neutral 1 (auto, no parent)
-
+
+
Neutral 1 (no parent)
+
Neutral 2 (auto-incremented)
-
+
Neutral 3 (auto-incremented, capped)
diff --git a/packages/ui/src/components/Flex/definition.ts b/packages/ui/src/components/Flex/definition.ts
index d0452d6b2c..2701ce5e10 100644
--- a/packages/ui/src/components/Flex/definition.ts
+++ b/packages/ui/src/components/Flex/definition.ts
@@ -45,13 +45,6 @@ export const FlexDefinition = {
'direction',
],
dataAttributes: {
- bg: [
- 'neutral-1',
- 'neutral-2',
- 'neutral-3',
- 'danger',
- 'warning',
- 'success',
- ] as const,
+ bg: ['neutral', 'danger', 'warning', 'success'] as const,
},
} as const satisfies ComponentDefinition;
diff --git a/packages/ui/src/components/Grid/Grid.stories.tsx b/packages/ui/src/components/Grid/Grid.stories.tsx
index b3b6957359..f60517bc31 100644
--- a/packages/ui/src/components/Grid/Grid.stories.tsx
+++ b/packages/ui/src/components/Grid/Grid.stories.tsx
@@ -113,18 +113,21 @@ export const Backgrounds = meta.story({
render: args => (
-
- Neutral 1
-
-
- Neutral 2
-
-
- Neutral 3
-
-
- Responsive Bg
+
+ Neutral (level 1)
+
+
+ Neutral (level 2)
+
+
+
+
+
+ Neutral (level 3)
+
+
+
Danger
@@ -137,28 +140,26 @@ export const Backgrounds = meta.story({
-
- Neutral 1
-
-
-
-
- Neutral 2
-
-
-
-
- Neutral 3
-
-
-
-
- Responsive Bg
+
+ Neutral (level 1)
+
+
+
+ Neutral (level 2)
+
+
+
+
+
+
+
+ Neutral (level 3)
+
+
+
+
Danger
@@ -179,7 +180,7 @@ export const Backgrounds = meta.story({
),
});
-export const BgNeutralAuto = meta.story({
+export const BgNeutral = meta.story({
args: { px: '6', py: '4', columns: '2', gap: '4' },
render: args => (
@@ -188,12 +189,12 @@ export const BgNeutralAuto = meta.story({
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: neutral-2 (explicit)
- Nested: neutral-2 (explicit)
+
+ Nested: neutral-2 (auto-incremented)
+ Nested: neutral-2 (auto-incremented)
diff --git a/packages/ui/src/components/Grid/definition.ts b/packages/ui/src/components/Grid/definition.ts
index c3acb59274..2f417ab34b 100644
--- a/packages/ui/src/components/Grid/definition.ts
+++ b/packages/ui/src/components/Grid/definition.ts
@@ -43,14 +43,7 @@ export const GridDefinition = {
'py',
],
dataAttributes: {
- bg: [
- 'neutral-1',
- 'neutral-2',
- 'neutral-3',
- 'danger',
- 'warning',
- 'success',
- ] as const,
+ bg: ['neutral', 'danger', 'warning', 'success'] as const,
},
} as const satisfies ComponentDefinition;
@@ -64,13 +57,6 @@ export const GridItemDefinition = {
},
utilityProps: ['colSpan', 'colEnd', 'colStart', 'rowSpan'],
dataAttributes: {
- bg: [
- 'neutral-1',
- 'neutral-2',
- 'neutral-3',
- 'danger',
- 'warning',
- 'success',
- ] as const,
+ bg: ['neutral', 'danger', 'warning', 'success'] as const,
},
} as const satisfies ComponentDefinition;
diff --git a/packages/ui/src/components/Menu/Menu.tsx b/packages/ui/src/components/Menu/Menu.tsx
index da4243f643..a9835b2f68 100644
--- a/packages/ui/src/components/Menu/Menu.tsx
+++ b/packages/ui/src/components/Menu/Menu.tsx
@@ -59,6 +59,7 @@ import {
import styles from './Menu.module.css';
import clsx from 'clsx';
import { Box } from '../Box';
+import { BgReset } from '../../hooks/useBg';
const { RoutingProvider, useRoutingRegistrationEffect } =
createRoutingRegistration();
@@ -120,23 +121,25 @@ export const Menu = (props: MenuProps
+
);
@@ -314,48 +321,50 @@ export const MenuAutocompleteListbox = (
)}
placement={placement}
>
-
-
-
+
+
+
+
+
+
+
+
+ {virtualized ? (
+
+ {listBoxContent}
+
+ ) : (
+ listBoxContent
)}
- aria-label={props.placeholder || 'Search'}
- >
-
-
-
-
-
- {virtualized ? (
-
- {listBoxContent}
-
- ) : (
- listBoxContent
- )}
-
-
+
+
+
);
};
diff --git a/packages/ui/src/components/Popover/Popover.stories.tsx b/packages/ui/src/components/Popover/Popover.stories.tsx
index e05b352fc9..0a0e68b572 100644
--- a/packages/ui/src/components/Popover/Popover.stories.tsx
+++ b/packages/ui/src/components/Popover/Popover.stories.tsx
@@ -208,7 +208,7 @@ export const WithRichContent = Default.extend({
This is a popover with rich content. It can contain multiple
elements and formatted text.
-
+ You can also use the automatic bg system inside it.
diff --git a/packages/ui/src/components/Popover/Popover.tsx b/packages/ui/src/components/Popover/Popover.tsx
index ad8a203558..ed13328a94 100644
--- a/packages/ui/src/components/Popover/Popover.tsx
+++ b/packages/ui/src/components/Popover/Popover.tsx
@@ -23,6 +23,7 @@ import { useStyles } from '../../hooks/useStyles';
import { PopoverDefinition } from './definition';
import styles from './Popover.module.css';
import { Box } from '../Box';
+import { BgReset } from '../../hooks/useBg';
/**
* A popover component built on React Aria Components that displays floating
@@ -95,12 +96,14 @@ export const Popover = forwardRef(
)}
-
- {children}
-
+
+
+ {children}
+
+
>
)}
diff --git a/packages/ui/src/components/ToggleButton/ToggleButton.stories.tsx b/packages/ui/src/components/ToggleButton/ToggleButton.stories.tsx
index a89bbe3862..876ff654b0 100644
--- a/packages/ui/src/components/ToggleButton/ToggleButton.stories.tsx
+++ b/packages/ui/src/components/ToggleButton/ToggleButton.stories.tsx
@@ -16,6 +16,7 @@
import preview from '../../../../../.storybook/preview';
import { ToggleButton } from './ToggleButton';
+import { Box } from '../Box';
import { Flex } from '../Flex';
import { Text } from '../Text';
import { useState } from 'react';
@@ -65,21 +66,27 @@ export const Backgrounds = meta.story({
On Neutral 1
-
+ ToggleOn Neutral 2
-
- Toggle
-
+
+
+ Toggle
+
+ On Neutral 3
-
- Toggle
-
+
+
+
+ Toggle
+
+
+
),
diff --git a/packages/ui/src/components/ToggleButtonGroup/ToggleButtonGroup.stories.tsx b/packages/ui/src/components/ToggleButtonGroup/ToggleButtonGroup.stories.tsx
index 617cd0c0fa..c98a2e18f1 100644
--- a/packages/ui/src/components/ToggleButtonGroup/ToggleButtonGroup.stories.tsx
+++ b/packages/ui/src/components/ToggleButtonGroup/ToggleButtonGroup.stories.tsx
@@ -17,6 +17,7 @@
import preview from '../../../../../.storybook/preview';
import { ToggleButtonGroup } from './ToggleButtonGroup';
import { ToggleButton } from '../ToggleButton/ToggleButton';
+import { Box } from '../Box';
import { Flex } from '../Flex';
import { Text } from '../Text';
import { useState } from 'react';
@@ -100,7 +101,7 @@ export const Backgrounds = meta.story({
On Neutral 1
-
+ On Neutral 2
-
-
- Option 1
- Option 2
- Option 3
-
-
+
+
+
+ Option 1
+ Option 2
+ Option 3
+
+
+ On Neutral 3
-
-
- Option 1
- Option 2
- Option 3
-
-
+
+
+
+
+ Option 1
+ Option 2
+ Option 3
+
+
+
+
),
diff --git a/packages/ui/src/components/Tooltip/Tooltip.tsx b/packages/ui/src/components/Tooltip/Tooltip.tsx
index a64c8c072d..d7fb97005b 100644
--- a/packages/ui/src/components/Tooltip/Tooltip.tsx
+++ b/packages/ui/src/components/Tooltip/Tooltip.tsx
@@ -28,6 +28,7 @@ import { useStyles } from '../../hooks/useStyles';
import { TooltipDefinition } from './definition';
import styles from './Tooltip.module.css';
import { Box } from '../Box';
+import { BgReset } from '../../hooks/useBg';
/** @public */
export const TooltipTrigger = (props: TooltipTriggerComponentProps) => {
@@ -71,12 +72,14 @@ export const Tooltip = forwardRef(
-
- {children}
-
+
+
+ {children}
+
+
);
},
diff --git a/packages/ui/src/hooks/useBg.tsx b/packages/ui/src/hooks/useBg.tsx
index 6e298fedf9..2284e5f337 100644
--- a/packages/ui/src/hooks/useBg.tsx
+++ b/packages/ui/src/hooks/useBg.tsx
@@ -69,6 +69,24 @@ export const BgProvider = ({ bg, children }: BgProviderProps) => {
);
};
+/**
+ * Resets the bg context to undefined, cutting any inherited neutral chain.
+ * Use this inside overlay components (Popover, Tooltip, Dialog, Menu) so
+ * their content always starts from neutral-1 regardless of where the trigger
+ * is placed in the tree.
+ *
+ * @internal
+ */
+export const BgReset = ({ children }: { children: ReactNode }) => {
+ return (
+
+ {children}
+
+ );
+};
+
/**
* Hook for consumer components (e.g. Button) to read the parent bg context.
*
@@ -90,10 +108,14 @@ export function useBgConsumer(): BgContextValue {
*
* - `bg` is `undefined` -- transparent, no context change, returns `{ bg: undefined }`.
* This is the default for Box, Flex, and Grid (they do **not** auto-increment).
- * - `bg` is a `ContainerBg` value -- uses that value directly (e.g. `'neutral-1'`).
- * - `bg` is `'neutral-auto'` -- increments the neutral level from the parent context,
- * capping at `neutral-3`. Only components that explicitly pass `'neutral-auto'`
- * (e.g. Card) will auto-increment; it is never implicit.
+ * - `bg` is `'neutral'` -- when the parent bg is neutral, increments the neutral
+ * level from the parent context, capping at `neutral-3`. When the parent bg is
+ * an intent (`'danger'` | `'warning'` | `'success'`), the intent passes through
+ * unchanged (i.e. `bg: 'neutral'` does not override the parent intent). The
+ * increment is always relative to the parent; it is not possible to pin a
+ * container to an explicit neutral level.
+ * - `bg` is `'danger'` | `'warning'` | `'success'` -- sets the bg to that intent
+ * explicitly, regardless of the parent value.
*
* **Capping:**
*
@@ -116,7 +138,7 @@ export function useBgProvider(bg?: Responsive): BgContextValue {
const resolved = resolveResponsiveValue(bg, breakpoint);
- if (resolved === 'neutral-auto') {
+ if (resolved === 'neutral') {
return { bg: incrementNeutralBg(context.bg) };
}
diff --git a/packages/ui/src/types.ts b/packages/ui/src/types.ts
index d3dfd996aa..af87bfb20b 100644
--- a/packages/ui/src/types.ts
+++ b/packages/ui/src/types.ts
@@ -182,7 +182,7 @@ export interface ComponentDefinition {
}
/**
- * Background type for the neutral bg system.
+ * Resolved background level stored in context and applied as `data-bg` on DOM elements.
*
* Supports neutral levels ('neutral-1' through 'neutral-3') and
* intent backgrounds ('danger', 'warning', 'success').
@@ -190,6 +190,9 @@ export interface ComponentDefinition {
* The 'neutral-4' level is not exposed as a prop value -- it is reserved
* for leaf component CSS (e.g. Button on a 'neutral-3' surface).
*
+ * This is the resolved/internal representation used by the bg context system.
+ * For the prop type accepted by container components, use `ProviderBg` instead.
+ *
* @public
*/
export type ContainerBg =
@@ -201,11 +204,13 @@ export type ContainerBg =
| 'success';
/**
- * Background values accepted by provider components.
+ * Background values accepted by provider components (Box, Flex, Grid, Card, etc.).
*
- * Includes all `ContainerBg` values plus `'neutral-auto'` which
- * automatically increments the neutral level from the parent context.
+ * - `'neutral'` — automatically increments the neutral level from the parent context,
+ * capping at the maximum level. This is always incremental; explicit levels cannot
+ * be set directly.
+ * - `'danger'` | `'warning'` | `'success'` — intent backgrounds used as-is.
*
* @public
*/
-export type ProviderBg = ContainerBg | 'neutral-auto';
+export type ProviderBg = 'neutral' | 'danger' | 'warning' | 'success';