diff --git a/microsite-canon/app/components/[slug]/page.tsx b/microsite-canon/app/components/[slug]/page.tsx
index 53d2030504..499c28685c 100644
--- a/microsite-canon/app/components/[slug]/page.tsx
+++ b/microsite-canon/app/components/[slug]/page.tsx
@@ -1,4 +1,4 @@
-import { components } from '@/utils/data';
+import { components, layoutComponents } from '@/utils/data';
import { notFound } from 'next/navigation';
import fs from 'fs';
import path from 'path';
@@ -24,7 +24,9 @@ export default async function Page({
}
export function generateStaticParams() {
- return components.map(component => ({ slug: component.slug }));
+ return [...components, ...layoutComponents].map(component => ({
+ slug: component.slug,
+ }));
}
export const dynamicParams = false;
diff --git a/microsite-canon/app/playground/page.tsx b/microsite-canon/app/playground/page.tsx
index beeb015e65..4bb59426ce 100644
--- a/microsite-canon/app/playground/page.tsx
+++ b/microsite-canon/app/playground/page.tsx
@@ -1,3 +1,3 @@
export default function PlaygroundPage() {
- return
Playground
;
+ return ;
}
diff --git a/microsite-canon/app/playground/styles.module.css b/microsite-canon/app/playground/styles.module.css
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/microsite-canon/components/Sidebar/Sidebar.module.css b/microsite-canon/components/Sidebar/Sidebar.module.css
index a38ce6daf4..91dc8e03cc 100644
--- a/microsite-canon/components/Sidebar/Sidebar.module.css
+++ b/microsite-canon/components/Sidebar/Sidebar.module.css
@@ -10,6 +10,7 @@
padding-left: 20px;
padding-right: 20px;
transition: background-color 0.2s ease-in-out, color 0.2s ease-in-out;
+ overflow: hidden;
}
.sidebar svg path {
@@ -29,11 +30,28 @@
}
.menu {
- margin-top: 48px;
+ margin-top: 20px;
+ display: flex;
+ flex-direction: row;
+ position: relative;
}
.menu a {
- display: block;
+ display: flex;
text-decoration: none;
- height: 1.75rem;
+ height: 28px;
+ align-items: center;
+}
+
+.section {
+ width: 100%;
+}
+
+.line {
+ width: 100%;
+ display: flex;
+ flex-direction: row;
+ justify-content: space-between;
+ align-items: center;
+ height: 28px;
}
diff --git a/microsite-canon/components/Sidebar/docs.tsx b/microsite-canon/components/Sidebar/docs.tsx
new file mode 100644
index 0000000000..8d1e079768
--- /dev/null
+++ b/microsite-canon/components/Sidebar/docs.tsx
@@ -0,0 +1,61 @@
+'use client';
+
+import Link from 'next/link';
+import { components, coreConcepts, layoutComponents } from '@/utils/data';
+import { Box, Text } from '@backstage/canon';
+import { motion } from 'framer-motion';
+import styles from './Sidebar.module.css';
+import { usePathname } from 'next/navigation';
+
+export const Docs = () => {
+ const pathname = usePathname();
+ const isPlayground = pathname.includes('/playground');
+
+ return (
+
+
+
+ Core Concepts
+
+
+ {coreConcepts.map(concept => (
+
+ {concept.title}
+
+ ))}
+
+
+ Layout Components
+
+
+ {layoutComponents.map(component => (
+
+ {component.title}
+
+ ))}
+
+
+ Components
+
+
+ {components.map(component => (
+
+ {component.title}
+
+ ))}
+
+ );
+};
diff --git a/microsite-canon/components/Sidebar/index.tsx b/microsite-canon/components/Sidebar/index.tsx
index 126570619c..f4136886ae 100644
--- a/microsite-canon/components/Sidebar/index.tsx
+++ b/microsite-canon/components/Sidebar/index.tsx
@@ -1,11 +1,16 @@
+'use client';
+
import styles from './Sidebar.module.css';
-import Image from 'next/image';
import { TabsVersion, TabsTheme, TabsPages } from '../Tabs';
-import Link from 'next/link';
-import { components, coreConcepts } from '@/utils/data';
-import { Box, Text } from '@backstage/canon';
+import { usePathname } from 'next/navigation';
+import { AnimatePresence } from 'framer-motion';
+import { Docs } from './docs';
+import { Playground } from './playground';
export const Sidebar = () => {
+ const pathname = usePathname();
+ const isPlayground = pathname.includes('/playground');
+
return (
@@ -24,26 +29,8 @@ export const Sidebar = () => {
-
-
- Core Concepts
-
-
- {coreConcepts.map(concept => (
-
-
{concept.title}
-
- ))}
-
-
- Components
-
-
- {components.map(component => (
-
-
{component.title}
-
- ))}
+
+
diff --git a/microsite-canon/components/Sidebar/playground.tsx b/microsite-canon/components/Sidebar/playground.tsx
new file mode 100644
index 0000000000..ce4ffabcb2
--- /dev/null
+++ b/microsite-canon/components/Sidebar/playground.tsx
@@ -0,0 +1,61 @@
+import Link from 'next/link';
+import { components } from '@/utils/data';
+import { Box, Checkbox, Text } from '@backstage/canon';
+import { motion } from 'framer-motion';
+import styles from './Sidebar.module.css';
+import { usePathname } from 'next/navigation';
+
+const breakpoints = [
+ { title: 'XSmall', slug: 'xs' },
+ { title: 'Small', slug: 'sm' },
+ { title: 'Medium', slug: 'md' },
+ { title: 'Large', slug: 'lg' },
+ { title: 'XLarge', slug: 'xl' },
+ { title: 'XXLarge', slug: '2xl' },
+];
+
+export const Playground = () => {
+ const pathname = usePathname();
+ const isPlayground = pathname.includes('/playground');
+
+ return (
+
+
+
+ Components
+
+
+ {components.map(({ slug, title }) => (
+
+ {title}
+
+
+ ))}
+
+
+ Breakpoints
+
+
+ {breakpoints.map(({ title }) => (
+
+ {title}
+
+
+ ))}
+
+ );
+};
diff --git a/microsite-canon/package.json b/microsite-canon/package.json
index 1b942b48fd..d90e4524da 100644
--- a/microsite-canon/package.json
+++ b/microsite-canon/package.json
@@ -19,6 +19,7 @@
"@mdx-js/react": "^3.1.0",
"@next/mdx": "^15.1.3",
"@types/mdx": "^2.0.13",
+ "motion": "^11.15.0",
"next": "15.1.3",
"next-themes": "^0.4.4",
"react": "^19.0.0",
diff --git a/microsite-canon/utils/data.ts b/microsite-canon/utils/data.ts
index 12dbb1432c..bb4d52dec1 100644
--- a/microsite-canon/utils/data.ts
+++ b/microsite-canon/utils/data.ts
@@ -21,19 +21,11 @@ export const coreConcepts = [
},
];
-export const components = [
+export const layoutComponents = [
{
title: 'Box',
slug: 'box',
},
- {
- title: 'Button',
- slug: 'button',
- },
- {
- title: 'Checkbox',
- slug: 'checkbox',
- },
{
title: 'Container',
slug: 'container',
@@ -42,14 +34,6 @@ export const components = [
title: 'Grid',
slug: 'grid',
},
- {
- title: 'Heading',
- slug: 'heading',
- },
- {
- title: 'Icon',
- slug: 'icon',
- },
{
title: 'Inline',
slug: 'inline',
@@ -58,6 +42,25 @@ export const components = [
title: 'Stack',
slug: 'stack',
},
+];
+
+export const components = [
+ {
+ title: 'Button',
+ slug: 'button',
+ },
+ {
+ title: 'Checkbox',
+ slug: 'checkbox',
+ },
+ {
+ title: 'Heading',
+ slug: 'heading',
+ },
+ {
+ title: 'Icon',
+ slug: 'icon',
+ },
{
title: 'Table',
slug: 'table',
diff --git a/yarn.lock b/yarn.lock
index 0d9c715db8..f7b2dbb2fc 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -24544,6 +24544,7 @@ __metadata:
"@types/react-dom": ^19
eslint: ^9
eslint-config-next: 15.1.3
+ motion: ^11.15.0
next: 15.1.3
next-themes: ^0.4.4
react: ^19.0.0
@@ -30228,6 +30229,28 @@ __metadata:
languageName: node
linkType: hard
+"framer-motion@npm:^11.15.0":
+ version: 11.15.0
+ resolution: "framer-motion@npm:11.15.0"
+ dependencies:
+ motion-dom: ^11.14.3
+ motion-utils: ^11.14.3
+ tslib: ^2.4.0
+ peerDependencies:
+ "@emotion/is-prop-valid": "*"
+ react: ^18.0.0 || ^19.0.0
+ react-dom: ^18.0.0 || ^19.0.0
+ peerDependenciesMeta:
+ "@emotion/is-prop-valid":
+ optional: true
+ react:
+ optional: true
+ react-dom:
+ optional: true
+ checksum: 94ad254ca4874a9aa8eb64a721ad40e76a926489206d817ad4512b65d4506c65803714f9393309d352aa828b2272bbff8700878c92ab4d1202eab16a6efc45d7
+ languageName: node
+ linkType: hard
+
"framer-motion@npm:^6.5.1":
version: 6.5.1
resolution: "framer-motion@npm:6.5.1"
@@ -37728,6 +37751,41 @@ __metadata:
languageName: node
linkType: hard
+"motion-dom@npm:^11.14.3":
+ version: 11.14.3
+ resolution: "motion-dom@npm:11.14.3"
+ checksum: af9e6f6bfd1ff5c08ccd1138d10fb08a2f4b86e968a3eaf180cdcb80e9c8f7ed34dcedffc18c4bf99cf5b64d8938b5ba5e6b88e7f46ffe5ed7b1d4c23150cae2
+ languageName: node
+ linkType: hard
+
+"motion-utils@npm:^11.14.3":
+ version: 11.14.3
+ resolution: "motion-utils@npm:11.14.3"
+ checksum: 49ca9cd5731ce50a53b35f9d3f0b1858bcd24c6e602bfd6d64c9ec0006a1f87ee57be3872d55503233555c1fcfaf358fc4e18c6e2771d31a676f1243833352c4
+ languageName: node
+ linkType: hard
+
+"motion@npm:^11.15.0":
+ version: 11.15.0
+ resolution: "motion@npm:11.15.0"
+ dependencies:
+ framer-motion: ^11.15.0
+ tslib: ^2.4.0
+ peerDependencies:
+ "@emotion/is-prop-valid": "*"
+ react: ^18.0.0 || ^19.0.0
+ react-dom: ^18.0.0 || ^19.0.0
+ peerDependenciesMeta:
+ "@emotion/is-prop-valid":
+ optional: true
+ react:
+ optional: true
+ react-dom:
+ optional: true
+ checksum: fd8e07ce7552dfbc8bc845623b459c7d7742f2ae6d4cfe63717794a3016b6aa31687fe63da5e25d8bdb24f8894c83a1c5585e2bf8f6fdc7eb73c01d76070f590
+ languageName: node
+ linkType: hard
+
"mri@npm:1.1.4":
version: 1.1.4
resolution: "mri@npm:1.1.4"