Chapter 22: UI Component Library
The Shared Design System
This chapter defines the complete UI component library shared across all POS Platform applications. These specifications ensure visual consistency, reduce development time, and enable rapid prototyping.
Design Tokens
Color Palette
Primary Colors
| Token | Hex | RGB | Usage |
|---|---|---|---|
--color-primary | #1976D2 | 25, 118, 210 | Main brand, primary buttons, links |
--color-primary-dark | #1565C0 | 21, 101, 192 | Hover states, headers |
--color-primary-light | #BBDEFB | 187, 222, 251 | Selected backgrounds, info panels |
--color-primary-50 | #E3F2FD | 227, 242, 253 | Subtle backgrounds |
Secondary Colors
| Token | Hex | RGB | Usage |
|---|---|---|---|
--color-secondary | #424242 | 66, 66, 66 | Secondary buttons, icons |
--color-secondary-dark | #212121 | 33, 33, 33 | Text, headings |
--color-secondary-light | #757575 | 117, 117, 117 | Secondary text, labels |
Status Colors
| Token | Hex | RGB | Usage |
|---|---|---|---|
--color-success | #4CAF50 | 76, 175, 80 | Success states, positive |
--color-success-light | #E8F5E9 | 232, 245, 233 | Success backgrounds |
--color-success-dark | #2E7D32 | 46, 125, 50 | Success text on light bg |
--color-warning | #FF9800 | 255, 152, 0 | Warning states, caution |
--color-warning-light | #FFF3E0 | 255, 243, 224 | Warning backgrounds |
--color-warning-dark | #E65100 | 230, 81, 0 | Warning text on light bg |
--color-error | #F44336 | 244, 67, 54 | Error states, destructive |
--color-error-light | #FFEBEE | 255, 235, 238 | Error backgrounds |
--color-error-dark | #C62828 | 198, 40, 40 | Error text on light bg |
--color-info | #2196F3 | 33, 150, 243 | Informational states |
--color-info-light | #E3F2FD | 227, 242, 253 | Info backgrounds |
--color-info-dark | #1565C0 | 21, 101, 192 | Info text on light bg |
Neutral Colors
| Token | Hex | Usage |
|---|---|---|
--color-white | #FFFFFF | Card backgrounds, content areas |
--color-gray-50 | #FAFAFA | Alternating row backgrounds |
--color-gray-100 | #F5F5F5 | Page backgrounds, disabled |
--color-gray-200 | #EEEEEE | Light borders, dividers |
--color-gray-300 | #E0E0E0 | Standard borders |
--color-gray-400 | #BDBDBD | Input borders, icons |
--color-gray-500 | #9E9E9E | Disabled text, placeholders |
--color-gray-600 | #757575 | Secondary text |
--color-gray-700 | #616161 | Icons, labels |
--color-gray-800 | #424242 | Body text |
--color-gray-900 | #212121 | Headings, primary text |
--color-black | #000000 | Maximum contrast |
Typography Scale
Font Families
--font-family-base: 'Segoe UI', -apple-system, BlinkMacSystemFont,
'Roboto', 'Helvetica Neue', Arial, sans-serif;
--font-family-mono: 'Cascadia Code', 'Fira Code', 'Consolas',
'Monaco', 'Courier New', monospace;
Font Sizes
| Token | Size | Line Height | Usage |
|---|---|---|---|
--font-size-xs | 11px | 1.4 | Captions, badges |
--font-size-sm | 12px | 1.4 | Secondary text, timestamps |
--font-size-base | 14px | 1.5 | Body text, inputs |
--font-size-md | 16px | 1.5 | Emphasized body |
--font-size-lg | 18px | 1.4 | Section headers |
--font-size-xl | 20px | 1.3 | Card titles |
--font-size-2xl | 24px | 1.3 | Page titles |
--font-size-3xl | 30px | 1.2 | Dashboard stats |
--font-size-4xl | 36px | 1.1 | Large numbers |
Font Weights
| Token | Weight | Usage |
|---|---|---|
--font-weight-light | 300 | Large titles |
--font-weight-normal | 400 | Body text |
--font-weight-medium | 500 | Buttons, emphasized |
--font-weight-semibold | 600 | Headers, labels |
--font-weight-bold | 700 | Stats, strong emphasis |
Spacing System
| Token | Value | Usage |
|---|---|---|
--space-0 | 0 | No spacing |
--space-1 | 4px | Tight, inline elements |
--space-2 | 8px | Component padding, gaps |
--space-3 | 12px | Card padding |
--space-4 | 16px | Section margins |
--space-5 | 20px | Larger gaps |
--space-6 | 24px | Panel padding |
--space-8 | 32px | Section spacing |
--space-10 | 40px | Large separations |
--space-12 | 48px | Page margins |
Border Radius
| Token | Value | Usage |
|---|---|---|
--radius-none | 0 | Sharp corners |
--radius-sm | 2px | Subtle rounding |
--radius-base | 4px | Inputs, buttons |
--radius-md | 6px | Cards |
--radius-lg | 8px | Panels, modals |
--radius-xl | 12px | Large cards |
--radius-full | 9999px | Pills, circles |
Shadows
| Token | Value | Usage |
|---|---|---|
--shadow-sm | 0 1px 2px rgba(0,0,0,0.05) | Subtle lift |
--shadow-base | 0 2px 4px rgba(0,0,0,0.1) | Standard cards |
--shadow-md | 0 4px 8px rgba(0,0,0,0.12) | Elevated cards |
--shadow-lg | 0 8px 16px rgba(0,0,0,0.15) | Dropdowns, popovers |
--shadow-xl | 0 12px 24px rgba(0,0,0,0.2) | Modals |
Component Specifications
1. StatCard
Purpose: Display key metrics with trend indicators on dashboards.
ASCII Wireframe:
┌────────────────────────────────────┐
│ [icon] │
│ │
│ LABEL │
│ 12,450 │
│ +12.3% vs previous │
│ │
└────────────────────────────────────┘
Variants:
STANDARD COMPACT INLINE
┌──────────────────┐ ┌──────────────────┐ ┌──────────────────────────┐
│ [icon] │ │ Orders 1,234 │ │ [icon] Orders: 1,234 +5% │
│ Orders │ │ +12% ▲ │ └──────────────────────────┘
│ 1,234 │ └──────────────────┘
│ +12% ▲ │
└──────────────────┘
Props:
| Prop | Type | Default | Description |
|---|---|---|---|
Title | string | required | Metric label |
Value | string | required | Primary value |
Icon | IconType | null | Optional icon |
Change | string | null | Change indicator (e.g., “+12%”) |
IsPositive | bool | true | Trend direction |
Color | string | “primary” | primary, success, warning, error |
Size | string | “standard” | standard, compact, inline |
Blazor Usage:
<StatCard Title="Today's Sales"
Value="$12,450"
Icon="IconType.DollarSign"
Change="+12.3%"
IsPositive="true"
Color="success" />
2. DataGrid
Purpose: Display tabular data with sorting, filtering, and pagination.
ASCII Wireframe:
┌─────────────────────────────────────────────────────────────────────┐
│ [x] │ ORDER # ▼ │ DATE │ CUSTOMER │ AMOUNT ▼ │ STATUS │
├─────┼────────────┼────────────┼───────────────┼──────────┼─────────┤
│ [ ] │ #1234 │ 12/29/2024 │ John Smith │ $99.00 │ ● New │
│ [x] │ #1235 │ 12/29/2024 │ Jane Doe │ $149.00 │ ● Done │
│ [ ] │ #1236 │ 12/28/2024 │ Bob Johnson │ $75.50 │ ! Error │
├─────┴────────────┴────────────┴───────────────┴──────────┴─────────┤
│ Showing 1-50 of 256 << < Page 1 of 6 > >> │
└─────────────────────────────────────────────────────────────────────┘
Column Types:
TEXT COLUMN NUMBER COLUMN STATUS COLUMN ACTION COLUMN
┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ John Smith │ │ $99.00 │ │ ● Completed │ │ [Ed] [Del] │
└─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘
Left Right Center Center
Props:
| Prop | Type | Default | Description |
|---|---|---|---|
Items | IEnumerable | required | Data source |
Columns | List | required | Column definitions |
Selectable | bool | false | Enable row selection |
Sortable | bool | true | Enable column sorting |
Paginate | bool | true | Enable pagination |
PageSize | int | 25 | Items per page |
OnRowClick | EventCallback | null | Row click handler |
OnSelectionChange | EventCallback | null | Selection handler |
Column Definition:
public class Column<T>
{
public string Header { get; set; }
public Func<T, object> ValueFunc { get; set; }
public string Align { get; set; } = "left"; // left, center, right
public bool Sortable { get; set; } = true;
public string Width { get; set; } = "auto";
public Func<T, RenderFragment> Template { get; set; }
}
Blazor Usage:
<DataGrid Items="@orders" Selectable="true" OnRowClick="ViewOrder">
<Column Header="Order #" ValueFunc="@(o => o.OrderNumber)" />
<Column Header="Date" ValueFunc="@(o => o.Date.ToShortDateString())" />
<Column Header="Amount" ValueFunc="@(o => o.Total)" Align="right" />
<Column Header="Status">
<Template>
<StatusBadge Status="@context.Status" />
</Template>
</Column>
</DataGrid>
3. StatusBadge
Purpose: Display color-coded status indicators.
ASCII Wireframe:
SUCCESS WARNING ERROR INFO NEUTRAL
┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐
│● Active │ │● Pending│ │● Failed │ │● Syncing│ │● Draft │
└─────────┘ └─────────┘ └─────────┘ └─────────┘ └─────────┘
Green bg Orange bg Red bg Blue bg Gray bg
Size Variants:
SMALL MEDIUM (Default) LARGE
┌──────────┐ ┌─────────────┐ ┌────────────────┐
│ ● Active │ │ ● Active │ │ ● Active │
└──────────┘ └─────────────┘ └────────────────┘
11px font 13px font 15px font
Props:
| Prop | Type | Default | Description |
|---|---|---|---|
Status | string | required | Status text |
Variant | string | “info” | success, warning, error, info, neutral |
Size | string | “medium” | small, medium, large |
ShowDot | bool | true | Show status dot |
CSS Classes:
.status-badge {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 4px 8px;
border-radius: var(--radius-base);
font-size: var(--font-size-sm);
font-weight: var(--font-weight-medium);
}
.status-badge--success {
background: var(--color-success-light);
color: var(--color-success-dark);
}
.status-badge--warning {
background: var(--color-warning-light);
color: var(--color-warning-dark);
}
.status-badge--error {
background: var(--color-error-light);
color: var(--color-error-dark);
}
.status-badge--info {
background: var(--color-info-light);
color: var(--color-info-dark);
}
.status-badge--neutral {
background: var(--color-gray-100);
color: var(--color-gray-700);
}
.status-dot {
width: 8px;
height: 8px;
border-radius: 50%;
background: currentColor;
}
Blazor Usage:
<StatusBadge Status="Active" Variant="success" />
<StatusBadge Status="Pending" Variant="warning" />
<StatusBadge Status="Failed" Variant="error" ShowDot="false" />
4. SearchInput
Purpose: Debounced search input with autocomplete support.
ASCII Wireframe:
EMPTY STATE WITH VALUE
┌────────────────────────────────┐ ┌────────────────────────────────┐
│ [O] Search products... │ │ [O] galaxy v-neck [X] │
└────────────────────────────────┘ └────────────────────────────────┘
WITH AUTOCOMPLETE LOADING STATE
┌────────────────────────────────┐ ┌────────────────────────────────┐
│ [O] galaxy v │ │ [O] galaxy v-neck [...] │
├────────────────────────────────┤ └────────────────────────────────┘
│ Galaxy V-Neck Tee │
│ Galaxy V-Neck Tank │
│ Galaxy Vintage Wash │
└────────────────────────────────┘
Props:
| Prop | Type | Default | Description |
|---|---|---|---|
Value | string | “” | Current value |
Placeholder | string | “Search…” | Placeholder text |
DebounceMs | int | 300 | Debounce delay |
AutoComplete | bool | false | Enable autocomplete |
Items | IEnumerable | null | Autocomplete items |
OnSearch | EventCallback | null | Search handler |
OnSelect | EventCallback | null | Selection handler |
Disabled | bool | false | Disable input |
Blazor Usage:
<SearchInput @bind-Value="searchTerm"
Placeholder="Search products..."
DebounceMs="300"
OnSearch="HandleSearch" />
<SearchInput @bind-Value="productSearch"
AutoComplete="true"
Items="@productSuggestions"
OnSelect="SelectProduct" />
5. Modal
Purpose: Overlay dialog for forms, confirmations, and detail views.
ASCII Wireframe:
STANDARD MODAL
┌────────────────────────────────────────────────────────────┐
│ Modal Title [X] │
├────────────────────────────────────────────────────────────┤
│ │
│ Modal content goes here. │
│ │
│ This can include forms, text, images, or any other │
│ content that needs to be displayed in an overlay. │
│ │
├────────────────────────────────────────────────────────────┤
│ [Cancel] [Confirm] │
└────────────────────────────────────────────────────────────┘
CONFIRMATION MODAL (Compact)
┌─────────────────────────────────────────────┐
│ [!] Delete Item? [X] │
├─────────────────────────────────────────────┤
│ │
│ Are you sure you want to delete this item? │
│ This action cannot be undone. │
│ │
├─────────────────────────────────────────────┤
│ [Cancel] [Delete] │
└─────────────────────────────────────────────┘
FULLSCREEN MODAL (Mobile)
╔═════════════════════════════════════════════╗
║ [<] Modal Title ║
╠═════════════════════════════════════════════╣
║ ║
║ Full content area ║
║ (scrollable) ║
║ ║
╠═════════════════════════════════════════════╣
║ [Primary Action] ║
╚═════════════════════════════════════════════╝
Size Variants:
| Size | Width | Use Case |
|---|---|---|
small | 400px | Confirmations, alerts |
medium | 600px | Forms, details |
large | 800px | Complex forms, tables |
fullscreen | 100% | Mobile, immersive |
Props:
| Prop | Type | Default | Description |
|---|---|---|---|
Title | string | null | Modal title |
IsOpen | bool | false | Visibility state |
Size | string | “medium” | small, medium, large, fullscreen |
ShowClose | bool | true | Show close button |
CloseOnOverlay | bool | true | Close on backdrop click |
OnClose | EventCallback | null | Close handler |
ChildContent | RenderFragment | required | Modal body |
Footer | RenderFragment | null | Footer actions |
Blazor Usage:
<Modal Title="Edit Product"
IsOpen="@showModal"
Size="medium"
OnClose="CloseModal">
<ChildContent>
<EditForm Model="@product">
<!-- Form fields -->
</EditForm>
</ChildContent>
<Footer>
<Button Variant="secondary" OnClick="CloseModal">Cancel</Button>
<Button Variant="primary" OnClick="SaveProduct">Save</Button>
</Footer>
</Modal>
6. Toast
Purpose: Non-blocking notifications that auto-dismiss.
ASCII Wireframe:
SUCCESS TOAST ERROR TOAST
┌──────────────────────────┐ ┌──────────────────────────┐
│ [check] Product saved │ │ [X] Failed to save │
│ successfully │ │ Please try again │
│ [X] │ │ [X] │
└──────────────────────────┘ └──────────────────────────┘
WARNING TOAST INFO TOAST
┌──────────────────────────┐ ┌──────────────────────────┐
│ [!] Low inventory │ │ [i] Sync completed │
│ Check stock levels │ │ 245 items updated │
│ [X] │ │ [X] │
└──────────────────────────┘ └──────────────────────────┘
TOAST WITH ACTION
┌──────────────────────────────────────────┐
│ [!] Order requires attention │
│ Missing shipping address │
│ [View] [Dismiss]│
└──────────────────────────────────────────┘
Position Options:
TOP-RIGHT (Default) TOP-CENTER BOTTOM-RIGHT
┌─────────────────┐ ┌─────────────────┐
│ │ │ │
│ [T] │ │ [T] │
│ [T] │ │ [T] │ ┌─────────────────┐
│ │ │ │ │ │
│ │ │ │ │ [T] │
└─────────────────┘ └─────────────────┘ └─────────────────┘
Props:
| Prop | Type | Default | Description |
|---|---|---|---|
Message | string | required | Toast message |
Title | string | null | Optional title |
Variant | string | “info” | success, warning, error, info |
Duration | int | 5000 | Auto-dismiss (ms), 0 = persist |
Position | string | “top-right” | Toast position |
ShowClose | bool | true | Show dismiss button |
Action | RenderFragment | null | Action buttons |
Toast Service:
public interface IToastService
{
void ShowSuccess(string message, string title = null);
void ShowError(string message, string title = null);
void ShowWarning(string message, string title = null);
void ShowInfo(string message, string title = null);
void Show(ToastOptions options);
void DismissAll();
}
Blazor Usage:
@inject IToastService Toast
<button @onclick="SaveProduct">Save</button>
@code {
async Task SaveProduct()
{
try
{
await productService.SaveAsync(product);
Toast.ShowSuccess("Product saved successfully");
}
catch
{
Toast.ShowError("Failed to save product", "Error");
}
}
}
7. LoadingSpinner
Purpose: Indicate loading states.
ASCII Wireframe:
SPINNER ONLY WITH TEXT OVERLAY
◐ ◐ ┌─────────────────┐
╱ ╲ Loading... │ ░░░░░░░░░ │
◜ ◝ │ ░ ◐ ░ │
│ ░Loading░ │
│ ░░░░░░░░░ │
└─────────────────┘
Size Variants:
| Size | Diameter | Use Case |
|---|---|---|
small | 16px | Inline, buttons |
medium | 24px | Cards, sections |
large | 48px | Page, full overlay |
Props:
| Prop | Type | Default | Description |
|---|---|---|---|
Size | string | “medium” | small, medium, large |
Text | string | null | Loading text |
Overlay | bool | false | Full overlay mode |
Color | string | “primary” | Spinner color |
Blazor Usage:
<!-- Inline spinner -->
<LoadingSpinner Size="small" />
<!-- With text -->
<LoadingSpinner Text="Saving..." />
<!-- Full overlay -->
<LoadingSpinner Overlay="true" Text="Processing order..." />
<!-- In button -->
<Button Disabled="@isSaving">
@if (isSaving)
{
<LoadingSpinner Size="small" Color="white" />
<span>Saving...</span>
}
else
{
<span>Save</span>
}
</Button>
8. EmptyState
Purpose: Display meaningful placeholder when no data is available.
ASCII Wireframe:
STANDARD EMPTY STATE
┌─────────────────────────────────────────────────────┐
│ │
│ [ ICON ] │
│ │
│ No products found │
│ │
│ Try adjusting your search or filters to │
│ find what you're looking for. │
│ │
│ [Clear Filters] │
│ │
└─────────────────────────────────────────────────────┘
COMPACT EMPTY STATE WITH ACTION
┌─────────────────────────┐ ┌─────────────────────────┐
│ [icon] │ │ [icon] │
│ No items found │ │ No orders yet │
└─────────────────────────┘ │ │
│ [Create Order] │
└─────────────────────────┘
Props:
| Prop | Type | Default | Description |
|---|---|---|---|
Icon | IconType | null | Illustration icon |
Title | string | required | Empty state title |
Description | string | null | Explanatory text |
Action | RenderFragment | null | Action button(s) |
Size | string | “medium” | compact, medium, large |
Blazor Usage:
<EmptyState Icon="IconType.Box"
Title="No products found"
Description="Try adjusting your search or filters.">
<Action>
<Button Variant="secondary" OnClick="ClearFilters">Clear Filters</Button>
</Action>
</EmptyState>
Button Component
Purpose: Primary interactive element for triggering actions.
ASCII Wireframe:
PRIMARY SECONDARY TERTIARY/TEXT
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ Save │ │ Cancel │ │ Learn More │
└─────────────────┘ └─────────────────┘ └─────────────────┘
Solid background Outlined No border
DANGER WITH ICON LOADING
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ Delete │ │ [+] Add Item │ │ [o] Saving... │
└─────────────────┘ └─────────────────┘ └─────────────────┘
Red background Icon + text Spinner + text
Size Variants:
| Size | Height | Padding | Font Size |
|---|---|---|---|
small | 28px | 8px 12px | 12px |
medium | 36px | 10px 16px | 14px |
large | 44px | 12px 20px | 16px |
Props:
| Prop | Type | Default | Description |
|---|---|---|---|
Variant | string | “primary” | primary, secondary, tertiary, danger |
Size | string | “medium” | small, medium, large |
Icon | IconType | null | Leading icon |
IconPosition | string | “left” | left, right |
Loading | bool | false | Show loading state |
Disabled | bool | false | Disable button |
FullWidth | bool | false | 100% width |
OnClick | EventCallback | null | Click handler |
Form Components
TextInput
LABEL WITH INPUT ERROR STATE
┌────────────────────────────┐ ┌────────────────────────────┐
│ Email Address │ │ Email Address │
│ ┌────────────────────────┐ │ │ ┌────────────────────────┐ │
│ │ user@example.com │ │ │ │ invalid-email │ │
│ └────────────────────────┘ │ │ └────────────────────────┘ │
└────────────────────────────┘ │ Please enter a valid email │
└────────────────────────────┘
Select/Dropdown
CLOSED OPEN
┌────────────────────────────┐ ┌────────────────────────────┐
│ Select option [v] │ │ Option One [^] │
└────────────────────────────┘ ├────────────────────────────┤
│ Option One [check] │
│ Option Two │
│ Option Three │
└────────────────────────────┘
Checkbox
UNCHECKED CHECKED INDETERMINATE
[ ] Option One [x] Option Two [-] Select All
Radio Button
UNSELECTED SELECTED
( ) Option One (o) Option Two
Dark Mode Considerations
Color Mapping
| Light Mode | Dark Mode |
|---|---|
| #FFFFFF (white) | #1E1E1E (dark surface) |
| #F5F5F5 (gray-100) | #2D2D2D (elevated surface) |
| #212121 (text) | #FFFFFF (text) |
| #757575 (secondary) | #B0B0B0 (secondary) |
| #1976D2 (primary) | #64B5F6 (lighter primary) |
Dark Mode Tokens
:root[data-theme="dark"] {
--color-background: #121212;
--color-surface: #1E1E1E;
--color-surface-elevated: #2D2D2D;
--color-text-primary: #FFFFFF;
--color-text-secondary: #B0B0B0;
--color-text-disabled: #6B6B6B;
--color-border: #3D3D3D;
--color-primary: #64B5F6;
--color-primary-dark: #90CAF9;
}
Component Adjustments
| Component | Light | Dark |
|---|---|---|
| Cards | White bg, shadow | Dark surface, border |
| Inputs | White bg, gray border | Dark bg, light border |
| Badges | Colored bg | Reduced opacity bg |
| Buttons | Standard | Slightly elevated |
Accessibility Guidelines
Focus States
*:focus-visible {
outline: 2px solid var(--color-primary);
outline-offset: 2px;
}
/* High contrast mode */
@media (prefers-contrast: high) {
*:focus-visible {
outline-width: 3px;
}
}
Color Contrast
| Requirement | Ratio | Usage |
|---|---|---|
| AA Normal | 4.5:1 | Body text |
| AA Large | 3:1 | 18px+ text |
| AAA Normal | 7:1 | Enhanced |
| AAA Large | 4.5:1 | Enhanced large |
ARIA Labels
<!-- Button with icon only -->
<Button Icon="IconType.Search" aria-label="Search products" />
<!-- Loading state -->
<LoadingSpinner aria-label="Loading content" role="status" />
<!-- Badge with context -->
<StatusBadge Status="Error"
aria-label="Order status: Error - requires attention" />
Summary
The Component Library provides:
- StatCard: Dashboard metrics with trends
- DataGrid: Sortable, filterable data tables
- StatusBadge: Color-coded status indicators
- SearchInput: Debounced search with autocomplete
- Modal: Overlay dialogs for forms and confirmations
- Toast: Non-blocking notifications
- LoadingSpinner: Loading state indicators
- EmptyState: Meaningful placeholders
All components follow:
- Consistent design tokens
- Responsive sizing
- Dark mode support
- Accessibility standards
Implementation complete. Ready for engineer review.