Chapter 17: 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.


17.1 Design Tokens

Color Palette

Primary Colors

TokenHexRGBUsage
--color-primary#1976D225, 118, 210Main brand, primary buttons, links
--color-primary-dark#1565C021, 101, 192Hover states, headers
--color-primary-light#BBDEFB187, 222, 251Selected backgrounds, info panels
--color-primary-50#E3F2FD227, 242, 253Subtle backgrounds

Secondary Colors

TokenHexRGBUsage
--color-secondary#42424266, 66, 66Secondary buttons, icons
--color-secondary-dark#21212133, 33, 33Text, headings
--color-secondary-light#757575117, 117, 117Secondary text, labels

Status Colors

TokenHexRGBUsage
--color-success#4CAF5076, 175, 80Success states, positive
--color-success-light#E8F5E9232, 245, 233Success backgrounds
--color-success-dark#2E7D3246, 125, 50Success text on light bg
--color-warning#FF9800255, 152, 0Warning states, caution
--color-warning-light#FFF3E0255, 243, 224Warning backgrounds
--color-warning-dark#E65100230, 81, 0Warning text on light bg
--color-error#F44336244, 67, 54Error states, destructive
--color-error-light#FFEBEE255, 235, 238Error backgrounds
--color-error-dark#C62828198, 40, 40Error text on light bg
--color-info#2196F333, 150, 243Informational states
--color-info-light#E3F2FD227, 242, 253Info backgrounds
--color-info-dark#1565C021, 101, 192Info text on light bg

Neutral Colors

TokenHexUsage
--color-white#FFFFFFCard backgrounds, content areas
--color-gray-50#FAFAFAAlternating row backgrounds
--color-gray-100#F5F5F5Page backgrounds, disabled
--color-gray-200#EEEEEELight borders, dividers
--color-gray-300#E0E0E0Standard borders
--color-gray-400#BDBDBDInput borders, icons
--color-gray-500#9E9E9EDisabled text, placeholders
--color-gray-600#757575Secondary text
--color-gray-700#616161Icons, labels
--color-gray-800#424242Body text
--color-gray-900#212121Headings, primary text
--color-black#000000Maximum 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

TokenSizeLine HeightUsage
--font-size-xs11px1.4Captions, badges
--font-size-sm12px1.4Secondary text, timestamps
--font-size-base14px1.5Body text, inputs
--font-size-md16px1.5Emphasized body
--font-size-lg18px1.4Section headers
--font-size-xl20px1.3Card titles
--font-size-2xl24px1.3Page titles
--font-size-3xl30px1.2Dashboard stats
--font-size-4xl36px1.1Large numbers

Font Weights

TokenWeightUsage
--font-weight-light300Large titles
--font-weight-normal400Body text
--font-weight-medium500Buttons, emphasized
--font-weight-semibold600Headers, labels
--font-weight-bold700Stats, strong emphasis

Spacing System

TokenValueUsage
--space-00No spacing
--space-14pxTight, inline elements
--space-28pxComponent padding, gaps
--space-312pxCard padding
--space-416pxSection margins
--space-520pxLarger gaps
--space-624pxPanel padding
--space-832pxSection spacing
--space-1040pxLarge separations
--space-1248pxPage margins

Border Radius

TokenValueUsage
--radius-none0Sharp corners
--radius-sm2pxSubtle rounding
--radius-base4pxInputs, buttons
--radius-md6pxCards
--radius-lg8pxPanels, modals
--radius-xl12pxLarge cards
--radius-full9999pxPills, circles

Shadows

TokenValueUsage
--shadow-sm0 1px 2px rgba(0,0,0,0.05)Subtle lift
--shadow-base0 2px 4px rgba(0,0,0,0.1)Standard cards
--shadow-md0 4px 8px rgba(0,0,0,0.12)Elevated cards
--shadow-lg0 8px 16px rgba(0,0,0,0.15)Dropdowns, popovers
--shadow-xl0 12px 24px rgba(0,0,0,0.2)Modals

17.2 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:

PropTypeDefaultDescription
TitlestringrequiredMetric label
ValuestringrequiredPrimary value
IconIconTypenullOptional icon
ChangestringnullChange indicator (e.g., “+12%”)
IsPositivebooltrueTrend direction
Colorstring“primary”primary, success, warning, error
Sizestring“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:

PropTypeDefaultDescription
ItemsIEnumerablerequiredData source
ColumnsListrequiredColumn definitions
SelectableboolfalseEnable row selection
SortablebooltrueEnable column sorting
PaginatebooltrueEnable pagination
PageSizeint25Items per page
OnRowClickEventCallbacknullRow click handler
OnSelectionChangeEventCallbacknullSelection 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:

PropTypeDefaultDescription
StatusstringrequiredStatus text
Variantstring“info”success, warning, error, info, neutral
Sizestring“medium”small, medium, large
ShowDotbooltrueShow 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:

