diff --git a/packages/canon/src/components/box/box.mdx b/packages/canon/src/components/box/box.mdx
index e827d8fcfd..f8cadc316b 100644
--- a/packages/canon/src/components/box/box.mdx
+++ b/packages/canon/src/components/box/box.mdx
@@ -5,13 +5,10 @@ import * as Table from '../../storybook/table';
import { Box } from './box';
import { Stack } from '../stack/stack';
import { Title, Text } from '../../storybook';
-import {
- alignItems,
- backgroundColors,
- boxShadows,
- borderRadius,
- display,
-} from './properties';
+import { Padding } from './docs/padding';
+import { listResponsiveValues, listColorValues } from '../../utils/list-values';
+import { responsiveProperties } from './sprinkles.css';
+import { PropsTable } from './docs/props-table';
@@ -20,137 +17,19 @@ import {
Box
Box is the lowest-level component in Canon. We use it internally to build all
- of our components.
+ of our components. It is a wrapper around the HTML `` element and
+ provides a consistent API for styling and layout. You'll find below a list of all the available properties.
-
-
-
-
- padding
-
-
-
-
- paddingX
-
-
-
-
- paddingY
-
-
-
-
-
-
- paddingTop
-
-
-
-
- paddingBottom
-
-
-
-
- paddingLeft
-
-
-
-
- paddingRight
-
-
-
-
+
+
Padding & Margin
+
+ Padding and margin are used to create space around your component using our
+ predefined spacing tokens. We would recommend to use padding over margin to
+ avoid collapsing margins but both are available.
+
+
@@ -160,68 +39,6 @@ import {
-
-
- alignItems
-
-
- {alignItems.map(n => (
- {n}
- ))}
-
-
- stretch
-
-
-
-
- background
-
-
- {Object.keys(backgroundColors).map(n => (
- {n}
- ))}
-
-
- transparent
-
-
-
-
- borderRadius
-
-
- {Object.keys(borderRadius).map(n => (
- {n}
- ))}
-
-
- none
-
-
-
-
- boxShadow
-
-
- {Object.keys(boxShadows).map(n => (
- {n}
- ))}
-
-
-
-
- display
-
-
- {display.map(n => (
- {n}
- ))}
-
-
- block
-
-
padding
diff --git a/packages/canon/src/components/box/box.stories.tsx b/packages/canon/src/components/box/box.stories.tsx
index fa78813a73..e85c2d1eb9 100644
--- a/packages/canon/src/components/box/box.stories.tsx
+++ b/packages/canon/src/components/box/box.stories.tsx
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
import React from 'react';
import type { Meta, StoryObj } from '@storybook/react';
import { Box } from './box';
@@ -32,10 +33,10 @@ type Story = StoryObj;
export const Basic: Story = {
render: () => (
Basic Box
@@ -47,7 +48,7 @@ export const Responsive: Story = {
render: () => (
Responsive Box
@@ -60,16 +61,16 @@ export const FlexContainer: Story = {
-
+
Item 1
-
+
Item 2
-
+
Item 3
@@ -78,24 +79,24 @@ export const FlexContainer: Story = {
export const Nested: Story = {
render: () => (
-
-
+
+
Header
-
+
Sidebar
-
+
Main Content
-
+
Footer
@@ -106,13 +107,13 @@ export const Alignment: Story = {
render: () => (
-
+
Centered Content
@@ -122,17 +123,17 @@ export const Alignment: Story = {
// Example showing different spacing combinations
export const Spacing: Story = {
render: () => (
-
-
+
+
Small Padding
-
+
Medium Padding
-
+
Large Padding
-
+
Mixed Padding
@@ -142,14 +143,14 @@ export const Spacing: Story = {
// Example showing different display values
export const DisplayVariants: Story = {
render: () => (
-
-
+
+
Display Block
-
+
Display Flex
-
+
Display Inline
diff --git a/packages/canon/src/components/box/docs/padding.tsx b/packages/canon/src/components/box/docs/padding.tsx
new file mode 100644
index 0000000000..189846c39a
--- /dev/null
+++ b/packages/canon/src/components/box/docs/padding.tsx
@@ -0,0 +1,89 @@
+import React from 'react';
+import { Stack } from '../../stack/stack';
+import { Inline } from '../../inline/inline';
+import { Box } from '../box';
+
+const FakeBox = ({ children }: { children: string }) => (
+
+ {children}
+
+);
+
+export const Padding = () => {
+ return (
+
+
+
+ padding
+
+
+ paddingX
+
+
+ paddingY
+
+
+
+
+ paddingTop
+
+
+ paddingBottom
+
+
+ paddingLeft
+
+
+ paddingRight
+
+
+
+ );
+};
diff --git a/packages/canon/src/components/box/docs/props-table.tsx b/packages/canon/src/components/box/docs/props-table.tsx
new file mode 100644
index 0000000000..de4de09bfe
--- /dev/null
+++ b/packages/canon/src/components/box/docs/props-table.tsx
@@ -0,0 +1,68 @@
+import React from 'react';
+import * as Table from '../../../storybook';
+import { Chip } from '../../../storybook';
+import { listResponsiveValues } from '../../../utils/list-values';
+import { responsiveProperties } from '../sprinkles.css';
+
+export const PropsTable = () => {
+ return (
+
+
+
+ Prop
+ Type
+
+
+
+ {Object.keys(responsiveProperties.styles)
+ .filter(
+ n =>
+ ![
+ 'padding',
+ 'paddingX',
+ 'paddingY',
+ 'paddingLeft',
+ 'paddingRight',
+ 'paddingTop',
+ 'paddingBottom',
+ 'p',
+ 'px',
+ 'py',
+ 'pl',
+ 'pr',
+ 'pt',
+ 'pb',
+ 'margin',
+ 'marginX',
+ 'marginY',
+ 'marginLeft',
+ 'marginRight',
+ 'marginTop',
+ 'marginBottom',
+ 'm',
+ 'mx',
+ 'my',
+ 'ml',
+ 'mr',
+ 'mt',
+ 'mb',
+ ].includes(n),
+ )
+ .map(n => (
+
+
+ {n}
+
+
+ {listResponsiveValues(
+ n as keyof typeof responsiveProperties.styles,
+ ).map(value => (
+ {value}
+ ))}
+
+
+ ))}
+
+
+ );
+};
diff --git a/packages/canon/src/components/box/properties.ts b/packages/canon/src/components/box/properties.ts
index 5d4e851a0e..9063aa24d6 100644
--- a/packages/canon/src/components/box/properties.ts
+++ b/packages/canon/src/components/box/properties.ts
@@ -1,5 +1,3 @@
-export const alignItems = ['stretch', 'flex-start', 'center', 'flex-end'];
-
export const breakpoints = {
xs: {},
sm: { '@media': 'screen and (min-width: 640px)' },
@@ -9,49 +7,6 @@ export const breakpoints = {
'2xl': { '@media': 'screen and (min-width: 1536px)' },
};
-export const borderRadius = {
- none: 0,
- small: '4px',
- medium: '8px',
- full: '9999px',
-};
-
-export const backgroundColors = {
- background: 'var(--canon-color-background)',
- elevation1: 'var(--canon-color-elevation1)',
- elevation2: 'var(--canon-color-elevation2)',
- transparent: 'transparent',
-};
-
-export const border = {
- none: 'none',
- thin: '1px solid var(--canon-outline)',
- error: '1px solid var(--canon-error)',
-};
-
-export const boxShadows = {
- small: 'var(--canon-box-shadow-small)',
- medium: 'var(--canon-box-shadow-medium)',
- large: 'var(--canon-box-shadow-large)',
-};
-
-export const colors = {
- background: '#eff6ff',
-};
-
-export const display = ['none', 'flex', 'block', 'inline'];
-
-export const flexDirection = ['row', 'column'] as const;
-
-export const justifyContent = [
- 'stretch',
- 'flex-start',
- 'center',
- 'flex-end',
- 'space-around',
- 'space-between',
-];
-
export const space = {
none: 0,
xxs: 'var(--space-xxs)',
diff --git a/packages/canon/src/components/box/sprinkles.css.ts b/packages/canon/src/components/box/sprinkles.css.ts
index e8e912fef4..e8bcc8af15 100644
--- a/packages/canon/src/components/box/sprinkles.css.ts
+++ b/packages/canon/src/components/box/sprinkles.css.ts
@@ -13,84 +13,101 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-import { defineProperties, createSprinkles } from '@vanilla-extract/sprinkles';
+
import {
- alignItems,
- borderRadius,
- breakpoints,
- colors,
- backgroundColors,
- display,
- flexDirection,
- justifyContent,
- space,
- boxShadows,
- border,
-} from './properties';
+ defineProperties,
+ createSprinkles,
+ RequiredConditionalValue,
+ ConditionalValue,
+ createMapValueFn,
+} from '@vanilla-extract/sprinkles';
+import { breakpoints, space } from './properties';
-export const commonProperties = {
- flexDirection,
- justifyContent,
- alignItems,
- borderRadius,
- boxShadow: boxShadows,
- border,
- paddingTop: space,
- paddingBottom: space,
- paddingLeft: space,
- paddingRight: space,
- marginTop: space,
- marginBottom: space,
- marginLeft: space,
- marginRight: space,
- gap: space,
-};
-
-export const boxShorthands: Record = {
- padding: ['paddingTop', 'paddingBottom', 'paddingLeft', 'paddingRight'],
- paddingX: ['paddingLeft', 'paddingRight'],
- paddingY: ['paddingTop', 'paddingBottom'],
- p: ['paddingTop', 'paddingBottom', 'paddingLeft', 'paddingRight'],
- pt: ['paddingTop'],
- pr: ['paddingRight'],
- pb: ['paddingBottom'],
- pl: ['paddingLeft'],
- px: ['paddingLeft', 'paddingRight'],
- py: ['paddingTop', 'paddingBottom'],
- margin: ['marginTop', 'marginBottom', 'marginLeft', 'marginRight'],
- marginX: ['marginLeft', 'marginRight'],
- marginY: ['marginTop', 'marginBottom'],
- m: ['marginTop', 'marginBottom', 'marginLeft', 'marginRight'],
- mt: ['marginTop'],
- mr: ['marginRight'],
- mb: ['marginBottom'],
- ml: ['marginLeft'],
- mx: ['marginLeft', 'marginRight'],
- my: ['marginTop', 'marginBottom'],
-};
-
-const responsiveProperties = defineProperties({
+export const responsiveProperties = defineProperties({
conditions: breakpoints,
defaultCondition: 'xs',
+ responsiveArray: ['xs', 'sm', 'md', 'lg', 'xl', '2xl'],
properties: {
- ...commonProperties,
- display,
+ flexDirection: ['row', 'column'],
+ justifyContent: [
+ 'stretch',
+ 'flex-start',
+ 'center',
+ 'flex-end',
+ 'space-around',
+ 'space-between',
+ ],
+ alignItems: ['stretch', 'flex-start', 'center', 'flex-end'],
+ borderRadius: {
+ none: 0,
+ small: '4px',
+ medium: '8px',
+ full: '9999px',
+ },
+ boxShadow: {
+ small: 'var(--canon-box-shadow-small)',
+ medium: 'var(--canon-box-shadow-medium)',
+ large: 'var(--canon-box-shadow-large)',
+ },
+ border: {
+ none: 'none',
+ thin: '1px solid var(--canon-outline)',
+ error: '1px solid var(--canon-error)',
+ },
+ display: ['none', 'flex', 'block', 'inline'],
+ paddingTop: space,
+ paddingBottom: space,
+ paddingLeft: space,
+ paddingRight: space,
+ marginTop: space,
+ marginBottom: space,
+ marginLeft: space,
+ marginRight: space,
+ gap: space,
+ flexWrap: ['wrap', 'nowrap'],
},
shorthands: {
- ...boxShorthands,
- placeItems: ['justifyContent', 'alignItems'],
+ padding: ['paddingTop', 'paddingBottom', 'paddingLeft', 'paddingRight'],
+ paddingX: ['paddingLeft', 'paddingRight'],
+ paddingY: ['paddingTop', 'paddingBottom'],
+ p: ['paddingTop', 'paddingBottom', 'paddingLeft', 'paddingRight'],
+ pt: ['paddingTop'],
+ pr: ['paddingRight'],
+ pb: ['paddingBottom'],
+ pl: ['paddingLeft'],
+ px: ['paddingLeft', 'paddingRight'],
+ py: ['paddingTop', 'paddingBottom'],
+ margin: ['marginTop', 'marginBottom', 'marginLeft', 'marginRight'],
+ marginX: ['marginLeft', 'marginRight'],
+ marginY: ['marginTop', 'marginBottom'],
+ m: ['marginTop', 'marginBottom', 'marginLeft', 'marginRight'],
+ mt: ['marginTop'],
+ mr: ['marginRight'],
+ mb: ['marginBottom'],
+ ml: ['marginLeft'],
+ mx: ['marginLeft', 'marginRight'],
+ my: ['marginTop', 'marginBottom'],
},
});
-const colorProperties = defineProperties({
+export const colorProperties = defineProperties({
conditions: {
light: { selector: '[data-theme="light"] &' },
dark: { selector: '[data-theme="dark"] &' },
},
defaultCondition: ['light', 'dark'],
properties: {
- color: colors,
- background: backgroundColors,
+ color: {
+ primary: 'var(--canon-text-primary)',
+ secondary: 'var(--canon-text-secondary)',
+ error: 'var(--canon-error)',
+ },
+ background: {
+ background: 'var(--canon-background)',
+ elevation1: 'var(--canon-surface-1)',
+ elevation2: 'var(--canon-surface-2)',
+ transparent: 'transparent',
+ },
},
});
@@ -98,3 +115,11 @@ export const sprinkles = createSprinkles(responsiveProperties, colorProperties);
// It's a good idea to export the Sprinkles type too
export type Sprinkles = Parameters[0];
+
+export type OptionalResponsiveValue =
+ ConditionalValue;
+
+export type RequiredResponsiveValue =
+ RequiredConditionalValue;
+
+export const mapResponsiveValue = createMapValueFn(responsiveProperties);
diff --git a/packages/canon/src/components/inline/inline.stories.tsx b/packages/canon/src/components/inline/inline.stories.tsx
new file mode 100644
index 0000000000..9562a0b914
--- /dev/null
+++ b/packages/canon/src/components/inline/inline.stories.tsx
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2024 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 React from 'react';
+import type { Meta, StoryObj } from '@storybook/react';
+import { Inline } from './inline';
+import { Box } from '../box/box';
+
+const meta = {
+ title: 'Layout/Inline',
+ component: Inline,
+} satisfies Meta;
+
+export default meta;
+type Story = StoryObj;
+
+const FakeBox = () => (
+
+ Fake Box
+
+);
+
+export const Default: Story = {
+ args: {
+ children: (
+ <>
+
+
+
+ >
+ ),
+ },
+};
+
+export const LargeGap: Story = {
+ args: {
+ ...Default.args,
+ gap: 'xl',
+ },
+};
diff --git a/packages/canon/src/components/inline/inline.tsx b/packages/canon/src/components/inline/inline.tsx
new file mode 100644
index 0000000000..0da15303f9
--- /dev/null
+++ b/packages/canon/src/components/inline/inline.tsx
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2024 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 React from 'react';
+import { Box, BoxProps } from '../box/box';
+import { alignToFlexAlign } from '../../utils/align';
+
+export const validInlineComponents = [
+ 'div',
+ 'span',
+ 'p',
+ 'nav',
+ 'ul',
+ 'ol',
+ 'li',
+] as const;
+
+export interface InlineProps extends Omit {
+ as?: (typeof validInlineComponents)[number];
+ children: React.ReactNode;
+ align?: 'left' | 'center' | 'right';
+ gap?: BoxProps['gap'];
+}
+
+export const Inline = ({
+ align,
+ as = 'div',
+ children,
+ gap = 'xs',
+ ...restProps
+}: InlineProps) => {
+ return (
+
+ {children}
+
+ );
+};
diff --git a/packages/canon/src/components/stack/sprinkles.css.ts b/packages/canon/src/components/stack/sprinkles.css.ts
deleted file mode 100644
index 6a300a4044..0000000000
--- a/packages/canon/src/components/stack/sprinkles.css.ts
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright 2024 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 { defineProperties, createSprinkles } from '@vanilla-extract/sprinkles';
-import { breakpoints } from '../box/properties';
-import { boxShorthands, commonProperties } from '../box/sprinkles.css';
-
-const responsiveProperties = defineProperties({
- conditions: breakpoints,
- defaultCondition: 'xs',
- properties: {
- ...commonProperties,
- },
- shorthands: {
- ...boxShorthands,
- direction: ['flexDirection'],
- items: ['alignItems'],
- justify: ['justifyContent'],
- },
-});
-
-export const sprinkles = createSprinkles(responsiveProperties);
-
-// It's a good idea to export the Sprinkles type too
-export type Sprinkles = Parameters[0];
diff --git a/packages/canon/src/components/stack/stack.css.ts b/packages/canon/src/components/stack/stack.css.ts
deleted file mode 100644
index 3ede7d94e4..0000000000
--- a/packages/canon/src/components/stack/stack.css.ts
+++ /dev/null
@@ -1,21 +0,0 @@
-/*
- * Copyright 2024 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 { style } from '@vanilla-extract/css';
-
-export const stackStyles = style({
- fontFamily: 'var(--canon-font-sans)',
- display: 'flex',
-});
diff --git a/packages/canon/src/components/stack/stack.stories.tsx b/packages/canon/src/components/stack/stack.stories.tsx
index e60de21312..209811880a 100644
--- a/packages/canon/src/components/stack/stack.stories.tsx
+++ b/packages/canon/src/components/stack/stack.stories.tsx
@@ -21,42 +21,37 @@ import { Box } from '../box/box';
const meta = {
title: 'Layout/Stack',
component: Stack,
- parameters: {
- layout: 'centered',
- },
- // tags: ['autodocs'],
} satisfies Meta;
+export default meta;
+type Story = StoryObj;
+
const FakeBox = () => (
Fake Box
);
-export default meta;
-type Story = StoryObj;
-
-export const Column: Story = {
- render: () => (
-
-
-
-
-
- ),
+export const Default: Story = {
+ args: {
+ children: (
+ <>
+
+
+
+ >
+ ),
+ },
};
-export const Row: Story = {
- render: () => (
-
-
-
-
-
- ),
+export const LargeGap: Story = {
+ args: {
+ ...Default.args,
+ gap: 'xl',
+ },
};
diff --git a/packages/canon/src/components/stack/stack.tsx b/packages/canon/src/components/stack/stack.tsx
index b9b160e01b..90ea6c0502 100644
--- a/packages/canon/src/components/stack/stack.tsx
+++ b/packages/canon/src/components/stack/stack.tsx
@@ -13,47 +13,62 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-import { createElement } from 'react';
-import { sprinkles, Sprinkles } from './sprinkles.css';
-import { stackStyles } from './stack.css';
-type HTMLProperties = Omit<
- React.AllHTMLAttributes,
- keyof Sprinkles
->;
+import React from 'react';
+import { Box, BoxProps } from '../box/box';
+import { alignToFlexAlign } from '../../utils/align';
-export type StackProps = Sprinkles &
- HTMLProperties & {
- as?: keyof JSX.IntrinsicElements;
- };
+const validStackComponents = [
+ 'div',
+ 'span',
+ 'p',
+ 'article',
+ 'section',
+ 'main',
+ 'nav',
+ 'aside',
+ 'ul',
+ 'ol',
+ 'li',
+ 'details',
+ 'summary',
+ 'dd',
+ 'dl',
+ 'dt',
+] as const;
+
+export interface StackProps extends Omit {
+ children: React.ReactNode;
+ as?: (typeof validStackComponents)[number];
+ align?: 'left' | 'center' | 'right';
+ gap?: BoxProps['gap'];
+}
export const Stack = ({
as = 'div',
- className,
- style,
- ...props
+ children,
+ align: alignProp,
+ gap = 'xs',
+ ...restProps
}: StackProps) => {
- const sprinklesProps: Record = {};
- const nativeProps: Record = {};
+ /**
+ * Creating a seam between the provided prop and the default value
+ * to enable only setting the text alignment when the `align` prop
+ * is provided — not when it's defaulted.
+ */
+ const align = alignProp || 'left';
- // Split props between sprinkles and native HTML props
- Object.entries(props).forEach(([key, value]) => {
- if (value === undefined) return;
-
- if (sprinkles.properties.has(key as keyof Sprinkles)) {
- sprinklesProps[key] = value;
- } else {
- nativeProps[key] = value;
- }
- });
-
- const sprinklesClassName = sprinkles(sprinklesProps);
-
- return createElement(as, {
- className: [stackStyles, sprinklesClassName, className]
- .filter(Boolean)
- .join(' '),
- style,
- ...nativeProps,
- });
+ return (
+
+ {children}
+
+ );
};
diff --git a/packages/canon/src/theme/theme.css b/packages/canon/src/theme/theme.css
index ec69322d0d..1633a83fc0 100644
--- a/packages/canon/src/theme/theme.css
+++ b/packages/canon/src/theme/theme.css
@@ -1,7 +1,7 @@
/* Light theme */
:root {
--canon-accent: #1ed760;
- --canon-bg: #fff;
+ --canon-background: #fff;
--canon-surface-1: #f5f5f5;
--canon-surface-2: #000;
--canon-outline: #666;
@@ -31,7 +31,7 @@
/* Dark theme */
[data-theme='dark'] {
--canon-accent: #1ed760;
- --canon-bg: #fff;
+ --canon-background: #fff;
--canon-surface-1: #f5f5f5;
--canon-surface-2: #000;
--canon-outline: #666;
diff --git a/packages/canon/src/utils/align.ts b/packages/canon/src/utils/align.ts
new file mode 100644
index 0000000000..bee9b35e01
--- /dev/null
+++ b/packages/canon/src/utils/align.ts
@@ -0,0 +1,33 @@
+import {
+ type OptionalResponsiveValue,
+ mapResponsiveValue,
+} from '../components/box/sprinkles.css';
+
+export type Align = 'left' | 'center' | 'right';
+export type AlignY = 'top' | 'center' | 'bottom';
+
+const alignToFlexAlignLookup = {
+ left: 'flex-start',
+ center: 'center',
+ right: 'flex-end',
+} as const;
+
+export const alignToFlexAlign = (
+ align: OptionalResponsiveValue | undefined,
+) =>
+ align
+ ? mapResponsiveValue(align, value => alignToFlexAlignLookup[value])
+ : undefined;
+
+const alignYToFlexAlignLookup = {
+ top: 'flex-start',
+ center: 'center',
+ bottom: 'flex-end',
+} as const;
+
+export const alignYToFlexAlign = (
+ alignY: OptionalResponsiveValue | undefined,
+) =>
+ alignY
+ ? mapResponsiveValue(alignY, value => alignYToFlexAlignLookup[value])
+ : undefined;
diff --git a/packages/canon/src/utils/list-values.ts b/packages/canon/src/utils/list-values.ts
new file mode 100644
index 0000000000..31ccc7e0ed
--- /dev/null
+++ b/packages/canon/src/utils/list-values.ts
@@ -0,0 +1,26 @@
+import {
+ responsiveProperties,
+ colorProperties,
+} from '../components/box/sprinkles.css';
+
+export const listResponsiveValues = (
+ value: keyof typeof responsiveProperties.styles,
+) => {
+ const values = responsiveProperties.styles[value];
+
+ if ('values' in values) {
+ return Object.keys(values.values);
+ }
+
+ return [];
+};
+
+export const listColorValues = (value: keyof typeof colorProperties.styles) => {
+ const values = colorProperties.styles[value];
+
+ if ('values' in values) {
+ return Object.keys(values.values);
+ }
+
+ return [];
+};