← Hub Portal Starter Template
⌘K v10.0
GFS Design System v10.0
Portal
Starter
Copy-paste foundation for every GFS portal, dashboard, and internal tool.
This template demonstrates every UI pattern in the GFS design system with working examples. Each section is a self-contained reference that can be copied directly into a new project.
24 Sections Working JS Dark Mode Responsive
Document Metadata
Type Starter Template
Version 9.0
Updated 2026-05-20
Author Design Systems
Status Production
Classification Internal Use
02 Sidebar Layout Working sidebar + main content pattern for portal apps
■ Sidebar collapses on mobile (< 768px). Use .dash-body + .dash-sidebar from shared.css for simpler layouts.
03 KPI Dashboard Row Key performance indicators with sparklines and deltas
Total Revenue
$2.1M
+12.3% vs last month
Active Accounts
3,864
+8.1% vs last month
Orders Today
47
-2.1% vs yesterday
Fill Rate
96.2%
+0.4% vs last week
■ KPI grid collapses to 2-col on tablet, 1-col on mobile. Use .kpi-up / .kpi-down for delta colors.
04 Data Table Sortable, searchable table with status badges and actions
8 records
Order # ↕ Customer ↕ Amount ↕ Status ↕ Date ↕ Actions
SO-24871 Driscoll Foods $48,250.00 Shipped 2026-05-19
SO-24870 Sysco Northeast $32,180.50 Processing 2026-05-19
SO-24869 US Foods Philadelphia $27,445.00 Shipped 2026-05-18
SO-24868 Restaurant Depot NYC $19,830.75 Pending 2026-05-18
SO-24867 Baldor Specialty Foods $15,620.00 Shipped 2026-05-17
SO-24866 Jetro Cash & Carry $42,910.25 On Hold 2026-05-17
SO-24865 Performance Food Group $56,340.00 Processing 2026-05-16
SO-24864 Ben E. Keith Foods $8,775.50 Shipped 2026-05-16
Showing 1-8 of 247 results

Column Visibility Toggle
Columns:

Export Buttons

Row Expansion (Click Row to Expand Detail)
Order # Customer Amount Status
SO-24871 Driscoll Foods $48,250.00 Shipped
SO-24870 Sysco Northeast $32,180.50 Processing

Editable Cells (Click to Edit Inline)
SKU Product Price Qty on Hand
GFS-4420 Cheddar Cheese Block 40lb $142.50 1,240
GFS-5510 Mozzarella Shred 5lb $28.75 3,820
GFS-6601 Cream Cheese Brick 3lb $16.90 5,115
Click any editable cell to modify its value. Press Enter to save, Escape to cancel.

Filter Presets / Saved Filters
|
■ Click column headers to sort. Type in the filter box to search. Expand rows for detail. Editable cells support inline edit. All using vanilla JS.
05 Forms Input types, validation, and a complete form example
Individual Form Controls
Legal entity name as registered.
Drop files here or click to browse
PDF, XLSX, CSV up to 10MB
Email alerts enabled
Dairy & Cheese
Frozen Foods
Deli & Prepared
Dry Goods & Pantry
Distributor
Retailer
Food Service
Email address verified.
Please enter a valid email address.

Complete Form — New Customer

Multi-Step Wizard Form (3 Steps)
1
2
3
Company Info Contact Details Confirmation

Auto-Save Pattern
Saved

Textarea with Character Counter
0 / 200

Password Strength Meter
Enter a password to see strength.

File Upload with Drag-Drop & Preview
Drag files here or click to browse
PDF, XLSX, CSV, JPG, PNG up to 10MB
■ All form controls use shared.css classes. Wizard, auto-save, strength meter, and drag-drop all use vanilla JS. Validation runs on submit.
06 Modals & Feedback Dialogs, alerts, toasts, empty states, loading skeletons
Modal Dialog

Alert Variants
Info: New pricing schedule takes effect June 1, 2026. Review updated terms in the vendor portal.
Success: Order SO-24871 shipped via LTL carrier. Tracking number: PRO-4829174.
Warning: Inventory for SKU GFS-4420 is below minimum threshold (24 cases remaining).
Error: Payment declined for Invoice #INV-8847. Customer credit limit exceeded by $4,230.

Toast Notifications

Empty State / No Data
No orders found
Try adjusting your filters or create a new order to get started.

Loading Skeleton States
Loading data...
■ Modal uses fixed overlay with z-index 150. Toast auto-dismisses after 3 seconds.
07 Navigation Patterns Tabs, breadcrumbs, pagination, sidebar nav
Horizontal Tabs

Overview tab content. This is the default active panel showing a summary of the selected record.

This Month
$142K
Last Month
$128K
Growth
+10.9%

Details panel with account information, contact records, and delivery addresses.

Order history for the last 12 months. 247 transactions totaling $1.7M.

Account settings: pricing tier, payment terms, credit limit, and notification preferences.


Breadcrumbs

Pagination

Sidebar Navigation (Standalone)
■ Tabs use JS switching. Pagination buttons are interactive. Sidebar nav uses border-left accent.
08 Charts & Visualization Bar charts, donut charts, sparklines, progress bars
Monthly Revenue (2026)
Jan
Feb
Mar
Apr
May
Jun
Revenue by Channel
$2.1M
Distributor (36%)
Direct (22%)
Retail (21%)
Food Svc (12%)
Other (9%)

