Expanded from testing-focused guide to comprehensive UX implementation guide covering all UX work from last 2 days: Part 1: Common Platform UI Integration - Product Adapter Pattern with type-safe extensions - Centralized import point for all UI components - Product-specific variants without modifying common platform Part 2: Design Token Usage - CSS variable integration patterns - Component token patterns - Token migration checklist Part 3: Component Normalization - Badge normalization (commit:94ce743) - Alert banner unification (commit:d4846b7) - Table controls standardization - Control normalization pattern with 5-step process Part 4: Accessibility Improvements - Keyboard navigation patterns (commits:3c52593,a65d726) - ARIA labels for buttons, status indicators, forms - Focus indicators and focus order Part 5: Responsive Design - Shell breakpoints (commit:c51544a) - Viewport matrix testing - Mobile-first patterns Part 6: Testing Infrastructure - Playwright setup (commit:8db23bd) - Test suite structure (commit:79f0021) - Test runner with server lifecycle (commits:df7c57e,a572293,2bd38e1) - AI-friendly report generation - Storybook setup Part 7: Cipher Design System Integration - Design principles: hierarchy, spacing, typography, color, accessibility - Component patterns: cards, buttons, alerts - WCAG AA compliance Part 8: Implementation Roadmap - 10-week phased approach - Clear verification commands at each phase - Commit references for specific patterns Part 9: Verification Commands - UI audit, type checking, build verification - Test execution commands - Storybook commands Part 10: Troubleshooting - Common issues and solutions - Playwright, design tokens, component imports Part 11: Best Practices - Component development, code organization - Testing strategy, documentation Part 12: References - Common platform, Cipher design system, testing tools This guide enables other products to replicate the exact same UX implementation approach with 40+ commits of UX work documented.
31 KiB
UX Implementation Guide
Purpose: Comprehensive guide for UX implementation across ByteLyst products using common platform UI packages, design tokens, and Cipher design system integration.
Target Audience: Product teams implementing UX improvements and testing infrastructure.
Overview
This guide documents the complete UX implementation approach for the trading dashboard, which can be replicated across all ByteLyst products. The approach covers:
- Common Platform UI Integration - Leveraging
@bytelyst/uishared primitives - Design Token Usage - Using
@bytelyst/design-tokensfor consistent styling - Component Normalization - Replacing one-off components with shared primitives
- Accessibility Improvements - Keyboard navigation, ARIA labels, focus management
- Responsive Design - Viewport matrix testing, shell breakpoints
- Testing Infrastructure - Playwright E2E tests, Storybook, AI-friendly reports
- Cipher Design System - Visual hierarchy, spacing, typography principles
Part 1: Common Platform UI Integration
Product Adapter Pattern
Create a product adapter to normalize imports and extend shared primitives with product-specific variants:
File: web/src/components/ui/Primitives.tsx
import * as React from 'react';
import {
Badge as CommonBadge,
Button as CommonButton,
Field,
FieldContent,
FieldDescription,
FieldError,
FieldGroup,
FieldLabel,
FieldTitle,
Input as CommonInput,
Select as CommonSelect,
Textarea as CommonTextarea,
type BadgeProps as CommonBadgeProps,
type ButtonProps as CommonButtonProps,
type InputProps as CommonInputProps,
type SelectProps as CommonSelectProps,
type TextareaProps as CommonTextareaProps,
} from '@bytelyst/ui';
// Re-export all shared primitives
export {
ActionMenu,
AlertBanner,
DataList,
DataTable,
Drawer,
EmptyState,
EntityCard,
FieldGrid,
FilterBar,
FormSection,
MetricCard,
Modal,
PageHeader,
Panel,
Skeleton,
Timeline,
Toolbar,
// ... all other @bytelyst/ui components
} from '@bytelyst/ui';
// Define product-specific variants
type ProductButtonVariant = NonNullable<CommonButtonProps['variant']> | 'link';
type ProductButtonSize = NonNullable<CommonButtonProps['size']> | 'icon';
type ProductFieldVariant = 'surface' | 'muted';
type ProductFieldSize = 'sm' | 'md';
type ProductBadgeVariant = NonNullable<CommonBadgeProps['variant']> | 'danger';
type ProductStatusTone = 'success' | 'warning' | 'error' | 'info' | 'neutral';
// Extend interfaces with product-specific props
export interface ButtonProps extends Omit<CommonButtonProps, 'variant' | 'size'> {
variant?: ProductButtonVariant;
size?: ProductButtonSize;
}
export interface IconButtonProps extends Omit<ButtonProps, 'children'> {
icon: React.ReactNode;
label: string;
}
export interface InputProps extends CommonInputProps {
controlSize?: ProductFieldSize;
variant?: ProductFieldVariant;
}
// Product status mapping for badges
export type ProductStatus =
| 'active' | 'approved' | 'blocked' | 'buy' | 'cancelled'
| 'connected' | 'danger' | 'degraded' | 'disabled' | 'error'
| 'failed' | 'idle' | 'info' | 'live' | 'neutral' | 'off'
| 'ok' | 'paper' | 'pending' | 'rejected' | 'sell' | 'success'
| 'synced' | 'warning';
const productStatusTone: Record<ProductStatus, ProductStatusTone> = {
active: 'success',
approved: 'success',
blocked: 'error',
buy: 'success',
cancelled: 'neutral',
connected: 'success',
danger: 'error',
degraded: 'warning',
disabled: 'neutral',
error: 'error',
failed: 'error',
idle: 'neutral',
info: 'info',
live: 'warning',
neutral: 'neutral',
off: 'neutral',
ok: 'success',
paper: 'info',
pending: 'warning',
rejected: 'error',
sell: 'error',
success: 'success',
synced: 'success',
warning: 'warning',
};
// Helper function to map product status to tone
export function statusToneFor(status: ProductStatus | string | null | undefined): ProductStatusTone {
if (!status) return 'neutral';
const normalized = status.trim().toLowerCase().replace(/[\s_]+/g, '-') as ProductStatus;
return productStatusTone[normalized] ?? 'neutral';
}
// Product-specific component implementations
export const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
({ variant = 'primary', size = 'md', className, ...props }, ref) => (
<CommonButton
ref={ref}
variant={variant === 'link' ? 'ghost' : variant}
size={size === 'icon' ? 'sm' : size}
className={cn('product-button', className)}
{...props}
/>
),
);
export const IconButton = React.forwardRef<HTMLButtonElement, IconButtonProps>(
({ icon, label, variant = 'ghost', size = 'icon', className, ...props }, ref) => (
<Button
ref={ref}
type="button"
aria-label={label}
variant={variant}
size={size}
className={cn('shrink-0', className)}
{...props}
>
{icon}
</Button>
),
);
export const Input = React.forwardRef<HTMLInputElement, InputProps>(
({ controlSize = 'md', variant = 'surface', className, ...props }, ref) => (
<CommonInput
ref={ref}
className={cn(
controlSize === 'sm' ? 'min-h-9 px-3 py-2 text-xs' : 'min-h-11 px-3.5 py-2.5 text-sm',
variant === 'surface' ? 'bg-[var(--bl-input)]' : 'bg-[var(--bl-surface-muted)]',
className,
)}
{...props}
/>
),
);
export function ProductStatusBadge({
status,
children,
}: {
status: ProductStatus | string | null | undefined;
children?: React.ReactNode;
}) {
return (
<Badge variant={statusToneFor(status)} dot>
{children ?? status ?? 'Unknown'}
</Badge>
);
}
Benefits of Product Adapter Pattern:
- Centralized import point for all UI components
- Product-specific variants without modifying common platform
- Consistent styling across the application
- Easy to migrate to new common platform versions
- Type-safe extensions with TypeScript
Part 2: Design Token Usage
CSS Variable Integration
Use design tokens from @bytelyst/design-tokens via CSS custom properties:
/* Surface colors */
background: var(--bl-surface-card);
background: var(--bl-surface-muted);
background: var(--bl-surface-overlay);
/* Text colors */
color: var(--bl-text-primary);
color: var(--bl-text-secondary);
color: var(--bl-text-muted);
/* Border colors */
border-color: var(--bl-border);
border-color: var(--bl-border-muted);
/* Input styling */
background: var(--bl-input);
color: var(--bl-text-primary);
border-color: var(--bl-border);
/* Focus states */
border-color: var(--bl-focus-ring);
box-shadow: 0 0 0 2px var(--bl-focus-ring-muted);
/* Semantic colors */
color: var(--bl-accent);
background: var(--bl-success);
background: var(--bl-warning);
background: var(--bl-error);
/* Spacing */
padding: var(--bl-spacing-sm);
padding: var(--bl-spacing-md);
padding: var(--bl-spacing-lg);
padding: var(--bl-spacing-xl);
/* Border radius */
border-radius: var(--bl-radius-control);
border-radius: var(--bl-radius-md);
border-radius: var(--bl-radius-lg);
/* Typography */
font-size: var(--bl-text-sm);
font-size: var(--bl-text-base);
font-size: var(--bl-text-lg);
font-weight: var(--bl-font-medium);
font-weight: var(--bl-font-semibold);
Component Token Patterns
Example: Badge Component
// Using design tokens for badge styling
const badgeStyles = {
success: {
background: 'var(--bl-success-light)',
color: 'var(--bl-success-dark)',
borderColor: 'var(--bl-success)',
},
warning: {
background: 'var(--bl-warning-light)',
color: 'var(--bl-warning-dark)',
borderColor: 'var(--bl-warning)',
},
error: {
background: 'var(--bl-error-light)',
color: 'var(--bl-error-dark)',
borderColor: 'var(--bl-error)',
},
};
Token Migration Checklist
When migrating to design tokens:
- Identify hardcoded colors - Search for hex codes and named colors
- Map to semantic tokens - Use semantic names (success, warning, error) not literal colors
- Replace with CSS variables - Use
var(--bl-*)syntax - Test across themes - Ensure tokens work in light/dark modes
- Audit for missed tokens - Use
audit:uito find remaining hardcoded values
Part 3: Component Normalization
Badge Normalization
Before: One-off CSS classes for different badge styles
/* Old approach - one-off classes */
.stat-chip {
background: #f0f0f0;
padding: 4px 8px;
border-radius: 4px;
font-size: 12px;
}
.saved-setup-id-chip {
background: #e0e0e0;
padding: 6px 12px;
border-radius: 6px;
}
.health-pill {
background: #d0d0d0;
padding: 5px 10px;
border-radius: 20px;
}
After: Shared Badge component from product adapter
import { Badge, ProductStatusBadge } from '../components/ui/Primitives';
// Replace .stat-chip with Badge
<Badge variant="success">Active</Badge>
// Replace .saved-setup-id-chip with Badge
<Badge variant="info">Setup #123</Badge>
// Replace .health-pill with ProductStatusBadge
<ProductStatusBadge status="healthy" />
Commit: 94ce743 - refactor(ui): replace one-off visual language with shared Badge components
Alert Banner Unification
Before: Different alert implementations across components
// HistoryTab.tsx - custom alert
<div className="alert-banner" style={{ background: '#fff3cd' }}>
<span>⚠️</span>
<span>Warning message</span>
</div>
// PositionsTab.tsx - different alert
<div className="warning-box" style={{ border: '1px solid #ffc107' }}>
<div className="warning-icon">!</div>
<div className="warning-text">Warning text</div>
</div>
After: Shared AlertBanner component
import { AlertBanner } from '../components/ui/Primitives';
// Both components now use shared AlertBanner
<AlertBanner tone="warning" title="Warning">
Warning message
</AlertBanner>
<AlertBanner tone="error" title="Error">
Error message
</AlertBanner>
Commit: d4846b7 - refactor(ui): unify operational alert banners
Table Controls Standardization
Before: Custom table controls with inconsistent styling
// Different button styles across tables
<button className="table-action-btn-small">Edit</button>
<button className="history-filter-btn">Filter</button>
<button className="backtest-action">Run</button>
After: Standardized Button component
import { Button, IconButton } from '../components/ui/Primitives';
// Consistent button styles
<Button variant="ghost" size="sm">Edit</Button>
<Button variant="secondary" size="sm">Filter</Button>
<IconButton icon={<PlayIcon />} label="Run" />
Commits:
3951767- refactor(ui): standardize operations table badgesfd64fec- refactor(ui): refine positions table controlsbfd7d3b- refactor(ui): align history filters controls
Control Normalization Pattern
Step 1: Identify one-off controls
# Search for custom CSS classes
grep -r "className.*chip" web/src/
grep -r "className.*btn" web/src/
grep -r "className.*pill" web/src/
Step 2: Map to shared primitives
- Chips → Badge
- Custom buttons → Button
- Custom inputs → Input
- Custom selects → Select
- Custom alerts → AlertBanner
Step 3: Replace component by component
// Before
<div className="custom-chip">Text</div>
// After
<Badge variant="neutral">Text</Badge>
Step 4: Remove old CSS
/* Delete one-off CSS classes */
.custom-chip { /* delete */ }
.custom-btn { /* delete */ }
Step 5: Verify with audit
pnpm run audit:ui
Part 4: Accessibility Improvements
Keyboard Navigation
Focus Management
// Ensure interactive elements are focusable
<button
type="button"
tabIndex={0}
onKeyDown={(e) => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
handleClick();
}
}}
>
Action
</button>
Keyboard Toggles
// Commit: a65d726 - test(ui): cover profile rule keyboard toggles
<div
role="button"
tabIndex={0}
onKeyDown={(e) => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
toggle();
}
}}
aria-pressed={isActive}
>
Toggle Option
</div>
Commit: 3c52593 - fix(ui): improve profile rule keyboard access
ARIA Labels
Button Labels
// Icon buttons need explicit labels
<IconButton
icon={<EditIcon />}
label="Edit item"
aria-label="Edit item"
/>
Status Indicators
// Status badges need descriptive labels
<ProductStatusBadge
status="active"
aria-label="Status: Active"
>
Active
</ProductStatusBadge>
Form Labels
// All inputs need associated labels
<Field>
<FieldLabel htmlFor="email">Email</FieldLabel>
<Input id="email" type="email" />
<FieldDescription>Enter your email address</FieldDescription>
</Field>
Focus Indicators
Visible Focus States
/* Ensure focus is visible */
*:focus-visible {
outline: 2px solid var(--bl-focus-ring);
outline-offset: 2px;
}
/* Or use design token */
*:focus-visible {
outline: 2px solid var(--bl-focus-ring);
box-shadow: 0 0 0 2px var(--bl-focus-ring-muted);
}
Focus Order
// Ensure logical tab order
<div className="flex gap-4">
<Button>First</Button>
<Button>Second</Button>
<Button>Third</Button>
</div>
// Use tabIndex to control order if needed
<Button tabIndex={1}>First</Button>
<Button tabIndex={2}>Second</Button>
Part 5: Responsive Design
Shell Breakpoints
Responsive Shell Testing
// Commit: c51544a - test(ui): lock responsive shell breakpoints
const breakpoints = {
mobile: 'max-width: 560px',
tablet: 'max-width: 768px',
desktop: 'min-width: 769px',
};
// Shell adapts at these breakpoints
@media (max-width: 560px) {
.dashboard-main {
margin-left: 0;
}
.dashboard-right-panel {
display: none;
}
.trading-sidebar {
position: fixed;
bottom: 0;
width: 100%;
height: 60px;
}
}
Viewport Matrix Testing
Test all routes across viewports
// Commit: 79f0021 - test(ui): add comprehensive Playwright E2E test suite
const routes = ['/', '/portfolio', '/research', '/plans', '/markets'];
const viewports = [
{ name: 'Desktop', width: 1200, height: 800 },
{ name: 'Tablet', width: 768, height: 1024 },
{ name: 'Mobile', width: 375, height: 667 },
];
routes.forEach((route) => {
viewports.forEach((viewport) => {
test(`${route} - ${viewport.name} viewport`, async ({ page }) => {
await page.setViewportSize({ width: viewport.width, height: viewport.height });
await page.goto(route);
// Check for horizontal overflow
const bodyWidth = await page.evaluate(() => document.body.scrollWidth);
expect(bodyWidth).toBeLessThanOrEqual(viewport.width + 10);
// Check main content visibility
const mainContent = page.locator('main');
await expect(mainContent).toBeVisible();
});
});
});
Mobile-First Patterns
Responsive Components
// Use responsive design tokens
<div
style={{
padding: 'var(--bl-spacing-md)',
'@media (max-width: 560px)': {
padding: 'var(--bl-spacing-sm)',
},
}}
>
Content
</div>
Conditional Rendering
// Show/hide based on viewport
const isMobile = useMediaQuery('(max-width: 560px)');
{isMobile ? (
<MobileLayout />
) : (
<DesktopLayout />
)}
Part 6: Testing Infrastructure
Playwright Setup
Install Dependencies
cd web
pnpm add -D @playwright/test
pnpm exec playwright install chromium
Create Playwright Config
Create web/playwright.config.ts:
import { defineConfig, devices } from '@playwright/test';
export default defineConfig({
testDir: './e2e',
fullyParallel: true,
forbidOnly: !!process.env.CI,
retries: process.env.CI ? 2 : 0,
workers: process.env.CI ? 1 : undefined,
reporter: 'html',
use: {
baseURL: 'http://localhost:3050',
trace: 'on-first-retry',
screenshot: 'only-on-failure',
},
projects: [
{ name: 'chromium', use: { ...devices['Desktop Chrome'] } },
{ name: 'firefox', use: { ...devices['Desktop Firefox'] } },
{ name: 'webkit', use: { ...devices['Desktop Safari'] } },
{ name: 'Mobile Chrome', use: { ...devices['Pixel 5'] } },
{ name: 'Mobile Safari', use: { ...devices['iPhone 12'] } },
{ name: 'Desktop Chrome HiDPI', use: { ...devices['Desktop Chrome HiDPI'] } },
],
});
Commit: 8db23bd - test(ui): add Playwright and Storybook testing infrastructure
Test Suite Structure
Create tests in web/e2e/ directory:
web/e2e/
├── viewport-matrix.spec.ts # Viewport compliance
├── horizontal-overflow.spec.ts # Overflow detection
├── alert-positioning.spec.ts # Critical alerts positioning
├── assistant-positioning.spec.ts # Assistant widget positioning
├── destructive-actions.spec.ts # Destructive actions confirmation
├── feedback.spec.ts # Save/delete/update feedback
├── page-states.spec.ts # Loading/empty/error/success states
├── form-validation.spec.ts # Form validation
└── keyboard-navigation.spec.ts # Keyboard navigation
Commit: 79f0021 - test(ui): add comprehensive Playwright E2E test suite
Test Runner Script
Create scripts/tests/run-e2e.sh with server lifecycle management:
#!/bin/bash
set -e
# Colors for visual output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
PURPLE='\033[0;35m'
CYAN='\033[0;36m'
BOLD='\033[1m'
NC='\033[0m'
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
WEB_DIR="$PROJECT_ROOT/web"
REPORTS_DIR="$SCRIPT_DIR/reports"
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
PORT=3050
cd "$WEB_DIR"
# Function to kill any process on the specified port
kill_port() {
local port=$1
local pid=$(lsof -ti:$port 2>/dev/null || echo "")
if [ -n "$pid" ]; then
kill -9 $pid 2>/dev/null || true
sleep 2
fi
}
# Function to wait for server to be ready
wait_for_server() {
local max_attempts=30
local attempt=0
while [ $attempt -lt $max_attempts ]; do
if curl -s http://localhost:$PORT > /dev/null 2>&1; then
return 0
fi
attempt=$((attempt + 1))
sleep 2
done
return 1
}
# Kill existing server, start fresh, run tests, cleanup
kill_port $PORT
pnpm dev --port $PORT --strict-port > "$REPORTS_DIR/server-$TIMESTAMP.log" 2>&1 &
SERVER_PID=$!
if ! wait_for_server; then
kill_port $PORT
exit 1
fi
pnpm exec playwright test \
--reporter=json \
--reporter=list \
--output="$REPORTS_DIR/results-$TIMESTAMP.json" \
2>&1 | tee "$REPORTS_DIR/test-output-$TIMESTAMP.log"
TEST_EXIT_CODE=$?
kill $SERVER_PID 2>/dev/null || true
kill_port $PORT
exit $TEST_EXIT_CODE
Commits:
a572293- test(ui): enhance E2E test runner with AI-friendly detailed reportsdf7c57e- feat(test): add automatic server lifecycle management to run-e2e.sh2bd38e1- ui(test): enhance script output with visual formatting and colors
AI-Friendly Report Generation
The test runner generates three types of reports:
- JSON Report - Full machine-readable data
- Markdown Report - Human-readable summary
- AI Summary - Optimized for AI agents with actionable next steps
AI Summary Format:
{
"timestamp": "20260509_135006",
"test_status": "passed",
"test_exit_code": 0,
"tests_total": 333,
"tests_passed": 333,
"tests_failed": 0,
"duration_seconds": 447,
"health_check": {
"status": "healthy",
"app_running": true,
"app_url": "http://localhost:3050"
},
"for_ai_agents": {
"quick_summary": "passed - 333/333 tests passed in 447s",
"if_failed_check": "scripts/tests/reports/test-output-20260509_135006.log",
"html_report": "web/playwright-report/index.html",
"next_action": "Review HTML report for performance optimization"
}
}
Storybook Setup
Install Storybook
cd web
pnpm add -D @storybook/react @storybook/addon-essentials @storybook/addon-interactions
Create Storybook Config
Create web/.storybook/main.ts:
import type { StorybookConfig } from '@storybook/react-vite';
const config: StorybookConfig = {
stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|ts|tsx)'],
addons: [
'@storybook/addon-essentials',
'@storybook/addon-interactions',
],
framework: {
name: '@storybook/react-vite',
options: {},
},
docs: {
autodocs: 'tag',
},
};
export default config;
Commit: 8db23bd - test(ui): add Playwright and Storybook testing infrastructure
Part 7: Cipher Design System Integration
Design Principles
Follow Cipher design system principles for consistent UX across products:
1. Visual Hierarchy
Size and Weight
// Use design tokens for consistent hierarchy
<h1 style={{ fontSize: 'var(--bl-text-2xl)', fontWeight: 'var(--bl-font-bold)' }}>
Primary Heading
</h1>
<h2 style={{ fontSize: 'var(--bl-text-xl)', fontWeight: 'var(--bl-font-semibold)' }}>
Secondary Heading
</h2>
<p style={{ fontSize: 'var(--bl-text-base)', color: 'var(--bl-text-secondary)' }}>
Body text
</p>
2. Spacing System
Consistent Spacing
// Use design token spacing
<div style={{ padding: 'var(--bl-spacing-md)' }}>
Content
</div>
<div style={{ gap: 'var(--bl-spacing-sm)' }}>
<Button>First</Button>
<Button>Second</Button>
</div>
3. Typography
Font Scales
// Use design token font sizes
const textStyles = {
xs: 'var(--bl-text-xs)',
sm: 'var(--bl-text-sm)',
base: 'var(--bl-text-base)',
lg: 'var(--bl-text-lg)',
xl: 'var(--bl-text-xl)',
'2xl': 'var(--bl-text-2xl)',
};
<p style={{ fontSize: textStyles.base }}>Body text</p>
4. Color System
Semantic Colors
// Use semantic color tokens, not literal colors
const statusColors = {
success: 'var(--bl-success)',
warning: 'var(--bl-warning)',
error: 'var(--bl-error)',
info: 'var(--bl-info)',
};
<div style={{ color: statusColors.success }}>Success message</div>
5. Accessibility
WCAG AA Compliance
// Ensure contrast ratios meet WCAG AA
// Design tokens already ensure compliance
<button style={{
background: 'var(--bl-accent)',
color: 'var(--bl-accent-foreground)',
}}>
Accessible Button
</button>
Component Patterns
Card Pattern
<div style={{
background: 'var(--bl-surface-card)',
border: '1px solid var(--bl-border)',
borderRadius: 'var(--bl-radius-md)',
padding: 'var(--bl-spacing-lg)',
boxShadow: 'var(--bl-shadow-sm)',
}}>
<h3 style={{ fontSize: 'var(--bl-text-lg)', fontWeight: 'var(--bl-font-semibold)' }}>
Card Title
</h3>
<p style={{ color: 'var(--bl-text-secondary)', marginTop: 'var(--bl-spacing-sm)' }}>
Card content
</p>
</div>
Button Pattern
<Button variant="primary" size="md">
Primary Action
</Button>
<Button variant="secondary" size="md">
Secondary Action
</Button>
<Button variant="ghost" size="md">
Ghost Action
</Button>
Alert Pattern
<AlertBanner tone="info" title="Information">
Info message
</AlertBanner>
<AlertBanner tone="warning" title="Warning">
Warning message
</AlertBanner>
<AlertBanner tone="error" title="Error">
Error message
</AlertBanner>
Part 8: Implementation Roadmap
Phase 1: Foundation Setup
Week 1-2
- Install common platform packages
- Create product adapter (
Primitives.tsx) - Set up design token integration
- Configure Playwright
- Configure Storybook
Verification:
pnpm install @bytelyst/ui @bytelyst/design-tokens
pnpm run typecheck
pnpm run build
Phase 2: Component Normalization
Week 3-4
- Replace one-off badges with Badge component
- Replace custom buttons with Button component
- Replace custom alerts with AlertBanner component
- Replace custom inputs with Input component
- Remove old CSS classes
Commits Reference:
94ce743- Badge normalizationd4846b7- Alert banner unification3951767- Table badges standardization- Multiple control normalization commits
Verification:
pnpm run audit:ui
# Should show 0 raw controls, 0 direct @bytelyst/ui imports
Phase 3: Design Token Migration
Week 5-6
- Identify hardcoded colors
- Map to semantic tokens
- Replace with CSS variables
- Test across themes
- Audit for missed tokens
Verification:
# Search for hardcoded colors
grep -r "#[0-9a-fA-F]\{6\}" web/src/
# Should return minimal results (semantic colors only)
Phase 4: Accessibility Improvements
Week 7
- Add keyboard navigation
- Add ARIA labels
- Improve focus indicators
- Test with screen readers
Commits Reference:
3c52593- Profile rule keyboard accessa65d726- Profile rule keyboard toggles
Verification:
# Run accessibility tests
pnpm exec playwright test e2e/keyboard-navigation.spec.ts
pnpm exec playwright test e2e/form-validation.spec.ts
Phase 5: Responsive Design
Week 8
- Test viewport matrix
- Fix horizontal overflow
- Optimize mobile layout
- Test breakpoints
Commits Reference:
c51544a- Responsive shell breakpoints31d8932- Stabilize responsive shell chrome
Verification:
pnpm exec playwright test e2e/viewport-matrix.spec.ts
pnpm exec playwright test e2e/horizontal-overflow.spec.ts
Phase 6: Testing Infrastructure
Week 9-10
- Create E2E test suite
- Set up test runner script
- Configure AI-friendly reports
- Set up Storybook
- Integrate with CI
Commits Reference:
8db23bd- Playwright and Storybook setup79f0021- Comprehensive E2E test suitea572293- AI-friendly reportsdf7c57e- Test runner with server lifecycle
Verification:
./scripts/tests/run-e2e.sh
# Should pass all tests and generate reports
Part 9: Verification Commands
UI Audit
# Check for raw controls and direct imports
pnpm run audit:ui
# Strict audit (enforces token usage)
pnpm run audit:ui:strict
Type Checking
# Verify TypeScript types
pnpm run typecheck
Build Verification
# Ensure build succeeds
pnpm run build
Test Execution
# Run all E2E tests
./scripts/tests/run-e2e.sh
# Run specific test suites
cd web
pnpm test:e2e:viewport
pnpm test:e2e:overflow
pnpm test:e2e:ui
Storybook
# Start Storybook
cd web
pnpm storybook
Part 10: Troubleshooting
Common Issues
Issue: Playwright tests fail on VM
Solution:
cd web
pnpm install
Issue: Design tokens not working
Solution:
# Ensure design tokens package is installed
pnpm install @bytelyst/design-tokens
# Check CSS variable usage
grep -r "var(--bl-)" web/src/
Issue: Component imports failing
Solution:
# Ensure common platform is accessible
cd ../learning_ai_common_plat
pnpm build
# In product repo
pnpm install
Issue: Audit showing raw controls
Solution:
# Find raw controls
grep -r "className.*btn" web/src/
grep -r "className.*chip" web/src/
# Replace with shared primitives
Part 11: Best Practices
Component Development
- Use shared primitives first - Always check
@bytelyst/uibefore creating custom components - Follow product adapter pattern - Extend shared primitives via product adapter, not direct modification
- Use design tokens - Never hardcode colors, spacing, or typography
- Test accessibility - Include keyboard navigation and ARIA labels
- Test responsive - Verify components work across viewports
Code Organization
- Centralize imports - Use
Primitives.tsxfor all UI component imports - Group related components - Organize by feature, not by component type
- Separate concerns - Keep presentation logic separate from business logic
- Use TypeScript - Leverage type safety for component props
Testing Strategy
- Test critical paths - Focus on user workflows
- Test across browsers - Use Playwright's multi-browser support
- Test accessibility - Include keyboard and screen reader tests
- Test responsive - Verify viewport matrix compliance
- Use AI-friendly reports - Enable automated analysis
Documentation
- Document decisions - Record why certain patterns were chosen
- Update roadmaps - Keep launch readiness checklist current
- Share learnings - Document patterns for other products
- Maintain guides - Update this guide as patterns evolve
Part 12: References
Common Platform
- UI Package:
@bytelyst/ui- Shared UI components - Design Tokens:
@bytelyst/design-tokens- Design system tokens - Documentation: Common platform documentation in
learning_ai_common_plat
Cipher Design System
- Principles: Visual hierarchy, spacing, typography, color, accessibility
- Documentation: Internal design system documentation
- Reference: https://www.usecipher.dev/
Testing
- Playwright: https://playwright.dev
- Storybook: https://storybook.js.org
- Accessibility: WCAG 2.1 AA guidelines
Project Documentation
- Launch Readiness Roadmap:
docs/inprogress/LAUNCH_READY_UI_UX_ROADMAP.md - Production Grade UX:
docs/completed/PRODUCTION_GRADE_UX_REMEDIATION_ROADMAP.md - UI Migration Roadmap:
docs/completed/UI_UX_PLATFORM_CORE_MIGRATION_ROADMAP.md
Summary
This comprehensive guide documents the complete UX implementation approach for the trading dashboard, covering:
Key Achievements (Last 2 Days):
- 40+ commits focused on UX improvements
- Complete component normalization using shared primitives
- Comprehensive design token integration
- Accessibility improvements across the application
- Responsive design testing and fixes
- Full E2E testing infrastructure with Playwright
- Storybook setup for component development
- AI-friendly reporting for automated analysis
Reproducible Patterns:
- Product Adapter Pattern - Centralized import point with type-safe extensions
- Component Normalization - Replace one-off components with shared primitives
- Design Token Integration - Use CSS variables for consistent styling
- Accessibility First - Keyboard navigation, ARIA labels, focus management
- Responsive by Default - Viewport matrix testing, mobile-first patterns
- Test-Driven UX - E2E tests verify UX quality automatically
Implementation Roadmap:
- 10-week phased approach from foundation to full testing
- Clear verification commands at each phase
- Commit references for specific patterns
- Troubleshooting guide for common issues
Benefits for Other Products:
- Consistent UX across all ByteLyst products
- Reduced development time with shared primitives
- Improved accessibility and responsive design
- Automated testing ensures quality
- AI-friendly reports enable continuous improvement
This guide enables other products to replicate the exact same UX implementation approach, leveraging common platform packages, design tokens, and Cipher design system principles.