GFS Design System v10.0
Portal
Starter
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
02
Sidebar Layout
■ Sidebar collapses on mobile (< 768px). Use .dash-body + .dash-sidebar from shared.css for simpler layouts.
03
KPI Dashboard Row
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
| 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
←
1
2
3
...
31
→
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
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
Modal Dialog
Confirm Delete
Are you sure you want to delete order SO-24866? This action cannot be undone. All associated records, shipping labels, and invoice data will be permanently removed.
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.
08
Charts & Visualization
Monthly Revenue (2026)
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 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
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
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
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
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;">← Hub</a>
<span style="opacity:.4">—</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()">◔ 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;">← Back to Hub</a>
· GFS Design System v10.0 · Global Food Solutions, Inc.
· 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">←</div>
<div class="page-btn active">1</div>
<div class="page-btn">2</div>
<div class="page-btn">3</div>
<div class="page-btn">→</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()">×</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 Checklist
| Pass | Criterion | WCAG Ref | Notes |
|---|---|---|---|
| ✓ | Text alternatives for non-text content | 1.1.1 | All images have alt text or aria-label |
| ✓ | Captions for multimedia | 1.2.2 | Video content has synchronized captions |
| ✓ | Info and relationships conveyed programmatically | 1.3.1 | Semantic HTML, proper heading hierarchy |
| ✓ | Meaningful sequence preserved | 1.3.2 | DOM order matches visual order |
| ✓ | Sensory characteristics not sole identifier | 1.3.3 | Instructions do not rely on shape/color alone |
| ✓ | Color contrast ratio 4.5:1 for text | 1.4.3 | All token pairs verified (see table below) |
| ✓ | Color contrast ratio 3:1 for large text | 1.4.3 | Headlines and large text meet 3:1 minimum |
| ✓ | Text resize up to 200% without loss | 1.4.4 | All layouts use relative units where possible |
| ✓ | Images of text avoided | 1.4.5 | Real text used everywhere; no text-as-image |
| ✓ | Reflow: no horizontal scroll at 320px | 1.4.10 | Responsive layouts reflow content |
| ✓ | Non-text contrast 3:1 for UI components | 1.4.11 | Borders, icons, and controls meet ratio |
| ✓ | Text spacing adjustable | 1.4.12 | No content loss with increased spacing |
| ✓ | Keyboard accessible (all functionality) | 2.1.1 | All interactive elements reachable via keyboard |
| ✓ | No keyboard trap | 2.1.2 | Focus can leave all components including modals |
| ✓ | Timing adjustable | 2.2.1 | Toast notifications stay 3s minimum |
| ✓ | Pause, Stop, Hide for auto-updating | 2.2.2 | Animations respect prefers-reduced-motion |
| ✓ | Skip navigation link | 2.4.1 | "Skip to content" link on every page |
| ✓ | Page titled | 2.4.2 | Descriptive title on every page |
| ✓ | Focus order meaningful | 2.4.3 | Tab order follows visual flow |
| ✓ | Link purpose clear from text | 2.4.4 | No "click here" links; descriptive text |
| ✓ | Focus visible | 2.4.7 | Custom focus ring: 2px solid tufts blue |
| ✓ | Language of page defined | 3.1.1 | lang="en" on html element |
| ✓ | On focus: no context change | 3.2.1 | Focus does not trigger unexpected behavior |
| ✓ | Error identification | 3.3.1 | Form errors identified in text, not color alone |
| ✓ | Labels or instructions for input | 3.3.2 | All inputs have visible labels |
| ✓ | Name, role, value programmatic | 4.1.2 | ARIA attributes on custom widgets |
| ✓ | Status messages announced | 4.1.3 | aria-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)
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)
| Foreground | Background | Ratio | Normal | Large |
|---|---|---|---|---|
| --cobalt (#092F64) | --white (#FFFFFF) | 12.63:1 | AAA | AAA |
| --navy (#1A5799) | --white (#FFFFFF) | 6.48:1 | AA | AAA |
| --tufts (#468BE6) | --white (#FFFFFF) | 3.52:1 | Fail | AA |
| --graphite (#4B5F7A) | --white (#FFFFFF) | 6.14:1 | AA | AAA |
| --slate (#6B7280) | --white (#FFFFFF) | 4.64:1 | AA | AAA |
| --success (#0F766E) | --white (#FFFFFF) | 5.87:1 | AA | AAA |
| --warning (#B45309) | --white (#FFFFFF) | 4.96:1 | AA | AAA |
| --error (#DC2626) | --white (#FFFFFF) | 4.63:1 | AA | AAA |
| --steel (#9CA3AF) | --white (#FFFFFF) | 2.86:1 | Fail | Fail |
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
| Component | Key | Action |
|---|---|---|
| All | Tab | Move focus to next interactive element |
| All | Shift+Tab | Move focus to previous element |
| Button | Enter / Space | Activate the button |
| Link | Enter | Follow the link |
| Checkbox | Space | Toggle checked state |
| Radio group | Arrow Up/Down | Move between options |
| Tabs | Arrow Left/Right | Switch tabs |
| Modal | Escape | Close the modal |
| Modal | Tab | Cycle focusable elements (trapped) |
| Dropdown | Arrow Up/Down | Navigate menu items |
| Table sort | Enter | Sort by column |
| Collapsible | Enter / Space | Toggle 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
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)
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)
Please fix 3 errors:
- Company Name is required
- Email must be valid
- Region must be selected
Field-Level Inline Errors
Please enter a valid email (e.g., user@company.com).
Must be a positive number.
Toast Errors with Retry
404 -- Bad Route Within Portal
■ Always provide a clear message, explanation, and actionable CTA for every error state.
15
Data Loading Patterns
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
Breakpoint Reference Table
| Name | Width | Devices | Layout |
|---|---|---|---|
| xs | < 550px | Small phones | Single column, bottom nav |
| sm | 550-767px | Large phones | 2-col cards, stacked forms |
| md | 768-1023px | Tablets | Sidebar as drawer, 2-col grids |
| lg | 1024-1279px | Small laptops | Sidebar visible, 3-col grids |
| xl | ≥ 1280px | Desktops | Full 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)
■ Test all layouts at each breakpoint. Mobile: 44px targets, 16px inputs, stacked layouts.
17
Dark Mode Reference
Dark Mode Token Mapping (Light to Dark)
| Token | Light | Dark | Purpose |
|---|---|---|---|
| --cobalt | #092F64 | #93BFEF | Primary brand / headings |
| --navy | #1A5799 | #468BE6 | Secondary / links |
| --tufts | #468BE6 | #6BA3ED | Interactive accent |
| --alice | #E9F5FF | #0A1929 | Light accent bg |
| --black | #1F1F1F | #F0F2F5 | Primary text |
| --graphite | #4B5F7A | #9CA3AF | Body text |
| --silver | #D9E1EA | #2D2D2D | Borders |
| --fog | #F0F2F5 | #1A1A1A | Surface / headers |
| --white | #FFFFFF | #111111 | Page background |
| --success | #0F766E | #34D399 | Success states |
| --warning | #B45309 | #FBBF24 | Warning states |
| --error | #DC2626 | #F87171 | Error 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: 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 = {
'&': '&', '<': '<',
'>': '>', '"': '"',
"'": '''
};
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)
| Field | Client | Server |
|---|---|---|
| type="email" | RFC 5322, MX lookup | |
| Phone | pattern | E.164 normalization |
| Currency | step="0.01" | Decimal check, range |
| Date | type="date" | ISO 8601 validation |
| Free text | maxlength | Strip HTML, encoding |
| File | accept=".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 dataAuthentication 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 Checklist
| Item | Recommendation | Priority |
|---|---|---|
| Inline critical CSS | Above-the-fold styles in head | Critical |
| Defer non-critical CSS | rel="preload" for shared.css | Critical |
| Remove unused CSS | DevTools Coverage audit | High |
| Minify CSS | Strip comments/whitespace | High |
| Avoid @import | Use link tags | High |
Image Optimization
| Format | Use Case | Max Size |
|---|---|---|
| WebP | Photos | <100KB |
| AVIF | Progressive enhancement | <80KB |
| SVG | Icons, logos | <5KB |
| PNG | Screenshots | <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
| Attribute | Behavior | Use |
|---|---|---|
| (none) | Blocks parsing | Critical inline only |
| defer | Parallel, runs after HTML | Most scripts |
| async | Parallel, runs when ready | Analytics |
| type="module" | Deferred, strict | ES6+ modules |
Bundle Size Budget
| Asset | Budget | Current | Status |
|---|---|---|---|
| HTML (page) | <100KB | ~85KB | Pass |
| shared.css | <50KB | ~38KB | Pass |
| JS (page) | <30KB | ~8KB | Pass |
| Fonts | <150KB | ~120KB | Pass |
| Total | <400KB | ~250KB | Pass |
Lighthouse Score Targets
Performance
95+
Accessibility
100
Best Practices
100
SEO
100
| Metric | Target | Description |
|---|---|---|
| FCP | <1.0s | First Contentful Paint |
| LCP | <2.5s | Largest Contentful Paint |
| CLS | <0.1 | Layout Shift |
| TBT | <200ms | Blocking Time |
| TTI | <3.0s | Time to Interactive |
■ Zero frameworks = fast by default. Target <400KB total. Monitor Lighthouse in CI.
20
Naming Conventions & Code Standards
CSS Class Naming
| Pattern | Example | Usage |
|---|---|---|
| Component | .kpi, .modal-box | Block-level |
| Element | .kpi-label, .kpi-val | Child (hyphen) |
| Modifier | .kpi-up, .btn-primary | State/variant |
| Layout | .g2, .g3, .flex | Grid/flex |
| Spacing | .mt-4, .gap-3 | Utilities |
| State | .active, .collapsed | Interactive |
JavaScript Function Naming
| Category | Pattern | Examples |
|---|---|---|
| Handlers | handle{Event} | handleClick, handleSubmit |
| Getters | get{Thing} | getValue, getRows |
| Renderers | render{Comp} | renderTable, renderKPI |
| Toggles | toggle{Thing} | toggleTheme, toggleModal |
| Validators | validate{Thing} | validateForm, validateEmail |
| Formatters | format{Thing} | formatCurrency, formatDate |
| Booleans | is{Condition} | isEmpty, isValid |
File and ID Naming
Files (kebab-case)
✓ portal-sales-orders.html
✓ shared.css
✗ Portal_SalesOrders.html
✓ shared.css
✗ Portal_SalesOrders.html
IDs (camelCase)
✓ id="mainContent"
✓ id="dataTable"
✗ id="main-content-area"
✓ 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
| Level | Token | Use |
|---|---|---|
| Primitive | --cobalt, --navy | Raw values (rarely direct) |
| Semantic | --primary, --background | 90% of usage |
| Component | --sidebar-background | Scoped to component |
■ kebab-case files, camelCase JS/IDs, hyphen-separated CSS classes.
21
Print Styles
@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 Property | Usage |
|---|---|
| page-break-before: always | New page before element |
| page-break-after: always | New page after element |
| page-break-inside: avoid | Keep on one page |
| break-inside: avoid | Modern equivalent |
| orphans: 3; widows: 3 | Min lines per page |
Print Preview Button
■ print.css loaded with media="print". Test with Ctrl+P or DevTools emulation.
22
Animation & Transition Reference
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
| Token | Value | Use Case |
|---|---|---|
| --dur-fast | 120ms | Hover, focus, color |
| --dur-base | 200ms | Button, toggle, dropdown |
| --dur-slow | 400ms | Modal, sidebar, toast |
| --dur-slower | 600ms | Page 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
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
| Version | Date | Type | Changes |
|---|---|---|---|
| v10.0 | 2026-05-20 | Current | Added sections 13-24. Expanded sections 04, 05, 09. |
| v10.0 | 2026-05-15 | Previous | Initial 12-section portal-starter template. |
| v8.x | 2026-03-01 | Deprecated | Shadow-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
| Feature | Target | Status |
|---|---|---|
| Component playground | v9.2 | Planned |
| Design tokens JSON export | v9.2 | In Progress |
| Visual regression testing | v9.3 | Planned |
| Web Component wrappers | v10.0 | Backlog |
| RTL language support | v10.0 | Backlog |
| Multi-theme palettes | v10.0 | Backlog |
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.