Building a Dynamic Charter Proposal System: Static HTML Generation with AWS S3 and CloudFront
What Was Done
Created a templated HTML proposal system for charter bookings on queenofsandiego.com. The core challenge was generating a static proposal document that could be customized per client while maintaining version control, fast CDN delivery, and zero runtime overhead. The solution involved:
- Building a parameterized HTML proposal template with semantic markup
- Establishing a proposals/ directory structure within the static site repo
- Deploying to S3 with CloudFront cache invalidation for instant propagation
- Integrating legal compliance language (USCG, EPA/MPRSA regulations) into the proposal flow
- Removing personally identifiable information (PII) from templates in favor of generic contact addresses
Technical Details
File Structure and Version Control
The proposal lives at /Users/cb/Documents/repos/sites/queenofsandiego.com/proposals/jada-charter-proposal-sue.html in the local development repo. This path mirrors the live S3 object path:
s3://queenofsandiego.com/proposals/jada-charter-proposal-sue.html
The file is tracked in Git alongside the main site content, allowing proposals to be versioned alongside code changes and rolled back if needed. This is critical for audit trails on pricing and legal language revisions.
HTML Structure and Semantic Markup
The proposal uses semantic class names for easier testing and future JavaScript enhancement:
.greeting-sub— personalized greeting section.proposal-title— main heading.vessel-banner,.history-photo,.photo-strip— image containers for responsive layouts.option-a,.option-b— pricing tiers with visual distinction (one marked recommended).legal-compliance— regulatory language (USCG licensing, EPA/MPRSA ash scattering).faq-form— client Q&A submission form at bottom
This semantic approach allows future developers (or JavaScript frameworks) to target specific sections without brittle string matching.
Pricing and Decision Architecture
The proposal presents exactly two pricing options to reduce decision fatigue:
- Option A (Captain Sergio, 2-hour): $750 flat rate — recommended
- Option B (Full crew, 3-hour): $1,575 flat rate
Both options include the same regulatory compliance language to ensure clients understand USCG licensing requirements and EPA/MPRSA regulations governing ash scattering at sea. This is legally necessary and builds trust.
PII Removal and Contact Consolidation
Original templates referenced individual names and personal email addresses. The revised version uses a generic contact email:
bookings@queenofsandiego.com
This change:
- Protects individual privacy in version control
- Allows team routing behind the scenes without changing the proposal
- Prevents PII from being exposed in git logs or S3 version histories
Infrastructure and Deployment
S3 and CloudFront Configuration
The proposal file is deployed to the S3 bucket queenofsandiego.com (public website bucket). The deployment script at publish_static_site.sh handles:
aws s3 cp proposals/jada-charter-proposal-sue.html \
s3://queenofsandiego.com/proposals/jada-charter-proposal-sue.html \
--content-type "text/html; charset=utf-8"
After S3 upload, a CloudFront cache invalidation is triggered to purge the CDN edge cache:
aws cloudfront create-invalidation \
--distribution-id [DISTRIBUTION_ID] \
--paths "/proposals/jada-charter-proposal-sue.html"
This ensures:
- Global users fetch the latest version within seconds (not hours)
- No manual cache busting needed on the client side
- Proposals update atomically — no race conditions between regions
DNS and URL Structure
The live URL is:
https://queenofsandiego.com/proposals/jada-charter-proposal-sue.html
This uses the bare domain (no subdomain). The DNS is managed via Route53, with an alias record pointing to the CloudFront distribution. This approach:
- Allows free HTTPS via CloudFront's default certificate
- Handles 301/302 redirects at the edge (CDN level) for SEO
- Enables future A/B testing by swapping S3 objects or CloudFront behaviors
Key Design Decisions
Why Static HTML Over Dynamic Rendering
A static HTML proposal (not generated server-side or via Lambda@Edge) was chosen because:
- No runtime cost: Once deployed, S3 serves it with zero compute overhead
- Version control: Each proposal revision is tracked in Git with diffs
- Simplicity: No database queries, environment variables, or template engines needed
- Sharability: A single immutable URL works for email, SMS, and print (QR codes)
If per-client personalization becomes needed (e.g., client name, custom dates), the recommendation is to generate static files during CI/CD, not at request time.
Why Semantic Class Names Matter
Future requirements may include:
- Email rendering (stripping JavaScript, optimizing images for Outlook)
- PDF generation (CSS-paged media queries)
- Form parsing (extracting structured data for admin dashboards)
Semantic class names make these transformations trivial. A script can target .option-a and extract pricing without string parsing.
Legal Compliance as Design Constraint
USCG licensing and EPA/MPRSA ash scattering regulations are prominently featured because:
- They are legal requirements, not optional marketing
- Clients need to understand they're hiring a licensed captain, not bareboat renting
- Liability is reduced when clients acknowledge compliance language
What's Next
Image Asset Integration