关于 component-preview
Component Preview 是一套借助 Ladle 隔离预览 React 组件的工作流,使用 Mantine v7 和 Tailwind 样式。它涵盖创建 story、本地渲染、截图及迭代,适用于 UI 检查。
主要功能
- 01隔离预览 React 组件
- 02为组件状态创建 Ladle stories
- 03使用 Mantine v7 和 Tailwind 样式渲染
- 04捕获浅色与深色主题截图
- 05根据视觉变化迭代
适用场景
- 01修改 UI 后检查组件效果
- 02复现并排查视觉问题
- 03提交前审查组件变更
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ civitai/ civitai --skill component-preview安装后在 Claude Code 中输入/component-preview确认已加载。
详细介绍
组件预览
使用 Ladle 隔离预览 React 组件(轻量级 Storybook 替代方案),并应用真实的 Mantine v7 + Tailwind 样式。无需开发服务器。
适用场景
- 修改 UI 组件后 — 主动提出预览组件
- 用户提出“让我看看效果”或“生成预览”时
- 调试视觉问题时 — 创建 story 以复现问题并迭代调整
- 提交前审查组件变更时
前置条件
项目根目录已配置 Ladle:
.ladle/components.tsx— 包含 MantineProvider + theme 的全局 provider.ladle/config.mjs— Story 发现配置.ladle/vite.config.ts— 配置了~/路径别名 + PostCSS 的 Vite 配置
如果当前工作树中没有这些文件,请从 main 复制或创建。请参阅下方的设置参考。
工作流
1. 创建/更新 Story
在要预览的组件附近创建一个 .stories.tsx 文件:
src/components/MyComponent/MyComponent.stories.tsx
src/pages/challenges/EligibleModels.stories.tsx
Story 结构:
import { /* Mantine components */ } from '@mantine/core';
// Import the component or recreate the relevant JSX
// Mock data that represents realistic API responses
const mockData = [ ... ];
// Render the component with different states
function Preview({ data }) {
return (
<div style={{ width: 320 }}> {/* Constrain to realistic width */}
<MyComponent data={data} />
</div>
);
}
/** Default state */
export const Default = () => <Preview data={mockData} />;
/** Empty state */
export const Empty = () => <Preview data={[]} />;
/** Loading or edge case states */
export const LongList = () => <Preview data={longMockData} />;
重要模式:
- 为包装元素设置一个符合实际情况的
width(例如,侧边栏设为 320px,主要内容设为 600px) - 从真实组件复制 Mantine 组件的确切 props 和 Tailwind 类
- 从父级上下文复制所有内联
stylesprops(例如 Accordion 样式) - 如果组件使用
useComputedColorScheme和useMantineTheme,也要使用它们 - 创建 2-4 个变体来展示不同状态(默认、空、单个项目、溢出)
2. 启动 Ladle
# Check if Ladle is already running
curl -s -o /dev/null -w "%{http_code}" http://localhost:61111/
# If not running, start it (from project root or worktree root)
cd <worktree-path>
npx ladle serve --port 61111 &
# Wait for it to be ready (~3-5 seconds)
Ladle 会自动发现符合 src/**/*.stories.tsx 的 story。
3. 捕获屏幕截图
使用浏览器自动化技能捕获裁剪并留有边距的屏幕截图:
# Create a browser session
node ~/.claude/skills/browser-automation/cli.mjs session http://localhost:61111 --name ladle
# Capture all story variants in dark and light themes
node ~/.claude/skills/browser-automation/cli.mjs run "
const stories = [
{ name: 'default', path: 'my-component--default' },
{ name: 'empty', path: 'my-component--empty' },
];
const themes = ['dark', 'light'];
const dir = '<session-screenshots-dir>';
for (const theme of themes) {
for (const story of stories) {
await page.goto('http://localhost:61111/?story=' + story.path + '&theme=' + theme + '&mode=preview');
await page.waitForTimeout(800);
const wrapper = page.locator('.ladle-story-wrapper');
await wrapper.screenshot({ path: dir + '/crop-' + theme + '-' + story.name + '.png' });
}
}
" --label "Component preview screenshots" -s ladle
Story 路径格式: Story 路径根据文件名和导出名称生成:
- 文件:
EligibleModels.stories.tsx,导出:Default-> 路径:eligible-models--default - 文件:
ModelCard.stories.tsx,导出:WithBadge-> 路径:model-card--with-badge
模式:kebab-case 文件名 + -- + kebab-case 导出名称。
4. 呈现给用户
- 使用 PNG 文件上的 Read 工具内嵌显示屏幕截图
- 如果用户想用图像查看器查看,请打开文件:
start "" "<path-to-screenshot>" - 征求反馈 — “这样看起来对吗?需要我做什么调整吗?”
- 迭代 — 如果用户希望修改,则修改组件、重新截图并再次呈现
处理复杂组件
有些组件严重依赖应用上下文。遇到这种情况时:
简单(直接处理)
- 展示型组件(徽章、卡片、列表、折叠面板)
- 仅使用 Mantine + Tailwind 的组件
- props 简单的组件
中等(模拟数据)
- 使用 tRPC 数据的组件 — 提取类型并创建模拟对象
- 包含图片的组件 — 使用占位 div 或 null 图片回退
- 包含链接的组件 — 使用
<div>或<a href="#">替代 Next.js<Link>
困难(请用户决定)
- 与多个 provider(身份验证、路由器、tRPC 上下文)深度耦合的组件
- 使用会调用 API 的复杂钩子的组件
- 严重依赖 CSS 模块的组件
遇到困难的情况时,告诉用户:
“这个组件依赖 [auth/router/tRPC context]。我可以:
- 模拟这些依赖(需要更多设置,但更准确)
- 只提取视觉部分放入 story(更快,效果也足够接近)
- 跳过预览,我们可以改在开发服务器上检查
你希望怎么做?”
设置参考
如果工作树中尚未配置 Ladle,请创建以下文件:
.ladle/components.tsx
import { MantineProvider, createTheme, Modal } from '@mantine/core';
import type { GlobalProvider } from '@ladle/react';
import '@mantine/core/styles.layer.css';
import '../src/styles/globals.css';
// Theme subset from src/providers/ThemeProvider.tsx
const theme = createTheme({
components: {
Badge: {
styles: { leftSection: { lineHeight: 1 } },
defaultProps: { radius: 'sm', variant: 'light' },
},
ActionIcon: {
defaultProps: { color: 'gray', variant: 'subtle' },
},
Tooltip: {
defaultProps: { withArrow: true },
},
},
colors: {
dark: ['#C1C2C5','#A6A7AB','#8c8fa3','#5C5F66','#373A40','#2C2E33','#25262B','#1A1B1E','#141517','#101113'],
blue: ['#E7F5FF','#D0EBFF','#A5D8FF','#74C0FC','#4DABF7','#339AF0','#228BE6','#1C7ED6','#1971C2','#1864AB'],
},
white: '#fefefe',
black: '#222',
});
export const Provider: GlobalProvider = ({ children, globalState }) => (
<MantineProvider
theme={theme}
defaultColorScheme={globalState.theme === 'dark' ? 'dark' : 'light'}
forceColorScheme={globalState.theme === 'dark' ? 'dark' : 'light'}
>
<div className="ladle-story-wrapper" style={{ padding: 24, width: 'fit-content' }}>
{children}
</div>
</MantineProvider>
);
.ladle/config.mjs
/** @type {import('@ladle/react').UserConfig} */
export default {
stories: 'src/**/*.stories.tsx',
defaultStory: '',
viteConfig: '.ladle/vite.config.ts',
};
.ladle/vite.config.ts
import { defineConfig } from 'vite';
import path from 'path';
export default defineConfig({
resolve: {
alias: { '~': path.resolve(__dirname, '../src') },
},
css: {
postcss: path.resolve(__dirname, '..'),
},
});
确保已安装 Ladle
pnpm add -D @ladle/react
提示
- 优先使用深色主题 — Civitai 默认使用深色模式,因此先截取深色主题
- 限制宽度 — 始终设置与实际场景匹配的宽度(侧边栏 = ~320px,主内容 = ~600px,整页 = ~1200px)
- 复制父级样式 — 如果组件位于 Accordion、Card 或其他容器内,请在 story 中复现这些父级样式
- 保持 story 临时性 — 一次性评审的 story 之后可以删除;可复用组件的 story 可以保留
- Ladle 端口 — 始终使用 61111,以避免与开发服务器 (3000) 和其他服务冲突
评论
支持 Markdown