@mofei-dev/ui
v1.0.3
Published
Shared React UI components for Mofei projects. The package focuses on the Mofei Life visual system: dark glass surfaces, bilingual navigation, blog listing and article layouts, comment/message primitives, admin workspace screens, and reusable form/status
Readme
@mofei-dev/ui
Shared React UI components for Mofei projects. The package focuses on the Mofei Life visual system: dark glass surfaces, bilingual navigation, blog listing and article layouts, comment/message primitives, admin workspace screens, and reusable form/status controls.
The package ships compiled JavaScript and type declarations in dist. React
and Next are peer dependencies, so each consuming app owns its runtime versions.
Installation
pnpm add @mofei-dev/uiImport from the package root:
import { AppBackground, StickyTopNav, BlogArticleLayout } from "@mofei-dev/ui";
import type { StickyTopNavProps } from "@mofei-dev/ui";For the complete root export list, see docs/API.md.
Tailwind CSS
Components use Tailwind utility classes in the distributed files. Tailwind CSS
v4 consumers must include this package's dist directory as a source so
responsive, state, and arbitrary-value classes are generated.
@import "tailwindcss";
@source "../node_modules/@mofei-dev/ui/dist";Adjust the relative path from your stylesheet location. A Next app with
src/app/globals.css commonly needs:
@source "../../node_modules/@mofei-dev/ui/dist";Without this source entry, components such as StickyTopNav can render mobile
states on desktop because classes like lg:hidden and lg:flex are not
emitted.
The same rule applies to admin/workspace components. If a consuming app shows mostly unstyled borders, table outlines, or "wireframe" surfaces after a production build, first check that its global Tailwind stylesheet includes the package source path and that the path is correct relative to that stylesheet. For example:
/* apps/admin/src/app/globals.css */
@import "tailwindcss";
@source "../../node_modules/@mofei-dev/ui/dist";After adding or changing the source entry, rebuild the consuming app before
deploying. Deploy-only commands that reuse an old .open-next directory can
publish stale CSS and make the production site appear to roll back.
App Shell
Use AppBackground inside a positioned isolated shell. The component renders
fixed layers with negative z-index, so the host shell must create the stacking
context.
import Link from "next/link";
import {
AppBackground,
FooterPreview,
LogoMark,
StickyTopNav,
} from "@mofei-dev/ui";
export function AppShell({ children }: { children: React.ReactNode }) {
return (
<div className="relative isolate min-h-screen overflow-x-clip bg-[#0b1120] text-white">
<AppBackground />
<StickyTopNav
lang="en"
LinkComponent={Link}
brand={<LogoMark alt="Mofei" />}
items={[
{ id: "blog", zh: "博客", en: "Blog", href: "/blog" },
{ id: "about", zh: "关于", en: "About", href: "/about" },
]}
activeId="blog"
languageLinks={{ zh: "/zh/blog", en: "/en/blog" }}
className="fixed left-0 right-0 top-0"
innerClassName="border-b border-white/[0.08] bg-white/[0.02] backdrop-blur-xl"
/>
<main className="pt-24">{children}</main>
<FooterPreview lang="en" />
</div>
);
}Keep the document or body background close to #0b1120 so the page does not
flash a different color before the app shell paints.
Blog Listing
Use the blog/listing components together for index pages, filtered archives, and collection pages.
import {
BlogListHero,
CategoryFilterNav,
BlogListContentSection,
BlogListEmptyState,
ResponsivePagination,
} from "@mofei-dev/ui";
export function BlogIndex({ posts, activeCategory, setCategory }) {
return (
<>
<BlogListHero
lang="en"
title="Mofei Life"
description="Notes on family, migration, engineering, and AI workflows."
/>
<CategoryFilterNav
lang="en"
activeId={activeCategory}
onChange={setCategory}
items={[
{ id: "all", label: { zh: "全部", en: "All" }, count: posts.length },
{ id: "tech", label: { zh: "技术", en: "Tech" } },
]}
/>
<BlogListContentSection
lang="en"
cards={posts}
emptyState={<BlogListEmptyState lang="en" kind="empty" />}
/>
<ResponsivePagination
lang="en"
currentPage={1}
totalPages={5}
getPageHref={(page) => `/blog/page/${page}`}
/>
</>
);
}For simpler lists, BlogCardList wraps BlogListContentSection. Use
BlogCollectionList for grouped collections, ActivityFeedList for timelines,
FriendLinkCardList for friend links, and LinkList or LinkGroup for compact
link groups.
Article Page
BlogArticleLayout composes the article title, metadata, summary, table of
contents, action area, and reading column. The smaller article components can
also be used independently.
import {
ArticleCodeBlock,
ArticleMetaBar,
ArticleProse,
ArticleShareBar,
ArticleSummaryCard,
ArticleTocCard,
BlogArticleLayout,
PrevNextNav,
} from "@mofei-dev/ui";
const code = `pnpm add @mofei-dev/ui`;
export function ArticlePage() {
const toc = (
<ArticleTocCard
eyebrow="Contents"
title="On this page"
items={[
{ id: "setup", label: "Setup", href: "#setup" },
{ id: "usage", label: "Usage", href: "#usage" },
]}
/>
);
return (
<BlogArticleLayout
lang="en"
title="Building with the Mofei UI package"
metaBar={<ArticleMetaBar label="Published" trailing="2026-04-26" />}
summary={<ArticleSummaryCard label="Summary">Reusable UI for Mofei apps.</ArticleSummaryCard>}
toc={toc}
actions={
<ArticleShareBar
mode="inline"
preset={{
lang: "en",
canonicalUrl: "https://www.mofei.life/blog/ui",
shareTitle: "Building with the Mofei UI package",
}}
/>
}
>
<ArticleProse>
<h2 id="setup">Setup</h2>
<p>Install the package and include its Tailwind source.</p>
<ArticleCodeBlock code={code} language="bash">
<pre><code>{code}</code></pre>
</ArticleCodeBlock>
</ArticleProse>
<PrevNextNav
lang="en"
prev={{ title: "Previous article", href: "/blog/previous" }}
next={{ title: "Next article", href: "/blog/next" }}
/>
</BlogArticleLayout>
);
}Forms And Messaging
Form controls are styled but mostly unopinionated. MessageComposer and
AiChatDockPanel are higher-level pieces for comment and AI assistant
experiences.
import {
AiChatDockPanel,
AiChatDockTrigger,
ColorInput,
FormPanel,
FormPanelHeader,
MessageComposer,
SelectInput,
TextInput,
UploadDropzone,
UploadQueueList,
} from "@mofei-dev/ui";
export function EditorTools({ open, setOpen }) {
return (
<>
<FormPanel>
<FormPanelHeader title="Asset settings" description="Prepare upload metadata." />
<TextInput placeholder="Title" />
<SelectInput defaultValue="cover">
<option value="cover">Cover</option>
<option value="gallery">Gallery</option>
</SelectInput>
<ColorInput value="#67e8f9" onChange={() => {}} />
<UploadDropzone
title="Drop files here"
description="Release to upload assets."
isDragging={false}
onDrop={() => {}}
onDragOver={(event) => event.preventDefault()}
onDragLeave={() => {}}
/>
<UploadQueueList items={[]} emptyState="No uploads yet" />
</FormPanel>
<AiChatDockTrigger isOpen={open} onToggle={() => setOpen(!open)} unreadCount={2} />
{open ? (
<AiChatDockPanel title="AI assistant" subtitle="Draft support" onClose={() => setOpen(false)}>
<MessageComposer
copy={{
title: "Leave a message",
submitLabel: "Send",
secondaryLabel: "Clear",
messagePlaceholder: "Write here...",
}}
onSubmit={(payload) => console.log(payload)}
/>
</AiChatDockPanel>
) : null}
</>
);
}Comments, Status, And Workspace UI
import {
AITransparencyBadge,
CommentList,
LoadingPanel,
MetricGrid,
NoticeBanner,
SectionHeader,
StatusToastStack,
ThreadedCommentList,
WorkspaceTabs,
} from "@mofei-dev/ui";
export function FeedbackPanel() {
return (
<section>
<SectionHeader title="Comments" meta={<AITransparencyBadge context="comment" mode="assisted" lang="en" />} />
<NoticeBanner tone="info">Replies may include AI-assisted summaries.</NoticeBanner>
<WorkspaceTabs
activeKey="comments"
onChange={() => {}}
items={[
{ key: "comments", label: "Comments" },
{ key: "metrics", label: "Metrics" },
]}
/>
<MetricGrid>
<LoadingPanel label="Loading metrics..." />
</MetricGrid>
<CommentList lang="en" comments={[]} />
<ThreadedCommentList lang="en" comments={[]} />
<StatusToastStack items={[]} />
</section>
);
}Admin UI
Admin components are for dense workspace screens with shared dark surfaces.
import {
AdminShell,
DataTable,
FeatureCard,
FilterBar,
FilterField,
Modal,
PageHeader,
PrimaryButton,
StatusBadge,
} from "@mofei-dev/ui";
type Row = { id: string; title: string; status: "draft" | "published" };
export function AdminPage({ rows }: { rows: Row[] }) {
return (
<AdminShell
title="Blog Admin"
activeNavKey="posts"
navItems={[{ key: "posts", label: "Posts", href: "/admin/posts" }]}
>
<PageHeader
title="Posts"
description="Review drafts and published articles."
actions={<PrimaryButton>New post</PrimaryButton>}
/>
<FilterBar onSubmit={(event) => event.preventDefault()}>
<FilterField label="Search"><input name="q" /></FilterField>
</FilterBar>
<FeatureCard title="Publishing queue" description="Items waiting for review." href="/admin/posts" />
<DataTable
rows={rows}
rowKey={(row) => row.id}
columns={[
{ key: "title", header: "Title", render: (row) => row.title },
{ key: "status", header: "Status", render: (row) => <StatusBadge label={row.status} tone="info" /> },
]}
/>
<Modal open={false} title="Confirm" onClose={() => {}}>Modal content</Modal>
</AdminShell>
);
}Component Catalog
Navigation And Pagination
| Export | Use |
| --- | --- |
| StickyTopNav | Sticky responsive top nav with desktop links, mobile drawer, brand slot, active state, and language links. |
| SiteGlobalNav | Desktop global nav used by StickyTopNav; useful when the shell owns its own header. |
| PageSectionNav | Section anchor navigation for long pages. |
| CategoryFilterNav | Horizontal category chips with counts, active state, and scroll fades. |
| PrevNextNav | Previous/next article cards. |
| HeaderNavBar | Compact header navigation bar. |
| BlogEntryTabs | Blog entry tab switcher. |
| ResponsivePagination | Pagination links with responsive page ranges. |
Buttons
| Export | Use |
| --- | --- |
| PrimaryButton | Main command button. |
| SecondaryButton | Secondary command button. |
| TextButton | Low-emphasis text command. |
| PrimaryPillButton | Pill-shaped button for compact actions. |
| PrimaryPillLink | Pill-shaped anchor link. |
Content Shells And Heroes
| Export | Use |
| --- | --- |
| AppBackground | Fixed or absolute Mofei background layers. |
| ContentBackground / BlogListBackground | Content-page background layer alias. |
| ContentPageShell / BlogListPageShell | Page shell with background-aware layout. |
| SecondaryHero | Secondary page hero with bilingual/text slots. |
| SecondaryHeroPageShell | Shell that combines secondary hero and page content. |
| BlogListHero | Blog index hero. |
| MessageHeroVisual | Visual element for message/contact pages. |
Blog Lists
| Export | Use |
| --- | --- |
| BlogListContentSection | Full featured blog listing section with filters, cards, empty states, and layout controls. |
| BlogCardList | Compatibility wrapper around BlogListContentSection. |
| BlogCard | Individual blog card renderer. |
| BlogCollectionList | Grouped blog collections. |
| BlogListEmptyState | Empty-state copy for blog filters/searches. |
| LinkList | Simple list of links. |
| LinkGroup | Titled grouped links. |
| ActivityFeedList | Timeline/feed list. |
| FriendLinkCardList | Friend link cards. |
Article
| Export | Use |
| --- | --- |
| BlogArticleLayout | Article title, metadata, TOC rail, reading column, summary, and actions layout. |
| ArticleMetaBar | Small metadata row. |
| ArticleActionBar | Action panel/row for article-level commands. |
| ArticleTocCard | Table-of-contents card. |
| ArticleSummaryCard | Summary callout. |
| ArticleProse | Prose wrapper for article body content. |
| ArticleCodeBlock | Copyable code block shell. |
| ArticleShareBar | Share buttons, inline/panel/collapsed modes. |
| buildArticleShareItems | Helper that builds localized share item URLs from a canonical URL and title. |
Forms, Uploads, And Messaging
| Export | Use |
| --- | --- |
| TextInput | Styled text input. |
| SelectInput | Styled select with custom adornment. |
| TextAreaInput | Styled textarea. |
| CheckboxInput | Styled checkbox with optional label. |
| ReadonlyField | Read-only field display. |
| CheckboxFieldRow | Label/description checkbox row. |
| ColorInput | Color value input with swatch treatment. |
| FormPanel | Surface for grouped controls. |
| FormPanelHeader | Title/description/action header for FormPanel. |
| InlineSearchBar | Compact inline search input. |
| UploadDropzone | Drag/drop upload target. |
| UploadQueueList | Upload progress/list display. |
| AssetList | Uploaded asset list. |
| SubscribeForm | Email subscription form. |
| MessageComposer | Identity-aware message/comment composer with optional chat messages and status toasts. |
| AiChatDockPanel | Fixed or embedded chat panel. |
| AiChatDockTrigger | Floating trigger button with unread badge. |
Comments And Message Threads
| Export | Use |
| --- | --- |
| CommentList | Flat comment list. |
| ThreadedCommentList | Nested/threaded comment list. |
| MessageThreadList | Rich message thread renderer. |
| MessageThreadRichText | Sanitized/rich text block used in message threads. |
| MessageThreadReplyQuote | Reply quote block. |
| MessageThreadAiReply | AI reply block. |
| MessageThreadActionBar | Action row for message threads. |
| CommentSectionTitle | Comment section heading primitive. |
| CommentAvatar | Comment avatar primitive. |
| CommentAuthor | Comment author link/text primitive. |
| CommentMetaCluster | Metadata group primitive. |
| CommentActionButton | Comment action button primitive. |
| CommentReplyQuote | Comment quote primitive. |
| CommentAiReply | Comment AI-reply primitive. |
Status And Feedback
| Export | Use |
| --- | --- |
| StatusToast | Single toast item renderer. |
| StatusToastStack | Toast stack. |
| NoticeBanner | Inline info/success/warning/danger banner. |
| LoadingPanel | Loading state panel. |
| AITransparencyBadge | Localized AI disclosure badge for articles, comments, search, translation, ranking, and generated/assisted content. |
Layout, Surfaces, And Text
| Export | Use |
| --- | --- |
| GlassPanel | Glass surface wrapper. |
| SectionFrame | Framed section wrapper. |
| SectionHeading | Section heading block. |
| PreviewBody | Preview-page body wrapper. |
| PreviewGroupDivider | Divider for preview groups. |
| PreviewIntroShell | Intro shell for preview pages. |
| PreviewSectionShell | Section shell for preview pages. |
| LogoMark | Mofei logo image helper. |
| InlineNote | Inline note/callout. |
| InfoMetricCard | Small metric card. |
| FooterPreview | Site footer preview with localized content. |
| ProgressBar | Determinate or loading progress bar. |
| CopyToken | Monospace copy/value token. |
| Eyebrow | Small eyebrow label. |
| SectionLabel | Section label text primitive. |
| MetaLabel | Metadata label text primitive. |
Workspace
| Export | Use |
| --- | --- |
| MetricCard | Single workspace metric. |
| MetricGrid | Responsive metric grid. |
| SectionHeader | Workspace section header with meta/actions. |
| WorkspaceTabs | Controlled workspace tabs. |
Admin
| Export | Use |
| --- | --- |
| AdminShell | Full admin shell with background, nav, title, logout action, and content area. |
| PageHeader | Admin page title, description, actions, and metrics. |
| FilterBar | Filter form wrapper. |
| FilterField | Labeled filter field. |
| Modal | Admin modal shell. |
| StatusBadge | Admin status badge. |
| DataTable | Generic typed data table. |
| FeatureCard | Admin feature/summary card. |
Design Tokens And Helpers
The package also exports class-name tokens for consumers that need to build custom surfaces while staying visually aligned:
- Typography:
typeDisplay,typeHeadline,typeTitleLarge,typeTitleMedium,typeTitleSmall,typeBodyLarge,typeBodyMedium,typeBodySmall,typeCaption,typeLabel,typeEyebrow,typeSystemLabel,typeScale - Surface colors and borders:
contentPageBg,appBackgroundBg,surfaceTextPrimary,surfaceTextStrong,surfaceTextSecondary,surfaceTextBody,surfaceTextSupport,surfaceTextMeta,surfaceTextFaint,surfaceTextGhost,surfaceBgShell,surfaceBgShellSoft,surfaceBgShellMuted,surfaceBgShellLifted,surfaceBgShellSubtle,accentTextPrimary,accentTextSecondary,accentBorderSubtle,accentBorderStrong,accentBgSoft,accentBgMuted,accentGradientBar,borderDefault,borderStrong,borderCyanSubtle,borderCyanStrong - Shape and motion:
radiusXl,radiusCard,radiusPanel,radiusComponent,radiusToast,radiusStandard,radiusSmall,radiusInput,radiusPill,shadowNav,shadowGlassPanel,shadowSectionShell,shadowModal,shadowInputInset,blurNavHover,blurGlassPanel,blurDrawer,spacingScaleTokens,radiusTokens,shadowTokens,blurTokens
Types
Every exported component with custom props also exports its prop type from the package root. The most frequently used types are grouped below.
Navigation and pagination:
StickyTopNavProps, CategoryFilterNavProps, CategoryFilterNavItem,
PrevNextNavItem, HeaderNavBarProps, BlogEntryTabsProps
Blog lists:
BlogCardItem, BlogCardLayout, BlogCardLinkComponent, BlogCardTag,
BlogCardTagVariant, BlogCardBrand, BlogCardImageMeta,
BlogCardImageTransform, BlogFilterItem, BlogRealCard, BlogPreviewCard,
BlogCollectionListItem, BlogCollectionListProps, BlogCardImageComponent,
BlogCardActionsRenderProps, BlogCardActionsRenderer, LinkListItem,
LinkGroupItem, LinkGroupProps, ActivityFeedItem,
ActivityFeedListProps, FriendLinkListItem
Article:
BlogArticleLayoutProps, ArticleMetaBarProps, ArticleActionBarProps,
ArticleTocCardProps, ArticleTocCardItem, ArticleSummaryCardProps,
ArticleProseProps, ArticleCodeBlockProps, ArticleShareBarProps,
ArticleShareBarPreset, ArticleShareItem, ArticleSharePlatform
Forms and messaging:
MessageComposerProps, MessageComposerCopy, MessageComposerMessage,
MessageComposerMessageAction, MessageComposerIdentity,
AiChatDockPanelProps, AiChatDockTriggerProps, TextInputProps,
SelectInputProps, TextAreaInputProps, CheckboxInputProps,
ReadonlyFieldProps, CheckboxFieldRowProps, ColorInputProps,
FormPanelProps, FormPanelHeaderProps, UploadDropzoneProps,
UploadQueueItem, UploadQueueListProps, AssetListItem, AssetListProps,
SubscribeFormProps, InlineSearchBarProps
Comments and threads:
CommentItem, ThreadedCommentItem, ThreadedCommentAction,
MessageThreadItem, MessageThreadAction, MessageThreadListProps,
MessageThreadRichTextProps, MessageThreadReplyQuoteProps,
MessageThreadAiReplyProps, MessageThreadActionBarProps
Status and workspace:
StatusToastItem, StatusToastStackProps, NoticeBannerProps,
NoticeBannerTone, LoadingPanelProps, AITransparencyBadgeProps,
AITransparencyContext, AITransparencyMode, AITransparencyLanguage,
MetricCardProps, MetricGridProps, SectionHeaderProps,
WorkspaceTabsProps, WorkspaceTabItem
Layout and text:
ContentBackgroundProps, BlogListBackgroundProps, BlogListHeroProps,
SecondaryHeroProps, SecondaryHeroPageShellProps, ContentPageShellProps,
BlogListPageShellProps, PreviewBodyProps, PreviewGroupDividerProps,
PreviewIntroShellProps, PreviewSectionShellProps, LogoMarkProps,
InlineNoteProps, InfoMetricCardProps, AppBackgroundProps,
ProgressBarProps, CopyTokenProps, EyebrowProps, SectionLabelProps,
MetaLabelProps
Admin:
ShellProps, ShellNavItem, AdminNavItem, PageHeaderProps,
FilterBarProps, FilterFieldProps, ModalProps, StatusBadgeProps,
StatusTone, DataTableProps, DataTableColumn, FeatureCardProps
Development
pnpm install
pnpm typecheck
pnpm testBefore publishing, run the consumer fallback guard:
pnpm typecheck
pnpm test:consumer-fallback
pnpm build:libReleases
This repository publishes to the public npm registry through GitHub Actions and
npm Trusted Publishing. It does not require a long-lived NPM_TOKEN.
Release workflow:
- Workflow file:
.github/workflows/npm-release.yml - npm Trusted Publisher workflow filename:
npm-release.yml - GitHub Actions environment:
npm-publish - Package:
@mofei-dev/ui - Registry:
https://registry.npmjs.org
Configure npm Trusted Publishing for zmofei/mofei-life-ui before using the
workflow:
- Workflow filename:
npm-release.yml - GitHub Actions environment:
npm-publish
Release Dev
Use a dev release when another project needs to test the current main branch
without moving the stable latest tag.
From GitHub:
- Open Actions.
- Select
NPM Release. - Click
Run workflow. - Choose
channel: dev. - Run from
main.
From the CLI:
gh workflow run npm-release.yml --ref main -f channel=devThe workflow creates a temporary prerelease version without committing it back to the repo:
<package.json version>-dev.<github run number>.<short sha>Example:
0.1.2-dev.2.a85355fInstall the latest dev build in a consumer app:
pnpm add @mofei-dev/ui@devInstall an exact dev build:
pnpm add @mofei-dev/[email protected]Release Production
Use a production release when the package is ready to become the npm latest
version.
- Start from a clean
mainbranch. - Bump
package.jsonto a stable semver version. Do not include a prerelease suffix such as-devor-beta. - Run the publish guard locally:
pnpm typecheck
pnpm test:consumer-fallback
pnpm build:lib- Commit and push the version bump:
git add package.json
git commit -m "Release v0.1.3"
git push origin main- Create and push a matching tag:
git tag v0.1.3
git push origin v0.1.3The production workflow verifies that v0.1.3 matches
package.json version 0.1.3, runs the same publish guard, publishes to npm
with the latest dist-tag, and creates a GitHub Release for the tag.
Manual production dispatch is available from Actions with channel:
production, but tag-based release is preferred because it gives the published
package a durable GitHub Release and an auditable version ref.
