Overview
A full-stack website for a local vintage shop built with a serverless JAMstack architecture. Features a client-side content management system, event management, form submissions with email notifications, and comprehensive accessibility features. The site emphasizes community connection and face-to-face relationships, serving as both an online presence and community hub.
Problem & Context
Tender Heart Vintage needed a modern, accessible website that could be managed by non-technical staff without requiring a traditional database or server infrastructure. The solution needed to support community events, consignment services, and content updates while maintaining fast performance and WCAG 2.1 AA accessibility compliance. The architecture had to be cost-effective, scalable, and easy to deploy.
Constraints
- Must be manageable by non-technical staff without database knowledge
- No traditional server infrastructure (serverless architecture required)
- Must achieve WCAG 2.1 AA accessibility compliance
- Cost-effective hosting solution
- Fast performance and good SEO
- Support for events, consignment, and content management
- Progressive enhancement (works without JavaScript)
Approach & Design Decisions
I chose a JAMstack architecture with serverless functions:
- Static Site Generation: HTML/CSS/JS served from Cloudflare Pages CDN for fast performance
- Serverless API: Cloudflare Workers for API endpoints (events, listings, form submissions)
- Object Storage: Cloudflare R2 for persistent data storage (events, featured items, form submissions)
- Client-Side CMS: localStorage for immediate access with R2 synchronization for persistence
- Progressive Enhancement: Core functionality works without JavaScript, enhanced features load progressively
The dual storage strategy (localStorage + R2) provides immediate access while maintaining persistence, and the serverless architecture eliminates server management overhead while keeping costs low.
Technical Implementation
Serverless Architecture
Deployed on Cloudflare Pages with Workers for API endpoints, eliminating server management overhead. The architecture scales automatically and provides global CDN distribution for fast load times.
Progressive Enhancement
Site functions without JavaScript: core navigation, content display, and form submissions work with HTML/CSS only. Enhanced features (CMS, real-time updates, animations) load progressively for better accessibility.
Dual Storage Strategy
Content stored in both localStorage (immediate access) and Cloudflare R2 (persistence), with automatic sync. The API client automatically falls back to localStorage when API calls fail, ensuring the site always functions.
Timezone Handling
Created custom parseLocalDate() function to handle date-only strings in local timezone, preventing UTC conversion bugs. This ensures event dates display correctly regardless of user timezone.
Cross-Tab Communication
Real-time content updates across browser tabs using localStorage events. When content is updated in one tab, other tabs automatically refresh to show the latest data.
Accessibility-First Design
WCAG 2.1 AA compliant with keyboard navigation, ARIA labels, high contrast ratios, and screen reader support. Semantic HTML5 ensures proper document structure and screen reader compatibility.
// Code coming soon...
// Implementation details will be added hereArchitecture
Client (HTML/CSS/JS)
↓ fetch()
Cloudflare Workers (functions/api/)
↓ R2 API
Cloudflare R2 (Object Storage)
↓ Resend API
Email Notifications
Content Flow:
- Admin updates content → localStorage → API → R2 → Frontend fetches from R2
- Form submissions → API → R2 storage + Email notification
- Images → Upload API → R2 → Served via CDN
Key Concepts Demonstrated
- JAMstack Architecture: JavaScript, APIs, and Markup without traditional server infrastructure
- Serverless Functions: Stateless API endpoints that scale automatically
- Progressive Enhancement: Core functionality works without JavaScript, enhanced features load progressively
- Client-Side State Management: localStorage for immediate access with R2 for persistence
- RESTful API Design: Standard HTTP methods (GET, POST, PUT, DELETE) with proper status codes
- Caching Strategies: ETag validation, Cache-Control headers, and client-side cache busting
- Error Handling & Fallbacks: Graceful degradation when API calls fail, fallback to localStorage
- Cross-Tab Communication: Using localStorage events for real-time synchronization
- Timezone Handling: Parsing date-only strings in local timezone to prevent UTC conversion issues
- Accessibility Patterns: Semantic HTML, ARIA attributes, keyboard navigation, screen reader support
What I Learned
Building a serverless JAMstack site taught valuable lessons about modern web architecture:
- Serverless Benefits: Eliminating server management reduces operational overhead and costs
- Progressive Enhancement: Building core functionality first ensures accessibility and reliability
- Dual Storage Strategy: Combining localStorage and cloud storage provides both speed and persistence
- Timezone Complexity: Date handling requires careful consideration of timezone conversions
- Accessibility First: Building with accessibility in mind from the start is easier than retrofitting
Next Steps
- Add automated testing suite (unit tests for date parsing, integration tests for API endpoints)
- Implement proper authentication system (currently uses localStorage, should use server-side sessions)
- Add image optimization pipeline (WebP conversion, responsive image sets)
- Implement rate limiting on API endpoints to prevent abuse
- Add monitoring and error tracking (e.g., Sentry integration)
- Create build pipeline for CSS/JS minification and bundling
- Add TypeScript for better type safety in complex functions
