# 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`: `` - [ ] 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) ### 4.4 Footer Component - [ ] 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: `` - Add label for hamburger: `` - Add label for close button inside menu: `` - 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: `` - Label buttons: `` - 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: `Tab 1` - Panel IDs: `