This skill turns "writing UI with lobe-ui" from a matter of luck into something controllable: it makes component semantics, visual values, craft standards, and acceptance mechanisms explicit, intervening at every step of generation rather than trying to patch things up at the end with a single prompt.
The model has seen far more interfaces than any designer. The problem is not a lack of capability, but that when requirements are vague it converges on the most frequent, safest kind of result in its training data—uniform sans-serif fonts, purple-blue gradients, steady equal-sized cards. Generic, safe, and that is precisely the root of mediocrity. Every step below fights against this tendency.
Division of labor with other skills
| Focus | Ownership |
|---|---|
| What this interface should be and why | Upstream product design judgment, outside the scope of this skill |
| Which component to use, which token to take, whether it counts as qualified | This skill |
| How the floors of a marketing landing page should be arranged | building-landing-home |
| Evidence that it actually runs in the browser | This repository's local-testing skill |
Choose the minimal run mode
Don't read all six files every time. First decide what this delivery is:
| Mode | Endpoint | Needs to read |
|---|---|---|
| Choose component | Answer "which component should this information use" | Hard prerequisites + components.md |
| Values | Write a segment that conforms to the system's style.ts |
Hard prerequisites + design.md |
| Build interface | A complete page or component, including non-ideal states | The full five-step process + all references |
| Review | Judge whether an existing interface can be approved | evaluator.md + the rules that were hit |
For narrow questions, read only the relevant file, and state the scope of the check.
Hard prerequisites: get these three things right, otherwise it's all wasted work
One: new code enters from @lobehub/ui/base-ui , forms enter separately from @lobehub/ui/base-ui/form . The top-level component of the same name is the antd wrapper layer, and has been marked deprecated by @lobehub/ui/eslint . Components in antd that already have a base-ui corresponding implementation are likewise banned, including Table、Badge、Progress、Spin、Result、Empty、Card、Typography。Form Do not import from @lobehub/ui or base-ui barrel—the one in the barrel is the soon-to-be-deleted uncontrolled implementation. For the complete list, see components.md C-01.
Two: styles only write createStaticStyles + cssVar。 createStyles has been disabled by eslint. A component's style.ts is always written this way:
import { createStaticStyles } from 'antd-style';
export const styles = createStaticStyles(({ css, cssVar, responsive }) => ({
root: css`
border-block-end: 1px solid ${cssVar.colorBorderSecondary};
border-radius: ${cssVar.borderRadiusLG};
${responsive.sm} {
padding-inline: 12px;
}
`,
}));
Styles written with cssVar natively support dark mode and require no branching whatsoever.
Three: the Provider order cannot be reversed, and motion must be passed explicitly.
import { ConfigProvider, ThemeProvider } from '@lobehub/ui';
import { zhCn } from '@lobehub/ui/i18n';
import { motion } from 'motion/react';
<ConfigProvider locale="zh-CN" motion={motion} resources={zhCn}>
<ThemeProvider>
<App />
</ThemeProvider>
</ConfigProvider>;
ConfigProvider must be on the outer layer:ThemeProvider renders antd's App(carrying static notification / modal holders) and reads the CDN config to load webfonts; both depend on the context above.
Three easy pitfalls:
motionis required. Not passing it will make any component that uses animation directly throw atuseMotionComponent(). Apps usingLazyMotionshould passmrather thanmotion。- Component-internal copy (chat actions, forms, EmojiPicker, etc.) is provided by
resources, optionallyenorzhCn。Do not wrap another layer ofI18nProvider—it itself is a forwarding ofConfigProvider(which also requiresmotion), nesting is equivalent to installing two providers. ConfigProviderinstalls a global keyboard focus ring for the entire document, including native controls and controls outside the provider subtree. So under normal circumstances you don't need to write your own focus styles, and you must never useoutline: noneto erase it.
Five-step chain
Don't start by drawing the interface. Each step presupposes the output of the previous step; upstream deviations amplify along the chain, so constraints must enter as early as possible and every step must be checked.
1. Plan — first turn vague requirements into an executable functional definition
Before writing any JSX, expand the requirements into six layers. Whatever part you can't write out is the part that needs to be confirmed with the user.
L1 定位与意图 一句话定义 · 目标用户 · 场景清单 · 非目标 · 行为边界
L2 信息架构 空间区域 · 区域边界规则 · 内容生长规则
L3 核心链路 状态清单 · 主链路 · 分支链路
L4 组件功能 组件定位 · 功能清单 · 默认/悬停/加载/禁用/错误各态
L5 边界条件 空态 · 加载态 · 错误态 · 权限降级
L6 验收标准 Given/When/Then · 完成的定义
The easiest to skip are L5 and L6. Models tend to prioritize completing the main flow and the success state, because that looks most like a complete screenshot; but in real products, users more often encounter loading, no data, no permission, and retry after failure.If L5 isn't written, the page will lose operability at critical moments.
At the same time, set the visual direction. If you don't, the model will first fill in with default visuals, then carry those default choices into the later structure and components.
2. Build the skeleton — decide in what spatial structure the information is laid out
First determine which type of page this is, then choose the skeleton; don't piece together components from a blank page:
| Scenario | Starting point |
|---|---|
| Document / application page shell | Layout + LayoutHeader / LayoutSidebar / LayoutMain / LayoutToc / LayoutFooter |
| Product console | @lobehub/ui/dashboard:ConsoleShell + ConsoleNav + PageHeader + Surface. For metrics, filters, and login boxes, see C-07 |
| Conversation interface | ChatHeader → ChatList → ChatInputArea(for composition order, see C-04) |
| Mobile shell | @lobehub/ui/mobile of ChatHeader / ChatInputArea / TabBar / SafeArea |
| Fixed narrow navigation bar | SideNav; if draggable resizing is needed, use DraggableSideNav |
| Resizable sidebar / floating panel | base-ui of DraggablePanel |
| Marketing landing page | building-landing-home skill. The components are in @lobehub/ui/awesome, don't assemble them on the spot here Hero |
The skeleton determines the relationship between the main area, sidebar, action area, and status area.If this step is wrong, the whole page will be off—if a list page is made into a card wall, or a batch task page has no batch action area, no amount of later component changes can save it.
Also set density along the way: landing pages and consoles cannot use the same density (see craft.md K-09).
3. Fill — map information to the correct component semantics
It's not about filling the slots. "Running," "high risk," "ECS," and "production environment" are all short texts, but they have different responsibilities: one is a status, one is a risk, one is a resource type, and one is a business category. Making them all the same kind of tag looks visually neat, but flattens the semantics, and the user's judgment becomes slower.
First determine whether this piece of information expresses status, category, action, container, or navigation relationship, then choose the component. For details, see components.md —the decision table for looking up by requirement, the real differences between easily confused components, and where to get components that this library doesn't have.
4. Refine — complete states, feedback, and craftsmanship
The main flow running only means it can display a successful result. This step handles non-ideal states and visual craftsmanship:
- Visual value selection, token derivation, dark mode, execution constraints → design.md
- Loading tiers, animation purpose, accessibility baseline, anti-AI flavor → craft.md
- Charts, model badges, emoji, streaming markdown → ecosystem.md
5. Evaluate — take it back and check against the standards
Don't say "it's basically good enough" on your own. Check item by item against the six dimensions, and if any blocking item is hit, loop back. See evaluator.md。
Browser evidence cannot be omitted: existing in the DOM does not equal being visible to the user. The local-testing skill in this repository defines the complete verification process.
Loopback: problems must be traced to a specific rule
When a problem is found, do not change the prompt at the outermost layer and then wait for the next result. Trace it to the stage and rule number using the table below:
| Symptom | Which step to return to | Rule |
|---|---|---|
| Empty state / loading / error / permission state missing | L5 of planning | craft.md K-05 |
| Page structure looks like a wall of cards, not a board | Build the skeleton | Step 2 of this document + K-09 |
| Component looks right but semantics are used incorrectly | Filling | components.md C-03 |
| Used a deprecated top-level component or its antd counterpart | Filling | components.md C-01 |
| Form imported from the wrong entry point, or schema written inside the render | Filling | components.md C-02 |
| Raw hex, raw rgba | Refined visual values | design.md D-01 / D-10 |
| State colors are newly invented | Refined visual values | design.md D-03 |
| Instantly looks like an AI template | Refined craftsmanship | craft.md K-01 |
| Landing page effects were carried into the console | Filling | components.md C-06 |
| Chart colors are disconnected from the theme | Filling ecosystem selection | ecosystem.md E-02 |
| Can't articulate what's wrong | Evaluation | evaluator.md |
Anti-patterns
- From
antdimport components that already have base-ui implementations (Button、Table、Select、Modal、Badge、Spin、Empty、Card、Typography, etc.), or use antd'smessage/notificationinstead of base-ui'stoast。 - From
@lobehub/uior@lobehub/ui/base-uibarrel importForm. Controlled forms are imported only from@lobehub/ui/base-ui/form. - Choosing between top-level and
base-uiby intuition. Most components of the same name at the top level are deprecated antd wrapper layers. - Hand-writing a spinner, using
Icon's spin, or importingthinking-orbs. Choose the waiting indicator by tier per components.md C-02Spin/Skeleton/LoadingDots。 - Overriding lobe-ui / antd theme variables in the app (such as
--ant-color-borderadding!important), or writing your own background, border, and shadow for cards. Use dashboard components oruseSurfaceCardClassName(), and if it's wrong, fix it upstream (see components.md "Console: choose components by scenario"). - Treat examples and templates as copy sheets. The skeleton is a starting point; business fields, content density, and the state list all need to be adjusted to real requirements.
- Where
cssVaris needed, hard-code values. When no corresponding token can be found, explicitly state the gap (design.md D-11), do not quietly write a hex to bypass the system. - Pulling in a second chart library for one chart, or fetching a remote SVG for one model badge.
- Submitting only a screenshot and declaring completion. "Verification" without runtime evidence does not hold.
- Treat
lint/type-check/ passing unit tests as acceptance items. They are preconditions, not delivery evidence.
Reference map
Load by run mode, do not read everything at once:
| Needed | File |
|---|---|
| Component selection decision table, comparison of easily confused components, conversation interface composition | components.md C- |
| Token values, state color derivation, dark mode, execution constraints | design.md D- |
| Craft rules, AI Slop anti-patterns, accessibility, coding pitfalls | craft.md K- |
| APIs for charts / model logos / emoji / streaming markdown | ecosystem.md E- |
| Six-dimension scoring, blockers, evidence requirements, release strategy | evaluator.md |