Sparklines
Dairy
Frozen
Produce
Meat

Progress Bars
Order Fulfillment 96%
Warehouse Capacity 78%
Delivery On-Time 92%
Returns Rate 4%
■ Charts are pure CSS. For interactive charts, use ECharts (see advanced-charts.html).
09 Cards & Layouts Card grids, list views, split panels, master-detail
Card Grid (3-Column)
Inventory Portal
Status
Live v3.2.1

Real-time inventory tracking across both warehouse facilities. 14,200 active SKUs.

Pricing Engine
Status
Beta v1.0.4

CME-linked commodity pricing with configurable margin rules and customer tier logic.

Fleet Tracker
Status
Maintenance v2.8.0

GPS fleet monitoring, route optimization, and cold chain temperature alerts for 47 vehicles.


List View
09:42
Order SO-24871 shipped to Driscoll Foods via Heartland Express
#SO-24871
09:38
Payment received: $32,180.50 from Sysco Northeast (ACH)
#PAY-7741
09:15
Temperature alert: Dock 3 cooler at 42F (threshold: 40F)
#ALERT-112
09:02
New customer onboarded: Ben E. Keith Foods (Distributor, TX)
#CUST-3865
08:47
Credit hold applied: Jetro Cash & Carry exceeds $50K limit
#HOLD-294

Master-Detail Pattern
Driscoll Foods
Distributor · Northeast · $1.2M YTD
Sysco Northeast
Distributor · Northeast · $890K YTD
US Foods Philadelphia
Distributor · Mid-Atlantic · $720K YTD
Restaurant Depot NYC
Cash & Carry · Northeast · $540K YTD
Performance Food Group
Distributor · National · $480K YTD
DF
Driscoll Foods
Customer #CUST-1001 · Active since 2019
YTD Revenue
$1.2M
Open Orders
12
AR Balance
$86K

Primary contact: Dave Jordan, VP Procurement. Net 30 payment terms. Preferred carrier: Heartland Express. Weekly standing order for dairy and deli products.


Kanban Board Layout
To Do 4
Update pricing matrix
Due: May 25 · High
ML
Urgent
Review Q2 vendor contracts
Due: May 28 · Medium
DJ
Medium
USDA license renewal
Due: Jun 1 · High
KR
New product photos for catalog
Due: Jun 5 · Low
TP
In Progress 2
Migrate AR reports to TBA
Started May 18 · 60%
ML
Dock operations portal v2
Started May 12 · 35%
DJ
KR
Done 3
Deploy pricing engine v1.0.4
Completed May 19
Bongard's moisture formula update
Completed May 17
System guide C-suite review
Completed May 15

Timeline / Activity Feed (Multiple Event Types)
May 20, 2026 · 09:42 AM
Order SO-24871 shipped
Shipped to Driscoll Foods via Heartland Express. PRO# 4829174. 847 cases, 18,420 lbs.
$
May 20, 2026 · 09:38 AM
Payment received: $32,180.50
ACH payment from Sysco Northeast. Applied to Invoice #INV-8841.
!
May 20, 2026 · 09:15 AM
Temperature alert: Dock 3 cooler
Temperature reading 42F exceeds threshold of 40F. Maintenance notified automatically.
+
May 20, 2026 · 09:02 AM
New customer onboarded
Ben E. Keith Foods added as Distributor (TX). Assigned to Northeast sales team.
May 20, 2026 · 08:47 AM
Credit hold applied
Jetro Cash & Carry exceeds $50K limit. AR balance: $52,430. Requires manager approval.

Comparison Layout (Side by Side)
Standard
$49/mo
Up to 500 SKUs
Basic reporting
Email support
API access
Custom dashboards
Dedicated account manager
Recommended
Enterprise
$149/mo
Unlimited SKUs
Advanced analytics
Priority support (24/7)
Full API access
Custom dashboards
Dedicated account manager

Statistics Dashboard Layout
Weekly Order Volume
Mon
Tue
Wed
Thu
Fri
Avg Order Value
$31.2K
+5.7% this week
Fulfillment Time
1.4d
-0.3d improvement
Customer Satisfaction
4.8/5
+0.2 this month
■ Kanban, timeline, comparison, and statistics layouts all use shared.css classes. Responsive grid collapses on mobile.
10 Status & Badges Badge variants, tags, avatars, avatar stacks
Badge Variants
Processing Shipped Pending On Hold Draft Info Complete Warning Critical

Tags
Dairy × Frozen × Northeast × Priority × LTL × Net 30 ×

Avatar Sizes
ML
SM (24px)
DJ
Default (32px)
KR
LG (48px)

Avatar Stacks
ML
DJ
KR
3 Members
ML
DJ
KR
TP
+5
9 Members

Status Dot Indicators
Online
Away
Offline
Unknown
■ Badges use .badge-[color]. Tags support close buttons. Avatar stacks overlap with negative margin.
11 JavaScript Patterns Working vanilla JS examples for all interactive components

All JavaScript on this page is vanilla JS with zero dependencies. Each pattern below corresponds to a working demo in the sections above. View source to see the full implementations in the script block at the bottom of this file.

