Patterns
Composed recipes — two or more primitives wired together to solve a recurring UI problem. Each preview below is a live render using @mihcm/ui components. Copy the code, swap the content, ship.
The examples are split into application patterns for product screens and marketing patterns for website surfaces. Marketing patterns should not change the core application system; they reuse the same tokens and primitives with campaign-specific content.
Marketing patterns
Use these for public website, campaign, and product-marketing pages. Keep them conversion-focused, accessible, and fast; do not introduce raw brand colours or decorative-only complexity.
Outcome hero with product proof
A compact SaaS hero for website pages. It pairs a value proposition, two outcome CTAs, and a product-stat preview so the user understands the offer without scrolling.
import { Badge } from '@mihcm/ui/Badge';
import { Button } from '@mihcm/ui/Button';
import { StatCard } from '@mihcm/ui/StatCard';
import { Text } from '@mihcm/ui/Text';
<section className="grid gap-6 rounded-lg border border-border bg-card p-6 lg:grid-cols-2 lg:p-8">
<div className="max-w-xl">
<Badge variant="outline">Human capital platform</Badge>
<Text size="3xl" weight="bold" className="mt-4 block leading-tight">
Run payroll, HR, and workforce operations from one connected system.
</Text>
<Text size="body" tone="muted" className="mt-3 block">
Explain the outcome, not just the module list.
</Text>
<div className="mt-6 flex flex-wrap gap-3">
<Button>Book a demo</Button>
<Button variant="outline">View modules</Button>
</div>
</div>
<div className="rounded-lg border border-border bg-muted p-4">
<div className="grid gap-3 sm:grid-cols-2">
<StatCard tone="primary" value="98%" label="Payroll accuracy" />
<StatCard tone="accent" value="12k" label="Employees" />
<StatCard tone="success" value="42" label="Automations" />
<StatCard tone="neutral" value="4.8" label="CSAT" />
</div>
</div>
</section>- Use one primary outcome CTA and one lower-commitment secondary CTA.
- Keep proof close to the headline; do not hide credibility below the fold.
- Use
StatCardfor real metrics only. Do not invent proof numbers in production.
Proof strip
A reusable credibility row for campaign pages. Use it after a hero or before pricing to reduce doubt without adding a full testimonial section.
import { Card } from '@mihcm/ui/Card';
import { Text } from '@mihcm/ui/Text';
<Card className="p-5">
<div className="grid gap-4 md:grid-cols-2 md:items-center">
<div>
<Text size="lg" weight="bold" className="block">Proof before persuasion</Text>
<Text size="body-sm" tone="muted" className="mt-1 block">
Pair a short credibility line with measurable outcomes.
</Text>
</div>
<div className="grid gap-3 sm:grid-cols-3">
{metrics.map((metric) => (
<div key={metric.label} className="rounded-md border border-border bg-muted p-4 text-center">
<Text size="xl" weight="bold" className="block">{metric.value}</Text>
<Text size="caption" tone="muted" className="mt-1 block uppercase tracking-wide-sm">
{metric.label}
</Text>
</div>
))}
</div>
</div>
</Card>- Prefer three strong metrics over a cluttered logo wall.
- Use neutral copy; proof should clarify, not oversell.
- Keep the layout as a compact strip so it can sit between larger sections.
Pricing comparison
A three-plan comparison block for marketing websites. Use this when the user needs a quick plan signal before contacting sales or starting a trial.
import { Badge } from '@mihcm/ui/Badge';
import { Button } from '@mihcm/ui/Button';
import { Card } from '@mihcm/ui/Card';
import { Text } from '@mihcm/ui/Text';
<div className="grid gap-4 lg:grid-cols-3">
{plans.map((plan) => (
<Card key={plan.name} className={plan.highlight ? 'border-primary shadow-mi-card' : undefined}>
<div className="p-5">
<div className="flex items-start justify-between gap-3">
<div>
<Text size="lg" weight="bold" className="block">{plan.name}</Text>
<Text size="body-sm" tone="muted" className="mt-1 block">{plan.description}</Text>
</div>
<Badge variant={plan.highlight ? 'default' : 'outline'}>{plan.label}</Badge>
</div>
<div className="mt-5 space-y-2 text-sm text-muted-foreground">
{plan.features.map((feature) => <div key={feature}>{feature}</div>)}
</div>
<Button className="mt-5 w-full" variant={plan.highlight ? 'default' : 'outline'}>
Compare plan
</Button>
</div>
</Card>
))}
</div>- Highlight only one recommended plan.
- Keep feature names short enough to scan across columns.
- Use honest plan labels such as
Start,Popular, orScale.
Application patterns
Use these inside authenticated product experiences. They prioritize scanning, comparison, keyboard flow, and clear next actions over campaign storytelling.
Page layout
The full-page scaffold used across MiHCM admin surfaces. PageShell composes three fixed regions (TopBar, Main Sidebar, TitleBar) around a scrolling content area.
import { PageShell } from '@mihcm/ui/PageShell';
import { TopBar, TopBarLogo, TopBarHomeButton, TopBarBackLink, TopBarNotification, TopBarProfile, TopBarDivider, TopBarAction } from '@mihcm/ui/TopBar';
import { MainSidebar } from '@mihcm/ui/MainSidebar';
import { TitleBar } from '@mihcm/ui/TitleBar';
import { Avatar, AvatarFallback, AvatarImage } from '@mihcm/ui/Avatar';
import { Card } from '@mihcm/ui/Card';
import { Button } from '@mihcm/ui/Button';
import { SectionHeader } from '@mihcm/ui/SectionHeader';
import { StatCard } from '@mihcm/ui/StatCard';
import { StatusBadge } from '@mihcm/ui/StatusBadge';
<PageShell
topBar={
<TopBar
start={
<>
<TopBarLogo />
<TopBarHomeButton icon={<HomeIcon />} />
<TopBarBackLink>Back to main</TopBarBackLink>
<TopBarNotification icon={<BellIcon />} count={5} />
</>
}
end={
<>
<TopBarProfile
name="Good Morning Yashi"
subtitle="Edit Profile"
avatar={
<Avatar size="sm">
<AvatarImage src="/people/alex-kim.svg" alt="Yashi" />
<AvatarFallback>YA</AvatarFallback>
</Avatar>
}
/>
<TopBarDivider />
<TopBarAction icon={<HeadphonesIcon />} />
<TopBarAction>Logout</TopBarAction>
</>
}
/>
}
sidebar={
<MainSidebar
items={NAV_ITEMS}
activeKey="security"
onItemSelect={(key) => setActive(key)}
/>
}
titleBar={
<TitleBar
icon={<ShieldIcon />}
supertitle="Security workspace"
title="Permissions cockpit"
action={<Button size="sm">Review access</Button>}
/>
}
>
<div className="grid gap-4 lg:grid-cols-3">
<Card className="overflow-hidden lg:col-span-2">
<div className="border-b border-border p-4">
<SectionHeader
title="Access review"
description="High-risk changes grouped by team, role, and module."
action={<StatusBadge tone="warning">8 pending</StatusBadge>}
/>
</div>
<div className="grid gap-3 p-4 sm:grid-cols-3">
<StatCard tone="primary" value="6" label="Role changes" />
<StatCard tone="warning" value="8" label="Exceptions" />
<StatCard tone="success" value="92%" label="Policy match" />
</div>
</Card>
</div>
</PageShell>- TopBar pins at the top with
shadow-mi-softandh-15on desktop. - Sidebar is
w-11.5(46 px), hidden belowmd, usesbg-primary. - TitleBar is sticky below the TopBar with a
border-b. - Content area gets
bg-mutedand scrolls independently.
Stat row
A grid of StatCard components for at-a-glance metrics. Use three or four cards in a responsive row.
import { StatCard } from '@mihcm/ui/StatCard';
<div className="grid grid-cols-1 gap-3 sm:grid-cols-3">
<StatCard tone="primary" value="6" label="Active Roles" icon={<ShieldIcon />} />
<StatCard tone="accent" value="13,283" label="Total Users" icon={<UsersIcon />} />
<StatCard tone="success" value="16" label="Modules" icon={<GridIcon />} />
</div>- Each card is
text-centerwith an optional 36 px icon tile. - Value is 22 px bold; label is 11 px muted.
- Six tones available:
primary,accent,success,warning,danger,neutral.
Form section
A SectionHeader followed by grouped controls. Used inside role editors and settings panels.
import { SectionHeader } from '@mihcm/ui/SectionHeader';
import { Switch } from '@mihcm/ui/Switch';
import { Label } from '@mihcm/ui/Label';
<SectionHeader title="Notifications" description="Control how you receive alerts" />
<div className="space-y-3">
<div className="flex items-center justify-between">
<Label>Email notifications</Label>
<Switch checked={emailEnabled} onCheckedChange={setEmailEnabled} />
</div>
<div className="flex items-center justify-between">
<Label>Push notifications</Label>
<Switch checked={pushEnabled} onCheckedChange={setPushEnabled} />
</div>
</div>SectionHeaderprovides a 15 px semibold title and optional 12 px muted description.- The
actionslot can hold a count badge or action button. - Controls stack vertically with
space-y-3.
Empty state
The centered placeholder shown when no item is selected. Combines an illustration, a message, and action buttons.
<div className="flex min-h-96 items-center justify-center p-4">
<div className="max-w-md text-center">
<div className="relative w-20 h-20 mx-auto mb-6">
<div className="absolute inset-0 rounded-2xl bg-muted" />
<div className="absolute inset-0 bg-card border-2 border-secondary rounded-2xl flex items-center justify-center">
<ShieldIcon className="w-9 h-9 text-primary" />
</div>
</div>
<Text size="xl" weight="bold" className="block mb-2">Configure Roles</Text>
<Text size="sm" tone="muted" className="block mb-7">
Select a role from the left to edit its access.
</Text>
<div className="flex gap-2.5 justify-center">
<Button variant="outline" size="sm">Create a new role</Button>
<Button variant="outline" size="sm">Lookup an employee</Button>
</div>
</div>
</div>- The icon tile uses semantic background, border, and text tokens.
- Action buttons use
variant="outline"andsize="sm"for a lightweight feel. - Minimum height keeps the state centered on tall viewports.
Split pane
A master-detail layout inside a bordered card. The left panel holds a list; the right panel holds an editor or detail view.
import { Card } from '@mihcm/ui/Card';
import { SearchField } from '@mihcm/ui/SearchField';
<Card className="flex flex-col md:flex-row overflow-hidden flex-1 min-h-0">
{/* Left panel — list */}
<div className="w-full flex-shrink-0 border-b border-secondary md:basis-72 md:border-b-0 md:border-r">
<div className="border-b border-secondary p-3">
<SearchField placeholder="Filter..." />
</div>
<div className="p-2 flex-1 overflow-y-auto">
{items.map(item => <ListItem key={item.id} {...item} />)}
</div>
</div>
{/* Right panel — detail */}
<div className="flex-1 bg-muted overflow-y-auto p-4">
{selectedItem ? <Editor item={selectedItem} /> : <EmptyState />}
</div>
</Card>- On mobile the panels stack vertically (
flex-col); on desktop they sit side-by-side (md:flex-row). - The left panel uses a fixed spacing-scale basis with its own scroll.
- The right panel has
bg-mutedto visually separate it from the list.
Search bar with context
A scoped search bar combining a label, a SearchField, and optional results.
import { SearchField } from '@mihcm/ui/SearchField';
import { Text } from '@mihcm/ui/Text';
import { Card } from '@mihcm/ui/Card';
<Card className="p-4 flex flex-col sm:flex-row items-stretch sm:items-center gap-4">
<div className="flex items-center gap-2 flex-shrink-0">
<UserIcon className="w-5 h-5 text-secondary-foreground" />
<Text size="body-sm" weight="semibold">Employee Lookup</Text>
</div>
<SearchField
placeholder="Search by name, email, or employee number..."
className="flex-1"
/>
</Card>- The card wraps the search in a contained block distinct from page content.
- The icon + label pair identifies the search scope.
- On mobile the label stacks above the field; on desktop they sit inline.
Application command header
A dense product-page header with status, search, secondary commands, and a primary action. Use it above tables, queues, records, or dashboards.
import { Button } from '@mihcm/ui/Button';
import { Card } from '@mihcm/ui/Card';
import { SearchField } from '@mihcm/ui/SearchField';
import { Text } from '@mihcm/ui/Text';
<Card className="p-4">
<div className="flex flex-col gap-4 lg:flex-row lg:items-center lg:justify-between">
<div>
<Text size="lg" weight="bold">Employee records</Text>
<Text size="body-sm" tone="muted" className="mt-1 block">
Operational header for dense application pages.
</Text>
</div>
<div className="flex flex-col gap-2 sm:flex-row">
<SearchField placeholder="Search employees..." className="sm:w-64" />
<Button variant="outline">Export</Button>
<Button>Add employee</Button>
</div>
</div>
</Card>- Use the primary button for the highest-frequency creation action.
- Keep destructive or bulk actions outside the default header state.
- Stack controls on mobile so search remains usable at 375 px.
Review queue
A filterable queue for approvals, exceptions, support tasks, or compliance checks. Filters sit beside the list on desktop and can stack above it on smaller screens.
import { Button } from '@mihcm/ui/Button';
import { Card } from '@mihcm/ui/Card';
import { SectionHeader } from '@mihcm/ui/SectionHeader';
import { Text } from '@mihcm/ui/Text';
<div className="grid gap-4 lg:grid-cols-3">
<Card className="p-4 lg:col-span-1">
<SectionHeader title="Queue filters" description="Narrow the review list." />
<div className="mt-4 space-y-2">
{filters.map((filter) => (
<Button
key={filter.name}
type="button"
variant={filter.active ? 'default' : 'ghost'}
size="sm"
className="w-full justify-between"
>
<span>{filter.name}</span>
<span>{filter.count}</span>
</Button>
))}
</div>
</Card>
<Card className="overflow-hidden lg:col-span-2">
<div className="border-b border-border p-4">
<SectionHeader title="Leave approvals" description="Review exceptions before payroll cutoff." />
</div>
<div className="divide-y divide-border">
{items.map((item) => (
<div key={item.id} className="flex flex-col gap-3 p-4 sm:flex-row sm:items-center sm:justify-between">
<div>
<Text size="body-sm" weight="semibold" className="block">{item.name}</Text>
<Text size="caption" tone="muted" className="mt-1 block">{item.summary}</Text>
</div>
<div className="flex gap-2">
<Button size="sm" variant="outline">Decline</Button>
<Button size="sm">Approve</Button>
</div>
</div>
))}
</div>
</Card>
</div>- Show counts in filters so users understand work volume before selecting a view.
- Keep row-level actions close to the item they affect.
- Use neutral labels for review states; avoid celebratory copy in task queues.
Block-inspired component recipes
These recipes translate dashboard, table, upload, marketing, and account-management block patterns into MiHCM components. Use them as production starting points for component pages, templates, and AI-generated UI reviews.
KPI card row
Use for top-of-page operational metrics with value, delta, and compact trend evidence.
Payroll accuracy
+1.2%99.4%
Open approvals
-18128
Policy match
+6%87%
<div className="grid gap-3 md:grid-cols-3">
<StatCard tone="primary" value="99.4%" label="Payroll accuracy" />
<StatCard tone="warning" value="128" label="Open approvals" />
<SparkChart data={trend} showArea />
</div>Secure file upload panel
Pair Dropzone with validation rules, acceptance hints, and rejection copy.
Upload onboarding evidence
PDF or PNG files. Keep each file under 8 MB.
Choose files
Validation rules
Type, size, count, and server scan.
- Zod validates metadata.
- No file content is rendered.
- Rejections stay visible.
<Dropzone
accept={{ 'application/pdf': ['.pdf'] }}
maxFiles={4}
maxSize={8_000_000}
heading="Upload onboarding evidence"
/>Chart composition card
Combine trend, distribution, and ranked contributors in one scan-friendly analytics block.
Workforce health
Trend, distribution, and ranked contributors.
<Card>
<SparkChart data={trend} showArea showDots />
<CategoryBar data={segments} legendPosition="top" />
<BarList data={rankedData} />
</Card>Persistent filterbar
Use search, filter chips, and explicit apply/clear actions above dense product data.
<div className="flex flex-col gap-3 md:flex-row md:items-center">
<SearchField placeholder="Search employees..." />
<Button variant="outline">Department</Button>
<Button>Apply filters</Button>
</div>Actionable empty state
Tell the user what happened, why it matters, and the next useful action.
No reports match these filters
Adjust the filter set or create a scheduled report for this team.
<EmptyState
title="No reports match these filters"
description="Adjust the filter set or create a scheduled report."
action={<Button>Create report</Button>}
/>Sensitive-action dialog
Use a clear title, explicit body copy, required input, and destructive/primary confirmation.
<Dialog open={open} onOpenChange={setOpen}>
<DialogContent>
<DialogTitle>Approve elevated access</DialogTitle>
<DialogBody><Textarea /></DialogBody>
<DialogFooter><Button>Approve access</Button></DialogFooter>
</DialogContent>
</Dialog>Responsive grid list
Use card grids for selectable modules, applications, or team summaries.
Payroll
Configured for 12 teams.
Recruiting
Configured for 15 teams.
Benefits
Configured for 18 teams.
Learning
Configured for 21 teams.
<div className="grid gap-3 sm:grid-cols-2 lg:grid-cols-4">
{modules.map((module) => <Card key={module.name}>{module.name}</Card>)}
</div>Operational banner stack
Reserve banners for page-level state and alerts for local status messages.
Scheduled payroll maintenance
Exports pause tonight from 23:00 to 23:30.
Sync complete
All employee records are current.
<Banner
tone="warning"
title="Scheduled payroll maintenance"
action={<Button size="sm">View window</Button>}
/>Status badge system
Use badges for labels and StatusBadge for state that needs semantic meaning.
<Badge variant="success">Compliant</Badge>
<StatusBadge tone="warning">Pending</StatusBadge>Onboarding feed
Show progress, accountable people, and next actions in a compact workflow card.
New hire onboarding
Four steps remain for the People team.
<ProgressCircle value={64} label="Complete" />
<Tracker data={steps} radius="full" />
<AvatarGroup>{reviewers}</AvatarGroup>Billing and usage meter
Use progress, segmented allocation, and plan summary cards for usage-heavy screens.
Usage this cycle
Limits reset in 12 days.
Enterprise
$4,280
Estimated monthly bill.
<Progress value={72} showValue trackLabelPosition="bottom" />
<CategoryBar data={usageSegments} />Ranked metric list
Use for compact ranked queues, top modules, traffic sources, or exceptions.
<BarList data={rankedData} valueFormatter={(value) => `${value} items`} />Status monitoring panel
Combine status strips and summary cards for uptime, sync, and workflow monitoring.
Integration monitor
Last 24 sync windows across core services.
Uptime
+0.1%99.9%
Warnings
-23
Queue
+842
<Tracker data={windows} size="sm" radius="full" />
<StatusBadge tone="success">Operational</StatusBadge>Spark chart summaries
Use small trend charts inside cards and tables where full axes would add noise.
Hiring velocity
Offer acceptance
Time to approve
<SparkChart data={trend} showArea showDots label="Hiring velocity" />Accessible form layout
Pair every field with labels, helper text, validation states, and explicit actions.
Notify manager
Send a summary when this is submitted.
<Label htmlFor="employee-email">Work email</Label>
<Input id="employee-email" type="email" />
<Button type="submit">Submit request</Button>Responsive data table
Keep headers meaningful, status visible, and wide content inside a stable responsive wrapper.
| Employee | Department | Status | Risk |
|---|---|---|---|
| Aisha Kumari | Engineering | Active | Low |
| Malik Chen | Design | Review | Medium |
| Sara Johansson | Product | Active | Low |
<Table>
<TableHeader tone="primary">...</TableHeader>
<TableBody>...</TableBody>
</Table>Table action toolbar
Place search, column controls, export, and bulk actions in a predictable toolbar.
| Employee | Department | Status | Risk |
|---|---|---|---|
| Aisha Kumari | Engineering | Active | Low |
| Malik Chen | Design | Review | Medium |
| Sara Johansson | Product | Active | Low |
<SearchField placeholder="Search table..." />
<Button variant="outline">Columns</Button>
<Button>Bulk review</Button>Pagination footer
Show range, page state, and next/previous controls without moving the table.
| Employee | Department | Status | Risk |
|---|---|---|---|
| Aisha Kumari | Engineering | Active | Low |
| Malik Chen | Design | Review | Medium |
| Sara Johansson | Product | Active | Low |
Showing 1-10 of 128 employees
<Pagination />
<Button variant="outline" size="sm">Previous</Button>
<Button variant="outline" size="sm">Next</Button>Pricing comparison cards
Use one recommended plan, direct CTAs, and short comparable feature rows.
Core
For growing HR teams.
$18
- Employee records
- Workflow approvals
- Reporting exports
Growth
For growing HR teams.
$27
- Employee records
- Workflow approvals
- Reporting exports
Enterprise
For regional HR teams.
Custom
- Employee records
- Workflow approvals
- Reporting exports
<Card className="border-primary">
<Badge>Recommended</Badge>
<Button className="w-full">Compare plan</Button>
</Card>Secure login form
Use semantic inputs, explicit labels, SSO alternatives, and strong focus states.
Sign in securely
Use SSO or your work email.
<Label htmlFor="login-email">Email</Label>
<Input id="login-email" type="email" />
<Button type="submit">Continue</Button>Account and user management
Combine people, permissions, invite actions, and status tables for admin surfaces.
People operations
4 reviewers, 2 pending invites.
| Employee | Department | Status | Risk |
|---|---|---|---|
| Aisha Kumari | Engineering | Active | Low |
| Malik Chen | Design | Review | Medium |
| Sara Johansson | Product | Active | Low |
Access controls
<AvatarGroup>{reviewers}</AvatarGroup>
<Button leadingIcon={<UserPlus />}>Invite</Button>
<Table>...</Table>Sortable task queue
Use SortableList for ordered workflows where the returned item array is the source of truth.
Priority queue
Drag rows to match the next review sequence.
Review policy exceptions
People Ops
Approve payroll export
Finance
Assign equipment owner
IT
Schedule manager handoff
HRBP
Reorder rules
Persist the returned item array in host state, keep row actions away from the drag area, and support keyboard sorting.
<SortableList
items={tasks}
onItemsChange={setTasks}
renderItem={(item, state) => <TaskRow item={item} index={state.index} />}
/>Decision card group
Use RadioCardGroup when each option needs an icon, title, and explanatory copy.
Choose review mode
Use rich option cards when labels need supporting proof.
<RadioCardGroup
value={mode}
onValueChange={setMode}
orientation="horizontal"
columns={3}
options={reviewModes}
/>Overlay content rules
Choose tooltip, popover, popper, or hover card based on content size and interaction need.
Tooltip
Short label for icon-only actions.
Popover
Rich contextual controls and filters.
HoverCard
Preview a user or object without navigation.
<Tooltip content="Copy to clipboard">
<Button variant="outline">Copy</Button>
</Tooltip>Navigation context stack
Use breadcrumbs for hierarchy and tab navigation for peer destinations.
<Breadcrumb>...</Breadcrumb>
<TabNavigation items={tabs} variant="contained" />Local tabbed review panel
Use Tabs for same-page state, tab panels, and keyboard-navigable product sections.
Matched
+4%91%
Pending
-618
Owners
+27
<Tabs value={tab} onValueChange={setTab}>
<TabsList layout="equal">
<TabsTrigger value="summary">Summary</TabsTrigger>
</TabsList>
<TabsContent value="summary">...</TabsContent>
</Tabs>Responsive application layout
Use stable page regions, spacious content bands, and clear action hierarchy.
Workspace overview
Use spacious regions, stable grids, and clear actions.
Tasks
+842
Approvals
-312
Complete
+4%91%
<PageShell topBar={topBar} sidebar={sidebar} titleBar={titleBar}>
<main className="grid gap-4 lg:grid-cols-[16rem_1fr]">...</main>
</PageShell>