design-prototype
GitHub生成基于真实 LobeHub UI 6.x 设计系统的单文件交互 HTML 原型,支持完整状态与动效。
Trigger Scenarios
Install
npx skills add lobehub/lobehub --skill design-prototype -g -y
SKILL.md
Frontmatter
{
"name": "design-prototype",
"description": "Use for interactive HTML prototypes (交互原型) with the real LobeHub UI stack. Excludes production UI implementation."
}
Design Prototype
One HTML file, double-click to open, fully interactive, rendered by the actual
design system (@lobehub/ui 6.x components, compiled style.css, --lobe-* theme tokens — exact
versions from this repo's node_modules) — while the source stays production-style
React (import { Block } from '@lobehub/ui', createStaticStyles(({ css, cssVar }) => …),
hooks, memo). Promotion to production is mostly "split into files", not "rewrite".
Prototype code may be quick-and-dirty (one file, no i18n, inline data) — but the interaction must be complete: states, transitions, and affordances are the point of a prototype, not its code style.
How it works
one-time (~2s, cached): scripts/build-runtime.sh → lobe-prototype-runtime.js (IIFE global)
+ lobe-prototype-runtime.css
+ vendored babel.min.js
per prototype (no build): single HTML = <link css> + <script runtime> + babel-standalone
+ <script type="text/babel"> with production-style React
- The runtime bundles
react,react-dom/client, the full@lobehub/uinamespace,lucide-reactandmotionfrom the repo's ownnode_modules— so tokens/components/ versions match production, and react/emotion/theme-scope are singletons by construction. entry.mjsalso imports@lobehub/ui/theme.css+global.css+style.css(the same three the app loads insrc/layout/GlobalProvider/AppTheme.tsx); esbuild emits them aslobe-prototype-runtime.css, linked from the template's<head>.- A 3-line
window.requireshim + babel'stransform-modules-commonjslets the prototype keep realimportstatements. - Theming is CSS attributes, not a provider: the template's head script calls
setLobeTheme({ appearance })fromprefers-color-scheme(and on change), so light/dark follow the OS before first paint. Force a theme withsetLobeTheme({ appearance: 'dark' }), or<ThemeScope appearance="dark">for an island. The React tree is wrapped in<ConfigProvider motion={motion}>like the app'sAppTheme(motion forButton,Drawer,Modal, …, plus fonts, focus ring and the popup portal host) and<ToastHost />fortoast.
⚠️ Don't re-attempt the pure-CDN route (esm.sh import maps). It was tried and rejected: esm.sh's CJS named-export analysis failed serially across the dep chain and
?bundleleaked an unversionedreact-is404. Bundling from the repo's ownnode_modulesis the only way to match production versions exactly.
Quickstart
-
Build the runtime into the prototype's directory (any dir works;
/tmpis fine):bash .agents/skills/design-prototype/scripts/build-runtime.sh /tmp/my-protoSkip if
lobe-prototype-runtime.{js,css}+babel.min.jsare already there and the design-system versions haven't bumped. The first run downloadsbabel.min.jsfrom unpkg (pinned); later runs reuse it. -
Copy
references/template.htmlinto the same dir, rename, and replace the sampleAppwith the real surface. Keep the runtime shim block untouched. -
Open it (
open /tmp/my-proto/xxx.html) — no server, no build.
Verify before delivering
Headless-check it renders with zero console errors (the repo's e2e Playwright works):
// node /tmp/check.mjs — adjust paths
import { chromium } from '<repo>/e2e/node_modules/playwright/index.mjs';
const b = await chromium.launch();
const p = await b.newPage();
const errs = [];
p.on('pageerror', (e) => errs.push(String(e)));
await p.goto('file:///tmp/my-proto/xxx.html');
await p.waitForSelector('#root *', { timeout: 30000 });
await p.screenshot({ path: '/tmp/proto.png' }); // Read the screenshot yourself
console.log('errors:', errs);
await b.close();
Then Read the screenshot — a prototype is a visual deliverable; don't ship it
sight-unseen. Check light + dark (emulate prefers-color-scheme) and a narrow
viewport if the surface has a mobile story.
What's in the runtime (extend freely)
See assets/entry.mjs — the single source of truth. Currently:
@lobehub/ui: full namespace — every component (Button,Input,Modal,createModal,confirmModal,Table,Switch,Checkbox,Radio,Badge,Progress,Slider,Steps,Divider,DropdownMenu,Accordion,Collapsible,Spin,Skeleton,DraggablePanel,FloatingSheet, …) plustoast/ToastHost,MotionProvider,setLobeTheme,ThemeScope,useThemeMode,useTheme, and the styling APIcreateStaticStyles,cssVar,cx,css,keyframes,responsive,useResponsive@lobehub/icons: Amp, ClaudeCode, Codex, HermesAgent, OpenClaw, OpenCodelucide-react,motion/react,motion/react-m,react,react/jsx-runtime,react-dom,react-dom/client: full namespaces
Not included: @lobehub/ui/form, @lobehub/ui/pro-table, @lobehub/ui/chat and the
other subpaths — add import * as x from '@lobehub/ui/<sub>' + a '@lobehub/ui/<sub>': x
entry and rebuild (~2s). Size ≈ 17MB JS + 0.3MB CSS (fine from disk).
Picking the v6 equivalent
- Feedback:
toast.success(…)+<ToastHost />; dialogs:Modal,createModal,confirmModal. - Menus:
DropdownMenu items={…}(opens on click); context menus:ContextMenuTrigger. - Layout:
Flexbox horizontal gap={…}/Center/Grid— there is noSpace. - Disclosure:
Accordionfor several sections,Collapsiblefor one region. Badge,Checkbox,Divider,Progress,Radio,Slider,Steps,Switch,Table(columns/dataSource/rowSelection) are root exports.- Unsure about a name → check
node_modules/@lobehub/ui/es/index.d.mtsrather than guessing; an unknown import isundefinedat runtime, not a build error.
Pitfalls
- Style with
createStaticStyles+cssVar.*exactly as production does — it is emotion-backed and inserts at call time, so it needs no build step. Tokens resolve tovar(--lobe-*)and flip with the theme; never hardcode light/dark colors. - Component styles live in cascade layers (
lobe-base,lobe-popup,lobe-ui), so any unlayered CSS in the prototype's<style>beats them. Override throughclassName/style/classNames/stylesslot props, not global selectors likebutton { … }. - Keep the
<link>tolobe-prototype-runtime.css— without it components render unstyled and--lobe-*tokens are undefined. - babel must stay the raw UMD file (unpkg/jsdelivr). esm.sh rewrites it to ESM
and a classic
<script src>chokes onexport. - The require shim throws with the module name when an import isn't in the runtime — that's the "add to entry.mjs and rebuild" signal, not a template bug.
Text typeaccepts onlysecondary|success|warning|danger|info;Tag colorhas noprimary(DESIGN.md "Applying tokens in components").
Design bar (same as production surfaces)
A prototype is a surface — the ux checklists apply to what it demonstrates:
- Show at least one non-happy-path state (empty / loading / error / in-progress), ideally behind a state-toggle strip like the template's. A happy-path-only prototype under-specifies the design and silently blesses missing states.
- Reuse the app's surface contracts: side panels are
DraggablePanel(collapse + drag-resize come free), loading follows the ux skill's feedback §4.1 scenario table (Skeleton/Spin,variant="network"only for AI work), modals viacreateModal-style flows. - Don't paint affordances you don't wire (
cursor: zoom-inwith no zoom, keycap chips with no keys) — in an interactive prototype a dead affordance is a spec bug. - Walk the ux Quick review against the prototype before delivering; annotate anything deliberately out of scope in an HTML comment so the implementer knows it's a cut, not a decision.
Version History
-
aace12f
Current 2026-10-20 06:05
从 @lobehub/ui 5.x + antd 迁移至 6.x,移除 antd 依赖,更新主题系统。
- 69012b8 2026-09-28 20:13
- e0d4b61 2026-09-23 09:52
- 29fe043 2026-08-20 18:33


