feat(site): tabs (#144)

This commit is contained in:
Darius Cepulis
2025-10-29 12:43:03 -05:00
committed by GitHub
parent 419911f2f2
commit df4692dbda
35 changed files with 999 additions and 304 deletions
+157 -38
View File
@@ -6,7 +6,8 @@ description: 'A guide on writing documentation for the Video.js project, and a t
import FrameworkCase from '@/components/docs/FrameworkCase.astro';
import StyleCase from '@/components/docs/StyleCase.astro';
import Container from '@/components/docs/Container.astro';
import ServerCode from '@/components/ServerCode.astro';
import ServerCode from '@/components/Code/ServerCode.astro';
import { TabsRoot, TabsPanel } from '@/components/Tabs.tsx';
## What kind of guide are you writing?
I haven't written this section yet, but when I do, it'll rehash [Diátaxis](https://diataxis.fr/).
@@ -32,50 +33,17 @@ will render:
Use the `<StyleCase>` component to show content only for specific styling approaches. For example,
```mdx
<StyleCase styles={["tailwind"]}>
Tailwind-only content
<StyleCase styles={["css"]}>
Css-only content
</StyleCase>
```
will render:
<StyleCase styles={["tailwind"]}>
Tailwind-only content
<StyleCase styles={["css"]}>
Css-only content
</StyleCase>
## Displaying Code from Files
Use the `<ServerCode>` component to display code imported from source files with syntax highlighting. Supports any language that [Shiki supports](https://shiki.style/languages).
```mdx
import exampleCode from '@/examples/react/Example.tsx?raw';
import ServerCode from '@/components/ServerCode.astro';
<ServerCode code={exampleCode} lang="tsx" />
```
will render:
<ServerCode code={`import { useState } from 'react';
export function Example() {
const [count, setCount] = useState(0);
return <button onClick={() => setCount(count + 1)}>{count}</button>;
}`} lang="tsx" />
## Wrapping Live Demos
Use the `<Container>` component to constrain live demos to a readable width.
```mdx
import { MyDemo } from '@/examples/react/MyDemo';
import Container from '@/components/docs/Container.astro';
<Container>
<MyDemo client:load />
</Container>
```
## Use Github-Flavored Markdown
### Headings
@@ -279,3 +247,154 @@ Press <kbd>CTRL</kbd> + <kbd>ALT</kbd> + <kbd>Delete</kbd> to end the session.
Most <mark>salamanders</mark> are nocturnal, and hunt for insects, worms, and other small creatures.
## Code and Code Frames
### Default frame
Regular markdown code blocks automatically get wrapped in tabs with a copy button:
````markdown
```ts
console.log('Hello, TypeScript!');
```
````
Renders
```ts
console.log('Hello, TypeScript!');
```
### Tabs
Use `<TabsRoot>` and `<TabsPanel>` to show multiple code examples side-by-side:
````mdx
<TabsRoot
aria-label="Code examples"
titles={{ typescript: 'TypeScript', javascript: 'JavaScript' }}
client:load
>
<TabsPanel value="typescript" client:load>
```ts
console.log('Hello, TypeScript!');
```
</TabsPanel>
<TabsPanel value="javascript" client:load>
```js
console.log('Hello, JavaScript!');
```
</TabsPanel>
</TabsRoot>
````
Which renders
<TabsRoot
aria-label="Code examples"
titles={{ typescript: 'TypeScript', javascript: 'JavaScript' }}
client:load
>
<TabsPanel value="typescript" client:load>
```ts
console.log('Hello, TypeScript!');
```
</TabsPanel>
<TabsPanel value="javascript" client:load>
```js
console.log('Hello, JavaScript!');
```
</TabsPanel>
</TabsRoot>
**Important notes:**
- You might've noticed that elsewhere in the codebase, TabsRoot requires `id` and TabsPanel requires `tabsId`. In MDX, these are generated for you with a rehype plugin
- Both `<TabsRoot>` and `<TabsPanel>` require `client:load` directive in MDX
- The first key in `titles` will be the default active tab. Your panels should be in the same order as the keys in `titles`.
- Use descriptive `aria-label` for accessibility
## Displaying Code from Files
Use the `<ServerCode>` component to display code imported from source files with syntax highlighting. Supports any language that [Shiki supports](https://shiki.style/languages).
**Important:** Unlike regular markdown code blocks, `<ServerCode>` does **not** automatically get wrapped in a frame. You should use `TabsRoot` with a single `TabPanel` to make it look pretty
```mdx
import componentCode from '@/examples/react/Component.tsx?raw';
import { TabsRoot, TabsPanel } from '@/components/Tabs';
import ServerCode from '@/components/Code/ServerCode.astro';
<TabsRoot
aria-label="Component implementation"
titles={{ component: 'Component', css: 'CSS Module' }}
client:load
>
<TabsPanel value="component" client:load>
<ServerCode code={componentCode} lang="tsx" />
</TabsPanel>
</TabsRoot>
```
Which will happily render
<TabsRoot
aria-label="Component implementation"
titles={{ component: 'Component' }}
client:load
>
<TabsPanel value="component" client:load>
<ServerCode code={`import React from 'react';
function Component() {
return <div>Hello, world!</div>;
}`} lang="tsx" />
</TabsPanel>
</TabsRoot>
## Wrapping Live Demos
There are two main patterns for displaying live demos, depending on whether you want to show code alongside the demo.
### Option 1: Standalone Demo with Container
Use the `<Container>` component to constrain standalone demos to a readable width:
```mdx
import { MyDemo } from '@/examples/react/MyDemo';
import Container from '@/components/docs/Container.astro';
<Container>
<MyDemo client:load />
</Container>
```
This is best for demos that don't need accompanying code, or when the code is shown separately elsewhere on the page.
### Option 2: Demo with Code in TabsRoot
Place the demo directly inside `<TabsRoot>` (as a sibling to `<TabsPanel>` elements) to keep code and demo together:
```mdx
import { MyDemo } from '@/examples/react/MyDemo';
import componentCode from '@/examples/react/MyDemo.tsx?raw';
import cssCode from '@/examples/react/MyDemo.module.css?raw';
import { TabsRoot, TabsPanel } from '@/components/Tabs';
import ServerCode from '@/components/Code/ServerCode.astro';
<TabsRoot
aria-label="MyDemo implementation"
titles={{ component: 'Component', css: 'CSS Module' }}
client:load
>
<TabsPanel value="component" client:load>
<ServerCode code={componentCode} lang="tsx" />
</TabsPanel>
<TabsPanel value="css" client:load>
<ServerCode code={cssCode} lang="css" />
</TabsPanel>
<MyDemo client:load />
</TabsRoot>
```
The demo will appear at the bottom of the tabs component, creating a cohesive unit of code and preview. This pattern is used throughout our resource documentation (see [PlayButton](/docs/framework/react/style/css/resources/play-button) for an example).
@@ -13,7 +13,8 @@ import htmlCssStr from '@/examples/html/fullscreen-button/fullscreen-button.css?
import htmlJsStr from '@/examples/html/fullscreen-button/fullscreen-button.js?raw';
import FrameworkCase from '@/components/docs/FrameworkCase.astro';
import Container from '@/components/docs/Container.astro';
import ServerCode from '@/components/ServerCode.astro';
import ServerCode from '@/components/Code/ServerCode.astro';
import { TabsRoot, TabsPanel } from '@/components/Tabs';
## Features
@@ -22,38 +23,46 @@ import ServerCode from '@/components/ServerCode.astro';
- Falls back gracefully when fullscreen not supported
- Accessible keyboard navigation
## Live Example
<Container>
<FullscreenButtonDemo client:load />
</Container>
## Usage
## Example
<FrameworkCase frameworks={["react"]}>
<TabsRoot
aria-label="React implementation"
titles={{ component: 'Component', css: 'CSS Module' }}
client:load
>
<TabsPanel value="component" client:load>
<ServerCode code={componentModuleStr} lang="tsx" />
</TabsPanel>
### Component
<ServerCode code={componentModuleStr} lang="tsx" />
### CSS Module
<ServerCode code={cssModuleStr} lang="css" />
<TabsPanel value="css" client:load>
<ServerCode code={cssModuleStr} lang="css" />
</TabsPanel>
<FullscreenButtonDemo client:load />
</TabsRoot>
</FrameworkCase>
<FrameworkCase frameworks={["html"]}>
<TabsRoot
aria-label="HTML implementation"
titles={{ html: 'HTML', css: 'CSS', javascript: 'JS' }}
client:load
>
<TabsPanel value="html" client:load>
<ServerCode code={htmlStr} lang="html" />
</TabsPanel>
### HTML
<TabsPanel value="css" client:load>
<ServerCode code={htmlCssStr} lang="css" />
</TabsPanel>
<ServerCode code={htmlStr} lang="html" />
### CSS
<ServerCode code={htmlCssStr} lang="css" />
### JavaScript
<ServerCode code={htmlJsStr} lang="js" />
<TabsPanel value="javascript" client:load>
<ServerCode code={htmlJsStr} lang="js" />
</TabsPanel>
<FullscreenButtonDemo client:load />
</TabsRoot>
</FrameworkCase>
## Data Attributes
+35 -25
View File
@@ -13,7 +13,8 @@ import htmlCssStr from '@/examples/html/mute-button/mute-button.css?raw';
import htmlJsStr from '@/examples/html/mute-button/mute-button.js?raw';
import FrameworkCase from '@/components/docs/FrameworkCase.astro';
import Container from '@/components/docs/Container.astro';
import ServerCode from '@/components/ServerCode.astro';
import ServerCode from '@/components/Code/ServerCode.astro';
import { TabsRoot, TabsPanel } from '@/components/Tabs';
## Features
@@ -22,41 +23,50 @@ import ServerCode from '@/components/ServerCode.astro';
- Toggles mute/unmute on click
- Accessible keyboard navigation
## Live Example
<Container>
<MuteButtonDemo client:load />
</Container>
## Usage
## Example
<FrameworkCase frameworks={["react"]}>
<TabsRoot
aria-label="React implementation"
titles={{ component: 'Component', css: 'CSS Module' }}
client:load
>
<TabsPanel value="component" client:load>
<ServerCode code={componentModuleStr} lang="tsx" />
</TabsPanel>
### Component
<ServerCode code={componentModuleStr} lang="tsx" />
### CSS Module
<ServerCode code={cssModuleStr} lang="css" />
<TabsPanel value="css" client:load>
<ServerCode code={cssModuleStr} lang="css" />
</TabsPanel>
<MuteButtonDemo client:load />
</TabsRoot>
</FrameworkCase>
<FrameworkCase frameworks={["html"]}>
<TabsRoot
aria-label="HTML implementation"
titles={{ html: 'HTML', css: 'CSS', javascript: 'JS' }}
client:load
>
<TabsPanel value="html" client:load>
<ServerCode code={htmlStr} lang="html" />
</TabsPanel>
### HTML
<TabsPanel value="css" client:load>
<ServerCode code={htmlCssStr} lang="css" />
</TabsPanel>
<ServerCode code={htmlStr} lang="html" />
### CSS
<ServerCode code={htmlCssStr} lang="css" />
### JavaScript
<ServerCode code={htmlJsStr} lang="js" />
<TabsPanel value="javascript" client:load>
<ServerCode code={htmlJsStr} lang="js" />
</TabsPanel>
<MuteButtonDemo client:load />
</TabsRoot>
</FrameworkCase>
## Data Attributes
The MuteButton automatically sets data attributes based on volume level:
+32 -25
View File
@@ -13,7 +13,8 @@ import htmlCssStr from '@/examples/html/play-button/play-button.css?raw';
import htmlJsStr from '@/examples/html/play-button/play-button.js?raw';
import FrameworkCase from '@/components/docs/FrameworkCase.astro';
import Container from '@/components/docs/Container.astro';
import ServerCode from '@/components/ServerCode.astro';
import ServerCode from '@/components/Code/ServerCode.astro';
import { TabsRoot, TabsPanel } from '@/components/Tabs';
## Features
@@ -22,40 +23,46 @@ import ServerCode from '@/components/ServerCode.astro';
- Accessible keyboard navigation
- Works with any media element
## Live Example
<Container>
<PlayButtonDemo client:load />
</Container>
## Usage
## Example
<FrameworkCase frameworks={["react"]}>
<TabsRoot
aria-label="React implementation"
titles={{ component: 'Component', css: 'CSS Module' }}
client:load
>
<TabsPanel value="component" client:load>
<ServerCode code={componentModuleStr} lang="tsx" />
</TabsPanel>
### Component
<ServerCode code={componentModuleStr} lang="tsx" />
### CSS Module
<ServerCode code={cssModuleStr} lang="css" />
<TabsPanel value="css" client:load>
<ServerCode code={cssModuleStr} lang="css" />
</TabsPanel>
<PlayButtonDemo client:load />
</TabsRoot>
</FrameworkCase>
<FrameworkCase frameworks={["html"]}>
<TabsRoot
aria-label="HTML implementation"
titles={{ html: 'HTML', css: 'CSS', javascript: 'JS' }}
client:load
>
<TabsPanel value="html" client:load>
<ServerCode code={htmlStr} lang="html" />
</TabsPanel>
### HTML
<TabsPanel value="css" client:load>
<ServerCode code={htmlCssStr} lang="css" />
</TabsPanel>
<ServerCode code={htmlStr} lang="html" />
### CSS
<ServerCode code={htmlCssStr} lang="css" />
### JavaScript
<ServerCode code={htmlJsStr} lang="js" />
<TabsPanel value="javascript" client:load>
<ServerCode code={htmlJsStr} lang="js" />
</TabsPanel>
<PlayButtonDemo client:load />
</TabsRoot>
</FrameworkCase>
## Data Attributes
+32 -25
View File
@@ -13,7 +13,8 @@ import htmlVerticalStr from '@/examples/html/time-slider/snippet-vertical.html?r
import htmlCssStr from '@/examples/html/time-slider/time-slider.css?raw';
import FrameworkCase from '@/components/docs/FrameworkCase.astro';
import Container from '@/components/docs/Container.astro';
import ServerCode from '@/components/ServerCode.astro';
import ServerCode from '@/components/Code/ServerCode.astro';
import { TabsRoot, TabsPanel } from '@/components/Tabs';
## Features
@@ -23,40 +24,46 @@ import ServerCode from '@/components/ServerCode.astro';
- Keyboard accessible (Arrow keys for seeking)
- Touch-friendly drag interaction
## Live Example
<Container>
<TimeSliderDemo client:load />
</Container>
## Usage
## Example
<FrameworkCase frameworks={["react"]}>
<TabsRoot
aria-label="React implementation"
titles={{ component: 'Component', css: 'CSS Module' }}
client:load
>
<TabsPanel value="component" client:load>
<ServerCode code={componentModuleStr} lang="tsx" />
</TabsPanel>
### Component
<ServerCode code={componentModuleStr} lang="tsx" />
### CSS Module
<ServerCode code={cssModuleStr} lang="css" />
<TabsPanel value="css" client:load>
<ServerCode code={cssModuleStr} lang="css" />
</TabsPanel>
<TimeSliderDemo client:load />
</TabsRoot>
</FrameworkCase>
<FrameworkCase frameworks={["html"]}>
<TabsRoot
aria-label="HTML implementation"
titles={{ 'html-horizontal': 'HTML (Horizontal)', 'html-vertical': 'HTML (Vertical)', css: 'CSS' }}
client:load
>
<TabsPanel value="html-horizontal" client:load>
<ServerCode code={htmlHorizontalStr} lang="html" />
</TabsPanel>
### Horizontal Orientation
<TabsPanel value="html-vertical" client:load>
<ServerCode code={htmlVerticalStr} lang="html" />
</TabsPanel>
<ServerCode code={htmlHorizontalStr} lang="html" />
### Vertical Orientation
<ServerCode code={htmlVerticalStr} lang="html" />
### CSS
<ServerCode code={htmlCssStr} lang="css" />
<TabsPanel value="css" client:load>
<ServerCode code={htmlCssStr} lang="css" />
</TabsPanel>
<TimeSliderDemo client:load />
</TabsRoot>
</FrameworkCase>
<FrameworkCase frameworks={["react"]}>
@@ -13,7 +13,8 @@ import htmlVerticalStr from '@/examples/html/volume-slider/snippet-vertical.html
import htmlCssStr from '@/examples/html/volume-slider/volume-slider.css?raw';
import FrameworkCase from '@/components/docs/FrameworkCase.astro';
import Container from '@/components/docs/Container.astro';
import ServerCode from '@/components/ServerCode.astro';
import ServerCode from '@/components/Code/ServerCode.astro';
import { TabsRoot, TabsPanel } from '@/components/Tabs';
## Features
@@ -23,40 +24,46 @@ import ServerCode from '@/components/ServerCode.astro';
- Keyboard accessible (Arrow keys for volume adjustment)
- Touch-friendly drag interaction
## Live Example
<Container>
<VolumeSliderDemo client:load />
</Container>
## Usage
## Example
<FrameworkCase frameworks={["react"]}>
<TabsRoot
aria-label="React implementation"
titles={{ component: 'Component', css: 'CSS Module' }}
client:load
>
<TabsPanel value="component" client:load>
<ServerCode code={componentModuleStr} lang="tsx" />
</TabsPanel>
### Component
<ServerCode code={componentModuleStr} lang="tsx" />
### CSS Module
<ServerCode code={cssModuleStr} lang="css" />
<TabsPanel value="css" client:load>
<ServerCode code={cssModuleStr} lang="css" />
</TabsPanel>
<VolumeSliderDemo client:load />
</TabsRoot>
</FrameworkCase>
<FrameworkCase frameworks={["html"]}>
<TabsRoot
aria-label="HTML implementation"
titles={{ 'html-horizontal': 'HTML (Horizontal)', 'html-vertical': 'HTML (Vertical)', css: 'CSS' }}
client:load
>
<TabsPanel value="html-horizontal" client:load>
<ServerCode code={htmlHorizontalStr} lang="html" />
</TabsPanel>
### Horizontal Orientation
<TabsPanel value="html-vertical" client:load>
<ServerCode code={htmlVerticalStr} lang="html" />
</TabsPanel>
<ServerCode code={htmlHorizontalStr} lang="html" />
### Vertical Orientation
<ServerCode code={htmlVerticalStr} lang="html" />
### CSS
<ServerCode code={htmlCssStr} lang="css" />
<TabsPanel value="css" client:load>
<ServerCode code={htmlCssStr} lang="css" />
</TabsPanel>
<VolumeSliderDemo client:load />
</TabsRoot>
</FrameworkCase>
<FrameworkCase frameworks={["react"]}>