Included Patterns
Pattern Function Section
Theme Toggle toggleTheme() Topbar
Section Search searchSections(q) Topbar
Table Sorting sortTable(col) 04
Table Filtering filterTable(q) 04
Form Validation validateForm(e) 05
Modal Open/Close openModal() / closeModal() 06
Toast Notifications showToast(msg, type) 06
Tab Switching switchTab(el, id) 07
Sidebar Navigation sidebarNav(el) 02
Master-Detail Select selectMaster(el, idx) 09
Radio Group Select selectRadio(el, group) 05
Copy to Clipboard copySnippet(id) 12
Collapsible Sections .s-head click handler All
■ All JS is in a single script block at page bottom. No build step. No transpilation. No frameworks.
12 Copy-Paste Snippets Ready-to-use HTML blocks for rapid portal development
Standard Page Skeleton
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>GFS Design System — [PAGE TITLE]</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;500;600;700&family=Inter:opsz,wght@14..32,100..900&display=swap" rel="stylesheet">
<link rel="stylesheet" href="shared.css">
<link rel="stylesheet" href="print.css" media="print">
</head>
<body>
<a href="#main-content" class="skip-link">Skip to content</a>
<div class="reading-progress" id="readingProgress"></div>
<div class="topbar">
  <div class="topbar-l">
    <span class="dot-live"></span>
    <a href="index.html" style="color:inherit;text-decoration:none;">&larr; Hub</a>
    <span style="opacity:.4">&mdash;</span>
    <span>[PAGE TITLE]</span>
  </div>
  <div class="topbar-r">
    <input type="text" placeholder="Search sections..."
      oninput="searchSections(this.value)"
      style="background:rgba(255,255,255,.12);border:1px solid rgba(255,255,255,.2);
      color:inherit;font:inherit;padding:3px 10px;border-radius:2px;width:160px;outline:none;">
    <span>v10.0</span>
    <button class="topbar-btn" onclick="toggleTheme()">&#9684; Mode</button>
  </div>
</div>
<div class="page" id="main-content">
  <!-- SECTIONS HERE -->
</div>
<div style="text-align:center;padding:24px;font-family:'IBM Plex Mono',monospace;
  font-size:11px;color:var(--slate);border-top:1px solid var(--silver);margin-top:32px;">
  <a href="index.html" style="color:var(--tufts);text-decoration:none;">&larr; Back to Hub</a>
  &middot; GFS Design System v10.0 &middot; Global Food Solutions, Inc.
  &middot; 131 Heartland Blvd, Edgewood, NY 11717
</div>
<script>
function toggleTheme(){
  var h=document.documentElement,d=h.getAttribute('data-theme')==='dark';
  h.setAttribute('data-theme',d?'':'dark');
  localStorage.setItem('gfs-theme',d?'light':'dark');
}
(function(){var s=localStorage.getItem('gfs-theme');
if(s==='dark')document.documentElement.setAttribute('data-theme','dark');})();
function searchSections(q){q=q.toLowerCase().trim();
document.querySelectorAll('.s').forEach(function(s){
  s.style.display=!q||(s.textContent||'').toLowerCase().includes(q)?'':'none';});}
</script>
<button class="back-to-top" id="backToTop"
  onclick="window.scrollTo({top:0,behavior:'smooth'})">
  <svg viewBox="0 0 24 24"><polyline points="18 15 12 9 6 15"/></svg>
</button>
<script src="analytics.js" async></script>
</body>
</html>
Sidebar Layout
<div class="dash-mock">
  <div class="dash-topbar">
    <span>GFS Portal</span>
    <span>M. Levine</span>
  </div>
  <div class="dash-body">
    <div class="dash-sidebar">
      <div class="dash-sidebar-item on">Dashboard</div>
      <div class="dash-sidebar-item">Orders</div>
      <div class="dash-sidebar-item">Customers</div>
      <div class="dash-sidebar-item">Reports</div>
      <div class="dash-sidebar-item">Settings</div>
    </div>
    <div class="dash-content">
      <!-- Main content here -->
    </div>
  </div>
</div>
KPI Row
<div class="kpi-grid">
  <div class="kpi">
    <div class="kpi-label">Total Revenue</div>
    <div class="kpi-val">$2.1M</div>
    <div class="kpi-delta kpi-up">+12.3% vs last month</div>
    <div class="sparkline">
      <div class="spark-bar" style="height:40%"></div>
      <div class="spark-bar" style="height:55%"></div>
      <div class="spark-bar" style="height:70%"></div>
      <div class="spark-bar" style="height:85%"></div>
      <div class="spark-bar" style="height:100%"></div>
    </div>
  </div>
  <!-- Repeat .kpi blocks as needed -->
</div>
Data Table
<table class="demo-table">
  <thead>
    <tr>
      <th>Order #</th>
      <th>Customer</th>
      <th>Amount</th>
      <th>Status</th>
      <th>Date</th>
      <th>Actions</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td class="mono">SO-24871</td>
      <td>Driscoll Foods</td>
      <td class="mono">$48,250.00</td>
      <td><span class="badge badge-green">Shipped</span></td>
      <td class="mono">2026-05-19</td>
      <td>
        <button class="btn btn-ghost btn-sm">View</button>
        <button class="btn btn-ghost btn-sm">Edit</button>
      </td>
    </tr>
  </tbody>
</table>
<div class="pagination" style="margin-top:var(--sp-4);">
  <div class="page-btn">&larr;</div>
  <div class="page-btn active">1</div>
  <div class="page-btn">2</div>
  <div class="page-btn">3</div>
  <div class="page-btn">&rarr;</div>
