From 510f9ede8b72481f2dd4f19a39626dc17e743251 Mon Sep 17 00:00:00 2001 From: Charles de Dreuille Date: Mon, 3 Nov 2025 18:13:26 +0000 Subject: [PATCH 1/5] First pass at bringing our new navigation to BUI's docs Signed-off-by: Charles de Dreuille --- docs-ui/package.json | 1 + docs-ui/src/app/about/page.mdx | 32 --- docs-ui/src/app/components/page.mdx | 173 ++++++++++++++ docs-ui/src/app/install/page.mdx | 52 ---- docs-ui/src/app/layout.module.css | 1 - docs-ui/src/app/layout/page.mdx | 42 ---- docs-ui/src/app/page.mdx | 226 +++++------------- docs-ui/src/app/responsive/page.mdx | 107 --------- docs-ui/src/app/{install => }/snippets.ts | 0 docs-ui/src/app/{theming => tokens}/page.mdx | 141 +++++++---- .../ComponentCards/ComponentCards.module.css | 4 +- .../components/CustomTheme/styles.module.css | 4 +- .../HeadlessBanners/styles.module.css | 2 +- .../LayoutComponents.module.css | 2 +- .../PropsTable/TypePopup.module.css | 4 +- .../src/components/Sidebar/Sidebar.module.css | 67 +++++- docs-ui/src/components/Sidebar/index.tsx | 147 +++++++----- .../src/components/Table/styles.module.css | 2 +- .../components/Toolbar/theme-name.module.css | 2 +- docs-ui/src/css/globals.css | 12 +- docs-ui/src/mdx-components.tsx | 2 +- docs-ui/src/utils/data.ts | 65 +---- docs-ui/src/utils/getPageName.ts | 14 +- docs-ui/yarn.lock | 8 + 24 files changed, 506 insertions(+), 604 deletions(-) delete mode 100644 docs-ui/src/app/about/page.mdx create mode 100644 docs-ui/src/app/components/page.mdx delete mode 100644 docs-ui/src/app/install/page.mdx delete mode 100644 docs-ui/src/app/layout/page.mdx delete mode 100644 docs-ui/src/app/responsive/page.mdx rename docs-ui/src/app/{install => }/snippets.ts (100%) rename docs-ui/src/app/{theming => tokens}/page.mdx (86%) diff --git a/docs-ui/package.json b/docs-ui/package.json index 17c13f64f3..41df7eb2ce 100644 --- a/docs-ui/package.json +++ b/docs-ui/package.json @@ -26,6 +26,7 @@ "@storybook/react": "^8.6.12", "@uiw/codemirror-themes": "^4.23.7", "@uiw/react-codemirror": "^4.23.7", + "clsx": "^2.1.1", "html-react-parser": "^5.2.5", "motion": "^12.4.1", "next": "15.4.7", diff --git a/docs-ui/src/app/about/page.mdx b/docs-ui/src/app/about/page.mdx deleted file mode 100644 index 2c87a3d03e..0000000000 --- a/docs-ui/src/app/about/page.mdx +++ /dev/null @@ -1,32 +0,0 @@ -# About Backstage UI - -Backstage UI is a design system created specifically for Backstage, built with React, TypeScript, and vanilla CSS. -This open-source library is hosted in the Backstage monorepo. While it can be used in other projects, Backstage UI -is designed to deliver a consistent, accessible, and extensible experience tailored to Backstage users. - -## Philosophy - -Backstage empowers product teams to build software faster and with greater quality. Its extensibility, -however, required us to rethink how to deliver a consistent and accessible user experience. Our goal is -to enable plugin creators to design plugins that seamlessly integrate with Backstage's look and feel while -still allowing customization to match individual brands. - -Instead of reinventing the wheel, we chose to focus on layout and styling while leveraging existing headless -component libraries for functionality. This approach allows us to dedicate our efforts to creating a cohesive -and flexible theming system. - -## Team - -Backstage UI is designed and maintained primarily by Spotify's Backstage team, leveraging Spotify's expertise in -crafting high-quality design and technology. Drawing from our experience in building reliable and intuitive -user experiences for the music industry, we've created a design system that looks great and works seamlessly. - -## Community - -Backstage UI is an open-source project and we welcome contributions from the community. If you are interested in -contributing to Backstage UI, please read our [contributing guide](https://github.com/backstage/backstage/blob/master/CONTRIBUTING.md) -and our [code of conduct](https://github.com/backstage/backstage/blob/master/CODE_OF_CONDUCT.md). - -## License - -Backstage UI is licensed under the Apache 2.0 license. See the [LICENSE](https://github.com/backstage/backstage/blob/master/LICENSE) file for more details. diff --git a/docs-ui/src/app/components/page.mdx b/docs-ui/src/app/components/page.mdx new file mode 100644 index 0000000000..bd18005d19 --- /dev/null +++ b/docs-ui/src/app/components/page.mdx @@ -0,0 +1,173 @@ +import { ComponentCards, ComponentCard } from '@/components/ComponentCards'; +import { LayoutComponents } from '@/components/LayoutComponents'; +import { CodeBlock } from '@/components/CodeBlock'; + +# Components + +## Layout Components + +We built a couple of layout components to help you build responsive elements +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. + + + Hello World + + Project 1 + Project 2 + + +`} +/> + + + +## Components + +### Actions + + + + + + + + +### Content display + + + + + + +### Selection and inputs + + + + + + + + + + + +### Navigation + + + + + + + + +### Images and icons + + + + + + +### Feedback indicators + + + + + + +### Typography + + + + diff --git a/docs-ui/src/app/install/page.mdx b/docs-ui/src/app/install/page.mdx deleted file mode 100644 index cf14691441..0000000000 --- a/docs-ui/src/app/install/page.mdx +++ /dev/null @@ -1,52 +0,0 @@ -import { CodeBlock } from '@/components/CodeBlock'; -import { Banner } from '@/components/Banner'; -import { snippet } from './snippets'; - -# How to install Backstage UI - -## How to import BUI's global styles - -Backstage UI works by importing a global CSS file at the root of your application. This file includes all the default styles for the components. -First, you'll need to install the package using a package manager. For example, if you're using Yarn: - - - -);`} -/> - - - -## How to use BUI components - -As a plugin maintainer, you can use BUI components in your plugin. As mentioned above, you should not import the styles -again in your plugin as this will be handled at the root of your application. To get started, just add the library to -your plugin and import the components you need. - - - - - -## Next steps - -Now that you have the basics down, you can start building your plugin using the new design system. -Please familiarise yourself first with our theming principles. This will help you understand the core concepts of the design system. -If you have any questions, please reach out to us on [Discord](https://discord.gg/MUpMjP2). diff --git a/docs-ui/src/app/layout.module.css b/docs-ui/src/app/layout.module.css index 7399fc2485..6664be49c9 100644 --- a/docs-ui/src/app/layout.module.css +++ b/docs-ui/src/app/layout.module.css @@ -9,7 +9,6 @@ margin-bottom: 48px; padding-inline: 40px; padding-block: 48px; - background-color: var(--panel); border-radius: 0.5rem; } diff --git a/docs-ui/src/app/layout/page.mdx b/docs-ui/src/app/layout/page.mdx deleted file mode 100644 index 3c3b998490..0000000000 --- a/docs-ui/src/app/layout/page.mdx +++ /dev/null @@ -1,42 +0,0 @@ -import { LayoutComponents } from '@/components/LayoutComponents'; -import { CodeBlock } from '@/components/CodeBlock'; - -# Layout - -Backstage UI is made for extensibility. We built this library to make it easy for any -Backstage plugin creator to be able to build their ideas at speed ensuring -consistency across the rest of your ecosystem. Each component is designed to -be editable to match your need but sometimes you want to have more control -over the layout of your page. To help you with that, we created a set of -layout components that you can use to build your own layouts. All of these -components are built to extend on our theming system, making it easy for you -to build your own layouts. Sometimes these components are not enough so we -created a set of helpers to be used with any CSS-in-JS library. - -## Layout Components - -We built a couple of layout components to help you build responsive elements -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. - - - Hello World - - Project 1 - Project 2 - - -`} -/> - - - -## Layout Helpers - -Sometimes you want to use global tokens dynamically outside of React -components. To help you with that we would like to provide a set of helpers -that you can use in your code. These helpers are not available just yet but we -are working on it. diff --git a/docs-ui/src/app/page.mdx b/docs-ui/src/app/page.mdx index ffd3bc9dcb..9a796b401b 100644 --- a/docs-ui/src/app/page.mdx +++ b/docs-ui/src/app/page.mdx @@ -1,179 +1,83 @@ -import { ComponentCards, ComponentCard } from '@/components/ComponentCards'; +import { CodeBlock } from '@/components/CodeBlock'; +import { Banner } from '@/components/Banner'; +import { snippet } from './snippets'; -## Welcome to Backstage UI, the new design library for Backstage plugins. +# Welcome to Backstage UI -This project is still under active development but we will make sure to document -the API as we go. We are aiming to improve the general UI of Backstage and -plugins across Backstage. This new library will take time to build but we are -building it incrementally with not conflict with the existing theming system. +Backstage UI is a design system created specifically for Backstage, built with React, TypeScript, and vanilla CSS. +This open-source library is hosted in the Backstage monorepo. While it can be used in other projects, Backstage UI +is designed to deliver a consistent, accessible, and extensible experience tailored to Backstage users. -### Actions +## Import BUI's global styles - - - - - - +Backstage UI works by importing a global CSS file at the root of your application. This file includes all the default styles for the components. +First, you'll need to install the package using a package manager. For example, if you're using Yarn: -### Layout + - - - - - - - - +);`} +/> - - - - - - - - - + -### Navigation +## Use BUI components - - - - - - +As a plugin maintainer, you can use BUI components in your plugin. As mentioned above, you should not import the styles +again in your plugin as this will be handled at the root of your application. To get started, just add the library to +your plugin and import the components you need. -### Images and icons + - - - - - -### Feedback indicators - - - - - - -### Typography - - - - + ## Support Now that you have the basics down, you can start building your plugin using the new design system. Please familiarise yourself first with our theming principles. This will help you understand the core concepts of the design system. If you have any questions, please reach out to us on [Discord](https://discord.gg/MUpMjP2). + +## Philosophy + +Backstage empowers product teams to build software faster and with greater quality. Its extensibility, +however, required us to rethink how to deliver a consistent and accessible user experience. Our goal is +to enable plugin creators to design plugins that seamlessly integrate with Backstage's look and feel while +still allowing customization to match individual brands. + +Instead of reinventing the wheel, we chose to focus on layout and styling while leveraging existing headless +component libraries for functionality. This approach allows us to dedicate our efforts to creating a cohesive +and flexible theming system. + +## Team + +Backstage UI is designed and maintained primarily by Spotify's Backstage team, leveraging Spotify's expertise in +crafting high-quality design and technology. Drawing from our experience in building reliable and intuitive +user experiences for the music industry, we've created a design system that looks great and works seamlessly. + +## Community + +Backstage UI is an open-source project and we welcome contributions from the community. If you are interested in +contributing to Backstage UI, please read our [contributing guide](https://github.com/backstage/backstage/blob/master/CONTRIBUTING.md) +and our [code of conduct](https://github.com/backstage/backstage/blob/master/CODE_OF_CONDUCT.md). + +## License + +Backstage UI is licensed under the Apache 2.0 license. See the [LICENSE](https://github.com/backstage/backstage/blob/master/LICENSE) file for more details. diff --git a/docs-ui/src/app/responsive/page.mdx b/docs-ui/src/app/responsive/page.mdx deleted file mode 100644 index 9d37b1329f..0000000000 --- a/docs-ui/src/app/responsive/page.mdx +++ /dev/null @@ -1,107 +0,0 @@ -import * as Table from '@/components/Table'; -import { Chip } from '@/components/Chip'; -import { CodeBlock } from '@/components/CodeBlock'; - -# Responsive - -Backstage UI is built on a responsive design system, meaning that the components are -designed to adapt to different screen sizes. By default we offer a set of -breakpoints that you can use to create responsive components. - -## Breakpoints - - - - - Breakpoint prefix - Minimum width - CSS - - - - - - xs - - - 0px - - - {`{ ... }`} - - - - - sm - - - 640px - - - {`@media (min-width: 640px) { ... }`} - - - - - md - - - 768px - - - {`@media (min-width: 768px) { ... }`} - - - - - lg - - - 1024px - - - {`@media (min-width: 1024px) { ... }`} - - - - - xl - - - 1280px - - - {`@media (min-width: 1280px) { ... }`} - - - - - 2xl - - - 1536px - - - {`@media (min-width: 1536px) { ... }`} - - - - - -## Responsive components - -Backstage UI components are designed to be responsive, meaning that they will adapt -to different screen sizes. Not every component is responsive, but the ones -that are will have a prop to control the responsive behavior. - -The behaviour is the same for each component. For each prop, instead of adding -the value, you add an object with the value and the breakpoint prefix. - -Button - -// Responsive value - -`} /> diff --git a/docs-ui/src/app/install/snippets.ts b/docs-ui/src/app/snippets.ts similarity index 100% rename from docs-ui/src/app/install/snippets.ts rename to docs-ui/src/app/snippets.ts diff --git a/docs-ui/src/app/theming/page.mdx b/docs-ui/src/app/tokens/page.mdx similarity index 86% rename from docs-ui/src/app/theming/page.mdx rename to docs-ui/src/app/tokens/page.mdx index 455b2cd7f5..8e9031c81c 100644 --- a/docs-ui/src/app/theming/page.mdx +++ b/docs-ui/src/app/tokens/page.mdx @@ -3,48 +3,109 @@ import * as Table from '@/components/Table'; import { Chip } from '@/components/Chip'; import { customTheme } from '@/snippets/code-snippets'; -# Theming +# Tokens -Backstage UI theming is built entirely on CSS, without relying on any CSS-in-JS libraries. -At its core, it provides a solid default theme that is easily customizable using a -comprehensive set of CSS variables. Additionally, it enables anyone to adapt the design -to their specific needs. Each component comes with fixed class names, making customization -even more straightforward. +## Responsive breakpoints -## Light & Dark modes +Backstage UI is built on a responsive design system, meaning that the components are +designed to adapt to different screen sizes. By default we offer a set of +breakpoints that you can use to create responsive components. -By default, Backstage UI supports both light and dark modes using the `data-theme-mode` attribute. -The light theme is applied by default if no `data-theme-mode` attribute is specified. To create -a custom theme, you'll need to define both light and dark modes as outlined below. If -only one mode is defined, the other will fall back to the default theme. + + + + Breakpoint prefix + Minimum width + CSS + + + + + + xs + + + 0px + + + {`{ ... }`} + + + + + sm + + + 640px + + + {`@media (min-width: 640px) { ... }`} + + + + + md + + + 768px + + + {`@media (min-width: 768px) { ... }`} + + + + + lg + + + 1024px + + + {`@media (min-width: 1024px) { ... }`} + + + + + xl + + + 1280px + + + {`@media (min-width: 1280px) { ... }`} + + + + + 2xl + + + 1536px + + + {`@media (min-width: 1536px) { ... }`} + + + + -## How to create your own theme +Backstage UI components are designed to be responsive, meaning that they will adapt +to different screen sizes. Not every component is responsive, but the ones +that are will have a prop to control the responsive behavior. -In our [started guide](/), we ask you to import two css files. The `core.css` file includes -the default set of variables. We recommend to keep this file in place and add your own theme -on top of it. `core.css` also include an opinionated reset. If you decided to remove `core.css` -you will have to provide your own reset css. +The behaviour is the same for each component. For each prop, instead of adding +the value, you add an object with the value and the breakpoint prefix. -Here's an example of how your theme.css file should look like: + + -## CSS class name structure +// Responsive value -All Backstage UI components come with a set of CSS classes that you can use to style them. To make it -easier to identify the class name you can use, we use a specific structure for the class names. +`} /> - - -Every component has a unique prefix `.bui-` followed by the component name. Component props -are represented using the `data-` attribute. That way, class names are easily identifiable. - -## Available CSS variables - -### Base colors +## Base colors These colors are used for special purposes like ring, scrollbar, ... @@ -123,7 +184,7 @@ These colors are used for special purposes like ring, scrollbar, ... -### Core background colors +## Core background colors These colors are used for the background of your application. We are mostly using for now a single elevated background for panels. `--bui-bg` should mostly use as the main background @@ -224,7 +285,7 @@ color of your app. -### Foreground colors +## Foreground colors Foreground colours are meant to work in pair with a background colours. Typically this would work for icons, texts, shapes, ... Use a matching name to know what foreground color to use. These colors @@ -329,7 +390,7 @@ are prefixed with `fg` to make it easier to identify. -### Border colors +## Border colors These border colors are mostly meant to be used as borders on top of any components with low contrast to help as a separator with the different background colors. @@ -391,7 +452,7 @@ low contrast to help as a separator with the different background colors. -### Special colors +## Special colors These colors are used for special purposes like ring, scrollbar, ... @@ -424,7 +485,7 @@ These colors are used for special purposes like ring, scrollbar, ... -### Font families +## Font families We have two fonts that we use across Backstage UI. The first one is the sans-serif font that we use for the body of the application. The second one is the @@ -453,7 +514,7 @@ monospace font that we use for code blocks and tables. -### Font weights +## Font weights We have two font weights that we use across Backstage UI. Regular or Bold. @@ -480,7 +541,7 @@ We have two font weights that we use across Backstage UI. Regular or Bold. -### Spacing +## Spacing We built a spacing system based on a single value `--bui-space`. This value is used to calculate the spacing for all the components. By default if you would like to @@ -619,7 +680,7 @@ tokens for pretty much each spacing properties like padding, margin, gaps, ... -### Radius +## Radius We use a radius system to make sure that the components have a consistent look and feel. diff --git a/docs-ui/src/components/ComponentCards/ComponentCards.module.css b/docs-ui/src/components/ComponentCards/ComponentCards.module.css index 688010110c..8eb9dbd3d0 100644 --- a/docs-ui/src/components/ComponentCards/ComponentCards.module.css +++ b/docs-ui/src/components/ComponentCards/ComponentCards.module.css @@ -28,7 +28,7 @@ display: flex; flex-direction: column; justify-content: flex-end; - background-color: var(--panel); + background-color: var(--bg); border-radius: 8px; border: 1px solid var(--border); padding: 16px; @@ -36,7 +36,7 @@ min-height: 120px; &:hover { - background-color: var(--panel-hover); + background-color: var(--bg-hover); } } diff --git a/docs-ui/src/components/CustomTheme/styles.module.css b/docs-ui/src/components/CustomTheme/styles.module.css index 05604c48be..dddd3ff7e4 100644 --- a/docs-ui/src/components/CustomTheme/styles.module.css +++ b/docs-ui/src/components/CustomTheme/styles.module.css @@ -5,7 +5,7 @@ right: 16px; width: 240px; height: 47px; - background-color: var(--panel); + background-color: var(--bg); border-radius: 0.375rem; border: 1px solid var(--border); display: flex; @@ -33,7 +33,7 @@ height: 46px; flex-shrink: 0; border-bottom: 1px solid var(--border); - background-color: var(--panel); + background-color: var(--bg); display: flex; justify-content: space-between; align-items: center; diff --git a/docs-ui/src/components/HeadlessBanners/styles.module.css b/docs-ui/src/components/HeadlessBanners/styles.module.css index 384327c394..c0066eccff 100644 --- a/docs-ui/src/components/HeadlessBanners/styles.module.css +++ b/docs-ui/src/components/HeadlessBanners/styles.module.css @@ -1,6 +1,6 @@ .container { display: flex; - background-color: var(--panel); + background-color: var(--bg); padding: 1.5rem; border-radius: 0.25rem; border: 1px solid var(--border); diff --git a/docs-ui/src/components/LayoutComponents/LayoutComponents.module.css b/docs-ui/src/components/LayoutComponents/LayoutComponents.module.css index a09a04fb5c..9009cdb6a4 100644 --- a/docs-ui/src/components/LayoutComponents/LayoutComponents.module.css +++ b/docs-ui/src/components/LayoutComponents/LayoutComponents.module.css @@ -19,7 +19,7 @@ & .content { flex: none; - background-color: var(--panel); + background-color: var(--bg); border: 1px solid var(--border); border-radius: 4px; width: 100%; diff --git a/docs-ui/src/components/PropsTable/TypePopup.module.css b/docs-ui/src/components/PropsTable/TypePopup.module.css index f8240566c7..b1423a7e4a 100644 --- a/docs-ui/src/components/PropsTable/TypePopup.module.css +++ b/docs-ui/src/components/PropsTable/TypePopup.module.css @@ -25,7 +25,7 @@ border: 1px solid var(--border); box-shadow: 0 8px 20px rgba(0 0 0 / 0.1); border-radius: 6px; - background: var(--panel); + background: var(--bg); color: var(--primary); outline: none; /* max-width: 400px; */ @@ -43,7 +43,7 @@ .arrow svg { display: block; - fill: var(--panel); + fill: var(--bg); stroke: var(--border); stroke-width: 1px; transform: rotate(180deg); diff --git a/docs-ui/src/components/Sidebar/Sidebar.module.css b/docs-ui/src/components/Sidebar/Sidebar.module.css index 75ac541d9f..7b13fd65fd 100644 --- a/docs-ui/src/components/Sidebar/Sidebar.module.css +++ b/docs-ui/src/components/Sidebar/Sidebar.module.css @@ -60,16 +60,59 @@ } .menu { - display: flex; - flex-direction: row; - position: relative; -} - -.section { width: 100%; display: flex; flex-direction: column; gap: 2px; + position: relative; +} + +.topNav { + & ul { + margin: 0; + padding: 0; + list-style: none; + display: flex; + flex-direction: column; + gap: 2px; + } + + & li { + margin: 0; + padding: 0; + list-style: none; + } + + & li div, + & li a { + display: flex; + align-items: center; + gap: 8px; + padding: 8px 12px; + border-radius: 4px; + cursor: pointer; + + &:hover { + background-color: var(--action); + + &[data-disabled='true'] { + background-color: transparent; + } + } + + &[data-active='true'] { + background-color: var(--action); + + &[data-disabled='true'] { + background-color: transparent; + } + } + + &[data-disabled='true'] { + opacity: 0.5; + cursor: not-allowed; + } + } } .sectionTitle { @@ -100,14 +143,14 @@ &:hover { background-color: var(--action); } -} -.line.active { - background-color: var(--action); -} + &.active { + background-color: var(--action); + } -.line.active .lineTitle { - color: var(--primary); + &.active .lineTitle { + color: var(--primary); + } } .lineTitle { diff --git a/docs-ui/src/components/Sidebar/index.tsx b/docs-ui/src/components/Sidebar/index.tsx index 59e297e26e..dd4416f205 100644 --- a/docs-ui/src/components/Sidebar/index.tsx +++ b/docs-ui/src/components/Sidebar/index.tsx @@ -1,29 +1,22 @@ 'use client'; import styles from './Sidebar.module.css'; -import { - components, - overview, - layoutComponents, - coreConcepts, -} from '@/utils/data'; +import { components, layoutComponents } from '@/utils/data'; import { ScrollArea } from '@base-ui-components/react/scroll-area'; import Link from 'next/link'; import { usePathname } from 'next/navigation'; -import { motion } from 'motion/react'; import { Fragment } from 'react'; +import clsx from 'clsx'; +import { + RiCollageLine, + RiFileHistoryLine, + RiHazeLine, + RiPaletteLine, + RiServiceLine, + RiStackLine, +} from '@remixicon/react'; const data = [ - { - title: 'Overview', - content: overview, - url: '', - }, - { - title: 'Core Concepts', - content: coreConcepts, - url: '', - }, { title: 'Layout Components', content: layoutComponents, @@ -38,7 +31,6 @@ const data = [ export const Sidebar = () => { const pathname = usePathname(); - const isPlayground = pathname.includes('/playground'); return (
@@ -46,52 +38,83 @@ export const Sidebar = () => {
- - {data.map(section => { - return ( - -
{section.title}
+ + {data.map(section => { + return ( + +
{section.title}
- {section.content.map(item => { - const isActive = - pathname === `${section.url}/${item.slug}`; + {section.content.map(item => { + const isActive = + pathname === `${section.url}/${item.slug}`; - return ( - -
{item.title}
-
- {item.status === 'alpha' && 'Alpha'} - {item.status === 'beta' && 'Beta'} - {item.status === 'inProgress' && 'In Progress'} - {item.status === 'stable' && 'Stable'} - {item.status === 'deprecated' && 'Deprecated'} -
- - ); - })} -
- ); - })} -
+ return ( + +
{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/docs-ui/src/components/Table/styles.module.css b/docs-ui/src/components/Table/styles.module.css index 5a5ebab450..8f38ac6979 100644 --- a/docs-ui/src/components/Table/styles.module.css +++ b/docs-ui/src/components/Table/styles.module.css @@ -17,7 +17,7 @@ padding: 12px 16px !important; border: none !important; text-align: left; - background-color: var(--panel) !important; + background-color: var(--bg) !important; font-size: 16px; & p { diff --git a/docs-ui/src/components/Toolbar/theme-name.module.css b/docs-ui/src/components/Toolbar/theme-name.module.css index 1e1943b886..56f3401d8d 100644 --- a/docs-ui/src/components/Toolbar/theme-name.module.css +++ b/docs-ui/src/components/Toolbar/theme-name.module.css @@ -29,7 +29,7 @@ box-sizing: border-box; padding-block: 0.25rem; border-radius: 0.375rem; - background-color: var(--panel); + background-color: var(--bg); color: var(--color-gray-900); border: 1px solid var(--border); padding-inline: 0.25rem; diff --git a/docs-ui/src/css/globals.css b/docs-ui/src/css/globals.css index fa1a61eb4f..be75bad36d 100644 --- a/docs-ui/src/css/globals.css +++ b/docs-ui/src/css/globals.css @@ -1,10 +1,9 @@ :root { - --bg: #f4f4f4; - --panel: #fff; - --panel-hover: #fafafa; + --bg: #ffffff; + --bg-hover: #fafafa; --primary: #000; --secondary: #929292; - --action: #fff; + --action: #f3f3f3; --link: #4f5ce0; --font-regular: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', @@ -19,9 +18,8 @@ } [data-theme-mode='dark'] { - --bg: #000; - --panel: #181818; - --panel-hover: #202020; + --bg: #000000; + --bg-hover: #202020; --primary: #fff; --secondary: #818181; --action: #202020; diff --git a/docs-ui/src/mdx-components.tsx b/docs-ui/src/mdx-components.tsx index 75dbc81df8..01a865ae3d 100644 --- a/docs-ui/src/mdx-components.tsx +++ b/docs-ui/src/mdx-components.tsx @@ -27,7 +27,7 @@ export const formattedMDXComponents: MDXComponents = { p.slug === slug); - if (overviewPage) { - return overviewPage.title; - } - - // Search in core concepts array - const coreConcept = coreConcepts.find(c => c.slug === slug); - if (coreConcept) { - return coreConcept.title; - } - // Search in components array const component = components.find(c => c.slug === slug); if (component) { diff --git a/docs-ui/yarn.lock b/docs-ui/yarn.lock index b79dea01fb..7c27ea11dd 100644 --- a/docs-ui/yarn.lock +++ b/docs-ui/yarn.lock @@ -2072,6 +2072,13 @@ __metadata: languageName: node linkType: hard +"clsx@npm:^2.1.1": + version: 2.1.1 + resolution: "clsx@npm:2.1.1" + checksum: 10/cdfb57fa6c7649bbff98d9028c2f0de2f91c86f551179541cf784b1cfdc1562dcb951955f46d54d930a3879931a980e32a46b598acaea274728dbe068deca919 + languageName: node + linkType: hard + "codemirror@npm:^6.0.0": version: 6.0.2 resolution: "codemirror@npm:6.0.2" @@ -2347,6 +2354,7 @@ __metadata: "@uiw/codemirror-themes": "npm:^4.23.7" "@uiw/react-codemirror": "npm:^4.23.7" chokidar: "npm:^3.6.0" + clsx: "npm:^2.1.1" concurrently: "npm:^8.2.2" eslint: "npm:^8" eslint-config-next: "npm:15.3.4" From f084d37b3f1d73f22889aed6fe4e3505637d4872 Mon Sep 17 00:00:00 2001 From: Charles de Dreuille Date: Tue, 4 Nov 2025 12:19:05 +0000 Subject: [PATCH 2/5] Update toolbar Signed-off-by: Charles de Dreuille --- docs-ui/src/app/layout.module.css | 15 +- docs-ui/src/app/layout.tsx | 10 +- docs-ui/src/components/PageTitle/index.tsx | 1 - .../components/{Toolbar => Sidebar}/Logo.tsx | 0 .../src/components/Sidebar/Sidebar.module.css | 17 +- .../Sidebar/{index.tsx => Sidebar.tsx} | 4 + .../src/components/Toolbar/Toolbar.module.css | 275 +++++++++++++----- docs-ui/src/components/Toolbar/Toolbar.tsx | 183 +++++++++--- .../components/Toolbar/theme-name.module.css | 110 ------- docs-ui/src/components/Toolbar/theme-name.tsx | 44 --- .../src/components/Toolbar/theme.module.css | 79 ----- docs-ui/src/components/Toolbar/theme.tsx | 28 -- docs-ui/src/utils/playground-context.tsx | 27 +- 13 files changed, 392 insertions(+), 401 deletions(-) rename docs-ui/src/components/{Toolbar => Sidebar}/Logo.tsx (100%) rename docs-ui/src/components/Sidebar/{index.tsx => Sidebar.tsx} (97%) delete mode 100644 docs-ui/src/components/Toolbar/theme-name.module.css delete mode 100644 docs-ui/src/components/Toolbar/theme-name.tsx delete mode 100644 docs-ui/src/components/Toolbar/theme.module.css delete mode 100644 docs-ui/src/components/Toolbar/theme.tsx diff --git a/docs-ui/src/app/layout.module.css b/docs-ui/src/app/layout.module.css index 6664be49c9..5c1d113a98 100644 --- a/docs-ui/src/app/layout.module.css +++ b/docs-ui/src/app/layout.module.css @@ -1,27 +1,20 @@ .container { position: relative; z-index: 20; - width: calc(100% - 64px); - margin-inline: 0 16px; - margin-left: 32px; - margin-right: 32px; - margin-top: 112px; margin-bottom: 48px; - padding-inline: 40px; - padding-block: 48px; - border-radius: 0.5rem; + padding-inline: 24px; } .content { width: 100%; - max-width: 960px; + max-width: 1200px; margin: 0 auto; } @media (min-width: 768px) { .container { - width: calc(100% - 332px - 40px); - margin-left: 332px; + width: calc(100% - 260px); + margin-left: 260px; margin-right: 40px; } } diff --git a/docs-ui/src/app/layout.tsx b/docs-ui/src/app/layout.tsx index d197112431..2bafa97d32 100644 --- a/docs-ui/src/app/layout.tsx +++ b/docs-ui/src/app/layout.tsx @@ -1,5 +1,5 @@ import type { Metadata } from 'next'; -import { Sidebar } from '../components/Sidebar'; +import { Sidebar } from '@/components/Sidebar/Sidebar'; import { Toolbar } from '@/components/Toolbar'; import { StickyHeader } from '../components/StickyHeader/StickyHeader'; import { Providers } from './providers'; @@ -51,10 +51,12 @@ export default async function RootLayout({ - - + {/* */}
-
{children}
+
+ + {children} +
diff --git a/docs-ui/src/components/PageTitle/index.tsx b/docs-ui/src/components/PageTitle/index.tsx index 540e19eb36..aef3d75d70 100644 --- a/docs-ui/src/components/PageTitle/index.tsx +++ b/docs-ui/src/components/PageTitle/index.tsx @@ -13,7 +13,6 @@ export const PageTitle = ({ }) => { return (
-
{type}
{ return (
+
+ +
diff --git a/docs-ui/src/components/Toolbar/Toolbar.module.css b/docs-ui/src/components/Toolbar/Toolbar.module.css index f0e58f2edb..4566904df5 100644 --- a/docs-ui/src/components/Toolbar/Toolbar.module.css +++ b/docs-ui/src/components/Toolbar/Toolbar.module.css @@ -1,76 +1,59 @@ .toolbar { - position: fixed; display: flex; - top: 0; - left: 32px; - right: 32px; - height: 112px; - z-index: 10; + height: 80px; align-items: center; justify-content: space-between; - padding-left: 0.25rem; -} + margin-bottom: 24px; -.left { - /* width: 296px; */ - display: flex; - align-items: center; - gap: 0.5rem; - padding-right: 20px; -} - -.right { - flex: 1; - display: flex; - align-items: center; - justify-content: flex-end; -} - -.actions { - display: none; -} - -.version { - display: none; -} - -.versionLinks { - display: flex; - align-items: center; - - a { - width: 48px; - height: 48px; - display: flex; - align-items: center; - justify-content: center; - color: var(--secondary); - transition: color 0.2s ease-in-out, background-color 0.2s ease-in-out; - border-radius: 48px; - - &:hover { - color: var(--primary); - background-color: var(--action); - } - } -} - -@media (min-width: 600px) { - .actions { - display: flex; - align-items: center; - gap: 1rem; - } -} - -@media (min-width: 768px) { - .toolbar { + @media (min-width: 768px) { right: 40px; } } +.breadcrumb { + display: flex; + align-items: center; + gap: 0.5rem; + font-size: 0.875rem; + font-weight: 500; +} + +.breadcrumbLink { + color: var(--secondary); + text-decoration: none; + cursor: pointer; + + &:hover { + color: var(--primary); + text-decoration: underline; + transition: color 0.2s ease-in-out; + text-decoration-thickness: 1px; + text-underline-offset: 4px; + } +} + +.breadcrumbSeparator { + color: var(--secondary); + flex-shrink: 0; +} + +.breadcrumbCurrent { + color: var(--primary); +} + +.actions { + display: none; + + @media (min-width: 320px) { + display: flex; + display: flex; + align-items: center; + gap: 0.5rem; + } +} + @media (min-width: 820px) { - .right { + .content { justify-content: space-between; } @@ -79,18 +62,164 @@ align-items: center; justify-content: center; /* background-color: var(--action); */ - border: 1px solid var(--border2); - border-radius: 48px; - padding-inline: 20px; - color: var(--primary); - font-size: 0.875rem; - font-weight: 500; - height: 48px; } } -@media (min-width: 960px) { - .left { - width: 296px; +.bubble { + display: flex; + align-items: center; + justify-content: center; + background-color: var(--bg); + border: 1px solid var(--border2); + border-radius: 32px; + padding-inline: 16px; + color: var(--primary); + font-size: 0.875rem; + font-weight: 500; + height: 32px; + cursor: pointer; + gap: 4px; + + &[data-selected] { + background-color: var(--action); + color: var(--primary); + } + + &:hover { + background-color: var(--action); + transition: background-color 0.2s ease-in-out; + } + + &[data-hide-tablet] { + display: none; + } + + @media (min-width: 1024px) { + &[data-hide-tablet] { + display: flex; + } + } +} + +.buttonGroup { + display: flex; + align-items: center; + gap: 4px; + border: 1px solid var(--border2); + border-radius: 32px; + padding-inline: 4px; + height: 32px; + display: none; + + @media (min-width: 480px) { + display: flex; + } +} + +.buttonGroup button { + height: 24px; + background-color: var(--bg); + border: none; + border-radius: 28px; + padding-inline: 16px; + color: var(--primary); + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + gap: 4px; + + &[data-selected] { + background-color: var(--action); + color: var(--primary); + } + + &:hover { + background-color: var(--action); + transition: background-color 0.2s ease-in-out; + } +} + +.Popup { + box-sizing: border-box; + padding-block: 0.25rem; + border-radius: 0.375rem; + background-color: var(--bg); + color: var(--color-gray-900); + border: 1px solid var(--border); + 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; + } +} + +.Popup[data-trigger='Select'] { + min-width: var(--trigger-width); + + .ListBox { + display: block; + width: unset; + max-height: inherit; + min-height: unset; + border: none; + + .react-aria-Header { + padding-left: 1.571rem; + } + } + + .Item { + position: relative; + padding: 0 0.571rem 0 1.571rem; + height: 2rem; + display: flex; + align-items: center; + + &[data-focus-visible] { + outline: none; + } + + &[data-selected] { + font-weight: 600; + background: unset; + color: var(--text-color); + + &::before { + content: '✓'; + content: '✓' / ''; + alt: ' '; + position: absolute; + left: 4px; + } + } + + &[data-focused], + &[data-pressed] { + background: var(--bg); + color: var(--primary); + cursor: pointer; + } } } diff --git a/docs-ui/src/components/Toolbar/Toolbar.tsx b/docs-ui/src/components/Toolbar/Toolbar.tsx index 29570739f8..f0399797ea 100644 --- a/docs-ui/src/components/Toolbar/Toolbar.tsx +++ b/docs-ui/src/components/Toolbar/Toolbar.tsx @@ -1,52 +1,161 @@ 'use client'; -import { RiGithubLine, RiNpmjsLine } from '@remixicon/react'; -import { motion, useScroll, useTransform } from 'framer-motion'; -import { useRef } from 'react'; -import { Logo } from './Logo'; -import { ThemeSelector } from './theme'; -import { ThemeNameSelector } from './theme-name'; +import { + RiArrowDownSLine, + RiArrowRightSLine, + RiGithubLine, + RiMoonLine, + RiSunLine, +} from '@remixicon/react'; +import { + Button, + ListBox, + ListBoxItem, + Popover, + Select, + SelectValue, + ToggleButton, + ToggleButtonGroup, +} from 'react-aria-components'; import styles from './Toolbar.module.css'; +import { Tabs } from '@base-ui-components/react/tabs'; +import { usePlayground } from '@/utils/playground-context'; +import { usePathname } from 'next/navigation'; +import Link from 'next/link'; +import { components, layoutComponents } from '@/utils/data'; interface ToolbarProps { version: string; } -export const Toolbar = ({ version }: ToolbarProps) => { - const containerRef = useRef(null); - const { scrollY } = useScroll(); +const themes = [ + { name: 'Backstage', value: 'backstage' }, + { name: 'Spotify', value: 'spotify' }, + { name: 'Custom theme', value: 'custom' }, +]; - // Transform scroll velocity to vertical movement - const y = useTransform(scrollY, [0, 100], [0, -20], { - clamp: false, - }); +export const Toolbar = ({ version }: ToolbarProps) => { + const { + selectedTheme, + setSelectedTheme, + selectedThemeName, + setSelectedThemeName, + } = usePlayground(); + + const pathname = usePathname(); + + // Determine breadcrumb content based on current path + const getBreadcrumb = () => { + const allComponents = [...components, ...layoutComponents]; + + // Root page + if (pathname === '/') { + return { section: null, title: 'Getting Started' }; + } + + // Components index page + if (pathname === '/components') { + return { section: null, title: 'Components' }; + } + + // Component detail pages + if (pathname?.startsWith('/components/')) { + const slug = pathname.split('/components/')[1]; + const component = allComponents.find(c => c.slug === slug); + return { + section: 'Components', + sectionLink: '/components', + title: component?.title || slug, + }; + } + + // Tokens page + if (pathname === '/tokens') { + return { section: null, title: 'Tokens' }; + } + + // Changelog page + if (pathname === '/changelog') { + return { section: null, title: 'Changelog' }; + } + + return { section: null, title: '' }; + }; + + const breadcrumb = getBreadcrumb(); return ( -
-
- +
+
+ {breadcrumb.section && breadcrumb.sectionLink ? ( + <> + + {breadcrumb.section} + + + {breadcrumb.title} + + ) : ( + {breadcrumb.title} + )} +
+
+ + + Version {version} + + + + + + + + + +
- -
Version {version} - Alpha
-
- - - -
-
); }; diff --git a/docs-ui/src/components/Toolbar/theme-name.module.css b/docs-ui/src/components/Toolbar/theme-name.module.css deleted file mode 100644 index 56f3401d8d..0000000000 --- a/docs-ui/src/components/Toolbar/theme-name.module.css +++ /dev/null @@ -1,110 +0,0 @@ -.Select { - box-sizing: border-box; - display: flex; - align-items: center; - justify-content: space-between; - gap: 0.75rem; - height: 3rem; - padding-left: 1.25rem; - padding-right: 1rem; - margin: 0; - outline: 0; - border: 1px solid var(--color-gray-200); - border-radius: 3rem; - font-family: inherit; - font-size: 1rem; - line-height: 1.5rem; - color: var(--color-gray-900); - cursor: pointer; - user-select: none; - background-color: var(--action); - - &:focus-visible { - outline: 2px solid var(--color-blue); - outline-offset: -1px; - } -} - -.Popup { - box-sizing: border-box; - padding-block: 0.25rem; - border-radius: 0.375rem; - background-color: var(--bg); - color: var(--color-gray-900); - border: 1px solid var(--border); - 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; - } -} - -.Popup[data-trigger='Select'] { - min-width: var(--trigger-width); - - .ListBox { - display: block; - width: unset; - max-height: inherit; - min-height: unset; - border: none; - - .react-aria-Header { - padding-left: 1.571rem; - } - } - - .Item { - position: relative; - padding: 0 0.571rem 0 1.571rem; - height: 2rem; - display: flex; - align-items: center; - - &[data-focus-visible] { - outline: none; - } - - &[data-selected] { - font-weight: 600; - background: unset; - color: var(--text-color); - - &::before { - content: '✓'; - content: '✓' / ''; - alt: ' '; - position: absolute; - left: 4px; - } - } - - &[data-focused], - &[data-pressed] { - background: var(--bg); - color: var(--primary); - cursor: pointer; - } - } -} diff --git a/docs-ui/src/components/Toolbar/theme-name.tsx b/docs-ui/src/components/Toolbar/theme-name.tsx deleted file mode 100644 index ae9e7f9f35..0000000000 --- a/docs-ui/src/components/Toolbar/theme-name.tsx +++ /dev/null @@ -1,44 +0,0 @@ -'use client'; - -import { - Button, - ListBox, - ListBoxItem, - Popover, - Select, - SelectValue, -} from 'react-aria-components'; -import styles from './theme-name.module.css'; -import { usePlayground } from '@/utils/playground-context'; -import { RiArrowDownSLine } from '@remixicon/react'; - -const themes = [ - { name: 'Backstage', value: 'backstage' }, - { name: 'Spotify', value: 'spotify' }, - { name: 'Custom theme', value: 'custom' }, -]; - -export const ThemeNameSelector = () => { - const { selectedThemeName, setSelectedThemeName } = usePlayground(); - - return ( - - ); -}; diff --git a/docs-ui/src/components/Toolbar/theme.module.css b/docs-ui/src/components/Toolbar/theme.module.css deleted file mode 100644 index c5490a72d7..0000000000 --- a/docs-ui/src/components/Toolbar/theme.module.css +++ /dev/null @@ -1,79 +0,0 @@ -.tabs { - border-radius: 0.375rem; - width: 100%; -} - -.tabsTheme { - width: 100px; - border-radius: 0.375rem; -} - -.list { - display: flex; - position: relative; - z-index: 0; - gap: 0.25rem; -} - -.tab { - display: flex; - align-items: center; - justify-content: center; - border: 0; - margin: 0; - outline: 0; - background: none; - appearance: none; - color: var(--secondary); - user-select: none; - flex: 1; - cursor: pointer; - width: 3rem; - height: 3rem; - - &[data-selected] { - color: var(--primary); - - & p { - color: var(--primary); - } - } - - @media (hover: hover) { - &:hover { - color: var(--primary); - } - } - - &:focus-visible { - position: relative; - - &::before { - content: ''; - position: absolute; - inset: 0.25rem 0; - border-radius: 0.25rem; - outline: 2px solid var(--action); - outline-offset: -1px; - } - } -} - -.tab p { - color: var(--secondary) !important; -} - -.indicator { - position: absolute; - z-index: -1; - left: 0; - top: 50%; - translate: var(--active-tab-left) -50%; - width: var(--active-tab-width); - height: 3rem; - border-radius: 3rem; - background-color: var(--action); - transition-property: translate, width; - transition-duration: 200ms; - transition-timing-function: ease-in-out; -} diff --git a/docs-ui/src/components/Toolbar/theme.tsx b/docs-ui/src/components/Toolbar/theme.tsx deleted file mode 100644 index 7ece7bfe7b..0000000000 --- a/docs-ui/src/components/Toolbar/theme.tsx +++ /dev/null @@ -1,28 +0,0 @@ -'use client'; - -import { Tabs } from '@base-ui-components/react/tabs'; -import { usePlayground } from '@/utils/playground-context'; -import styles from './theme.module.css'; -import { RiMoonLine, RiSunLine } from '@remixicon/react'; - -export const ThemeSelector = () => { - const { selectedTheme, setSelectedTheme } = usePlayground(); - - return ( - - - - - - - - - - ); -}; diff --git a/docs-ui/src/utils/playground-context.tsx b/docs-ui/src/utils/playground-context.tsx index 2f8ffbaee4..401b079966 100644 --- a/docs-ui/src/utils/playground-context.tsx +++ b/docs-ui/src/utils/playground-context.tsx @@ -4,6 +4,7 @@ import { ReactNode, useState, useEffect, + Key, } from 'react'; import { components } from './data'; @@ -16,8 +17,8 @@ const PlaygroundContext = createContext<{ setSelectedScreenSizes: (screenSizes: string[]) => void; selectedComponents: string[]; setSelectedComponents: (components: string[]) => void; - selectedTheme: Theme; - setSelectedTheme: (theme: Theme) => void; + selectedTheme: Set; + setSelectedTheme: (keys: Set) => void; selectedThemeName: ThemeName; setSelectedThemeName: (themeName: ThemeName) => void; }>({ @@ -25,7 +26,7 @@ const PlaygroundContext = createContext<{ setSelectedScreenSizes: () => {}, selectedComponents: [], setSelectedComponents: () => {}, - selectedTheme: 'light', + selectedTheme: new Set(['light']), setSelectedTheme: () => {}, selectedThemeName: 'backstage', setSelectedThemeName: () => {}, @@ -40,16 +41,24 @@ export const PlaygroundProvider = ({ children }: { children: ReactNode }) => { const [selectedComponents, setSelectedComponents] = useState( components.map(component => component.slug), ); - const [selectedTheme, setSelectedTheme] = useState('light'); + const [selectedTheme, setSelectedTheme] = useState>( + new Set(['light']), + ); const [selectedThemeName, setSelectedThemeName] = useState('backstage'); // Load saved theme from localStorage after hydration useEffect(() => { if (isBrowser) { - const savedTheme = localStorage.getItem('theme-mode') as Theme; - if (savedTheme) { - setSelectedTheme(savedTheme); + const savedThemeString = localStorage.getItem('theme-mode'); + if (savedThemeString) { + // Parse the comma-separated string back into a Set + const themeArray = savedThemeString + .split(',') + .filter(Boolean) as Theme[]; + setSelectedTheme(new Set(themeArray)); + } else { + setSelectedTheme(new Set(['light'])); } } }, [isBrowser]); @@ -68,9 +77,9 @@ export const PlaygroundProvider = ({ children }: { children: ReactNode }) => { if (isBrowser) { document.documentElement.setAttribute( 'data-theme-mode', - selectedTheme || 'light', + Array.from(selectedTheme).join(','), ); - localStorage.setItem('theme-mode', selectedTheme || 'light'); + localStorage.setItem('theme-mode', Array.from(selectedTheme).join(',')); } }, [selectedTheme, isBrowser]); From 2d01298ed13b9c1dd59716a37ed772fe5ca4c0b3 Mon Sep 17 00:00:00 2001 From: Charles de Dreuille Date: Tue, 4 Nov 2025 14:11:33 +0000 Subject: [PATCH 3/5] Improve layout Signed-off-by: Charles de Dreuille --- docs-ui/src/app/components/[slug]/page.tsx | 4 +- docs-ui/src/app/layout.module.css | 28 +++- docs-ui/src/app/layout.tsx | 10 +- docs-ui/src/components/CodeBlock/index.tsx | 19 ++- .../components/CodeBlock/styles.module.css | 20 ++- .../src/components/Snippet/styles.module.css | 4 +- .../TableOfContents.module.css | 93 ++++++++++++ .../TableOfContents/TableOfContents.tsx | 132 ++++++++++++++++++ .../src/components/TableOfContents/index.ts | 1 + .../src/content/{components => }/avatar.mdx | 0 .../content/{components => }/avatar.props.ts | 0 docs-ui/src/content/{components => }/box.mdx | 0 .../src/content/{components => }/box.props.ts | 0 .../content/{components => }/button-icon.mdx | 0 .../{components => }/button-icon.props.ts | 0 .../content/{components => }/button-link.mdx | 0 .../{components => }/button-link.props.ts | 4 +- .../src/content/{components => }/button.mdx | 0 .../content/{components => }/button.props.ts | 4 +- docs-ui/src/content/{components => }/card.mdx | 0 .../content/{components => }/card.props.ts | 0 .../src/content/{components => }/checkbox.mdx | 0 .../{components => }/checkbox.props.ts | 0 .../content/{components => }/collapsible.mdx | 0 .../{components => }/collapsible.props.ts | 0 .../content/{components => }/container.mdx | 0 .../{components => }/container.props.ts | 0 .../src/content/{components => }/dialog.mdx | 0 .../content/{components => }/dialog.props.ts | 0 docs-ui/src/content/{components => }/flex.mdx | 0 .../content/{components => }/flex.props.ts | 0 docs-ui/src/content/{components => }/grid.mdx | 0 .../content/{components => }/grid.props.ts | 0 .../content/{components => }/header-page.mdx | 0 .../{components => }/header-page.props.ts | 0 .../src/content/{components => }/header.mdx | 0 .../content/{components => }/header.props.ts | 0 docs-ui/src/content/{components => }/link.mdx | 0 .../content/{components => }/link.props.ts | 0 docs-ui/src/content/{components => }/menu.mdx | 0 .../content/{components => }/menu.props.ts | 0 .../{components => }/password-field.mdx | 0 .../{components => }/password-field.props.ts | 0 .../content/{components => }/radio-group.mdx | 0 .../{components => }/radio-group.props.ts | 0 .../content/{components => }/search-field.mdx | 0 .../{components => }/search-field.props.ts | 0 .../src/content/{components => }/select.mdx | 0 .../content/{components => }/select.props.ts | 0 .../src/content/{components => }/skeleton.mdx | 0 .../{components => }/skeleton.props.ts | 0 .../src/content/{components => }/switch.mdx | 0 .../content/{components => }/switch.props.ts | 0 .../src/content/{components => }/table.mdx | 0 .../content/{components => }/table.props.ts | 2 +- docs-ui/src/content/{components => }/tabs.mdx | 0 .../content/{components => }/tabs.props.ts | 0 .../content/{components => }/tag-group.mdx | 0 .../{components => }/tag-group.props.ts | 0 .../content/{components => }/text-field.mdx | 0 .../{components => }/text-field.props.ts | 0 docs-ui/src/content/{components => }/text.mdx | 0 .../content/{components => }/text.props.ts | 0 .../src/content/{components => }/tooltip.mdx | 0 .../content/{components => }/tooltip.props.ts | 0 .../{components => }/visually-hidden.mdx | 0 .../{components => }/visually-hidden.props.ts | 0 docs-ui/src/css/globals.css | 4 + docs-ui/src/css/mdx.module.css | 24 ++++ docs-ui/src/mdx-components.tsx | 55 +++++++- docs-ui/src/utils/data.ts | 13 -- 71 files changed, 372 insertions(+), 45 deletions(-) create mode 100644 docs-ui/src/components/TableOfContents/TableOfContents.module.css create mode 100644 docs-ui/src/components/TableOfContents/TableOfContents.tsx create mode 100644 docs-ui/src/components/TableOfContents/index.ts rename docs-ui/src/content/{components => }/avatar.mdx (100%) rename docs-ui/src/content/{components => }/avatar.props.ts (100%) rename docs-ui/src/content/{components => }/box.mdx (100%) rename docs-ui/src/content/{components => }/box.props.ts (100%) rename docs-ui/src/content/{components => }/button-icon.mdx (100%) rename docs-ui/src/content/{components => }/button-icon.props.ts (100%) rename docs-ui/src/content/{components => }/button-link.mdx (100%) rename docs-ui/src/content/{components => }/button-link.props.ts (94%) rename docs-ui/src/content/{components => }/button.mdx (100%) rename docs-ui/src/content/{components => }/button.props.ts (94%) rename docs-ui/src/content/{components => }/card.mdx (100%) rename docs-ui/src/content/{components => }/card.props.ts (100%) rename docs-ui/src/content/{components => }/checkbox.mdx (100%) rename docs-ui/src/content/{components => }/checkbox.props.ts (100%) rename docs-ui/src/content/{components => }/collapsible.mdx (100%) rename docs-ui/src/content/{components => }/collapsible.props.ts (100%) rename docs-ui/src/content/{components => }/container.mdx (100%) rename docs-ui/src/content/{components => }/container.props.ts (100%) rename docs-ui/src/content/{components => }/dialog.mdx (100%) rename docs-ui/src/content/{components => }/dialog.props.ts (100%) rename docs-ui/src/content/{components => }/flex.mdx (100%) rename docs-ui/src/content/{components => }/flex.props.ts (100%) rename docs-ui/src/content/{components => }/grid.mdx (100%) rename docs-ui/src/content/{components => }/grid.props.ts (100%) rename docs-ui/src/content/{components => }/header-page.mdx (100%) rename docs-ui/src/content/{components => }/header-page.props.ts (100%) rename docs-ui/src/content/{components => }/header.mdx (100%) rename docs-ui/src/content/{components => }/header.props.ts (100%) rename docs-ui/src/content/{components => }/link.mdx (100%) rename docs-ui/src/content/{components => }/link.props.ts (100%) rename docs-ui/src/content/{components => }/menu.mdx (100%) rename docs-ui/src/content/{components => }/menu.props.ts (100%) rename docs-ui/src/content/{components => }/password-field.mdx (100%) rename docs-ui/src/content/{components => }/password-field.props.ts (100%) rename docs-ui/src/content/{components => }/radio-group.mdx (100%) rename docs-ui/src/content/{components => }/radio-group.props.ts (100%) rename docs-ui/src/content/{components => }/search-field.mdx (100%) rename docs-ui/src/content/{components => }/search-field.props.ts (100%) rename docs-ui/src/content/{components => }/select.mdx (100%) rename docs-ui/src/content/{components => }/select.props.ts (100%) rename docs-ui/src/content/{components => }/skeleton.mdx (100%) rename docs-ui/src/content/{components => }/skeleton.props.ts (100%) rename docs-ui/src/content/{components => }/switch.mdx (100%) rename docs-ui/src/content/{components => }/switch.props.ts (100%) rename docs-ui/src/content/{components => }/table.mdx (100%) rename docs-ui/src/content/{components => }/table.props.ts (99%) rename docs-ui/src/content/{components => }/tabs.mdx (100%) rename docs-ui/src/content/{components => }/tabs.props.ts (100%) rename docs-ui/src/content/{components => }/tag-group.mdx (100%) rename docs-ui/src/content/{components => }/tag-group.props.ts (100%) rename docs-ui/src/content/{components => }/text-field.mdx (100%) rename docs-ui/src/content/{components => }/text-field.props.ts (100%) rename docs-ui/src/content/{components => }/text.mdx (100%) rename docs-ui/src/content/{components => }/text.props.ts (100%) rename docs-ui/src/content/{components => }/tooltip.mdx (100%) rename docs-ui/src/content/{components => }/tooltip.props.ts (100%) rename docs-ui/src/content/{components => }/visually-hidden.mdx (100%) rename docs-ui/src/content/{components => }/visually-hidden.props.ts (100%) diff --git a/docs-ui/src/app/components/[slug]/page.tsx b/docs-ui/src/app/components/[slug]/page.tsx index d6f98c8f93..1422334471 100644 --- a/docs-ui/src/app/components/[slug]/page.tsx +++ b/docs-ui/src/app/components/[slug]/page.tsx @@ -7,9 +7,7 @@ export default async function Page({ }) { const { slug } = await params; - const { default: Component } = await import( - `@/content/components/${slug}.mdx` - ); + const { default: Component } = await import(`@/content/${slug}.mdx`); return ; } diff --git a/docs-ui/src/app/layout.module.css b/docs-ui/src/app/layout.module.css index 5c1d113a98..df89f02283 100644 --- a/docs-ui/src/app/layout.module.css +++ b/docs-ui/src/app/layout.module.css @@ -5,10 +5,28 @@ padding-inline: 24px; } -.content { +.contentWrapper { + display: flex; width: 100%; max-width: 1200px; margin: 0 auto; + justify-content: center; + flex-direction: column; +} + +.content { + display: flex; + gap: 40px; +} + +.contentInner { + flex: 1; + min-width: 0; + overflow-x: hidden; +} + +.toc { + display: none; } @media (min-width: 768px) { @@ -18,3 +36,11 @@ margin-right: 40px; } } + +@media (min-width: 1280px) { + .toc { + display: block; + width: 240px; + flex-shrink: 0; + } +} diff --git a/docs-ui/src/app/layout.tsx b/docs-ui/src/app/layout.tsx index 2bafa97d32..738a995795 100644 --- a/docs-ui/src/app/layout.tsx +++ b/docs-ui/src/app/layout.tsx @@ -4,6 +4,7 @@ import { Toolbar } from '@/components/Toolbar'; import { StickyHeader } from '../components/StickyHeader/StickyHeader'; import { Providers } from './providers'; import { CustomTheme } from '@/components/CustomTheme'; +import { TableOfContents } from '@/components/TableOfContents'; import styles from './layout.module.css'; import '../css/globals.css'; @@ -53,9 +54,14 @@ export default async function RootLayout({ {/* */}
-
+
- {children} +
+
{children}
+ +
diff --git a/docs-ui/src/components/CodeBlock/index.tsx b/docs-ui/src/components/CodeBlock/index.tsx index 96fdb6bea5..1467bca465 100644 --- a/docs-ui/src/components/CodeBlock/index.tsx +++ b/docs-ui/src/components/CodeBlock/index.tsx @@ -9,15 +9,24 @@ export interface CodeBlockProps { code?: string; } -export async function CodeBlock({ lang = 'tsx', title, code }: CodeBlockProps) { - const out = await codeToHtml(code || '', { - lang: lang, +export async function CodeBlock(props: CodeBlockProps) { + const { lang = 'tsx', title, code } = props; + let out = await codeToHtml(code || '', { + lang, + transformers: [transformerNotationDiff({ matchAlgorithm: 'v3' })], themes: { - light: 'min-light', + light: 'github-dark', dark: 'min-dark', }, - transformers: [transformerNotationDiff({ matchAlgorithm: 'v3' })], }); + // Remove background-color from the pre tag to use our theme colors + out = out.replace( + /style="([^"]*?)background-color:[^;]+;?([^"]*?)"/g, + 'style="$1$2"', + ); + // Clean up empty style attributes + out = out.replace(/style=""\s?/g, ''); + return ; } diff --git a/docs-ui/src/components/CodeBlock/styles.module.css b/docs-ui/src/components/CodeBlock/styles.module.css index d786140482..30e1837f60 100644 --- a/docs-ui/src/components/CodeBlock/styles.module.css +++ b/docs-ui/src/components/CodeBlock/styles.module.css @@ -1,33 +1,29 @@ .codeBlock { - border-radius: 4px; - border: 1px solid var(--border); + border-radius: 8px; position: relative; background: transparent; overflow-x: auto; font-family: var(--font-mono); - background-color: #fff; + background-color: var(--code-bg); margin-bottom: 1rem; } -[data-theme-mode='dark'] .codeBlock { - background-color: #121212; -} - .title { - border-bottom: 1px solid var(--border); padding: 12px 20px; font-size: 0.8125rem; - color: var(--primary); + color: #fff; font-family: var(--font-regular); font-weight: var(--font-weight-bold); + background-color: var(--code-title); } .title code { - background-color: var(--surface-1); + background-color: rgba(255, 255, 255, 0.1); padding: 0.2rem 0.375rem; border-radius: 0.25rem; - color: var(--secondary); - font-size: 0.8125rem; + color: #fff; + font-size: 0.75rem; + font-weight: var(--font-weight-bold); } .code { diff --git a/docs-ui/src/components/Snippet/styles.module.css b/docs-ui/src/components/Snippet/styles.module.css index bb0d1cbc50..3141564293 100644 --- a/docs-ui/src/components/Snippet/styles.module.css +++ b/docs-ui/src/components/Snippet/styles.module.css @@ -5,9 +5,9 @@ } .preview { - border-radius: 4px; + border-radius: 8px; box-shadow: inset 0 0 0 1px var(--border); - background-color: var(--bg); + background-color: var(--bui-bg); padding: 1px; position: relative; } diff --git a/docs-ui/src/components/TableOfContents/TableOfContents.module.css b/docs-ui/src/components/TableOfContents/TableOfContents.module.css new file mode 100644 index 0000000000..7275c24520 --- /dev/null +++ b/docs-ui/src/components/TableOfContents/TableOfContents.module.css @@ -0,0 +1,93 @@ +.container { + position: sticky; + top: 24px; + max-height: calc(100vh - 48px); + overflow-y: auto; + padding: 16px 0; +} + +.list { + list-style: none; + padding: 0; + margin: 0; + position: relative; +} + +.list::before { + content: ''; + position: absolute; + left: 0; + top: 0; + bottom: 0; + width: 2px; + background-color: var(--border); +} + +.indicator { + position: absolute; + left: 0; + width: 2px; + background-color: var(--primary); + transition: top 0.3s cubic-bezier(0.4, 0, 0.2, 1), + height 0.3s cubic-bezier(0.4, 0, 0.2, 1); + pointer-events: none; + z-index: 1; +} + +.item { + position: relative; + margin: 0; +} + +.itemNested { + padding-left: 16px; +} + +.link { + display: block; + width: 100%; + text-align: left; + background: none; + border: none; + padding: 6px 16px; + font-size: 0.875rem; + color: var(--text-secondary); + cursor: pointer; + transition: color 0.2s ease; + line-height: 1.4; + position: relative; +} + +.link:hover { + color: var(--text); +} + +.itemActive .link { + color: var(--primary); + font-weight: 500; +} + +/* Hide scrollbar for webkit browsers */ +.container::-webkit-scrollbar { + width: 4px; +} + +.container::-webkit-scrollbar-track { + background: transparent; +} + +.container::-webkit-scrollbar-thumb { + background: var(--border); + border-radius: 2px; +} + +.container::-webkit-scrollbar-thumb:hover { + background: var(--text-secondary); +} + +/* Responsive: hide on smaller screens */ +@media (max-width: 1280px) { + .container { + display: none; + } +} diff --git a/docs-ui/src/components/TableOfContents/TableOfContents.tsx b/docs-ui/src/components/TableOfContents/TableOfContents.tsx new file mode 100644 index 0000000000..d18265415b --- /dev/null +++ b/docs-ui/src/components/TableOfContents/TableOfContents.tsx @@ -0,0 +1,132 @@ +'use client'; + +import { useEffect, useState } from 'react'; +import { usePathname } from 'next/navigation'; +import styles from './TableOfContents.module.css'; + +interface Heading { + id: string; + text: string; + level: number; +} + +export function TableOfContents() { + const [headings, setHeadings] = useState([]); + const [activeId, setActiveId] = useState(''); + const [indicatorTop, setIndicatorTop] = useState(0); + const [indicatorHeight, setIndicatorHeight] = useState(0); + const pathname = usePathname(); + + useEffect(() => { + // Extract all H2 and H3 headings from the document + const elements = Array.from( + document.querySelectorAll('h2[id], h3[id]'), + ) as HTMLHeadingElement[]; + + const headingData: Heading[] = elements.map(element => ({ + id: element.id, + text: (element.textContent || '').replace('#', '').trim(), + level: parseInt(element.tagName.substring(1)), + })); + + setHeadings(headingData); + + // Set initial active heading (first visible heading or first heading) + if (headingData.length > 0) { + const viewportTop = window.scrollY + 100; // offset for header + const visibleHeading = elements.find(element => { + const rect = element.getBoundingClientRect(); + return rect.top + window.scrollY >= viewportTop - 200; + }); + setActiveId(visibleHeading?.id || headingData[0].id); + } + + // Set up IntersectionObserver to track visible headings + const observerOptions = { + rootMargin: '-80px 0px -80% 0px', + threshold: 1, + }; + + const observerCallback = (entries: IntersectionObserverEntry[]) => { + entries.forEach(entry => { + if (entry.isIntersecting) { + setActiveId(entry.target.id); + } + }); + }; + + const observer = new IntersectionObserver( + observerCallback, + observerOptions, + ); + + elements.forEach(element => observer.observe(element)); + + return () => { + elements.forEach(element => observer.unobserve(element)); + }; + }, [pathname]); + + // Update indicator position when activeId changes + useEffect(() => { + if (activeId) { + const activeElement = document.querySelector( + `[data-toc-id="${activeId}"]`, + ) as HTMLElement; + if (activeElement) { + const list = activeElement.closest('ul'); + if (list) { + const listRect = list.getBoundingClientRect(); + const elementRect = activeElement.getBoundingClientRect(); + setIndicatorTop(elementRect.top - listRect.top); + setIndicatorHeight(elementRect.height); + } + } + } + }, [activeId, headings]); + + const handleClick = (id: string) => { + const element = document.getElementById(id); + if (element) { + element.scrollIntoView({ behavior: 'smooth', block: 'start' }); + // Update URL without scrolling (already handled above) + window.history.pushState(null, '', `#${id}`); + } + }; + + if (headings.length === 0) { + return null; + } + + return ( + + ); +} diff --git a/docs-ui/src/components/TableOfContents/index.ts b/docs-ui/src/components/TableOfContents/index.ts new file mode 100644 index 0000000000..ffe4593ca7 --- /dev/null +++ b/docs-ui/src/components/TableOfContents/index.ts @@ -0,0 +1 @@ +export { TableOfContents } from './TableOfContents'; diff --git a/docs-ui/src/content/components/avatar.mdx b/docs-ui/src/content/avatar.mdx similarity index 100% rename from docs-ui/src/content/components/avatar.mdx rename to docs-ui/src/content/avatar.mdx diff --git a/docs-ui/src/content/components/avatar.props.ts b/docs-ui/src/content/avatar.props.ts similarity index 100% rename from docs-ui/src/content/components/avatar.props.ts rename to docs-ui/src/content/avatar.props.ts diff --git a/docs-ui/src/content/components/box.mdx b/docs-ui/src/content/box.mdx similarity index 100% rename from docs-ui/src/content/components/box.mdx rename to docs-ui/src/content/box.mdx diff --git a/docs-ui/src/content/components/box.props.ts b/docs-ui/src/content/box.props.ts similarity index 100% rename from docs-ui/src/content/components/box.props.ts rename to docs-ui/src/content/box.props.ts diff --git a/docs-ui/src/content/components/button-icon.mdx b/docs-ui/src/content/button-icon.mdx similarity index 100% rename from docs-ui/src/content/components/button-icon.mdx rename to docs-ui/src/content/button-icon.mdx diff --git a/docs-ui/src/content/components/button-icon.props.ts b/docs-ui/src/content/button-icon.props.ts similarity index 100% rename from docs-ui/src/content/components/button-icon.props.ts rename to docs-ui/src/content/button-icon.props.ts diff --git a/docs-ui/src/content/components/button-link.mdx b/docs-ui/src/content/button-link.mdx similarity index 100% rename from docs-ui/src/content/components/button-link.mdx rename to docs-ui/src/content/button-link.mdx diff --git a/docs-ui/src/content/components/button-link.props.ts b/docs-ui/src/content/button-link.props.ts similarity index 94% rename from docs-ui/src/content/components/button-link.props.ts rename to docs-ui/src/content/button-link.props.ts index 758d6fd811..2bd4e2196a 100644 --- a/docs-ui/src/content/components/button-link.props.ts +++ b/docs-ui/src/content/button-link.props.ts @@ -1,5 +1,5 @@ -import { classNamePropDefs, stylePropDefs } from '../../utils/propDefs'; -import type { PropDef } from '../../utils/propDefs'; +import { classNamePropDefs, stylePropDefs } from '@/utils/propDefs'; +import type { PropDef } from '@/utils/propDefs'; export const buttonLinkPropDefs: Record = { variant: { diff --git a/docs-ui/src/content/components/button.mdx b/docs-ui/src/content/button.mdx similarity index 100% rename from docs-ui/src/content/components/button.mdx rename to docs-ui/src/content/button.mdx diff --git a/docs-ui/src/content/components/button.props.ts b/docs-ui/src/content/button.props.ts similarity index 94% rename from docs-ui/src/content/components/button.props.ts rename to docs-ui/src/content/button.props.ts index 4647c20956..a3bb2659bf 100644 --- a/docs-ui/src/content/components/button.props.ts +++ b/docs-ui/src/content/button.props.ts @@ -1,5 +1,5 @@ -import { classNamePropDefs, stylePropDefs } from '../../utils/propDefs'; -import type { PropDef } from '../../utils/propDefs'; +import { classNamePropDefs, stylePropDefs } from '@/utils/propDefs'; +import type { PropDef } from '@/utils/propDefs'; export const buttonPropDefs: Record = { variant: { diff --git a/docs-ui/src/content/components/card.mdx b/docs-ui/src/content/card.mdx similarity index 100% rename from docs-ui/src/content/components/card.mdx rename to docs-ui/src/content/card.mdx diff --git a/docs-ui/src/content/components/card.props.ts b/docs-ui/src/content/card.props.ts similarity index 100% rename from docs-ui/src/content/components/card.props.ts rename to docs-ui/src/content/card.props.ts diff --git a/docs-ui/src/content/components/checkbox.mdx b/docs-ui/src/content/checkbox.mdx similarity index 100% rename from docs-ui/src/content/components/checkbox.mdx rename to docs-ui/src/content/checkbox.mdx diff --git a/docs-ui/src/content/components/checkbox.props.ts b/docs-ui/src/content/checkbox.props.ts similarity index 100% rename from docs-ui/src/content/components/checkbox.props.ts rename to docs-ui/src/content/checkbox.props.ts diff --git a/docs-ui/src/content/components/collapsible.mdx b/docs-ui/src/content/collapsible.mdx similarity index 100% rename from docs-ui/src/content/components/collapsible.mdx rename to docs-ui/src/content/collapsible.mdx diff --git a/docs-ui/src/content/components/collapsible.props.ts b/docs-ui/src/content/collapsible.props.ts similarity index 100% rename from docs-ui/src/content/components/collapsible.props.ts rename to docs-ui/src/content/collapsible.props.ts diff --git a/docs-ui/src/content/components/container.mdx b/docs-ui/src/content/container.mdx similarity index 100% rename from docs-ui/src/content/components/container.mdx rename to docs-ui/src/content/container.mdx diff --git a/docs-ui/src/content/components/container.props.ts b/docs-ui/src/content/container.props.ts similarity index 100% rename from docs-ui/src/content/components/container.props.ts rename to docs-ui/src/content/container.props.ts diff --git a/docs-ui/src/content/components/dialog.mdx b/docs-ui/src/content/dialog.mdx similarity index 100% rename from docs-ui/src/content/components/dialog.mdx rename to docs-ui/src/content/dialog.mdx diff --git a/docs-ui/src/content/components/dialog.props.ts b/docs-ui/src/content/dialog.props.ts similarity index 100% rename from docs-ui/src/content/components/dialog.props.ts rename to docs-ui/src/content/dialog.props.ts diff --git a/docs-ui/src/content/components/flex.mdx b/docs-ui/src/content/flex.mdx similarity index 100% rename from docs-ui/src/content/components/flex.mdx rename to docs-ui/src/content/flex.mdx diff --git a/docs-ui/src/content/components/flex.props.ts b/docs-ui/src/content/flex.props.ts similarity index 100% rename from docs-ui/src/content/components/flex.props.ts rename to docs-ui/src/content/flex.props.ts diff --git a/docs-ui/src/content/components/grid.mdx b/docs-ui/src/content/grid.mdx similarity index 100% rename from docs-ui/src/content/components/grid.mdx rename to docs-ui/src/content/grid.mdx diff --git a/docs-ui/src/content/components/grid.props.ts b/docs-ui/src/content/grid.props.ts similarity index 100% rename from docs-ui/src/content/components/grid.props.ts rename to docs-ui/src/content/grid.props.ts diff --git a/docs-ui/src/content/components/header-page.mdx b/docs-ui/src/content/header-page.mdx similarity index 100% rename from docs-ui/src/content/components/header-page.mdx rename to docs-ui/src/content/header-page.mdx diff --git a/docs-ui/src/content/components/header-page.props.ts b/docs-ui/src/content/header-page.props.ts similarity index 100% rename from docs-ui/src/content/components/header-page.props.ts rename to docs-ui/src/content/header-page.props.ts diff --git a/docs-ui/src/content/components/header.mdx b/docs-ui/src/content/header.mdx similarity index 100% rename from docs-ui/src/content/components/header.mdx rename to docs-ui/src/content/header.mdx diff --git a/docs-ui/src/content/components/header.props.ts b/docs-ui/src/content/header.props.ts similarity index 100% rename from docs-ui/src/content/components/header.props.ts rename to docs-ui/src/content/header.props.ts diff --git a/docs-ui/src/content/components/link.mdx b/docs-ui/src/content/link.mdx similarity index 100% rename from docs-ui/src/content/components/link.mdx rename to docs-ui/src/content/link.mdx diff --git a/docs-ui/src/content/components/link.props.ts b/docs-ui/src/content/link.props.ts similarity index 100% rename from docs-ui/src/content/components/link.props.ts rename to docs-ui/src/content/link.props.ts diff --git a/docs-ui/src/content/components/menu.mdx b/docs-ui/src/content/menu.mdx similarity index 100% rename from docs-ui/src/content/components/menu.mdx rename to docs-ui/src/content/menu.mdx diff --git a/docs-ui/src/content/components/menu.props.ts b/docs-ui/src/content/menu.props.ts similarity index 100% rename from docs-ui/src/content/components/menu.props.ts rename to docs-ui/src/content/menu.props.ts diff --git a/docs-ui/src/content/components/password-field.mdx b/docs-ui/src/content/password-field.mdx similarity index 100% rename from docs-ui/src/content/components/password-field.mdx rename to docs-ui/src/content/password-field.mdx diff --git a/docs-ui/src/content/components/password-field.props.ts b/docs-ui/src/content/password-field.props.ts similarity index 100% rename from docs-ui/src/content/components/password-field.props.ts rename to docs-ui/src/content/password-field.props.ts diff --git a/docs-ui/src/content/components/radio-group.mdx b/docs-ui/src/content/radio-group.mdx similarity index 100% rename from docs-ui/src/content/components/radio-group.mdx rename to docs-ui/src/content/radio-group.mdx diff --git a/docs-ui/src/content/components/radio-group.props.ts b/docs-ui/src/content/radio-group.props.ts similarity index 100% rename from docs-ui/src/content/components/radio-group.props.ts rename to docs-ui/src/content/radio-group.props.ts diff --git a/docs-ui/src/content/components/search-field.mdx b/docs-ui/src/content/search-field.mdx similarity index 100% rename from docs-ui/src/content/components/search-field.mdx rename to docs-ui/src/content/search-field.mdx diff --git a/docs-ui/src/content/components/search-field.props.ts b/docs-ui/src/content/search-field.props.ts similarity index 100% rename from docs-ui/src/content/components/search-field.props.ts rename to docs-ui/src/content/search-field.props.ts diff --git a/docs-ui/src/content/components/select.mdx b/docs-ui/src/content/select.mdx similarity index 100% rename from docs-ui/src/content/components/select.mdx rename to docs-ui/src/content/select.mdx diff --git a/docs-ui/src/content/components/select.props.ts b/docs-ui/src/content/select.props.ts similarity index 100% rename from docs-ui/src/content/components/select.props.ts rename to docs-ui/src/content/select.props.ts diff --git a/docs-ui/src/content/components/skeleton.mdx b/docs-ui/src/content/skeleton.mdx similarity index 100% rename from docs-ui/src/content/components/skeleton.mdx rename to docs-ui/src/content/skeleton.mdx diff --git a/docs-ui/src/content/components/skeleton.props.ts b/docs-ui/src/content/skeleton.props.ts similarity index 100% rename from docs-ui/src/content/components/skeleton.props.ts rename to docs-ui/src/content/skeleton.props.ts diff --git a/docs-ui/src/content/components/switch.mdx b/docs-ui/src/content/switch.mdx similarity index 100% rename from docs-ui/src/content/components/switch.mdx rename to docs-ui/src/content/switch.mdx diff --git a/docs-ui/src/content/components/switch.props.ts b/docs-ui/src/content/switch.props.ts similarity index 100% rename from docs-ui/src/content/components/switch.props.ts rename to docs-ui/src/content/switch.props.ts diff --git a/docs-ui/src/content/components/table.mdx b/docs-ui/src/content/table.mdx similarity index 100% rename from docs-ui/src/content/components/table.mdx rename to docs-ui/src/content/table.mdx diff --git a/docs-ui/src/content/components/table.props.ts b/docs-ui/src/content/table.props.ts similarity index 99% rename from docs-ui/src/content/components/table.props.ts rename to docs-ui/src/content/table.props.ts index d46c6b8ea7..443526182e 100644 --- a/docs-ui/src/content/components/table.props.ts +++ b/docs-ui/src/content/table.props.ts @@ -2,7 +2,7 @@ import { classNamePropDefs, stylePropDefs, type PropDef, -} from '../../utils/propDefs'; +} from '@/utils/propDefs'; export const tablePropDefs: Record = { selectionBehavior: { diff --git a/docs-ui/src/content/components/tabs.mdx b/docs-ui/src/content/tabs.mdx similarity index 100% rename from docs-ui/src/content/components/tabs.mdx rename to docs-ui/src/content/tabs.mdx diff --git a/docs-ui/src/content/components/tabs.props.ts b/docs-ui/src/content/tabs.props.ts similarity index 100% rename from docs-ui/src/content/components/tabs.props.ts rename to docs-ui/src/content/tabs.props.ts diff --git a/docs-ui/src/content/components/tag-group.mdx b/docs-ui/src/content/tag-group.mdx similarity index 100% rename from docs-ui/src/content/components/tag-group.mdx rename to docs-ui/src/content/tag-group.mdx diff --git a/docs-ui/src/content/components/tag-group.props.ts b/docs-ui/src/content/tag-group.props.ts similarity index 100% rename from docs-ui/src/content/components/tag-group.props.ts rename to docs-ui/src/content/tag-group.props.ts diff --git a/docs-ui/src/content/components/text-field.mdx b/docs-ui/src/content/text-field.mdx similarity index 100% rename from docs-ui/src/content/components/text-field.mdx rename to docs-ui/src/content/text-field.mdx diff --git a/docs-ui/src/content/components/text-field.props.ts b/docs-ui/src/content/text-field.props.ts similarity index 100% rename from docs-ui/src/content/components/text-field.props.ts rename to docs-ui/src/content/text-field.props.ts diff --git a/docs-ui/src/content/components/text.mdx b/docs-ui/src/content/text.mdx similarity index 100% rename from docs-ui/src/content/components/text.mdx rename to docs-ui/src/content/text.mdx diff --git a/docs-ui/src/content/components/text.props.ts b/docs-ui/src/content/text.props.ts similarity index 100% rename from docs-ui/src/content/components/text.props.ts rename to docs-ui/src/content/text.props.ts diff --git a/docs-ui/src/content/components/tooltip.mdx b/docs-ui/src/content/tooltip.mdx similarity index 100% rename from docs-ui/src/content/components/tooltip.mdx rename to docs-ui/src/content/tooltip.mdx diff --git a/docs-ui/src/content/components/tooltip.props.ts b/docs-ui/src/content/tooltip.props.ts similarity index 100% rename from docs-ui/src/content/components/tooltip.props.ts rename to docs-ui/src/content/tooltip.props.ts diff --git a/docs-ui/src/content/components/visually-hidden.mdx b/docs-ui/src/content/visually-hidden.mdx similarity index 100% rename from docs-ui/src/content/components/visually-hidden.mdx rename to docs-ui/src/content/visually-hidden.mdx diff --git a/docs-ui/src/content/components/visually-hidden.props.ts b/docs-ui/src/content/visually-hidden.props.ts similarity index 100% rename from docs-ui/src/content/components/visually-hidden.props.ts rename to docs-ui/src/content/visually-hidden.props.ts diff --git a/docs-ui/src/css/globals.css b/docs-ui/src/css/globals.css index be75bad36d..dcc348a9ee 100644 --- a/docs-ui/src/css/globals.css +++ b/docs-ui/src/css/globals.css @@ -15,11 +15,15 @@ --border: #e5e5e5; --border2: #cdcdcd; --surface-1: #f4f4f4; + --code-bg: #3e444f; + --code-title: #505865; } [data-theme-mode='dark'] { --bg: #000000; --bg-hover: #202020; + --code-bg: #202020; + --code-title: #292929; --primary: #fff; --secondary: #818181; --action: #202020; diff --git a/docs-ui/src/css/mdx.module.css b/docs-ui/src/css/mdx.module.css index 6a65995003..7cc2422ecd 100644 --- a/docs-ui/src/css/mdx.module.css +++ b/docs-ui/src/css/mdx.module.css @@ -49,3 +49,27 @@ .a:hover { text-decoration: underline; } + +.headingWithAnchor { + position: relative; + scroll-margin-top: 80px; +} + +.anchorLink { + color: inherit; + text-decoration: none; + display: inline-flex; + align-items: center; + gap: 0.5rem; +} + +.anchorHash { + opacity: 0; + transition: opacity 0.2s; + font-size: 0.8em; + color: var(--text-secondary); +} + +.anchorLink:hover .anchorHash { + opacity: 1; +} diff --git a/docs-ui/src/mdx-components.tsx b/docs-ui/src/mdx-components.tsx index 01a865ae3d..3e3efbe1ee 100644 --- a/docs-ui/src/mdx-components.tsx +++ b/docs-ui/src/mdx-components.tsx @@ -4,10 +4,61 @@ import Image, { ImageProps } from 'next/image'; import { CodeBlock } from '@/components/CodeBlock'; import styles from './css/mdx.module.css'; +// Utility function to generate slug from heading text +function slugify(text: string): string { + return text + .toString() + .toLowerCase() + .trim() + .replace(/\s+/g, '-') + .replace(/[^\w\-]+/g, '') + .replace(/\-\-+/g, '-') + .replace(/^-+/, '') + .replace(/-+$/, ''); +} + +// Component for heading with anchor link +function HeadingWithAnchor({ + level, + children, + className, +}: { + level: number; + children: ReactNode; + className: string; +}) { + const text = + typeof children === 'string' + ? children + : Array.isArray(children) + ? children.join('') + : ''; + const id = slugify(text); + + const Tag = `h${level}` as keyof JSX.IntrinsicElements; + + return ( + + + {children} + # + + + ); +} + export const formattedMDXComponents: MDXComponents = { h1: ({ children }) =>

{children as ReactNode}

, - h2: ({ children }) =>

{children as ReactNode}

, - h3: ({ children }) =>

{children as ReactNode}

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

{children as ReactNode}

, a: ({ children, href }) => ( diff --git a/docs-ui/src/utils/data.ts b/docs-ui/src/utils/data.ts index 98f042e6de..ae150b4c1e 100644 --- a/docs-ui/src/utils/data.ts +++ b/docs-ui/src/utils/data.ts @@ -125,16 +125,3 @@ export const components: Page[] = [ slug: 'visually-hidden', }, ]; - -export type ScreenSize = { - title: string; - slug: string; - width: number; -}; - -export const screenSizes: ScreenSize[] = [ - { title: 'Mobile', slug: 'mobile', width: 390 }, - { title: 'Tablet', slug: 'tablet', width: 768 }, - { title: 'Desktop', slug: 'desktop', width: 1280 }, - { title: 'Wide', slug: 'wide', width: 1600 }, -]; From d7d87d57603a6c089c0bcd092e004b73146a4706 Mon Sep 17 00:00:00 2001 From: Charles de Dreuille Date: Tue, 4 Nov 2025 14:23:33 +0000 Subject: [PATCH 4/5] Cleaning build Signed-off-by: Charles de Dreuille --- docs-ui/next.config.mjs | 6 + docs-ui/package.json | 3 +- docs-ui/src/app/layout.tsx | 2 - docs-ui/src/components/PageTitle/index.tsx | 2 - .../StickyHeader/StickyHeader.module.css | 98 ------------ .../components/StickyHeader/StickyHeader.tsx | 50 ------ docs-ui/src/components/Toolbar/Toolbar.tsx | 1 - docs-ui/src/mdx-components.tsx | 2 +- docs-ui/src/utils/playground-context.tsx | 1 - docs-ui/yarn.lock | 149 +----------------- 10 files changed, 13 insertions(+), 301 deletions(-) delete mode 100644 docs-ui/src/components/StickyHeader/StickyHeader.module.css delete mode 100644 docs-ui/src/components/StickyHeader/StickyHeader.tsx diff --git a/docs-ui/next.config.mjs b/docs-ui/next.config.mjs index 8e6ed8fdc0..2ff341b609 100644 --- a/docs-ui/next.config.mjs +++ b/docs-ui/next.config.mjs @@ -1,4 +1,6 @@ import createMDX from '@next/mdx'; +import path from 'path'; +import { fileURLToPath } from 'url'; const nextConfig = { pageExtensions: ['js', 'jsx', 'md', 'mdx', 'ts', 'tsx'], @@ -13,6 +15,10 @@ const nextConfig = { // These are type-level conflicts that don't affect runtime behavior ignoreBuildErrors: true, }, + outputFileTracingRoot: path.join( + path.dirname(fileURLToPath(import.meta.url)), + '..', + ), }; const withMDX = createMDX({}); diff --git a/docs-ui/package.json b/docs-ui/package.json index 41df7eb2ce..75b79d1000 100644 --- a/docs-ui/package.json +++ b/docs-ui/package.json @@ -7,7 +7,7 @@ "build": "next build", "lint": "next lint", "prestart": "yarn sync:css", - "start": "concurrently \"yarn sync:css:watch\" \"next dev\"", + "start": "next dev", "sync:css": "node scripts/sync-css.js", "sync:css:watch": "node scripts/sync-css.js --watch" }, @@ -44,7 +44,6 @@ "@types/react": "19.1.9", "@types/react-dom": "19.1.7", "chokidar": "^3.6.0", - "concurrently": "^8.2.2", "eslint": "^8", "eslint-config-next": "15.3.4", "lightningcss": "^1.28.2", diff --git a/docs-ui/src/app/layout.tsx b/docs-ui/src/app/layout.tsx index 738a995795..c91535a44b 100644 --- a/docs-ui/src/app/layout.tsx +++ b/docs-ui/src/app/layout.tsx @@ -1,7 +1,6 @@ import type { Metadata } from 'next'; import { Sidebar } from '@/components/Sidebar/Sidebar'; import { Toolbar } from '@/components/Toolbar'; -import { StickyHeader } from '../components/StickyHeader/StickyHeader'; import { Providers } from './providers'; import { CustomTheme } from '@/components/CustomTheme'; import { TableOfContents } from '@/components/TableOfContents'; @@ -52,7 +51,6 @@ export default async function RootLayout({ - {/* */}
diff --git a/docs-ui/src/components/PageTitle/index.tsx b/docs-ui/src/components/PageTitle/index.tsx index aef3d75d70..c43ff75fc7 100644 --- a/docs-ui/src/components/PageTitle/index.tsx +++ b/docs-ui/src/components/PageTitle/index.tsx @@ -5,10 +5,8 @@ import styles from './PageTitle.module.css'; export const PageTitle = ({ title, description, - type = 'component', }: { title: string; - type?: string; description: string; }) => { return ( diff --git a/docs-ui/src/components/StickyHeader/StickyHeader.module.css b/docs-ui/src/components/StickyHeader/StickyHeader.module.css deleted file mode 100644 index 7cfc197f83..0000000000 --- a/docs-ui/src/components/StickyHeader/StickyHeader.module.css +++ /dev/null @@ -1,98 +0,0 @@ -.stickyHeader { - position: fixed; - top: 0px; - left: 32px; - right: 32px; - z-index: 99999; - background-color: var(--background); - padding: 16px 20px 16px 40px; - display: flex; - justify-content: space-between; - align-items: center; - backdrop-filter: blur(10px); - pointer-events: auto; - isolation: isolate; - transform: translateZ(0); - opacity: 0; - width: calc(100% - 64px); - mask-image: linear-gradient(to bottom, black 0%, black 80%, transparent 100%); - -webkit-mask-image: linear-gradient( - to bottom, - black 0%, - black 80%, - transparent 100% - ); -} - -@media (max-width: 768px) { - .stickyHeader { - display: none; - } -} - -.right { - display: flex; - align-items: center; - gap: 24px; -} - -.name { - font-size: 24px; - color: var(--text-primary); - font-weight: 400; -} - -.version { - font-size: 14px; - color: var(--text-secondary); - font-weight: 500; -} - -.actions { - display: flex; - align-items: center; - gap: 16px; -} - -.versionLinks { - display: flex; - align-items: center; - - a { - width: 48px; - height: 48px; - display: flex; - align-items: center; - justify-content: center; - color: var(--secondary); - transition: color 0.2s ease-in-out, background-color 0.2s ease-in-out; - border-radius: 48px; - - &:hover { - color: var(--primary); - background-color: var(--action); - } - } -} - -@media (max-width: 768px) { - .stickyHeader { - padding: 12px 16px; - } - - .right { - gap: 16px; - } - - .version { - font-size: 12px; - } -} - -@media (min-width: 768px) { - .stickyHeader { - width: calc(100% - 332px - 40px); - left: 332px; - right: 40px; - } -} diff --git a/docs-ui/src/components/StickyHeader/StickyHeader.tsx b/docs-ui/src/components/StickyHeader/StickyHeader.tsx deleted file mode 100644 index 804d759715..0000000000 --- a/docs-ui/src/components/StickyHeader/StickyHeader.tsx +++ /dev/null @@ -1,50 +0,0 @@ -'use client'; - -import { motion, useScroll, useTransform, circOut } from 'framer-motion'; -import { RiGithubLine, RiNpmjsLine } from '@remixicon/react'; -import { ThemeSelector } from '../Toolbar/theme'; -import { ThemeNameSelector } from '../Toolbar/theme-name'; -import { useCurrentPage } from '@/hooks/useCurrentPage'; -import styles from './StickyHeader.module.css'; - -export const StickyHeader = () => { - const { scrollY } = useScroll(); - const currentPage = useCurrentPage(); - - // Transform scroll position to opacity only - const opacity = useTransform(scrollY, [100, 200], [0, 1], { - clamp: false, - }); - - const yPos = useTransform(scrollY, [200, 500], [-60, 0], { - clamp: true, - ease: circOut, - }); - - return ( - -
{currentPage || 'Backstage UI'}
-
- - ); -}; diff --git a/docs-ui/src/components/Toolbar/Toolbar.tsx b/docs-ui/src/components/Toolbar/Toolbar.tsx index f0399797ea..3ba8b76739 100644 --- a/docs-ui/src/components/Toolbar/Toolbar.tsx +++ b/docs-ui/src/components/Toolbar/Toolbar.tsx @@ -18,7 +18,6 @@ import { ToggleButtonGroup, } from 'react-aria-components'; import styles from './Toolbar.module.css'; -import { Tabs } from '@base-ui-components/react/tabs'; import { usePlayground } from '@/utils/playground-context'; import { usePathname } from 'next/navigation'; import Link from 'next/link'; diff --git a/docs-ui/src/mdx-components.tsx b/docs-ui/src/mdx-components.tsx index 3e3efbe1ee..bae798bf7c 100644 --- a/docs-ui/src/mdx-components.tsx +++ b/docs-ui/src/mdx-components.tsx @@ -8,7 +8,7 @@ import styles from './css/mdx.module.css'; function slugify(text: string): string { return text .toString() - .toLowerCase() + .toLocaleLowerCase('en-US') .trim() .replace(/\s+/g, '-') .replace(/[^\w\-]+/g, '') diff --git a/docs-ui/src/utils/playground-context.tsx b/docs-ui/src/utils/playground-context.tsx index 401b079966..a1cee6ecba 100644 --- a/docs-ui/src/utils/playground-context.tsx +++ b/docs-ui/src/utils/playground-context.tsx @@ -4,7 +4,6 @@ import { ReactNode, useState, useEffect, - Key, } from 'react'; import { components } from './data'; diff --git a/docs-ui/yarn.lock b/docs-ui/yarn.lock index 7c27ea11dd..5e4be18156 100644 --- a/docs-ui/yarn.lock +++ b/docs-ui/yarn.lock @@ -23,7 +23,7 @@ __metadata: languageName: node linkType: hard -"@babel/runtime@npm:^7.18.6, @babel/runtime@npm:^7.21.0": +"@babel/runtime@npm:^7.18.6": version: 7.28.3 resolution: "@babel/runtime@npm:7.28.3" checksum: 10/f2415e4dbface7496f6fc561d640b44be203071fb0dfb63fbe338c7d2d2047419cb054ef13d1ebb8fc11e35d2b55aa3045def4b985e8b82aea5d7e58e1133e52 @@ -1990,7 +1990,7 @@ __metadata: languageName: node linkType: hard -"chalk@npm:^4.0.0, chalk@npm:^4.1.2": +"chalk@npm:^4.0.0": version: 4.1.2 resolution: "chalk@npm:4.1.2" dependencies: @@ -2061,17 +2061,6 @@ __metadata: languageName: node linkType: hard -"cliui@npm:^8.0.1": - version: 8.0.1 - resolution: "cliui@npm:8.0.1" - dependencies: - string-width: "npm:^4.2.0" - strip-ansi: "npm:^6.0.1" - wrap-ansi: "npm:^7.0.0" - checksum: 10/eaa5561aeb3135c2cddf7a3b3f562fc4238ff3b3fc666869ef2adf264be0f372136702f16add9299087fb1907c2e4ec5dbfe83bd24bce815c70a80c6c1a2e950 - languageName: node - linkType: hard - "clsx@npm:^2.1.1": version: 2.1.1 resolution: "clsx@npm:2.1.1" @@ -2151,26 +2140,6 @@ __metadata: languageName: node linkType: hard -"concurrently@npm:^8.2.2": - version: 8.2.2 - resolution: "concurrently@npm:8.2.2" - dependencies: - chalk: "npm:^4.1.2" - date-fns: "npm:^2.30.0" - lodash: "npm:^4.17.21" - rxjs: "npm:^7.8.1" - shell-quote: "npm:^1.8.1" - spawn-command: "npm:0.0.2" - supports-color: "npm:^8.1.1" - tree-kill: "npm:^1.2.2" - yargs: "npm:^17.7.2" - bin: - conc: dist/bin/concurrently.js - concurrently: dist/bin/concurrently.js - checksum: 10/dcb1aa69d9c611a7bda9d4fc0fe1e388f971d1744acec7e0d52dffa2ef55743f1266ec9292f414c5789b9f61734b3fce772bd005d4de9564a949fb121b97bae1 - languageName: node - linkType: hard - "crelt@npm:^1.0.5, crelt@npm:^1.0.6": version: 1.0.6 resolution: "crelt@npm:1.0.6" @@ -2236,15 +2205,6 @@ __metadata: languageName: node linkType: hard -"date-fns@npm:^2.30.0": - version: 2.30.0 - resolution: "date-fns@npm:2.30.0" - dependencies: - "@babel/runtime": "npm:^7.21.0" - checksum: 10/70b3e8ea7aaaaeaa2cd80bd889622a4bcb5d8028b4de9162cbcda359db06e16ff6e9309e54eead5341e71031818497f19aaf9839c87d1aba1e27bb4796e758a9 - languageName: node - linkType: hard - "debug@npm:4, debug@npm:^4.0.0, debug@npm:^4.3.1, debug@npm:^4.3.2, debug@npm:^4.3.4, debug@npm:^4.4.0": version: 4.4.1 resolution: "debug@npm:4.4.1" @@ -2355,7 +2315,6 @@ __metadata: "@uiw/react-codemirror": "npm:^4.23.7" chokidar: "npm:^3.6.0" clsx: "npm:^2.1.1" - concurrently: "npm:^8.2.2" eslint: "npm:^8" eslint-config-next: "npm:15.3.4" html-react-parser: "npm:^5.2.5" @@ -2762,13 +2721,6 @@ __metadata: languageName: node linkType: hard -"escalade@npm:^3.1.1": - version: 3.2.0 - resolution: "escalade@npm:3.2.0" - checksum: 10/9d7169e3965b2f9ae46971afa392f6e5a25545ea30f2e2dd99c9b0a95a3f52b5653681a84f5b2911a413ddad2d7a93d3514165072f349b5ffc59c75a899970d6 - languageName: node - linkType: hard - "escape-string-regexp@npm:^4.0.0": version: 4.0.0 resolution: "escape-string-regexp@npm:4.0.0" @@ -3363,13 +3315,6 @@ __metadata: languageName: node linkType: hard -"get-caller-file@npm:^2.0.5": - version: 2.0.5 - resolution: "get-caller-file@npm:2.0.5" - checksum: 10/b9769a836d2a98c3ee734a88ba712e62703f1df31b94b784762c433c27a386dd6029ff55c2a920c392e33657d80191edbf18c61487e198844844516f843496b9 - languageName: node - linkType: hard - "get-intrinsic@npm:^1.2.4, get-intrinsic@npm:^1.2.5, get-intrinsic@npm:^1.2.6, get-intrinsic@npm:^1.2.7, get-intrinsic@npm:^1.3.0": version: 1.3.0 resolution: "get-intrinsic@npm:1.3.0" @@ -4409,13 +4354,6 @@ __metadata: languageName: node linkType: hard -"lodash@npm:^4.17.21": - version: 4.17.21 - resolution: "lodash@npm:4.17.21" - checksum: 10/c08619c038846ea6ac754abd6dd29d2568aa705feb69339e836dfa8d8b09abbb2f859371e86863eda41848221f9af43714491467b5b0299122431e202bb0c532 - languageName: node - linkType: hard - "longest-streak@npm:^3.0.0": version: 3.1.0 resolution: "longest-streak@npm:3.1.0" @@ -5829,13 +5767,6 @@ __metadata: languageName: node linkType: hard -"require-directory@npm:^2.1.1": - version: 2.1.1 - resolution: "require-directory@npm:2.1.1" - checksum: 10/a72468e2589270d91f06c7d36ec97a88db53ae5d6fe3787fadc943f0b0276b10347f89b363b2a82285f650bdcc135ad4a257c61bdd4d00d6df1fa24875b0ddaf - languageName: node - linkType: hard - "resolve-from@npm:^4.0.0": version: 4.0.0 resolution: "resolve-from@npm:4.0.0" @@ -5936,15 +5867,6 @@ __metadata: languageName: node linkType: hard -"rxjs@npm:^7.8.1": - version: 7.8.2 - resolution: "rxjs@npm:7.8.2" - dependencies: - tslib: "npm:^2.1.0" - checksum: 10/03dff09191356b2b87d94fbc1e97c4e9eb3c09d4452399dddd451b09c2f1ba8d56925a40af114282d7bc0c6fe7514a2236ca09f903cf70e4bbf156650dddb49d - languageName: node - linkType: hard - "safe-array-concat@npm:^1.1.3": version: 1.1.3 resolution: "safe-array-concat@npm:1.1.3" @@ -6151,13 +6073,6 @@ __metadata: languageName: node linkType: hard -"shell-quote@npm:^1.8.1": - version: 1.8.3 - resolution: "shell-quote@npm:1.8.3" - checksum: 10/5473e354637c2bd698911224129c9a8961697486cff1fb221f234d71c153fc377674029b0223d1d3c953a68d451d79366abfe53d1a0b46ee1f28eb9ade928f4c - languageName: node - linkType: hard - "shiki@npm:^3.13.0": version: 3.13.0 resolution: "shiki@npm:3.13.0" @@ -6294,13 +6209,6 @@ __metadata: languageName: node linkType: hard -"spawn-command@npm:0.0.2": - version: 0.0.2 - resolution: "spawn-command@npm:0.0.2" - checksum: 10/f13e8c3c63abd4a0b52fb567eba5f7940d480c5ed3ec61781d38a1850f179b1196c39e6efa2bbd301f82c1bf1cd7807abc8fbd8fc8e44bcaa3975a124c0d1657 - languageName: node - linkType: hard - "ssri@npm:^12.0.0": version: 12.0.0 resolution: "ssri@npm:12.0.0" @@ -6345,7 +6253,7 @@ __metadata: languageName: node linkType: hard -"string-width-cjs@npm:string-width@^4.2.0, string-width@npm:^4.1.0, string-width@npm:^4.2.0, string-width@npm:^4.2.3": +"string-width-cjs@npm:string-width@^4.2.0, string-width@npm:^4.1.0": version: 4.2.3 resolution: "string-width@npm:4.2.3" dependencies: @@ -6539,15 +6447,6 @@ __metadata: languageName: node linkType: hard -"supports-color@npm:^8.1.1": - version: 8.1.1 - resolution: "supports-color@npm:8.1.1" - dependencies: - has-flag: "npm:^4.0.0" - checksum: 10/157b534df88e39c5518c5e78c35580c1eca848d7dbaf31bbe06cdfc048e22c7ff1a9d046ae17b25691128f631a51d9ec373c1b740c12ae4f0de6e292037e4282 - languageName: node - linkType: hard - "supports-preserve-symlinks-flag@npm:^1.0.0": version: 1.0.0 resolution: "supports-preserve-symlinks-flag@npm:1.0.0" @@ -6602,15 +6501,6 @@ __metadata: languageName: node linkType: hard -"tree-kill@npm:^1.2.2": - version: 1.2.2 - resolution: "tree-kill@npm:1.2.2" - bin: - tree-kill: cli.js - checksum: 10/49117f5f410d19c84b0464d29afb9642c863bc5ba40fcb9a245d474c6d5cc64d1b177a6e6713129eb346b40aebb9d4631d967517f9fbe8251c35b21b13cd96c7 - languageName: node - linkType: hard - "trim-lines@npm:^3.0.0": version: 3.0.1 resolution: "trim-lines@npm:3.0.1" @@ -6646,7 +6536,7 @@ __metadata: languageName: node linkType: hard -"tslib@npm:^2.0.1, tslib@npm:^2.1.0, tslib@npm:^2.4.0, tslib@npm:^2.8.0": +"tslib@npm:^2.0.1, tslib@npm:^2.4.0, tslib@npm:^2.8.0": version: 2.8.1 resolution: "tslib@npm:2.8.1" checksum: 10/3e2e043d5c2316461cb54e5c7fe02c30ef6dccb3384717ca22ae5c6b5bc95232a6241df19c622d9c73b809bea33b187f6dbc73030963e29950c2141bc32a79f7 @@ -7085,7 +6975,7 @@ __metadata: languageName: node linkType: hard -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0, wrap-ansi@npm:^7.0.0": +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": version: 7.0.0 resolution: "wrap-ansi@npm:7.0.0" dependencies: @@ -7129,13 +7019,6 @@ __metadata: languageName: node linkType: hard -"y18n@npm:^5.0.5": - version: 5.0.8 - resolution: "y18n@npm:5.0.8" - checksum: 10/5f1b5f95e3775de4514edbb142398a2c37849ccfaf04a015be5d75521e9629d3be29bd4432d23c57f37e5b61ade592fb0197022e9993f81a06a5afbdcda9346d - languageName: node - linkType: hard - "yallist@npm:^4.0.0": version: 4.0.0 resolution: "yallist@npm:4.0.0" @@ -7159,28 +7042,6 @@ __metadata: languageName: node linkType: hard -"yargs-parser@npm:^21.1.1": - version: 21.1.1 - resolution: "yargs-parser@npm:21.1.1" - checksum: 10/9dc2c217ea3bf8d858041252d43e074f7166b53f3d010a8c711275e09cd3d62a002969a39858b92bbda2a6a63a585c7127014534a560b9c69ed2d923d113406e - languageName: node - linkType: hard - -"yargs@npm:^17.7.2": - version: 17.7.2 - resolution: "yargs@npm:17.7.2" - dependencies: - cliui: "npm:^8.0.1" - escalade: "npm:^3.1.1" - get-caller-file: "npm:^2.0.5" - require-directory: "npm:^2.1.1" - string-width: "npm:^4.2.3" - y18n: "npm:^5.0.5" - yargs-parser: "npm:^21.1.1" - checksum: 10/abb3e37678d6e38ea85485ed86ebe0d1e3464c640d7d9069805ea0da12f69d5a32df8e5625e370f9c96dd1c2dc088ab2d0a4dd32af18222ef3c4224a19471576 - languageName: node - linkType: hard - "yocto-queue@npm:^0.1.0": version: 0.1.0 resolution: "yocto-queue@npm:0.1.0" From 7f91058ce4aa140e13ec60d7d94ff3f9a4ed1a94 Mon Sep 17 00:00:00 2001 From: Charles de Dreuille Date: Tue, 4 Nov 2025 19:02:24 +0000 Subject: [PATCH 5/5] Add mobile navigation Signed-off-by: Charles de Dreuille --- docs-ui/src/app/layout.tsx | 2 + .../AnimatedMenuIcon.module.css | 40 ++++ .../MobileBottomNav/AnimatedMenuIcon.tsx | 56 +++++ .../MobileBottomNav.module.css | 205 ++++++++++++++++++ .../MobileBottomNav/MobileBottomNav.tsx | 44 ++++ .../components/MobileBottomNav/MobileMenu.tsx | 32 +++ .../src/components/MobileBottomNav/index.tsx | 1 + .../Navigation/Navigation.module.css | 100 +++++++++ .../src/components/Navigation/Navigation.tsx | 125 +++++++++++ docs-ui/src/components/Navigation/index.tsx | 1 + .../src/components/Sidebar/Sidebar.module.css | 97 --------- docs-ui/src/components/Sidebar/Sidebar.tsx | 107 +-------- .../src/components/Toolbar/Toolbar.module.css | 22 +- docs-ui/src/components/Toolbar/Toolbar.tsx | 40 ++-- 14 files changed, 652 insertions(+), 220 deletions(-) create mode 100644 docs-ui/src/components/MobileBottomNav/AnimatedMenuIcon.module.css create mode 100644 docs-ui/src/components/MobileBottomNav/AnimatedMenuIcon.tsx create mode 100644 docs-ui/src/components/MobileBottomNav/MobileBottomNav.module.css create mode 100644 docs-ui/src/components/MobileBottomNav/MobileBottomNav.tsx create mode 100644 docs-ui/src/components/MobileBottomNav/MobileMenu.tsx create mode 100644 docs-ui/src/components/MobileBottomNav/index.tsx create mode 100644 docs-ui/src/components/Navigation/Navigation.module.css create mode 100644 docs-ui/src/components/Navigation/Navigation.tsx create mode 100644 docs-ui/src/components/Navigation/index.tsx diff --git a/docs-ui/src/app/layout.tsx b/docs-ui/src/app/layout.tsx index c91535a44b..ad056626d8 100644 --- a/docs-ui/src/app/layout.tsx +++ b/docs-ui/src/app/layout.tsx @@ -4,6 +4,7 @@ import { Toolbar } from '@/components/Toolbar'; import { Providers } from './providers'; import { CustomTheme } from '@/components/CustomTheme'; import { TableOfContents } from '@/components/TableOfContents'; +import { MobileBottomNav } from '@/components/MobileBottomNav'; import styles from './layout.module.css'; import '../css/globals.css'; @@ -62,6 +63,7 @@ export default async function RootLayout({
+ diff --git a/docs-ui/src/components/MobileBottomNav/AnimatedMenuIcon.module.css b/docs-ui/src/components/MobileBottomNav/AnimatedMenuIcon.module.css new file mode 100644 index 0000000000..632f005b65 --- /dev/null +++ b/docs-ui/src/components/MobileBottomNav/AnimatedMenuIcon.module.css @@ -0,0 +1,40 @@ +.icon { + display: block; +} + +.topLine, +.middleLine, +.bottomLine { + transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); +} + +/* Menu state (default) */ +.topLine { + transform-origin: 10px 5px; + transform: rotate(0deg); +} + +.middleLine { + opacity: 1; + transform-origin: 10px 10px; +} + +.bottomLine { + transform-origin: 10px 15px; + transform: rotate(0deg); +} + +/* Close state (when open) */ +.icon[data-open='true'] .topLine { + transform-origin: 10px 5px; + transform: translateY(5px) rotate(45deg); +} + +.icon[data-open='true'] .middleLine { + opacity: 0; +} + +.icon[data-open='true'] .bottomLine { + transform-origin: 10px 15px; + transform: translateY(-5px) rotate(-45deg); +} diff --git a/docs-ui/src/components/MobileBottomNav/AnimatedMenuIcon.tsx b/docs-ui/src/components/MobileBottomNav/AnimatedMenuIcon.tsx new file mode 100644 index 0000000000..80edd64d43 --- /dev/null +++ b/docs-ui/src/components/MobileBottomNav/AnimatedMenuIcon.tsx @@ -0,0 +1,56 @@ +import styles from './AnimatedMenuIcon.module.css'; + +interface AnimatedMenuIconProps { + isOpen: boolean; + size?: number; +} + +export const AnimatedMenuIcon = ({ + isOpen, + size = 20, +}: AnimatedMenuIconProps) => { + return ( + + + + + + + + ); +}; diff --git a/docs-ui/src/components/MobileBottomNav/MobileBottomNav.module.css b/docs-ui/src/components/MobileBottomNav/MobileBottomNav.module.css new file mode 100644 index 0000000000..9c128c3959 --- /dev/null +++ b/docs-ui/src/components/MobileBottomNav/MobileBottomNav.module.css @@ -0,0 +1,205 @@ +/* Mobile Bottom Navigation Island */ +.mobileBottomNav { + display: block; + position: fixed; + bottom: 0; + left: 0; + right: 0; + padding: 16px; + z-index: 300; + pointer-events: none; + + @media (min-width: 768px) { + display: none; + } +} + +.island { + display: flex; + align-items: center; + justify-content: space-between; + gap: 12px; + margin: 0 auto; + width: 50vw; + padding: 8px 12px 8px 12px; + border-radius: 32px; + pointer-events: auto; + background-color: #000; +} + +:global([data-theme-mode='dark']) .island { + background-color: #fff; + box-shadow: 0 4px 24px rgba(0, 0, 0, 0.3); +} + +.menuButton { + display: flex; + align-items: center; + justify-content: center; + background: none; + border: none; + cursor: pointer; + padding: 8px; + border-radius: 50%; + transition: background-color 0.2s ease-in-out; + + /* White icons on light mode (black island) */ + color: white; + + &:hover { + background-color: rgba(255, 255, 255, 0.1); + } +} + +:global([data-theme-mode='dark']) .menuButton { + /* Black icons on dark mode (white island) */ + color: black; + + &:hover { + background-color: rgba(0, 0, 0, 0.1); + } +} + +.buttonGroup { + display: flex; + align-items: center; + gap: 4px; + padding: 4px; + border-radius: 24px; + background-color: rgba(255, 255, 255, 0.2); +} + +:global([data-theme-mode='dark']) .buttonGroup { + background-color: rgba(0, 0, 0, 0.08); +} + +.buttonGroup button { + display: flex; + align-items: center; + justify-content: center; + background: none; + border: none; + cursor: pointer; + padding: 6px 12px; + border-radius: 20px; + transition: background-color 0.2s ease-in-out; + + /* White icons on light mode */ + color: white; + + &[data-selected] { + background-color: rgba(255, 255, 255, 0.2); + } + + &:hover { + background-color: rgba(255, 255, 255, 0.15); + } +} + +:global([data-theme-mode='dark']) .buttonGroup button { + /* Black icons on dark mode */ + color: black; + + &[data-selected] { + background-color: rgba(0, 0, 0, 0.2); + } + + &:hover { + background-color: rgba(0, 0, 0, 0.15); + } +} + +/* Mobile Menu Bottom Sheet */ +.overlay { + position: fixed; + inset: 0; + z-index: 200; + background-color: rgba(38, 38, 38, 0.7); + display: flex; + align-items: flex-end; + justify-content: center; + + &[data-entering] { + animation: overlayFadeIn 200ms ease-out; + } + + &[data-exiting] { + animation: overlayFadeOut 200ms ease-in; + } +} + +@keyframes overlayFadeIn { + from { + opacity: 0; + } + to { + opacity: 1; + } +} + +@keyframes overlayFadeOut { + from { + opacity: 1; + } + to { + opacity: 0; + } +} + +.modal { + width: calc(100% - 48px); + max-height: 50vh; + background-color: var(--bg); + border-radius: 16px; + box-shadow: 0 -4px 24px rgba(0, 0, 0, 0.2); + display: flex; + flex-direction: column; + outline: none; + margin-bottom: 96px; + + &[data-entering] { + animation: slideUp 300ms cubic-bezier(0.32, 0.72, 0, 1); + } + + &[data-exiting] { + animation: slideDown 200ms cubic-bezier(0.32, 0.72, 0, 1); + } +} + +@keyframes slideUp { + from { + transform: translateY(8%); + } + to { + transform: translateY(0); + } +} + +@keyframes slideDown { + from { + transform: translateY(0); + } + to { + transform: translateY(8%); + } +} + +.dialog { + display: flex; + flex-direction: column; + height: 100%; + max-height: 70vh; + outline: none; + overflow: hidden; +} + +.menuContent { + flex: 1; + overflow-y: auto; + overflow-x: hidden; + padding: 16px; + display: flex; + flex-direction: column; + gap: 2px; + min-height: 0; +} diff --git a/docs-ui/src/components/MobileBottomNav/MobileBottomNav.tsx b/docs-ui/src/components/MobileBottomNav/MobileBottomNav.tsx new file mode 100644 index 0000000000..52ec2ee9d9 --- /dev/null +++ b/docs-ui/src/components/MobileBottomNav/MobileBottomNav.tsx @@ -0,0 +1,44 @@ +'use client'; + +import { useState } from 'react'; +import { RiMoonLine, RiSunLine } from '@remixicon/react'; +import { Button, ToggleButton, ToggleButtonGroup } from 'react-aria-components'; +import styles from './MobileBottomNav.module.css'; +import { usePlayground } from '@/utils/playground-context'; +import { MobileMenu } from './MobileMenu'; +import { AnimatedMenuIcon } from './AnimatedMenuIcon'; + +export const MobileBottomNav = () => { + const [isMenuOpen, setIsMenuOpen] = useState(false); + const { selectedTheme, setSelectedTheme } = usePlayground(); + + return ( + <> +
+
+ + + + + + + + + +
+
+ setIsMenuOpen(false)} /> + + ); +}; diff --git a/docs-ui/src/components/MobileBottomNav/MobileMenu.tsx b/docs-ui/src/components/MobileBottomNav/MobileMenu.tsx new file mode 100644 index 0000000000..4d84527f30 --- /dev/null +++ b/docs-ui/src/components/MobileBottomNav/MobileMenu.tsx @@ -0,0 +1,32 @@ +'use client'; + +import { Button, Dialog, Modal, ModalOverlay } from 'react-aria-components'; +import { RiCloseLine } from '@remixicon/react'; +import styles from './MobileBottomNav.module.css'; +import { Navigation } from '@/components/Navigation'; + +interface MobileMenuProps { + isOpen: boolean; + onClose: () => void; +} + +export const MobileMenu = ({ isOpen, onClose }: MobileMenuProps) => { + return ( + + + + {({ close }) => ( +
+ +
+ )} +
+
+
+ ); +}; diff --git a/docs-ui/src/components/MobileBottomNav/index.tsx b/docs-ui/src/components/MobileBottomNav/index.tsx new file mode 100644 index 0000000000..4b7449c9c5 --- /dev/null +++ b/docs-ui/src/components/MobileBottomNav/index.tsx @@ -0,0 +1 @@ +export { MobileBottomNav } from './MobileBottomNav'; diff --git a/docs-ui/src/components/Navigation/Navigation.module.css b/docs-ui/src/components/Navigation/Navigation.module.css new file mode 100644 index 0000000000..f5e1312ab1 --- /dev/null +++ b/docs-ui/src/components/Navigation/Navigation.module.css @@ -0,0 +1,100 @@ +.topNav { + & ul { + margin: 0; + padding: 0; + list-style: none; + display: flex; + flex-direction: column; + gap: 2px; + } + + & li { + margin: 0; + padding: 0; + list-style: none; + } + + & li div, + & li a { + display: flex; + align-items: center; + gap: 8px; + padding: 8px 12px; + border-radius: 4px; + cursor: pointer; + color: var(--primary); + text-decoration: none; + + &:hover { + background-color: var(--action); + + &[data-disabled='true'] { + background-color: transparent; + } + } + + &[data-active='true'] { + background-color: var(--action); + + &[data-disabled='true'] { + background-color: transparent; + } + } + + &[data-disabled='true'] { + opacity: 0.5; + cursor: not-allowed; + } + } +} + +.sectionTitle { + font-size: 0.6875rem; + font-weight: 500; + padding: 0 12px 4px; + color: var(--secondary); + margin-top: 40px; + text-transform: uppercase; + + &:first-child { + margin-top: 12px; + } +} + +.line { + text-decoration: none; + align-items: center; + width: 100%; + display: flex; + flex-direction: row; + justify-content: space-between; + align-items: center; + height: 28px; + padding: 0 12px; + border-radius: 4px; + color: var(--primary); + flex-shrink: 0; + + &:hover { + background-color: var(--action); + } + + &.active { + background-color: var(--action); + } + + &.active .lineTitle { + color: var(--primary); + } +} + +.lineTitle { + font-size: 14px; + font-weight: 400; + color: var(--primary); +} + +.lineStatus { + font-size: 14px; + color: var(--secondary); +} diff --git a/docs-ui/src/components/Navigation/Navigation.tsx b/docs-ui/src/components/Navigation/Navigation.tsx new file mode 100644 index 0000000000..13fa87661f --- /dev/null +++ b/docs-ui/src/components/Navigation/Navigation.tsx @@ -0,0 +1,125 @@ +'use client'; + +import Link from 'next/link'; +import { usePathname } from 'next/navigation'; +import { Fragment } from 'react'; +import clsx from 'clsx'; +import { + RiCollageLine, + RiFileHistoryLine, + RiHazeLine, + RiPaletteLine, + RiServiceLine, + RiStackLine, +} from '@remixicon/react'; +import { components, layoutComponents } from '@/utils/data'; +import styles from './Navigation.module.css'; + +interface NavigationProps { + onLinkClick?: () => void; +} + +const data = [ + { + title: 'Layout Components', + content: layoutComponents, + url: '/components', + }, + { + title: 'Components', + content: components, + url: '/components', + }, +]; + +export const Navigation = ({ onLinkClick }: NavigationProps) => { + const pathname = usePathname(); + + return ( + <> + + {data.map(section => { + return ( + +
{section.title}
+ + {section.content.map(item => { + const isActive = pathname === `${section.url}/${item.slug}`; + + return ( + +
{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/docs-ui/src/components/Navigation/index.tsx b/docs-ui/src/components/Navigation/index.tsx new file mode 100644 index 0000000000..61f39d15c9 --- /dev/null +++ b/docs-ui/src/components/Navigation/index.tsx @@ -0,0 +1 @@ +export { Navigation } from './Navigation'; diff --git a/docs-ui/src/components/Sidebar/Sidebar.module.css b/docs-ui/src/components/Sidebar/Sidebar.module.css index 426073c071..83c1e0bde5 100644 --- a/docs-ui/src/components/Sidebar/Sidebar.module.css +++ b/docs-ui/src/components/Sidebar/Sidebar.module.css @@ -73,100 +73,3 @@ gap: 2px; position: relative; } - -.topNav { - & ul { - margin: 0; - padding: 0; - list-style: none; - display: flex; - flex-direction: column; - gap: 2px; - } - - & li { - margin: 0; - padding: 0; - list-style: none; - } - - & li div, - & li a { - display: flex; - align-items: center; - gap: 8px; - padding: 8px 12px; - border-radius: 4px; - cursor: pointer; - - &:hover { - background-color: var(--action); - - &[data-disabled='true'] { - background-color: transparent; - } - } - - &[data-active='true'] { - background-color: var(--action); - - &[data-disabled='true'] { - background-color: transparent; - } - } - - &[data-disabled='true'] { - opacity: 0.5; - cursor: not-allowed; - } - } -} - -.sectionTitle { - font-size: 0.6875rem; - font-weight: 500; - padding: 0 12px 4px; - color: var(--secondary); - margin-top: 40px; - text-transform: uppercase; - - &:first-child { - margin-top: 12px; - } -} - -.line { - text-decoration: none; - align-items: center; - width: 100%; - display: flex; - flex-direction: row; - justify-content: space-between; - align-items: center; - height: 26px; - padding: 0 12px; - border-radius: 4px; - - &:hover { - background-color: var(--action); - } - - &.active { - background-color: var(--action); - } - - &.active .lineTitle { - color: var(--primary); - } -} - -.lineTitle { - font-size: 14px; - font-weight: 400; - color: var(--primary); -} - -.lineStatus { - font-size: 14px; - color: var(--secondary); -} diff --git a/docs-ui/src/components/Sidebar/Sidebar.tsx b/docs-ui/src/components/Sidebar/Sidebar.tsx index 08b9feac86..d9c16f1905 100644 --- a/docs-ui/src/components/Sidebar/Sidebar.tsx +++ b/docs-ui/src/components/Sidebar/Sidebar.tsx @@ -1,38 +1,11 @@ 'use client'; import styles from './Sidebar.module.css'; -import { components, layoutComponents } from '@/utils/data'; import { ScrollArea } from '@base-ui-components/react/scroll-area'; -import Link from 'next/link'; -import { usePathname } from 'next/navigation'; -import { Fragment } from 'react'; -import clsx from 'clsx'; -import { - RiCollageLine, - RiFileHistoryLine, - RiHazeLine, - RiPaletteLine, - RiServiceLine, - RiStackLine, -} from '@remixicon/react'; import { Logo } from './Logo'; - -const data = [ - { - title: 'Layout Components', - content: layoutComponents, - url: '/components', - }, - { - title: 'Components', - content: components, - url: '/components', - }, -]; +import { Navigation } from '@/components/Navigation'; export const Sidebar = () => { - const pathname = usePathname(); - return (
@@ -42,83 +15,7 @@ export const Sidebar = () => {
- - {data.map(section => { - return ( - -
{section.title}
- - {section.content.map(item => { - const isActive = - pathname === `${section.url}/${item.slug}`; - - return ( - -
{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/docs-ui/src/components/Toolbar/Toolbar.module.css b/docs-ui/src/components/Toolbar/Toolbar.module.css index 4566904df5..581fc599b3 100644 --- a/docs-ui/src/components/Toolbar/Toolbar.module.css +++ b/docs-ui/src/components/Toolbar/Toolbar.module.css @@ -18,6 +18,24 @@ font-weight: 500; } +.logoMobile { + display: block; + + @media (min-width: 768px) { + display: none; + } +} + +.breadcrumbDesktop { + display: none; + + @media (min-width: 768px) { + display: flex; + align-items: center; + gap: 0.5rem; + } +} + .breadcrumbLink { color: var(--secondary); text-decoration: none; @@ -70,7 +88,7 @@ align-items: center; justify-content: center; background-color: var(--bg); - border: 1px solid var(--border2); + border: 1px solid var(--border); border-radius: 32px; padding-inline: 16px; color: var(--primary); @@ -105,7 +123,7 @@ display: flex; align-items: center; gap: 4px; - border: 1px solid var(--border2); + border: 1px solid var(--border); border-radius: 32px; padding-inline: 4px; height: 32px; diff --git a/docs-ui/src/components/Toolbar/Toolbar.tsx b/docs-ui/src/components/Toolbar/Toolbar.tsx index 3ba8b76739..e7defd5e8a 100644 --- a/docs-ui/src/components/Toolbar/Toolbar.tsx +++ b/docs-ui/src/components/Toolbar/Toolbar.tsx @@ -22,6 +22,7 @@ import { usePlayground } from '@/utils/playground-context'; import { usePathname } from 'next/navigation'; import Link from 'next/link'; import { components, layoutComponents } from '@/utils/data'; +import { Logo } from '@/components/Sidebar/Logo'; interface ToolbarProps { version: string; @@ -86,23 +87,30 @@ export const Toolbar = ({ version }: ToolbarProps) => { return (
- {breadcrumb.section && breadcrumb.sectionLink ? ( - <> - - {breadcrumb.section} - - +
+ +
+
+ {breadcrumb.section && breadcrumb.sectionLink ? ( + <> + + {breadcrumb.section} + + + + {breadcrumb.title} + + + ) : ( {breadcrumb.title} - - ) : ( - {breadcrumb.title} - )} + )} +