PropTypeDefaultDescription
Valuestring“”Current value
Placeholderstring“Search…”Placeholder text
DebounceMsint300Debounce delay
AutoCompleteboolfalseEnable autocomplete
ItemsIEnumerablenullAutocomplete items
OnSearchEventCallbacknullSearch handler
OnSelectEventCallbacknullSelection handler
DisabledboolfalseDisable 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:

SizeWidthUse Case
small400pxConfirmations, alerts
medium600pxForms, details
large800pxComplex forms, tables
fullscreen100%Mobile, immersive

Props:

PropTypeDefaultDescription
TitlestringnullModal title
IsOpenboolfalseVisibility state
Sizestring“medium”small, medium, large, fullscreen
ShowClosebooltrueShow close button
CloseOnOverlaybooltrueClose on backdrop click
OnCloseEventCallbacknullClose handler
ChildContentRenderFragmentrequiredModal body
FooterRenderFragmentnullFooter 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:

PropTypeDefaultDescription
MessagestringrequiredToast message
TitlestringnullOptional title
Variantstring“info”success, warning, error, info
Durationint5000Auto-dismiss (ms), 0 = persist
Positionstring“top-right”Toast position
ShowClosebooltrueShow dismiss button
ActionRenderFragmentnullAction 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:

SizeDiameterUse Case
small16pxInline, buttons
medium24pxCards, sections
large48pxPage, full overlay

Props:

PropTypeDefaultDescription
Sizestring“medium”small, medium, large
TextstringnullLoading text
OverlayboolfalseFull overlay mode
Colorstring“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:

PropTypeDefaultDescription
IconIconTypenullIllustration icon
TitlestringrequiredEmpty state title
DescriptionstringnullExplanatory text
ActionRenderFragmentnullAction button(s)
Sizestring“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>

17.3 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:

SizeHeightPaddingFont Size
small28px8px 12px12px
medium36px10px 16px14px
large44px12px 20px16px

Props:

PropTypeDefaultDescription
Variantstring“primary”primary, secondary, tertiary, danger
Sizestring“medium”small, medium, large
IconIconTypenullLeading icon
IconPositionstring“left”left, right
LoadingboolfalseShow loading state
DisabledboolfalseDisable button
FullWidthboolfalse100% width
OnClickEventCallbacknullClick handler

17.4 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

17.5 Dark Mode Considerations

Color Mapping

Light ModeDark 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

ComponentLightDark
CardsWhite bg, shadowDark surface, border
InputsWhite bg, gray borderDark bg, light border
BadgesColored bgReduced opacity bg
ButtonsStandardSlightly elevated

17.6 Accessibility (WCAG 2.1 AA)

All components must meet WCAG 2.1 Level AA compliance. This is non-negotiable for a POS system used in retail environments with diverse staff.

Color Contrast Requirements

RequirementRatioApplies To
AA Normal text4.5:1Body text (< 18px), input labels
AA Large text3:1Text 18px+ or 14px+ bold, headings
AA UI Components3:1Borders, icons, focus indicators, form controls
AAA Normal (goal)7:1Critical POS text (totals, prices, error messages)

Focus Management

/* Visible focus ring for keyboard navigation */
*:focus-visible {
    outline: 2px solid var(--color-primary);
    outline-offset: 2px;
}

/* High contrast mode */
@media (prefers-contrast: high) {
    *:focus-visible {
        outline-width: 3px;
        outline-color: Highlight;
    }
}

/* Never remove focus outlines -- override only with visible alternatives */

Focus management patterns:

  • Modal open: Focus moves to first focusable element inside modal
  • Modal close: Focus returns to the element that triggered the modal
  • Toast: Does not steal focus; announced via aria-live="polite"
  • DataGrid pagination: Focus stays on grid after page change
  • Cart item removal: Focus moves to next cart item (or previous if last)

Keyboard Navigation

ComponentKeysBehavior
ButtonEnter, SpaceActivate
ModalEscapeClose
DataGridArrow Up/DownNavigate rows
DataGridEnterSelect row / trigger action
SearchInputArrow DownOpen autocomplete
SearchInputEscapeClose autocomplete
SelectArrow Up/DownNavigate options
SelectEnterSelect option
Numpad (POS)0-9Enter digits
Numpad (POS)BackspaceClear last digit

Screen Reader Support

<!-- Button with icon only -- MUST have aria-label -->
<Button Icon="IconType.Search" aria-label="Search products" />

<!-- Loading state -- announced as status -->
<LoadingSpinner aria-label="Loading content" role="status" />

<!-- Status badge with full context -->
<StatusBadge Status="Error"
             aria-label="Order status: Error - requires attention" />

