diff --git a/microsite-canon/app/globals.css b/microsite-canon/app/globals.css index adeee8bc02..797a7d73cd 100644 --- a/microsite-canon/app/globals.css +++ b/microsite-canon/app/globals.css @@ -3,6 +3,7 @@ body { flex-direction: row; background-color: var(--canon-background); color: var(--canon-text-primary); + transition: background-color 0.2s ease-in-out; } iframe { @@ -10,26 +11,22 @@ iframe { width: 100%; } -pre { - border-radius: 0.5rem; - border: 1px solid var(--canon-border-base); - position: relative; - padding: 16px 20px; - background: transparent; - overflow-x: auto; - font-family: var(--canon-font-monospace); +.shiki { + margin: 0; } -pre code span { +.shiki, +.shiki span { + background-color: var(--canon-surface-1) !important; font-family: var(--canon-font-monospace); font-size: 0.875rem; line-height: 1.7; + transition: background-color 0.2s ease-in-out; } [data-theme='dark'] .shiki, [data-theme='dark'] .shiki span { color: var(--shiki-dark) !important; - background-color: var(--shiki-dark-bg) !important; /* Optional, if you also want font styles */ font-style: var(--shiki-dark-font-style) !important; font-weight: var(--shiki-dark-font-weight) !important; diff --git a/microsite-canon/app/layout.tsx b/microsite-canon/app/layout.tsx index 8fa3b306a6..4694e187dc 100644 --- a/microsite-canon/app/layout.tsx +++ b/microsite-canon/app/layout.tsx @@ -1,11 +1,12 @@ import type { Metadata } from 'next'; import { Sidebar } from '../components/Sidebar'; import './globals.css'; -import { CanonProvider } from '@backstage/canon'; import '../../packages/canon/src/css/core.css'; import '../../packages/canon/src/css/components.css'; import styles from './page.module.css'; +import { Global } from '@/components/Global'; +import { Providers } from './providers'; export const metadata: Metadata = { title: 'Canon', @@ -18,14 +19,14 @@ export default function RootLayout({ children: React.ReactNode; }>) { return ( - + - - <> + +
{children}
- -
+ + ); diff --git a/microsite-canon/app/providers.tsx b/microsite-canon/app/providers.tsx new file mode 100644 index 0000000000..23229653ac --- /dev/null +++ b/microsite-canon/app/providers.tsx @@ -0,0 +1,13 @@ +'use client'; + +import { ReactNode } from 'react'; +import { CanonProvider } from '@backstage/canon'; +import { ThemeProvider } from 'next-themes'; + +export const Providers = ({ children }: { children: ReactNode }) => { + return ( + + {children} + + ); +}; diff --git a/microsite-canon/components/CodeBlock/index.tsx b/microsite-canon/components/CodeBlock/index.tsx new file mode 100644 index 0000000000..1ed25902b9 --- /dev/null +++ b/microsite-canon/components/CodeBlock/index.tsx @@ -0,0 +1,28 @@ +import { ReactNode } from 'react'; +import type { BundledLanguage } from 'shiki'; +import { codeToHtml } from 'shiki'; +import React from 'react'; +import styles from './styles.module.css'; + +interface CodeBlockProps { + lang?: BundledLanguage; + title?: string; + code?: string; +} + +export async function CodeBlock({ lang, title, code }: CodeBlockProps) { + const out = await codeToHtml(code || '', { + lang: lang || 'ts', + themes: { + light: 'vitesse-light', + dark: 'vitesse-dark', + }, + }); + + return ( +
+ {title &&
{title}
} +
+
+ ); +} diff --git a/microsite-canon/components/CodeBlock/styles.module.css b/microsite-canon/components/CodeBlock/styles.module.css new file mode 100644 index 0000000000..6affa1b13d --- /dev/null +++ b/microsite-canon/components/CodeBlock/styles.module.css @@ -0,0 +1,26 @@ +.codeBlock { + border-radius: 0.5rem; + border: 1px solid var(--canon-border-base); + position: relative; + background: transparent; + overflow-x: auto; + font-family: var(--canon-font-monospace); + background-color: #fff; + transition: background-color 0.2s ease-in-out; + margin-bottom: 1rem; +} + +[data-theme='dark'] .codeBlock { + background-color: #121212; +} + +.title { + border-bottom: 1px solid var(--canon-border-base); + padding: 12px 20px; + font-size: 0.875rem; + color: var(--canon-text-secondary); +} + +.code { + padding: 20px; +} diff --git a/microsite-canon/components/Global/index.tsx b/microsite-canon/components/Global/index.tsx new file mode 100644 index 0000000000..ab089eca25 --- /dev/null +++ b/microsite-canon/components/Global/index.tsx @@ -0,0 +1,9 @@ +'use client'; + +import { useEffect } from 'react'; +import { useSearchParams } from 'next/navigation'; +import styles from './styles.module.css'; + +export const Global = ({ children }: { children: React.ReactNode }) => { + return
{children}
; +}; diff --git a/microsite-canon/components/Global/styles.module.css b/microsite-canon/components/Global/styles.module.css new file mode 100644 index 0000000000..5cbd2d4b5e --- /dev/null +++ b/microsite-canon/components/Global/styles.module.css @@ -0,0 +1,3 @@ +.global { + width: 100%; +} diff --git a/microsite-canon/components/Sidebar/Sidebar.module.css b/microsite-canon/components/Sidebar/Sidebar.module.css index 335fd0e3cd..52d1a1ed51 100644 --- a/microsite-canon/components/Sidebar/Sidebar.module.css +++ b/microsite-canon/components/Sidebar/Sidebar.module.css @@ -9,6 +9,7 @@ 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; } .sidebar svg path { diff --git a/microsite-canon/components/Tabs/Tabs.module.css b/microsite-canon/components/Tabs/Tabs.module.css index 24e0ecc741..d70184dd58 100644 --- a/microsite-canon/components/Tabs/Tabs.module.css +++ b/microsite-canon/components/Tabs/Tabs.module.css @@ -3,6 +3,7 @@ flex: 1; width: 100%; background-color: var(--canon-surface-2); + transition: background-color 0.2s ease-in-out, color 0.2s ease-in-out; } .list { @@ -69,4 +70,5 @@ transition-property: translate, width; transition-duration: 200ms; transition-timing-function: ease-in-out; + transition: background-color 0.2s ease-in-out; } diff --git a/microsite-canon/components/Tabs/index.tsx b/microsite-canon/components/Tabs/index.tsx index 777f9eae51..6d095c1ad7 100644 --- a/microsite-canon/components/Tabs/index.tsx +++ b/microsite-canon/components/Tabs/index.tsx @@ -5,6 +5,8 @@ import { Tabs } from '@base-ui-components/react/tabs'; import { Icon } from '@backstage/canon'; import { usePathname, useSearchParams } from 'next/navigation'; import { useRouter } from 'next/navigation'; +import { useTheme } from 'next-themes'; +import { useEffect, useState } from 'react'; export const TabsVersion = () => { return ( @@ -23,21 +25,22 @@ export const TabsVersion = () => { }; export const TabsTheme = () => { - const searchParams = useSearchParams(); - const theme = searchParams.get('theme') === 'dark' ? 'dark' : 'light'; - const current = new URLSearchParams(Array.from(searchParams.entries())); - const router = useRouter(); + const { theme, setTheme } = useTheme(); const onValueChange = (value: string) => { - current.set('theme', value); - router.push(`/?${current.toString()}`); - document.documentElement.setAttribute('data-theme', value); + setTheme(value); }; + const [isClient, setIsClient] = useState(false); + + useEffect(() => { + setIsClient(true); + }, []); + return ( diff --git a/microsite-canon/content/layout.mdx b/microsite-canon/content/layout.mdx index 7fdbcbfc57..4b0622d92e 100644 --- a/microsite-canon/content/layout.mdx +++ b/microsite-canon/content/layout.mdx @@ -1,4 +1,5 @@ import { LayoutComponents } from '@/components/LayoutComponents'; +import { CodeBlock } from '@/components/CodeBlock'; # Layout @@ -19,7 +20,19 @@ that will be consistent with the rest of your Backstage instance. These components are opinionated and use TypeScript to ensure that the props you provide are the ones coming from the theme. -```tsx title=boom + + Hello World + + Project 1 + Project 2 + + +`} +/> + +```tsx Hello World diff --git a/microsite-canon/eslint.config.mjs b/microsite-canon/eslint.config.mjs index c85fb67c46..7f86eca7f3 100644 --- a/microsite-canon/eslint.config.mjs +++ b/microsite-canon/eslint.config.mjs @@ -1,6 +1,6 @@ -import { dirname } from "path"; -import { fileURLToPath } from "url"; -import { FlatCompat } from "@eslint/eslintrc"; +import { dirname } from 'path'; +import { fileURLToPath } from 'url'; +import { FlatCompat } from '@eslint/eslintrc'; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); @@ -10,7 +10,7 @@ const compat = new FlatCompat({ }); const eslintConfig = [ - ...compat.extends("next/core-web-vitals", "next/typescript"), + ...compat.extends('next/core-web-vitals', 'next/typescript'), ]; export default eslintConfig; diff --git a/microsite-canon/mdx-components.tsx b/microsite-canon/mdx-components.tsx index 21479dc4e6..b0ae6de4f3 100644 --- a/microsite-canon/mdx-components.tsx +++ b/microsite-canon/mdx-components.tsx @@ -4,6 +4,7 @@ import { ReactNode, ReactElement } from 'react'; import type { BundledLanguage } from 'shiki'; import { codeToHtml } from 'shiki'; import React from 'react'; +import { CodeBlock } from '@/components/CodeBlock'; // This file allows you to provide custom React components // to be used in MDX files. You can import and use any @@ -47,8 +48,11 @@ export function useMDXComponents(components: MDXComponents): MDXComponents {

), pre: ({ children }) => { - console.log(children); - return {children as ReactNode}; + const codeContent = React.isValidElement(children) + ? (children.props as { children: string }).children + : ''; + + return ; }, img: props => ( ; -} diff --git a/microsite-canon/package.json b/microsite-canon/package.json index 963f219ddf..856c1124de 100644 --- a/microsite-canon/package.json +++ b/microsite-canon/package.json @@ -19,6 +19,7 @@ "@next/mdx": "^15.1.3", "@types/mdx": "^2.0.13", "next": "15.1.3", + "next-themes": "^0.4.4", "react": "^19.0.0", "react-dom": "^19.0.0" }, diff --git a/microsite-canon/yarn.lock b/microsite-canon/yarn.lock index 2af0e436e7..25272cdc9e 100644 --- a/microsite-canon/yarn.lock +++ b/microsite-canon/yarn.lock @@ -1199,6 +1199,7 @@ __metadata: eslint: ^9 eslint-config-next: 15.1.3 next: 15.1.3 + next-themes: ^0.4.4 react: ^19.0.0 react-dom: ^19.0.0 shiki: ^1.24.4 @@ -3427,6 +3428,16 @@ __metadata: languageName: node linkType: hard +"next-themes@npm:^0.4.4": + version: 0.4.4 + resolution: "next-themes@npm:0.4.4" + peerDependencies: + react: ^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc + react-dom: ^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc + checksum: 9107fd68152eec5e4681e18c7ddbf17f8d4d65abc0ff3b17f6f5d8ee61d2b2106ee5f26d93942da52799214354eed3bb5d4a76a8bf2c7274e73ac09467441390 + languageName: node + linkType: hard + "next@npm:15.1.3": version: 15.1.3 resolution: "next@npm:15.1.3"