UI playbook — ProComponents
Also served to coding agents as MCP framework_docs("pro"). Read before building screens. Sources: the official demos of @ant-design/pro-components@2.8.10 (exactly our runtime) and the page compositions of ant-design-pro (src/pages/*), adapted to Manh (antd v6, react-router, no umi/less/CSS). Every pattern below runs on manh.run — reference app: the Duyệt mua sắm template (templates/purchase-requests/, live at duyet-mua-sam.manh.run).
Why apps look “plastic”: hand-rolled shells (Layout + Menu), plain Card + Statistic dashboards, Table with no search/tabs/badges, forms in bare Modals, every screen the same grey box. The fix is not more CSS — it is using the Pro component that already encodes the polished pattern, with its semantic props filled in.
Every pattern has a runnable example: open it in the Playground, edit, copy the code or a ready-made prompt for your agent.
Rules (apply to every screen)
- Pick by job (decision table):
- app shell →
ProLayout· page header →PageContainer - list of records →
ProTable(columns withvalueType/valueEnum) · cards/feed →ProList(metas) - KPIs →
StatisticCard.Group· panels/grids →ProCard(split,gutter,colSpan,ghost) - read-only record →
ProDescriptions(reuse the table columns) - form on a page →
ProForm(+FooterToolbar) · in overlay →ModalForm/DrawerForm· wizard →StepsForm - filters above a list → ProTable
search(orQueryFilter) · compact pills →LightFilterintoolbar.filter - login/register →
LoginForm· empty/success/error →Result
- app shell →
- Fill the semantic props — they are what makes it look finished:
title/subTitle/tooltip,headerBordered,cardBordered,extra,content/extraContent,valueEnumwithstatus,valueType="money"|"dateTime"|"percent",showTotal, Badge counts on tabs,footertoolbars. - One rhythm: 24px between sections (
ProCard direction="column" ghost gutter={[0, 16|24]}), 16px inside dense rows. Sections sit oncolorBgLayoutgrey; content in white cards. No extra margins. - Color only from tokens — pick a preset in
app/layout.tsx(PRESETS/BRAND/DARKin theauthstarter), never invent hex colors (code.hardcoded-colorwarns); read colors withtheme.useToken(); brand viaConfigProvider theme.token(setcolorPrimaryandcolorPrimaryBg/colorInfoBgexplicitly if the generated light shade looks muddy). ProLayout brand via itstoken={{ header, sider, pageContainer }}. - Status = Badge semantics everywhere (table, list, detail):
{ text, status: "Success" | "Error" | "Processing" | "Warning" | "Default" }. - Never
onBackonPageContainer/PageHeaderin this runtime (crashes, React #130 — gatecode.no-pagecontainer-onback); useheader.breadcrumb.
Polish checklist (run before deploy)
- Shell is ProLayout; menu per role; selected item highlights on detail routes.
- Every list: search fields chosen deliberately, status as Badge, money right-aligned and formatted, tab counts for the main status, empty text that says what to do.
- Every dashboard number has a secondary line; the role’s to-do is first.
- Detail page: summary in header, key number in
extraContent, progressSteps, historyTimeline, decisions infooter. - Forms in Drawer/Modal with
submitTextin the user’s language, tooltips on non-obvious fields,ProFormMoney/DatePickerwith VN formats. - No hard-coded colors; brand +
colorPrimaryBgset once inConfigProvider. - Walk each role’s main flow against the deployed API (a token per role), confirm DB effects with
db_queryandlogs, then runscreenshot({ path, audit: true })on each main screen (phone + desktop score: blank page, console errors, sideways scroll, broken images, icons standing in for photos, tap targets, contrast, placeholder copy, SEO head) and fix until clean — then ask the user to look at the deployed URL against Page recipes.