mcp0_get_screenshot(nodeId: "<node-id>")
mcp0_get_design_context(nodeId: "<node-id>", artifactType: "REUSABLE_COMPONENT", forceCode: true)
'use client';
import { forwardRef } from 'react';
import { cn } from '../../lib/utils';
export interface Illustration<Name>Props
extends React.HTMLAttributes<HTMLDivElement> {
size?: number;
}
const Illustration<Name> = forwardRef<HTMLDivElement, Illustration<Name>Props>(
({ className, size = 160, ...props }, ref) => {
return (
<div
ref={ref}
className={cn('relative shrink-0', className)}
style={{ width: size, height: size }}
role="img"
aria-label="<descriptive label>"
{...props}
>
{/* Layer comment */}
<div
className="absolute"
style={{
top: '<top>%',
right: '<right>%',
bottom: '<bottom>%',
left: '<left>%',
}}
>
<img
alt=""
className="block size-full"
src="/assets/illustrations/<name>/<asset>.svg"
style={{ maxWidth: 'none' }}
/>
</div>
{/* ... repeat for each layer ... */}
</div>
);
}
);
Illustration<Name>.displayName = 'Illustration<Name>';
export { Illustration<Name> };
import type { Meta, StoryObj } from '@storybook/react';
import { Illustration<Name> } from './Illustration<Name>';
const meta: Meta<typeof Illustration<Name>> = {
title: 'Atoms/Illustration<Name>',
component: Illustration<Name>,
tags: ['autodocs'],
argTypes: {
size: {
control: { type: 'range', min: 40, max: 320, step: 8 },
},
},
};
export default meta;
type Story = StoryObj<typeof Illustration<Name>>;
export const Default: Story = { args: { size: 160 } };
export const Small: Story = { args: { size: 80 } };
export const Large: Story = { args: { size: 240 } };
export const OnLightBackground: Story = {
args: { size: 160 },
decorators: [
(Story) => (
<div style={{ padding: '2rem', background: '#ffffff', borderRadius: '8px' }}>
<Story />
</div>
),
],
};
export const OnDarkBackground: Story = {
args: { size: 160 },
decorators: [
(Story) => (
<div style={{ padding: '2rem', background: '#020712', borderRadius: '8px' }}>
<Story />
</div>
),
],
};
export const LightAndDarkComparison: Story = {
args: { size: 160 },
render: (args) => (
<div style={{ display: 'flex', gap: '2rem', alignItems: 'center' }}>
<div style={{ padding: '2rem', background: '#ffffff', borderRadius: '8px', border: '1px solid #d7dde9' }}>
<p style={{ marginBottom: '1rem', fontSize: '12px', color: '#7184af', fontFamily: 'Titillium Web, sans-serif' }}>Light</p>
<Illustration<Name> {...args} />
</div>
<div style={{ padding: '2rem', background: '#020712', borderRadius: '8px', border: '1px solid #2f3953' }}>
<p style={{ marginBottom: '1rem', fontSize: '12px', color: '#99a8c6', fontFamily: 'Titillium Web, sans-serif' }}>Dark</p>
<Illustration<Name> {...args} />
</div>
</div>
),
};
const config: StorybookConfig = {
stories: ['../**/*.@(mdx|stories.@(js|jsx|ts|tsx))'],
addons: [],
staticDirs: ['../public'],
};
Illustrations use the same assets for both light and dark themes. The SVGs use var(--fill-0, <fallback>) where the fallback is the light-theme color. Theme differentiation comes from the parent background color, not from swapping assets. A single component works on both light and dark backgrounds.