Merge pull request #30428 from backstage/canon-themes

Canon - Add custom theme capacity to Storybook and documentation
This commit is contained in:
Charles de Dreuille
2025-07-02 10:05:46 +01:00
committed by GitHub
15 changed files with 1364 additions and 94 deletions
+10 -3
View File
@@ -3,10 +3,13 @@
"version": "0.1.0",
"private": true,
"scripts": {
"build": "npm run build:css && next build",
"build:css": "node scripts/build-css.js",
"prebuild": "yarn sync:css",
"build": "next build",
"lint": "next lint",
"start": "npm run build:css && next dev"
"prestart": "yarn sync:css",
"start": "concurrently \"yarn sync:css:watch\" \"next dev\"",
"sync:css": "node scripts/sync-css.js",
"sync:css:watch": "node scripts/sync-css.js --watch"
},
"resolutions": {
"@types/react": "19.1.8",
@@ -14,6 +17,7 @@
},
"dependencies": {
"@codemirror/lang-sass": "^6.0.2",
"@codemirror/view": "^6.34.4",
"@lezer/highlight": "^1.2.1",
"@mdx-js/loader": "^3.1.0",
"@mdx-js/react": "^3.1.0",
@@ -25,6 +29,7 @@
"motion": "^12.4.1",
"next": "15.3.4",
"next-mdx-remote-client": "^2.1.2",
"prop-types": "^15.8.1",
"react": "19.1.0",
"react-dom": "19.1.0",
"react-frame-component": "^5.2.7",
@@ -36,6 +41,8 @@
"@types/node": "^20",
"@types/react": "19.1.8",
"@types/react-dom": "19.1.6",
"chokidar": "^3.6.0",
"concurrently": "^8.2.2",
"eslint": "^8",
"eslint-config-next": "15.3.4",
"lightningcss": "^1.28.2",
File diff suppressed because one or more lines are too long
+1
View File
@@ -0,0 +1 @@
@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=dark][data-theme-name=spotify]{--canon-font-text:CircularSp,CircularSp-Arab,CircularSp-Hebr,CircularSp-Cyrl,CircularSp-Grek,CircularSp-Deva;--canon-font-title:CircularSpTitle,CircularSp-Arab,CircularSp-Hebr,CircularSp-Cyrl,CircularSp-Grek,CircularSp-Deva;--canon-font-regular:CircularSp,CircularSp-Arab,CircularSp-Hebr,CircularSp-Cyrl,CircularSp-Grek,CircularSp-Deva;--canon-gray-1:#191919;--canon-gray-2:#242424;--canon-gray-3:#373737;--canon-gray-4:#464646;--canon-gray-5:#575757;--canon-gray-6:#7b7b7b;--canon-gray-7:#9e9e9e;--canon-gray-8:#b4b4b4;--canon-bg:#000;--canon-bg-surface-1:#191919;--canon-bg-surface-2:#242424;--canon-bg-solid:#1ed760;--canon-bg-solid-hover:#3be477;--canon-bg-solid-pressed:#1abc54;--canon-bg-solid-disabled:#0f6c30;--canon-bg-tint:#242424;--canon-bg-tint-hover:#202020;--canon-bg-tint-pressed:#292929;--canon-bg-tint-disabled:#fffc;--canon-bg-danger:#3b1219;--canon-bg-warning:#302008;--canon-bg-success:#132d21;--canon-bg-underline:#d16ecb;--canon-fg-primary:#fff;--canon-fg-secondary:#9e9e9e;--canon-fg-link:#fff;--canon-fg-link-hover:#fff;--canon-fg-disabled:#575757;--canon-fg-solid:#000;--canon-fg-solid-disabled:#083618;--canon-fg-tint:#fff;--canon-fg-tint-disabled:#575757;--canon-fg-danger:#e22b2b;--canon-fg-warning:#e36d05;--canon-fg-success:#1db954;--canon-border:var(--canon-gray-3);--canon-border-hover:#fff6;--canon-border-pressed:#ffffff80;--canon-border-disabled:#fff3;--canon-border-danger:#f87a7a;--canon-border-warning:#e36d05;--canon-border-success:#53db83;--canon-ring:#fff3}#root{padding-top:var(--header-height)}.canon-Button,.canon-IconButton{border-radius:var(--canon-radius-3);transition:background-color .2s ease-in-out,color .2s ease-in-out,box-shadow .2s ease-in-out}.canon-Button--variant-primary,.canon-IconButton--variant-primary{&:hover{box-shadow:inset 0 0 0 1px var(--canon-bg-solid);color:var(--canon-fg-primary);background-color:#0000}&:active{background-color:#1ed7601a}&:disabled{background-color:var(--canon-bg-solid-disabled);color:var(--canon-fg-solid-disabled);box-shadow:none}}.canon-Button[data-size=medium],.canon-Button[data-size=small]{padding-inline:var(--canon-space-3)}.canon-MenuTrigger{all:unset;display:inline-flex}.canon-MenuPopup{box-sizing:border-box;max-width:21.25rem}.canon-MenuSubmenuTrigger,.canon-MenuItem{height:auto;min-height:2rem}.canon-Text{font-family:var(--canon-font-text)}.canon-Heading{font-family:var(--canon-font-title)}.canon-TableRow{border-radius:var(--canon-radius-4);border:none}.canon-TableRow:hover td:first-child{border-top-left-radius:var(--canon-radius-2);border-bottom-left-radius:var(--canon-radius-2)}.canon-TableRow:hover td:last-child{border-top-right-radius:var(--canon-radius-2);border-bottom-right-radius:var(--canon-radius-2)}.canon-TableBody:before,.canon-TableBody:after{line-height:var(--canon-space-1);content:"";display:block}.canon-TableHeader .canon-TableRow{border-bottom:1px solid var(--canon-border)}.canon-TableHead{font-size:var(--canon-font-size-2);color:var(--canon-fg-secondary);font-weight:var(--canon-font-weight-regular)}.canon-ScrollAreaRoot{width:100%;height:unset}.canon-TabsIndicator{background-color:var(--canon-bg-underline)}.canon-TabsTab[data-selected]{background:radial-gradient(at bottom,#d16ecb40 0%,#0000 95%)}
-34
View File
@@ -1,34 +0,0 @@
const fs = require('fs');
const path = require('path');
const { bundle } = require('lightningcss');
const source = '../../packages/canon/src/css';
const destination = '../public';
const source1 = path.join(__dirname, `${source}/styles.css`);
const destination1 = path.join(__dirname, `${destination}/styles.css`);
// Function to bundle and copy the CSS file
const bundleAndCopyFile = async (source, destination) => {
try {
const result = await bundle({
filename: source,
minify: true,
});
fs.writeFileSync(destination, result.code);
console.log('File bundled and copied successfully!');
} catch (err) {
console.error('Error bundling file:', err);
}
};
// Initial bundle and copy
Promise.all([bundleAndCopyFile(source1, destination1)])
.then(() => {
// Add an empty line after all operations are complete - It looks better in the terminal :)
console.log('');
})
.catch(err => {
console.error('Error in processing files:', err);
});
+149
View File
@@ -0,0 +1,149 @@
const fs = require('fs');
const path = require('path');
const { bundle } = require('lightningcss');
const chokidar = require('chokidar');
// Configuration
const config = {
canonPath: '../../packages/canon',
publicPath: '../public',
files: [
{
source: 'css/styles.css',
destination: 'theme-backstage.css',
name: 'Main Styles',
},
{
source: '.storybook/themes/spotify.css',
destination: 'theme-spotify.css',
name: 'Spotify Theme',
},
],
};
class CSSSync {
constructor() {
this.canonPath = path.resolve(__dirname, config.canonPath);
this.publicPath = path.resolve(__dirname, config.publicPath);
this.isWatching = process.argv.includes('--watch');
}
async syncFile(fileConfig) {
const sourcePath = path.join(this.canonPath, fileConfig.source);
const destPath = path.join(this.publicPath, fileConfig.destination);
try {
// Check if source file exists
if (!fs.existsSync(sourcePath)) {
console.warn(`⚠️ Source file not found: ${sourcePath}`);
return false;
}
// Ensure destination directory exists
fs.mkdirSync(path.dirname(destPath), { recursive: true });
// Bundle and optimize CSS
const result = await bundle({
filename: sourcePath,
minify: true,
});
// Write to destination
fs.writeFileSync(destPath, result.code);
console.log(
`${fileConfig.name}: ${fileConfig.source}${fileConfig.destination}`,
);
return true;
} catch (error) {
console.error(`❌ Error syncing ${fileConfig.name}:`, error.message);
return false;
}
}
async syncAll() {
console.log('🔄 Syncing CSS files...\n');
let successCount = 0;
for (const fileConfig of config.files) {
if (await this.syncFile(fileConfig)) {
successCount++;
}
}
console.log(
`\n✨ Synced ${successCount}/${config.files.length} CSS files successfully!`,
);
if (successCount > 0) {
console.log('\n📁 Available CSS files in public/:');
config.files.forEach(file => {
const destPath = path.join(this.publicPath, file.destination);
if (fs.existsSync(destPath)) {
const stats = fs.statSync(destPath);
const size = (stats.size / 1024).toFixed(2);
console.log(`${file.destination} (${size} KB)`);
}
});
}
}
startWatching() {
console.log('👀 Watching for CSS changes...\n');
// Watch all source files
const watchPaths = config.files.map(file =>
path.join(this.canonPath, file.source),
);
const watcher = chokidar.watch(watchPaths, {
ignored: /node_modules/,
persistent: true,
});
watcher.on('change', async filePath => {
console.log(
`\n🔄 Change detected: ${path.relative(this.canonPath, filePath)}`,
);
// Find which file changed and sync it
const fileConfig = config.files.find(file =>
filePath.endsWith(file.source.replace(/\//g, path.sep)),
);
if (fileConfig) {
await this.syncFile(fileConfig);
}
});
watcher.on('error', error => console.error('❌ Watch error:', error));
// Handle process termination
process.on('SIGINT', () => {
console.log('\n👋 Stopping CSS sync...');
watcher.close();
process.exit(0);
});
}
async run() {
console.log('🎨 Canon CSS Sync Tool\n');
console.log(`📂 Canon path: ${this.canonPath}`);
console.log(`📂 Public path: ${this.publicPath}\n`);
// Initial sync
await this.syncAll();
// Watch for changes if requested
if (this.isWatching) {
this.startWatching();
}
}
}
// Run the sync tool
const cssSync = new CSSSync();
cssSync.run().catch(error => {
console.error('❌ CSS Sync failed:', error);
process.exit(1);
});
+2 -1
View File
@@ -6,7 +6,8 @@ import { CustomTheme } from '@/components/CustomTheme';
import styles from '../css/page.module.css';
import '../css/globals.css';
import '/public/styles.css';
import '/public/theme-backstage.css';
import '/public/theme-spotify.css';
export const metadata: Metadata = {
title: 'Canon',
@@ -7,6 +7,7 @@ import { usePlayground } from '@/utils/playground-context';
const themes = [
{ name: 'Backstage', value: 'default' },
{ name: 'Spotify', value: 'spotify' },
{ name: 'Custom theme', value: 'custom' },
];
+23 -10
View File
@@ -40,16 +40,29 @@ export const PlaygroundProvider = ({ children }: { children: ReactNode }) => {
const [selectedComponents, setSelectedComponents] = useState<string[]>(
components.map(component => component.slug),
);
const [selectedTheme, setSelectedTheme] = useState<Theme>(() => {
return isBrowser
? (localStorage.getItem('theme') as Theme) || 'light'
: 'light';
});
const [selectedThemeName, setSelectedThemeName] = useState<ThemeName>(() => {
return isBrowser
? (localStorage.getItem('theme-name') as ThemeName) || 'default'
: 'default';
});
const [selectedTheme, setSelectedTheme] = useState<Theme>('light');
const [selectedThemeName, setSelectedThemeName] =
useState<ThemeName>('default');
// Load saved theme from localStorage after hydration
useEffect(() => {
if (isBrowser) {
const savedTheme = localStorage.getItem('theme') as Theme;
if (savedTheme) {
setSelectedTheme(savedTheme);
}
}
}, [isBrowser]);
// Load saved theme name from localStorage after hydration
useEffect(() => {
if (isBrowser) {
const savedThemeName = localStorage.getItem('theme-name') as ThemeName;
if (savedThemeName) {
setSelectedThemeName(savedThemeName);
}
}
}, [isBrowser]);
useEffect(() => {
if (isBrowser) {
+32
View File
@@ -0,0 +1,32 @@
// Auto-generated theme switcher utility
export const availableThemes = {
default: '/styles.css',
spotify: '/spotify-theme.css',
} as const;
export type ThemeName = keyof typeof availableThemes;
export function switchTheme(themeName: ThemeName) {
// Remove existing theme links
const existingLinks = document.querySelectorAll('link[data-theme]');
existingLinks.forEach(link => link.remove());
// Add new theme link
const link = document.createElement('link');
link.rel = 'stylesheet';
link.href = availableThemes[themeName];
link.setAttribute('data-theme', themeName);
document.head.appendChild(link);
}
export function getCurrentTheme(): ThemeName {
const existingLink = document.querySelector('link[data-theme]');
if (existingLink) {
const href = existingLink.getAttribute('href');
for (const [name, path] of Object.entries(availableThemes)) {
if (path === href) return name as ThemeName;
}
}
return 'default';
}
+882 -22
View File
File diff suppressed because it is too large Load Diff
-1
View File
@@ -15,7 +15,6 @@ const config: StorybookConfig = {
getAbsolutePath('@storybook/addon-webpack5-compiler-swc'),
getAbsolutePath('@storybook/addon-essentials'),
getAbsolutePath('@storybook/addon-interactions'),
getAbsolutePath('@storybook/addon-themes'),
],
framework: {
name: getAbsolutePath('@storybook/react-webpack5'),
+69 -9
View File
@@ -1,8 +1,74 @@
import type { Preview, ReactRenderer } from '@storybook/react';
import { withThemeByDataAttribute } from '@storybook/addon-themes';
import React, { useEffect } from 'react';
import type { Decorator, Preview, ReactRenderer } from '@storybook/react';
import { useGlobals } from '@storybook/preview-api';
// Default Backstage theme
import '../src/css/styles.css';
// Custom themes
import './themes/spotify.css';
export const withThemes: Decorator = StoryFn => {
const [globals] = useGlobals();
const selectedTheme = globals.themeMode || 'light';
const selectedThemeName = globals.themeName || 'default';
useEffect(() => {
const htmlElement = document.documentElement;
// Remove any existing theme data attributes
htmlElement.removeAttribute('data-theme');
htmlElement.removeAttribute('data-theme-name');
// Add the selected theme data attribute
htmlElement.setAttribute('data-theme', selectedTheme);
htmlElement.setAttribute('data-theme-name', selectedThemeName);
// Cleanup on unmount
return () => {
htmlElement.removeAttribute('data-theme');
htmlElement.removeAttribute('data-theme-name');
};
}, [selectedTheme, selectedThemeName]);
return <StoryFn />;
};
const preview: Preview = {
globalTypes: {
themeMode: {
name: 'Theme Mode',
description: 'Global theme mode for components',
defaultValue: 'light',
toolbar: {
icon: 'circlehollow',
items: [
{ value: 'light', icon: 'circlehollow', title: 'Light' },
{ value: 'dark', icon: 'circle', title: 'Dark' },
],
showName: true,
dynamicTitle: true,
},
},
themeName: {
name: 'Theme Name',
description: 'Global theme name for components',
defaultValue: 'default',
toolbar: {
icon: 'paintbrush',
items: [
{ value: 'default', title: 'Default (Backstage)' },
{ value: 'spotify', title: 'Spotify' },
],
showName: true,
dynamicTitle: true,
},
},
},
initialGlobals: {
themeMode: 'light',
themeName: 'default',
},
parameters: {
controls: {
matchers: {
@@ -67,13 +133,7 @@ const preview: Preview = {
},
},
decorators: [
withThemeByDataAttribute<ReactRenderer>({
themes: {
Light: 'light',
Dark: 'dark',
},
defaultTheme: 'Light',
}),
withThemes,
Story => {
document.body.style.backgroundColor = 'var(--canon-bg)';
@@ -0,0 +1,194 @@
@font-face {
font-family: CircularSp;
font-weight: 700;
font-display: swap;
unicode-range: U+0000, U+000D, U+0020-007E, U+00A0-017E, U+018F, U+0192,
U+01A0-01A1, U+01AF-01B0, U+01B5-01B6, U+01C4-01C6, U+01F1-01F3, U+01FA-01FF,
U+0218-021B, U+0237, U+0259, U+02BB-02BC, U+02C6-02C7, U+02C9, U+02D8-02DD,
U+0300-0301, U+0303, U+0309, U+0323, U+0394, U+03A9, U+03BC, U+03C0,
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='dark'][data-theme-name='spotify'] {
--canon-font-text: CircularSp, CircularSp-Arab, CircularSp-Hebr,
CircularSp-Cyrl, CircularSp-Grek, CircularSp-Deva;
--canon-font-title: CircularSpTitle, CircularSp-Arab, CircularSp-Hebr,
CircularSp-Cyrl, CircularSp-Grek, CircularSp-Deva;
--canon-font-regular: CircularSp, CircularSp-Arab, CircularSp-Hebr,
CircularSp-Cyrl, CircularSp-Grek, CircularSp-Deva;
/* Gray scale */
--canon-gray-1: #191919;
--canon-gray-2: #242424;
--canon-gray-3: #373737;
--canon-gray-4: #464646;
--canon-gray-5: #575757;
--canon-gray-6: #7b7b7b;
--canon-gray-7: #9e9e9e;
--canon-gray-8: #b4b4b4;
/* Background Colors */
--canon-bg: #000000;
--canon-bg-surface-1: #191919;
--canon-bg-surface-2: #242424;
--canon-bg-solid: #1ed760;
--canon-bg-solid-hover: #3be477;
--canon-bg-solid-pressed: #1abc54;
--canon-bg-solid-disabled: #0f6c30;
--canon-bg-tint: #242424;
--canon-bg-tint-hover: #202020;
--canon-bg-tint-pressed: #292929;
--canon-bg-tint-disabled: rgba(255, 255, 255, 0.8);
--canon-bg-danger: #3b1219;
--canon-bg-warning: #302008;
--canon-bg-success: #132d21;
--canon-bg-underline: #d16ecb;
/* Foreground Colors */
--canon-fg-primary: #ffffff;
--canon-fg-secondary: #9e9e9e;
--canon-fg-link: #ffffff;
--canon-fg-link-hover: #ffffff;
--canon-fg-disabled: #575757;
--canon-fg-solid: #000000;
--canon-fg-solid-disabled: #083618;
--canon-fg-tint: #ffffff;
--canon-fg-tint-disabled: #575757;
--canon-fg-danger: #e22b2b;
--canon-fg-warning: #e36d05;
--canon-fg-success: #1db954;
/* Border Colors */
--canon-border: var(--canon-gray-3);
--canon-border-hover: rgba(255, 255, 255, 0.4);
--canon-border-pressed: rgba(255, 255, 255, 0.5);
--canon-border-disabled: rgba(255, 255, 255, 0.2);
--canon-border-danger: #f87a7a;
--canon-border-warning: #e36d05;
--canon-border-success: #53db83;
/* Ring Colors */
--canon-ring: rgba(255, 255, 255, 0.2);
}
#root {
padding-top: var(--header-height);
}
.canon-Button,
.canon-IconButton {
border-radius: var(--canon-radius-3);
transition: background-color 0.2s ease-in-out, color 0.2s ease-in-out,
box-shadow 0.2s ease-in-out;
}
.canon-Button--variant-primary,
.canon-IconButton--variant-primary {
&:hover {
box-shadow: inset 0 0 0 1px var(--canon-bg-solid);
background-color: transparent;
color: var(--canon-fg-primary);
}
&:active {
background-color: rgba(30, 215, 96, 0.1);
}
&:disabled {
background-color: var(--canon-bg-solid-disabled);
color: var(--canon-fg-solid-disabled);
box-shadow: none;
}
}
.canon-Button[data-size='medium'],
.canon-Button[data-size='small'] {
padding-inline: var(--canon-space-3);
}
.canon-MenuTrigger {
all: unset;
display: inline-flex;
}
.canon-MenuPopup {
box-sizing: border-box;
max-width: 21.25rem;
}
.canon-MenuSubmenuTrigger,
.canon-MenuItem {
height: auto;
min-height: 2rem;
}
.canon-Text {
font-family: var(--canon-font-text);
}
.canon-Heading {
font-family: var(--canon-font-title);
}
.canon-TableRow {
border: none;
border-radius: var(--canon-radius-4);
}
.canon-TableRow:hover td:first-child {
border-top-left-radius: var(--canon-radius-2);
border-bottom-left-radius: var(--canon-radius-2);
}
.canon-TableRow:hover td:last-child {
border-top-right-radius: var(--canon-radius-2);
border-bottom-right-radius: var(--canon-radius-2);
}
.canon-TableBody:before,
.canon-TableBody:after {
line-height: var(--canon-space-1);
content: '\200C';
display: block;
}
.canon-TableHeader .canon-TableRow {
border-bottom: 1px solid var(--canon-border);
}
.canon-TableHead {
font-size: var(--canon-font-size-2);
color: var(--canon-fg-secondary);
font-weight: var(--canon-font-weight-regular);
}
.canon-ScrollAreaRoot {
width: 100%;
/* TODO: Bring this as a fix in OS */
height: unset;
}
.canon-TabsIndicator {
background-color: var(--canon-bg-underline);
}
.canon-TabsTab[data-selected] {
background: radial-gradient(
ellipse at bottom,
rgba(209, 110, 203, 0.25) 0%,
transparent 95%
);
}
-1
View File
@@ -52,7 +52,6 @@
"@storybook/addon-essentials": "^8.6.12",
"@storybook/addon-interactions": "^8.6.12",
"@storybook/addon-styling-webpack": "^1.0.1",
"@storybook/addon-themes": "^8.6.12",
"@storybook/addon-webpack5-compiler-swc": "^3.0.0",
"@storybook/blocks": "^8.6.12",
"@storybook/react": "^8.6.12",
-12
View File
@@ -3821,7 +3821,6 @@ __metadata:
"@storybook/addon-essentials": "npm:^8.6.12"
"@storybook/addon-interactions": "npm:^8.6.12"
"@storybook/addon-styling-webpack": "npm:^1.0.1"
"@storybook/addon-themes": "npm:^8.6.12"
"@storybook/addon-webpack5-compiler-swc": "npm:^3.0.0"
"@storybook/blocks": "npm:^8.6.12"
"@storybook/react": "npm:^8.6.12"
@@ -19996,17 +19995,6 @@ __metadata:
languageName: node
linkType: hard
"@storybook/addon-themes@npm:^8.6.12":
version: 8.6.12
resolution: "@storybook/addon-themes@npm:8.6.12"
dependencies:
ts-dedent: "npm:^2.0.0"
peerDependencies:
storybook: ^8.6.12
checksum: 10/2df945accd7c0546785f71e2adc4ed68d71a057bc54aaef53f16ce8fcbc5a494380d60f9dd58520fe7d93b021a1b09e3d73553766d4bf2a1d07a3d731132b939
languageName: node
linkType: hard
"@storybook/addon-toolbars@npm:8.6.12":
version: 8.6.12
resolution: "@storybook/addon-toolbars@npm:8.6.12"