First pass at adding the new surfaces
Signed-off-by: Charles de Dreuille <charles.dedreuille@gmail.com>
This commit is contained in:
@@ -22,4 +22,20 @@
|
||||
font-weight: var(--bui-font-weight-regular);
|
||||
color: var(--bui-fg-primary);
|
||||
}
|
||||
|
||||
.bui-Box[data-surface='0'] {
|
||||
background-color: var(--bui-bg-surface-0);
|
||||
}
|
||||
|
||||
.bui-Box[data-surface='1'] {
|
||||
background-color: var(--bui-bg-surface-1);
|
||||
}
|
||||
|
||||
.bui-Box[data-surface='2'] {
|
||||
background-color: var(--bui-bg-surface-2);
|
||||
}
|
||||
|
||||
.bui-Box[data-surface='3'] {
|
||||
background-color: var(--bui-bg-surface-3);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ import preview from '../../../../../.storybook/preview';
|
||||
import { ReactNode } from 'react';
|
||||
import { Box } from './Box';
|
||||
import { Flex } from '../Flex';
|
||||
import { Button } from '../Button';
|
||||
|
||||
const meta = preview.meta({
|
||||
title: 'Backstage UI/Box',
|
||||
@@ -344,3 +345,50 @@ export const Display = meta.story({
|
||||
</Flex>
|
||||
),
|
||||
});
|
||||
|
||||
export const Surfaces = meta.story({
|
||||
args: { px: '6', py: '4' },
|
||||
render: args => (
|
||||
<Flex align="center">
|
||||
<Box {...args}>Default</Box>
|
||||
<Box surface="0" {...args}>
|
||||
Surface 0
|
||||
</Box>
|
||||
<Box surface="1" {...args}>
|
||||
Surface 1
|
||||
</Box>
|
||||
<Box surface="2" {...args}>
|
||||
Surface 2
|
||||
</Box>
|
||||
<Box surface="3" {...args}>
|
||||
Surface 3
|
||||
</Box>
|
||||
<Box surface={{ initial: '0', sm: '1' }} {...args}>
|
||||
Responsive Surface
|
||||
</Box>
|
||||
</Flex>
|
||||
),
|
||||
});
|
||||
|
||||
export const SurfacesNested = meta.story({
|
||||
args: { px: '6', py: '4' },
|
||||
render: args => (
|
||||
<Flex direction="column">
|
||||
<Box style={{ maxWidth: '600px' }} mb="4">
|
||||
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.
|
||||
</Box>
|
||||
<Box {...args} surface="1">
|
||||
<Button>Button</Button>
|
||||
<Box {...args} surface="2" mt="4">
|
||||
<Button>Button</Button>
|
||||
<Box {...args} mt="4">
|
||||
<Button>Button</Button>
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
</Flex>
|
||||
),
|
||||
});
|
||||
|
||||
@@ -20,15 +20,14 @@ import clsx from 'clsx';
|
||||
import { useStyles } from '../../hooks/useStyles';
|
||||
import styles from './Box.module.css';
|
||||
import { BoxDefinition } from './definition';
|
||||
import { SurfaceProvider } from '../../hooks/useSurface';
|
||||
|
||||
/** @public */
|
||||
export const Box = forwardRef<HTMLDivElement, BoxProps>((props, ref) => {
|
||||
const { classNames, utilityClasses, style, cleanedProps } = useStyles(
|
||||
BoxDefinition,
|
||||
props,
|
||||
);
|
||||
const { classNames, dataAttributes, utilityClasses, style, cleanedProps } =
|
||||
useStyles(BoxDefinition, props);
|
||||
|
||||
const { as = 'div', children, className, ...rest } = cleanedProps;
|
||||
const { as = 'div', children, className, surface, ...rest } = cleanedProps;
|
||||
|
||||
return createElement(
|
||||
as,
|
||||
@@ -41,9 +40,14 @@ export const Box = forwardRef<HTMLDivElement, BoxProps>((props, ref) => {
|
||||
className,
|
||||
),
|
||||
style,
|
||||
...dataAttributes,
|
||||
...rest,
|
||||
},
|
||||
children,
|
||||
surface ? (
|
||||
<SurfaceProvider surface={surface}>{children}</SurfaceProvider>
|
||||
) : (
|
||||
children
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -48,4 +48,7 @@ export const BoxDefinition = {
|
||||
'minHeight',
|
||||
'maxHeight',
|
||||
],
|
||||
dataAttributes: {
|
||||
surface: ['0', '1', '2', '3'] as const,
|
||||
},
|
||||
} as const satisfies ComponentDefinition;
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import type { SpaceProps, Responsive } from '../../types';
|
||||
import type { SpaceProps, Responsive, Surface } from '../../types';
|
||||
|
||||
/** @public */
|
||||
export interface BoxProps extends SpaceProps {
|
||||
@@ -32,4 +32,5 @@ export interface BoxProps extends SpaceProps {
|
||||
children?: React.ReactNode;
|
||||
className?: string;
|
||||
style?: React.CSSProperties;
|
||||
surface?: Responsive<Surface>;
|
||||
}
|
||||
|
||||
@@ -80,24 +80,45 @@
|
||||
--bui-gray-7: #757575;
|
||||
--bui-gray-8: #595959;
|
||||
|
||||
/* Background Colors */
|
||||
/* Surfaces colors */
|
||||
--bui-bg-surface-0: var(--bui-gray-1);
|
||||
--bui-bg-surface-1: var(--bui-white);
|
||||
--bui-bg-surface-2: var(--bui-gray-1);
|
||||
--bui-bg-surface-3: var(--bui-gray-2);
|
||||
|
||||
/* Solid background colors */
|
||||
--bui-bg-solid: #1f5493;
|
||||
--bui-bg-solid-hover: #163a66;
|
||||
--bui-bg-solid-pressed: #0f2b4e;
|
||||
--bui-bg-solid-disabled: #ebebeb;
|
||||
--bui-bg-tint: transparent;
|
||||
--bui-bg-tint-hover: rgba(31, 84, 147, 0.4);
|
||||
--bui-bg-tint-pressed: rgba(31, 84, 147, 0.6);
|
||||
--bui-bg-tint-disabled: #ebebeb;
|
||||
|
||||
/* Neutral background colors */
|
||||
--bui-bg-neutral-on-surface-0: oklch(0% 0 0 / 6%);
|
||||
--bui-bg-neutral-on-surface-0-hover: oklch(0% 0 0 / 12%);
|
||||
--bui-bg-neutral-on-surface-0-pressed: oklch(0% 0 0 / 16%);
|
||||
--bui-bg-neutral-on-surface-0-disabled: oklch(0% 0 0 / 6%);
|
||||
|
||||
--bui-bg-neutral-on-surface-1: oklch(0% 0 0 / 6%);
|
||||
--bui-bg-neutral-on-surface-1-hover: oklch(0% 0 0 / 12%);
|
||||
--bui-bg-neutral-on-surface-1-pressed: oklch(0% 0 0 / 16%);
|
||||
--bui-bg-neutral-on-surface-1-disabled: oklch(0% 0 0 / 6%);
|
||||
|
||||
--bui-bg-neutral-on-surface-2: oklch(0% 0 0 / 6%);
|
||||
--bui-bg-neutral-on-surface-2-hover: oklch(0% 0 0 / 12%);
|
||||
--bui-bg-neutral-on-surface-2-pressed: oklch(0% 0 0 / 16%);
|
||||
--bui-bg-neutral-on-surface-2-disabled: oklch(0% 0 0 / 6%);
|
||||
|
||||
--bui-bg-neutral-on-surface-3: oklch(0% 0 0 / 6%);
|
||||
--bui-bg-neutral-on-surface-3-hover: oklch(0% 0 0 / 12%);
|
||||
--bui-bg-neutral-on-surface-3-pressed: oklch(0% 0 0 / 16%);
|
||||
--bui-bg-neutral-on-surface-3-disabled: oklch(0% 0 0 / 6%);
|
||||
|
||||
/* Status background colors */
|
||||
--bui-bg-danger: #feebe7;
|
||||
--bui-bg-warning: #fff2b2;
|
||||
--bui-bg-success: #e6f6eb;
|
||||
|
||||
/* Foreground Colors */
|
||||
/* Foreground colors */
|
||||
--bui-fg-primary: var(--bui-black);
|
||||
--bui-fg-secondary: var(--bui-gray-7);
|
||||
--bui-fg-link: #1f5493;
|
||||
@@ -111,7 +132,7 @@
|
||||
--bui-fg-warning: #e36d05;
|
||||
--bui-fg-success: #1db954;
|
||||
|
||||
/* Border Colors */
|
||||
/* Border colors */
|
||||
--bui-border: rgba(0, 0, 0, 0.1);
|
||||
--bui-border-hover: rgba(0, 0, 0, 0.2);
|
||||
--bui-border-pressed: rgba(0, 0, 0, 0.4);
|
||||
@@ -120,7 +141,7 @@
|
||||
--bui-border-warning: #e36d05;
|
||||
--bui-border-success: #53db83;
|
||||
|
||||
/* Special Colors */
|
||||
/* Special colors */
|
||||
--bui-ring: #1f5493;
|
||||
--bui-scrollbar: #a0a0a03b;
|
||||
--bui-scrollbar-thumb: #a0a0a0;
|
||||
@@ -140,24 +161,45 @@
|
||||
--bui-gray-7: #9e9e9e;
|
||||
--bui-gray-8: #b4b4b4;
|
||||
|
||||
/* Background Colors */
|
||||
/* Surfaces colors */
|
||||
--bui-bg-surface-0: var(--bui-black);
|
||||
--bui-bg-surface-1: var(--bui-gray-1);
|
||||
--bui-bg-surface-2: var(--bui-gray-2);
|
||||
--bui-bg-surface-3: var(--bui-gray-3);
|
||||
|
||||
/* Solid background colors */
|
||||
--bui-bg-solid: #9cc9ff;
|
||||
--bui-bg-solid-hover: #83b9fd;
|
||||
--bui-bg-solid-pressed: #83b9fd;
|
||||
--bui-bg-solid-disabled: #222222;
|
||||
--bui-bg-tint: transparent;
|
||||
--bui-bg-tint-hover: rgba(156, 201, 255, 0.12);
|
||||
--bui-bg-tint-pressed: rgba(156, 201, 255, 0.16);
|
||||
--bui-bg-tint-disabled: transparent;
|
||||
|
||||
/* Neutral background colors */
|
||||
--bui-bg-neutral-on-surface-0: oklch(100% 0 0 / 10%);
|
||||
--bui-bg-neutral-on-surface-0-hover: oklch(100% 0 0 / 14%);
|
||||
--bui-bg-neutral-on-surface-0-pressed: oklch(100% 0 0 / 20%);
|
||||
--bui-bg-neutral-on-surface-0-disabled: oklch(100% 0 0 / 10%);
|
||||
|
||||
--bui-bg-neutral-on-surface-1: oklch(100% 0 0 / 6%);
|
||||
--bui-bg-neutral-on-surface-1-hover: oklch(100% 0 0 / 10%);
|
||||
--bui-bg-neutral-on-surface-1-pressed: oklch(100% 0 0 / 16%);
|
||||
--bui-bg-neutral-on-surface-1-disabled: oklch(100% 0 0 / 6%);
|
||||
|
||||
--bui-bg-neutral-on-surface-2: oklch(100% 0 0 / 8%);
|
||||
--bui-bg-neutral-on-surface-2-hover: oklch(100% 0 0 / 12%);
|
||||
--bui-bg-neutral-on-surface-2-pressed: oklch(100% 0 0 / 20%);
|
||||
--bui-bg-neutral-on-surface-2-disabled: oklch(100% 0 0 / 8%);
|
||||
|
||||
--bui-bg-neutral-on-surface-3: oklch(100% 0 0 / 8%);
|
||||
--bui-bg-neutral-on-surface-3-hover: oklch(100% 0 0 / 12%);
|
||||
--bui-bg-neutral-on-surface-3-pressed: oklch(100% 0 0 / 20%);
|
||||
--bui-bg-neutral-on-surface-3-disabled: oklch(100% 0 0 / 8%);
|
||||
|
||||
/* Status background colors */
|
||||
--bui-bg-danger: #3b1219;
|
||||
--bui-bg-warning: #302008;
|
||||
--bui-bg-success: #132d21;
|
||||
|
||||
/* Foreground Colors */
|
||||
/* Foreground colors */
|
||||
--bui-fg-primary: var(--bui-white);
|
||||
--bui-fg-secondary: var(--bui-gray-7);
|
||||
--bui-fg-link: #9cc9ff;
|
||||
@@ -171,7 +213,7 @@
|
||||
--bui-fg-warning: #e36d05;
|
||||
--bui-fg-success: #1db954;
|
||||
|
||||
/* Border Colors */
|
||||
/* Border colors */
|
||||
--bui-border: rgba(255, 255, 255, 0.12);
|
||||
--bui-border-hover: rgba(255, 255, 255, 0.4);
|
||||
--bui-border-pressed: rgba(255, 255, 255, 0.5);
|
||||
@@ -180,7 +222,7 @@
|
||||
--bui-border-warning: #e36d05;
|
||||
--bui-border-success: #53db83;
|
||||
|
||||
/* Special Colors */
|
||||
/* Special colors */
|
||||
--bui-ring: #1f5493;
|
||||
--bui-scrollbar: #3636363a;
|
||||
--bui-scrollbar-thumb: #575757;
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* 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 { createContext, useContext, ReactNode } from 'react';
|
||||
import { Surface, Responsive } from '../types';
|
||||
|
||||
/** @public */
|
||||
export interface SurfaceContextValue {
|
||||
surface: Responsive<Surface> | undefined;
|
||||
}
|
||||
|
||||
/** @public */
|
||||
export interface SurfaceProviderProps {
|
||||
surface: Responsive<Surface>;
|
||||
children: ReactNode;
|
||||
}
|
||||
|
||||
const SurfaceContext = createContext<SurfaceContextValue>({
|
||||
surface: undefined,
|
||||
});
|
||||
|
||||
/**
|
||||
* Provider component that establishes the surface context for child components.
|
||||
* This allows components to adapt their styling based on their background surface.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export const SurfaceProvider = ({
|
||||
surface,
|
||||
children,
|
||||
}: SurfaceProviderProps) => {
|
||||
return (
|
||||
<SurfaceContext.Provider value={{ surface }}>
|
||||
{children}
|
||||
</SurfaceContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Hook to access the current surface context.
|
||||
* Returns the current surface level, or undefined if no provider is present.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export const useSurface = (): SurfaceContextValue => {
|
||||
return useContext(SurfaceContext);
|
||||
};
|
||||
@@ -173,3 +173,9 @@ export interface ComponentDefinition {
|
||||
dataAttributes?: DataAttributesMap;
|
||||
utilityProps?: string[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Surface type
|
||||
* @public
|
||||
*/
|
||||
export type Surface = '0' | '1' | '2' | '3';
|
||||
|
||||
Reference in New Issue
Block a user