Compare commits
9 Commits
fad16cb46f
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| cc96a5044e | |||
|
|
4188b7982b | ||
|
|
e9293854b4 | ||
|
|
3bbf61ccaa | ||
|
|
f3dcee5660 | ||
|
|
c358e5fb85 | ||
|
|
341795973b | ||
|
|
7457f6b4c4 | ||
| dcd116940c |
49
.gitea/workflows/deploy.yml
Normal file
49
.gitea/workflows/deploy.yml
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
name: Build and Deploy
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v3
|
||||||
|
|
||||||
|
- name: Build image
|
||||||
|
run: |
|
||||||
|
docker build -t portfolio:latest .
|
||||||
|
docker save portfolio:latest | gzip > portfolio.tar.gz
|
||||||
|
|
||||||
|
- name: Deploy to server
|
||||||
|
uses: appleboy/scp-action@v0.1.7
|
||||||
|
with:
|
||||||
|
host: ${{ secrets.SERVER_HOST }}
|
||||||
|
username: ${{ secrets.SERVER_USER }}
|
||||||
|
key: ${{ secrets.SERVER_SSH_KEY }}
|
||||||
|
source: "portfolio.tar.gz"
|
||||||
|
target: "/mnt/apps/docker-config/portfolio/"
|
||||||
|
|
||||||
|
- name: Restart container
|
||||||
|
uses: appleboy/ssh-action@v1.0.3
|
||||||
|
with:
|
||||||
|
host: ${{ secrets.SERVER_HOST }}
|
||||||
|
username: ${{ secrets.SERVER_USER }}
|
||||||
|
key: ${{ secrets.SERVER_SSH_KEY }}
|
||||||
|
script: |
|
||||||
|
cd /mnt/apps/docker-config/portfolio
|
||||||
|
sudo sh -c "gunzip -c portfolio.tar.gz | docker load"
|
||||||
|
sudo docker compose down
|
||||||
|
sudo docker compose up -d
|
||||||
|
|
||||||
|
- name: Purge Cloudflare cache
|
||||||
|
run: |
|
||||||
|
curl -X POST "https://api.cloudflare.com/client/v4/zones/${{ secrets.CLOUDFLARE_ZONE_ID }}/purge_cache" \
|
||||||
|
-H "Authorization: Bearer ${{ secrets.CLOUDFLARE_API_TOKEN }}" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
--data '{"purge_everything":true}'
|
||||||
45
.github/workflows/azure-static-web-apps.yml
vendored
45
.github/workflows/azure-static-web-apps.yml
vendored
@@ -1,45 +0,0 @@
|
|||||||
name: Azure Static Web Apps CI/CD
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- main
|
|
||||||
pull_request:
|
|
||||||
types: [opened, synchronize, reopened, closed]
|
|
||||||
branches:
|
|
||||||
- main
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build_and_deploy_job:
|
|
||||||
if: github.event_name == 'push' || (github.event_name == 'pull_request' && github.event.action != 'closed')
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
name: Build and Deploy Job
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
with:
|
|
||||||
submodules: true
|
|
||||||
lfs: false
|
|
||||||
- name: Build And Deploy
|
|
||||||
id: builddeploy
|
|
||||||
uses: Azure/static-web-apps-deploy@v1
|
|
||||||
with:
|
|
||||||
azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN_VICTORIOUS_TREE_0B68B6103 }}
|
|
||||||
repo_token: ${{ secrets.GITHUB_TOKEN }} # Used for Github integrations (i.e. PR comments)
|
|
||||||
action: "upload"
|
|
||||||
###### Repository/Build Configurations - These values can be configured to match your app requirements. ######
|
|
||||||
# For more information regarding Static Web App workflow configurations, please visit: https://aka.ms/swaworkflowconfig
|
|
||||||
app_location: "/src/Client" # App source code path
|
|
||||||
output_location: "dist" # Built app content directory - optional
|
|
||||||
###### End of Repository/Build Configurations ######
|
|
||||||
|
|
||||||
close_pull_request_job:
|
|
||||||
if: github.event_name == 'pull_request' && github.event.action == 'closed'
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
name: Close Pull Request Job
|
|
||||||
steps:
|
|
||||||
- name: Close Pull Request
|
|
||||||
id: closepullrequest
|
|
||||||
uses: Azure/static-web-apps-deploy@v1
|
|
||||||
with:
|
|
||||||
azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN_VICTORIOUS_TREE_0B68B6103 }}
|
|
||||||
action: "close"
|
|
||||||
@@ -1,2 +1,2 @@
|
|||||||
# [beaufindlay.com](https://beaufindlay.com)
|
# [beaufindlay.com](https://beaufindlay.com)
|
||||||
My personal site built in React with TypeScript and published to an Azure Static Web App.
|
My personal site built in .NET Blazor and published to a self-hosted docker container via a self-hosted Gitea runner. This code repo is also on a self-hosted instance of Gitea running on a headless linux server.
|
||||||
766
migration.md
766
migration.md
@@ -1,766 +0,0 @@
|
|||||||
# 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)
|
|
||||||
|
|
||||||
### 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: `<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`:
|
|
||||||
```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:
|
|
||||||
```css
|
|
||||||
.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:
|
|
||||||
```css
|
|
||||||
@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:
|
|
||||||
```xml
|
|
||||||
<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`:
|
|
||||||
```yaml
|
|
||||||
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:
|
|
||||||
```markdown
|
|
||||||
# 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)
|
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
@switch (Type)
|
@switch (Type)
|
||||||
{
|
{
|
||||||
case IconType.Github:
|
case IconType.Gitea:
|
||||||
<svg viewBox="0 0 24 24"
|
<svg fill="currentColor"
|
||||||
fill="currentColor"
|
viewBox="0 0 32 32"
|
||||||
xmlns="http://www.w3.org/2000/svg">
|
xmlns="http://www.w3.org/2000/svg">
|
||||||
<path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"/>
|
<path d="M5.583 7.229c-2.464-0.005-5.755 1.557-5.573 5.479 0.281 6.125 6.557 6.693 9.068 6.745 0.271 1.146 3.224 5.109 5.411 5.318h9.573c5.74-0.38 10.036-17.365 6.854-17.427-5.271 0.25-8.396 0.375-11.073 0.396v5.297l-0.839-0.365-0.005-4.932c-3.073 0-5.781-0.141-10.917-0.396-0.646-0.005-1.542-0.115-2.5-0.115zM5.927 9.396h0.297c0.349 3.141 0.917 4.974 2.068 7.781-2.938-0.349-5.432-1.198-5.891-4.38-0.24-1.646 0.563-3.365 3.526-3.401zM17.339 12.479c0.198 0.005 0.406 0.042 0.594 0.13l1 0.432-0.714 1.302c-0.109 0-0.219 0.016-0.323 0.052-0.464 0.151-0.708 0.604-0.542 1.021 0.036 0.083 0.089 0.161 0.151 0.229l-1.234 2.25c-0.099 0-0.203 0.016-0.297 0.052-0.464 0.146-0.708 0.604-0.542 1.016 0.172 0.417 0.682 0.63 1.151 0.479 0.464-0.146 0.703-0.604 0.536-1.021-0.047-0.109-0.115-0.208-0.208-0.292l1.203-2.188c0.13 0.010 0.26 0 0.391-0.042 0.104-0.031 0.198-0.083 0.281-0.151 0.464 0.198 0.844 0.354 1.12 0.49 0.406 0.203 0.552 0.339 0.599 0.49 0.042 0.146-0.005 0.427-0.24 0.922-0.172 0.37-0.458 0.896-0.797 1.51-0.115 0-0.229 0.016-0.333 0.052-0.469 0.151-0.708 0.604-0.542 1.021 0.167 0.411 0.682 0.625 1.146 0.479 0.469-0.151 0.708-0.604 0.542-1.021-0.042-0.099-0.104-0.193-0.182-0.271 0.333-0.609 0.62-1.135 0.807-1.526 0.25-0.536 0.38-0.938 0.266-1.323s-0.469-0.635-0.932-0.865c-0.307-0.151-0.693-0.313-1.146-0.505 0.005-0.109-0.010-0.214-0.052-0.318s-0.109-0.198-0.193-0.281l0.703-1.281 3.901 1.682c0.703 0.307 0.995 1.057 0.651 1.682l-2.682 4.906c-0.339 0.625-1.182 0.885-1.885 0.578l-5.516-2.38c-0.703-0.307-0.995-1.057-0.656-1.682l2.682-4.906c0.234-0.432 0.708-0.688 1.208-0.708h0.083z"/>
|
||||||
</svg>
|
</svg>
|
||||||
break;
|
break;
|
||||||
case IconType.LinkedIn:
|
case IconType.LinkedIn:
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ namespace BlazorApp.Components.Icons;
|
|||||||
|
|
||||||
public enum IconType
|
public enum IconType
|
||||||
{
|
{
|
||||||
Github,
|
Gitea,
|
||||||
LinkedIn,
|
LinkedIn,
|
||||||
Email,
|
Email,
|
||||||
Code,
|
Code,
|
||||||
|
|||||||
@@ -1,18 +1,21 @@
|
|||||||
@* Social media links *@
|
@* Social media links *@
|
||||||
|
|
||||||
<div class="social-icons-container">
|
<div class="social-icons-container">
|
||||||
<a href="https://github.com/bdfin"
|
<a href="https://git.beaufindlay.com/beau"
|
||||||
class="social-icon-link">
|
class="social-icon-link"
|
||||||
<span class="sr-only">GitHub</span>
|
target="_blank">
|
||||||
<Icon Type="IconType.Github"/>
|
<span class="sr-only">Gitea</span>
|
||||||
|
<Icon Type="IconType.Gitea"/>
|
||||||
</a>
|
</a>
|
||||||
<a href="https://www.linkedin.com/in/beau-findlay/"
|
<a href="https://www.linkedin.com/in/beau-findlay/"
|
||||||
class="social-icon-link">
|
class="social-icon-link"
|
||||||
|
target="_blank">
|
||||||
<span class="sr-only">LinkedIn</span>
|
<span class="sr-only">LinkedIn</span>
|
||||||
<Icon Type="IconType.LinkedIn"/>
|
<Icon Type="IconType.LinkedIn"/>
|
||||||
</a>
|
</a>
|
||||||
<a href="mailto:me@beaufindlay.com"
|
<a href="mailto:me@beaufindlay.com"
|
||||||
class="social-icon-link">
|
class="social-icon-link"
|
||||||
|
target="_blank">
|
||||||
<span class="sr-only">Email</span>
|
<span class="sr-only">Email</span>
|
||||||
<Icon Type="IconType.Email"/>
|
<Icon Type="IconType.Email"/>
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@@ -7,8 +7,8 @@
|
|||||||
Below is an overview of how this simple app is made and what
|
Below is an overview of how this simple app is made and what
|
||||||
technologies are used. If you'd like to dive straight in, the full
|
technologies are used. If you'd like to dive straight in, the full
|
||||||
project is available on my
|
project is available on my
|
||||||
<AnchorLink Href="https://github.com/bdfin/my-portfolio">
|
<AnchorLink Href="https://git.beaufindlay.com/beau/my-portfolio">
|
||||||
GitHub
|
Gitea
|
||||||
</AnchorLink>.
|
</AnchorLink>.
|
||||||
</Text>
|
</Text>
|
||||||
|
|
||||||
@@ -53,23 +53,23 @@
|
|||||||
preferred a server to work with there wasn't many free options.
|
preferred a server to work with there wasn't many free options.
|
||||||
</Text>
|
</Text>
|
||||||
<Text>
|
<Text>
|
||||||
I've always been a bit of a networking/homelab/server nerd so as soon as Cloudflare announced their
|
I've always been a bit of a networking/home-lab/server nerd so as soon as Cloudflare announced their
|
||||||
<AnchorLink Href="https://developers.cloudflare.com/cloudflare-one/networks/connectors/cloudflare-tunnel/">
|
<AnchorLink Href="https://developers.cloudflare.com/cloudflare-one/networks/connectors/cloudflare-tunnel/">
|
||||||
Tunnels
|
Tunnels
|
||||||
</AnchorLink>
|
</AnchorLink>
|
||||||
feature I decided to revisit this site, rebuild in a server-side technology and host it myself on a tiny,
|
feature I decided to revisit this site, rebuild in a server-side technology and host it myself on server I setup at home running a headless linux server operating system. I configured the server with
|
||||||
low-power
|
|
||||||
<AnchorLink Href="https://www.raspberrypi.com/">RaspberryPi</AnchorLink>
|
|
||||||
computer running a headless linux operating system.
|
|
||||||
</Text>
|
|
||||||
<Text>
|
|
||||||
I setup the RaspberryPi with
|
|
||||||
<AnchorLink Href="https://www.docker.com/">Docker</AnchorLink>
|
<AnchorLink Href="https://www.docker.com/">Docker</AnchorLink>
|
||||||
and
|
and
|
||||||
<AnchorLink Href="https://docs.docker.com/compose/">Docker Compose</AnchorLink>, built a Docker container for the .NET app to run from, copied it to my server and created a docker-compose.yml
|
<AnchorLink Href="https://docs.docker.com/compose/">Docker Compose</AnchorLink>, built a Docker container for the .NET app to run from, copied it to my server and created a <code>docker-compose.yml</code>
|
||||||
file to run it with the
|
file to run it with the Cloudflare Tunnel
|
||||||
<AnchorLink Href="https://hub.docker.com/r/cloudflare/cloudflared">Cloudflare Tunnel</AnchorLink>
|
<AnchorLink Href="https://hub.docker.com/r/cloudflare/cloudflared">Docker image</AnchorLink>.
|
||||||
docker image.
|
</Text>
|
||||||
|
<Text>
|
||||||
|
After ensuring the app was working correctly I decided to migrate the code repo from GitHub to a locally hosted
|
||||||
|
<AnchorLink Href="https://about.gitea.com/">Gitea</AnchorLink>
|
||||||
|
instance running on the same server. I also setup a self-hosted runner and created a
|
||||||
|
<AnchorLink Href="https://git.beaufindlay.com/beau/my-portfolio/src/branch/main/.gitea/workflows/deploy.yml">build and deploy workflow</AnchorLink>
|
||||||
|
that creates a new docker image, copies it to the app server, runs the <code>docker-compose.yml</code> file and busts the Cloudflare cache to ensure a clean deployment when new changes are pushed to the main branch.
|
||||||
</Text>
|
</Text>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
|||||||
@@ -437,7 +437,7 @@ body:has(.menu-toggle:checked) {
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
gap: var(--space-2);
|
gap: var(--space-2);
|
||||||
border: 1px solid var(--color-slate-700);
|
border: 1px solid var(--color-white);
|
||||||
background-color: var(--color-black);
|
background-color: var(--color-black);
|
||||||
padding: 0.625rem 0.875rem;
|
padding: 0.625rem 0.875rem;
|
||||||
margin-top: var(--space-2);
|
margin-top: var(--space-2);
|
||||||
@@ -594,3 +594,16 @@ body:has(.menu-toggle:checked) {
|
|||||||
#tab3:checked ~ .tab-panels #panel3 {
|
#tab3:checked ~ .tab-panels #panel3 {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pre, code {
|
||||||
|
font-family: var(--font-mono), monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
code {
|
||||||
|
font-size: 0.9em;
|
||||||
|
padding: 0.125rem 0.375rem;
|
||||||
|
background-color: rgba(15, 23, 42, 0.8);
|
||||||
|
border: 1px solid rgba(100, 116, 139, 0.3);
|
||||||
|
border-radius: var(--radius-base);
|
||||||
|
color: var(--color-slate-50);
|
||||||
|
}
|
||||||
2
src/BlazorApp/wwwroot/robots.txt
Normal file
2
src/BlazorApp/wwwroot/robots.txt
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
User-agent: *
|
||||||
|
Allow: /
|
||||||
Reference in New Issue
Block a user