Files
easystream-main/INTEGRATION_SNIPPETS.md
SamiAhmed7777 d22b3e1c0d feat: Add complete Docker deployment with web-based setup wizard
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>
2025-10-26 01:42:31 -07:00

18 KiB

EasyStream Design System - Integration Snippets

Quick copy-paste snippets to integrate the new design system into EasyStream templates.

Table of Contents

  1. HTML Head Updates
  2. Skip Links
  3. Theme Switcher UI
  4. Accessibility Improvements
  5. Responsive Components

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>

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>

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 - Margins
  • p-xs, p-sm, p-md, p-lg - Padding

Typography:

  • text-xs, text-sm, text-md, text-lg, text-xl - Font sizes
  • font-light, font-normal, font-medium, font-bold - Font weights

Layout:

  • flex, flex-col, flex-wrap - Flexbox
  • grid, grid-cols-{n} - Grid
  • container - Responsive container

Display:

  • hidden, block, flex - Display
  • xs:hidden, md:block - Responsive display

Colors:

  • text-primary, text-secondary - Text colors
  • bg-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 only
  • touch-target - Minimum touch size
  • focus-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.