</div>
Form Group
<div class="form-group">
  <label class="form-label" for="field-name">Field Label</label>
  <input type="text" class="input" id="field-name" placeholder="Enter value...">
  <div class="form-hint">Helper text for this field.</div>
</div>

<div class="form-group">
  <label class="form-label">Select</label>
  <select class="input select">
    <option>Option 1</option>
    <option>Option 2</option>
  </select>
</div>

<div class="form-group">
  <label class="form-label">Toggle</label>
  <div class="toggle active" onclick="this.classList.toggle('active')"></div>
</div>

<div class="form-group">
  <label class="form-label">Checkbox</label>
  <div style="display:flex;align-items:center;gap:var(--sp-3);">
    <div class="check-box checked" onclick="this.classList.toggle('checked')"></div>
    <span>Label text</span>
  </div>
</div>
Modal Dialog
<!-- Trigger -->
<button class="btn btn-primary" onclick="openModal()">Open Modal</button>

<!-- Overlay -->
<div class="modal-overlay" id="modal">
  <div class="modal-box">
    <div class="modal-header">
      Modal Title
      <span class="modal-close" onclick="closeModal()">&times;</span>
    </div>
    <div class="modal-body">
      Modal content goes here.
    </div>
    <div class="modal-footer">
      <button class="btn btn-ghost" onclick="closeModal()">Cancel</button>
      <button class="btn btn-primary">Confirm</button>
    </div>
  </div>
</div>

<script>
function openModal(){document.getElementById('modal').classList.add('show');}
function closeModal(){document.getElementById('modal').classList.remove('show');}
</script>
■ Click "Copy" to copy any snippet to clipboard. Paste directly into a new GFS portal file.
13 Accessibility Checklist WCAG 2.1 AA compliance reference, focus management, screen reader support
WCAG 2.1 AA Compliance Checklist
PassCriterionWCAG RefNotes
Text alternatives for non-text content1.1.1All images have alt text or aria-label
Captions for multimedia1.2.2Video content has synchronized captions
Info and relationships conveyed programmatically1.3.1Semantic HTML, proper heading hierarchy
Meaningful sequence preserved1.3.2DOM order matches visual order
Sensory characteristics not sole identifier1.3.3Instructions do not rely on shape/color alone
Color contrast ratio 4.5:1 for text1.4.3All token pairs verified (see table below)
Color contrast ratio 3:1 for large text1.4.3Headlines and large text meet 3:1 minimum
Text resize up to 200% without loss1.4.4All layouts use relative units where possible
Images of text avoided1.4.5Real text used everywhere; no text-as-image
Reflow: no horizontal scroll at 320px1.4.10Responsive layouts reflow content
Non-text contrast 3:1 for UI components1.4.11Borders, icons, and controls meet ratio
Text spacing adjustable1.4.12No content loss with increased spacing
Keyboard accessible (all functionality)2.1.1All interactive elements reachable via keyboard
No keyboard trap2.1.2Focus can leave all components including modals
Timing adjustable2.2.1Toast notifications stay 3s minimum
Pause, Stop, Hide for auto-updating2.2.2Animations respect prefers-reduced-motion
Skip navigation link2.4.1"Skip to content" link on every page
Page titled2.4.2Descriptive title on every page
Focus order meaningful2.4.3Tab order follows visual flow
Link purpose clear from text2.4.4No "click here" links; descriptive text
Focus visible2.4.7Custom focus ring: 2px solid tufts blue
Language of page defined3.1.1lang="en" on html element
On focus: no context change3.2.1Focus does not trigger unexpected behavior
Error identification3.3.1Form errors identified in text, not color alone
Labels or instructions for input3.3.2All inputs have visible labels
Name, role, value programmatic4.1.2ARIA attributes on custom widgets
Status messages announced4.1.3aria-live regions for toast/alert updates

Focus Management Patterns
Skip Link (present on this page)
Skip Link HTML
<a href="#main-content" class="skip-link">Skip to content</a>
Focus Trap for Modals
Focus Trap Pattern
function trapFocus(modal) {
  var focusable = modal.querySelectorAll(
    'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
  );
  var first = focusable[0];
  var last = focusable[focusable.length - 1];
  first.focus();
  modal.addEventListener('keydown', function(e) {
    if (e.key !== 'Tab') return;
    if (e.shiftKey && document.activeElement === first) {
      e.preventDefault(); last.focus();
    } else if (!e.shiftKey && document.activeElement === last) {
      e.preventDefault(); first.focus();
    }
  });
}
Tab Order Reference
Natural tab order (default): Elements focused in DOM order. Never use tabindex > 0.

tabindex="0" -- Add to natural tab order (custom interactive elements)

tabindex="-1" -- Programmatically focusable only (managed focus)
Screen Reader Announcements
aria-live Region
<div aria-live="polite" id="statusRegion"></div>
<div aria-live="assertive" id="alertRegion"></div>

<script>
document.getElementById('statusRegion')
  .textContent = '8 results found';
</script>

