diff --git a/canon-docs/src/app/globals.css b/canon-docs/src/app/globals.css index de791f589a..4b39735a22 100644 --- a/canon-docs/src/app/globals.css +++ b/canon-docs/src/app/globals.css @@ -4,6 +4,10 @@ body { background-color: var(--canon-background); color: var(--canon-text-primary); transition: background-color 0.2s ease-in-out; + + --docs-font: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, + 'Helvetica Neue', Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', + 'Segoe UI Symbol', 'Noto Color Emoji'; } iframe { diff --git a/canon-docs/src/app/layout.tsx b/canon-docs/src/app/layout.tsx index f51e130a67..6eb1856a1d 100644 --- a/canon-docs/src/app/layout.tsx +++ b/canon-docs/src/app/layout.tsx @@ -1,8 +1,9 @@ import type { Metadata } from 'next'; import { Sidebar } from '../components/Sidebar'; +import { Toolbar } from '@/components/Toolbar'; +import { Providers } from './providers'; import styles from './page.module.css'; -import { Providers } from './providers'; import './globals.css'; import '/public/core.css'; @@ -19,10 +20,6 @@ export default function RootLayout({ }: Readonly<{ children: React.ReactNode; }>) { - // const cookieStore = await cookies(); - // const theme = cookieStore.get('theme')?.value || 'light'; - // const themeName = cookieStore.get('theme-name')?.value || 'default'; - return (
-
{children}
+
+ + {children} +
diff --git a/canon-docs/src/app/opengraph-image.jpg b/canon-docs/src/app/opengraph-image.jpg new file mode 100644 index 0000000000..4dc9025ec5 Binary files /dev/null and b/canon-docs/src/app/opengraph-image.jpg differ diff --git a/canon-docs/src/app/page.module.css b/canon-docs/src/app/page.module.css index 9119d2e9c5..b0fbed71c1 100644 --- a/canon-docs/src/app/page.module.css +++ b/canon-docs/src/app/page.module.css @@ -3,10 +3,15 @@ } .container { - padding-left: 300px; width: 100%; } +@media (min-width: 768px) { + .container { + padding-left: 300px; + } +} + .page { flex: 1; } diff --git a/canon-docs/src/components/Sidebar/Sidebar.module.css b/canon-docs/src/components/Sidebar/Sidebar.module.css index 809f1b560b..870f504d28 100644 --- a/canon-docs/src/components/Sidebar/Sidebar.module.css +++ b/canon-docs/src/components/Sidebar/Sidebar.module.css @@ -1,34 +1,34 @@ .sidebar { - position: fixed; - top: 0; - left: 0; - width: 300px; - height: 100vh; - color: var(--canon-text-primary); - background-color: var(--canon-surface-1); - border-right: 1px solid var(--canon-border-base); - padding-left: 20px; - padding-right: 20px; - transition: background-color 0.2s ease-in-out, color 0.2s ease-in-out; - overflow: hidden; + display: none; +} + +@media (min-width: 768px) { + .sidebar { + display: block; + position: fixed; + top: 0; + left: 0; + width: 300px; + height: 100vh; + color: var(--canon-text-primary); + background-color: var(--canon-surface-1); + border-right: 1px solid var(--canon-border-base); + padding-left: 20px; + padding-right: 20px; + transition: background-color 0.2s ease-in-out, color 0.2s ease-in-out; + overflow: hidden; + } +} + +.logoContainer { + padding-left: 6px; + padding-top: 32px; } .logo path { fill: var(--canon-text-primary); } -.content { - padding-top: 20px; -} - -.actions { - display: flex; - flex-direction: row; - gap: 12px; - margin-top: 20px; - margin-bottom: 12px; -} - .menu { display: flex; flex-direction: row; @@ -46,6 +46,13 @@ width: 100%; } +.sectionTitle { + font-family: var(--docs-font); + font-size: var(--canon-font-size-body); + font-weight: var(--canon-font-weight-bold); + padding: 12px 0; +} + .line { width: 100%; display: flex; @@ -65,3 +72,16 @@ .line.active { background-color: var(--canon-surface-2); } + +.lineTitle { + font-family: var(--docs-font); + font-size: var(--canon-font-size-body); + font-weight: var(--canon-font-weight-regular); + color: var(--canon-text-primary); +} + +.lineStatus { + font-family: var(--docs-font); + font-size: var(--canon-font-size-body); + color: var(--canon-text-secondary); +} diff --git a/canon-docs/src/components/Sidebar/docs.tsx b/canon-docs/src/components/Sidebar/docs.tsx index 0d83b9bb0f..13c1675c20 100644 --- a/canon-docs/src/components/Sidebar/docs.tsx +++ b/canon-docs/src/components/Sidebar/docs.tsx @@ -55,9 +55,7 @@ export const Docs = () => { return ( - - {section.title} - +
{section.title}
{section.content.map(item => { const isActive = pathname === `${section.url}/${item.slug}`; @@ -68,17 +66,14 @@ export const Docs = () => { key={item.slug} className={`${styles.line} ${isActive ? styles.active : ''}`} > - {item.title} - +
{item.title}
+
{item.status === 'alpha' && 'Alpha'} {item.status === 'beta' && 'Beta'} {item.status === 'inProgress' && 'In Progress'} {item.status === 'stable' && 'Stable'} {item.status === 'deprecated' && 'Deprecated'} - +
); })} diff --git a/canon-docs/src/components/Sidebar/index.tsx b/canon-docs/src/components/Sidebar/index.tsx index ecbf157d6c..455ab4be9b 100644 --- a/canon-docs/src/components/Sidebar/index.tsx +++ b/canon-docs/src/components/Sidebar/index.tsx @@ -1,5 +1,4 @@ import styles from './Sidebar.module.css'; -import { TabsVersion, TabsPages, TabsTheme } from './tabs'; import { Docs } from './docs'; import { Playground } from './playground'; import Link from 'next/link'; @@ -7,7 +6,7 @@ import Link from 'next/link'; export const Sidebar = () => { return (
-
+
{ -
- - -
- -
- - -
+
+
+ +
); diff --git a/canon-docs/src/components/Sidebar/tabs.tsx b/canon-docs/src/components/Sidebar/tabs.tsx deleted file mode 100644 index e43f9a0422..0000000000 --- a/canon-docs/src/components/Sidebar/tabs.tsx +++ /dev/null @@ -1,103 +0,0 @@ -'use client'; - -import styles from './Tabs.module.css'; -import { Tabs } from '@base-ui-components/react/tabs'; -import { Icon } from '../../../../packages/canon/src/components/Icon'; -import { Text } from '../../../../packages/canon/src/components/Text'; -import { usePathname } from 'next/navigation'; -import { useRouter } from 'next/navigation'; -import { usePlayground } from '@/utils/playground-context'; - -export const TabsVersion = () => { - const { selectedThemeName, setSelectedThemeName } = usePlayground(); - return ( - - - - - Theme 1 - - - - - Theme 2 - - - - - - ); -}; - -export const TabsTheme = () => { - const { selectedTheme, setSelectedTheme } = usePlayground(); - - return ( - - - - - - - - - - - - ); -}; - -export const TabsPages = () => { - const pathname = usePathname(); - const router = useRouter(); - - const onValueChange = (value: string) => { - if (value === 'docs') { - router.push('/'); - } else { - router.push('/playground'); - } - }; - - return ( - - - { - router.push('/'); - }} - > - - Documentation - - - { - router.push('/playground'); - }} - > - - Playground - - - - - - ); -}; diff --git a/canon-docs/src/components/Toolbar/index.tsx b/canon-docs/src/components/Toolbar/index.tsx new file mode 100644 index 0000000000..eac51f1857 --- /dev/null +++ b/canon-docs/src/components/Toolbar/index.tsx @@ -0,0 +1,18 @@ +import { ThemeSelector } from './theme'; +import { ThemeNameSelector } from './theme-name'; +import styles from './styles.module.css'; +import { Nav } from './nav'; + +export const Toolbar = () => { + return ( +
+
+
+
+ + +
+
+ ); +}; diff --git a/canon-docs/src/components/Toolbar/nav.module.css b/canon-docs/src/components/Toolbar/nav.module.css new file mode 100644 index 0000000000..9b90f047d3 --- /dev/null +++ b/canon-docs/src/components/Toolbar/nav.module.css @@ -0,0 +1,79 @@ +.tabs { + display: none; +} + +@media (min-width: 768px) { + .tabs { + display: block; + height: 60px; + } +} + +.tabsTheme { + width: 142px; + border-radius: 0.375rem; + background-color: var(--canon-surface-2); + transition: background-color 0.2s ease-in-out, color 0.2s ease-in-out; +} + +.list { + display: flex; + position: relative; + z-index: 0; + gap: var(--canon-spacing-lg); +} + +.tab { + all: unset; + height: 60px; + font-family: var(--docs-font); + color: var(--canon-text-secondary); + font-size: var(--canon-font-size-body); + font-weight: var(--canon-font-weight-bold); + cursor: pointer; + transition: color 0.2s ease-in-out; + + &:hover { + color: var(--canon-text-primary); + } + + &[data-selected] { + color: var(--canon-text-primary); + + & p { + color: var(--canon-text-primary); + } + } + + &:focus-visible { + position: relative; + + &::before { + content: ''; + position: absolute; + inset: 0.25rem 0; + border-radius: 0.25rem; + outline: 2px solid var(--canon-surface-1); + outline-offset: -1px; + } + } +} + +.tab p { + color: var(--canon-text-secondary) !important; +} + +.indicator { + position: absolute; + z-index: -1; + left: 0; + bottom: -1px; + translate: var(--active-tab-left) -50%; + width: var(--active-tab-width); + height: 1px; + border-radius: 0.25rem; + background-color: var(--canon-text-primary); + transition-property: translate, width, background-color; + transition-duration: 200ms; + transition-timing-function: ease-in-out; +} diff --git a/canon-docs/src/components/Toolbar/nav.tsx b/canon-docs/src/components/Toolbar/nav.tsx new file mode 100644 index 0000000000..017599581e --- /dev/null +++ b/canon-docs/src/components/Toolbar/nav.tsx @@ -0,0 +1,49 @@ +'use client'; + +import { Tabs } from '@base-ui-components/react/tabs'; +import { usePathname } from 'next/navigation'; +import { useRouter } from 'next/navigation'; +import styles from './nav.module.css'; + +export const Nav = () => { + const pathname = usePathname(); + const router = useRouter(); + + const onValueChange = (value: string) => { + if (value === 'docs') { + router.push('/'); + } else { + router.push('/playground'); + } + }; + + return ( + + + { + router.push('/'); + }} + > + Documentation + + { + router.push('/playground'); + }} + > + Playground + + + + + ); +}; diff --git a/canon-docs/src/components/Toolbar/styles.module.css b/canon-docs/src/components/Toolbar/styles.module.css new file mode 100644 index 0000000000..c960dca891 --- /dev/null +++ b/canon-docs/src/components/Toolbar/styles.module.css @@ -0,0 +1,21 @@ +.toolbar { + position: sticky; + top: 0; + left: 0; + right: 0; + z-index: 10; + background-color: var(--canon-surface-1); + border-bottom: 1px solid var(--canon-border-base); + height: 60px; + display: flex; + align-items: center; + justify-content: space-between; + padding: 0 var(--canon-spacing-md); + transition: background-color 0.2s ease-in-out; +} + +.actions { + display: flex; + align-items: center; + gap: var(--canon-spacing-sm); +} diff --git a/canon-docs/src/components/Toolbar/theme-name.module.css b/canon-docs/src/components/Toolbar/theme-name.module.css new file mode 100644 index 0000000000..6f2ceb427f --- /dev/null +++ b/canon-docs/src/components/Toolbar/theme-name.module.css @@ -0,0 +1,137 @@ +.Select { + box-sizing: border-box; + display: flex; + align-items: center; + justify-content: space-between; + gap: 0.75rem; + height: 2rem; + padding-left: 0.875rem; + padding-right: 0.75rem; + margin: 0; + outline: 0; + border: 1px solid var(--color-gray-200); + border-radius: 0.375rem; + font-family: inherit; + font-size: 1rem; + line-height: 1.5rem; + color: var(--color-gray-900); + cursor: pointer; + user-select: none; + background-color: var(--canon-surface-2); + transition: background-color 0.2s ease-in-out; + + &:focus-visible { + outline: 2px solid var(--color-blue); + outline-offset: -1px; + } +} + +.SelectIcon { + display: flex; +} + +.SelectValue { + font-size: var(--canon-font-size-body); + font-weight: var(--canon-font-weight-regular); +} + +.Positioner { + z-index: 20; +} + +.Popup { + box-sizing: border-box; + padding-block: 0.25rem; + border-radius: 0.375rem; + background-color: canvas; + color: var(--color-gray-900); + border: 1px solid var(--canon-border-base); + padding-inline: 0.25rem; + transform-origin: var(--transform-origin); + transition: transform 150ms, opacity 150ms; + + &[data-starting-style], + &[data-ending-style] { + opacity: 0; + transform: scale(0.9); + } + + &[data-side='none'] { + transition: none; + transform: none; + opacity: 1; + } + + @media (prefers-color-scheme: light) { + outline: 1px solid var(--color-gray-200); + box-shadow: 0px 10px 15px -3px var(--color-gray-200), + 0px 4px 6px -4px var(--color-gray-200); + } + + @media (prefers-color-scheme: dark) { + outline: 1px solid var(--color-gray-300); + outline-offset: -1px; + } +} + +.Item { + box-sizing: border-box; + outline: 0; + font-size: 0.875rem; + line-height: 1rem; + padding-block: 0.5rem; + padding-left: 0.625rem; + padding-right: 1rem; + font-size: var(--canon-font-size-body); + font-weight: var(--canon-font-weight-bold); + display: grid; + gap: 0.5rem; + align-items: center; + grid-template-columns: 0.75rem 1fr; + cursor: default; + user-select: none; + border-radius: 0.25rem; + cursor: pointer; + transition: background-color 0.2s ease-in-out; + + [data-side='none'] & { + font-size: 1rem; + padding-right: 3rem; + min-width: calc(var(--anchor-width) + 1rem); + } + + &[data-highlighted] { + z-index: 0; + position: relative; + color: var(--color-gray-50); + background-color: var(--canon-surface-2); + } + + &[data-highlighted]::before { + content: ''; + z-index: -1; + position: absolute; + inset-block: 0; + inset-inline: 0.25rem; + border-radius: 0.25rem; + background-color: var(--color-gray-900); + } +} + +.ItemIndicator { + grid-column-start: 1; + width: 1rem; + height: 1rem; + display: flex; + align-items: center; +} + +.ItemIndicatorIcon { + display: block; + width: 0.75rem; + height: 0.75rem; +} + +.ItemText { + grid-column-start: 2; +} diff --git a/canon-docs/src/components/Toolbar/theme-name.tsx b/canon-docs/src/components/Toolbar/theme-name.tsx new file mode 100644 index 0000000000..57c858dd44 --- /dev/null +++ b/canon-docs/src/components/Toolbar/theme-name.tsx @@ -0,0 +1,48 @@ +'use client'; + +import { Select } from '@base-ui-components/react/select'; +import styles from './theme-name.module.css'; +import { Icon } from '@backstage/canon'; +import { usePlayground } from '@/utils/playground-context'; + +const themes = [ + { name: 'Backstage Legacy', value: 'legacy' }, + { name: 'Backstage Default', value: 'default' }, +]; + +export const ThemeNameSelector = () => { + const { selectedThemeName, setSelectedThemeName } = usePlayground(); + + return ( + + + + + + + + + + + {themes.map(({ name, value }) => ( + + + + + + {name} + + + ))} + + + + + ); +}; diff --git a/canon-docs/src/components/Sidebar/Tabs.module.css b/canon-docs/src/components/Toolbar/theme.module.css similarity index 83% rename from canon-docs/src/components/Sidebar/Tabs.module.css rename to canon-docs/src/components/Toolbar/theme.module.css index e74b47853a..2d17ffbac2 100644 --- a/canon-docs/src/components/Sidebar/Tabs.module.css +++ b/canon-docs/src/components/Toolbar/theme.module.css @@ -6,7 +6,7 @@ } .tabsTheme { - width: 142px; + width: 100px; border-radius: 0.375rem; background-color: var(--canon-surface-2); transition: background-color 0.2s ease-in-out, color 0.2s ease-in-out; @@ -29,21 +29,17 @@ outline: 0; background: none; appearance: none; - color: var(--canon-text-secondary) !important; - font-family: inherit; - font-size: 0.75rem; - line-height: 1.25rem; - font-weight: 500; + color: var(--canon-text-secondary); user-select: none; - height: 2.25rem; + height: 2rem; flex: 1; cursor: pointer; &[data-selected] { - color: var(--canon-text-primary) !important; + color: var(--canon-text-primary); & p { - color: var(--canon-text-primary) !important; + color: var(--canon-text-primary); } } @@ -78,7 +74,7 @@ top: 50%; translate: var(--active-tab-left) -50%; width: var(--active-tab-width); - height: 1.8rem; + height: 1.5rem; border-radius: 0.25rem; background-color: var(--canon-surface-1); transition-property: translate, width, background-color; diff --git a/canon-docs/src/components/Toolbar/theme.tsx b/canon-docs/src/components/Toolbar/theme.tsx new file mode 100644 index 0000000000..94224048ea --- /dev/null +++ b/canon-docs/src/components/Toolbar/theme.tsx @@ -0,0 +1,28 @@ +'use client'; + +import { Tabs } from '@base-ui-components/react/tabs'; +import { Icon } from '@backstage/canon'; +import { usePlayground } from '@/utils/playground-context'; +import styles from './theme.module.css'; + +export const ThemeSelector = () => { + const { selectedTheme, setSelectedTheme } = usePlayground(); + + return ( + + + + + + + + + + + + ); +}; diff --git a/canon-docs/src/mdx-components.tsx b/canon-docs/src/mdx-components.tsx index 84239d2995..308356bc67 100644 --- a/canon-docs/src/mdx-components.tsx +++ b/canon-docs/src/mdx-components.tsx @@ -1,33 +1,64 @@ +import React, { ReactNode } from 'react'; import type { MDXComponents } from 'mdx/types'; import Image, { ImageProps } from 'next/image'; -import { ReactNode } from 'react'; -import React from 'react'; import { CodeBlock } from '@/components/CodeBlock'; import { Heading } from '../../packages/canon/src/components/Heading'; -import { Text } from '../../packages/canon/src/components/Text'; import { Box } from '../../packages/canon/src/components/Box'; export function useMDXComponents(components: MDXComponents): MDXComponents { return { // Allows customizing built-in components, e.g. to add styling. h1: ({ children }) => ( - - {children as ReactNode} + +

+ {children as ReactNode} +

), h2: ({ children }) => ( - {children as ReactNode} +

+ {children as ReactNode} +

), h3: ({ children }) => ( - {children as ReactNode} +

+ {children as ReactNode} +

), p: ({ children }) => ( - {children as ReactNode} +

+ {children as ReactNode} +

), a: ({ children, href }) => ( @@ -42,6 +73,21 @@ export function useMDXComponents(components: MDXComponents): MDXComponents { return ; }, + code: ({ children }) => ( + + {children} + + ), img: props => (