Personal website built with LiveView, generative art patterns and monospace web aesthetics.
Live: DROO.FOO
- Monospace Web Design: Character-perfect grid using 1ch units and rem-based line-height
- Blog System: File-based markdown posts with YAML frontmatter and series support
- Generative Patterns: Unique SVG patterns per post with 8 animation styles
- Real-time Updates: Phoenix LiveView for instant page updates
- Projects Showcase: GitHub integration with stats and contribution visualization
- Resume System: Structured JSON-based resume with filtering and search
- SEO Optimized: JSON-LD structured data for enhanced search engine visibility
- Web3 Wallet: Connect MetaMask for wallet authentication, ENS resolution, NFT/token viewing
- Spotify Player: OAuth integration with playback controls, playlist browsing, and real-time progress
- GitHub Stats: Repository visualization and contribution graphs
- STL Viewer: 3D model viewer with Three.js integration
# Install dependencies
mix setup
# Install 1Password CLI
# Install the desktop app
# https://1password.com/downloads
# https://developer.1password.com/docs/cli/app-integration/
brew install --cask 1password-cli
# Sign in to 1Password
op signin
# Create secrets in 1Password.
op item create --category=login --title="droodotfoo-dev" \
SPOTIFY_CLIENT_ID="your_client_id" \
SPOTIFY_CLIENT_SECRET="your_client_secret"
# Start server with secrets loaded from 1Password
./bin/dev
# After changes re-deploy with:
# 1. Deploy updated assets to CDN
./scripts/deploy-cdn.sh
# 2. Deploy Phoenix app to Fly.io
fly deploy# Set Spotify credentials (optional)
export SPOTIFY_CLIENT_ID="your_client_id"
export SPOTIFY_CLIENT_SECRET="your_client_secret"
# Start server
mix phx.serverVisit localhost:4000
The site implements Wickström's monospace web technique for character-perfect grid alignment:
Horizontal Grid (1ch units):
- Each character occupies exactly 1ch width
- Container widths snap to character boundaries:
calc(round(down, 80ch, 1ch)) - Table columns use character-based widths (8ch, 12ch, 20ch, etc.)
Vertical Grid (rem-based line-height):
- Fixed
--line-height: 1.5remfor predictable calculations - Prevents line-height compounding in nested elements
- Scales proportionally: 24px at 16px base, 21px at 14px mobile
Visual Refinements:
- Double-line horizontal rules with layered pseudo-elements
- Precision table padding compensates for border thickness
- Media element grid alignment (images/videos snap to line-height)
- All spacing uses multiples of 1ch or line-height
Benefits:
- Zero layout shift (every character positioned exactly)
- Maintainable vertical rhythm throughout content
- Professional terminal-inspired aesthetic
- Consistent across all themes and screen sizes
See wickstrom.tech for the original technique.
Built with modular Phoenix LiveView architecture:
Monospace Web Design: Character-perfect grid using Wickstrom's monospace web technique with 1ch horizontal units and rem-based line-height for predictable vertical rhythm. Double-line dividers, precision table padding, and media element grid alignment maintain visual consistency.
Rendering: Phoenix LiveView handles real-time updates over WebSockets.
State Management: GenServers orchestrate content loading and GitHub API integration. Functional patterns with immutable state throughout.
Performance: ETS caching for GitHub API data, blog post metadata, and SVG patterns. Pattern cache provides 568x speedup (26ms -> 47us). Brotli compression for static assets. Page loads under 200ms.
Content System: File-based blog posts with markdown + YAML frontmatter. Deterministic SVG pattern generation per post with 8 animation styles. No database required.
Test Coverage: 1084 tests passing (41.2% code coverage)
- Backend: Elixir 1.17+, Phoenix 1.8.1, LiveView 1.1.12, Bandit web server
- Styling: Tailwind CSS v4, Monaspace fonts (woff2 format with preload optimization)
- Monospace Grid: 1ch units + rem-based line-height for character-perfect alignment
- Frontend: TypeScript, esbuild for bundling, lazy-loaded hooks
- Content: MDEx for markdown parsing with syntax highlighting
- Caching: ETS for GitHub API, posts, patterns (568x speedup for patterns)
- Compression: Brotli for static assets (JS, CSS, SVG, fonts)
- SEO: JSON-LD structured data for enhanced search visibility
- Rust NIFs: ex_keccak, ex_secp256k1 (Web3 crypto), autumn, mdex (compiled from source)
- Optional: ethers.js (Web3), Three.js (STL viewer)
- Testing: ExUnit with 1084 tests passing
# Run tests
mix test
# Run specific test file
mix test test/droodotfoo/raxol_app_test.exs
# Format code
mix format
# Compile with warnings
mix compile --warning-as-errors
# Full precommit check (compile, deps, format, test)
mix precommit
# Generate ExDoc documentation
mix docs# Install Fly CLI
brew install flyctl
# Login to Fly.io
fly auth login
# Create fly.toml and guides through setup
# Fly.io will handle the routing from external ports (80/443) to your app's internal port 8080 (default)
# Even though our config/runtime.exs specifies port 4000
fly launch
# Or if you want to create the app manually:
fly apps create droodotfoo
# Set production secrets (required)
# once successfully set with fly secrets set, they're stored encrypted in Fly.io's infra
# and injected into your app as environment variables at runtime
fly secrets set \
SECRET_KEY_BASE=$(mix phx.gen.secret) \
PHX_HOST="droodotfoo.fly.dev"
# Set blog API token (required for Obsidian publishing)
fly secrets set BLOG_API_TOKEN=$(mix phx.gen.secret)
# Optional: Spotify integration
# obtain secrets from https://developer.spotify.com/dashboard
fly secrets set \
SPOTIFY_CLIENT_ID="prod_client_id" \
SPOTIFY_CLIENT_SECRET="prod_client_secret"
# Then register https://droo.foo/auth/spotify/callback in Spotify Dashboard
# https://developer.spotify.com/dashboard
# add redirect URI and the website link (i.e. `PHX_HOST`)
SPOTIFY_REDIRECT_URI="https://your-app.fly.dev/auth/spotify/callback"
# Optional: GitHub API token for higher rate limits (5000/hr vs 60/hr)
fly secrets set GITHUB_TOKEN="ghp_xxxxx"
# Optional: Configure Cloudflare Pages CDN for static assets
fly secrets set CDN_HOST="your-project.pages.dev"
# Deploy
fly deployThe Dockerfile compiles Rust NIFs from source because GitHub release assets are often blocked from CI builders. If deployment fails with NIF download errors, ensure these env vars are set in the Dockerfile:
ENV EX_KECCAK_BUILD="1"
ENV RUSTLER_BUILD="1"
ENV AUTUMN_BUILD="1"
ENV MDEX_BUILD="1"See docs/guides/deployment.md for complete environment variable reference.
Required: SECRET_KEY_BASE, PHX_HOST, BLOG_API_TOKEN
Optional: SPOTIFY_CLIENT_ID, SPOTIFY_CLIENT_SECRET, GITHUB_TOKEN, CDN_HOST
See docs/guides/security.md for comprehensive security documentation.
Key Features:
- OAuth 2.0 for Spotify, Bearer token for Blog API
- Rate limiting on all public endpoints
- Input validation with path traversal prevention
- HTTPS enforcement, CSP headers, secure cookies
- CLAUDE.md - Project overview, patterns, and AI assistant context
- AGENTS.md - Phoenix/Elixir development guidelines
- docs/README.md - Documentation index and quick start
- docs/TODO.md - Active tasks, priorities, and roadmap
- docs/guides/deployment.md - Fly.io production deployment
- docs/guides/security.md - Authentication, rate limiting, input validation
- docs/guides/seo.md - SEO optimization with JSON-LD
- docs/guides/assets.md - Image and asset optimization
Generate comprehensive documentation with typespecs:
# Generate HTML documentation
mix docs
# View in browser
open doc/index.htmlIncludes:
- Module documentation with examples
- Function signatures with @spec annotations
- Type definitions and behaviors
- Searchable interface