Case Study: HTML Email Compiler & Component System

The Problem: Quality Decay and Technical Debt

  • The Situation: Legacy HTML templates were being manually edited by teams of different HTML coding skill levels, leading to frequent "code breaking" and a high volume of emergency technical support tickets.
  • The Constraint: "No-Code" builders (Mailchimp, Pardot) failed to meet the agency’s strict standards for responsiveness and custom design, leaving the team stuck between poor quality or high-risk manual coding.
  • The Goal: Create a system that empowered non-technical staff to deploy responsive emails without altering any raw code.

The Action: Creating a Proprietary "Component-Based" Compiler

  • Architecture: Built a full-stack web application using modular Vanilla JavaScript. By creating a custom "component" system, I isolated "bulletproof," thoroughly tested email code blocks (like buttons, headers, and tricky nested tables) completely away from the text and images.
  • Data Management: Implemented a NoSQL (DynamoDB) backend to store template configurations as JSON objects, ensuring a seamless data flow between the UI and the final HTML output.
  • State Management: Developed a custom system to handle "Universal Fields." This allowed a user to change a brand color or logo once and have it instantly update across the entire email template. I also integrated a text editor and built flexible nesting logic to handle complex, stacked table layouts automatically.
    • Integrated a WYSIWYG editor and hierarchical "component-within-component" logic to handle complex nested table structures.
    • Engineered a "Download-to-Deploy" workflow where the app compiles modular JS components into a single ESP-ready HTML file.
  • Stakeholder Management: Collaborated with leadership to define the balance between user flexibility and brand governance, ensuring the tool enhanced the existing design workflow rather than replacing it.

The Result: Scalability and Risk Mitigation

  • Efficiency: Reduced production time by shifting the technical burden away from the Digital Operations team.
  • Quality Assurance: Achieved 100% template integrity; by locking the core HTML/CSS logic within JS modules, "broken" templates were eliminated.
  • Scale: Successfully onboarded dozens of clients and templates into the system, creating a centralized, maintainable library of "certified" code blocks that can be updated once and deployed everywhere.

Under the Hood: The Compiler Architecture

How it works

To guarantee perfect rendering across tricky email clients like Outlook without forcing marketing teams to touch code, the app acts as a custom compiler. It takes simple form inputs filled out by a user and instantly converts them into complex, bulletproof HTML layouts using modular JavaScript components.

Phase 1: The Modular Component Wrapper (EcButton.js)

To keep the application modular and easily maintainable, every layout element—from complex grids to simple buttons—is isolated into a pure functional component.

This component safely processes the text, links, and styling choices entered by the user, applies smart fallback styles if any fields are left blank, and outputs clean, formatted email HTML.

/**
 * Generates a bulletproof, responsive HTML button component.
 * Abstracted into a modular function to dynamically inject client data and global overrides.
 */
export function ecButton(data, formData, component, family = 'parent') {
  const htmlArr = [];

  // Map incoming user form inputs to component schema
  addFormVals(data, formData, component);

  // Generate an array of attributes and value pairs for the component instance
  const compArr = createCompAttArr(data, component);

  // Compile individual elements into HTML strings
  compArr.forEach((comp) => {
    createHtml(comp);
  });

  function createHtml(obj) {
    // Dynamic fallbacks mapping local values -> universal user values -> defaults
    const activeColor = obj.background_color || state.universalFields.buttonColor || def.background_color;
    const activeHref = obj.href || state.universalFields.srcCode;

    const html = `
      <!-- button begins -->
      <tr>
        <td class="${obj.class_list || def.class_list}" align="${obj.align || def.align}" style="padding:${obj.top || def.top}px ${obj.left || def.left}px ${obj.bottom || def.bottom}px;">
          <table role="button" border="0" cellspacing="0" cellpadding="0">
            <tr>
              <td align="center" bgcolor="${activeColor}" style="background-color:${activeColor}; margin:auto; width:${obj.width || def.width}px; height:${obj.height || def.height}px;">
                <a title="${obj.title || def.title}" href="${activeHref}" style="font-size:${obj.font_size || def.font_size}px; font-family:${obj.font_family || def.font_family}; color:${obj.color || def.color}; font-weight:${obj.font_weight || def.font_weight}; text-align:center; background-color:${activeColor}; text-decoration:none; border:none; display:inline-block; text-transform:uppercase; line-height:${obj.height || def.height}px; width:${obj.width || def.width}px;">
                  <span style="font-size:${obj.font_size || def.font_size}px; font-family:${obj.font_family || def.font_family}; color:${obj.color || def.color}; font-weight:${obj.font_weight || def.font_weight}; text-align:center; text-transform:uppercase; line-height:${obj.height || def.height}px;">${obj.text || def.text}</span>
                </a>
              </td>
            </tr>
          </table>
        </td>
      </tr>
      <!-- button ends -->
    `;

    htmlArr.push(html);
  }

  // Map compiled template strings into structural state tree
  const att = createCompObj(family, htmlArr, component);
  return att;
}

// Register component callback to global state coordinator
state.compFuncs.ecButton = ecButton;

Phase 2: The Core Compiler Engine (Compiler.js)

Instead of using fixed, rigid layouts, the compiler engine looks at the exact list of components a user has added to their email, grabs their matching JavaScript modules, and groups the final HTML into the correct sections (like the Body, Footer, or Liftnote) of the global template.

/**
 * Iterates through active document layers, identifying corresponding
 * component functions and invoking them to populate the compiler's rendering state.
 */
function getFuncNames(dataSet, formData) {
  dataSet.forEach(entry => {
    const entryLowerCase = entry.toLowerCase();

    // Isolate component name and trim prefixes
    const preCompName = entryLowerCase.slice(entryLowerCase.indexOf('_') + 1, entryLowerCase.length);
    const compName = preCompName.includes('_') ? getCompName(preCompName) : preCompName;

    // Dynamically resolve component module mapping from dynamic array elements
    const funcArr = entryLowerCase.split('_');
    const compFunc = getCompFuncName(funcArr);

    // Orchestrate component initialization
    invokeFunc(compFunc, compName, entry, formData);
  });
}

/**
 * Executes a dynamically mapped component compiler, registering the produced
 * HTML outputs into the correct layout region (Body, Footer, or Liftnote).
 */
function invokeFunc(compFunc, compName, elemName, formData) {
  compEntries.forEach(entry => {
    if (entry[0] !== compFunc) return;

    // Invoke matched component builder and pass execution context
    const newComp = state.compFuncs[entry[0]](state.data, formData, elemName, 'parent');

    // Route compiled elements to respective state nodes
    const isFooter = elemName.slice(-6) === 'FOOTER';
    const isLiftnote = elemName.slice(-8) === 'LIFTNOTE';

    if (!isFooter && !isLiftnote) {
      state.components[compName] = newComp;
    } else if (isFooter) {
      state.footer = newComp;
    } else if (isLiftnote) {
      state.liftnote = newComp;

      // Mutate and clean active dataset to prevent redundant processing
      state.data.forEach(d => {
        if (d.tagName === entry) {
          state.data.splice(state.data.indexOf(d), 1);
        }
      });
    }
  });
}

The Architectural Takeaway

  • Easy Scalability: BBecause the main compiler engine (Compiler.js) is completely independent of individual design modules (EcButton.js), adding a brand-new design pattern to the system requires zero core code changes. A developer just writes a new component file and plugs it into the registry list.
  • Reliability: Because nesting rules, attributes, and legacy fallback classes are entirely abstracted into JavaScript templates, there is zero risk of non-technical content editors breaking a design system or running into rendering bugs in production.