Major additions: - Web-based setup wizard (setup.php, setup_wizard.php, setup-wizard.js) - Production Docker configuration (docker-compose.prod.yml, .env.production) - Database initialization SQL files (deploy/init_settings.sql) - Template builder system with drag-and-drop UI - Advanced features (OAuth, CDN, enhanced analytics, monetization) - Comprehensive documentation (deployment guides, quick start, feature docs) - Design system with accessibility and responsive layout - Deployment automation scripts (deploy.ps1, generate-secrets.ps1) Setup wizard allows customization of: - Platform name and branding - Domain configuration - Membership tiers and pricing - Admin credentials - Feature toggles Database includes 270+ tables for complete video streaming platform with advanced features for analytics, moderation, template building, and monetization. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
18 KiB
EasyStream Design System - Integration Snippets
Quick copy-paste snippets to integrate the new design system into EasyStream templates.
Table of Contents
HTML Head Updates
Add to Smarty Template Headers
For frontend templates (f_templates/tpl_frontend/tpl_head_min.tpl):
{* Add after existing CSS includes *}
<!-- Design System v2.0 -->
<link rel="stylesheet" href="{$main_url}/f_scripts/shared/design-system.css">
<link rel="stylesheet" href="{$main_url}/f_scripts/shared/accessibility.css">
<link rel="stylesheet" href="{$main_url}/f_scripts/shared/responsive.css">
<!-- Meta tags for PWA -->
<meta name="theme-color" content="#06a2cb">
<meta name="mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
<meta name="apple-mobile-web-app-title" content="EasyStream">
<!-- Manifest -->
<link rel="manifest" href="{$main_url}/manifest.json">
<!-- Preload critical fonts -->
<link rel="preload" as="font" type="font/woff2" crossorigin>
Add to Footer Scripts
For frontend templates (f_templates/tpl_frontend/tpl_footerjs_min.tpl):
{* Add before closing body tag *}
<!-- Theme Switcher -->
<script src="{$main_url}/f_scripts/shared/theme-switcher.js"></script>
<!-- Service Worker Registration (already in index.js but ensure it's loaded) -->
<script>
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw.js?v=2')
.then(reg => console.log('[SW] Registered'))
.catch(err => console.error('[SW] Registration failed:', err));
}
</script>
Skip Links
Add to Body Start
Add to (f_templates/tpl_frontend/tpl_body.tpl) at the very beginning:
<body class="fe media-width-768 is-fw{if $is_mobile eq 1} is-mobile{/if}" data-theme="{$theme_name|default:'blue'}">
{* Skip links for accessibility *}
<div class="skip-links" role="navigation" aria-label="Skip links">
<a href="#main-content" class="skip-to-content">Skip to main content</a>
<a href="#navigation" class="skip-to-content">Skip to navigation</a>
<a href="#search" class="skip-to-content">Skip to search</a>
</div>
{* Rest of body content *}
Add Main Content ID
Update main wrapper in f_templates/tpl_frontend/tpl_body_main.tpl:
<main id="main-content" role="main" class="container">
{* Your main content *}
</main>
Theme Switcher UI
Option 1: Add to Header Navigation
Add to f_templates/tpl_frontend/tpl_header/tpl_headernav_yt.tpl:
{* Add to header navigation area *}
<div class="header-controls">
{* Theme toggle button *}
<button
id="theme-toggle"
class="btn btn-secondary touch-target"
aria-label="Toggle dark mode"
title="Toggle dark mode">
<i class="icon-moon"></i>
<span class="sr-only">Toggle theme</span>
</button>
{* Existing notification bell, user menu, etc. *}
</div>
Option 2: Full Theme Picker Modal
Create new template: f_templates/tpl_frontend/tpl_theme_picker.tpl
{* Theme Picker Modal *}
<div id="theme-picker-modal" class="modal" role="dialog" aria-labelledby="theme-modal-title" aria-hidden="true">
<div class="modal-backdrop" data-dismiss="modal"></div>
<div class="modal-content card">
<div class="modal-header">
<h2 id="theme-modal-title" class="text-xl font-semibold">Appearance Settings</h2>
<button class="modal-close" data-dismiss="modal" aria-label="Close">
<i class="icon-close"></i>
</button>
</div>
<div class="modal-body p-lg">
{* Theme mode toggle *}
<div class="theme-setting">
<label class="theme-label flex justify-between items-center">
<span class="font-medium">Theme Mode</span>
<button id="theme-toggle" class="btn btn-secondary touch-target" aria-label="Toggle theme mode">
<i class="icon-moon"></i>
</button>
</label>
</div>
<div class="hr m-y-md"></div>
{* Color picker *}
<div class="theme-setting">
<span class="theme-label font-medium block m-b-sm">Color Theme</span>
<div class="color-options flex gap-sm flex-wrap" role="group" aria-label="Color themes">
<button class="color-btn color-blue touch-target" data-color-theme="blue" aria-label="Blue theme" title="Blue">
<span class="sr-only">Blue</span>
</button>
<button class="color-btn color-red touch-target" data-color-theme="red" aria-label="Red theme" title="Red">
<span class="sr-only">Red</span>
</button>
<button class="color-btn color-cyan touch-target" data-color-theme="cyan" aria-label="Cyan theme" title="Cyan">
<span class="sr-only">Cyan</span>
</button>
<button class="color-btn color-green touch-target" data-color-theme="green" aria-label="Green theme" title="Green">
<span class="sr-only">Green</span>
</button>
<button class="color-btn color-orange touch-target" data-color-theme="orange" aria-label="Orange theme" title="Orange">
<span class="sr-only">Orange</span>
</button>
<button class="color-btn color-pink touch-target" data-color-theme="pink" aria-label="Pink theme" title="Pink">
<span class="sr-only">Pink</span>
</button>
<button class="color-btn color-purple touch-target" data-color-theme="purple" aria-label="Purple theme" title="Purple">
<span class="sr-only">Purple</span>
</button>
</div>
</div>
</div>
</div>
</div>
<style>
.color-btn {
width: 44px;
height: 44px;
border-radius: var(--border-radius-full);
border: 3px solid transparent;
cursor: pointer;
transition: all var(--transition-base);
position: relative;
}
.color-btn:hover {
transform: scale(1.1);
box-shadow: var(--shadow-md);
}
.color-btn.active {
border-color: var(--color-text-primary);
box-shadow: var(--shadow-lg);
}
.color-btn.active::after {
content: '✓';
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
color: white;
font-weight: bold;
font-size: 1.25rem;
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.5);
}
.color-blue { background: #06a2cb; }
.color-red { background: #dd1e2f; }
.color-cyan { background: #00997a; }
.color-green { background: #199900; }
.color-orange { background: #f28410; }
.color-pink { background: #ec7ab9; }
.color-purple { background: #b25c8b; }
</style>
Accessibility Improvements
Form Labels
Before:
<input type="text" name="username" placeholder="Username">
After:
<label for="username" class="font-medium m-b-xs">
Username
<span class="required" aria-label="required">*</span>
</label>
<input
type="text"
id="username"
name="username"
class="input"
aria-required="true"
aria-describedby="username-error">
<div id="username-error" class="error-message" role="alert" style="display: none;">
Please enter a username
</div>
Image Alt Text
Before:
<img src="{$video.thumbnail}">
After:
<img
src="{$video.thumbnail}"
alt="{$video.title|escape} - Thumbnail"
loading="lazy"
width="320"
height="180">
Button Accessibility
Before:
<button onclick="likeVideo()">
<i class="icon-like"></i>
</button>
After:
<button
onclick="likeVideo()"
class="btn btn-secondary touch-target"
aria-label="Like this video"
aria-pressed="false">
<i class="icon-like" aria-hidden="true"></i>
<span class="sr-only">Like</span>
</button>
Heading Hierarchy
Before:
<div class="title">Featured Videos</div>
<div class="video-title">My Video</div>
After:
<h2 class="content-title text-2xl font-semibold">Featured Videos</h2>
<h3 class="video-title text-lg">My Video</h3>
ARIA Landmarks
Add to templates:
<header role="banner">
{* Header content *}
</header>
<nav role="navigation" aria-label="Main navigation">
{* Navigation menu *}
</nav>
<main role="main" id="main-content">
{* Main content *}
</main>
<aside role="complementary" aria-label="Sidebar">
{* Sidebar content *}
</aside>
<footer role="contentinfo">
{* Footer content *}
</footer>
Responsive Components
Video Grid
Before:
<div class="thumbs-wrapper">
{foreach from=$videos item=video}
<div class="vs-column">
{* Video thumbnail *}
</div>
{/foreach}
</div>
After:
<div class="video-grid">
{foreach from=$videos item=video}
<article class="video-card">
<a href="{$video.url}" class="video-link">
<div class="aspect-video">
<img
src="{$video.thumbnail}"
alt="{$video.title|escape} - Thumbnail"
loading="lazy"
class="video-thumbnail">
</div>
<div class="video-info p-sm">
<h3 class="video-title text-md font-medium">{$video.title}</h3>
<p class="video-meta text-sm text-secondary">
<span>{$video.views} views</span>
<span aria-hidden="true">•</span>
<span>{$video.date}</span>
</p>
</div>
</a>
</article>
{/foreach}
</div>
Responsive Container
Before:
<div class="inner-block">
{* Content *}
</div>
After:
<div class="container">
{* Content auto-sizes with padding *}
</div>
Flex Layout
Before:
<div style="display: flex; justify-content: space-between; align-items: center;">
<div>{$title}</div>
<div>{$actions}</div>
</div>
After:
<div class="flex justify-between items-center gap-md">
<div>{$title}</div>
<div>{$actions}</div>
</div>
Responsive Text
Before:
<h1 style="font-size: 36px;">{$title}</h1>
After:
<h1 class="text-responsive-xl font-bold">{$title}</h1>
Responsive Spacing
Before:
<div style="padding: 16px; margin-bottom: 24px;">
{* Content *}
</div>
After:
<div class="p-responsive m-b-lg">
{* Content *}
</div>
Card Component
New pattern:
<div class="card shadow-md">
<div class="card-header p-md">
<h2 class="text-lg font-semibold">Card Title</h2>
</div>
<div class="card-body p-lg">
{* Card content *}
</div>
<div class="card-footer p-md">
<button class="btn btn-primary">Action</button>
</div>
</div>
Alert Messages
Before:
{if $error_message}
<div class="error-message-text">{$error_message}</div>
{/if}
After:
{if $error_message}
<div class="alert alert-error" role="alert">
<i class="icon-warning" aria-hidden="true"></i>
<span>{$error_message}</span>
</div>
{/if}
{if $success_message}
<div class="alert alert-success" role="alert">
<i class="icon-check" aria-hidden="true"></i>
<span>{$success_message}</span>
</div>
{/if}
JavaScript Enhancements
Theme Switcher Events
// Listen for theme changes
document.addEventListener('easystream:theme-change', (e) => {
console.log('Theme changed:', e.detail);
// Update other components if needed
});
// Programmatically change theme
window.themeSwitcher.toggleMode(); // Toggle light/dark
window.themeSwitcher.setColor('red'); // Change color
// Get current theme
const theme = window.themeSwitcher.getCurrentTheme();
console.log(theme); // { mode: 'dark', color: 'blue', ... }
Service Worker Updates
// Update service worker
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw.js?v=2')
.then(reg => {
// Check for updates
reg.update();
// Listen for updates
reg.addEventListener('updatefound', () => {
const newWorker = reg.installing;
newWorker.addEventListener('statechange', () => {
if (newWorker.state === 'installed' && navigator.serviceWorker.controller) {
// New version available
if (confirm('New version available! Reload to update?')) {
window.location.reload();
}
}
});
});
});
}
Offline Detection
// Detect online/offline
window.addEventListener('online', () => {
console.log('Back online!');
// Show success message
showNotification('You are back online', 'success');
});
window.addEventListener('offline', () => {
console.log('Gone offline');
// Show warning message
showNotification('You are offline. Some features may be unavailable.', 'warning');
});
function showNotification(message, type) {
const alert = document.createElement('div');
alert.className = `alert alert-${type}`;
alert.textContent = message;
alert.role = 'alert';
document.body.appendChild(alert);
setTimeout(() => alert.remove(), 5000);
}
Testing Snippets
Check Accessibility
// Check for images without alt text
const imagesWithoutAlt = document.querySelectorAll('img:not([alt])');
console.log('Images missing alt text:', imagesWithoutAlt.length);
// Check for buttons without labels
const buttonsWithoutLabel = document.querySelectorAll('button:not([aria-label]):not(:has(.sr-only))');
console.log('Buttons missing labels:', buttonsWithoutLabel.length);
// Check heading hierarchy
const headings = document.querySelectorAll('h1, h2, h3, h4, h5, h6');
headings.forEach(h => console.log(h.tagName, h.textContent.substring(0, 50)));
Check Contrast Ratios
// Check text contrast (simplified)
function checkContrast(element) {
const style = getComputedStyle(element);
const color = style.color;
const bgColor = style.backgroundColor;
console.log(`Element: ${element.tagName}`, { color, bgColor });
}
// Check all text elements
document.querySelectorAll('p, h1, h2, h3, h4, h5, h6, a, button, span').forEach(checkContrast);
Test Keyboard Navigation
// Highlight focusable elements
document.querySelectorAll('a, button, input, select, textarea, [tabindex]').forEach(el => {
el.style.outline = '2px solid red';
});
// Tab order test
let tabIndex = 0;
document.addEventListener('focus', (e) => {
console.log(`Tab ${++tabIndex}:`, e.target);
}, true);
Common Patterns
Modal Dialog
<div id="my-modal" class="modal" role="dialog" aria-labelledby="modal-title" aria-hidden="true">
<div class="modal-backdrop" data-dismiss="modal"></div>
<div class="modal-content card shadow-xl">
<div class="modal-header p-md flex justify-between items-center">
<h2 id="modal-title" class="text-xl font-semibold">Modal Title</h2>
<button class="modal-close btn btn-secondary" data-dismiss="modal" aria-label="Close">
<i class="icon-close"></i>
</button>
</div>
<div class="modal-body p-lg">
{* Modal content *}
</div>
<div class="modal-footer p-md flex justify-end gap-sm">
<button class="btn btn-secondary" data-dismiss="modal">Cancel</button>
<button class="btn btn-primary">Confirm</button>
</div>
</div>
</div>
Dropdown Menu
<div class="dropdown">
<button
class="btn btn-secondary dropdown-toggle touch-target"
aria-haspopup="true"
aria-expanded="false"
id="dropdown-menu-btn">
Menu <i class="icon-chevron-down"></i>
</button>
<ul
class="dropdown-menu"
role="menu"
aria-labelledby="dropdown-menu-btn"
hidden>
<li role="none">
<a href="#" role="menuitem" class="dropdown-item">Option 1</a>
</li>
<li role="none">
<a href="#" role="menuitem" class="dropdown-item">Option 2</a>
</li>
</ul>
</div>
Loading Spinner
<div class="loading-spinner" role="status" aria-live="polite">
<i class="icon-spinner spinner"></i>
<span class="sr-only">Loading...</span>
</div>
Breadcrumbs
<nav aria-label="Breadcrumb">
<ol class="breadcrumb flex gap-xs items-center">
<li><a href="/">Home</a></li>
<li aria-hidden="true">/</li>
<li><a href="/videos">Videos</a></li>
<li aria-hidden="true">/</li>
<li aria-current="page">Current Page</li>
</ol>
</nav>
Quick Reference
Class Names Cheat Sheet
Spacing:
m-xs,m-sm,m-md,m-lg- Marginsp-xs,p-sm,p-md,p-lg- Padding
Typography:
text-xs,text-sm,text-md,text-lg,text-xl- Font sizesfont-light,font-normal,font-medium,font-bold- Font weights
Layout:
flex,flex-col,flex-wrap- Flexboxgrid,grid-cols-{n}- Gridcontainer- Responsive container
Display:
hidden,block,flex- Displayxs:hidden,md:block- Responsive display
Colors:
text-primary,text-secondary- Text colorsbg-primary,bg-secondary- Background colors
Borders:
rounded-sm,rounded-md,rounded-lg,rounded-full- Border radius
Shadows:
shadow-sm,shadow-md,shadow-lg- Box shadows
Accessibility:
sr-only- Screen reader onlytouch-target- Minimum touch sizefocus-visible- Focus indicator
Migration Checklist
- Include new CSS files in templates
- Include theme-switcher.js
- Add skip links to body
- Add main content ID
- Update navigation with theme toggle
- Replace inline styles with utility classes
- Add alt text to all images
- Add ARIA labels to buttons
- Add labels to form inputs
- Fix heading hierarchy
- Add ARIA landmarks
- Test keyboard navigation
- Test with screen reader
- Test on mobile devices
- Test all theme combinations
- Run Lighthouse audit
Next Steps: See DESIGN_SYSTEM_GUIDE.md for complete documentation.