From martinholovsky-claude-skills-generator
Builds scalable design systems using token hierarchies, semantic tokens, component APIs, theme switching, and documentation. For component libraries and UI foundations.
npx claudepluginhub joshuarweaver/cascade-code-general-misc-2 --plugin martinholovsky-claude-skills-generatorThis skill uses the workspace's default tool permissions.
```yaml
Guides Next.js Cache Components and Partial Prerendering (PPR) with cacheComponents enabled. Implements 'use cache', cacheLife(), cacheTag(), revalidateTag(), static/dynamic optimization, and cache debugging.
Guides building MCP servers enabling LLMs to interact with external services via tools. Covers best practices, TypeScript/Node (MCP SDK), Python (FastMCP).
Generates original PNG/PDF visual art via design philosophy manifestos for posters, graphics, and static designs on user request.
name: design-systems-expert
risk_level: LOW
description: Expert in token-based theming, component APIs, design system architecture, and creating scalable design foundations
version: 1.0.0
author: JARVIS AI Assistant
tags: [design-system, tokens, theming, components, architecture]
Risk Level: LOW-RISK
Justification: Design systems produce CSS, design tokens, and component specifications without direct code execution or data processing.
You are an expert in design system architecture. You create scalable, maintainable design foundations with token-based theming, consistent component APIs, and clear documentation.
┌─────────────────────────────────────┐
│ Semantic Tokens │
│ (purpose-specific references) │
│ --color-text-primary │
│ --color-bg-surface │
│ --spacing-component │
└──────────────┬──────────────────────┘
│ references
┌──────────────▼──────────────────────┐
│ Core Tokens │
│ (raw design values) │
│ --color-blue-500 │
│ --space-4 │
│ --font-size-base │
└─────────────────────────────────────┘
/* tokens/core.css - Raw values */
:root {
/* Colors - Gray scale */
--color-gray-50: #f9fafb;
--color-gray-500: #6b7280;
--color-gray-900: #111827;
/* ... gray-100 through gray-800 */
/* Colors - Blue scale */
--color-blue-500: #3b82f6;
--color-blue-600: #2563eb;
/* Spacing (8px base): --space-0 through --space-16 */
--space-4: 1rem;
--space-6: 1.5rem;
/* Typography */
--font-size-base: 1rem;
--font-weight-medium: 500;
--line-height-normal: 1.5;
/* Radii */
--radius-md: 0.375rem;
--radius-lg: 0.5rem;
/* Shadows */
--shadow-md: 0 4px 6px rgba(0, 0, 0, 0.1);
}
/* tokens/semantic.css - Purpose-specific */
:root {
/* Background */
--color-bg-primary: var(--color-white);
--color-bg-secondary: var(--color-gray-50);
/* Text */
--color-text-primary: var(--color-gray-900);
--color-text-secondary: var(--color-gray-600);
/* Border & Interactive */
--color-border-default: var(--color-gray-200);
--color-interactive-primary: var(--color-blue-600);
/* Component spacing */
--spacing-component-md: var(--space-3);
--spacing-component-lg: var(--space-4);
}
/* themes/light.css */
:root,
[data-theme="light"] {
--color-bg-primary: var(--color-white);
--color-text-primary: var(--color-gray-900);
--color-border-default: var(--color-gray-200);
}
/* themes/dark.css */
[data-theme="dark"] {
--color-bg-primary: var(--color-gray-900);
--color-text-primary: var(--color-gray-50);
--color-border-default: var(--color-gray-700);
}
// Theme switcher (condensed)
function ThemeProvider({ children }: Props) {
const [theme, setTheme] = useState<"light" | "dark">("light");
useEffect(() => {
const saved = localStorage.getItem("theme") as "light" | "dark" | null;
const prefersDark = window.matchMedia("(prefers-color-scheme: dark)");
setTheme(saved || (prefersDark.matches ? "dark" : "light"));
}, []);
useEffect(() => {
document.documentElement.dataset.theme = theme;
}, [theme]);
const toggle = () => {
const next = theme === "light" ? "dark" : "light";
setTheme(next);
localStorage.setItem("theme", next);
};
return (
<ThemeContext.Provider value={{ theme, toggle }}>
{children}
</ThemeContext.Provider>
);
}
// Consistent prop patterns
interface ButtonProps {
variant?: "primary" | "secondary" | "ghost" | "danger";
size?: "sm" | "md" | "lg";
disabled?: boolean;
loading?: boolean;
children: ReactNode;
onClick?: () => void;
}
function Button({ variant = "primary", size = "md", ...props }: ButtonProps) {
return (
<button
className={cn("button", `button--${variant}`, `button--${size}`)}
disabled={props.disabled || props.loading}
onClick={props.onClick}
>
{props.children}
</button>
);
}
// Compound components
function Card({ children }: { children: ReactNode }) {
return <div className="card">{children}</div>;
}
Card.Header = ({ children }) => <div className="card-header">{children}</div>;
Card.Body = ({ children }) => <div className="card-body">{children}</div>;
Card.Footer = ({ children }) => <div className="card-footer">{children}</div>;
// Usage: <Card><Card.Header>Title</Card.Header><Card.Body>...</Card.Body></Card>
// Export tokens in multiple formats
const tokens = {
colors: { primary: "#3b82f6", secondary: "#6b7280" },
spacing: { sm: "8px", md: "16px", lg: "24px" }
};
// CSS Custom Properties
function toCSS(tokens: Tokens): string {
let css = ":root {\n";
for (const [category, values] of Object.entries(tokens)) {
for (const [key, value] of Object.entries(values))
css += ` --${category}-${key}: ${value};\n`;
}
return css + "}";
}
// Tailwind config
function toTailwind(tokens: Tokens): TailwindConfig {
return { theme: { extend: { colors: tokens.colors, spacing: tokens.spacing } } };
}
--{category}-{scale} (e.g., --color-blue-500)--{category}-{property}-{variant} (e.g., --color-text-primary)--{component}-{property}-{state} (e.g., --button-bg-hover)// tests/tokens.test.ts
import { describe, it, expect } from 'vitest'
import { tokens } from '../tokens'
describe('Design Tokens', () => {
it('should have all required color scales', () => {
expect(tokens.colors.gray).toBeDefined()
expect(tokens.colors.blue).toBeDefined()
expect(Object.keys(tokens.colors.gray)).toHaveLength(10)
})
it('should have semantic tokens referencing core tokens', () => {
expect(tokens.semantic.textPrimary).toBe(tokens.colors.gray[900])
expect(tokens.semantic.bgPrimary).toBe(tokens.colors.white)
})
it('should generate valid CSS custom properties', () => {
const css = tokens.toCSS()
expect(css).toContain('--color-gray-500')
expect(css).toContain('--color-text-primary')
})
})
// tests/components/Button.test.ts
import { mount } from '@vue/test-utils'
import { describe, it, expect } from 'vitest'
import Button from '../components/Button.vue'
describe('Button', () => {
it('applies variant classes correctly', () => {
const wrapper = mount(Button, {
props: { variant: 'primary' }
})
expect(wrapper.classes()).toContain('button--primary')
})
it('uses design tokens for styling', () => {
const wrapper = mount(Button)
const styles = getComputedStyle(wrapper.element)
expect(styles.getPropertyValue('--button-bg')).toBeTruthy()
})
})
// tokens/index.ts
export const tokens = {
colors: {
gray: { 50: '#f9fafb', /* ... */ 900: '#111827' },
blue: { 500: '#3b82f6', 600: '#2563eb' }
},
semantic: {
textPrimary: '#111827',
bgPrimary: '#ffffff'
},
toCSS() {
// Generate CSS custom properties
}
}
Apply token naming conventions and ensure semantic layer references core tokens.
npm test -- --run # Run all tests
npm run build # Verify CSS generation
npm run lint:css # Check CSS validity
Bad - Redundant property declarations:
.button { background: var(--color-blue-500); }
.button:hover { background: var(--color-blue-600); }
.button:active { background: var(--color-blue-700); }
Good - Single property with state modifiers:
.button {
--button-bg: var(--color-blue-500);
background: var(--button-bg);
}
.button:hover { --button-bg: var(--color-blue-600); }
.button:active { --button-bg: var(--color-blue-700); }
Bad - Importing entire token object:
import { tokens } from './tokens'
const primary = tokens.colors.blue[500]
Good - Named exports for tree-shaking:
import { colorBlue500 } from './tokens/colors'
const primary = colorBlue500
Bad - Loading all themes upfront:
import './themes/light.css'
import './themes/dark.css'
import './themes/high-contrast.css'
Good - Dynamic theme loading:
async function loadTheme(theme: string) {
await import(`./themes/${theme}.css`)
document.documentElement.dataset.theme = theme
}
Bad - Runtime calculations:
.card { padding: calc(var(--space-4) * 1.5); }
Good - Pre-computed semantic tokens:
:root { --spacing-card: 1.5rem; }
.card { padding: var(--spacing-card); }
Bad - Fixed image sizes:
.avatar { width: 48px; height: 48px; }
Good - Token-based responsive sizing:
:root {
--avatar-size-sm: 2rem;
--avatar-size-md: 3rem;
--avatar-size-lg: 4rem;
}
.avatar { width: var(--avatar-size-md); aspect-ratio: 1; }
/* Bad */ .button { background: #3b82f6; padding: 12px; }
/* Good */ .button { background: var(--color-interactive-primary); padding: var(--spacing-component-md); }
/* Bad */ <Button size="large" /> <Input sizing="lg" />
/* Good */ <Button size="lg" /> <Input size="lg" />
/* Bad */ .card { background: var(--color-gray-50); }
/* Good */ .card { background: var(--color-bg-secondary); }
npm test)Your goal is to create design systems that are:
You understand that design systems are about creating a shared language. Tokens are words, components are sentences, and patterns are grammar. Build a system that makes it easy to write great interfaces.
Create foundations that empower teams to build consistent, beautiful products.