diff --git a/packages/canon/docs/Home.mdx b/packages/canon/docs/Home.mdx
index 3a00de5d07..51574f57a1 100644
--- a/packages/canon/docs/Home.mdx
+++ b/packages/canon/docs/Home.mdx
@@ -43,33 +43,38 @@ import { list } from './components/Roadmap/list';
link="/?path=/docs/components-box--docs"
/>
+
+
+
+
-
-
-
-
+
+
-
-
+
diff --git a/packages/canon/src/components/Grid/Grid.stories.tsx b/packages/canon/src/components/Grid/Grid.stories.tsx
new file mode 100644
index 0000000000..e9833a0325
--- /dev/null
+++ b/packages/canon/src/components/Grid/Grid.stories.tsx
@@ -0,0 +1,91 @@
+/*
+ * 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 { Grid } from './Grid';
+import type { GridItemProps } from './types';
+import { Box } from '../Box/Box';
+import { argTypesSpacing, argTypesColor } from '../../../docs/utils/argTypes';
+import { Stack } from '../Stack';
+
+const meta = {
+ title: 'Components/Grid',
+ component: Grid,
+ argTypes: {
+ ...argTypesSpacing,
+ ...argTypesColor,
+ children: {
+ control: false,
+ },
+ className: {
+ control: 'text',
+ },
+ },
+ args: {
+ gap: 'xs',
+ },
+} satisfies Meta;
+
+export default meta;
+type Story = StoryObj;
+
+const FakeBox = () => (
+
+);
+
+export const Default: Story = {
+ args: {
+ columns: 3,
+ },
+ render: args => (
+
+
+
+
+
+
+
+
+
+
+
+ ),
+};
+
+export const ColumnSizes: Story = {
+ args: {
+ columns: 12,
+ gap: 'md',
+ },
+ render: args => (
+
+ {Array.from({ length: 11 }, (_, i) => (
+
+
+
+
+
+
+
+
+ ))}
+
+ ),
+};
diff --git a/packages/canon/src/components/Grid/Grid.tsx b/packages/canon/src/components/Grid/Grid.tsx
new file mode 100644
index 0000000000..28c49d87b1
--- /dev/null
+++ b/packages/canon/src/components/Grid/Grid.tsx
@@ -0,0 +1,70 @@
+/*
+ * 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 { createElement } from 'react';
+import { GridItemProps, GridProps } from './types';
+import { gridItemSprinkles, gridSprinkles } from './sprinkles.css';
+
+export const Grid = ({
+ children,
+ columns = 1,
+ gap = 'xs',
+ className,
+ style,
+ ...restProps
+}: GridProps) => {
+ const sprinklesClassName = gridSprinkles({
+ ...restProps,
+ gap,
+ gridTemplateColumns: columns,
+ });
+
+ const classNames = ['grid', sprinklesClassName, className]
+ .filter(Boolean)
+ .join(' ');
+
+ return createElement(
+ 'div',
+ {
+ className: classNames,
+ style,
+ },
+ children,
+ );
+};
+
+const GridItem = ({
+ children,
+ span = 1,
+ start,
+ end,
+ className,
+ style,
+}: GridItemProps) => {
+ const sprinklesClassName = gridItemSprinkles({
+ span,
+ start,
+ end,
+ });
+
+ const classNames = ['grid-item', sprinklesClassName, className]
+ .filter(Boolean)
+ .join(' ');
+
+ return createElement('div', { className: classNames, style }, children);
+};
+
+Grid.Item = GridItem;
diff --git a/packages/canon/src/components/Grid/index.ts b/packages/canon/src/components/Grid/index.ts
new file mode 100644
index 0000000000..b2534c6b77
--- /dev/null
+++ b/packages/canon/src/components/Grid/index.ts
@@ -0,0 +1,17 @@
+/*
+ * 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.
+ */
+export { Grid } from './Grid';
+export type { GridProps, GridItemProps } from './types';
diff --git a/packages/canon/src/components/Grid/sprinkles.css.ts b/packages/canon/src/components/Grid/sprinkles.css.ts
new file mode 100644
index 0000000000..a2ac056d61
--- /dev/null
+++ b/packages/canon/src/components/Grid/sprinkles.css.ts
@@ -0,0 +1,113 @@
+/*
+ * 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 '../../layout/properties';
+import { colorProperties, spacingProperties } from '../../layout/sprinkles.css';
+
+const gridProperties = defineProperties({
+ conditions: breakpoints,
+ defaultCondition: 'xs',
+ properties: {
+ gridTemplateColumns: {
+ 1: 'repeat(1, minmax(0, 1fr))',
+ 2: 'repeat(2, minmax(0, 1fr))',
+ 3: 'repeat(3, minmax(0, 1fr))',
+ 4: 'repeat(4, minmax(0, 1fr))',
+ 5: 'repeat(5, minmax(0, 1fr))',
+ 6: 'repeat(6, minmax(0, 1fr))',
+ 7: 'repeat(7, minmax(0, 1fr))',
+ 8: 'repeat(8, minmax(0, 1fr))',
+ 9: 'repeat(9, minmax(0, 1fr))',
+ 10: 'repeat(10, minmax(0, 1fr))',
+ 11: 'repeat(11, minmax(0, 1fr))',
+ 12: 'repeat(12, minmax(0, 1fr))',
+ },
+ },
+ shorthands: {
+ columns: ['gridTemplateColumns'],
+ },
+});
+
+export const gridSprinkles = createSprinkles(
+ spacingProperties,
+ colorProperties,
+ gridProperties,
+);
+
+const gridItemProperties = defineProperties({
+ conditions: breakpoints,
+ defaultCondition: 'xs',
+ properties: {
+ gridColumn: {
+ 1: 'span 1 / span 1',
+ 2: 'span 2 / span 2',
+ 3: 'span 3 / span 3',
+ 4: 'span 4 / span 4',
+ 5: 'span 5 / span 5',
+ 6: 'span 6 / span 6',
+ 7: 'span 7 / span 7',
+ 8: 'span 8 / span 8',
+ 9: 'span 9 / span 9',
+ 10: 'span 10 / span 10',
+ 11: 'span 11 / span 11',
+ 12: 'span 12 / span 12',
+ full: '1 / -1',
+ },
+ gridColumnStart: {
+ 1: '1',
+ 2: '2',
+ 3: '3',
+ 4: '4',
+ 5: '5',
+ 6: '6',
+ 7: '7',
+ 8: '8',
+ 9: '9',
+ 10: '10',
+ 11: '11',
+ 12: '12',
+ 13: '13',
+ auto: 'auto',
+ },
+ gridColumnEnd: {
+ 1: '1',
+ 2: '2',
+ 3: '3',
+ 4: '4',
+ 5: '5',
+ 6: '6',
+ 7: '7',
+ 8: '8',
+ 9: '9',
+ 10: '10',
+ 11: '11',
+ 12: '12',
+ 13: '13',
+ auto: 'auto',
+ },
+ },
+ shorthands: {
+ span: ['gridColumn'],
+ start: ['gridColumnStart'],
+ end: ['gridColumnEnd'],
+ },
+});
+
+export const gridItemSprinkles = createSprinkles(
+ colorProperties,
+ gridItemProperties,
+);
diff --git a/packages/canon/src/components/Grid/styles.css b/packages/canon/src/components/Grid/styles.css
new file mode 100644
index 0000000000..81f8d5471d
--- /dev/null
+++ b/packages/canon/src/components/Grid/styles.css
@@ -0,0 +1,3 @@
+.grid {
+ display: grid;
+}
diff --git a/packages/canon/src/components/Grid/types.ts b/packages/canon/src/components/Grid/types.ts
new file mode 100644
index 0000000000..6432a8312c
--- /dev/null
+++ b/packages/canon/src/components/Grid/types.ts
@@ -0,0 +1,48 @@
+/*
+ * 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 { Breakpoint, ColorProps } from '../../layout/types';
+import { SpaceProps } from '../../layout/types';
+
+export interface GridProps extends SpaceProps, ColorProps {
+ children?: React.ReactNode;
+ columns?:
+ | 1
+ | 2
+ | 3
+ | 4
+ | 5
+ | 6
+ | 7
+ | 8
+ | 9
+ | 10
+ | 11
+ | 12
+ | Partial<
+ Record
+ >;
+ className?: string;
+ style?: React.CSSProperties;
+}
+
+export interface GridItemProps {
+ children: React.ReactNode;
+ span?: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 'full';
+ start?: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 'auto';
+ end?: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 'auto';
+ className?: string;
+ style?: React.CSSProperties;
+}
diff --git a/packages/canon/src/index.ts b/packages/canon/src/index.ts
index 198a626f06..d020041e4e 100644
--- a/packages/canon/src/index.ts
+++ b/packages/canon/src/index.ts
@@ -24,3 +24,6 @@ export * from './components/Button';
export * from './components/Box';
export * from './components/Icon';
export * from './layout/types';
+export * from './components/Grid';
+export * from './components/Stack';
+export * from './components/Inline';
diff --git a/packages/canon/src/theme/styles.css b/packages/canon/src/theme/styles.css
index 4a31340575..65aaabe909 100644
--- a/packages/canon/src/theme/styles.css
+++ b/packages/canon/src/theme/styles.css
@@ -25,3 +25,4 @@
@import '../components/Button/styles.css';
@import '../components/Stack/styles.css';
@import '../components/Inline/styles.css';
+@import '../components/Grid/styles.css';