feat(dss-ui): Button component with design tokens only
Some checks failed
DSS Project Analysis / dss-context-update (push) Has been cancelled
Some checks failed
DSS Project Analysis / dss-context-update (push) Has been cancelled
- Button.css uses only CSS custom properties, no fallbacks - Token validator now blocks hardcoded values (strict_mode: true) - Hook scripts converted from ESM (.js) to CommonJS (.cjs) - Storybook unified config with HMR disabled for nginx proxy - Added dss-ui package with Figma-synced components and stories 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
94
.storybook/main.ts
Normal file
94
.storybook/main.ts
Normal file
@@ -0,0 +1,94 @@
|
||||
import type { StorybookConfig } from '@storybook/preact-vite';
|
||||
import { join, dirname } from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
import { readFileSync, existsSync } from 'fs';
|
||||
|
||||
// ESM-compatible __dirname
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = dirname(__filename);
|
||||
|
||||
/**
|
||||
* DSS Unified Storybook Configuration
|
||||
*
|
||||
* Loads stories from:
|
||||
* 1. DSS Core (packages/dss-ui/stories/) - atoms, molecules, organisms
|
||||
* 2. Active project stories - templates, pages, project-specific components
|
||||
*
|
||||
* Project selection is controlled via:
|
||||
* 1. STORYBOOK_PROJECT env var (highest priority)
|
||||
* 2. .dss/state.json activeProject field (managed by Claude/admin-ui)
|
||||
* 3. Default: 'admin-ui'
|
||||
*/
|
||||
|
||||
// Read active project from DSS state
|
||||
function getActiveProject(): string {
|
||||
// Env var takes priority
|
||||
if (process.env.STORYBOOK_PROJECT) {
|
||||
return process.env.STORYBOOK_PROJECT;
|
||||
}
|
||||
|
||||
// Try reading from DSS state
|
||||
const statePath = join(__dirname, '../.dss/state.json');
|
||||
if (existsSync(statePath)) {
|
||||
try {
|
||||
const state = JSON.parse(readFileSync(statePath, 'utf-8'));
|
||||
if (state.activeProject) {
|
||||
return state.activeProject;
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn('Failed to read DSS state, using default project');
|
||||
}
|
||||
}
|
||||
|
||||
return 'admin-ui';
|
||||
}
|
||||
|
||||
const activeProject = getActiveProject();
|
||||
console.log(`[DSS] Active project: ${activeProject}`);
|
||||
|
||||
const config: StorybookConfig = {
|
||||
stories: [
|
||||
// DSS Core stories (always loaded)
|
||||
'../packages/dss-ui/stories/**/*.stories.@(js|jsx|ts|tsx)',
|
||||
'../packages/dss-ui/stories/**/*.mdx',
|
||||
// Project-specific stories
|
||||
`../${activeProject}/src/**/*.stories.@(js|jsx|ts|tsx)`,
|
||||
`../${activeProject}/src/**/*.mdx`,
|
||||
],
|
||||
addons: [
|
||||
'@storybook/addon-docs',
|
||||
'@storybook/addon-a11y',
|
||||
],
|
||||
framework: {
|
||||
name: '@storybook/preact-vite',
|
||||
options: {},
|
||||
},
|
||||
docs: {
|
||||
autodocs: 'tag',
|
||||
},
|
||||
staticDirs: [
|
||||
'../packages/dss-ui/src/primitives',
|
||||
],
|
||||
viteFinal: async (config) => {
|
||||
config.server = config.server || {};
|
||||
config.server.allowedHosts = [
|
||||
'localhost',
|
||||
'storybook.dss.overbits.luz.uy'
|
||||
];
|
||||
// HMR configuration
|
||||
// For local development: HMR works normally on localhost:6006
|
||||
// For external access (storybook.dss.overbits.luz.uy): HMR is disabled
|
||||
// because Vite uses a separate port (24678) that isn't proxied through nginx
|
||||
// Disable HMR for external access - Vite's HMR websocket isn't proxied through nginx
|
||||
config.server.hmr = false;
|
||||
// Resolve packages for proper imports
|
||||
config.resolve = config.resolve || {};
|
||||
config.resolve.alias = {
|
||||
...config.resolve.alias,
|
||||
'@dss/ui': join(__dirname, '../packages/dss-ui/src'),
|
||||
};
|
||||
return config;
|
||||
},
|
||||
};
|
||||
|
||||
export default config;
|
||||
238
.storybook/preview.jsx
Normal file
238
.storybook/preview.jsx
Normal file
@@ -0,0 +1,238 @@
|
||||
/**
|
||||
* DSS Unified Storybook Preview
|
||||
*
|
||||
* Single Storybook instance that loads:
|
||||
* - DSS Core components (atoms, molecules, organisms)
|
||||
* - Project-specific stories (templates, pages)
|
||||
*
|
||||
* Toolbar controls:
|
||||
* - Skin: shadcn, material, heroui
|
||||
* - Mode: light/dark
|
||||
*/
|
||||
|
||||
import { h } from 'preact';
|
||||
|
||||
// Import all component CSS from DSS Core
|
||||
const coreStyles = import.meta.glob('../packages/dss-ui/src/atoms/*.css', { eager: true });
|
||||
|
||||
// Import design tokens
|
||||
import '../packages/dss-ui/src/primitives/tokens.css';
|
||||
|
||||
// Skin CSS variables - generated from skin dictionaries
|
||||
const skinStyles = {
|
||||
shadcn: {
|
||||
'--color-background': 'hsl(0 0% 100%)',
|
||||
'--color-foreground': 'hsl(240 10% 3.9%)',
|
||||
'--color-card': 'hsl(0 0% 100%)',
|
||||
'--color-card-foreground': 'hsl(240 10% 3.9%)',
|
||||
'--color-popover': 'hsl(0 0% 100%)',
|
||||
'--color-popover-foreground': 'hsl(240 10% 3.9%)',
|
||||
'--color-primary': 'hsl(240 5.9% 10%)',
|
||||
'--color-primary-foreground': 'hsl(0 0% 98%)',
|
||||
'--color-secondary': 'hsl(240 4.8% 95.9%)',
|
||||
'--color-secondary-foreground': 'hsl(240 5.9% 10%)',
|
||||
'--color-muted': 'hsl(240 4.8% 95.9%)',
|
||||
'--color-muted-foreground': 'hsl(240 3.8% 46.1%)',
|
||||
'--color-accent': 'hsl(240 4.8% 95.9%)',
|
||||
'--color-accent-foreground': 'hsl(240 5.9% 10%)',
|
||||
'--color-destructive': 'hsl(0 84.2% 60.2%)',
|
||||
'--color-destructive-foreground': 'hsl(0 0% 98%)',
|
||||
'--color-border': 'hsl(240 5.9% 90%)',
|
||||
'--color-input': 'hsl(240 5.9% 90%)',
|
||||
'--color-ring': 'hsl(240 5.9% 10%)',
|
||||
'--radius': '0.5rem',
|
||||
},
|
||||
material: {
|
||||
'--color-background': 'hsl(0 0% 99%)',
|
||||
'--color-foreground': 'hsl(0 0% 10%)',
|
||||
'--color-card': 'hsl(0 0% 100%)',
|
||||
'--color-card-foreground': 'hsl(0 0% 10%)',
|
||||
'--color-popover': 'hsl(0 0% 100%)',
|
||||
'--color-popover-foreground': 'hsl(0 0% 10%)',
|
||||
'--color-primary': 'hsl(262 80% 50%)',
|
||||
'--color-primary-foreground': 'hsl(0 0% 100%)',
|
||||
'--color-secondary': 'hsl(262 20% 90%)',
|
||||
'--color-secondary-foreground': 'hsl(262 80% 30%)',
|
||||
'--color-muted': 'hsl(0 0% 96%)',
|
||||
'--color-muted-foreground': 'hsl(0 0% 45%)',
|
||||
'--color-accent': 'hsl(262 20% 95%)',
|
||||
'--color-accent-foreground': 'hsl(262 80% 40%)',
|
||||
'--color-destructive': 'hsl(0 75% 42%)',
|
||||
'--color-destructive-foreground': 'hsl(0 0% 100%)',
|
||||
'--color-border': 'hsl(0 0% 88%)',
|
||||
'--color-input': 'hsl(0 0% 88%)',
|
||||
'--color-ring': 'hsl(262 80% 50%)',
|
||||
'--radius': '1rem',
|
||||
},
|
||||
heroui: {
|
||||
'--color-background': 'hsl(0 0% 100%)',
|
||||
'--color-foreground': 'hsl(240 6% 10%)',
|
||||
'--color-card': 'hsl(0 0% 100%)',
|
||||
'--color-card-foreground': 'hsl(240 6% 10%)',
|
||||
'--color-popover': 'hsl(0 0% 100%)',
|
||||
'--color-popover-foreground': 'hsl(240 6% 10%)',
|
||||
'--color-primary': 'hsl(212 100% 48%)',
|
||||
'--color-primary-foreground': 'hsl(0 0% 100%)',
|
||||
'--color-secondary': 'hsl(270 60% 52%)',
|
||||
'--color-secondary-foreground': 'hsl(0 0% 100%)',
|
||||
'--color-muted': 'hsl(240 5% 96%)',
|
||||
'--color-muted-foreground': 'hsl(240 4% 46%)',
|
||||
'--color-accent': 'hsl(212 100% 95%)',
|
||||
'--color-accent-foreground': 'hsl(212 100% 40%)',
|
||||
'--color-destructive': 'hsl(0 72% 51%)',
|
||||
'--color-destructive-foreground': 'hsl(0 0% 100%)',
|
||||
'--color-border': 'hsl(240 6% 90%)',
|
||||
'--color-input': 'hsl(240 6% 90%)',
|
||||
'--color-ring': 'hsl(212 100% 48%)',
|
||||
'--radius': '0.75rem',
|
||||
},
|
||||
};
|
||||
|
||||
// Dark mode overrides
|
||||
const darkModeStyles = {
|
||||
shadcn: {
|
||||
'--color-background': 'hsl(240 10% 3.9%)',
|
||||
'--color-foreground': 'hsl(0 0% 98%)',
|
||||
'--color-card': 'hsl(240 10% 3.9%)',
|
||||
'--color-card-foreground': 'hsl(0 0% 98%)',
|
||||
'--color-popover': 'hsl(240 10% 3.9%)',
|
||||
'--color-popover-foreground': 'hsl(0 0% 98%)',
|
||||
'--color-primary': 'hsl(0 0% 98%)',
|
||||
'--color-primary-foreground': 'hsl(240 5.9% 10%)',
|
||||
'--color-secondary': 'hsl(240 3.7% 15.9%)',
|
||||
'--color-secondary-foreground': 'hsl(0 0% 98%)',
|
||||
'--color-muted': 'hsl(240 3.7% 15.9%)',
|
||||
'--color-muted-foreground': 'hsl(240 5% 64.9%)',
|
||||
'--color-accent': 'hsl(240 3.7% 15.9%)',
|
||||
'--color-accent-foreground': 'hsl(0 0% 98%)',
|
||||
'--color-destructive': 'hsl(0 62.8% 30.6%)',
|
||||
'--color-destructive-foreground': 'hsl(0 0% 98%)',
|
||||
'--color-border': 'hsl(240 3.7% 15.9%)',
|
||||
'--color-input': 'hsl(240 3.7% 15.9%)',
|
||||
'--color-ring': 'hsl(240 4.9% 83.9%)',
|
||||
},
|
||||
material: {
|
||||
'--color-background': 'hsl(280 10% 8%)',
|
||||
'--color-foreground': 'hsl(280 5% 90%)',
|
||||
'--color-card': 'hsl(280 10% 12%)',
|
||||
'--color-card-foreground': 'hsl(280 5% 90%)',
|
||||
'--color-popover': 'hsl(280 10% 12%)',
|
||||
'--color-popover-foreground': 'hsl(280 5% 90%)',
|
||||
'--color-primary': 'hsl(262 80% 70%)',
|
||||
'--color-primary-foreground': 'hsl(280 10% 8%)',
|
||||
'--color-secondary': 'hsl(262 20% 25%)',
|
||||
'--color-secondary-foreground': 'hsl(262 50% 85%)',
|
||||
'--color-muted': 'hsl(280 10% 15%)',
|
||||
'--color-muted-foreground': 'hsl(280 5% 60%)',
|
||||
'--color-accent': 'hsl(262 20% 20%)',
|
||||
'--color-accent-foreground': 'hsl(262 80% 80%)',
|
||||
'--color-destructive': 'hsl(0 60% 50%)',
|
||||
'--color-destructive-foreground': 'hsl(0 0% 100%)',
|
||||
'--color-border': 'hsl(280 10% 20%)',
|
||||
'--color-input': 'hsl(280 10% 20%)',
|
||||
'--color-ring': 'hsl(262 80% 70%)',
|
||||
},
|
||||
heroui: {
|
||||
'--color-background': 'hsl(240 10% 4%)',
|
||||
'--color-foreground': 'hsl(0 0% 95%)',
|
||||
'--color-card': 'hsl(240 10% 8%)',
|
||||
'--color-card-foreground': 'hsl(0 0% 95%)',
|
||||
'--color-popover': 'hsl(240 10% 8%)',
|
||||
'--color-popover-foreground': 'hsl(0 0% 95%)',
|
||||
'--color-primary': 'hsl(212 100% 55%)',
|
||||
'--color-primary-foreground': 'hsl(0 0% 100%)',
|
||||
'--color-secondary': 'hsl(270 60% 60%)',
|
||||
'--color-secondary-foreground': 'hsl(0 0% 100%)',
|
||||
'--color-muted': 'hsl(240 5% 15%)',
|
||||
'--color-muted-foreground': 'hsl(240 4% 60%)',
|
||||
'--color-accent': 'hsl(212 50% 15%)',
|
||||
'--color-accent-foreground': 'hsl(212 100% 70%)',
|
||||
'--color-destructive': 'hsl(0 72% 55%)',
|
||||
'--color-destructive-foreground': 'hsl(0 0% 100%)',
|
||||
'--color-border': 'hsl(240 6% 20%)',
|
||||
'--color-input': 'hsl(240 6% 20%)',
|
||||
'--color-ring': 'hsl(212 100% 55%)',
|
||||
},
|
||||
};
|
||||
|
||||
// Apply theme to document root
|
||||
const applyTheme = (skin, mode) => {
|
||||
const root = document.documentElement;
|
||||
const styles = mode === 'dark'
|
||||
? { ...skinStyles[skin], ...darkModeStyles[skin] }
|
||||
: skinStyles[skin];
|
||||
|
||||
Object.entries(styles).forEach(([prop, value]) => {
|
||||
root.style.setProperty(prop, value);
|
||||
});
|
||||
|
||||
// Set background color on body for Storybook canvas
|
||||
document.body.style.backgroundColor = styles['--color-background'];
|
||||
document.body.style.color = styles['--color-foreground'];
|
||||
};
|
||||
|
||||
/** @type { import('@storybook/preact').Preview } */
|
||||
const preview = {
|
||||
globalTypes: {
|
||||
skin: {
|
||||
description: 'Design system skin',
|
||||
defaultValue: 'shadcn',
|
||||
toolbar: {
|
||||
title: 'Skin',
|
||||
icon: 'paintbrush',
|
||||
items: [
|
||||
{ value: 'shadcn', title: 'shadcn/ui (Default)' },
|
||||
{ value: 'material', title: 'Material Design' },
|
||||
{ value: 'heroui', title: 'HeroUI' },
|
||||
],
|
||||
dynamicTitle: true,
|
||||
},
|
||||
},
|
||||
mode: {
|
||||
description: 'Color mode',
|
||||
defaultValue: 'light',
|
||||
toolbar: {
|
||||
title: 'Mode',
|
||||
icon: 'circlehollow',
|
||||
items: [
|
||||
{ value: 'light', title: 'Light', icon: 'sun' },
|
||||
{ value: 'dark', title: 'Dark', icon: 'moon' },
|
||||
],
|
||||
dynamicTitle: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
parameters: {
|
||||
controls: {
|
||||
matchers: {
|
||||
color: /(background|color)$/i,
|
||||
date: /Date$/i,
|
||||
},
|
||||
},
|
||||
backgrounds: {
|
||||
disable: true, // Use our custom theme system instead
|
||||
},
|
||||
options: {
|
||||
storySort: {
|
||||
order: [
|
||||
'0. Overview',
|
||||
'1. Primitives',
|
||||
'2. Atoms',
|
||||
'3. Molecules',
|
||||
'4. Organisms',
|
||||
'5. Templates', // Project-specific
|
||||
'6. Pages', // Project-specific
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
decorators: [
|
||||
(Story, context) => {
|
||||
const { skin, mode } = context.globals;
|
||||
applyTheme(skin || 'shadcn', mode || 'light');
|
||||
return h(Story, null);
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export default preview;
|
||||
Reference in New Issue
Block a user