Files
my-portfolio/migration.md
2026-01-30 22:20:41 +00:00

26 KiB

Blazor SSR Migration Plan

Project Overview

Migrating a React/TypeScript portfolio website to a minimal, dependency-free .NET Blazor application with static server-side rendering.

Current Stack:

  • React 18.2 + TypeScript
  • Vite build tool
  • Tailwind CSS (to be replaced with vanilla CSS)
  • React Router DOM
  • Headless UI components (Dialog/Tabs - to be replaced with vanilla HTML/CSS/JS)
  • React Icons (to be replaced with SVG icons)
  • Azure Static Web Apps hosting

Target Stack:

  • .NET 10 Blazor with Static SSR (no WebSockets/SignalR)
  • C# Razor components (server-rendered only)
  • Vanilla CSS only (no frameworks, no preprocessors)
  • Blazor Router (built-in)
  • No external component libraries
  • No icon dependencies (inline SVG)
  • Azure App Service or Azure Container Apps hosting

Migration Philosophy:

  • Zero external dependencies beyond .NET runtime
  • Simple, maintainable vanilla CSS
  • Static server-side rendering (no interactivity circuits)
  • Modern CSS features (Grid, Flexbox, CSS Variables, Container Queries)
  • Native HTML elements with custom styling
  • Zero JavaScript - pure CSS solutions for all interactivity (checkbox hack, :target pseudo-class)

Phase 1: Project Setup

1.1 .NET Project Initialization

  • Install .NET 10 SDK
  • Create new Blazor Web App project with SSR only: dotnet new blazor -o src/BlazorApp -int None
    • Note: -int None disables interactivity (no SignalR/WebSockets)
  • Verify project builds: dotnet build
  • Review generated files: Program.cs, App.razor, Components/_Imports.razor, appsettings.json
  • Confirm Program.cs does NOT include AddInteractiveServerComponents() or SignalR setup

1.2 Solution Structure

  • Create solution file: dotnet new sln -n my-portfolio
  • Add project to solution: dotnet sln add src/BlazorApp/BlazorApp.csproj
  • Configure project properties (nullable, implicit usings)
  • Ensure render modes are set to static SSR only

1.3 Git Configuration

  • Update .gitignore for .NET (bin/, obj/, .vs/)
  • Archive React project (move to archive/react-version/ or create pre-blazor-migration tag)
  • Commit initial Blazor project structure

Phase 2: CSS Architecture & Design System