Color Contrast Requirements (GFS Token Pairs)
ForegroundBackgroundRatioNormalLarge
--cobalt (#092F64)--white (#FFFFFF)12.63:1AAAAAA
--navy (#1A5799)--white (#FFFFFF)6.48:1AAAAA
--tufts (#468BE6)--white (#FFFFFF)3.52:1FailAA
--graphite (#4B5F7A)--white (#FFFFFF)6.14:1AAAAA
--slate (#6B7280)--white (#FFFFFF)4.64:1AAAAA
--success (#0F766E)--white (#FFFFFF)5.87:1AAAAA
--warning (#B45309)--white (#FFFFFF)4.96:1AAAAA
--error (#DC2626)--white (#FFFFFF)4.63:1AAAAA
--steel (#9CA3AF)--white (#FFFFFF)2.86:1FailFail
Note: --tufts on white and --steel on white fail AA for normal text. Use --tufts only for large text/icons. Use --steel only for decorative text.

Keyboard Navigation Reference
ComponentKeyAction
AllTabMove focus to next interactive element
AllShift+TabMove focus to previous element
ButtonEnter / SpaceActivate the button
LinkEnterFollow the link
CheckboxSpaceToggle checked state
Radio groupArrow Up/DownMove between options
TabsArrow Left/RightSwitch tabs
ModalEscapeClose the modal
ModalTabCycle focusable elements (trapped)
DropdownArrow Up/DownNavigate menu items
Table sortEnterSort by column
CollapsibleEnter / SpaceToggle open/closed

Form Accessibility Best Practices
Labels and Descriptions
<label for="email">Email Address</label>
<input id="email" type="email"
  aria-describedby="email-hint email-error">
<div id="email-hint">We will not share your email.</div>
<div id="email-error" role="alert">
  Please enter a valid email.
</div>

<label for="name">
  Full Name <span aria-label="required">*</span>
</label>
<input id="name" required aria-required="true">
ARIA on Custom Widgets
<div role="switch"
  aria-checked="true"
  aria-label="Email notifications"
  tabindex="0">
</div>

<button aria-expanded="false"
  aria-controls="panel-1">
  Details
</button>
<div id="panel-1" role="region"
  hidden>
  Content here
</div>

Live Demo: aria-live Announcements
aria-live="polite"
aria-live="assertive"
■ All GFS portals must meet WCAG 2.1 AA. Test with keyboard-only navigation + screen reader.
14 Error Handling & Empty States Every error pattern, empty state, and edge case design
Empty States
No orders found
Your order list is empty. Create your first order to get started.
🔍
No search results
No records match "xyz123". Try different keywords or clear your filters.
🔒
No permission
You don't have access to this resource. Contact your administrator.
🎉
Welcome to your dashboard
This is your first time here. Start by setting up your profile.
Something went wrong
An unexpected error occurred while loading this page. Our team has been notified.

Error Boundary Pattern (JavaScript Failure)
Component failed to load
TypeError: Cannot read properties of undefined (reading 'data')
at renderDashboard (portal.js:142:8)

Network Error States
You're offline
Check your network connection.
Request timed out
Server took too long to respond.
500
Server error
Internal server error. Team notified.

Validation Error Summary (Top-of-Form)
Field-Level Inline Errors

Toast Errors with Retry

404 -- Bad Route Within Portal
404
Page not found
The page /portal/orders/xyz does not exist.
Hub
■ Always provide a clear message, explanation, and actionable CTA for every error state.
15 Data Loading Patterns Skeleton screens, spinners, progressive loading, optimistic UI
Skeleton Screens for Every Component
Table Skeleton
Card Skeleton
KPI Skeleton
Chart Skeleton
Form Skeleton
Sidebar Skeleton

Spinner Variants
Loading...
Inline
Small
Large
Button Loading

Progressive Loading (Header First)
Sales Dashboard
Last updated: just now

Optimistic UI Pattern
Click "Mark Complete" -- item updates immediately, simulates server response.
Review vendor contract
Update Q3 pricing
Send AR reconciliation

Infinite Scroll Pattern
Row 1
Order SO-24871 -- Driscoll Foods
Row 2
Order SO-24870 -- Sysco Northeast
Row 3
Order SO-24869 -- US Foods
Row 4
Order SO-24868 -- Restaurant Depot
Row 5
Order SO-24867 -- Baldor Foods
Row 6
Order SO-24866 -- Jetro Cash
Row 7
Order SO-24865 -- Performance FG
Row 8
Order SO-24864 -- Ben E. Keith
Scroll to bottom to load more rows.

Refresh / Polling Indicator
Live · Auto-refresh 30s
Syncing...
Synced 2 min ago
■ Skeletons for initial load, spinners for actions, optimistic UI for instant feedback.
16 Responsive & Mobile Patterns Breakpoints, mobile navigation, touch targets, responsive tables
Breakpoint Reference Table
NameWidthDevicesLayout
xs< 550pxSmall phonesSingle column, bottom nav
sm550-767pxLarge phones2-col cards, stacked forms
md768-1023pxTabletsSidebar as drawer, 2-col grids
lg1024-1279pxSmall laptopsSidebar visible, 3-col grids
xl≥ 1280pxDesktopsFull layout, 4-col KPIs

Sidebar Behavior Across Breakpoints
Desktop
Persistent sidebar
Tablet
Hamburger drawer
Mobile
Bottom nav bar

Touch Target Sizes (Minimum 44x44px)
44px Pass
32px Fail
48px Pass

Mobile Form Pattern (16px font, 44px height)
16px prevents iOS zoom. 44px meets touch target minimum.

Bottom Sheet Pattern (Mobile Actions)
Page content (dimmed)
Actions
■ Test all layouts at each breakpoint. Mobile: 44px targets, 16px inputs, stacked layouts.
17 Dark Mode Reference Complete token mapping, component comparisons, testing checklist
Dark Mode Token Mapping (Light to Dark)
TokenLightDarkPurpose
--cobalt#092F64#93BFEFPrimary brand / headings
--navy#1A5799#468BE6Secondary / links
--tufts#468BE6#6BA3EDInteractive accent
--alice#E9F5FF#0A1929Light accent bg
--black#1F1F1F#F0F2F5Primary text
--graphite#4B5F7A#9CA3AFBody text
--silver#D9E1EA#2D2D2DBorders
--fog#F0F2F5#1A1A1ASurface / headers
--white#FFFFFF#111111Page background
--success#0F766E#34D399Success states
--warning#B45309#FBBF24Warning states
--error#DC2626#F87171Error states

Component Comparison
LIGHT MODE
Revenue
$2.1M
+12.3%
Header row
Driscoll · $48K
DARK MODE
Revenue
$2.1M
+12.3%
Header row
Driscoll · $48K

Dark Mode Testing Checklist
All text meets 4.5:1 contrast on dark backgrounds
Borders visible but not harsh
Badges readable on dark surfaces
Charts and sparklines visible
Skeleton animations visible
Focus rings clearly visible
Modal overlays dim content sufficiently
Form inputs have clear borders
Theme persists via localStorage
■ Dark mode via [data-theme="dark"]. All tokens auto-swap. Test every component in both modes.
18 Security Patterns XSS prevention, CSRF, CSP, input sanitization, auth flow UI
XSS Prevention: Safe DOM Methods
Safe DOM Manipulation
// SAFE: textContent for user data
var el = document.createElement('div');
el.textContent = userInput;  // auto-escaped
container.appendChild(el);

// SAFE: setAttribute for attributes
link.setAttribute('href', sanitizedUrl);

// SAFE: createElement for structure
function renderRow(data) {
  var tr = document.createElement('tr');
  var td = document.createElement('td');
  td.textContent = data.name;
  tr.appendChild(td);
  return tr;
}
Input Sanitization
// Escape HTML entities
function escapeHtml(str) {
  var map = {
    '&': '&amp;', '<': '&lt;',
    '>': '&gt;', '"': '&quot;',
    "'": '&#39;'
  };
  return str.replace(/[&<>"']/g,
    function(c) { return map[c]; });
}

// Validate URL protocol
function isSafeUrl(url) {
  try {
    var parsed = new URL(url, location.origin);
    return ['http:','https:'].includes(
      parsed.protocol);
  } catch(e) { return false; }
}

CSRF Token Pattern
CSRF in Forms and Fetch
<meta name="csrf-token" content="abc123">

<form method="POST">
  <input type="hidden" name="_csrf" value="abc123">
</form>

var token = document.querySelector(
  'meta[name="csrf-token"]').content;
fetch('/api', {
  headers: { 'X-CSRF-Token': token }
});

Content Security Policy
CSP Header
Content-Security-Policy:
  default-src 'self';
  script-src 'self' https://cdn.jsdelivr.net;
  style-src 'self' 'unsafe-inline' https://fonts.googleapis.com;
  font-src 'self' https://fonts.gstatic.com;
  img-src 'self' data: https:;
  connect-src 'self' https://api.globalfoodsolutions.co;
  frame-ancestors 'none';

Input Validation (Client + Server)
FieldClientServer
Emailtype="email"RFC 5322, MX lookup
PhonepatternE.164 normalization
Currencystep="0.01"Decimal check, range
Datetype="date"ISO 8601 validation
Free textmaxlengthStrip HTML, encoding
Fileaccept=".pdf"MIME check, virus scan

Secure Storage Rules
Cookie Flags
Set-Cookie: session=abc;
  HttpOnly; Secure;
  SameSite=Lax;
  Max-Age=3600;
localStorage
// OK: UI preferences
localStorage.setItem('gfs-theme','dark');

// NEVER store:
// session tokens, API keys,
// passwords, PII, financial data

Authentication Flow UI
1. Login
2. MFA
3. Dashboard
4. Timeout
5:00 remaining
■ Use textContent for user data. Validate on server. CSP headers. Never store tokens in localStorage.
19 Performance Best Practices Critical CSS, images, fonts, scripts, Lighthouse targets
Critical CSS Checklist
ItemRecommendationPriority
Inline critical CSSAbove-the-fold styles in headCritical
Defer non-critical CSSrel="preload" for shared.cssCritical
Remove unused CSSDevTools Coverage auditHigh
Minify CSSStrip comments/whitespaceHigh
Avoid @importUse link tagsHigh

Image Optimization
FormatUse CaseMax Size
WebPPhotos<100KB
AVIFProgressive enhancement<80KB
SVGIcons, logos<5KB
PNGScreenshots<200KB

Font Loading
Optimal Font Loading
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="...&display=swap" rel="stylesheet">

Script Loading
AttributeBehaviorUse
(none)Blocks parsingCritical inline only
deferParallel, runs after HTMLMost scripts
asyncParallel, runs when readyAnalytics
type="module"Deferred, strictES6+ modules

Bundle Size Budget
AssetBudgetCurrentStatus
HTML (page)<100KB~85KBPass
shared.css<50KB~38KBPass
JS (page)<30KB~8KBPass
Fonts<150KB~120KBPass
Total<400KB~250KBPass

Lighthouse Score Targets
Performance
95+
Accessibility
100
Best Practices
100
SEO
100
MetricTargetDescription
FCP<1.0sFirst Contentful Paint
LCP<2.5sLargest Contentful Paint
CLS<0.1Layout Shift
TBT<200msBlocking Time
TTI<3.0sTime to Interactive
■ Zero frameworks = fast by default. Target <400KB total. Monitor Lighthouse in CI.
20 Naming Conventions & Code Standards CSS, JavaScript, file naming, data attributes, variable hierarchy
CSS Class Naming
PatternExampleUsage
Component.kpi, .modal-boxBlock-level
Element.kpi-label, .kpi-valChild (hyphen)
Modifier.kpi-up, .btn-primaryState/variant
Layout.g2, .g3, .flexGrid/flex
Spacing.mt-4, .gap-3Utilities
State.active, .collapsedInteractive

JavaScript Function Naming
CategoryPatternExamples
Handlershandle{Event}handleClick, handleSubmit
Gettersget{Thing}getValue, getRows
Renderersrender{Comp}renderTable, renderKPI
Togglestoggle{Thing}toggleTheme, toggleModal
Validatorsvalidate{Thing}validateForm, validateEmail
Formattersformat{Thing}formatCurrency, formatDate
Booleansis{Condition}isEmpty, isValid

File and ID Naming
Files (kebab-case)
portal-sales-orders.html
shared.css
Portal_SalesOrders.html
IDs (camelCase)
id="mainContent"
id="dataTable"
id="main-content-area"

Data Attribute Convention
data-{context}-{property}
<div data-page="dashboard">
<tr data-order-id="SO-24871">
<div data-theme="dark">
<button data-action="delete">
<div data-sort-dir="asc">

Color Variable Hierarchy
LevelTokenUse
Primitive--cobalt, --navyRaw values (rarely direct)
Semantic--primary, --background90% of usage
Component--sidebar-backgroundScoped to component
■ kebab-case files, camelCase JS/IDs, hyphen-separated CSS classes.
21 Print Styles @media print reference, page breaks, print button
@media print Stylesheet
print.css
@media print {
  .topbar, .sidebar-nav, .back-to-top,
  .reading-progress, .toast-container,
  .modal-overlay, .skip-link, .btn,
  .filter-bar, .pagination, .tabs
  { display: none !important; }

  @page { margin: 1.5cm; size: A4; }
  body { color: #000 !important;
    background: #fff !important; font-size: 12pt; }
  .s { page-break-inside: avoid; }
  thead { display: table-header-group; }
  tr { page-break-inside: avoid; }
  a[href]::after { content: " (" attr(href) ")";
    font-size: 10pt; color: #666; }
  a[href^="#"]::after { content: ""; }
  .page::before { display: none; }
}

Page Break Controls
CSS PropertyUsage
page-break-before: alwaysNew page before element
page-break-after: alwaysNew page after element
page-break-inside: avoidKeep on one page
break-inside: avoidModern equivalent
orphans: 3; widows: 3Min lines per page

Print Preview Button
■ print.css loaded with media="print". Test with Ctrl+P or DevTools emulation.
22 Animation & Transition Reference Easing curves, durations, micro-interactions, reduced motion
GFS Easing Curves
--ease-out
cubic-bezier(0.16, 1, 0.3, 1)
Primary. Modals, dropdowns, toasts.
--ease-in-out
cubic-bezier(0.65, 0, 0.35, 1)
Symmetric. Loading bars, progress.
--spring
cubic-bezier(0.34, 1.56, 0.64, 1)
Bouncy. Toggles, checkboxes.

Duration Tokens
TokenValueUse Case
--dur-fast120msHover, focus, color
--dur-base200msButton, toggle, dropdown
--dur-slow400msModal, sidebar, toast
--dur-slower600msPage transition, shimmer

Micro-Interaction Demos
Button Press
Toggle
Checkbox
Hover Card
Card Lift

Reduced Motion
@prefers-reduced-motion
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
  .spinner { animation: none; }
  .skeleton { animation: none; }
}
/* JS check: */
var reduced = window.matchMedia(
  '(prefers-reduced-motion: reduce)').matches;
WCAG 2.3.3 requires respecting user motion preferences. Wrap non-essential animations in prefers-reduced-motion.
■ --ease-out for most, --spring for playful, always respect prefers-reduced-motion.
23 Complete Do/Don't Guide 30+ do/don't pairs: typography, color, spacing, buttons, tables, forms, layout
Typography

Do

Use Inter for body and headings
Use IBM Plex Mono for labels, data, code
Maintain heading hierarchy (h1 > h2 > h3)
Use --text-* tokens for all sizes
Set line-height 1.6 body, 1.1 headings

Don't

Mix more than 2 fonts on one page
Use arbitrary px for font sizes
Skip heading levels
Use font-weight below 400 for body
Set text smaller than 11px

Colors

Do

Use CSS custom property tokens
Use semantic tokens (--primary, --error)
Ensure 4.5:1 contrast for all text
Test every combo in light and dark mode
Use --success for positive, --error for negative

Don't

Hardcode hex values
Use color as only indicator
Use --tufts for small text on white (fails)
Create new colors outside the palette
Use opacity to darken

Spacing

Do

Use --sp-* tokens for all spacing
Use consistent gap values (4px scale)
Use mt-4, mb-4, gap-3 utilities
Group related elements with smaller gaps
Separate sections with --sp-8+

Don't

Use arbitrary pixels (padding: 13px)
Mix spacing systems randomly
Remove all spacing
Use negative margins for layout
Use margin:0 auto (use flex/grid)

Buttons

Do

One primary button per section
btn-ghost for secondary actions
btn-danger for destructive actions
Loading state for async operations
btn-sm in tables and compact UIs

Don't

Multiple primary buttons in same row
Buttons for navigation (use links)
Disable without explanation
Custom styles outside the system
Buttons that look like links

Tables

Do

Hover striping for 10+ rows
Right-align currency/numbers
Left-align text columns
Include sort indicators
Show record count + pagination

Don't

Center-align data columns
More than 8-10 columns
Hide header on scroll
H-scroll as only mobile strategy
Mix badge colors arbitrarily

Forms

Do

Labels above inputs
Mark required with * and aria-required
Inline errors below the field
Proper input types (email, tel, number)
Group related fields

Don't

Placeholder as only label
All-red for errors
Auto-clear fields on error
Require unnecessary fields
Custom dropdowns losing a11y

Layout

Do

Max-width containers for text
CSS Grid for 2D layouts
Flexbox for 1D alignment
minmax(0,1fr) for equal columns
Collapse to 1-col on mobile

Don't

Full viewport width text
Float for layout
Fixed heights breaking with content
position:absolute for layout
H-scroll on desktop

Dark Mode

Do

Test all states (hover, focus, active)
CSS custom properties for all colors
Check contrast in dark mode separately
Darker shadows with higher opacity
Persist theme in localStorage

Don't

Opacity to darken (reduces contrast)
Invert images
Pure white text on dark bg
Forget skeleton states in dark
Hardcode light colors inline

Performance

Do

Lazy-load images (loading="lazy")
defer/async on scripts
Preconnect to font origins
Width/height on images (prevent CLS)
Vanilla JS, zero frameworks

Don't

Inline large SVGs (>5KB)
Load all data on init
Import entire libs for one fn
Animate width/height/top/left
Blocking scripts in head

Accessibility

Do

Skip-to-content on every page
Semantic HTML (button, a, label)
Test keyboard-only navigation
aria-label on icon-only buttons
aria-live for dynamic updates

Don't

div/span for clickable elements
Remove focus outlines
Rely on color alone
tabindex > 0
Auto-play audio/video
■ 30+ rules across 9 categories. Review before every code review.
24 Version History & Changelog Release history, migration notes, deprecation warnings, roadmap
Release History
VersionDateTypeChanges
v10.02026-05-20CurrentAdded sections 13-24. Expanded sections 04, 05, 09.
v10.02026-05-15PreviousInitial 12-section portal-starter template.
v8.x2026-03-01DeprecatedShadow-based cards, old palette, no dark mode.

v10.0 to v10.0 Changes
Added
  • Section 13: WCAG 2.1 AA checklist (27 items), contrast table, keyboard ref
  • Section 14: 5 empty states, error boundary, network errors, validation summary
  • Section 15: 6 skeleton types, spinners, optimistic UI, infinite scroll
  • Section 16: Breakpoints, responsive sidebar, touch targets, bottom sheet
  • Section 17: Dark mode tokens, component comparison, testing checklist
  • Section 18: XSS prevention, CSRF, CSP, auth flow UI
  • Section 19: Image optimization, font loading, bundle budget, Lighthouse
  • Section 20: CSS/JS/file naming conventions, data attributes
  • Section 21: Print stylesheet, page breaks
  • Section 22: Easing curves, durations, micro-interactions, reduced motion
  • Section 23: 30+ do/don't pairs across 9 categories
  • Section 24: Changelog and roadmap
  • Section 04: Column toggle, exports, row expansion, editable cells, filter presets
  • Section 05: Wizard form, conditional fields, auto-save, char counter, password strength, drag-drop
  • Section 09: Kanban board, timeline, comparison layout, statistics dashboard

Deprecation Warnings
  • .card -- Use .app-card or .kpi. Removed in v10.
  • box-shadow on cards -- v9 uses flat borders.
  • document.execCommand('copy') -- Fallback only. Use navigator.clipboard.

Future Roadmap
FeatureTargetStatus
Component playgroundv9.2Planned
Design tokens JSON exportv9.2In Progress
Visual regression testingv9.3Planned
Web Component wrappersv10.0Backlog
RTL language supportv10.0Backlog
Multi-theme palettesv10.0Backlog

Migration (v8 to v9)
Breaking:
  • box-shadow removed from cards
  • Color palette updated
  • border-radius: 8px changed to 2-4px
  • Fonts: Inter + IBM Plex Mono
  • .row/.col renamed to .g2/.g3/.g4
  • Dark mode: all colors must use tokens
■ Check changelog before updating. Review deprecation warnings when auditing code.
ESC
← Back to Hub · GFS Design System v10.0 · Global Food Solutions, Inc. · 131 Heartland Blvd, Edgewood, NY 11717 · (877) 728-5550