Merge branch 'master' into docs-ui-props-updates

This commit is contained in:
Charles de Dreuille
2026-02-03 08:06:09 +00:00
166 changed files with 11586 additions and 1375 deletions
-8
View File
@@ -1,8 +0,0 @@
{
"extends": "next/core-web-vitals",
"rules": {
"notice/notice": "off",
"react/forbid-elements": "off",
"jsx-a11y/alt-text": "off"
}
}
+16
View File
@@ -0,0 +1,16 @@
import { defineConfig, globalIgnores } from 'eslint/config';
import nextCoreWebVitals from 'eslint-config-next/core-web-vitals';
const eslintConfig = defineConfig([
...nextCoreWebVitals,
globalIgnores(['.next/**', 'dist/**', 'node_modules/**']),
{
rules: {
'notice/notice': 'off',
'react/forbid-elements': 'off',
'jsx-a11y/alt-text': 'off',
},
},
]);
export default eslintConfig;
+2 -2
View File
@@ -5,7 +5,7 @@
"scripts": {
"prebuild": "yarn sync:css",
"build": "next build",
"lint": "next lint",
"lint": "eslint .",
"prestart": "yarn sync:css",
"start": "next dev",
"sync:changelog": "node scripts/sync-changelog.mjs",
@@ -47,7 +47,7 @@
"@types/react": "19.2.10",
"@types/react-dom": "19.2.3",
"chokidar": "^3.6.0",
"eslint": "^8",
"eslint": "^9",
"eslint-config-next": "16.1.6",
"lightningcss": "^1.28.2",
"typescript": "^5",
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
-1
View File
@@ -1,5 +1,4 @@
#!/usr/bin/env node
/* eslint-disable no-restricted-syntax */
import fs from 'fs';
import path from 'path';
+6 -6
View File
@@ -6,7 +6,7 @@ const chokidar = require('chokidar');
// Configuration
const config = {
UIPath: '../../packages/ui',
publicPath: '../public',
outputPath: '../src/css',
files: [
{
source: 'css/styles.css',
@@ -24,13 +24,13 @@ const config = {
class CSSSync {
constructor() {
this.UIPath = path.resolve(__dirname, config.UIPath);
this.publicPath = path.resolve(__dirname, config.publicPath);
this.outputPath = path.resolve(__dirname, config.outputPath);
this.isWatching = process.argv.includes('--watch');
}
async syncFile(fileConfig) {
const sourcePath = path.join(this.UIPath, fileConfig.source);
const destPath = path.join(this.publicPath, fileConfig.destination);
const destPath = path.join(this.outputPath, fileConfig.destination);
try {
// Check if source file exists
@@ -76,9 +76,9 @@ class CSSSync {
);
if (successCount > 0) {
console.log('\n📁 Available CSS files in public/:');
console.log('\n📁 Available CSS files in src/css/:');
config.files.forEach(file => {
const destPath = path.join(this.publicPath, file.destination);
const destPath = path.join(this.outputPath, file.destination);
if (fs.existsSync(destPath)) {
const stats = fs.statSync(destPath);
const size = (stats.size / 1024).toFixed(2);
@@ -129,7 +129,7 @@ class CSSSync {
async run() {
console.log('🎨 BUI CSS Sync Tool\n');
console.log(`📂 BUI path: ${this.UIPath}`);
console.log(`📂 Public path: ${this.publicPath}\n`);
console.log(`📂 Output path: ${this.outputPath}\n`);
// Initial sync
await this.syncAll();
@@ -2,11 +2,12 @@
import { Box } from '../../../../../packages/ui/src/components/Box/Box';
import { Flex } from '../../../../../packages/ui/src/components/Flex/Flex';
import { DecorativeBox } from '@/components/DecorativeBox';
export const Default = () => {
return (
<Box p="4" surface="1">
Hello World
<Box width="64px" height="64px">
<DecorativeBox />
</Box>
);
};
@@ -36,7 +37,7 @@ export const Responsive = () => {
p={{ initial: '2', md: '4' }}
display={{ initial: 'block', md: 'flex' }}
>
Hello World
<DecorativeBox>Resize to see change</DecorativeBox>
</Box>
);
};
+1 -1
View File
@@ -34,7 +34,7 @@ import { ChangelogComponent } from '@/components/ChangelogComponent';
### Surface
Here's a view when boxes have different surface levels.
Use surface levels to create visual hierarchy.
<Snippet preview={<Surface />} code={boxSurfaceSnippet} layout="side-by-side" />
+3 -3
View File
@@ -4,8 +4,8 @@ export const snippetUsage = `import { Box } from '@backstage/ui';
Content with padding and background
</Box>`;
export const defaultSnippet = `<Box p="4" surface="1">
Hello World
export const defaultSnippet = `<Box width="64px" height="64px">
<DecorativeBox />
</Box>`;
export const boxSurfaceSnippet = `<Flex direction="column" gap="4">
@@ -19,5 +19,5 @@ export const boxResponsiveSnippet = `<Box
p={{ initial: '2', md: '4' }}
display={{ initial: 'block', md: 'flex' }}
>
Hello World
Content
</Box>`;
@@ -1,33 +1,20 @@
'use client';
import { Container } from '../../../../../packages/ui/src/components/Container/Container';
import { Box } from '../../../../../packages/ui/src/components/Box/Box';
import { Text } from '../../../../../packages/ui/src/components/Text/Text';
import { DecorativeBox } from '@/components/DecorativeBox';
export const Preview = () => {
return (
<Container py="4">
<Text>Content is centered with max-width</Text>
</Container>
);
};
export const BasicUsagePreview = () => {
export const Default = () => {
return (
<Container>
<Box>Hello World</Box>
<Box>Hello World</Box>
<Box>Hello World</Box>
<DecorativeBox>Page content goes here</DecorativeBox>
</Container>
);
};
export const ResponsiveSpacingPreview = () => {
export const ResponsiveSpacing = () => {
return (
<Container py={{ initial: '2', md: '4' }}>
<Box>Hello World</Box>
<Box>Hello World</Box>
<Box>Hello World</Box>
<Container py={{ initial: '4', md: '8' }}>
<DecorativeBox>Content with vertical spacing</DecorativeBox>
</Container>
);
};
+12 -25
View File
@@ -4,15 +4,10 @@ import { Snippet } from '@/components/Snippet';
import { containerPropDefs } from './props-definition';
import {
containerUsageSnippet,
previewSnippet,
containerSimpleSnippet,
defaultSnippet,
containerResponsiveSnippet,
} from './snippets';
import {
Preview,
BasicUsagePreview,
ResponsiveSpacingPreview,
} from './components';
import { Default, ResponsiveSpacing } from './components';
import { PageTitle } from '@/components/PageTitle';
import { Theming } from '@/components/Theming';
import { ContainerDefinition } from '../../../utils/definitions';
@@ -21,38 +16,30 @@ import { ChangelogComponent } from '@/components/ChangelogComponent';
<PageTitle
type="Layout"
title="Container"
description="A centered layout wrapper with max-width and vertical spacing props."
description="A page wrapper that centers content and constrains its maximum width."
/>
<Snippet py={4} preview={<Preview />} code={previewSnippet} />
<Snippet preview={<Default />} code={defaultSnippet} />
## Usage
<CodeBlock code={containerUsageSnippet} />
## API reference
## Core Concepts
Container provides the standard page layout for plugin content. It constrains content to a maximum width, centers it horizontally, and adds consistent horizontal gutters. Use it once per page to wrap your main content area.
## API Reference
<PropsTable data={containerPropDefs} />
## Examples
### Basic Usage
### Responsive spacing
<Snippet
preview={<BasicUsagePreview />}
code={containerSimpleSnippet}
layout="side-by-side"
/>
Vertical spacing props accept breakpoint objects.
### Responsive Spacing
Spacing props accept breakpoint objects for responsive behavior.
<Snippet
preview={<ResponsiveSpacingPreview />}
code={containerResponsiveSnippet}
layout="side-by-side"
/>
<Snippet preview={<ResponsiveSpacing />} code={containerResponsiveSnippet} />
<Theming definition={ContainerDefinition} />
@@ -1,19 +1,13 @@
export const containerUsageSnippet = `import { Container } from "@backstage/ui";
<Container>Hello World!</Container>`;
export const previewSnippet = `<Container py="4">
Content is centered with max-width
<Container>
{/* Your plugin's main content */}
</Container>`;
export const containerSimpleSnippet = `<Container>
<Box>Hello World</Box>
<Box>Hello World</Box>
<Box>Hello World</Box>
export const defaultSnippet = `<Container>
<DecorativeBox>Page content goes here</DecorativeBox>
</Container>`;
export const containerResponsiveSnippet = `<Container py={{ initial: '2', md: '4' }}>
<Box>Hello World</Box>
<Box>Hello World</Box>
<Box>Hello World</Box>
export const containerResponsiveSnippet = `<Container py={{ initial: '4', md: '8' }}>
<DecorativeBox>Content with vertical spacing</DecorativeBox>
</Container>`;
+13 -28
View File
@@ -1,29 +1,14 @@
'use client';
import { Flex } from '../../../../../packages/ui/src/components/Flex/Flex';
import { Box } from '../../../../../packages/ui/src/components/Box/Box';
const boxStyle = {
width: '64px',
height: '64px',
background: '#eaf2fd',
border: '1px solid #2563eb',
borderRadius: '4px',
};
const smallBoxStyle = {
padding: '8px 12px',
background: '#eaf2fd',
border: '1px solid #2563eb',
borderRadius: '4px',
};
import { DecorativeBox } from '@/components/DecorativeBox';
export const Default = () => {
return (
<Flex gap="4">
<Box style={boxStyle} />
<Box style={boxStyle} />
<Box style={boxStyle} />
<DecorativeBox />
<DecorativeBox />
<DecorativeBox />
</Flex>
);
};
@@ -31,9 +16,9 @@ export const Default = () => {
export const DirectionExample = () => {
return (
<Flex direction="column" gap="2">
<Box style={smallBoxStyle}>First</Box>
<Box style={smallBoxStyle}>Second</Box>
<Box style={smallBoxStyle}>Third</Box>
<DecorativeBox>First</DecorativeBox>
<DecorativeBox>Second</DecorativeBox>
<DecorativeBox>Third</DecorativeBox>
</Flex>
);
};
@@ -41,9 +26,9 @@ export const DirectionExample = () => {
export const ResponsiveExample = () => {
return (
<Flex gap={{ initial: '2', md: '4' }}>
<Box style={smallBoxStyle}>Item 1</Box>
<Box style={smallBoxStyle}>Item 2</Box>
<Box style={smallBoxStyle}>Item 3</Box>
<DecorativeBox>1</DecorativeBox>
<DecorativeBox>2</DecorativeBox>
<DecorativeBox>3</DecorativeBox>
</Flex>
);
};
@@ -51,9 +36,9 @@ export const ResponsiveExample = () => {
export const AlignExample = () => {
return (
<Flex align="center" justify="between" gap="4" style={{ width: '300px' }}>
<Box style={smallBoxStyle}>Start</Box>
<Box style={smallBoxStyle}>Middle</Box>
<Box style={smallBoxStyle}>End</Box>
<DecorativeBox>Start</DecorativeBox>
<DecorativeBox>Middle</DecorativeBox>
<DecorativeBox>End</DecorativeBox>
</Flex>
);
};
+12 -12
View File
@@ -7,25 +7,25 @@ export const flexUsageSnippet = `import { Flex } from '@backstage/ui';
</Flex>`;
export const defaultSnippet = `<Flex gap="4">
<Box style={{ width: '64px', height: '64px' }} />
<Box style={{ width: '64px', height: '64px' }} />
<Box style={{ width: '64px', height: '64px' }} />
<DecorativeBox />
<DecorativeBox />
<DecorativeBox />
</Flex>`;
export const flexDirectionSnippet = `<Flex direction="column" gap="2">
<Box>First</Box>
<Box>Second</Box>
<Box>Third</Box>
<DecorativeBox>First</DecorativeBox>
<DecorativeBox>Second</DecorativeBox>
<DecorativeBox>Third</DecorativeBox>
</Flex>`;
export const flexResponsiveSnippet = `<Flex gap={{ initial: '2', md: '4' }}>
<Box>Item 1</Box>
<Box>Item 2</Box>
<Box>Item 3</Box>
<DecorativeBox>1</DecorativeBox>
<DecorativeBox>2</DecorativeBox>
<DecorativeBox>3</DecorativeBox>
</Flex>`;
export const flexAlignSnippet = `<Flex align="center" justify="between" gap="4">
<Box>Start</Box>
<Box>Middle</Box>
<Box>End</Box>
<DecorativeBox>Start</DecorativeBox>
<DecorativeBox>Middle</DecorativeBox>
<DecorativeBox>End</DecorativeBox>
</Flex>`;
+15 -29
View File
@@ -1,31 +1,17 @@
'use client';
import { Grid } from '../../../../../packages/ui/src/components/Grid/Grid';
import { Box } from '../../../../../packages/ui/src/components/Box/Box';
const boxStyle = {
height: '64px',
background: '#eaf2fd',
border: '1px solid #2563eb',
borderRadius: '4px',
};
const labelBoxStyle = {
padding: '8px 12px',
background: '#eaf2fd',
border: '1px solid #2563eb',
borderRadius: '4px',
};
import { DecorativeBox } from '@/components/DecorativeBox';
export const Default = () => {
return (
<Grid.Root columns="3" gap="4">
<Box style={boxStyle} />
<Box style={boxStyle} />
<Box style={boxStyle} />
<Box style={boxStyle} />
<Box style={boxStyle} />
<Box style={boxStyle} />
<DecorativeBox />
<DecorativeBox />
<DecorativeBox />
<DecorativeBox />
<DecorativeBox />
<DecorativeBox />
</Grid.Root>
);
};
@@ -33,10 +19,10 @@ export const Default = () => {
export const ResponsiveExample = () => {
return (
<Grid.Root columns={{ initial: '2', md: '4' }} gap="4">
<Box style={labelBoxStyle}>1</Box>
<Box style={labelBoxStyle}>2</Box>
<Box style={labelBoxStyle}>3</Box>
<Box style={labelBoxStyle}>4</Box>
<DecorativeBox>1</DecorativeBox>
<DecorativeBox>2</DecorativeBox>
<DecorativeBox>3</DecorativeBox>
<DecorativeBox>4</DecorativeBox>
</Grid.Root>
);
};
@@ -44,11 +30,11 @@ export const ResponsiveExample = () => {
export const GridItemExample = () => {
return (
<Grid.Root columns="4" gap="4">
<Grid.Item colSpan="2" style={labelBoxStyle}>
Spans 2 columns
<Grid.Item colSpan="2">
<DecorativeBox>Spans 2 columns</DecorativeBox>
</Grid.Item>
<Box style={labelBoxStyle}>1 column</Box>
<Box style={labelBoxStyle}>1 column</Box>
<DecorativeBox>1 column</DecorativeBox>
<DecorativeBox>1 column</DecorativeBox>
</Grid.Root>
);
};
+15 -13
View File
@@ -7,23 +7,25 @@ export const gridUsageSnippet = `import { Grid } from '@backstage/ui';
</Grid.Root>`;
export const defaultSnippet = `<Grid.Root columns="3" gap="4">
<Box style={{ height: '64px' }} />
<Box style={{ height: '64px' }} />
<Box style={{ height: '64px' }} />
<Box style={{ height: '64px' }} />
<Box style={{ height: '64px' }} />
<Box style={{ height: '64px' }} />
<DecorativeBox />
<DecorativeBox />
<DecorativeBox />
<DecorativeBox />
<DecorativeBox />
<DecorativeBox />
</Grid.Root>`;
export const gridResponsiveSnippet = `<Grid.Root columns={{ initial: '2', md: '4' }} gap="4">
<Box>1</Box>
<Box>2</Box>
<Box>3</Box>
<Box>4</Box>
<DecorativeBox>1</DecorativeBox>
<DecorativeBox>2</DecorativeBox>
<DecorativeBox>3</DecorativeBox>
<DecorativeBox>4</DecorativeBox>
</Grid.Root>`;
export const gridItemSnippet = `<Grid.Root columns="4" gap="4">
<Grid.Item colSpan="2">Spans 2 columns</Grid.Item>
<Grid.Item>1 column</Grid.Item>
<Grid.Item>1 column</Grid.Item>
<Grid.Item colSpan="2">
<DecorativeBox>Spans 2 columns</DecorativeBox>
</Grid.Item>
<DecorativeBox>1 column</DecorativeBox>
<DecorativeBox>1 column</DecorativeBox>
</Grid.Root>`;
+30 -10
View File
@@ -7,7 +7,9 @@ import { MemoryRouter } from 'react-router-dom';
export const Default = () => {
return (
<MemoryRouter>
<Link href="/">Sign up for Backstage</Link>
<Link href="/" variant="body-large">
Sign up for Backstage
</Link>
</MemoryRouter>
);
};
@@ -15,7 +17,7 @@ export const Default = () => {
export const ExternalLink = () => {
return (
<MemoryRouter>
<Link href="https://backstage.io" target="_blank">
<Link href="https://backstage.io" target="_blank" variant="body-large">
Sign up for Backstage
</Link>
</MemoryRouter>
@@ -58,22 +60,25 @@ export const AllVariants = () => {
export const AllColors = () => {
return (
<MemoryRouter>
<Flex gap="4" direction="column">
<Link href="#" color="primary">
<Flex gap="2" direction="column">
<Link href="#" color="primary" variant="body-large">
Primary
</Link>
<Link href="#" color="secondary">
<Link href="#" color="secondary" variant="body-large">
Secondary
</Link>
<Link href="#" color="danger">
<Link href="#" color="danger" variant="body-large">
Danger
</Link>
<Link href="#" color="warning">
<Link href="#" color="warning" variant="body-large">
Warning
</Link>
<Link href="#" color="success">
<Link href="#" color="success" variant="body-large">
Success
</Link>
<Link href="#" color="info" variant="body-large">
Info
</Link>
</Flex>
</MemoryRouter>
);
@@ -83,13 +88,28 @@ export const Weight = () => {
return (
<MemoryRouter>
<Flex gap="4">
<Link href="#" weight="regular">
<Link href="#" weight="regular" variant="body-large">
Regular
</Link>
<Link href="#" weight="bold">
<Link href="#" weight="bold" variant="body-large">
Bold
</Link>
</Flex>
</MemoryRouter>
);
};
export const Standalone = () => {
return (
<MemoryRouter>
<Flex gap="4">
<Link href="#" variant="body-large">
Default link
</Link>
<Link href="#" variant="body-large" standalone>
Standalone link
</Link>
</Flex>
</MemoryRouter>
);
};
+22 -1
View File
@@ -10,6 +10,7 @@ import {
allVariantsSnippet,
allColorsSnippet,
weightSnippet,
standaloneSnippet,
} from './snippets';
import {
Default,
@@ -17,6 +18,7 @@ import {
AllVariants,
AllColors,
Weight,
Standalone,
} from './components';
import { PageTitle } from '@/components/PageTitle';
import { Theming } from '@/components/Theming';
@@ -55,6 +57,7 @@ Use `target="_blank"` to open links in a new tab.
py={4}
preview={<ExternalLink />}
code={externalLinkSnippet}
layout="side-by-side"
/>
### Variants
@@ -79,7 +82,25 @@ Status colors for contextual links.
### Weight
<Snippet align="center" py={4} preview={<Weight />} code={weightSnippet} />
<Snippet
align="center"
py={4}
preview={<Weight />}
code={weightSnippet}
layout="side-by-side"
/>
### Standalone
Use `standalone` to remove the underline by default. The underline will appear on hover.
<Snippet
align="center"
py={4}
preview={<Standalone />}
code={standaloneSnippet}
layout="side-by-side"
/>
<Theming definition={LinkDefinition} />
@@ -66,6 +66,12 @@ export const linkPropDefs: Record<string, PropDef> = {
type: 'boolean',
description:
'Truncates text with ellipsis when it overflows its container.',
default: 'false',
},
standalone: {
type: 'boolean',
description: 'Removes underline by default. Underline appears on hover.',
default: 'false',
},
...childrenPropDefs,
...classNamePropDefs,
+7 -1
View File
@@ -4,7 +4,7 @@ export const linkUsageSnippet = `import { Link } from '@backstage/ui';
export const defaultSnippet = `<Link href="/">Sign up for Backstage</Link>`;
export const externalLinkSnippet = `<Link href="https://backstage.io" target="_blank">
export const externalLinkSnippet = `<Link href="#" target="_blank">
Sign up for Backstage
</Link>`;
@@ -25,9 +25,15 @@ export const allColorsSnippet = `<Flex gap="4" direction="column">
<Link href="#" color="danger">Danger</Link>
<Link href="#" color="warning">Warning</Link>
<Link href="#" color="success">Success</Link>
<Link href="#" color="info">Info</Link>
</Flex>`;
export const weightSnippet = `<Flex gap="4">
<Link href="#" weight="regular">Regular</Link>
<Link href="#" weight="bold">Bold</Link>
</Flex>`;
export const standaloneSnippet = `<Flex gap="4">
<Link href="#">Default link</Link>
<Link href="#" standalone>Standalone link</Link>
</Flex>`;
@@ -30,6 +30,7 @@ export const Colors = () => {
<Text color="danger">Danger</Text>
<Text color="warning">Warning</Text>
<Text color="success">Success</Text>
<Text color="info">Info</Text>
</Flex>
);
};
@@ -21,6 +21,7 @@ export const colorsSnippet = `<Flex direction="column" gap="2">
<Text color="danger">Danger</Text>
<Text color="warning">Warning</Text>
<Text color="success">Success</Text>
<Text color="info">Info</Text>
</Flex>`;
export const weightsSnippet = `<Flex direction="column" gap="2">
+2
View File
@@ -8,6 +8,8 @@ import { MobileBottomNav } from '@/components/MobileBottomNav';
import styles from './layout.module.css';
import '../css/globals.css';
import '../css/theme-backstage.css';
import '../css/theme-spotify.css';
export const metadata: Metadata = {
title: 'Backstage UI',
+50 -58
View File
@@ -240,30 +240,6 @@ color of your app.
</Table.Cell>
<Table.Cell>Used for solid background colors when disabled.</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>
<Chip head>--bui-bg-tint</Chip>
</Table.Cell>
<Table.Cell>Used for tint background colors.</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>
<Chip head>--bui-bg-tint-hover</Chip>
</Table.Cell>
<Table.Cell>Used for tint background colors when hovered.</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>
<Chip head>--bui-bg-tint-focus</Chip>
</Table.Cell>
<Table.Cell>Used for tint background colors when active.</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>
<Chip head>--bui-bg-tint-disabled</Chip>
</Table.Cell>
<Table.Cell>Used for tint background colors when disabled.</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>
<Chip head>--bui-bg-danger</Chip>
@@ -282,6 +258,12 @@ color of your app.
</Table.Cell>
<Table.Cell>Used to show success information.</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>
<Chip head>--bui-bg-info</Chip>
</Table.Cell>
<Table.Cell>Used to show informational content.</Table.Cell>
</Table.Row>
</Table.Body>
</Table.Root>
@@ -315,22 +297,6 @@ are prefixed with `fg` to make it easier to identify.
It should be used on top of main background surfaces.
</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>
<Chip head>--bui-fg-link</Chip>
</Table.Cell>
<Table.Cell>
It should be used on top of main background surfaces.
</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>
<Chip head>--bui-fg-link-hover</Chip>
</Table.Cell>
<Table.Cell>
It should be used on top of main background surfaces.
</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>
<Chip head>--bui-fg-disabled</Chip>
@@ -347,33 +313,45 @@ are prefixed with `fg` to make it easier to identify.
It should be used on top of solid background colors.
</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>
<Chip head>--bui-fg-tint</Chip>
</Table.Cell>
<Table.Cell>
It should be used on top of tint background colors.
</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>
<Chip head>--bui-fg-tint-disabled</Chip>
</Table.Cell>
<Table.Cell>
It should be used on top of tint background colors when disabled.
</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>
<Chip head>--bui-fg-danger</Chip>
</Table.Cell>
<Table.Cell>Used for error states and destructive actions.</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>
<Chip head>--bui-fg-warning</Chip>
</Table.Cell>
<Table.Cell>
Used for warning states and cautionary information.
</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>
<Chip head>--bui-fg-success</Chip>
</Table.Cell>
<Table.Cell>Used for success states and positive feedback.</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>
<Chip head>--bui-fg-info</Chip>
</Table.Cell>
<Table.Cell>
Used for informational content and neutral status.
</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>
<Chip head>--bui-fg-danger-on-bg</Chip>
</Table.Cell>
<Table.Cell>
It should be used on top of danger background colors.
</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>
<Chip head>--bui-fg-warning</Chip>
<Chip head>--bui-fg-warning-on-bg</Chip>
</Table.Cell>
<Table.Cell>
It should be used on top of warning background colors.
@@ -381,12 +359,20 @@ are prefixed with `fg` to make it easier to identify.
</Table.Row>
<Table.Row>
<Table.Cell>
<Chip head>--bui-fg-success</Chip>
<Chip head>--bui-fg-success-on-bg</Chip>
</Table.Cell>
<Table.Cell>
It should be used on top of success background colors.
</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>
<Chip head>--bui-fg-info-on-bg</Chip>
</Table.Cell>
<Table.Cell>
It should be used on top of info background colors.
</Table.Cell>
</Table.Row>
</Table.Body>
</Table.Root>
@@ -449,6 +435,12 @@ low contrast to help as a separator with the different background colors.
</Table.Cell>
<Table.Cell>It should be used on top of `--bui-bg-success`.</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>
<Chip head>--bui-border-info</Chip>
</Table.Cell>
<Table.Cell>It should be used on top of `--bui-bg-info`.</Table.Cell>
</Table.Row>
</Table.Body>
</Table.Root>
@@ -1,6 +1,6 @@
'use client';
import { useEffect, useState, useCallback } from 'react';
import { useEffect, useState, useCallback, useSyncExternalStore } from 'react';
import CodeMirror from '@uiw/react-codemirror';
import { sass } from '@codemirror/lang-sass';
import styles from './styles.module.css';
@@ -14,6 +14,10 @@ const defaultTheme = `:root {
--bui-bg-solid: #000;
}`;
// Stable server snapshots for useSyncExternalStore
const serverIsClient = false;
const serverDefaultTheme = defaultTheme;
const myTheme = createTheme({
theme: 'light',
settings: {
@@ -46,12 +50,41 @@ const myTheme = createTheme({
});
export const CustomTheme = () => {
const [isClient, setIsClient] = useState(false);
const [open, setOpen] = useState(true);
const [customTheme, setCustomTheme] = useState<string | undefined>(undefined);
const { selectedThemeName } = usePlayground();
const [savedMessage, setSavedMessage] = useState<string>('Save');
// SSR-safe client detection
const isClient = useSyncExternalStore(
() => () => {},
() => true,
() => serverIsClient,
);
// SSR-safe localStorage access for custom theme
const customThemeFromStorage = useSyncExternalStore(
callback => {
window.addEventListener('storage', callback);
return () => window.removeEventListener('storage', callback);
},
() => {
const stored = localStorage.getItem('customThemeCss');
if (!stored) {
localStorage.setItem('customThemeCss', defaultTheme);
return defaultTheme;
}
return stored;
},
() => serverDefaultTheme,
);
const [customTheme, setCustomTheme] = useState(customThemeFromStorage);
// Sync from storage when it changes
useEffect(() => {
setCustomTheme(customThemeFromStorage);
}, [customThemeFromStorage]);
const updateStyleElement = (theme: string) => {
let styleElement = document.getElementById(
'custom-theme-style',
@@ -66,16 +99,11 @@ export const CustomTheme = () => {
styleElement.textContent = theme;
};
// Apply custom theme to DOM
useEffect(() => {
if (selectedThemeName === 'custom') {
let storedTheme = localStorage.getItem('customThemeCss');
if (!storedTheme) {
storedTheme = defaultTheme;
localStorage.setItem('customThemeCss', storedTheme);
}
setCustomTheme(storedTheme);
updateStyleElement(storedTheme);
} else {
if (selectedThemeName === 'custom' && customTheme && isClient) {
updateStyleElement(customTheme);
} else if (isClient) {
const styleElement = document.getElementById(
'custom-theme-style',
) as HTMLStyleElement;
@@ -83,11 +111,7 @@ export const CustomTheme = () => {
styleElement.remove();
}
}
}, [selectedThemeName]);
useEffect(() => {
setIsClient(true);
}, []);
}, [selectedThemeName, customTheme, isClient]);
const handleSave = () => {
if (customTheme) {
+14 -2
View File
@@ -1,5 +1,17 @@
'use client';
import { CSSProperties, ReactNode } from 'react';
import styles from './styles.module.css';
export const DecorativeBox = () => {
return <div className={styles.box} />;
interface DecorativeBoxProps {
children?: ReactNode;
style?: CSSProperties;
}
export const DecorativeBox = ({ children, style }: DecorativeBoxProps) => {
return (
<div className={styles.box} style={style}>
{children}
</div>
);
};
@@ -1,8 +1,14 @@
.box {
min-width: 64px;
min-height: 64px;
padding: 8px 12px;
background-color: #eaf2fd;
border-radius: 4px;
box-shadow: 0 0 0 1px #2563eb;
background-image: url("data:image/svg+xml,%3Csvg width='6' height='6' viewBox='0 0 6 6' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='%232563eb' fill-opacity='0.3' fill-rule='evenodd'%3E%3Cpath d='M5 0h1L0 6V5zM6 5v1H5z'/%3E%3C/g%3E%3C/svg%3E");
color: #2563eb;
font-weight: 500;
display: flex;
align-items: center;
justify-content: center;
}
@@ -1,6 +1,6 @@
'use client';
import { useEffect, useState } from 'react';
import { useEffect, useState, useLayoutEffect } from 'react';
import { usePathname } from 'next/navigation';
import styles from './TableOfContents.module.css';
@@ -17,6 +17,29 @@ export function TableOfContents() {
const [indicatorHeight, setIndicatorHeight] = useState<number>(0);
const pathname = usePathname();
// Update indicator position when activeId changes
useLayoutEffect(() => {
if (!activeId) return;
// Use requestAnimationFrame to defer setState call
const rafId = requestAnimationFrame(() => {
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);
}
}
});
return () => cancelAnimationFrame(rafId);
}, [activeId, headings]);
useEffect(() => {
// Extract all H2 and H3 headings from the document
const elements = Array.from(
@@ -29,18 +52,6 @@ export function TableOfContents() {
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',
@@ -62,29 +73,26 @@ export function TableOfContents() {
elements.forEach(element => observer.observe(element));
// Initialize headings and active ID after observer is set up
requestAnimationFrame(() => {
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);
}
});
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) {
File diff suppressed because it is too large Load Diff
+239
View File
@@ -0,0 +1,239 @@
@font-face {
font-family: CircularSpTitle;
font-weight: 900;
font-display: swap;
unicode-range: U+0, U+D, U+20-7E, U+A0-17E, U+18F, U+192, U+1A0-1A1, U+1AF-1B0,
U+1B5-1B6, U+1C4-1C6, U+1F1-1F3, U+1FA-1FF, U+218-21B, U+237, U+259,
U+2BB-2BC, U+2C6-2C7, U+2C9, U+2D8-2DD, U+300-301, U+303, U+309, U+323,
U+394, U+3A9, U+3BC, U+3C0, U+1E80-1E85, U+1E8A-1E8B, U+1EA0-1EF9, U+1FD6,
U+2007-2008, U+200B, U+2010-2011, U+2013-2014, U+2018-201A, U+201C-201E,
U+2020-2022, U+2026, U+2030, U+2032-2033, U+2039-203A, U+2042, U+2044,
U+2051, U+2070, U+2074-2079, U+2080-2089, U+20AB-20AC, U+2113, U+2117,
U+2122, U+2126, U+2160-2169, U+216C-216F, U+2190-2193, U+2196-2199, U+21A9,
U+21B0-21B5, U+21C6, U+2202, U+2206, U+220F, U+2211-2212, U+2215,
U+2219-221A, U+221E, U+222B, U+2248, U+2260, U+2264-2265, U+22C5, U+24C5,
U+25A0-25A1, U+25AF, U+25B2-25B3, U+25CA-25CB, U+25CF, U+262E, U+2713,
U+2715, U+2780-2788, U+E000, U+E002, U+F6C3, U+FB00-FB04, U+FEFF, U+FF0C,
U+FF0E, U+FF1A-FF1B, U+FFFF;
src: url(https://encore.scdn.co/fonts/CircularSpTitle-Black-4588c99025b967475c31695b0743dd1a.woff2)
format('woff2'),
url(https://encore.scdn.co/fonts/CircularSpTitle-Black-506746f387a26f25aa3d023b3e501d34.woff)
format('woff');
}
@font-face {
font-family: CircularSpTitle;
font-weight: 700;
font-display: swap;
unicode-range: U+0, U+D, U+20-7E, U+A0-17E, U+18F, U+192, U+1A0-1A1, U+1AF-1B0,
U+1B5-1B6, U+1C4-1C6, U+1F1-1F3, U+1FA-1FF, U+218-21B, U+237, U+259,
U+2BB-2BC, U+2C6-2C7, U+2C9, U+2D8-2DD, U+300-301, U+303, U+309, U+323,
U+394, U+3A9, U+3BC, U+3C0, U+1E80-1E85, U+1E8A-1E8B, U+1EA0-1EF9, U+1FD6,
U+2007-2008, U+200B, U+2010-2011, U+2013-2014, U+2018-201A, U+201C-201E,
U+2020-2022, U+2026, U+2030, U+2032-2033, U+2039-203A, U+2042, U+2044,
U+2051, U+2070, U+2074-2079, U+2080-2089, U+20AB-20AC, U+2113, U+2117,
U+2122, U+2126, U+2160-2169, U+216C-216F, U+2190-2193, U+2196-2199, U+21A9,
U+21B0-21B5, U+21C6, U+2202, U+2206, U+220F, U+2211-2212, U+2215,
U+2219-221A, U+221E, U+222B, U+2248, U+2260, U+2264-2265, U+22C5, U+24C5,
U+25A0-25A1, U+25AF, U+25B2-25B3, U+25CA-25CB, U+25CF, U+262E, U+2713,
U+2715, U+2780-2788, U+E000, U+E002, U+F6C3, U+FB00-FB04, U+FEFF, U+FF0C,
U+FF0E, U+FF1A-FF1B, U+FFFF;
src: url(https://encore.scdn.co/fonts/CircularSpTitle-Bold-b2586b06a2e1522e9d879d84c2792a58.woff2)
format('woff2'),
url(https://encore.scdn.co/fonts/CircularSpTitle-Bold-1624fb2df28c20d7203d7fb86ce8b481.woff)
format('woff');
}
@font-face {
font-family: CircularSp;
font-weight: 700;
font-display: swap;
unicode-range: U+0, U+D, U+20-7E, U+A0-17E, U+18F, U+192, U+1A0-1A1, U+1AF-1B0,
U+1B5-1B6, U+1C4-1C6, U+1F1-1F3, U+1FA-1FF, U+218-21B, U+237, U+259,
U+2BB-2BC, U+2C6-2C7, U+2C9, U+2D8-2DD, U+300-301, U+303, U+309, U+323,
U+394, U+3A9, U+3BC, U+3C0, U+1E80-1E85, U+1E8A-1E8B, U+1EA0-1EF9, U+1FD6,
U+2007-2008, U+200B, U+2010-2011, U+2013-2014, U+2018-201A, U+201C-201E,
U+2020-2022, U+2026, U+2030, U+2032-2033, U+2039-203A, U+2042, U+2044,
U+2051, U+2070, U+2074-2079, U+2080-2089, U+20AB-20AC, U+2113, U+2117,
U+2122, U+2126, U+2160-2169, U+216C-216F, U+2190-2193, U+2196-2199, U+21A9,
U+21B0-21B5, U+21C6, U+2202, U+2206, U+220F, U+2211-2212, U+2215,
U+2219-221A, U+221E, U+222B, U+2248, U+2260, U+2264-2265, U+22C5, U+24C5,
U+25A0-25A1, U+25AF, U+25B2-25B3, U+25CA-25CB, U+25CF, U+262E, U+2713,
U+2715, U+2780-2788, U+E000, U+E002, U+F6C3, U+FB00-FB04, U+FEFF, U+FF0C,
U+FF0E, U+FF1A-FF1B, U+FFFF;
src: url(https://encore.scdn.co/fonts/CircularSp-Bold-602e7aefc706aa36c6ec1324b9bbc461.woff2)
format('woff2'),
url(https://encore.scdn.co/fonts/CircularSp-Bold-856afe2da4ba4e61239b129e2c16d633.woff)
format('woff');
}
@font-face {
font-family: CircularSp;
font-weight: 400;
font-display: swap;
unicode-range: U+0, U+D, U+20-7E, U+A0-17E, U+18F, U+192, U+1A0-1A1, U+1AF-1B0,
U+1B5-1B6, U+1C4-1C6, U+1F1-1F3, U+1FA-1FF, U+218-21B, U+237, U+259,
U+2BB-2BC, U+2C6-2C7, U+2C9, U+2D8-2DD, U+300-301, U+303, U+309, U+323,
U+394, U+3A9, U+3BC, U+3C0, U+1E80-1E85, U+1E8A-1E8B, U+1EA0-1EF9, U+1FD6,
U+2007-2008, U+200B, U+2010-2011, U+2013-2014, U+2018-201A, U+201C-201E,
U+2020-2022, U+2026, U+2030, U+2032-2033, U+2039-203A, U+2042, U+2044,
U+2051, U+2070, U+2074-2079, U+2080-2089, U+20AB-20AC, U+2113, U+2117,
U+2122, U+2126, U+2160-2169, U+216C-216F, U+2190-2193, U+2196-2199, U+21A9,
U+21B0-21B5, U+21C6, U+2202, U+2206, U+220F, U+2211-2212, U+2215,
U+2219-221A, U+221E, U+222B, U+2248, U+2260, U+2264-2265, U+22C5, U+24C5,
U+25A0-25A1, U+25AF, U+25B2-25B3, U+25CA-25CB, U+25CF, U+262E, U+2713,
U+2715, U+2780-2788, U+E000, U+E002, U+F6C3, U+FB00-FB04, U+FEFF, U+FF0C,
U+FF0E, U+FF1A-FF1B, U+FFFF;
src: url(https://encore.scdn.co/fonts/CircularSp-Book-a00e99ef9996a3a157fb6b746856d04f.woff2)
format('woff2'),
url(https://encore.scdn.co/fonts/CircularSp-Book-3f73da7d35bd81c706bce7bbb84964de.woff)
format('woff');
}
@font-face {
font-family: CircularSp;
font-weight: 700;
font-display: swap;
unicode-range: U+0, U+D, U+20-7E, U+A0-17E, U+18F, U+192, U+1A0-1A1, U+1AF-1B0,
U+1B5-1B6, U+1C4-1C6, U+1F1-1F3, U+1FA-1FF, U+218-21B, U+237, U+259,
U+2BB-2BC, U+2C6-2C7, U+2C9, U+2D8-2DD, U+300-301, U+303, U+309, U+323,
U+394, U+3A9, U+3BC, U+3C0, U+1E80-1E85, U+1E8A-1E8B, U+1EA0-1EF9, U+1FD6,
U+2007-2008, U+200B, U+2010-2011, U+2013-2014, U+2018-201A, U+201C-201E,
U+2020-2022, U+2026, U+2030, U+2032-2033, U+2039-203A, U+2042, U+2044,
U+2051, U+2070, U+2074-2079, U+2080-2089, U+20AB-20AC, U+2113, U+2117,
U+2122, U+2126, U+2160-2169, U+216C-216F, U+2190-2193, U+2196-2199, U+21A9,
U+21B0-21B5, U+21C6, U+2202, U+2206, U+220F, U+2211-2212, U+2215,
U+2219-221A, U+221E, U+222B, U+2248, U+2260, U+2264-2265, U+22C5, U+24C5,
U+25A0-25A1, U+25AF, U+25B2-25B3, U+25CA-25CB, U+25CF, U+262E, U+2713,
U+2715, U+2780-2788, U+E000, U+E002, U+F6C3, U+FB00-FB04, U+FEFF, U+FF0C,
U+FF0E, U+FF1A-FF1B, U+FFFF;
src: url(https://encore.scdn.co/fonts/CircularSp-Bold-602e7aefc706aa36c6ec1324b9bbc461.woff2)
format('woff2'),
url(https://encore.scdn.co/fonts/CircularSp-Bold-856afe2da4ba4e61239b129e2c16d633.woff)
format('woff');
}
[data-theme-name='spotify'] {
--bui-font-text: CircularSp, CircularSp-Arab, CircularSp-Hebr, CircularSp-Cyrl,
CircularSp-Grek, CircularSp-Deva;
--bui-font-title: CircularSpTitle, CircularSp-Arab, CircularSp-Hebr,
CircularSp-Cyrl, CircularSp-Grek, CircularSp-Deva;
--bui-font-regular: CircularSp, CircularSp-Arab, CircularSp-Hebr,
CircularSp-Cyrl, CircularSp-Grek, CircularSp-Deva;
& .bui-Button {
border-radius: var(--bui-radius-3);
padding-inline: var(--bui-space-3);
}
& .bui-ButtonIcon {
padding: 0;
}
& .bui-MenuPopup {
box-sizing: border-box;
max-width: 21.25rem;
}
& .bui-MenuSubmenuTrigger,
& .bui-MenuItem {
height: auto;
min-height: 2rem;
}
& .bui-Text {
font-family: var(--bui-font-text);
}
& .bui-Heading {
font-family: var(--bui-font-title);
}
& .bui-TableRow {
border-radius: var(--bui-radius-4);
border: none;
}
& .bui-TableRow:hover td:first-child {
border-top-left-radius: var(--bui-radius-2);
border-bottom-left-radius: var(--bui-radius-2);
}
& .bui-TableRow:hover td:last-child {
border-top-right-radius: var(--bui-radius-2);
border-bottom-right-radius: var(--bui-radius-2);
}
& .bui-TableBody:before,
& .bui-TableBody:after {
line-height: var(--bui-space-1);
content: '';
display: block;
}
& .bui-TableHeader .bui-TableRow {
border-bottom: 1px solid var(--bui-border);
}
& .bui-TableHead {
font-size: var(--bui-font-size-2);
color: var(--bui-fg-secondary);
font-weight: var(--bui-font-weight-regular);
}
& .bui-HeaderToolbar {
padding-top: var(--bui-space-2);
padding-inline: var(--bui-space-2);
}
& .bui-HeaderToolbarWrapper {
border-radius: var(--bui-radius-3);
padding-inline: var(--bui-space-3);
border: none;
}
& .bui-HeaderToolbarControls {
right: calc(var(--bui-space-3) - 1px);
}
& .bui-HeaderTabsWrapper {
margin-top: var(--bui-space-2);
margin-inline: var(--bui-space-2);
border-radius: var(--bui-radius-3);
padding-inline: var(--bui-space-1);
border: none;
}
& .bui-Input {
border-radius: var(--bui-radius-3);
}
& .bui-Tag {
border-radius: var(--bui-radius-full);
}
}
[data-theme-mode='light'][data-theme-name='spotify'] {
--bui-bg-solid: #1ed760;
--bui-bg-solid-hover: #3be477;
--bui-bg-solid-pressed: #1abc54;
--bui-bg-solid-disabled: #0f6c30;
--bui-fg-primary: var(--bui-black);
--bui-fg-secondary: var(--bui-gray-7);
--bui-fg-solid: var(--bui-black);
--bui-fg-solid-disabled: #62ab7c;
--bui-border: var(--bui-gray-3);
--bui-border-hover: #0000004d;
--bui-border-pressed: #00000080;
--bui-border-disabled: #0000001a;
--bui-border-danger: #f87a7a;
--bui-border-warning: #e36d05;
--bui-border-success: #53db83;
--bui-ring: #0003;
& .bui-HeaderToolbarWrapper,
& .bui-HeaderTabsWrapper {
border: 1px solid var(--bui-border);
}
}
[data-theme-mode='dark'][data-theme-name='spotify'] {
--bui-bg-surface-0: var(--bui-black);
--bui-bg-solid: #1ed760;
--bui-bg-solid-hover: #3be477;
--bui-bg-solid-pressed: #1abc54;
--bui-bg-solid-disabled: #0f6c30;
--bui-bg-danger: #3b1219;
--bui-bg-warning: #302008;
--bui-bg-success: #132d21;
--bui-fg-primary: var(--bui-white);
--bui-fg-secondary: var(--bui-gray-7);
--bui-fg-disabled: var(--bui-gray-5);
--bui-fg-solid: var(--bui-black);
--bui-fg-solid-disabled: #072f15;
--bui-fg-danger: #e22b2b;
--bui-fg-warning: #e36d05;
--bui-fg-success: #1db954;
--bui-border: var(--bui-gray-3);
--bui-border-hover: #fff6;
--bui-border-pressed: #ffffff80;
--bui-border-disabled: #fff3;
--bui-border-danger: #f87a7a;
--bui-border-warning: #e36d05;
--bui-border-success: #53db83;
--bui-ring: #fff3;
}
+68 -44
View File
@@ -4,6 +4,7 @@ import {
ReactNode,
useState,
useEffect,
useSyncExternalStore,
} from 'react';
import { components } from './data';
@@ -31,6 +32,14 @@ const PlaygroundContext = createContext<{
setSelectedThemeName: () => {},
});
// Stable server snapshots (outside component to avoid recreating)
const defaultThemeSet = new Set<Theme>(['light']);
const defaultThemeName: ThemeName = 'backstage';
// Cache for theme Sets to avoid creating new objects on every getSnapshot call
let cachedThemeValue: string | null = null;
let cachedThemeSet: Set<Theme> = defaultThemeSet;
// Create a provider component
export const PlaygroundProvider = ({ children }: { children: ReactNode }) => {
// Check if running in a browser environment
@@ -40,57 +49,72 @@ export const PlaygroundProvider = ({ children }: { children: ReactNode }) => {
const [selectedComponents, setSelectedComponents] = useState<string[]>(
components.map(component => component.slug),
);
const [selectedTheme, setSelectedTheme] = useState<Set<Theme>>(
new Set(['light']),
);
const [selectedThemeName, setSelectedThemeName] =
useState<ThemeName>('backstage');
// Load saved theme from localStorage after hydration
useEffect(() => {
if (isBrowser) {
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));
// Use useSyncExternalStore for SSR-safe localStorage access
const selectedTheme = useSyncExternalStore(
callback => {
window.addEventListener('storage', callback);
return () => window.removeEventListener('storage', callback);
},
() => {
const saved = localStorage.getItem('theme-mode');
// Return cached Set if value hasn't changed
if (saved === cachedThemeValue) {
return cachedThemeSet;
}
// Update cache with new value
cachedThemeValue = saved;
if (saved) {
const themeArray = saved.split(',').filter(Boolean) as Theme[];
cachedThemeSet = new Set(themeArray);
} else {
setSelectedTheme(new Set(['light']));
cachedThemeSet = defaultThemeSet;
}
}
}, [isBrowser]);
// Load saved theme name from localStorage after hydration
return cachedThemeSet;
},
() => defaultThemeSet, // Stable server snapshot
);
const selectedThemeName = useSyncExternalStore(
callback => {
window.addEventListener('storage', callback);
return () => window.removeEventListener('storage', callback);
},
() => {
const saved = localStorage.getItem('theme-name') as ThemeName;
return saved || defaultThemeName;
},
() => defaultThemeName, // Stable server snapshot
);
// Keep setter functions that update both state and localStorage
const setSelectedTheme = (keys: Set<Theme>) => {
const value = Array.from(keys).join(',');
localStorage.setItem('theme-mode', value);
// Invalidate cache
cachedThemeValue = null;
window.dispatchEvent(new Event('storage'));
};
const setSelectedThemeName = (name: ThemeName) => {
localStorage.setItem('theme-name', name);
window.dispatchEvent(new Event('storage'));
};
// Sync to DOM attributes when values change
useEffect(() => {
if (isBrowser) {
const savedThemeName = localStorage.getItem('theme-name') as ThemeName;
if (savedThemeName) {
setSelectedThemeName(savedThemeName);
}
}
}, [isBrowser]);
document.documentElement.setAttribute(
'data-theme-mode',
Array.from(selectedTheme).join(','),
);
}, [selectedTheme]);
useEffect(() => {
if (isBrowser) {
document.documentElement.setAttribute(
'data-theme-mode',
Array.from(selectedTheme).join(','),
);
localStorage.setItem('theme-mode', Array.from(selectedTheme).join(','));
}
}, [selectedTheme, isBrowser]);
useEffect(() => {
if (isBrowser) {
document.documentElement.setAttribute(
'data-theme-name',
selectedThemeName || 'backstage',
);
localStorage.setItem('theme-name', selectedThemeName || 'backstage');
}
}, [selectedThemeName, isBrowser]);
document.documentElement.setAttribute('data-theme-name', selectedThemeName);
}, [selectedThemeName]);
return (
<PlaygroundContext.Provider
+2 -1
View File
@@ -30,7 +30,8 @@
".next/types/**/*.ts",
"dist/types/**/*.ts",
"src/types/**/*.d.ts",
"dist/dev/types/**/*.ts"
"dist/dev/types/**/*.ts",
".next/dev/types/**/*.ts"
],
"exclude": ["node_modules"]
}
+191 -243
View File
@@ -375,7 +375,7 @@ __metadata:
languageName: node
linkType: hard
"@eslint-community/eslint-utils@npm:^4.2.0, @eslint-community/eslint-utils@npm:^4.9.1":
"@eslint-community/eslint-utils@npm:^4.8.0, @eslint-community/eslint-utils@npm:^4.9.1":
version: 4.9.1
resolution: "@eslint-community/eslint-utils@npm:4.9.1"
dependencies:
@@ -386,34 +386,80 @@ __metadata:
languageName: node
linkType: hard
"@eslint-community/regexpp@npm:^4.12.2, @eslint-community/regexpp@npm:^4.6.1":
"@eslint-community/regexpp@npm:^4.12.1, @eslint-community/regexpp@npm:^4.12.2":
version: 4.12.2
resolution: "@eslint-community/regexpp@npm:4.12.2"
checksum: 10/049b280fddf71dd325514e0a520024969431dc3a8b02fa77476e6820e9122f28ab4c9168c11821f91a27982d2453bcd7a66193356ea84e84fb7c8d793be1ba0c
languageName: node
linkType: hard
"@eslint/eslintrc@npm:^2.1.4":
version: 2.1.4
resolution: "@eslint/eslintrc@npm:2.1.4"
"@eslint/config-array@npm:^0.21.1":
version: 0.21.1
resolution: "@eslint/config-array@npm:0.21.1"
dependencies:
ajv: "npm:^6.12.4"
debug: "npm:^4.3.2"
espree: "npm:^9.6.0"
globals: "npm:^13.19.0"
ignore: "npm:^5.2.0"
import-fresh: "npm:^3.2.1"
js-yaml: "npm:^4.1.0"
"@eslint/object-schema": "npm:^2.1.7"
debug: "npm:^4.3.1"
minimatch: "npm:^3.1.2"
strip-json-comments: "npm:^3.1.1"
checksum: 10/7a3b14f4b40fc1a22624c3f84d9f467a3d9ea1ca6e9a372116cb92507e485260359465b58e25bcb6c9981b155416b98c9973ad9b796053fd7b3f776a6946bce8
checksum: 10/6eaa0435972f735ce52d581f355a0b616e50a9b8a73304a7015398096e252798b9b3b968a67b524eefb0fdeacc57c4d960f0ec6432abe1c1e24be815b88c5d18
languageName: node
linkType: hard
"@eslint/js@npm:8.57.1":
version: 8.57.1
resolution: "@eslint/js@npm:8.57.1"
checksum: 10/7562b21be10c2adbfa4aa5bb2eccec2cb9ac649a3569560742202c8d1cb6c931ce634937a2f0f551e078403a1c1285d6c2c0aa345dafc986149665cd69fe8b59
"@eslint/config-helpers@npm:^0.4.2":
version: 0.4.2
resolution: "@eslint/config-helpers@npm:0.4.2"
dependencies:
"@eslint/core": "npm:^0.17.0"
checksum: 10/3f2b4712d8e391c36ec98bc200f7dea423dfe518e42956569666831b89ede83b33120c761dfd3ab6347d8e8894a6d4af47254a18d464a71c6046fd88065f6daf
languageName: node
linkType: hard
"@eslint/core@npm:^0.17.0":
version: 0.17.0
resolution: "@eslint/core@npm:0.17.0"
dependencies:
"@types/json-schema": "npm:^7.0.15"
checksum: 10/f9a428cc651ec15fb60d7d60c2a7bacad4666e12508320eafa98258e976fafaa77d7be7be91519e75f801f15f830105420b14a458d4aab121a2b0a59bc43517b
languageName: node
linkType: hard
"@eslint/eslintrc@npm:^3.3.1":
version: 3.3.3
resolution: "@eslint/eslintrc@npm:3.3.3"
dependencies:
ajv: "npm:^6.12.4"
debug: "npm:^4.3.2"
espree: "npm:^10.0.1"
globals: "npm:^14.0.0"
ignore: "npm:^5.2.0"
import-fresh: "npm:^3.2.1"
js-yaml: "npm:^4.1.1"
minimatch: "npm:^3.1.2"
strip-json-comments: "npm:^3.1.1"
checksum: 10/b586a364ff15ce1b68993aefc051ca330b1fece15fb5baf4a708d00113f9a14895cffd84a5f24c5a97bd4b4321130ab2314f90aa462a250f6b859c2da2cba1f3
languageName: node
linkType: hard
"@eslint/js@npm:9.39.2":
version: 9.39.2
resolution: "@eslint/js@npm:9.39.2"
checksum: 10/6b7f676746f3111b5d1b23715319212ab9297868a0fa9980d483c3da8965d5841673aada2d5653e85a3f7156edee0893a7ae7035211b4efdcb2848154bb947f2
languageName: node
linkType: hard
"@eslint/object-schema@npm:^2.1.7":
version: 2.1.7
resolution: "@eslint/object-schema@npm:2.1.7"
checksum: 10/946ef5d6235b4d1c0907c6c6e6429c8895f535380c562b7705c131f63f2e961b06e8785043c86a293da48e0a60c6286d98ba395b8b32ea55561fe6e4417cb7e4
languageName: node
linkType: hard
"@eslint/plugin-kit@npm:^0.4.1":
version: 0.4.1
resolution: "@eslint/plugin-kit@npm:0.4.1"
dependencies:
"@eslint/core": "npm:^0.17.0"
levn: "npm:^0.4.1"
checksum: 10/c5947d0ffeddca77d996ac1b886a66060c1a15ed1d5e425d0c7e7d7044a4bd3813fc968892d03950a7831c9b89368a2f7b281e45dd3c74a048962b74bf3a1cb4
languageName: node
linkType: hard
@@ -455,14 +501,20 @@ __metadata:
languageName: node
linkType: hard
"@humanwhocodes/config-array@npm:^0.13.0":
version: 0.13.0
resolution: "@humanwhocodes/config-array@npm:0.13.0"
"@humanfs/core@npm:^0.19.1":
version: 0.19.1
resolution: "@humanfs/core@npm:0.19.1"
checksum: 10/270d936be483ab5921702623bc74ce394bf12abbf57d9145a69e8a0d1c87eb1c768bd2d93af16c5705041e257e6d9cc7529311f63a1349f3678abc776fc28523
languageName: node
linkType: hard
"@humanfs/node@npm:^0.16.6":
version: 0.16.7
resolution: "@humanfs/node@npm:0.16.7"
dependencies:
"@humanwhocodes/object-schema": "npm:^2.0.3"
debug: "npm:^4.3.1"
minimatch: "npm:^3.0.5"
checksum: 10/524df31e61a85392a2433bf5d03164e03da26c03d009f27852e7dcfdafbc4a23f17f021dacf88e0a7a9fe04ca032017945d19b57a16e2676d9114c22a53a9d11
"@humanfs/core": "npm:^0.19.1"
"@humanwhocodes/retry": "npm:^0.4.0"
checksum: 10/b3633d3dce898592cac515ba5e6693c78e6be92863541d3eaf2c009b10f52b2fa62ff6e6e06f240f2447ddbe7b5f1890bc34e9308470675c876eee207553a08d
languageName: node
linkType: hard
@@ -473,10 +525,10 @@ __metadata:
languageName: node
linkType: hard
"@humanwhocodes/object-schema@npm:^2.0.3":
version: 2.0.3
resolution: "@humanwhocodes/object-schema@npm:2.0.3"
checksum: 10/05bb99ed06c16408a45a833f03a732f59bf6184795d4efadd33238ff8699190a8c871ad1121241bb6501589a9598dc83bf25b99dcbcf41e155cdf36e35e937a3
"@humanwhocodes/retry@npm:^0.4.0, @humanwhocodes/retry@npm:^0.4.2":
version: 0.4.3
resolution: "@humanwhocodes/retry@npm:0.4.3"
checksum: 10/0b32cfd362bea7a30fbf80bb38dcaf77fee9c2cae477ee80b460871d03590110ac9c77d654f04ec5beaf71b6f6a89851bdf6c1e34ccdf2f686bd86fcd97d9e61
languageName: node
linkType: hard
@@ -1005,7 +1057,7 @@ __metadata:
languageName: node
linkType: hard
"@nodelib/fs.walk@npm:^1.2.3, @nodelib/fs.walk@npm:^1.2.8":
"@nodelib/fs.walk@npm:^1.2.3":
version: 1.2.8
resolution: "@nodelib/fs.walk@npm:1.2.8"
dependencies:
@@ -1305,7 +1357,7 @@ __metadata:
languageName: node
linkType: hard
"@types/estree@npm:*, @types/estree@npm:^1.0.0":
"@types/estree@npm:*, @types/estree@npm:^1.0.0, @types/estree@npm:^1.0.6":
version: 1.0.8
resolution: "@types/estree@npm:1.0.8"
checksum: 10/25a4c16a6752538ffde2826c2cc0c6491d90e69cd6187bef4a006dd2c3c45469f049e643d7e516c515f21484dc3d48fd5c870be158a5beb72f5baf3dc43e4099
@@ -1321,6 +1373,13 @@ __metadata:
languageName: node
linkType: hard
"@types/json-schema@npm:^7.0.15":
version: 7.0.15
resolution: "@types/json-schema@npm:7.0.15"
checksum: 10/1a3c3e06236e4c4aab89499c428d585527ce50c24fe8259e8b3926d3df4cfbbbcf306cfc73ddfb66cbafc973116efd15967020b0f738f63e09e64c7d260519e7
languageName: node
linkType: hard
"@types/json5@npm:^0.0.29":
version: 0.0.29
resolution: "@types/json5@npm:0.0.29"
@@ -1587,7 +1646,7 @@ __metadata:
languageName: node
linkType: hard
"@ungap/structured-clone@npm:^1.0.0, @ungap/structured-clone@npm:^1.2.0":
"@ungap/structured-clone@npm:^1.0.0":
version: 1.3.0
resolution: "@ungap/structured-clone@npm:1.3.0"
checksum: 10/80d6910946f2b1552a2406650051c91bbd1f24a6bf854354203d84fe2714b3e8ce4618f49cc3410494173a1c1e8e9777372fe68dce74bd45faf0a7a1a6ccf448
@@ -1745,7 +1804,7 @@ __metadata:
languageName: node
linkType: hard
"acorn@npm:^8.0.0, acorn@npm:^8.9.0":
"acorn@npm:^8.0.0, acorn@npm:^8.15.0":
version: 8.15.0
resolution: "acorn@npm:8.15.0"
bin:
@@ -2277,7 +2336,7 @@ __metadata:
languageName: node
linkType: hard
"cross-spawn@npm:^7.0.2, cross-spawn@npm:^7.0.6":
"cross-spawn@npm:^7.0.6":
version: 7.0.6
resolution: "cross-spawn@npm:7.0.6"
dependencies:
@@ -2439,7 +2498,7 @@ __metadata:
"@uiw/react-codemirror": "npm:^4.23.7"
chokidar: "npm:^3.6.0"
clsx: "npm:^2.1.1"
eslint: "npm:^8"
eslint: "npm:^9"
eslint-config-next: "npm:16.1.6"
html-react-parser: "npm:^5.2.5"
lightningcss: "npm:^1.28.2"
@@ -2464,15 +2523,6 @@ __metadata:
languageName: node
linkType: hard
"doctrine@npm:^3.0.0":
version: 3.0.0
resolution: "doctrine@npm:3.0.0"
dependencies:
esutils: "npm:^2.0.2"
checksum: 10/b4b28f1df5c563f7d876e7461254a4597b8cabe915abe94d7c5d1633fed263fcf9a85e8d3836591fc2d040108e822b0d32758e5ec1fe31c590dc7e08086e3e48
languageName: node
linkType: hard
"dom-serializer@npm:^2.0.0":
version: 2.0.0
resolution: "dom-serializer@npm:2.0.0"
@@ -2500,7 +2550,7 @@ __metadata:
languageName: node
linkType: hard
"domutils@npm:^3.2.1":
"domutils@npm:^3.2.2":
version: 3.2.2
resolution: "domutils@npm:3.2.2"
dependencies:
@@ -2566,10 +2616,10 @@ __metadata:
languageName: node
linkType: hard
"entities@npm:^6.0.0":
version: 6.0.1
resolution: "entities@npm:6.0.1"
checksum: 10/62af1307202884349d2867f0aac5c60d8b57102ea0b0e768b16246099512c28e239254ad772d6834e7e14cb1b6f153fc3d0c031934e3183b086c86d3838d874a
"entities@npm:^7.0.1":
version: 7.0.1
resolution: "entities@npm:7.0.1"
checksum: 10/3c0c58d869c45148463e96d21dee2d1b801bd3fe4cf47aa470cd26dfe81d59e9e0a9be92ae083fa02fa441283c883a471486e94538dcfb8544428aa80a55271b
languageName: node
linkType: hard
@@ -2933,17 +2983,17 @@ __metadata:
languageName: node
linkType: hard
"eslint-scope@npm:^7.2.2":
version: 7.2.2
resolution: "eslint-scope@npm:7.2.2"
"eslint-scope@npm:^8.4.0":
version: 8.4.0
resolution: "eslint-scope@npm:8.4.0"
dependencies:
esrecurse: "npm:^4.3.0"
estraverse: "npm:^5.2.0"
checksum: 10/5c660fb905d5883ad018a6fea2b49f3cb5b1cbf2cd4bd08e98646e9864f9bc2c74c0839bed2d292e90a4a328833accc197c8f0baed89cbe8d605d6f918465491
checksum: 10/e8e611701f65375e034c62123946e628894f0b54aa8cb11abe224816389abe5cd74cf16b62b72baa36504f22d1a958b9b8b0169b82397fe2e7997674c0d09b06
languageName: node
linkType: hard
"eslint-visitor-keys@npm:^3.4.1, eslint-visitor-keys@npm:^3.4.3":
"eslint-visitor-keys@npm:^3.4.3":
version: 3.4.3
resolution: "eslint-visitor-keys@npm:3.4.3"
checksum: 10/3f357c554a9ea794b094a09bd4187e5eacd1bc0d0653c3adeb87962c548e6a1ab8f982b86963ae1337f5d976004146536dcee5d0e2806665b193fbfbf1a9231b
@@ -2957,71 +3007,72 @@ __metadata:
languageName: node
linkType: hard
"eslint@npm:^8":
version: 8.57.1
resolution: "eslint@npm:8.57.1"
"eslint@npm:^9":
version: 9.39.2
resolution: "eslint@npm:9.39.2"
dependencies:
"@eslint-community/eslint-utils": "npm:^4.2.0"
"@eslint-community/regexpp": "npm:^4.6.1"
"@eslint/eslintrc": "npm:^2.1.4"
"@eslint/js": "npm:8.57.1"
"@humanwhocodes/config-array": "npm:^0.13.0"
"@eslint-community/eslint-utils": "npm:^4.8.0"
"@eslint-community/regexpp": "npm:^4.12.1"
"@eslint/config-array": "npm:^0.21.1"
"@eslint/config-helpers": "npm:^0.4.2"
"@eslint/core": "npm:^0.17.0"
"@eslint/eslintrc": "npm:^3.3.1"
"@eslint/js": "npm:9.39.2"
"@eslint/plugin-kit": "npm:^0.4.1"
"@humanfs/node": "npm:^0.16.6"
"@humanwhocodes/module-importer": "npm:^1.0.1"
"@nodelib/fs.walk": "npm:^1.2.8"
"@ungap/structured-clone": "npm:^1.2.0"
"@humanwhocodes/retry": "npm:^0.4.2"
"@types/estree": "npm:^1.0.6"
ajv: "npm:^6.12.4"
chalk: "npm:^4.0.0"
cross-spawn: "npm:^7.0.2"
cross-spawn: "npm:^7.0.6"
debug: "npm:^4.3.2"
doctrine: "npm:^3.0.0"
escape-string-regexp: "npm:^4.0.0"
eslint-scope: "npm:^7.2.2"
eslint-visitor-keys: "npm:^3.4.3"
espree: "npm:^9.6.1"
esquery: "npm:^1.4.2"
eslint-scope: "npm:^8.4.0"
eslint-visitor-keys: "npm:^4.2.1"
espree: "npm:^10.4.0"
esquery: "npm:^1.5.0"
esutils: "npm:^2.0.2"
fast-deep-equal: "npm:^3.1.3"
file-entry-cache: "npm:^6.0.1"
file-entry-cache: "npm:^8.0.0"
find-up: "npm:^5.0.0"
glob-parent: "npm:^6.0.2"
globals: "npm:^13.19.0"
graphemer: "npm:^1.4.0"
ignore: "npm:^5.2.0"
imurmurhash: "npm:^0.1.4"
is-glob: "npm:^4.0.0"
is-path-inside: "npm:^3.0.3"
js-yaml: "npm:^4.1.0"
json-stable-stringify-without-jsonify: "npm:^1.0.1"
levn: "npm:^0.4.1"
lodash.merge: "npm:^4.6.2"
minimatch: "npm:^3.1.2"
natural-compare: "npm:^1.4.0"
optionator: "npm:^0.9.3"
strip-ansi: "npm:^6.0.1"
text-table: "npm:^0.2.0"
peerDependencies:
jiti: "*"
peerDependenciesMeta:
jiti:
optional: true
bin:
eslint: bin/eslint.js
checksum: 10/5504fa24879afdd9f9929b2fbfc2ee9b9441a3d464efd9790fbda5f05738858530182029f13323add68d19fec749d3ab4a70320ded091ca4432b1e9cc4ed104c
checksum: 10/53ff0e9c8264e7e8d40d50fdc0c0df0b701cfc5289beedfb686c214e3e7b199702f894bbd1bb48653727bb1ecbd1147cf5f555a4ae71e1daf35020cdc9072d9f
languageName: node
linkType: hard
"espree@npm:^9.6.0, espree@npm:^9.6.1":
version: 9.6.1
resolution: "espree@npm:9.6.1"
"espree@npm:^10.0.1, espree@npm:^10.4.0":
version: 10.4.0
resolution: "espree@npm:10.4.0"
dependencies:
acorn: "npm:^8.9.0"
acorn: "npm:^8.15.0"
acorn-jsx: "npm:^5.3.2"
eslint-visitor-keys: "npm:^3.4.1"
checksum: 10/255ab260f0d711a54096bdeda93adff0eadf02a6f9b92f02b323e83a2b7fc258797919437ad331efec3930475feb0142c5ecaaf3cdab4befebd336d47d3f3134
eslint-visitor-keys: "npm:^4.2.1"
checksum: 10/9b355b32dbd1cc9f57121d5ee3be258fab87ebeb7c83fc6c02e5af1a74fc8c5ba79fe8c663e69ea112c3e84a1b95e6a2067ac4443ee7813bb85ac7581acb8bf9
languageName: node
linkType: hard
"esquery@npm:^1.4.2":
version: 1.6.0
resolution: "esquery@npm:1.6.0"
"esquery@npm:^1.5.0":
version: 1.7.0
resolution: "esquery@npm:1.7.0"
dependencies:
estraverse: "npm:^5.1.0"
checksum: 10/c587fb8ec9ed83f2b1bc97cf2f6854cc30bf784a79d62ba08c6e358bf22280d69aee12827521cf38e69ae9761d23fb7fde593ce315610f85655c139d99b05e5a
checksum: 10/4afaf3089367e1f5885caa116ef386dffd8bfd64da21fd3d0e56e938d2667cfb2e5400ab4a825aa70e799bb3741e5b5d63c0b94d86e2d4cf3095c9e64b2f5a15
languageName: node
linkType: hard
@@ -3192,12 +3243,12 @@ __metadata:
languageName: node
linkType: hard
"file-entry-cache@npm:^6.0.1":
version: 6.0.1
resolution: "file-entry-cache@npm:6.0.1"
"file-entry-cache@npm:^8.0.0":
version: 8.0.0
resolution: "file-entry-cache@npm:8.0.0"
dependencies:
flat-cache: "npm:^3.0.4"
checksum: 10/099bb9d4ab332cb93c48b14807a6918a1da87c45dce91d4b61fd40e6505d56d0697da060cb901c729c90487067d93c9243f5da3dc9c41f0358483bfdebca736b
flat-cache: "npm:^4.0.0"
checksum: 10/afe55c4de4e0d226a23c1eae62a7219aafb390859122608a89fa4df6addf55c7fd3f1a2da6f5b41e7cdff496e4cf28bbd215d53eab5c817afa96d2b40c81bfb0
languageName: node
linkType: hard
@@ -3220,14 +3271,13 @@ __metadata:
languageName: node
linkType: hard
"flat-cache@npm:^3.0.4":
version: 3.2.0
resolution: "flat-cache@npm:3.2.0"
"flat-cache@npm:^4.0.0":
version: 4.0.1
resolution: "flat-cache@npm:4.0.1"
dependencies:
flatted: "npm:^3.2.9"
keyv: "npm:^4.5.3"
rimraf: "npm:^3.0.2"
checksum: 10/02381c6ece5e9fa5b826c9bbea481d7fd77645d96e4b0b1395238124d581d10e56f17f723d897b6d133970f7a57f0fab9148cbbb67237a0a0ffe794ba60c0c70
keyv: "npm:^4.5.4"
checksum: 10/58ce851d9045fffc7871ce2bd718bc485ad7e777bf748c054904b87c351ff1080c2c11da00788d78738bfb51b71e4d5ea12d13b98eb36e3358851ffe495b62dc
languageName: node
linkType: hard
@@ -3257,12 +3307,12 @@ __metadata:
languageName: node
linkType: hard
"framer-motion@npm:^12.28.1":
version: 12.28.1
resolution: "framer-motion@npm:12.28.1"
"framer-motion@npm:^12.29.2":
version: 12.29.2
resolution: "framer-motion@npm:12.29.2"
dependencies:
motion-dom: "npm:^12.28.1"
motion-utils: "npm:^12.27.2"
motion-dom: "npm:^12.29.2"
motion-utils: "npm:^12.29.2"
tslib: "npm:^2.4.0"
peerDependencies:
"@emotion/is-prop-valid": "*"
@@ -3275,7 +3325,7 @@ __metadata:
optional: true
react-dom:
optional: true
checksum: 10/f8101df55b9e2c9ca7eb362aaff250ba2f77ef01770cbf3d27bc9c2103504d247936b4e62f2a4059bc1a0a878f558d32407db9b182976fdd48b2549d0631be54
checksum: 10/13ea43efa814e4df1f3a0bf7ee6c1736f3f01b96cb677e560b62d58db8768f00a53c17156bb5bf6bf06b786370468495dbb6b0a83e3b5b5289e5235046ccb03b
languageName: node
linkType: hard
@@ -3288,13 +3338,6 @@ __metadata:
languageName: node
linkType: hard
"fs.realpath@npm:^1.0.0":
version: 1.0.0
resolution: "fs.realpath@npm:1.0.0"
checksum: 10/e703107c28e362d8d7b910bbcbfd371e640a3bb45ae157a362b5952c0030c0b6d4981140ec319b347bce7adc025dd7813da1ff908a945ac214d64f5402a51b96
languageName: node
linkType: hard
"fsevents@npm:~2.3.2":
version: 2.3.3
resolution: "fsevents@npm:2.3.3"
@@ -3431,20 +3474,6 @@ __metadata:
languageName: node
linkType: hard
"glob@npm:^7.1.3":
version: 7.2.3
resolution: "glob@npm:7.2.3"
dependencies:
fs.realpath: "npm:^1.0.0"
inflight: "npm:^1.0.4"
inherits: "npm:2"
minimatch: "npm:^3.1.1"
once: "npm:^1.3.0"
path-is-absolute: "npm:^1.0.0"
checksum: 10/59452a9202c81d4508a43b8af7082ca5c76452b9fcc4a9ab17655822e6ce9b21d4f8fbadabe4fe3faef448294cec249af305e2cd824b7e9aaf689240e5e96a7b
languageName: node
linkType: hard
"globals@npm:16.4.0":
version: 16.4.0
resolution: "globals@npm:16.4.0"
@@ -3452,12 +3481,10 @@ __metadata:
languageName: node
linkType: hard
"globals@npm:^13.19.0":
version: 13.24.0
resolution: "globals@npm:13.24.0"
dependencies:
type-fest: "npm:^0.20.2"
checksum: 10/62c5b1997d06674fc7191d3e01e324d3eda4d65ac9cc4e78329fa3b5c4fd42a0e1c8722822497a6964eee075255ce21ccf1eec2d83f92ef3f06653af4d0ee28e
"globals@npm:^14.0.0":
version: 14.0.0
resolution: "globals@npm:14.0.0"
checksum: 10/03939c8af95c6df5014b137cac83aa909090c3a3985caef06ee9a5a669790877af8698ab38007e4c0186873adc14c0b13764acc754b16a754c216cc56aa5f021
languageName: node
linkType: hard
@@ -3485,13 +3512,6 @@ __metadata:
languageName: node
linkType: hard
"graphemer@npm:^1.4.0":
version: 1.4.0
resolution: "graphemer@npm:1.4.0"
checksum: 10/6dd60dba97007b21e3a829fab3f771803cc1292977fe610e240ea72afd67e5690ac9eeaafc4a99710e78962e5936ab5a460787c2a1180f1cb0ccfac37d29f897
languageName: node
linkType: hard
"has-bigints@npm:^1.0.2":
version: 1.1.0
resolution: "has-bigints@npm:1.1.0"
@@ -3640,22 +3660,22 @@ __metadata:
languageName: node
linkType: hard
"html-dom-parser@npm:5.1.2":
version: 5.1.2
resolution: "html-dom-parser@npm:5.1.2"
"html-dom-parser@npm:5.1.4":
version: 5.1.4
resolution: "html-dom-parser@npm:5.1.4"
dependencies:
domhandler: "npm:5.0.3"
htmlparser2: "npm:10.0.0"
checksum: 10/d54fcdafbc0b2881c441a396965ad285072d2e4df62bebe7074d7aaa2a1fffc95e9286bcb81d6f241c4cdb10a84435fa623738985b635574acb3c84e994e8bb6
htmlparser2: "npm:10.1.0"
checksum: 10/e2193fd081b229537cd7c735f27dac2de6b5789c822be1947ac54e104ecd852464313813c039e7bed0e2e574f5f6ce9d560b307fc0ba8d2d372c6a1c68a59117
languageName: node
linkType: hard
"html-react-parser@npm:^5.2.5":
version: 5.2.11
resolution: "html-react-parser@npm:5.2.11"
version: 5.2.14
resolution: "html-react-parser@npm:5.2.14"
dependencies:
domhandler: "npm:5.0.3"
html-dom-parser: "npm:5.1.2"
html-dom-parser: "npm:5.1.4"
react-property: "npm:2.0.2"
style-to-js: "npm:1.1.21"
peerDependencies:
@@ -3664,7 +3684,7 @@ __metadata:
peerDependenciesMeta:
"@types/react":
optional: true
checksum: 10/5b52699e63e6ebd7c1c2d76f6b5214fdbf017571785c5c01f0630704a84fcd3b75a9539e0f4e8bb65e91d8871ee3e80980baa21660cfd9749587dafd96a959bf
checksum: 10/992d7362f0cc8a8987c844a2f317e779215cf49b01f54400ccb750f519bb867fa9ce157be4d0c3daf21e0e217cee6b752fdca473c7a7decd70f53c995952afaf
languageName: node
linkType: hard
@@ -3675,15 +3695,15 @@ __metadata:
languageName: node
linkType: hard
"htmlparser2@npm:10.0.0":
version: 10.0.0
resolution: "htmlparser2@npm:10.0.0"
"htmlparser2@npm:10.1.0":
version: 10.1.0
resolution: "htmlparser2@npm:10.1.0"
dependencies:
domelementtype: "npm:^2.3.0"
domhandler: "npm:^5.0.3"
domutils: "npm:^3.2.1"
entities: "npm:^6.0.0"
checksum: 10/768870f0e020dca19dc45df206cb6ac466c5dba6566c8fca4ca880347eed409f9977028d08644ac516bca8628ac9c7ded5a3847dc3ee1c043f049abf9e817154
domutils: "npm:^3.2.2"
entities: "npm:^7.0.1"
checksum: 10/660fb094a53fb77a3c771db969778b58af0e8a572a1bdc8e5952a4241e4b04e0a6063b16f6422e22c821441081c8de339e3f06ddda362ac2a42c8767d5e5ad53
languageName: node
linkType: hard
@@ -3754,23 +3774,6 @@ __metadata:
languageName: node
linkType: hard
"inflight@npm:^1.0.4":
version: 1.0.6
resolution: "inflight@npm:1.0.6"
dependencies:
once: "npm:^1.3.0"
wrappy: "npm:1"
checksum: 10/d2ebd65441a38c8336c223d1b80b921b9fa737e37ea466fd7e253cb000c64ae1f17fa59e68130ef5bda92cfd8d36b83d37dab0eb0a4558bcfec8e8cdfd2dcb67
languageName: node
linkType: hard
"inherits@npm:2":
version: 2.0.4
resolution: "inherits@npm:2.0.4"
checksum: 10/cd45e923bee15186c07fa4c89db0aace24824c482fb887b528304694b2aa6ff8a898da8657046a5dcf3e46cd6db6c61629551f9215f208d7c3f157cf9b290521
languageName: node
linkType: hard
"inline-style-parser@npm:0.2.7":
version: 0.2.7
resolution: "inline-style-parser@npm:0.2.7"
@@ -4000,13 +4003,6 @@ __metadata:
languageName: node
linkType: hard
"is-path-inside@npm:^3.0.3":
version: 3.0.3
resolution: "is-path-inside@npm:3.0.3"
checksum: 10/abd50f06186a052b349c15e55b182326f1936c89a78bf6c8f2b707412517c097ce04bc49a0ca221787bc44e1049f51f09a2ffb63d22899051988d3a618ba13e9
languageName: node
linkType: hard
"is-plain-obj@npm:^4.0.0":
version: 4.1.0
resolution: "is-plain-obj@npm:4.1.0"
@@ -4153,7 +4149,7 @@ __metadata:
languageName: node
linkType: hard
"js-yaml@npm:^4.1.0":
"js-yaml@npm:^4.1.1":
version: 4.1.1
resolution: "js-yaml@npm:4.1.1"
dependencies:
@@ -4226,7 +4222,7 @@ __metadata:
languageName: node
linkType: hard
"keyv@npm:^4.5.3":
"keyv@npm:^4.5.4":
version: 4.5.4
resolution: "keyv@npm:4.5.4"
dependencies:
@@ -4959,7 +4955,7 @@ __metadata:
languageName: node
linkType: hard
"minimatch@npm:^3.0.5, minimatch@npm:^3.1.1, minimatch@npm:^3.1.2":
"minimatch@npm:^3.1.2":
version: 3.1.2
resolution: "minimatch@npm:3.1.2"
dependencies:
@@ -5060,27 +5056,27 @@ __metadata:
languageName: node
linkType: hard
"motion-dom@npm:^12.28.1":
version: 12.28.1
resolution: "motion-dom@npm:12.28.1"
"motion-dom@npm:^12.29.2":
version: 12.29.2
resolution: "motion-dom@npm:12.29.2"
dependencies:
motion-utils: "npm:^12.27.2"
checksum: 10/a0ba8bbd137d4b21df4fe517c2a9e02c891270974f212909929aadcc3043a3655bb28d2eceeb58152ee0ce25381fe0dd1a06c3b1e48ba15a5b13ec20938b42b0
motion-utils: "npm:^12.29.2"
checksum: 10/d1eeb6840363cc2b4d1e1c2d9a91becc117bb64747482b1a99f7f2d4da709f049c9544c3a566c641cada368037019c0fafd3e7cf56f0477e88d65e9886bcdc40
languageName: node
linkType: hard
"motion-utils@npm:^12.27.2":
version: 12.27.2
resolution: "motion-utils@npm:12.27.2"
checksum: 10/e4c4724b1bf11d5c6dca17e8c2fd42dc7270f03ca1fd1669908e3b8bb2cd528669c7aab8df134fedbc94806edb2f9e0e9f84c66cbe77d0cb923733417b5f9f79
"motion-utils@npm:^12.29.2":
version: 12.29.2
resolution: "motion-utils@npm:12.29.2"
checksum: 10/ae5f9be58c07939af72334894ed1a18653d724946182a718dc3d11268ef26e63804c3f16dee5a6110596d4406b539c4513822b74f86adebef9488601c34b18b7
languageName: node
linkType: hard
"motion@npm:^12.4.1":
version: 12.28.1
resolution: "motion@npm:12.28.1"
version: 12.29.2
resolution: "motion@npm:12.29.2"
dependencies:
framer-motion: "npm:^12.28.1"
framer-motion: "npm:^12.29.2"
tslib: "npm:^2.4.0"
peerDependencies:
"@emotion/is-prop-valid": "*"
@@ -5093,7 +5089,7 @@ __metadata:
optional: true
react-dom:
optional: true
checksum: 10/4ad4e36ec237f22341db24a3beb87d3126facc52acde4d02d8b59cf3cbb98512e7e2aefe3256cc6e00e53a44fcaf01c998c427d79a113048b5a676524608cac5
checksum: 10/0480a984192dd0f884709a007c1d28f31ff5fcdecb6674517edbe11ac0e318efc845373f35800e0606899613e6617ec87b1c09d5ecf3f5a5aeb3eb25fef22c7d
languageName: node
linkType: hard
@@ -5341,15 +5337,6 @@ __metadata:
languageName: node
linkType: hard
"once@npm:^1.3.0":
version: 1.4.0
resolution: "once@npm:1.4.0"
dependencies:
wrappy: "npm:1"
checksum: 10/cd0a88501333edd640d95f0d2700fbde6bff20b3d4d9bdc521bdd31af0656b5706570d6c6afe532045a20bb8dc0849f8332d6f2a416e0ba6d3d3b98806c7db68
languageName: node
linkType: hard
"oniguruma-parser@npm:^0.12.1":
version: 0.12.1
resolution: "oniguruma-parser@npm:0.12.1"
@@ -5456,13 +5443,6 @@ __metadata:
languageName: node
linkType: hard
"path-is-absolute@npm:^1.0.0":
version: 1.0.1
resolution: "path-is-absolute@npm:1.0.1"
checksum: 10/060840f92cf8effa293bcc1bea81281bd7d363731d214cbe5c227df207c34cd727430f70c6037b5159c8a870b9157cba65e775446b0ab06fd5ecc7e54615a3b8
languageName: node
linkType: hard
"path-key@npm:^3.1.0":
version: 3.1.1
resolution: "path-key@npm:3.1.1"
@@ -5874,17 +5854,6 @@ __metadata:
languageName: node
linkType: hard
"rimraf@npm:^3.0.2":
version: 3.0.2
resolution: "rimraf@npm:3.0.2"
dependencies:
glob: "npm:^7.1.3"
bin:
rimraf: bin.js
checksum: 10/063ffaccaaaca2cfd0ef3beafb12d6a03dd7ff1260d752d62a6077b5dfff6ae81bea571f655bb6b589d366930ec1bdd285d40d560c0dae9b12f125e54eb743d5
languageName: node
linkType: hard
"run-parallel@npm:^1.1.9":
version: 1.2.0
resolution: "run-parallel@npm:1.2.0"
@@ -6473,13 +6442,6 @@ __metadata:
languageName: node
linkType: hard
"text-table@npm:^0.2.0":
version: 0.2.0
resolution: "text-table@npm:0.2.0"
checksum: 10/4383b5baaeffa9bb4cda2ac33a4aa2e6d1f8aaf811848bf73513a9b88fd76372dc461f6fd6d2e9cb5100f48b473be32c6f95bd983509b7d92bb4d92c10747452
languageName: node
linkType: hard
"tinyglobby@npm:^0.2.12, tinyglobby@npm:^0.2.13, tinyglobby@npm:^0.2.15":
version: 0.2.15
resolution: "tinyglobby@npm:0.2.15"
@@ -6550,13 +6512,6 @@ __metadata:
languageName: node
linkType: hard
"type-fest@npm:^0.20.2":
version: 0.20.2
resolution: "type-fest@npm:0.20.2"
checksum: 10/8907e16284b2d6cfa4f4817e93520121941baba36b39219ea36acfe64c86b9dbc10c9941af450bd60832c8f43464974d51c0957f9858bc66b952b66b6914cbb9
languageName: node
linkType: hard
"type-fest@npm:^4.31.0":
version: 4.41.0
resolution: "type-fest@npm:4.41.0"
@@ -7027,13 +6982,6 @@ __metadata:
languageName: node
linkType: hard
"wrappy@npm:1":
version: 1.0.2
resolution: "wrappy@npm:1.0.2"
checksum: 10/159da4805f7e84a3d003d8841557196034155008f817172d4e986bd591f74aa82aa7db55929a54222309e01079a65a92a9e6414da5a6aa4b01ee44a511ac3ee5
languageName: node
linkType: hard
"yallist@npm:^3.0.2":
version: 3.1.1
resolution: "yallist@npm:3.1.1"