2.1 CSS Variables Setup

  • Create wwwroot/css/variables.css with design tokens:
    • Colors (black: #000, white: #fff, gray shades)
    • Spacing scale (0.25rem, 0.5rem, 1rem, 1.5rem, 2rem, etc.)
    • Typography (font-family: monospace, font-sizes, line-heights)
    • Breakpoints (mobile-first: 640px, 768px, 1024px, 1280px)
    • Z-index scale
    • Border radius values
    • Transition durations

2.2 Base Styles

  • Create wwwroot/css/reset.css with modern CSS reset
  • Create wwwroot/css/base.css:
    • Body styles (background: black, color: slate-50, font: monospace)
    • Typography defaults
    • Link styles
    • Focus styles for accessibility
    • Custom scrollbar styles
    • Selection styles

2.3 Layout Utilities

  • Create wwwroot/css/layout.css:
    • Container classes (max-width, centering)
    • Flexbox utilities (flex, flex-col, items-center, justify-between, gap, etc.)
    • Grid utilities (grid, grid-cols)
    • Spacing utilities (padding, margin classes)
    • Responsive utilities

2.4 Component Styles

  • Create wwwroot/css/components.css:
    • Button styles (primary, secondary, hover, focus, disabled states)
    • Input/textarea styles
    • Card styles
    • Navigation styles
    • Footer styles
    • Link styles
  • Create wwwroot/css/animations.css:
    • Fade-in animation
    • Hover transitions
    • Loading spinner animation

2.5 Compile CSS

  • Create wwwroot/css/app.css that imports all CSS files
  • Reference in index.html: <link href="css/app.css" rel="stylesheet" />
  • Test CSS loads correctly

Phase 3: SVG Icon System

3.1 Extract Icons

  • Identify all react-icons used:
    • FaBars (hamburger menu)
    • FaXmark (close X)
    • FaGithub
    • FaLinkedin
    • FaEnvelope
    • FaDatabase
    • FaDocker
    • FaReact
    • SiCsharp
    • SiMicrosoftazure
    • SiBlazor
  • Download SVG paths from icon sources (FontAwesome, Simple Icons)
  • Create reusable Icon component: Components/Icon.razor

3.2 Icon Component

  • Implement Icon.razor with parameters:
    • Name (string): icon identifier
    • Size (int, default 24): icon size in pixels
    • CssClass (string): additional CSS classes
  • Store SVG paths in C# dictionary or switch statement
  • Test all icons render correctly

Phase 4: Core Application Structure

4.1 Routing Setup

  • Configure routes in App.razor:
    • / → Home page
    • /work → Work page
    • /about → About page
    • NotFound → 404/Error page
  • Test routing and navigation

4.2 Layout Component

  • Create Shared/MainLayout.razor
  • Implement structure:
    • Header with NavBar
    • Main content area with @Body
    • Footer
  • Apply CSS classes (container, flex layout, min-height)
  • Add fade-in animation class
  • Test layout renders correctly

4.3 Navigation Component

  • Create Shared/NavBar.razor
  • Implement desktop navigation:
    • Logo image
    • Navigation links (Home, Work, About)
  • Implement mobile navigation:
    • Hamburger button (toggle mobile menu)
    • Mobile menu overlay with links
    • Close button
    • Social icons in mobile menu
  • Use Blazor's built-in NavLink component with custom styling
  • Implement mobile menu state with C# boolean property
  • Style with pure CSS (no Headless UI)
  • Create Shared/Footer.razor
  • Port footer content
  • Apply styling

Phase 5: Page Components

5.1 Home Page

  • Create Pages/Home.razor with @page "/"
  • Port content from HomePage.tsx
  • Reference child components (Title, Text, TechIcons)
  • Test rendering

5.2 Work Page

  • Create Pages/Work.razor with @page "/work"
  • Port content from WorkPage.tsx
  • Test rendering

5.3 About Page

  • Create Pages/About.razor with @page "/about"
  • Port content from AboutPage.tsx
  • Test rendering

5.4 Error/404 Page

  • Create Pages/NotFound.razor with @page "/404"
  • Port content from ErrorPage.tsx
  • Configure as NotFound in router
  • Test 404 handling

Phase 6: Reusable UI Components

6.1 Typography Components (7)

  • Components/Title.razor - H1 heading
  • Components/Subtitle.razor - H2 heading
  • Components/Text.razor - Paragraph with margin
  • Components/Label.razor - Form label
  • Components/List.razor - UL wrapper
  • Components/ListItem.razor - LI element
  • Components/AnchorLink.razor - External link with styling

6.2 Form Components (3)

  • Components/Button.razor - Button with hover/focus states
  • Components/TextInput.razor - Input field with label
  • Components/TextAreaInput.razor - Textarea with label

6.3 Display Components (5)

  • Components/TechIcons.razor - Tech stack grid with icons
  • Components/SocialIcons.razor - Social media links with icons
  • Components/WorkTimeline.razor - Work experience timeline
  • Components/Loading.razor - Loading state wrapper
  • Components/LoadingSpinner.razor - Spinner animation

6.4 Feature Components (2)

  • Components/ContactMe.razor - Contact form
  • Components/AboutTabs.razor - Tab interface (vanilla CSS/JS)

Phase 7: Interactive Components (Pure CSS - No JavaScript)

7.1 Mobile Menu (CSS Checkbox Hack)

  • Implement in NavBar.razor:
    • Add hidden checkbox: <input type="checkbox" id="mobile-menu-toggle" class="menu-toggle" />
    • Add label for hamburger: <label for="mobile-menu-toggle">☰</label>
    • Add label for close button inside menu: <label for="mobile-menu-toggle">✕</label>
    • Render menu panel as sibling to checkbox
  • Style with CSS:
    • Hide checkbox: .menu-toggle { display: none; }
    • Show/hide menu based on checkbox state: .menu-toggle:checked ~ .mobile-menu { ... }
    • Show/hide overlay: .menu-toggle:checked ~ .overlay { ... }
    • Slide-in animation using transform: translateX()
    • Transparent overlay with backdrop-filter blur
    • Z-index layering for proper stacking
  • Test keyboard navigation (checkbox is focusable)

7.2 Tabs Component (CSS :target Pseudo-Class or Radio Buttons)

  • Option A - Radio Button Approach (Recommended):
    • Create AboutTabs.razor:
      • Hidden radio inputs: <input type="radio" name="tabs" id="tab1" checked />
      • Label buttons: <label for="tab1">Tab 1</label>
      • Tab panels as siblings to inputs
    • Style with CSS:
      • Hide radio buttons: input[type="radio"] { display: none; }
      • Active tab styling: #tab1:checked ~ .tabs-labels label[for="tab1"] { ... }
      • Show panel: #tab1:checked ~ .tab-panels .panel1 { display: block; }
      • Hide other panels by default
  • Option B - :target Pseudo-Class:
    • Use anchor links: <a href="#tab1">Tab 1</a>
    • Panel IDs: <div id="tab1" class="tab-panel">...</div>
    • Style: .tab-panel:target { display: block; }
    • Note: Changes URL hash
  • Choose approach and implement
  • Add smooth transitions with CSS

7.3 Form Handling (Traditional POST)

  • Implement form submission in ContactMe.razor
  • Use standard HTML <form> with POST action
  • Server-side endpoint to handle form submission
  • Redirect after post pattern (PRG)
  • Add HTML5 validation attributes
  • Style validation states with CSS (:invalid, :valid)

Phase 8: Tailwind CSS to Vanilla CSS Conversion

8.1 Analyze Tailwind Usage

  • Document all Tailwind classes used in current app:
    • Layout: flex, flex-col, items-center, justify-between, max-w-7xl, etc.
    • Spacing: px-6, py-8, mt-4, mb-6, space-x-6, etc.
    • Typography: text-sm, text-xl, font-semibold, font-mono, etc.
    • Colors: bg-black, text-white, text-gray-200, ring-gray-300, etc.
    • Responsive: lg:flex, md:order-2, sm:max-w-sm, etc.
    • Effects: hover:bg-gray-800, focus-visible:outline, etc.

8.2 Create CSS Equivalents

  • Create utility classes in layout.css:
    .flex { display: flex; }
    .flex-col { flex-direction: column; }
    .items-center { align-items: center; }
    .justify-between { justify-content: space-between; }
    .max-w-7xl { max-width: 80rem; margin: 0 auto; }
    .container { max-width: 1280px; margin: 0 auto; padding: 0 1.5rem; }
    
  • Create spacing utilities:
    .px-6 { padding-left: 1.5rem; padding-right: 1.5rem; }
    .py-8 { padding-top: 2rem; padding-bottom: 2rem; }
    .mt-4 { margin-top: 1rem; }
    /* etc. */
    
  • Create responsive utilities using media queries:
    @media (min-width: 1024px) {
      .lg\:flex { display: flex; }
    }
    
  • Alternative: Use component-specific classes instead of utilities

8.3 Choose Approach

  • Option A (Recommended): Component-specific CSS classes
    • More maintainable for small projects
    • Better for this portfolio site (20 components)
    • Example: .nav-bar {}, .nav-bar__logo {}, .nav-bar__menu {}
  • Option B: Minimal utility classes
    • Create only the most-used utilities (flex, grid, spacing)
    • Combine with component classes
  • Document chosen approach in README

Phase 9: SEO & Meta Tags

9.1 HTML Head Configuration

  • Update wwwroot/index.html with meta tags:
    • Charset, viewport
    • Description, author
    • Open Graph tags (og:title, og:description, og:image, og:url)
    • Favicon reference
  • Ensure all tags from React version are migrated
  • Test with social media preview tools

9.2 Analytics Integration

  • Add Google Analytics script to index.html
  • Add CookieYes consent script to index.html
  • Test analytics on navigation (use NavigationManager.LocationChanged if needed)
  • Verify GDPR compliance

Phase 10: Build & Optimization

10.1 Build Configuration

  • Configure .csproj for optimizations:
    <PropertyGroup>
      <PublishReadyToRun>true</PublishReadyToRun>
      <PublishReadyToRunShowWarnings>true</PublishReadyToRunShowWarnings>
    </PropertyGroup>
    
  • Test production build: dotnet publish -c Release
  • Analyze output in bin/Release/net10.0/publish/
  • Verify CSS is minimal and unminified (easy to debug)
  • Verify NO JavaScript files present

10.2 CSS Optimization

  • Remove unused CSS
  • Combine CSS files if beneficial
  • Consider minification (optional - Azure handles this)
  • Test CSS loads and applies correctly
  • Verify CSS-only menu and tabs work in all browsers

10.3 Image Optimization

  • Verify logo.webp is optimized
  • Add width/height attributes to prevent layout shift
  • Test image loading

10.4 Zero JavaScript Verification

  • Confirm no .js files in wwwroot/js/
  • Confirm no <script> tags in layouts/pages
  • Test site functions without JavaScript enabled in browser

Phase 11: Deployment Configuration

11.1 Azure App Service Configuration

  • Create Azure App Service (Linux or Windows)
  • Configure environment variables in Azure portal:
    • ASPNETCORE_ENVIRONMENT=Production
  • Enable HTTPS only
  • Configure health check endpoint
  • Set up Application Insights (optional)

11.2 GitHub Actions Workflow

  • Create/update .github/workflows/azure-deploy.yml:
    name: Deploy to Azure App Service
    
    on:
      push:
        branches: [ main ]
      workflow_dispatch:
    
    jobs:
      build-and-deploy:
        runs-on: ubuntu-latest
    
        steps:
        - uses: actions/checkout@v3
    
        - name: Setup .NET
          uses: actions/setup-dotnet@v3
          with:
            dotnet-version: '10.0.x'
    
        - name: Build
          run: dotnet build src/BlazorApp/BlazorApp.csproj -c Release
    
        - name: Publish
          run: dotnet publish src/BlazorApp/BlazorApp.csproj -c Release -o ${{env.DOTNET_ROOT}}/myapp
    
        - name: Deploy to Azure Web App
          uses: azure/webapps-deploy@v2
          with:
            app-name: 'your-app-name'
            publish-profile: ${{ secrets.AZURE_WEBAPP_PUBLISH_PROFILE }}
            package: ${{env.DOTNET_ROOT}}/myapp
    
  • Set up Azure publish profile in GitHub secrets
  • Test workflow on branch

11.3 Local Development Setup

  • Document commands:
    • Run: dotnet watch (hot reload enabled)
    • Build: dotnet build
    • Publish: dotnet publish -c Release
  • Test hot reload functionality
  • Configure HTTPS for local dev (trust cert)
  • Verify no SignalR scripts loaded (check browser network tab)
  • Test CSS-only interactivity (menu, tabs) during development

Phase 12: Testing & Quality Assurance

12.1 Visual Testing

  • Compare React site vs Blazor site side-by-side
  • Test all pages render identically
  • Test all components display correctly
  • Test responsive design:
    • Mobile (320px, 375px, 414px)
    • Tablet (768px, 1024px)
    • Desktop (1280px, 1920px)
  • Test cross-browser:
    • Chrome/Edge (Chromium)
    • Firefox
    • Safari (if available)

12.2 Functional Testing

  • Test navigation between pages (full page reloads)
  • Test mobile menu open/close (CSS checkbox)
  • Test tab switching in About page (CSS radio/target)
  • Test all external links
  • Test form submission with POST/redirect
  • Test 404 page navigation
  • Test with JavaScript disabled in browser
  • Verify no JavaScript errors (should be none)

12.3 Accessibility Testing

  • Test keyboard navigation (Tab, Enter, Space for checkboxes/radios)
  • Test screen reader compatibility (aria-labels, proper label associations)
  • Test focus indicators (especially on hidden checkbox/radio controls)
  • Test color contrast (black/white theme)
  • Verify checkbox and radio button labels are properly associated
  • Run Lighthouse accessibility audit

12.4 Performance Testing

  • Test server response times for page requests
  • Test First Contentful Paint (FCP)
  • Test Time to Interactive (TTI)
  • Test with throttled network (3G)
  • Verify no WebSocket connections in network tab
  • Verify no JavaScript downloads
  • Run Lighthouse performance audit
  • Target: FCP < 1s, TTI < 1.5s (server-rendered, zero JavaScript, instant interactive)

12.5 SEO Testing

  • Test meta tags with social media debuggers:
    • Facebook Sharing Debugger
    • Twitter Card Validator
    • LinkedIn Post Inspector
  • Test robots.txt accessibility
  • Test favicon in browser tabs
  • Run Lighthouse SEO audit

Phase 13: Documentation & Cleanup

13.1 Update Documentation

  • Update README.md:
    # beaufindlay.com
    
    My personal portfolio site built with .NET Blazor WebAssembly.
    
    ## Tech Stack
    - .NET 10 Blazor SSR (static server-side rendering)
    - Pure CSS only (zero JavaScript)
    - Hosted on Azure App Service
    
    ## Development
    - Run: `dotnet watch`
    - Build: `dotnet build`
    - Publish: `dotnet publish -c Release`
    
    ## Features
    - Zero external dependencies (no npm, no frameworks, no JavaScript)
    - Static server-side rendering (no WebSockets)
    - Pure CSS interactivity (checkbox hack for menu, radio buttons for tabs)
    - Mobile-first responsive design
    - Accessible and SEO-optimized
    - Works with JavaScript disabled
    
  • Document CSS architecture in comments
  • Add migration notes (lessons learned, gotchas)

13.2 Clean Up React Files

  • Archive React project:
    • Option A: Move to archive/react-version/
    • Option B: Create git tag pre-blazor-migration and delete
    • Option C: Keep in separate branch
  • Remove Node.js files from root:
    • Delete src/Client/package.json
    • Delete src/Client/package-lock.json
    • Delete src/Client/node_modules/
    • Delete src/Client/tsconfig*.json
    • Delete src/Client/vite.config.ts
    • Delete src/Client/tailwind.config.js
    • Delete src/Client/postcss.config.js
    • Delete src/Client/.eslintrc.cjs
  • Update .gitignore (remove Node.js entries)

13.3 Code Quality

  • Run dotnet format on all files
  • Add XML documentation comments to public components
  • Ensure consistent naming conventions
  • Remove unused using statements
  • Remove commented-out code

Phase 14: Production Deployment

14.1 Pre-Deployment Checklist

  • All components migrated and tested ✓
  • All routes working ✓
  • Mobile menu functional ✓
  • Tabs functional ✓
  • Analytics configured ✓
  • SEO tags verified ✓
  • Performance acceptable ✓
  • No console errors ✓
  • Cross-browser tested ✓

14.2 Deploy to Production

  • Merge blazor-refactor branch to main
  • Monitor GitHub Actions workflow
  • Verify deployment succeeds
  • Test production site at beaufindlay.com

14.3 Post-Deployment

  • Verify site loads correctly in production
  • Test all functionality in production
  • Verify analytics tracking
  • Monitor for errors (first 24-48 hours)
  • Check browser console for any warnings
  • Verify SSL certificate
  • Test social media sharing previews

Component Migration Checklist

Pages (5)

  • Layout (MainLayout.razor)
  • Home (Pages/Home.razor)
  • Work (Pages/Work.razor)
  • About (Pages/About.razor)
  • Error/404 (Pages/NotFound.razor)

Typography Components (7)

  • Title.razor
  • Subtitle.razor
  • Text.razor
  • Label.razor
  • List.razor
  • ListItem.razor
  • AnchorLink.razor

Form Components (3)

  • Button.razor
  • TextInput.razor
  • TextAreaInput.razor

Display Components (5)

  • TechIcons.razor
  • SocialIcons.razor
  • WorkTimeline.razor
  • Loading.razor
  • LoadingSpinner.razor

Feature Components (3)

  • NavBar.razor (in Shared/)
  • Footer.razor (in Shared/)
  • ContactMe.razor
  • AboutTabs.razor

Shared/Infrastructure (2)

  • Icon.razor (new - SVG icon system)
  • CssHelper.cs (optional - CSS class utilities)

Total: 25 components


CSS File Structure

wwwroot/
├── css/
│   ├── variables.css      # CSS custom properties (design tokens)
│   ├── reset.css          # Modern CSS reset
│   ├── base.css           # Base typography and body styles
│   ├── layout.css         # Layout utilities (flex, grid, container)
│   ├── components.css     # Component-specific styles
│   ├── animations.css     # Animations and transitions
│   └── app.css            # Main file that imports all others
├── logo.webp
├── robots.txt
└── index.html

Key Syntax Conversions

React/JSX Blazor/Razor
className="..." class="..."
{variable} @variable
{condition && <Element />} @if (condition) { <Element /> }
{items.map(item => ...)} @foreach (var item in items) { ... }
const [state, setState] = useState() private bool state; + StateHasChanged()
onClick={handler} @onclick="Handler"
onChange={handler} @onchange="Handler"
<Component prop={value} /> <Component Prop="@value" />
Props interface [Parameter] properties
import Component from './Component' @using or implicit

Dependencies Eliminated

Removed Libraries

  • Tailwind CSS → Vanilla CSS
  • PostCSS → None
  • Autoprefixer → Modern browsers only
  • React Icons → Inline SVG
  • Headless UI → Native HTML + CSS
  • React Router DOM → Blazor Router (built-in)
  • Vite → .NET SDK
  • TypeScript → C#
  • ESLint → Roslyn analyzers (built-in)
  • Node.js → None

Added Technology

  • Static server-side rendering (Blazor SSR mode)
  • Pure CSS interactivity techniques (checkbox hack, radio buttons, :target pseudo-class)

Build Dependencies

  • Before: Node.js, npm, Vite, TypeScript compiler, Tailwind CLI
  • After: .NET SDK only

Hosting Requirements

  • Before: Static hosting (Azure Static Web Apps, any CDN)
  • After: ASP.NET Core server (Azure App Service, Container Apps, or any host supporting .NET)

Risk Assessment

High Risk (Requires Testing)

  • Mobile menu with CSS checkbox hack: CSS-only implementation complexity
    • Mitigation: Well-established pattern, test thoroughly across browsers
  • Tabs with CSS (radio buttons or :target): CSS-only implementation complexity
    • Mitigation: Test both approaches, choose most accessible
  • Icon system: 11 icons need SVG paths
    • Mitigation: Download from FontAwesome/Simple Icons, test early
  • CSS-only accessibility: Ensuring keyboard navigation works properly
    • Mitigation: Thorough accessibility testing with screen readers

Medium Risk

  • CSS conversion from Tailwind: Large effort
    • Mitigation: Component-specific CSS, test each component
  • Server hosting: Requires server infrastructure instead of static hosting
    • Mitigation: Azure App Service is straightforward to configure
  • Browser compatibility for CSS tricks: Checkbox hack and advanced selectors
    • Mitigation: Test on all major browsers, provide fallbacks if needed

Low Risk

  • Basic component migration: Straightforward
  • Routing: Blazor router is similar to React Router
  • Static assets: Simple copy operation

Success Criteria

  • Zero npm/Node.js dependencies
  • Zero CSS framework dependencies
  • Zero JavaScript (pure CSS interactivity)
  • All pages render identically to React version
  • Mobile menu works smoothly (CSS checkbox hack)
  • Tabs work smoothly (CSS radio buttons or :target)
  • Site loads in < 2 seconds on 4G (server-rendered)
  • Site works with JavaScript disabled
  • No WebSocket connections present
  • No SignalR scripts loaded
  • No JavaScript files served
  • Analytics functional (server-side or noscript fallback)
  • SEO tags correct
  • Mobile responsive
  • Cross-browser compatible (including CSS-only features)
  • Keyboard accessible (Tab/Enter/Space navigation)
  • Lighthouse score: 95+ (Performance, Accessibility, SEO)
  • Server resource usage minimal (stateless requests only)

Estimated Timeline

  • Phase 1-2 (Setup & CSS): 2-3 days
  • Phase 3 (Icons): 0.5-1 day
  • Phase 4 (Core Structure): 1-2 days
  • Phase 5-6 (Pages & Components): 3-4 days
  • Phase 7 (Interactive - CSS only): 1-2 days
  • Phase 8 (CSS Conversion): 2-3 days
  • Phase 9 (SEO & Analytics): 0.5-1 day
  • Phase 10-11 (Build & Deploy): 1-2 days (server configuration)
  • Phase 12 (Testing): 2-3 days (including CSS-only interactivity testing)
  • Phase 13-14 (Docs & Deploy): 1 day

Total: 14-22 days (depending on CSS approach and server setup complexity)


Notes

  • This plan prioritizes simplicity and maintainability over feature richness
  • No external dependencies means no breaking changes from library updates
  • Vanilla CSS may take longer initially but is easier to maintain long-term
  • Static SSR provides instant page loads with server-side rendering and excellent SEO
  • Zero JavaScript - pure CSS interactivity (checkbox hack, radio buttons) - no framework hydration, no parsing overhead
  • The site will be future-proof with minimal maintenance requirements
  • Focus on modern CSS features (Grid, Flexbox, Custom Properties) rather than utility classes
  • Test frequently throughout migration - don't wait until the end
  • Tradeoff: Requires server hosting instead of static hosting, but provides better initial load performance
  • Key Benefits:
    • No WebSocket connections = lower server resource usage, simpler architecture, no reconnection issues
    • No JavaScript = works with JS disabled, faster TTI, smaller payload, privacy-friendly
    • Accessible by default (native HTML form controls)