From 09db540228a391f32ae25ca0c465c07f818d466f Mon Sep 17 00:00:00 2001 From: Charles de Dreuille Date: Fri, 6 Feb 2026 18:46:26 +0000 Subject: [PATCH] Migrate useSurface to useBg Signed-off-by: Charles de Dreuille --- packages/ui/report.api.md | 239 ++++++++++++------ .../ui/src/components/Alert/Alert.stories.tsx | 46 ++-- .../ui/src/components/Alert/definition.ts | 2 - packages/ui/src/components/Alert/types.ts | 8 +- packages/ui/src/components/Box/Box.module.css | 20 +- .../ui/src/components/Box/Box.stories.tsx | 100 +++----- packages/ui/src/components/Box/Box.tsx | 4 +- packages/ui/src/components/Box/definition.ts | 4 +- packages/ui/src/components/Box/types.ts | 4 +- .../src/components/Button/Button.module.css | 12 +- .../src/components/Button/Button.stories.tsx | 194 ++++++-------- .../ui/src/components/Button/definition.ts | 3 +- packages/ui/src/components/Button/types.ts | 4 +- .../ButtonIcon/ButtonIcon.module.css | 12 +- .../src/components/ButtonIcon/definition.ts | 3 +- .../ui/src/components/ButtonIcon/types.ts | 4 +- .../ButtonLink/ButtonLink.module.css | 12 +- .../src/components/ButtonLink/definition.ts | 3 +- .../ui/src/components/ButtonLink/types.ts | 4 +- .../ui/src/components/Card/Card.module.css | 22 +- .../ui/src/components/Card/Card.stories.tsx | 97 ++++--- packages/ui/src/components/Card/Card.tsx | 4 +- packages/ui/src/components/Card/definition.ts | 4 +- packages/ui/src/components/Card/types.ts | 4 +- .../ui/src/components/Flex/Flex.module.css | 20 +- .../ui/src/components/Flex/Flex.stories.tsx | 60 ++--- packages/ui/src/components/Flex/Flex.tsx | 17 +- packages/ui/src/components/Flex/definition.ts | 10 +- packages/ui/src/components/Flex/types.ts | 4 +- .../ui/src/components/Grid/Grid.module.css | 34 +-- .../ui/src/components/Grid/Grid.stories.tsx | 85 +++---- packages/ui/src/components/Grid/Grid.tsx | 32 +-- packages/ui/src/components/Grid/definition.ts | 20 +- packages/ui/src/components/Grid/types.ts | 12 +- .../ToggleButton/ToggleButton.stories.tsx | 18 +- .../components/ToggleButton/ToggleButton.tsx | 9 +- .../ui/src/components/ToggleButton/types.ts | 3 - .../ToggleButtonGroup.stories.tsx | 18 +- packages/ui/src/hooks/useBg.tsx | 166 ++++++++++++ .../hooks/useDefinition/defineComponent.ts | 4 +- packages/ui/src/hooks/useDefinition/index.ts | 1 + packages/ui/src/hooks/useDefinition/types.ts | 35 +-- .../src/hooks/useDefinition/useDefinition.tsx | 67 +++-- packages/ui/src/hooks/useSurface.tsx | 204 --------------- packages/ui/src/index.ts | 6 + packages/ui/src/types.ts | 29 +-- 46 files changed, 809 insertions(+), 854 deletions(-) create mode 100644 packages/ui/src/hooks/useBg.tsx delete mode 100644 packages/ui/src/hooks/useSurface.tsx diff --git a/packages/ui/report.api.md b/packages/ui/report.api.md index 82069bc469..0db25deaf3 100644 --- a/packages/ui/report.api.md +++ b/packages/ui/report.api.md @@ -144,7 +144,6 @@ export const AlertDefinition: { readonly spinner: 'bui-AlertSpinner'; readonly actions: 'bui-AlertActions'; }; - readonly surface: 'container'; readonly propDefs: { readonly status: { readonly dataAttribute: true; @@ -157,7 +156,6 @@ export const AlertDefinition: { readonly customActions: {}; readonly title: {}; readonly description: {}; - readonly surface: {}; readonly className: {}; readonly style: {}; }; @@ -165,7 +163,7 @@ export const AlertDefinition: { }; // @public (undocumented) -export type AlertOwnProps = ContainerSurfaceProps & { +export type AlertOwnProps = { status?: Responsive<'info' | 'success' | 'warning' | 'danger'>; icon?: boolean | ReactElement; loading?: boolean; @@ -207,6 +205,33 @@ export interface AvatarProps extends React.ComponentPropsWithoutRef<'div'> { src: string; } +// @public +export type Bg = + | 'neutral-1' + | 'neutral-2' + | 'neutral-3' + | 'neutral-4' + | 'danger' + | 'warning' + | 'success'; + +// @public (undocumented) +export interface BgContextValue { + // (undocumented) + bg: Responsive | undefined; +} + +// @public +export const BgProvider: ({ bg, children }: BgProviderProps) => JSX_2.Element; + +// @public (undocumented) +export interface BgProviderProps { + // (undocumented) + bg: Responsive; + // (undocumented) + children: ReactNode; +} + // @public (undocumented) export type Border = 'none' | 'base' | 'error' | 'warning' | 'selected'; @@ -234,12 +259,12 @@ export const BoxDefinition: { readonly classNames: { readonly root: 'bui-Box'; }; - readonly surface: 'container'; + readonly bg: 'container'; readonly propDefs: { readonly as: { readonly default: 'div'; }; - readonly surface: { + readonly bg: { readonly dataAttribute: true; }; readonly children: {}; @@ -275,7 +300,7 @@ export const BoxDefinition: { // @public (undocumented) export type BoxOwnProps = { as?: keyof JSX.IntrinsicElements; - surface?: Responsive; + bg?: Responsive; children?: ReactNode; className?: string; style?: CSSProperties; @@ -320,7 +345,7 @@ export const ButtonDefinition: { readonly content: 'bui-ButtonContent'; readonly spinner: 'bui-ButtonSpinner'; }; - readonly surface: 'leaf'; + readonly bg: 'leaf'; readonly propDefs: { readonly size: { readonly dataAttribute: true; @@ -338,7 +363,6 @@ export const ButtonDefinition: { }; readonly iconStart: {}; readonly iconEnd: {}; - readonly onSurface: {}; readonly children: {}; readonly className: {}; readonly style: {}; @@ -360,7 +384,7 @@ export const ButtonIconDefinition: { readonly content: 'bui-ButtonIconContent'; readonly spinner: 'bui-ButtonIconSpinner'; }; - readonly surface: 'leaf'; + readonly bg: 'leaf'; readonly propDefs: { readonly size: { readonly dataAttribute: true; @@ -374,14 +398,13 @@ export const ButtonIconDefinition: { readonly dataAttribute: true; }; readonly icon: {}; - readonly onSurface: {}; readonly className: {}; readonly style: {}; }; }; // @public (undocumented) -export type ButtonIconOwnProps = LeafSurfaceProps & { +export type ButtonIconOwnProps = { size?: Responsive<'small' | 'medium'>; variant?: Responsive<'primary' | 'secondary' | 'tertiary'>; icon?: ReactElement; @@ -409,7 +432,7 @@ export const ButtonLinkDefinition: { readonly root: 'bui-ButtonLink'; readonly content: 'bui-ButtonLinkContent'; }; - readonly surface: 'leaf'; + readonly bg: 'leaf'; readonly propDefs: { readonly size: { readonly dataAttribute: true; @@ -421,7 +444,6 @@ export const ButtonLinkDefinition: { }; readonly iconStart: {}; readonly iconEnd: {}; - readonly onSurface: {}; readonly children: {}; readonly className: {}; readonly style: {}; @@ -429,7 +451,7 @@ export const ButtonLinkDefinition: { }; // @public (undocumented) -export type ButtonLinkOwnProps = LeafSurfaceProps & { +export type ButtonLinkOwnProps = { size?: Responsive<'small' | 'medium'>; variant?: Responsive<'primary' | 'secondary' | 'tertiary'>; iconStart?: ReactElement; @@ -445,7 +467,7 @@ export interface ButtonLinkProps ButtonLinkOwnProps {} // @public (undocumented) -export type ButtonOwnProps = LeafSurfaceProps & { +export type ButtonOwnProps = { size?: Responsive<'small' | 'medium'>; variant?: Responsive<'primary' | 'secondary' | 'tertiary'>; destructive?: boolean; @@ -473,18 +495,45 @@ export const CardBody: ForwardRefExoticComponent< >; // @public -export interface CardBodyProps extends React.HTMLAttributes { - // (undocumented) - children?: React.ReactNode; -} +export const CardBodyDefinition: { + readonly styles: { + readonly [key: string]: string; + }; + readonly classNames: { + readonly root: 'bui-CardBody'; + }; + readonly propDefs: { + readonly children: {}; + readonly className: {}; + }; +}; + +// @public (undocumented) +export type CardBodyOwnProps = { + children?: ReactNode; + className?: string; +}; + +// @public +export interface CardBodyProps + extends CardBodyOwnProps, + React.HTMLAttributes {} // @public export const CardDefinition: { + readonly styles: { + readonly [key: string]: string; + }; readonly classNames: { readonly root: 'bui-Card'; - readonly header: 'bui-CardHeader'; - readonly body: 'bui-CardBody'; - readonly footer: 'bui-CardFooter'; + }; + readonly bg: 'container'; + readonly propDefs: { + readonly bg: { + readonly dataAttribute: true; + }; + readonly children: {}; + readonly className: {}; }; }; @@ -494,10 +543,29 @@ export const CardFooter: ForwardRefExoticComponent< >; // @public -export interface CardFooterProps extends React.HTMLAttributes { - // (undocumented) - children?: React.ReactNode; -} +export const CardFooterDefinition: { + readonly styles: { + readonly [key: string]: string; + }; + readonly classNames: { + readonly root: 'bui-CardFooter'; + }; + readonly propDefs: { + readonly children: {}; + readonly className: {}; + }; +}; + +// @public (undocumented) +export type CardFooterOwnProps = { + children?: ReactNode; + className?: string; +}; + +// @public +export interface CardFooterProps + extends CardFooterOwnProps, + React.HTMLAttributes {} // @public export const CardHeader: ForwardRefExoticComponent< @@ -505,16 +573,41 @@ export const CardHeader: ForwardRefExoticComponent< >; // @public -export interface CardHeaderProps extends React.HTMLAttributes { - // (undocumented) - children?: React.ReactNode; -} +export const CardHeaderDefinition: { + readonly styles: { + readonly [key: string]: string; + }; + readonly classNames: { + readonly root: 'bui-CardHeader'; + }; + readonly propDefs: { + readonly children: {}; + readonly className: {}; + }; +}; + +// @public (undocumented) +export type CardHeaderOwnProps = { + children?: ReactNode; + className?: string; +}; // @public -export interface CardProps extends React.HTMLAttributes { - // (undocumented) - children?: React.ReactNode; -} +export interface CardHeaderProps + extends CardHeaderOwnProps, + React.HTMLAttributes {} + +// @public (undocumented) +export type CardOwnProps = { + bg?: Responsive; + children?: ReactNode; + className?: string; +}; + +// @public +export interface CardProps + extends CardOwnProps, + React.HTMLAttributes {} // @public (undocumented) export const Cell: { @@ -684,12 +777,6 @@ export interface ContainerProps { style?: React.CSSProperties; } -// @public (undocumented) -export interface ContainerSurfaceProps { - // (undocumented) - surface?: Responsive; -} - // @public (undocumented) export interface CursorParams { // (undocumented) @@ -861,11 +948,11 @@ export const FlexDefinition: { 'direction', ]; readonly dataAttributes: { - readonly surface: readonly [ - '0', - '1', - '2', - '3', + readonly bg: readonly [ + 'neutral-1', + 'neutral-2', + 'neutral-3', + 'neutral-4', 'danger', 'warning', 'success', @@ -881,6 +968,8 @@ export interface FlexProps extends SpaceProps { // (undocumented) align?: Responsive<'start' | 'center' | 'end' | 'baseline' | 'stretch'>; // (undocumented) + bg?: Responsive; + // (undocumented) children?: React.ReactNode; // (undocumented) className?: string; @@ -892,8 +981,6 @@ export interface FlexProps extends SpaceProps { justify?: Responsive<'start' | 'center' | 'end' | 'between'>; // (undocumented) style?: React.CSSProperties; - // (undocumented) - surface?: Responsive; } // @public (undocumented) @@ -931,11 +1018,11 @@ export const GridDefinition: { 'py', ]; readonly dataAttributes: { - readonly surface: readonly [ - '0', - '1', - '2', - '3', + readonly bg: readonly [ + 'neutral-1', + 'neutral-2', + 'neutral-3', + 'neutral-4', 'danger', 'warning', 'success', @@ -950,11 +1037,11 @@ export const GridItemDefinition: { }; readonly utilityProps: ['colSpan', 'colEnd', 'colStart', 'rowSpan']; readonly dataAttributes: { - readonly surface: readonly [ - '0', - '1', - '2', - '3', + readonly bg: readonly [ + 'neutral-1', + 'neutral-2', + 'neutral-3', + 'neutral-4', 'danger', 'warning', 'success', @@ -964,6 +1051,8 @@ export const GridItemDefinition: { // @public (undocumented) export interface GridItemProps { + // (undocumented) + bg?: Responsive; // (undocumented) children?: React.ReactNode; // (undocumented) @@ -978,12 +1067,12 @@ export interface GridItemProps { rowSpan?: Responsive; // (undocumented) style?: React.CSSProperties; - // (undocumented) - surface?: Responsive; } // @public (undocumented) export interface GridProps extends SpaceProps { + // (undocumented) + bg?: Responsive; // (undocumented) children?: React.ReactNode; // (undocumented) @@ -994,8 +1083,6 @@ export interface GridProps extends SpaceProps { gap?: Responsive; // (undocumented) style?: React.CSSProperties; - // (undocumented) - surface?: Responsive; } // @public @@ -1088,12 +1175,6 @@ export type JustifyContent = | 'around' | 'between'; -// @public (undocumented) -export interface LeafSurfaceProps { - // (undocumented) - onSurface?: Responsive; -} - // @public (undocumented) export const Link: ForwardRefExoticComponent< LinkProps & RefAttributes @@ -1655,17 +1736,6 @@ export const SubmenuTrigger: (props: SubmenuTriggerProps) => JSX_2.Element; // @public (undocumented) export interface SubmenuTriggerProps extends SubmenuTriggerProps_2 {} -// @public -export type Surface = - | '0' - | '1' - | '2' - | '3' - | 'danger' - | 'warning' - | 'success' - | 'auto'; - // @public (undocumented) export const Switch: ForwardRefExoticComponent< SwitchProps & RefAttributes @@ -2079,7 +2149,6 @@ export interface ToggleButtonProps extends ToggleButtonProps_2 { iconEnd?: ReactElement; // (undocumented) iconStart?: ReactElement; - onSurface?: Responsive; // (undocumented) size?: 'small' | 'medium' | Partial>; } @@ -2108,6 +2177,18 @@ export const TooltipTrigger: ( props: TooltipTriggerComponentProps, ) => JSX_2.Element; +// @public +export const useBg: (options?: UseBgOptions) => BgContextValue; + +// @public (undocumented) +export type UseBgOptions = + | { + bg: Responsive | undefined; + } + | { + leaf: true; + }; + // @public (undocumented) export const useBreakpoint: () => { breakpoint: Breakpoint; diff --git a/packages/ui/src/components/Alert/Alert.stories.tsx b/packages/ui/src/components/Alert/Alert.stories.tsx index a81337fe71..1cd21434d3 100644 --- a/packages/ui/src/components/Alert/Alert.stories.tsx +++ b/packages/ui/src/components/Alert/Alert.stories.tsx @@ -284,50 +284,46 @@ export const LongContent = meta.story({ ), }); -export const OnDifferentSurfaces = meta.story({ +export const OnDifferentBackgrounds = meta.story({ render: () => ( - Default Surface + Default - - + + - On Surface 0 - - - + On Neutral 1 + + + - On Surface 1 - - - + On Neutral 2 + + + - On Surface 2 - - - + On Neutral 3 + + + - On Surface 3 - - - + On Neutral 4 + + + @@ -349,7 +345,7 @@ export const Responsive = meta.story({ export const WithUtilityProps = meta.story({ render: () => ( - + ()({ spinner: 'bui-AlertSpinner', actions: 'bui-AlertActions', }, - surface: 'container', propDefs: { status: { dataAttribute: true, default: 'info' }, loading: { dataAttribute: true }, @@ -42,7 +41,6 @@ export const AlertDefinition = defineComponent()({ customActions: {}, title: {}, description: {}, - surface: {}, className: {}, style: {}, }, diff --git a/packages/ui/src/components/Alert/types.ts b/packages/ui/src/components/Alert/types.ts index b21fd6aa8c..c0829899f4 100644 --- a/packages/ui/src/components/Alert/types.ts +++ b/packages/ui/src/components/Alert/types.ts @@ -15,14 +15,10 @@ */ import type { ReactElement, ReactNode, CSSProperties } from 'react'; -import type { - ContainerSurfaceProps, - Responsive, - MarginProps, -} from '../../types'; +import type { Responsive, MarginProps } from '../../types'; /** @public */ -export type AlertOwnProps = ContainerSurfaceProps & { +export type AlertOwnProps = { status?: Responsive<'info' | 'success' | 'warning' | 'danger'>; icon?: boolean | ReactElement; loading?: boolean; diff --git a/packages/ui/src/components/Box/Box.module.css b/packages/ui/src/components/Box/Box.module.css index 6a0ebb9fdd..07b0ac2dc1 100644 --- a/packages/ui/src/components/Box/Box.module.css +++ b/packages/ui/src/components/Box/Box.module.css @@ -23,31 +23,31 @@ color: var(--bui-fg-primary); } - .bui-Box[data-surface='0'] { - background-color: var(--bui-bg-neutral-0); - } - - .bui-Box[data-surface='1'] { + .bui-Box[data-bg='neutral-1'] { background-color: var(--bui-bg-neutral-1); } - .bui-Box[data-surface='2'] { + .bui-Box[data-bg='neutral-2'] { background-color: var(--bui-bg-neutral-2); } - .bui-Box[data-surface='3'] { + .bui-Box[data-bg='neutral-3'] { background-color: var(--bui-bg-neutral-3); } - .bui-Box[data-surface='danger'] { + .bui-Box[data-bg='neutral-4'] { + background-color: var(--bui-bg-neutral-4); + } + + .bui-Box[data-bg='danger'] { background-color: var(--bui-bg-danger); } - .bui-Box[data-surface='warning'] { + .bui-Box[data-bg='warning'] { background-color: var(--bui-bg-warning); } - .bui-Box[data-surface='success'] { + .bui-Box[data-bg='success'] { background-color: var(--bui-bg-success); } } diff --git a/packages/ui/src/components/Box/Box.stories.tsx b/packages/ui/src/components/Box/Box.stories.tsx index db0c05a24b..a0504d98d3 100644 --- a/packages/ui/src/components/Box/Box.stories.tsx +++ b/packages/ui/src/components/Box/Box.stories.tsx @@ -346,90 +346,64 @@ export const Display = meta.story({ ), }); -export const Surfaces = meta.story({ +export const BackgroundColors = meta.story({ args: { px: '6', py: '4' }, render: args => ( Default - - Surface 0 + + Neutral 1 - - Surface 1 + + Neutral 2 - - Surface 2 + + Neutral 3 - - Surface 3 + + Responsive Neutral - - Responsive Surface + + Danger - - Surface Danger + + Warning - - Surface Warning - - - Surface Success + + Success ), }); -export const SurfacesNested = meta.story({ +export const NestedNeutralColors = meta.story({ args: { px: '6', py: '4' }, render: args => ( - - - In this test, we are nesting boxes and buttons on different surfaces to - ensure that the correct surface is applied to each element. If a Button - is placed on a surface that doesn't have the surface prop set, it will - inherit the surface from the parent. - - - - - + + +
With bg="neutral-1" — nested boxes and buttons auto-increment
+ + - - - -
-
- ), -}); - -export const SurfacesAutoIncrement = meta.story({ - args: { px: '6', py: '4' }, - render: args => ( - - - Using surface="auto" automatically increments from the parent surface - level. This makes components more reusable as they don't need to know - their absolute surface level. Notice how each nested Box with - surface="auto" automatically increments: 0 → 1 → 2 → 3 (capped at 3). - - - Surface 0 (explicit) - - Surface auto (becomes 1) - - - Surface auto (becomes 2) - - Surface auto (becomes 3) - - Surface auto (stays 3 - capped) - + + + - + + +
Without bg — no context, no auto-increment
+ + + + + + + + + +
), }); diff --git a/packages/ui/src/components/Box/Box.tsx b/packages/ui/src/components/Box/Box.tsx index 23ed5b9303..dc1a8521ea 100644 --- a/packages/ui/src/components/Box/Box.tsx +++ b/packages/ui/src/components/Box/Box.tsx @@ -25,7 +25,7 @@ export const Box = forwardRef((props, ref) => { BoxDefinition, props, ); - const { classes, as, surfaceChildren } = ownProps; + const { classes, as, bgChildren } = ownProps; return createElement( as, @@ -36,7 +36,7 @@ export const Box = forwardRef((props, ref) => { ...dataAttributes, ...restProps, }, - surfaceChildren, + bgChildren, ); }); diff --git a/packages/ui/src/components/Box/definition.ts b/packages/ui/src/components/Box/definition.ts index f202291175..9d9c3b9417 100644 --- a/packages/ui/src/components/Box/definition.ts +++ b/packages/ui/src/components/Box/definition.ts @@ -27,10 +27,10 @@ export const BoxDefinition = defineComponent()({ classNames: { root: 'bui-Box', }, - surface: 'container', + bg: 'container', propDefs: { as: { default: 'div' }, - surface: { dataAttribute: true }, + bg: { dataAttribute: true }, children: {}, className: {}, style: {}, diff --git a/packages/ui/src/components/Box/types.ts b/packages/ui/src/components/Box/types.ts index 09b45429c3..89325cf8f2 100644 --- a/packages/ui/src/components/Box/types.ts +++ b/packages/ui/src/components/Box/types.ts @@ -15,12 +15,12 @@ */ import type { ReactNode, CSSProperties } from 'react'; -import type { Responsive, Surface, SpaceProps } from '../../types'; +import type { Responsive, Bg, SpaceProps } from '../../types'; /** @public */ export type BoxOwnProps = { as?: keyof JSX.IntrinsicElements; - surface?: Responsive; + bg?: Responsive; children?: ReactNode; className?: string; style?: CSSProperties; diff --git a/packages/ui/src/components/Button/Button.module.css b/packages/ui/src/components/Button/Button.module.css index 9b6fbbd09d..847e9ced0d 100644 --- a/packages/ui/src/components/Button/Button.module.css +++ b/packages/ui/src/components/Button/Button.module.css @@ -118,19 +118,19 @@ --bg-active: var(--bui-bg-neutral-1-pressed); --fg: var(--bui-fg-primary); - &[data-on-surface='1'] { + &[data-bg='neutral-2'] { --bg: var(--bui-bg-neutral-2); --bg-hover: var(--bui-bg-neutral-2-hover); --bg-active: var(--bui-bg-neutral-2-pressed); } - &[data-on-surface='2'] { + &[data-bg='neutral-3'] { --bg: var(--bui-bg-neutral-3); --bg-hover: var(--bui-bg-neutral-3-hover); --bg-active: var(--bui-bg-neutral-3-pressed); } - &[data-on-surface='3'] { + &[data-bg='neutral-4'] { --bg: var(--bui-bg-neutral-4); --bg-hover: var(--bui-bg-neutral-4-hover); --bg-active: var(--bui-bg-neutral-4-pressed); @@ -182,17 +182,17 @@ --bg-active: var(--bui-bg-neutral-1-pressed); --fg: var(--bui-fg-primary); - &[data-on-surface='1'] { + &[data-bg='neutral-2'] { --bg-hover: var(--bui-bg-neutral-2-hover); --bg-active: var(--bui-bg-neutral-2-pressed); } - &[data-on-surface='2'] { + &[data-bg='neutral-3'] { --bg-hover: var(--bui-bg-neutral-3-hover); --bg-active: var(--bui-bg-neutral-3-pressed); } - &[data-on-surface='3'] { + &[data-bg='neutral-4'] { --bg-hover: var(--bui-bg-neutral-4-hover); --bg-active: var(--bui-bg-neutral-4-pressed); } diff --git a/packages/ui/src/components/Button/Button.stories.tsx b/packages/ui/src/components/Button/Button.stories.tsx index b5eb885930..f04550ef57 100644 --- a/packages/ui/src/components/Button/Button.stories.tsx +++ b/packages/ui/src/components/Button/Button.stories.tsx @@ -81,32 +81,9 @@ export const Variants = meta.story({
- - Neutral 0 - - - - - - - - - Neutral 1 - + @@ -129,63 +106,72 @@ export const Variants = meta.story({ Neutral 2 - - - - - - - - - - + + + + + + + + Neutral 3 - - - - - - - - - - - - + + + + + + + + + + + Neutral 4 + + + + + + + + ), @@ -246,8 +232,8 @@ export const Destructive = meta.story({
- On Surface 1 - + On Neutral 1 + @@ -462,49 +448,33 @@ export const LoadingVariants = meta.story({ ), }); -export const OnSurfaceAuto = meta.story({ +export const AutoBg = meta.story({ render: () => (
- Using onSurface="auto" on buttons inherits their container's surface - level, making them reusable. This is equivalent to not specifying - onSurface. To override, use explicit surface values like onSurface="0" - or onSurface="2". + Buttons automatically detect their parent bg context and increment the + neutral level by 1. No prop is needed on the button -- it's fully + automatic.
- - Surface 0 container + + Neutral 1 container - - - + + - - Surface 1 container + + Neutral 2 container - - - + + - - Surface 2 container + + Neutral 3 container - - - + +
diff --git a/packages/ui/src/components/Button/definition.ts b/packages/ui/src/components/Button/definition.ts index 65c4e82f53..6b6b49ab53 100644 --- a/packages/ui/src/components/Button/definition.ts +++ b/packages/ui/src/components/Button/definition.ts @@ -29,7 +29,7 @@ export const ButtonDefinition = defineComponent()({ content: 'bui-ButtonContent', spinner: 'bui-ButtonSpinner', }, - surface: 'leaf', + bg: 'leaf', propDefs: { size: { dataAttribute: true, default: 'small' }, variant: { dataAttribute: true, default: 'primary' }, @@ -37,7 +37,6 @@ export const ButtonDefinition = defineComponent()({ loading: { dataAttribute: true }, iconStart: {}, iconEnd: {}, - onSurface: {}, children: {}, className: {}, style: {}, diff --git a/packages/ui/src/components/Button/types.ts b/packages/ui/src/components/Button/types.ts index c50cd5c8fc..a18d9e9164 100644 --- a/packages/ui/src/components/Button/types.ts +++ b/packages/ui/src/components/Button/types.ts @@ -16,10 +16,10 @@ import type { ReactElement, ReactNode, CSSProperties } from 'react'; import type { ButtonProps as RAButtonProps } from 'react-aria-components'; -import type { LeafSurfaceProps, Responsive } from '../../types'; +import type { Responsive } from '../../types'; /** @public */ -export type ButtonOwnProps = LeafSurfaceProps & { +export type ButtonOwnProps = { size?: Responsive<'small' | 'medium'>; variant?: Responsive<'primary' | 'secondary' | 'tertiary'>; destructive?: boolean; diff --git a/packages/ui/src/components/ButtonIcon/ButtonIcon.module.css b/packages/ui/src/components/ButtonIcon/ButtonIcon.module.css index 19a74db3bf..5730efee0d 100644 --- a/packages/ui/src/components/ButtonIcon/ButtonIcon.module.css +++ b/packages/ui/src/components/ButtonIcon/ButtonIcon.module.css @@ -85,19 +85,19 @@ --bg-active: var(--bui-bg-neutral-1-pressed); --fg: var(--bui-fg-primary); - &[data-on-surface='1'] { + &[data-bg='neutral-2'] { --bg: var(--bui-bg-neutral-2); --bg-hover: var(--bui-bg-neutral-2-hover); --bg-active: var(--bui-bg-neutral-2-pressed); } - &[data-on-surface='2'] { + &[data-bg='neutral-3'] { --bg: var(--bui-bg-neutral-3); --bg-hover: var(--bui-bg-neutral-3-hover); --bg-active: var(--bui-bg-neutral-3-pressed); } - &[data-on-surface='3'] { + &[data-bg='neutral-4'] { --bg: var(--bui-bg-neutral-4); --bg-hover: var(--bui-bg-neutral-4-hover); --bg-active: var(--bui-bg-neutral-4-pressed); @@ -122,17 +122,17 @@ --bg-active: var(--bui-bg-neutral-1-pressed); --fg: var(--bui-fg-primary); - &[data-on-surface='1'] { + &[data-bg='neutral-2'] { --bg-hover: var(--bui-bg-neutral-2-hover); --bg-active: var(--bui-bg-neutral-2-pressed); } - &[data-on-surface='2'] { + &[data-bg='neutral-3'] { --bg-hover: var(--bui-bg-neutral-3-hover); --bg-active: var(--bui-bg-neutral-3-pressed); } - &[data-on-surface='3'] { + &[data-bg='neutral-4'] { --bg-hover: var(--bui-bg-neutral-4-hover); --bg-active: var(--bui-bg-neutral-4-pressed); } diff --git a/packages/ui/src/components/ButtonIcon/definition.ts b/packages/ui/src/components/ButtonIcon/definition.ts index 3befeef7c1..03c2bd9387 100644 --- a/packages/ui/src/components/ButtonIcon/definition.ts +++ b/packages/ui/src/components/ButtonIcon/definition.ts @@ -29,13 +29,12 @@ export const ButtonIconDefinition = defineComponent()({ content: 'bui-ButtonIconContent', spinner: 'bui-ButtonIconSpinner', }, - surface: 'leaf', + bg: 'leaf', propDefs: { size: { dataAttribute: true, default: 'small' }, variant: { dataAttribute: true, default: 'primary' }, loading: { dataAttribute: true }, icon: {}, - onSurface: {}, className: {}, style: {}, }, diff --git a/packages/ui/src/components/ButtonIcon/types.ts b/packages/ui/src/components/ButtonIcon/types.ts index 750e0db5e9..92618d5ff6 100644 --- a/packages/ui/src/components/ButtonIcon/types.ts +++ b/packages/ui/src/components/ButtonIcon/types.ts @@ -16,10 +16,10 @@ import type { ReactElement, CSSProperties } from 'react'; import type { ButtonProps as RAButtonProps } from 'react-aria-components'; -import type { LeafSurfaceProps, Responsive } from '../../types'; +import type { Responsive } from '../../types'; /** @public */ -export type ButtonIconOwnProps = LeafSurfaceProps & { +export type ButtonIconOwnProps = { size?: Responsive<'small' | 'medium'>; variant?: Responsive<'primary' | 'secondary' | 'tertiary'>; icon?: ReactElement; diff --git a/packages/ui/src/components/ButtonLink/ButtonLink.module.css b/packages/ui/src/components/ButtonLink/ButtonLink.module.css index 1184a160b7..8713a62a58 100644 --- a/packages/ui/src/components/ButtonLink/ButtonLink.module.css +++ b/packages/ui/src/components/ButtonLink/ButtonLink.module.css @@ -79,19 +79,19 @@ --bg-active: var(--bui-bg-neutral-1-pressed); --fg: var(--bui-fg-primary); - &[data-on-surface='1'] { + &[data-bg='neutral-2'] { --bg: var(--bui-bg-neutral-2); --bg-hover: var(--bui-bg-neutral-2-hover); --bg-active: var(--bui-bg-neutral-2-pressed); } - &[data-on-surface='2'] { + &[data-bg='neutral-3'] { --bg: var(--bui-bg-neutral-3); --bg-hover: var(--bui-bg-neutral-3-hover); --bg-active: var(--bui-bg-neutral-3-pressed); } - &[data-on-surface='3'] { + &[data-bg='neutral-4'] { --bg: var(--bui-bg-neutral-4); --bg-hover: var(--bui-bg-neutral-4-hover); --bg-active: var(--bui-bg-neutral-4-pressed); @@ -115,17 +115,17 @@ --bg-active: var(--bui-bg-neutral-1-pressed); --fg: var(--bui-fg-primary); - &[data-on-surface='1'] { + &[data-bg='neutral-2'] { --bg-hover: var(--bui-bg-neutral-2-hover); --bg-active: var(--bui-bg-neutral-2-pressed); } - &[data-on-surface='2'] { + &[data-bg='neutral-3'] { --bg-hover: var(--bui-bg-neutral-3-hover); --bg-active: var(--bui-bg-neutral-3-pressed); } - &[data-on-surface='3'] { + &[data-bg='neutral-4'] { --bg-hover: var(--bui-bg-neutral-4-hover); --bg-active: var(--bui-bg-neutral-4-pressed); } diff --git a/packages/ui/src/components/ButtonLink/definition.ts b/packages/ui/src/components/ButtonLink/definition.ts index e6a80e09b3..1e3727c60e 100644 --- a/packages/ui/src/components/ButtonLink/definition.ts +++ b/packages/ui/src/components/ButtonLink/definition.ts @@ -28,13 +28,12 @@ export const ButtonLinkDefinition = defineComponent()({ root: 'bui-ButtonLink', content: 'bui-ButtonLinkContent', }, - surface: 'leaf', + bg: 'leaf', propDefs: { size: { dataAttribute: true, default: 'small' }, variant: { dataAttribute: true, default: 'primary' }, iconStart: {}, iconEnd: {}, - onSurface: {}, children: {}, className: {}, style: {}, diff --git a/packages/ui/src/components/ButtonLink/types.ts b/packages/ui/src/components/ButtonLink/types.ts index 0379d72adb..0f01c3cf40 100644 --- a/packages/ui/src/components/ButtonLink/types.ts +++ b/packages/ui/src/components/ButtonLink/types.ts @@ -16,10 +16,10 @@ import type { ReactElement, ReactNode, CSSProperties } from 'react'; import type { LinkProps as RALinkProps } from 'react-aria-components'; -import type { LeafSurfaceProps, Responsive } from '../../types'; +import type { Responsive } from '../../types'; /** @public */ -export type ButtonLinkOwnProps = LeafSurfaceProps & { +export type ButtonLinkOwnProps = { size?: Responsive<'small' | 'medium'>; variant?: Responsive<'primary' | 'secondary' | 'tertiary'>; iconStart?: ReactElement; diff --git a/packages/ui/src/components/Card/Card.module.css b/packages/ui/src/components/Card/Card.module.css index 614aa31bf7..62c4c78cb7 100644 --- a/packages/ui/src/components/Card/Card.module.css +++ b/packages/ui/src/components/Card/Card.module.css @@ -30,31 +30,31 @@ width: 100%; } - .bui-Card[data-surface='0'] { - background-color: var(--bui-bg-surface-0); + .bui-Card[data-bg='neutral-1'] { + background-color: var(--bui-bg-neutral-1); } - .bui-Card[data-surface='1'] { - background-color: var(--bui-bg-surface-1); + .bui-Card[data-bg='neutral-2'] { + background-color: var(--bui-bg-neutral-2); } - .bui-Card[data-surface='2'] { - background-color: var(--bui-bg-surface-2); + .bui-Card[data-bg='neutral-3'] { + background-color: var(--bui-bg-neutral-3); } - .bui-Card[data-surface='3'] { - background-color: var(--bui-bg-surface-3); + .bui-Card[data-bg='neutral-4'] { + background-color: var(--bui-bg-neutral-4); } - .bui-Card[data-surface='danger'] { + .bui-Card[data-bg='danger'] { background-color: var(--bui-bg-danger); } - .bui-Card[data-surface='warning'] { + .bui-Card[data-bg='warning'] { background-color: var(--bui-bg-warning); } - .bui-Card[data-surface='success'] { + .bui-Card[data-bg='success'] { background-color: var(--bui-bg-success); } diff --git a/packages/ui/src/components/Card/Card.stories.tsx b/packages/ui/src/components/Card/Card.stories.tsx index 06ef9a2fa2..b934048622 100644 --- a/packages/ui/src/components/Card/Card.stories.tsx +++ b/packages/ui/src/components/Card/Card.stories.tsx @@ -128,72 +128,71 @@ export const WithListRow = meta.story({ ), }); -export const Surfaces = meta.story({ +export const Backgrounds = meta.story({ render: args => ( Default - No surface prop + No bg prop - - Surface 0 - Explicit surface 0 + + Neutral 1 + Explicit neutral-1 - - Surface 1 - Explicit surface 1 + + Neutral 2 + Explicit neutral-2 - - Surface 2 - Explicit surface 2 + + Neutral 3 + Explicit neutral-3 - - Surface 3 - Explicit surface 3 + + Neutral 4 + Explicit neutral-4 Responsive - Surface 0 → 1 + Neutral 1 → 2 - + Danger - Surface danger + Bg danger - + Warning - Surface warning + Bg warning - + Success - Surface success + Bg success ), }); -export const SurfacesNested = meta.story({ +export const BgNested = meta.story({ render: args => ( - In this test, we are nesting cards on different surfaces to ensure that - the correct surface is applied to each element. If a Button is placed on - a surface that doesn't have the surface prop set, it will inherit the - surface from the parent. + In this test, we are nesting cards to ensure that the correct background + is applied to each element. Buttons automatically inherit the bg context + and increment their neutral level. - - Surface 1 + + Neutral 1 - - Surface 2 + + Neutral 2 - Inherited + Auto-incremented @@ -206,31 +205,31 @@ export const SurfacesNested = meta.story({ ), }); -export const SurfacesAutoIncrement = meta.story({ +export const BgAutoIncrement = meta.story({ render: args => ( - - - On surface 0 - Card auto → 1 + + + On neutral-1 + Card auto → neutral-2 - - - On surface 1 - Card auto → 2 + + + On neutral-2 + Card auto → neutral-3 - - - On surface 2 - Card auto → 3 + + + On neutral-3 + Card auto → neutral-4 - - - On surface 3 - Card auto → 3 (capped) + + + On neutral-4 + Card auto → neutral-4 (capped) diff --git a/packages/ui/src/components/Card/Card.tsx b/packages/ui/src/components/Card/Card.tsx index b9ff308784..4e030fcc4c 100644 --- a/packages/ui/src/components/Card/Card.tsx +++ b/packages/ui/src/components/Card/Card.tsx @@ -39,11 +39,11 @@ export const Card = forwardRef((props, ref) => { CardDefinition, props, ); - const { classes, surfaceChildren } = ownProps; + const { classes, bgChildren } = ownProps; return (
- {surfaceChildren} + {bgChildren}
); }); diff --git a/packages/ui/src/components/Card/definition.ts b/packages/ui/src/components/Card/definition.ts index ec05494391..73dc1b02af 100644 --- a/packages/ui/src/components/Card/definition.ts +++ b/packages/ui/src/components/Card/definition.ts @@ -32,9 +32,9 @@ export const CardDefinition = defineComponent()({ classNames: { root: 'bui-Card', }, - surface: 'container', + bg: 'container', propDefs: { - surface: { dataAttribute: true, default: '1' }, + bg: { dataAttribute: true }, children: {}, className: {}, }, diff --git a/packages/ui/src/components/Card/types.ts b/packages/ui/src/components/Card/types.ts index 50a94683e2..fae864e621 100644 --- a/packages/ui/src/components/Card/types.ts +++ b/packages/ui/src/components/Card/types.ts @@ -15,11 +15,11 @@ */ import type { ReactNode } from 'react'; -import type { Responsive, Surface } from '../../types'; +import type { Responsive, Bg } from '../../types'; /** @public */ export type CardOwnProps = { - surface?: Responsive; + bg?: Responsive; children?: ReactNode; className?: string; }; diff --git a/packages/ui/src/components/Flex/Flex.module.css b/packages/ui/src/components/Flex/Flex.module.css index 5923fa160e..01f9c29dbc 100644 --- a/packages/ui/src/components/Flex/Flex.module.css +++ b/packages/ui/src/components/Flex/Flex.module.css @@ -24,31 +24,31 @@ min-width: 0; } - .bui-Flex[data-surface='0'] { - background-color: var(--bui-bg-neutral-0); - } - - .bui-Flex[data-surface='1'] { + .bui-Flex[data-bg='neutral-1'] { background-color: var(--bui-bg-neutral-1); } - .bui-Flex[data-surface='2'] { + .bui-Flex[data-bg='neutral-2'] { background-color: var(--bui-bg-neutral-2); } - .bui-Flex[data-surface='3'] { + .bui-Flex[data-bg='neutral-3'] { background-color: var(--bui-bg-neutral-3); } - .bui-Flex[data-surface='danger'] { + .bui-Flex[data-bg='neutral-4'] { + background-color: var(--bui-bg-neutral-4); + } + + .bui-Flex[data-bg='danger'] { background-color: var(--bui-bg-danger); } - .bui-Flex[data-surface='warning'] { + .bui-Flex[data-bg='warning'] { background-color: var(--bui-bg-warning); } - .bui-Flex[data-surface='success'] { + .bui-Flex[data-bg='success'] { background-color: var(--bui-bg-success); } } diff --git a/packages/ui/src/components/Flex/Flex.stories.tsx b/packages/ui/src/components/Flex/Flex.stories.tsx index 998d97bc0a..5bc7258312 100644 --- a/packages/ui/src/components/Flex/Flex.stories.tsx +++ b/packages/ui/src/components/Flex/Flex.stories.tsx @@ -244,7 +244,7 @@ export const WithTextTruncate = meta.story({ ), }); -export const Surfaces = meta.story({ +export const Backgrounds = meta.story({ args: { px: '6', py: '4', @@ -252,52 +252,52 @@ export const Surfaces = meta.story({ render: args => ( Default - - Surface 0 + + Neutral 1 - - Surface 1 + + Neutral 2 - - Surface 2 + + Neutral 3 - - Surface 3 + + Neutral 4 - - Responsive Surface + + Responsive Bg - - Surface Danger + + Danger - - Surface Warning + + Warning - - Surface Success + + Success ), }); -export const SurfacesAutoIncrement = meta.story({ +export const BgAutoIncrement = meta.story({ args: { px: '6', py: '4', gap: '4' }, render: args => (
- Using surface="auto" automatically increments from the parent surface. - This allows components to be reusable without hardcoding surface levels. + Nested Flex components automatically increment their neutral background + level. No explicit bg prop is needed on inner Flex components.
- -
Surface 0 (explicit)
- -
Surface auto (becomes 1)
- -
Surface auto (becomes 2)
- -
Surface auto (becomes 3)
- -
Surface auto (stays 3 - capped)
+ +
Neutral 1 (explicit)
+ +
Auto (becomes neutral-2)
+ +
Auto (becomes neutral-3)
+ +
Auto (becomes neutral-4)
+ +
Auto (stays neutral-4 - capped)
diff --git a/packages/ui/src/components/Flex/Flex.tsx b/packages/ui/src/components/Flex/Flex.tsx index 3d29408b2b..1af1050811 100644 --- a/packages/ui/src/components/Flex/Flex.tsx +++ b/packages/ui/src/components/Flex/Flex.tsx @@ -20,24 +20,21 @@ import clsx from 'clsx'; import { useStyles } from '../../hooks/useStyles'; import { FlexDefinition } from './definition'; import styles from './Flex.module.css'; -import { SurfaceProvider, useSurface } from '../../hooks/useSurface'; +import { BgProvider, useBg } from '../../hooks/useBg'; /** @public */ export const Flex = forwardRef((props, ref) => { - // Resolve the surface this Flex creates for its children - // Using 'surface' parameter = container behavior (auto increments) - const { surface: resolvedSurface } = useSurface({ - surface: props.surface, - }); + // Resolve the bg this Flex creates for its children + const { bg: resolvedBg } = useBg({ bg: props.bg }); const { classNames, dataAttributes, utilityClasses, style, cleanedProps } = useStyles(FlexDefinition, { gap: '4', ...props, - surface: resolvedSurface, // Use resolved surface for data attribute + bg: resolvedBg, // Use resolved bg for data attribute }); - const { className, surface, ...rest } = cleanedProps; + const { className, bg, ...rest } = cleanedProps; const content = (
((props, ref) => { /> ); - return resolvedSurface ? ( - {content} + return resolvedBg ? ( + {content} ) : ( content ); diff --git a/packages/ui/src/components/Flex/definition.ts b/packages/ui/src/components/Flex/definition.ts index 3f747e153b..12772e107e 100644 --- a/packages/ui/src/components/Flex/definition.ts +++ b/packages/ui/src/components/Flex/definition.ts @@ -45,6 +45,14 @@ export const FlexDefinition = { 'direction', ], dataAttributes: { - surface: ['0', '1', '2', '3', 'danger', 'warning', 'success'] as const, + bg: [ + 'neutral-1', + 'neutral-2', + 'neutral-3', + 'neutral-4', + 'danger', + 'warning', + 'success', + ] as const, }, } as const satisfies ComponentDefinition; diff --git a/packages/ui/src/components/Flex/types.ts b/packages/ui/src/components/Flex/types.ts index 33bc476f93..a3503dc79c 100644 --- a/packages/ui/src/components/Flex/types.ts +++ b/packages/ui/src/components/Flex/types.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import type { Responsive, Space, SpaceProps, Surface } from '../../types'; +import type { Responsive, Space, SpaceProps, Bg } from '../../types'; /** @public */ export interface FlexProps extends SpaceProps { @@ -25,5 +25,5 @@ export interface FlexProps extends SpaceProps { direction?: Responsive<'row' | 'column' | 'row-reverse' | 'column-reverse'>; className?: string; style?: React.CSSProperties; - surface?: Responsive; + bg?: Responsive; } diff --git a/packages/ui/src/components/Grid/Grid.module.css b/packages/ui/src/components/Grid/Grid.module.css index aa281b8695..e8a7248bad 100644 --- a/packages/ui/src/components/Grid/Grid.module.css +++ b/packages/ui/src/components/Grid/Grid.module.css @@ -21,38 +21,38 @@ display: grid; } - .bui-Grid[data-surface='0'], - .bui-GridItem[data-surface='0'] { - background-color: var(--bui-bg-neutral-0); - } - - .bui-Grid[data-surface='1'], - .bui-GridItem[data-surface='1'] { + .bui-Grid[data-bg='neutral-1'], + .bui-GridItem[data-bg='neutral-1'] { background-color: var(--bui-bg-neutral-1); } - .bui-Grid[data-surface='2'], - .bui-GridItem[data-surface='2'] { + .bui-Grid[data-bg='neutral-2'], + .bui-GridItem[data-bg='neutral-2'] { background-color: var(--bui-bg-neutral-2); } - .bui-Grid[data-surface='3'], - .bui-GridItem[data-surface='3'] { + .bui-Grid[data-bg='neutral-3'], + .bui-GridItem[data-bg='neutral-3'] { background-color: var(--bui-bg-neutral-3); } - .bui-Grid[data-surface='danger'], - .bui-GridItem[data-surface='danger'] { + .bui-Grid[data-bg='neutral-4'], + .bui-GridItem[data-bg='neutral-4'] { + background-color: var(--bui-bg-neutral-4); + } + + .bui-Grid[data-bg='danger'], + .bui-GridItem[data-bg='danger'] { background-color: var(--bui-bg-danger); } - .bui-Grid[data-surface='warning'], - .bui-GridItem[data-surface='warning'] { + .bui-Grid[data-bg='warning'], + .bui-GridItem[data-bg='warning'] { background-color: var(--bui-bg-warning); } - .bui-Grid[data-surface='success'], - .bui-GridItem[data-surface='success'] { + .bui-Grid[data-bg='success'], + .bui-GridItem[data-bg='success'] { background-color: var(--bui-bg-success); } } diff --git a/packages/ui/src/components/Grid/Grid.stories.tsx b/packages/ui/src/components/Grid/Grid.stories.tsx index b3524444fd..80c4327dce 100644 --- a/packages/ui/src/components/Grid/Grid.stories.tsx +++ b/packages/ui/src/components/Grid/Grid.stories.tsx @@ -106,78 +106,78 @@ export const RowAndColumns = meta.story({ ), }); -export const Surfaces = meta.story({ +export const Backgrounds = meta.story({ args: { px: '6', py: '4' }, render: args => ( - - Surface 0 + + Neutral 1 - - Surface 1 + + Neutral 2 - - Surface 2 + + Neutral 3 - - Surface 3 + + Neutral 4 - - Responsive Surface + + Responsive Bg - - Surface Danger + + Danger - - Surface Warning + + Warning - - Surface Success + + Success - - Surface 0 + + Neutral 1 - - Surface 1 + + Neutral 2 - - Surface 2 + + Neutral 3 - - Surface 3 + + Neutral 4 - Responsive Surface + Responsive Bg - - Surface Danger + + Danger - - Surface Warning + + Warning - - Surface Success + + Success @@ -185,23 +185,20 @@ export const Surfaces = meta.story({ ), }); -export const SurfacesAutoIncrement = meta.story({ +export const BgAutoIncrement = meta.story({ args: { px: '6', py: '4', columns: '2', gap: '4' }, render: args => (
- Using surface="auto" automatically increments from the parent surface. - Each Grid.Item with auto will be one level above its Grid.Root parent. + Nested Grid components automatically increment their neutral background. + Each nested Grid.Item inherits and increments from its parent's bg.
- - Surface 0 (Grid.Root) - Surface auto (becomes 1) + + Neutral 1 (Grid.Root) - - Nested: Surface auto (becomes 1) - - Nested: Surface auto (becomes 2) - + + Nested: Auto (becomes neutral-2) + Nested: Auto (becomes neutral-2) diff --git a/packages/ui/src/components/Grid/Grid.tsx b/packages/ui/src/components/Grid/Grid.tsx index 52dd22167e..45eb9c76d2 100644 --- a/packages/ui/src/components/Grid/Grid.tsx +++ b/packages/ui/src/components/Grid/Grid.tsx @@ -20,24 +20,21 @@ import type { GridItemProps, GridProps } from './types'; import { useStyles } from '../../hooks/useStyles'; import { GridDefinition, GridItemDefinition } from './definition'; import styles from './Grid.module.css'; -import { SurfaceProvider, useSurface } from '../../hooks/useSurface'; +import { BgProvider, useBg } from '../../hooks/useBg'; const GridRoot = forwardRef((props, ref) => { - // Resolve the surface this Grid creates for its children - // Using 'surface' parameter = container behavior (auto increments) - const { surface: resolvedSurface } = useSurface({ - surface: props.surface, - }); + // Resolve the bg this Grid creates for its children + const { bg: resolvedBg } = useBg({ bg: props.bg }); const { classNames, dataAttributes, utilityClasses, style, cleanedProps } = useStyles(GridDefinition, { columns: 'auto', gap: '4', ...props, - surface: resolvedSurface, // Use resolved surface for data attribute + bg: resolvedBg, // Use resolved bg for data attribute }); - const { className, surface, ...rest } = cleanedProps; + const { className, bg, ...rest } = cleanedProps; const content = (
((props, ref) => { /> ); - return resolvedSurface ? ( - {content} + return resolvedBg ? ( + {content} ) : ( content ); }); const GridItem = forwardRef((props, ref) => { - // Resolve the surface this GridItem creates for its children - // Using 'surface' parameter = container behavior (auto increments) - const { surface: resolvedSurface } = useSurface({ - surface: props.surface, - }); + // Resolve the bg this GridItem creates for its children + const { bg: resolvedBg } = useBg({ bg: props.bg }); const { classNames, dataAttributes, utilityClasses, style, cleanedProps } = useStyles(GridItemDefinition, { ...props, - surface: resolvedSurface, // Use resolved surface for data attribute + bg: resolvedBg, // Use resolved bg for data attribute }); - const { className, surface, ...rest } = cleanedProps; + const { className, bg, ...rest } = cleanedProps; const content = (
((props, ref) => { /> ); - return resolvedSurface ? ( - {content} + return resolvedBg ? ( + {content} ) : ( content ); diff --git a/packages/ui/src/components/Grid/definition.ts b/packages/ui/src/components/Grid/definition.ts index a6c5c582c7..cf2e14c58f 100644 --- a/packages/ui/src/components/Grid/definition.ts +++ b/packages/ui/src/components/Grid/definition.ts @@ -43,7 +43,15 @@ export const GridDefinition = { 'py', ], dataAttributes: { - surface: ['0', '1', '2', '3', 'danger', 'warning', 'success'] as const, + bg: [ + 'neutral-1', + 'neutral-2', + 'neutral-3', + 'neutral-4', + 'danger', + 'warning', + 'success', + ] as const, }, } as const satisfies ComponentDefinition; @@ -57,6 +65,14 @@ export const GridItemDefinition = { }, utilityProps: ['colSpan', 'colEnd', 'colStart', 'rowSpan'], dataAttributes: { - surface: ['0', '1', '2', '3', 'danger', 'warning', 'success'] as const, + bg: [ + 'neutral-1', + 'neutral-2', + 'neutral-3', + 'neutral-4', + 'danger', + 'warning', + 'success', + ] as const, }, } as const satisfies ComponentDefinition; diff --git a/packages/ui/src/components/Grid/types.ts b/packages/ui/src/components/Grid/types.ts index f5ce9ca9cb..cd782b88e4 100644 --- a/packages/ui/src/components/Grid/types.ts +++ b/packages/ui/src/components/Grid/types.ts @@ -14,13 +14,7 @@ * limitations under the License. */ -import type { - Space, - SpaceProps, - Responsive, - Columns, - Surface, -} from '../../types'; +import type { Space, SpaceProps, Responsive, Columns, Bg } from '../../types'; /** @public */ export interface GridProps extends SpaceProps { @@ -29,7 +23,7 @@ export interface GridProps extends SpaceProps { columns?: Responsive; gap?: Responsive; style?: React.CSSProperties; - surface?: Responsive; + bg?: Responsive; } /** @public */ @@ -41,5 +35,5 @@ export interface GridItemProps { colStart?: Responsive; rowSpan?: Responsive; style?: React.CSSProperties; - surface?: Responsive; + bg?: Responsive; } diff --git a/packages/ui/src/components/ToggleButton/ToggleButton.stories.tsx b/packages/ui/src/components/ToggleButton/ToggleButton.stories.tsx index 14183e6ede..1a40dc626a 100644 --- a/packages/ui/src/components/ToggleButton/ToggleButton.stories.tsx +++ b/packages/ui/src/components/ToggleButton/ToggleButton.stories.tsx @@ -44,7 +44,7 @@ export const Default = meta.story({ }, }); -export const Surfaces = meta.story({ +export const Backgrounds = meta.story({ args: { children: 'Toggle', }, @@ -64,26 +64,26 @@ export const Surfaces = meta.story({ - On Surface 0 - + On Neutral 1 + Toggle - On Surface 1 - + On Neutral 2 + Toggle - On Surface 2 - + On Neutral 3 + Toggle - On Surface 3 - + On Neutral 4 + Toggle diff --git a/packages/ui/src/components/ToggleButton/ToggleButton.tsx b/packages/ui/src/components/ToggleButton/ToggleButton.tsx index 9c96304a80..a497d97886 100644 --- a/packages/ui/src/components/ToggleButton/ToggleButton.tsx +++ b/packages/ui/src/components/ToggleButton/ToggleButton.tsx @@ -21,7 +21,7 @@ import type { ToggleButtonProps } from './types'; import { useStyles } from '../../hooks/useStyles'; import { ToggleButtonDefinition } from './definition'; import styles from './ToggleButton.module.css'; -import { useSurface } from '../../hooks/useSurface'; +import { useBg } from '../../hooks/useBg'; /** @public */ export const ToggleButton = forwardRef( @@ -34,17 +34,16 @@ export const ToggleButton = forwardRef( }, ); - const { children, className, iconStart, iconEnd, onSurface, ...rest } = - cleanedProps; + const { children, className, iconStart, iconEnd, ...rest } = cleanedProps; - const { surface } = useSurface({ onSurface }); + const { bg } = useBg({ leaf: true }); return ( {renderProps => { diff --git a/packages/ui/src/components/ToggleButton/types.ts b/packages/ui/src/components/ToggleButton/types.ts index de70bd3c29..2e5c5755f2 100644 --- a/packages/ui/src/components/ToggleButton/types.ts +++ b/packages/ui/src/components/ToggleButton/types.ts @@ -17,7 +17,6 @@ import type { Breakpoint } from '../..'; import type { ReactElement } from 'react'; import type { ToggleButtonProps as AriaToggleButtonProps } from 'react-aria-components'; -import type { Responsive, Surface } from '../../types'; /** * Properties for {@link ToggleButton} @@ -28,6 +27,4 @@ export interface ToggleButtonProps extends AriaToggleButtonProps { size?: 'small' | 'medium' | Partial>; iconStart?: ReactElement; iconEnd?: ReactElement; - /** Surface the toggle button is placed on. Defaults to context surface if available */ - onSurface?: Responsive; } diff --git a/packages/ui/src/components/ToggleButtonGroup/ToggleButtonGroup.stories.tsx b/packages/ui/src/components/ToggleButtonGroup/ToggleButtonGroup.stories.tsx index 30e53f0669..458d57683a 100644 --- a/packages/ui/src/components/ToggleButtonGroup/ToggleButtonGroup.stories.tsx +++ b/packages/ui/src/components/ToggleButtonGroup/ToggleButtonGroup.stories.tsx @@ -71,7 +71,7 @@ export const MultipleSelection = meta.story({ ), }); -export const Surfaces = meta.story({ +export const Backgrounds = meta.story({ args: { selectionMode: 'single', defaultSelectedKeys: ['option1'], @@ -99,8 +99,8 @@ export const Surfaces = meta.story({ - On Surface 0 - + On Neutral 1 + - On Surface 1 - + On Neutral 2 + - On Surface 2 - + On Neutral 3 + - On Surface 3 - + On Neutral 4 + | undefined; +} + +/** @public */ +export interface BgProviderProps { + bg: Responsive; + children: ReactNode; +} + +/** @public */ +export type UseBgOptions = + | { + /** + * Container mode: the explicit bg value from the component's prop. + * If undefined, the container auto-increments from parent context. + */ + bg: Responsive | undefined; + } + | { + /** + * Leaf mode: automatically reads bg from context and increments by 1. + * No prop is needed on the component. + */ + leaf: true; + }; + +const BgContext = createVersionedContext<{ + 1: BgContextValue; +}>('bg-context'); + +/** + * Increments a neutral bg level by one, capping at 'neutral-4'. + * Intent backgrounds (danger, warning, success) pass through unchanged. + * + * @param bg - The current bg value + * @returns The incremented bg value + * @internal + */ +function incrementNeutralBg(bg: Bg | undefined): Bg | undefined { + if (!bg) return undefined; + if (bg === 'neutral-1') return 'neutral-2'; + if (bg === 'neutral-2') return 'neutral-3'; + if (bg === 'neutral-3') return 'neutral-4'; + if (bg === 'neutral-4') return 'neutral-4'; // capped + // Intent values pass through unchanged + return bg; +} + +/** + * Resolves the bg value 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). + * + * @internal + */ +function resolveBgForContainer( + contextBg: Responsive | undefined, + propBg: Responsive | undefined, +): Responsive | undefined { + // Explicit bg prop takes priority + if (propBg !== undefined) { + return propBg; + } + + // No explicit bg: auto-increment from context if available + if (contextBg === undefined) { + return undefined; + } + + // If context is a responsive object, we can't auto-increment + if (typeof contextBg === 'object') { + return undefined; + } + + return incrementNeutralBg(contextBg); +} + +/** + * Resolves the bg value for a leaf component. + * + * Always auto-increments from parent context. If no context, returns undefined. + * + * @internal + */ +function resolveBgForLeaf( + contextBg: Responsive | undefined, +): Responsive | undefined { + if (contextBg === undefined) { + return undefined; + } + + // If context is a responsive object, we can't auto-increment + if (typeof contextBg === 'object') { + return undefined; + } + + return incrementNeutralBg(contextBg); +} + +/** + * Provider component that establishes the bg context for child components. + * + * @public + */ +export const BgProvider = ({ bg, children }: BgProviderProps) => { + return ( + + {children} + + ); +}; + +/** + * Hook to access and resolve the current bg context. + * + * Supports two modes: + * - **Container mode** (`{ bg }`) - for components like Box that establish bg context. + * If bg prop is provided, uses that value. If bg is undefined but parent context exists, + * auto-increments from parent. + * - **Leaf mode** (`{ leaf: true }`) - for components like Button that consume bg context. + * Always auto-increments from parent context. No prop needed. + * + * @param options - Configuration for bg resolution + * @public + */ +export const useBg = (options?: UseBgOptions): BgContextValue => { + const value = useContext(BgContext)?.atVersion(1); + const context = value ?? { bg: undefined }; + + if (!options) { + return context; + } + + if ('leaf' in options) { + return { bg: resolveBgForLeaf(context.bg) }; + } + + return { bg: resolveBgForContainer(context.bg, options.bg) }; +}; diff --git a/packages/ui/src/hooks/useDefinition/defineComponent.ts b/packages/ui/src/hooks/useDefinition/defineComponent.ts index 5e2a0139a7..6dbfb5742b 100644 --- a/packages/ui/src/hooks/useDefinition/defineComponent.ts +++ b/packages/ui/src/hooks/useDefinition/defineComponent.ts @@ -14,13 +14,13 @@ * limitations under the License. */ -import type { ComponentConfig, SurfacePropsConstraint } from './types'; +import type { ComponentConfig, BgPropsConstraint } from './types'; export function defineComponent

>() { return < const S extends Record, const C extends ComponentConfig, >( - config: C & SurfacePropsConstraint, + config: C & BgPropsConstraint, ): C => config; } diff --git a/packages/ui/src/hooks/useDefinition/index.ts b/packages/ui/src/hooks/useDefinition/index.ts index a180645975..533b7f8e21 100644 --- a/packages/ui/src/hooks/useDefinition/index.ts +++ b/packages/ui/src/hooks/useDefinition/index.ts @@ -21,4 +21,5 @@ export type { PropDefConfig, UseDefinitionOptions, UseDefinitionResult, + BgPropsConstraint, } from './types'; diff --git a/packages/ui/src/hooks/useDefinition/types.ts b/packages/ui/src/hooks/useDefinition/types.ts index 3cef56148e..1249e8e3db 100644 --- a/packages/ui/src/hooks/useDefinition/types.ts +++ b/packages/ui/src/hooks/useDefinition/types.ts @@ -36,25 +36,19 @@ export interface ComponentConfig< propDefs: { [K in keyof P]: PropDefConfig }; // readonly for compatibility with const inference from factory utilityProps?: readonly UtilityPropKey[]; - surface?: 'container' | 'leaf'; + bg?: 'container' | 'leaf'; } /** - * Type constraint that validates surface props are present in the props type. - * - If surface is 'leaf', P must include 'onSurface' - * - If surface is 'container', P must include 'surface' + * Type constraint that validates bg props are present in the props type. + * - If bg is 'container', P must include 'bg' + * - If bg is 'leaf', no prop constraint (fully automatic) */ -export type SurfacePropsConstraint = Surface extends 'leaf' - ? 'onSurface' extends keyof P +export type BgPropsConstraint = BgMode extends 'container' + ? 'bg' extends keyof P ? {} : { - __error: 'Leaf components must include onSurface in props type. Extend LeafProps.'; - } - : Surface extends 'container' - ? 'surface' extends keyof P - ? {} - : { - __error: 'Container components must include surface in props type. Extend ContainerProps.'; + __error: 'Bg container components must include bg in props type.'; } : {}; @@ -81,12 +75,11 @@ type ResolvedOwnProps< [K in keyof PropDefs & keyof P]: ResolvePropType; }; -type ChildrenProps = - Surface extends 'container' - ? { surfaceChildren: ReactNode; children?: never } - : Surface extends 'leaf' - ? { children: ReactNode; surfaceChildren?: never } - : { children: ReactNode }; +type ChildrenProps< + BgMode extends 'container' | 'leaf' | undefined = undefined, +> = BgMode extends 'container' + ? { bgChildren: ReactNode; children?: never } + : { children: ReactNode; bgChildren?: never }; type DataAttributeKeys = { [K in keyof PropDefs]: PropDefs[K] extends { dataAttribute: true } @@ -98,7 +91,7 @@ type DataAttributes = { [K in DataAttributeKeys as `data-${Lowercase< string & K >}`]?: string; -} & { 'data-on-surface'?: string }; +} & { 'data-bg'?: string }; export type UtilityKeys> = D['utilityProps'] extends ReadonlyArray ? K : never; @@ -127,7 +120,7 @@ export interface UseDefinitionResult< ownProps: { classes: Record; } & ResolvedOwnProps & - ChildrenProps; + ChildrenProps; // Rest props excludes both propDefs keys AND utility prop keys restProps: keyof Omit> extends never diff --git a/packages/ui/src/hooks/useDefinition/useDefinition.tsx b/packages/ui/src/hooks/useDefinition/useDefinition.tsx index c8bd3853dd..9b60feab07 100644 --- a/packages/ui/src/hooks/useDefinition/useDefinition.tsx +++ b/packages/ui/src/hooks/useDefinition/useDefinition.tsx @@ -17,7 +17,7 @@ import { ReactNode } from 'react'; import clsx from 'clsx'; import { useBreakpoint } from '../useBreakpoint'; -import { useSurface, SurfaceProvider, UseSurfaceOptions } from '../useSurface'; +import { useBg, BgProvider, UseBgOptions } from '../useBg'; import { resolveResponsiveValue, processUtilityProps } from './helpers'; import type { ComponentConfig, @@ -36,14 +36,14 @@ export function useDefinition< ): UseDefinitionResult { const { breakpoint } = useBreakpoint(); - const surfaceOptions: UseSurfaceOptions | undefined = - definition.surface === 'container' - ? { surface: props.surface } - : definition.surface === 'leaf' - ? { onSurface: props.onSurface } + const bgOptions: UseBgOptions | undefined = + definition.bg === 'container' + ? { bg: props.bg } + : definition.bg === 'leaf' + ? { leaf: true } : undefined; - const { surface: resolvedSurface } = useSurface(surfaceOptions); + const { bg: resolvedBg } = useBg(bgOptions); const ownPropKeys = new Set(Object.keys(definition.propDefs)); const utilityPropKeys = new Set(definition.utilityProps ?? []); @@ -77,31 +77,26 @@ export function useDefinition< } } - // Override data-surface for container components with the resolved value - // This ensures 'auto' is replaced with the actual computed surface level - if ( - definition.surface === 'container' && - resolvedSurface !== undefined && - dataAttributes['data-surface'] !== undefined - ) { - const surfaceValue = - typeof resolvedSurface === 'object' - ? resolveResponsiveValue(resolvedSurface as any, breakpoint) - : resolvedSurface; - if (surfaceValue !== undefined) { - dataAttributes['data-surface'] = String(surfaceValue); + // Set data-bg for bg container components with the resolved value + // This covers both explicit bg props and auto-incremented values + if (definition.bg === 'container' && resolvedBg !== undefined) { + const bgValue = + typeof resolvedBg === 'object' + ? resolveResponsiveValue(resolvedBg as any, breakpoint) + : resolvedBg; + if (bgValue !== undefined) { + dataAttributes['data-bg'] = String(bgValue); } } - // Add data-on-surface for leaf components - if (definition.surface === 'leaf' && resolvedSurface !== undefined) { - // Handle responsive surface values - for data attributes, use the resolved string - const surfaceValue = - typeof resolvedSurface === 'object' - ? resolveResponsiveValue(resolvedSurface as any, breakpoint) - : resolvedSurface; - if (surfaceValue !== undefined) { - dataAttributes['data-on-surface'] = String(surfaceValue); + // Add data-bg for bg leaf components (auto-incremented from context) + if (definition.bg === 'leaf' && resolvedBg !== undefined) { + const bgValue = + typeof resolvedBg === 'object' + ? resolveResponsiveValue(resolvedBg as any, breakpoint) + : resolvedBg; + if (bgValue !== undefined) { + dataAttributes['data-bg'] = String(bgValue); } } @@ -125,13 +120,11 @@ export function useDefinition< } let children: ReactNode | undefined; - let surfaceChildren: ReactNode | undefined; + let bgChildren: ReactNode | undefined; - if (definition.surface === 'container') { - surfaceChildren = resolvedSurface ? ( - - {props.children} - + if (definition.bg === 'container') { + bgChildren = resolvedBg ? ( + {props.children} ) : ( props.children ); @@ -143,9 +136,7 @@ export function useDefinition< ownProps: { classes, ...ownPropsResolved, - ...(definition.surface === 'container' - ? { surfaceChildren } - : { children }), + ...(definition.bg === 'container' ? { bgChildren } : { children }), }, restProps, dataAttributes, diff --git a/packages/ui/src/hooks/useSurface.tsx b/packages/ui/src/hooks/useSurface.tsx deleted file mode 100644 index a088fb3b63..0000000000 --- a/packages/ui/src/hooks/useSurface.tsx +++ /dev/null @@ -1,204 +0,0 @@ -/* - * Copyright 2025 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { useContext, ReactNode } from 'react'; -import { - createVersionedContext, - createVersionedValueMap, -} from '@backstage/version-bridge'; -import { Surface, Responsive } from '../types'; - -/** @public */ -export interface SurfaceContextValue { - surface: Responsive | undefined; -} - -/** @public */ -export interface SurfaceProviderProps { - surface: Responsive; - children: ReactNode; -} - -/** @public */ -export interface UseSurfaceOptions { - /** - * The surface value this component CREATES for its children (container behavior). - * When 'auto', increments from parent surface. - * - * Use this for components like Box, Flex, Grid that establish surface context. - */ - surface?: Responsive; - /** - * The surface value this component is ON for styling (leaf behavior). - * When 'auto', inherits from current surface. - * - * Use this for leaf components like Button that consume surface for styling. - */ - onSurface?: Responsive; -} - -const SurfaceContext = createVersionedContext<{ - 1: SurfaceContextValue; -}>('surface-context'); - -/** - * Increments a surface level by one, capping at '3'. - * Intent surfaces (danger, warning, success) remain unchanged. - * - * @internal - */ -function incrementSurface(surface: Surface | undefined): Surface { - if (!surface) return '0'; // no context = root level - if (surface === '0') return '1'; - if (surface === '1') return '2'; - if (surface === '2' || surface === '3') return '3'; // cap at max - // Intent surfaces remain unchanged - if (surface === 'danger') return 'danger'; - if (surface === 'warning') return 'warning'; - if (surface === 'success') return 'success'; - // 'auto' should not appear here, but handle it defensively - if (surface === 'auto') return '1'; - return surface; -} - -/** - * Resolves a surface value for containers (SurfaceProvider). - * When 'auto' is used, increments from the parent surface. - * For responsive surfaces (objects), returns them as-is without resolution. - * - * @param contextSurface - The surface from context - * @param requestedSurface - The requested surface value (may be 'auto') - * @returns The resolved surface value - * @internal - */ -export function resolveSurfaceForProvider( - contextSurface: Responsive | undefined, - requestedSurface: Responsive | undefined, -): Responsive | undefined { - if (!requestedSurface) { - return contextSurface; - } - - // If requestedSurface is a responsive object (breakpoint-based), return as-is - if (typeof requestedSurface === 'object') { - return requestedSurface; - } - - // If contextSurface is a responsive object, we can't auto-increment from it - // Return the requested surface as-is or default to '0' for auto - if (typeof contextSurface === 'object') { - if (requestedSurface === 'auto') { - return '0'; // fallback to root when context is responsive - } - return requestedSurface; - } - - // For containers, 'auto' means increment to create a new elevated context - if (requestedSurface === 'auto') { - return incrementSurface(contextSurface); - } - - return requestedSurface; -} - -/** - * Resolves a surface value for leaf components (useSurface hook). - * When 'auto' is used, inherits the current surface (doesn't increment). - * For responsive surfaces (objects), returns them as-is without resolution. - * - * @param contextSurface - The surface from context - * @param requestedSurface - The requested surface value (may be 'auto') - * @returns The resolved surface value - * @internal - */ -function resolveSurfaceForConsumer( - contextSurface: Responsive | undefined, - requestedSurface: Responsive | undefined, -): Responsive | undefined { - if (!requestedSurface) { - return contextSurface; - } - - // If requestedSurface is a responsive object (breakpoint-based), return as-is - if (typeof requestedSurface === 'object') { - return requestedSurface; - } - - // For leaf components, 'auto' means inherit the current surface - if (requestedSurface === 'auto') { - // If context is responsive, fallback to '0' - if (typeof contextSurface === 'object') { - return '0'; - } - return contextSurface; - } - - return requestedSurface; -} - -/** - * Provider component that establishes the surface context for child components. - * This allows components to adapt their styling based on their background surface. - * - * Note: The surface value should already be resolved before passing to this provider. - * Container components should use useSurface with the surface parameter. - * - * @internal - */ -export const SurfaceProvider = ({ - surface, - children, -}: SurfaceProviderProps) => { - return ( - - {children} - - ); -}; - -/** - * Hook to access the current surface context. - * Returns the current surface level, or undefined if no provider is present. - * - * The parameter name determines the behavior: - * - `surface`: Container behavior - 'auto' increments from parent - * - `onSurface`: Leaf behavior - 'auto' inherits from parent - * - * @param options - Optional configuration for surface resolution - * @internal - */ -export const useSurface = ( - options?: UseSurfaceOptions, -): SurfaceContextValue => { - const value = useContext(SurfaceContext)?.atVersion(1); - const context = value ?? { surface: undefined }; - - // Infer behavior from which parameter is provided - // 'surface' = provider behavior (increment) - // 'onSurface' = consumer behavior (inherit) - const isProvider = options?.surface !== undefined; - const requestedSurface = options?.surface ?? options?.onSurface; - - const resolvedSurface = isProvider - ? resolveSurfaceForProvider(context.surface, requestedSurface) - : resolveSurfaceForConsumer(context.surface, requestedSurface); - - return { - surface: resolvedSurface, - }; -}; diff --git a/packages/ui/src/index.ts b/packages/ui/src/index.ts index 4ce616d5f1..dc920900f3 100644 --- a/packages/ui/src/index.ts +++ b/packages/ui/src/index.ts @@ -64,3 +64,9 @@ export * from './types'; // Hooks export { useBreakpoint } from './hooks/useBreakpoint'; +export { useBg, BgProvider } from './hooks/useBg'; +export type { + BgContextValue, + BgProviderProps, + UseBgOptions, +} from './hooks/useBg'; diff --git a/packages/ui/src/types.ts b/packages/ui/src/types.ts index 933e1d8aa9..9587dd1142 100644 --- a/packages/ui/src/types.ts +++ b/packages/ui/src/types.ts @@ -182,29 +182,18 @@ export interface ComponentDefinition { } /** - * Surface type + * Background type for the neutral bg system. * - * Supports absolute levels ('0'-'3'), intent surfaces ('danger', 'warning', 'success'), - * and 'auto' which increments from the parent surface context. + * Supports neutral levels ('neutral-1' through 'neutral-4') and + * intent backgrounds ('danger', 'warning', 'success'). * * @public */ -export type Surface = - | '0' - | '1' - | '2' - | '3' +export type Bg = + | 'neutral-1' + | 'neutral-2' + | 'neutral-3' + | 'neutral-4' | 'danger' | 'warning' - | 'success' - | 'auto'; - -/** @public */ -export interface LeafSurfaceProps { - onSurface?: Responsive; -} - -/** @public */ -export interface ContainerSurfaceProps { - surface?: Responsive; -} + | 'success';