Initial commit... a site is born (again? finally?)
This commit is contained in:
465
README.md
Normal file
465
README.md
Normal file
@@ -0,0 +1,465 @@
|
||||
# mifi Ventures Landing Site
|
||||
|
||||
A minimal, production-ready static website for mifi Ventures, LLC — a software engineering consulting business.
|
||||
|
||||
## 🏗️ Technology Stack
|
||||
|
||||
- **Frontend**: Pure semantic HTML5, modern CSS with CSS variables, minimal JavaScript
|
||||
- **Server**: nginx (Alpine Linux)
|
||||
- **Containerization**: Docker
|
||||
- **CI/CD**: Woodpecker CI
|
||||
- **Deployment**: Linode VPS
|
||||
|
||||
## 🎨 Features
|
||||
|
||||
- ✅ **Single-page design** with anchored sections
|
||||
- ✅ **Responsive** and mobile-friendly
|
||||
- ✅ **Light/Dark mode** via `prefers-color-scheme`
|
||||
- ✅ **WCAG 2.2 AAA oriented** with strong focus states, keyboard navigation, semantic markup
|
||||
- ✅ **SEO optimized** with Open Graph, Twitter Cards, JSON-LD structured data
|
||||
- ✅ **Performance optimized** with nginx gzip compression and cache headers
|
||||
- ✅ **Zero frameworks** — pure HTML/CSS/JS for maximum speed and simplicity
|
||||
|
||||
## 🚀 Local Development
|
||||
|
||||
This project uses **pnpm** as the package manager. After cloning, run `pnpm install` (or ensure Corepack is enabled so `pnpm` is available).
|
||||
|
||||
| Command | Description |
|
||||
|---------|-------------|
|
||||
| `pnpm install` | Install dependencies |
|
||||
| `pnpm run dev` | Serve `site/` at http://localhost:3000 with **live reload** (watcher) |
|
||||
| `pnpm run build` | Copy `site/` → `dist/` and inline critical CSS in `index.html` |
|
||||
| `pnpm run preview` | Serve built `dist/` to test production output |
|
||||
|
||||
### Option 1: pnpm dev (recommended for editing)
|
||||
|
||||
From the project root:
|
||||
|
||||
```bash
|
||||
pnpm run dev
|
||||
```
|
||||
|
||||
Opens http://localhost:3000 with live reload when you change files in `site/`.
|
||||
|
||||
### Option 2: Other local servers (quick start)
|
||||
|
||||
Open `site/index.html` directly in a browser, or use a simple HTTP server:
|
||||
|
||||
```bash
|
||||
# Python 3
|
||||
cd site
|
||||
python3 -m http.server 8000
|
||||
|
||||
# Node (if you prefer not to use pnpm dev)
|
||||
cd site
|
||||
pnpm exec serve .
|
||||
|
||||
# PHP
|
||||
cd site
|
||||
php -S localhost:8000
|
||||
```
|
||||
|
||||
Then visit the URL shown (e.g. `http://localhost:8000`).
|
||||
|
||||
### Option 3: Dev Container
|
||||
|
||||
Open the project in a dev container for a consistent local environment:
|
||||
|
||||
1. **Open in Cursor or VS Code** with the [Dev Containers](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers) extension installed.
|
||||
2. **Reopen in Container**: Command Palette (`Cmd/Ctrl+Shift+P`) → **Dev Containers: Reopen in Container**.
|
||||
3. Wait for the container to build and start.
|
||||
|
||||
**Inside the container**, run:
|
||||
|
||||
```bash
|
||||
pnpm install
|
||||
pnpm run dev
|
||||
```
|
||||
|
||||
The site is served at **http://localhost:3000** with live reload (port forwarded automatically).
|
||||
|
||||
### Option 4: Docker (Production-like Test)
|
||||
|
||||
To test the production nginx image locally (same as deployed):
|
||||
|
||||
```bash
|
||||
docker build -t mifi-ventures-landing .
|
||||
docker run -d -p 8080:80 --name mifi-ventures-landing mifi-ventures-landing
|
||||
```
|
||||
|
||||
Then visit: `http://localhost:8080`. Stop with `docker stop mifi-ventures-landing && docker rm mifi-ventures-landing`.
|
||||
|
||||
## 📝 Content Updates
|
||||
|
||||
The HTML file includes an editable constants block at the top for easy updates:
|
||||
|
||||
```html
|
||||
<!--
|
||||
EDITABLE CONSTANTS:
|
||||
- DOMAIN
|
||||
- ORG_NAME
|
||||
- PRINCIPAL_NAME
|
||||
- CAL_LINK
|
||||
- RESUME_PATH
|
||||
- LINKEDIN_URL
|
||||
- GITHUB_URL
|
||||
-->
|
||||
```
|
||||
|
||||
Update these values directly in `site/index.html` to modify:
|
||||
- Company information
|
||||
- Calendar booking link
|
||||
- Social media links
|
||||
- Resume file path
|
||||
|
||||
## 🗂️ Project Structure
|
||||
|
||||
```
|
||||
mifi-ventures-landing/
|
||||
├── .devcontainer/ # Dev container for local development
|
||||
│ ├── devcontainer.json # Dev container config (port 3000, extensions)
|
||||
│ └── Dockerfile # Dev container image (Node + serve)
|
||||
├── .woodpecker.yml # CI/CD pipeline configuration
|
||||
├── Dockerfile # Production container (nginx:alpine)
|
||||
├── nginx.conf # nginx web server configuration
|
||||
├── README.md # This file
|
||||
├── .gitignore # Git ignore rules
|
||||
└── site/ # Static website files
|
||||
├── index.html # Main HTML file
|
||||
├── styles.css # CSS styles (light/dark mode)
|
||||
├── script.js # Minimal JavaScript (dynamic year)
|
||||
├── robots.txt # Search engine directives
|
||||
├── favicon.svg # Site favicon
|
||||
└── assets/
|
||||
├── resume.pdf # Resume download (placeholder)
|
||||
└── logos/ # Company logo SVGs
|
||||
├── atlassian.svg
|
||||
├── tjx.svg
|
||||
├── cargurus.svg
|
||||
├── timberland.svg
|
||||
└── mfa-boston.svg
|
||||
```
|
||||
|
||||
## 🚢 CI/CD Deployment (Woodpecker + Gitea)
|
||||
|
||||
> 📖 **Full deployment guide**: See [DEPLOYMENT.md](DEPLOYMENT.md) for step-by-step setup instructions, troubleshooting, and examples.
|
||||
|
||||
### Pipeline Overview
|
||||
|
||||
The `.woodpecker.yml` pipeline automates deployment on push to `main`:
|
||||
|
||||
1. **Build** — Builds Docker image tagged with commit SHA + `latest`
|
||||
2. **Push** — Pushes images to private Docker registry
|
||||
3. **Deploy** — SSH to Linode VPS, pulls latest image, restarts container with health checks
|
||||
|
||||
### Required Configuration
|
||||
|
||||
#### Secrets (Configure in Woodpecker UI)
|
||||
|
||||
Navigate to your repository → Settings → Secrets and add:
|
||||
|
||||
| Secret Name | Description | Example |
|
||||
|-------------|-------------|---------|
|
||||
| `registry_password` | Docker registry password or token | `dckr_pat_xxxxx` |
|
||||
| `deploy_host` | Linode VPS hostname or IP | `vps.example.com` or `192.0.2.1` |
|
||||
| `deploy_username` | SSH username | `deploy` or `root` |
|
||||
| `deploy_ssh_key` | Private SSH key (multi-line) | `-----BEGIN OPENSSH PRIVATE KEY-----...` |
|
||||
| `deploy_port` | SSH port | `22` (default) |
|
||||
|
||||
**Generate SSH key for deployment:**
|
||||
```bash
|
||||
ssh-keygen -t ed25519 -C "woodpecker-deploy" -f ~/.ssh/woodpecker_deploy
|
||||
# Add public key to server: ssh-copy-id -i ~/.ssh/woodpecker_deploy.pub user@host
|
||||
# Copy private key content to Woodpecker secret
|
||||
```
|
||||
|
||||
#### Environment Variables (Configure in Woodpecker)
|
||||
|
||||
Set these as repository or organization-level variables:
|
||||
|
||||
| Variable | Description | Example |
|
||||
|----------|-------------|---------|
|
||||
| `REGISTRY_URL` | Docker registry base URL | `registry.example.com` |
|
||||
| `REGISTRY_REPO` | Full image repository path | `registry.example.com/mifi-ventures-landing` |
|
||||
| `REGISTRY_USERNAME` | Registry username | `myusername` |
|
||||
| `CONTAINER_NAME` | Container name on server | `mifi-ventures-landing` |
|
||||
| `APP_PORT` | Host port to expose | `8080` (or `80` if direct) |
|
||||
|
||||
#### Example Configuration
|
||||
|
||||
**In Woodpecker UI (Repository Settings):**
|
||||
|
||||
```yaml
|
||||
# Secrets (Values tab)
|
||||
registry_password: "your-registry-token"
|
||||
deploy_host: "123.45.67.89"
|
||||
deploy_username: "deploy"
|
||||
deploy_ssh_key: |
|
||||
-----BEGIN OPENSSH PRIVATE KEY-----
|
||||
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
|
||||
...
|
||||
-----END OPENSSH PRIVATE KEY-----
|
||||
deploy_port: "22"
|
||||
|
||||
# Environment Variables (Variables tab)
|
||||
REGISTRY_URL: "registry.example.com"
|
||||
REGISTRY_REPO: "registry.example.com/mifi-ventures-landing"
|
||||
REGISTRY_USERNAME: "myuser"
|
||||
CONTAINER_NAME: "mifi-ventures-landing"
|
||||
APP_PORT: "8080"
|
||||
```
|
||||
|
||||
### Pipeline Features
|
||||
|
||||
- ✅ **Deterministic builds** — Commit SHA tagging ensures reproducibility
|
||||
- ✅ **Fail-fast** — Exits immediately on any error (`set -e`)
|
||||
- ✅ **Health checks** — Verifies container starts and responds before completing
|
||||
- ✅ **Automatic cleanup** — Prunes old images older than 72 hours
|
||||
- ✅ **Zero-downtime** — Old container runs until new one is healthy
|
||||
- ✅ **Detailed logging** — Clear output at each stage
|
||||
|
||||
### Troubleshooting
|
||||
|
||||
**Build fails:**
|
||||
```bash
|
||||
# Check Dockerfile syntax
|
||||
docker build -t test .
|
||||
|
||||
# Verify files are present
|
||||
ls -la site/
|
||||
```
|
||||
|
||||
**Push fails:**
|
||||
```bash
|
||||
# Test registry login locally
|
||||
echo "PASSWORD" | docker login registry.example.com -u username --password-stdin
|
||||
|
||||
# Verify registry URL and credentials
|
||||
```
|
||||
|
||||
**Deploy fails:**
|
||||
```bash
|
||||
# Test SSH connection
|
||||
ssh -i ~/.ssh/key user@host "docker ps"
|
||||
|
||||
# Check if Docker is installed on server
|
||||
ssh user@host "docker --version"
|
||||
|
||||
# Verify environment variables are passed
|
||||
# Check Woodpecker build logs for "REGISTRY_URL" values
|
||||
```
|
||||
|
||||
**Container fails health check:**
|
||||
```bash
|
||||
# SSH to server and check logs
|
||||
ssh user@host "docker logs mifi-ventures-landing"
|
||||
|
||||
# Check if port is already in use
|
||||
ssh user@host "netstat -tulpn | grep :80"
|
||||
```
|
||||
|
||||
### Manual Deployment
|
||||
|
||||
For emergency deployments or testing:
|
||||
|
||||
```bash
|
||||
# Build and push manually
|
||||
docker build -t registry.example.com/mifi-ventures-landing:latest .
|
||||
docker push registry.example.com/mifi-ventures-landing:latest
|
||||
|
||||
# Deploy manually via SSH
|
||||
ssh user@host << 'EOF'
|
||||
docker pull registry.example.com/mifi-ventures-landing:latest
|
||||
docker stop mifi-ventures-landing || true
|
||||
docker rm mifi-ventures-landing || true
|
||||
docker run -d \
|
||||
--name mifi-ventures-landing \
|
||||
--restart unless-stopped \
|
||||
-p 8080:80 \
|
||||
registry.example.com/mifi-ventures-landing:latest
|
||||
EOF
|
||||
```
|
||||
|
||||
## 🔧 nginx Configuration
|
||||
|
||||
The custom `nginx.conf` provides optimized static file delivery:
|
||||
|
||||
### Caching Strategy
|
||||
- **HTML files**: `no-cache, must-revalidate` (always fresh from server)
|
||||
- **CSS/JS**: `max-age=31536000, immutable` (1 year, content-addressed)
|
||||
- **Images** (JPG, PNG, WebP, AVIF): `max-age=2592000` (30 days)
|
||||
- **SVG images**: `max-age=2592000` (30 days)
|
||||
- **Fonts**: `max-age=31536000, immutable` (1 year)
|
||||
- **Documents** (PDF): `max-age=2592000` (30 days)
|
||||
- **robots.txt**: `max-age=86400` (1 day)
|
||||
- **favicon.svg**: `max-age=2592000` (30 days)
|
||||
|
||||
### Gzip Compression
|
||||
Enabled for all text-based content with compression level 6:
|
||||
- HTML, CSS, JavaScript
|
||||
- JSON, XML
|
||||
- SVG images
|
||||
|
||||
Minimum size: 256 bytes (avoids compressing tiny files)
|
||||
|
||||
### Other Features
|
||||
- **Server tokens**: Disabled for security
|
||||
- **Access logs**: Disabled for static assets (performance)
|
||||
- **Hidden files**: Denied (.git, .env, etc.)
|
||||
- **404 handling**: Falls back to index.html
|
||||
- **Health check**: Available on port 80 for container orchestration
|
||||
|
||||
### Security Headers
|
||||
**Note**: Security headers (CSP, HSTS, X-Frame-Options, etc.) are handled upstream by Traefik and are NOT included in this nginx configuration to avoid duplication.
|
||||
|
||||
## 🎯 SEO & Performance
|
||||
|
||||
### Current Optimizations
|
||||
|
||||
#### On-Page SEO
|
||||
- **Title tag**: Includes business name, service, and location
|
||||
- **Meta description**: Natural, compelling copy (155 characters) emphasizing Boston location and services
|
||||
- **Canonical URL**: Set to `https://mifi.ventures/` to prevent duplicate content issues
|
||||
- **Robots meta**: `index, follow` with enhanced directives for snippet and image preview control
|
||||
- **Semantic HTML5**: Proper heading hierarchy (single H1, logical H2 structure)
|
||||
- **Geographic metadata**: Boston, MA coordinates for local SEO
|
||||
- **Author attribution**: Mike Fitzpatrick properly credited
|
||||
- **Language declaration**: `lang="en-US"` for US English
|
||||
|
||||
#### Social Media Share Previews
|
||||
- **Open Graph tags**: Complete OG implementation for Facebook, LinkedIn
|
||||
- Site name, title, description, URL, image
|
||||
- Image dimensions (1200x630px) and alt text
|
||||
- Locale set to `en_US`
|
||||
- **Twitter Cards**: `summary_large_image` card with full metadata
|
||||
- Creator and site handles (update with actual Twitter)
|
||||
- Image with alt text for accessibility
|
||||
- **Theme colors**: Dynamic based on light/dark mode preference
|
||||
|
||||
#### Structured Data (JSON-LD)
|
||||
Comprehensive @graph structure with interconnected entities:
|
||||
- **Organization** (`#organization`): mifi Ventures, LLC with Boston address, geo coordinates, and service catalog
|
||||
- **Person** (`#principal`): Mike Fitzpatrick as "Principal Software Engineer and Architect" with worksFor relationship and knowsAbout expertise areas
|
||||
- **WebSite** (`#website`): Site-level metadata with ReserveAction pointing to Cal.com scheduling
|
||||
- **WebPage** (`#webpage`): Page-level metadata with inLanguage and primaryImageOfPage
|
||||
- **OfferCatalog** (`#services`): Six service offerings aligned with "What We Do" section
|
||||
- **LinkedIn profile**: https://linkedin.com/in/the-mifi
|
||||
- **No email or phone**: Complies with privacy requirements
|
||||
|
||||
#### Technical SEO
|
||||
- **robots.txt**: Properly configured for full site crawling
|
||||
- **Lazy loading**: Images load on-demand for performance
|
||||
- **Minimal JavaScript**: Only essential scripts (copyright year)
|
||||
- **System font stack**: No web font loading delays
|
||||
- **Clean URLs**: No parameters or session IDs
|
||||
- **Mobile-friendly**: Responsive design, passes mobile-usability tests
|
||||
- **Fast loading**: Optimized assets, gzip compression, cache headers
|
||||
|
||||
### Action Items
|
||||
|
||||
Before launch, update these placeholders:
|
||||
1. Create OG image: 1200x630px PNG at `/assets/og-image.png`
|
||||
2. Update Twitter handles in meta tags (lines 57-58) if you have a Twitter presence
|
||||
3. Update GitHub URL in footer and constants if you want to include it (currently optional)
|
||||
|
||||
### SEO Testing & Validation
|
||||
|
||||
Before going live, validate with these tools:
|
||||
- **Google Search Console**: Submit site, monitor indexing
|
||||
- **Rich Results Test**: Verify JSON-LD structured data
|
||||
- **Facebook Sharing Debugger**: Test OG tags preview
|
||||
- **Twitter Card Validator**: Test Twitter card appearance
|
||||
- **Lighthouse SEO Audit**: Aim for 100/100 score
|
||||
- **Mobile-Friendly Test**: Ensure mobile usability
|
||||
- **PageSpeed Insights**: Check Core Web Vitals
|
||||
|
||||
Key metrics to monitor post-launch:
|
||||
- Indexing status in Google Search Console
|
||||
- Click-through rates (CTR) from search results
|
||||
- Share engagement on social platforms
|
||||
- Core Web Vitals (LCP, FID, CLS)
|
||||
- Page load times and performance scores
|
||||
|
||||
### Future Enhancements
|
||||
|
||||
- Add sitemap.xml for better crawl efficiency
|
||||
- Implement Content Security Policy (CSP) headers
|
||||
- Add preconnect hints for external resources (Cal.com)
|
||||
- Consider blog or case studies for content marketing
|
||||
- Add FAQ schema markup if adding FAQ section
|
||||
- Consider breadcrumb schema for better SERP display
|
||||
- Local business listings (Google Business Profile)
|
||||
- Schema markup for reviews/testimonials if applicable
|
||||
|
||||
## ♿ Accessibility
|
||||
|
||||
This site is built to meet **WCAG 2.2 Level AAA** standards wherever applicable to a static informational website.
|
||||
|
||||
### Implemented Features
|
||||
|
||||
#### Keyboard Navigation
|
||||
- **Skip link**: Visible on keyboard focus, jumps directly to main content (`#main`)
|
||||
- **Logical tab order**: All interactive elements follow natural reading order
|
||||
- **No keyboard traps**: Users can navigate through and exit all interactive regions
|
||||
- **Focus indicators**: 4px high-contrast outlines with 4px offset and subtle glow on all focusable elements
|
||||
- **Focus never removed**: Outline styles are enforced with `!important` to prevent accidental removal
|
||||
|
||||
#### Semantic Structure
|
||||
- **Proper landmarks**: `<header>`, `<main>`, `<footer>`, and `<nav>` for clear page regions
|
||||
- **Single H1**: One `<h1>` element ("mifi Ventures") with logical H2 nesting for all sections
|
||||
- **ARIA labelledby**: All sections connected to their headings via `aria-labelledby` attributes
|
||||
- **Language declaration**: `lang="en-US"` attribute on `<html>` element
|
||||
|
||||
#### Visual & Color
|
||||
- **AAA contrast ratios**: All text meets AAA standards (7:1 for normal text, 4.5:1 for large text)
|
||||
- Light mode: `#1a1a1a` text on `#ffffff` background (16.1:1 ratio)
|
||||
- Dark mode: `#f5f5f5` text on `#0a0a0a` background (18.4:1 ratio)
|
||||
- **Color independence**: No information conveyed by color alone
|
||||
- **High contrast mode**: Enhanced borders, outlines, and contrast for users with `prefers-contrast: high`
|
||||
|
||||
#### Interactive Elements
|
||||
- **Adequate touch targets**: All buttons and links meet minimum 44x44px size (AAA requirement)
|
||||
- **Descriptive link text**: All links have meaningful text or enhanced ARIA labels
|
||||
- **External link warnings**: Links opening in new tabs clearly labeled "(opens in new tab)"
|
||||
- **Button spacing**: Generous gaps between CTAs prevent accidental activation
|
||||
|
||||
#### Motion & Animation
|
||||
- **Respects `prefers-reduced-motion`**: All animations and transforms disabled when users prefer reduced motion
|
||||
- **Safe default animations**: Subtle hover effects that don't cause vestibular issues
|
||||
- **No auto-playing content**: No carousels, videos, or content that moves automatically
|
||||
|
||||
#### Images & Media
|
||||
- **Descriptive alt text**: All images have clear, concise alternative text
|
||||
- **Text fallbacks**: Logo strip includes visually-hidden text that appears if images fail
|
||||
- **Mobile text list**: On small screens, logo images replaced with accessible text list
|
||||
- **Decorative images marked**: Images that don't convey content use appropriate ARIA attributes
|
||||
|
||||
#### Screen Reader Support
|
||||
- **Clear labels**: All form controls, buttons, and navigation have proper labels
|
||||
- **ARIA landmarks**: Supplementary ARIA roles for enhanced screen reader navigation
|
||||
- **Visually-hidden content**: Important text available to screen readers but hidden visually where appropriate
|
||||
- **Logical reading order**: Content structure follows visual hierarchy
|
||||
|
||||
### Testing Recommendations
|
||||
|
||||
For best results, test with:
|
||||
- **Keyboard only**: Tab through entire page without mouse
|
||||
- **Screen readers**: NVDA (Windows), JAWS (Windows), VoiceOver (macOS/iOS), TalkBack (Android)
|
||||
- **Browser extensions**: axe DevTools, WAVE, Lighthouse accessibility audit
|
||||
- **High contrast mode**: Windows High Contrast, macOS Increase Contrast
|
||||
- **Zoom**: Test at 200% and 400% zoom levels
|
||||
- **Reduced motion**: Enable in OS settings and verify animations stop
|
||||
|
||||
### Known Limitations
|
||||
|
||||
- Logo images use CSS filters for dark mode adaptation (works well but may not be perfect for all logos)
|
||||
- External link icons not implemented (relies on ARIA labels and "opens in new tab" text)
|
||||
- No live regions or dynamic content requiring ARIA live announcements
|
||||
|
||||
## 📄 License
|
||||
|
||||
© 2026 mifi Ventures, LLC. All rights reserved.
|
||||
|
||||
## 📞 Contact
|
||||
|
||||
For inquiries, visit [mifi.ventures](https://mifi.ventures/) or [schedule a call](https://cal.mifi.ventures/the-mifi).
|
||||
Reference in New Issue
Block a user