<!-- Live region for cart updates -->
<div aria-live="polite" aria-atomic="false">
    @foreach (var item in cart)
    {
        <CartItem Item="@item" />
    }
</div>

<!-- Descriptive form labels -->
<label for="qty-input">Quantity for @item.Name</label>
<input id="qty-input" type="number"
       aria-describedby="qty-help"
       min="1" max="999" />
<span id="qty-help">Enter quantity between 1 and 999</span>

Touch Target Minimums

ContextMinimum SizeRationale
POS terminal44x44pxWCAG 2.5.5 target size, touch-friendly for gloved hands
Admin portal44x44pxStandard web accessibility minimum
Mobile Raptag48x48pxMaterial Design guidance for handheld devices
Numpad buttons64x64pxFast, accurate PIN entry and quantity input

17.7 Component State Patterns

All data-driven components follow a standard state machine to ensure consistent loading, empty, and error UX:

                    ┌──────────┐
                    │          │
               ┌────│ Loading  │────┐
               │    │          │    │
               │    └──────────┘    │
               ▼                    ▼
        ┌──────────┐         ┌──────────┐
        │          │         │          │
        │Populated │         │  Empty   │
        │          │         │          │
        └──────────┘         └──────────┘
               │                    │
               ▼                    ▼
        ┌──────────┐         ┌──────────┐
        │          │         │          │
        │  Error   │────────▶│ Loading  │ (retry)
        │          │         │          │
        └──────────┘         └──────────┘

State Definitions:

StateVisualComponent
Loading<LoadingSpinner> centered in containerSpinner with optional text
PopulatedNormal content renderingData grid, cards, lists
Empty<EmptyState> with actionIcon + message + optional CTA
Error<ErrorState> with retryError icon + message + retry button

Blazor Pattern:

@if (isLoading)
{
    <LoadingSpinner Text="Loading orders..." />
}
else if (hasError)
{
    <EmptyState Icon="IconType.AlertCircle"
                Title="Failed to load orders"
                Description="@errorMessage">
        <Action>
            <Button Variant="secondary" OnClick="RetryLoad">Retry</Button>
        </Action>
    </EmptyState>
}
else if (!items.Any())
{
    <EmptyState Icon="IconType.Box"
                Title="No orders found"
                Description="Try adjusting your filters." />
}
else
{
    <DataGrid Items="@items" ... />
}

17.8 Offline Indicator Component

Purpose: Show network connectivity and sync status across all POS Client screens. This component appears in the status bar of every POS screen.

ASCII Wireframe:

ONLINE                      SYNCING                     QUEUED
┌───────────────────┐      ┌───────────────────┐      ┌───────────────────┐
│ ● Online          │      │ ◐ Syncing...      │      │ ● Queued (12)     │
└───────────────────┘      └───────────────────┘      └───────────────────┘
  Green dot                  Animated blue dot           Yellow dot + count

ERROR
┌───────────────────┐
│ ✕ Offline (!)     │
│   [Retry]         │
└───────────────────┘
  Red dot + retry

States:

StateDot ColorLabelBadgeAction
OnlineGreen“Online”NoneNone
SyncingBlue (animated)“Syncing…”NoneNone
QueuedYellow“Queued”Count of pending itemsTap to view queue
ErrorRed“Offline”Exclamation[Retry] button

Props:

PropTypeDefaultDescription
StatusConnectionStatusrequiredCurrent connection state
PendingCountint0Items awaiting sync
LastSyncAtDateTime?nullTimestamp of last successful sync
OnRetryEventCallbacknullRetry handler for error state

Blazor Usage:

<OfflineIndicator Status="@syncService.ConnectionStatus"
                  PendingCount="@syncService.PendingCount"
                  LastSyncAt="@syncService.LastSyncAt"
                  OnRetry="syncService.ForceSync" />

17.9 Summary

The Component Library provides:

  1. StatCard: Dashboard metrics with trends
  2. DataGrid: Sortable, filterable data tables
  3. StatusBadge: Color-coded status indicators
  4. SearchInput: Debounced search with autocomplete
  5. Modal: Overlay dialogs for forms and confirmations
  6. Toast: Non-blocking notifications
  7. LoadingSpinner: Loading state indicators
  8. EmptyState: Meaningful placeholders
  9. OfflineIndicator: Network status with sync queue count

All components follow:

  • Consistent design tokens
  • Responsive sizing
  • Dark mode support
  • WCAG 2.1 AA accessibility compliance
  • Standard component state patterns (Loading/Populated/Empty/Error)

Next: Part VI covers the Implementation Guide starting with Chapter 18: Development Environment.


Document Information

AttributeValue
Version5.0.0
Created2025-12-29
Updated2026-02-25
AuthorClaude Code
StatusActive
PartV - Frontend
Chapter17 of 32

This chapter is part of the POS Blueprint Book. All content is self-contained.