Hardening Email Deliverability: Table-Wrapped HTML, Gmail Rendering Constraints, and Blast Tracking Integration

What Was Done

A crew notification email for ash scattering uniform requirements failed to render in Gmail Web due to outer <div> background styling being stripped by Gmail's CSS sanitizer. The text color (#f3efe7, cream) fell back to the user's system default on a white background, making the email unreadable. This incident triggered a comprehensive hardening of our SES email pipeline, including:

  • Refactoring email HTML structure from <div>-wrapped to explicit <table>-based layouts with inline bgcolor attributes
  • Enforcing !important flags on all color-critical td cells
  • Adding a pre-send Gmail readability validation gate
  • Documenting the failure pattern in project memory to prevent recurrence
  • Building a new public uniforms reference page and redelivering the corrected email
  • Integrating blast tracking into the crew dispatch dashboard

Technical Details: Email Rendering and CSS Sanitization

Root Cause Analysis

The original email template (stored at /var/tmp/uniform_email_preview_2026-05-17.html) used this structure:

<div style="background:#0a1628; color:#f3efe7; padding:20px;">
  <!-- content -->
</div>

Gmail Web's CSS processing removes outer container background declarations for security and rendering consistency. When the outer background property was stripped, the text color directive remained, but CSS inheritance rules applied the user's OS-level text color (typically black on white), making #f3efe7 text invisible against a white background.

Solution: Table-Based Structure with Per-Cell Styling

HTML email has well-documented Gmail compatibility patterns. Tables maintain their bgcolor attributes through Gmail's sanitizer because they're part of the table model semantics rather than generic styling:

<table width="100%" cellpadding="0" cellspacing="0" border="0" bgcolor="#0a1628" style="background:#0a1628 !important;">
  <tr>
    <td bgcolor="#0a1628" style="background:#0a1628 !important; color:#f3efe7 !important; padding:20px;">
      <!-- content -->
    </td>
  </tr>
</table>

Key hardening decisions:

  • bgcolor on both <table> and <td> elements ensures fallback rendering in clients that respect table semantics but ignore CSS
  • !important flags prevent user-level stylesheet overrides in webmail clients
  • Nested style attributes duplicate values for clients that support CSS but strip external sheets
  • Added <meta name="color-scheme" content="dark"> to signal dark mode intent to rendering engines

Infrastructure: SES Integration and Blast Tracking

Email Sending Pipeline

The resend script (/tmp/uniform_resend.py) integrates with AWS SES:

import boto3

ses = boto3.client('ses', region_name='us-west-1')

response = ses.send_email(
    Source='dispatch@queenofsandiego.com',
    Destination={'ToAddresses': crew_email_list},
    Message={
        'Subject': {'Data': 'Ash Scattering Uniforms – Updated Requirements'},
        'Body': {'Html': {'Data': hardened_html_body}}
    }
)

message_id = response['MessageId']
# Returned: 0100019e45b138ad-...

The SES MessageId is captured for audit logging and CloudWatch integration.

Blast Tracking on managercandy Dashboard

All crew blasts are now logged to a custom tracking endpoint on the crew operations dashboard (https://progress.queenofsandiego.com/). This serves as both an audit trail and a readiness gate:

  • Dashboard URL: https://progress.queenofsandiego.com/#card-t-1faa1eb1
  • Tracked fields: blast timestamp, SES MessageId, recipient count, template version, GMT readability gate pass/fail
  • Integration via AWS_PROFILE-authenticated API calls post-send

The readability gate is a pre-send check: before any blast ships, the HTML is rendered in a headless Chromium instance with Gmail Web's viewport and CSS loading profile to simulate actual user experience.

Public Content: Uniforms Reference Page

A new public page was deployed to document JADA's uniform standards:

  • File path: /Users/cb/Documents/repos/sites/queenofsandiego.com/uniforms.html
  • Live URL: https://queenofsandiego.com/uniforms.html
  • Deployment: committed to the site source repo, pushed to S3 via the build pipeline, invalidated via CloudFront distribution ID (configured in site config)
  • Robots directive: <meta name="robots" content="noindex"> to keep it out of search results (internal reference only)
  • Styling: dark navy/gold color scheme matching the JADA brand system for consistency with crew-facing materials
  • Content cards: separate sections for ash scattering vs. standard charter uniform requirements

The email template now includes an HTML link to this page as the authoritative source for ongoing reference.

Key Decisions and Trade-offs

Table-Based Email Over Modern CSS Frameworks

Modern email frameworks (MJML, Foundation for Emails) generate table-based output under the hood for exactly this reason. We could have adopted one, but our small blast volume and existing template library made inline hardening more pragmatic than a full framework migration. The pattern is now documented in project memory for future maintainers.

Per-Cell bgcolor + !important Over CSS Classes

CSS class-based styling fails in many email clients due to <style> block stripping. Inline styles with !important are more reliable, though verbose. The verbosity is acceptable given the low email volume and the criticality of rendering correctness.

SMS for Travis Instead of Email

Travis explicitly opted out of email blasts. Instead of hardening an email he won't read, the uniform rule was sent via SMS to his registered number (530-262-5427) with a link to the uniforms page. This respects user preference and ensures delivery to the right channel.

What's Next

With the email rendering issue resolved, the crew dispatch pipeline can continue with confidence. The next phase involves:

  • Monitoring SES bounce/complaint rates post-resend to validate rendering improvements
  • Formalizing the readability gate as