Core philosophy: Maximum user simplicity with zero catastrophic failures. The formula stays clean with a single required parameter. Heavy lifting happens behind the scenes. Graceful degradation is non-negotiable because emails are permanent.
Writing HTML email code is a nightmare. There are dozens of email clients and all of them have created their own rules and chosen different aspects of CSS and HTML to support. When you also consider the sending constraints, it’s literally a no-win situation. But that’s also what makes this pack a fun project. There were many product decisions to be made and difficult criteria to consider. Below is a little bit about the reasoning I used for various decisions.
Here you can find a complete listing of .
Coda exports limited features and attributes as HTML
Some design and layout aspects, like callouts for example, do not carry the callout styling over in the HTML export. There are no hints whatsoever that content might be in a callout, let alone what color or icon goes with it. Another feature that does not come over at all is buttons. A call to action can be a very important aspect of an email though, so I got creative to solve for this one. Coda does send over linked text. Buttons in email require their own nested table HTML, meaning it’s best to show a button on a line by itself. This meant I could make a rule that looks for any linked text on a line by itself in Coda content and render it as a button in the email. What’s even more fun is that Coda also sends over any text color that may have been assigned. So I can pull that text color and set it as the button color.
Microsoft processes email HTML as a Word doc instead of as standard HTML Microsoft Outlook uses its own Microsoft Word processing engine to read emails, which means it doesn’t match anything else very well at all. To make up for this “completely-different-than-everyone-else” scenario, they offer a way to comment out code for others, yet still read it and use it themselves.
<!--[if mso]> CUSTOM MICROSOFT OUTLOOK CODE HERE <![endif]-->
All non-Outlook email clients see <!--, which starts a commented out portion of HTML, then skips everything until --> which signals the end of the commented out portion. Outlook sees the start of the comment and keeps checking for [if mso], which lets it know to keep processing. Then it sees ![endif] and know it’s at the end of the custom code.
Gmail strips MSO conditionals before sending
The MSO conditional solution is an alright hack, but when sending an email through Gmail, Google strips this code out. This nullifies the only real workaround for Microsoft treating email like a Word document. Because of this constraint, I built the email code as universally compliant as possible for all default sending. But I also wanted to allow for fully powered Enterprise level HTML code, so I included a sendingClient formula parameter that will swap in more bulletproof email code when sending from clients other than Gmail.
Formula philosophy - The pack does all the heavy lifting and the formula will be kept at a single required parameter no matter what. User simplicity is the top priority. Optional parameters should be direct and simple to understand with anything complex moved to ProMode or Color Map parameters. This will keep even the optional parameter list relatively short. Buttons - Make buttons in email possible and keep it simple for the user to accomplish. A text link on a line by itself. There should be no pre or post code tag requirement, just standard Coda text entry functions. Bulleted Lists - Standard bulleted lists are rendered differently by various email clients leaving them fairly unpredictable. Two aspects of bulleted lists were forced to ensure consistent spacing and display as well as full functionality like checked items displaying as checked. Layout - Nested tables are used to ensure consistent spacing and styling across all email clients. This allows for mixed list types to work, custom bullet markers through ProMode, and proper text wrapping. The issue is that copying a bulleted list from an email and pasting it somewhere else does not work. This was a trade off with no clear winning solution and I opted for consistent display and the ability to represent full functionality over retaining copy ability. Ordering - Numbered ordered lists start new each time there is a parent list type change in the pack. Standard mixed list types base the numbering used by off of the nested level. The pack reconfigures this and starts the numbering based on it being the a new list type regardless of where it’s nested. This means a checkbox item with numbered sub items will show as 1, 2, … instead of a, b, … This is different than Coda’s treatment of mixed list types, but opens up possibilities for email customization that wouldn’t be possible otherwise. Override philosophy - Overrides should be intuitive and control should be focused intent. A ProMode override is focused on creating a specific style and should be adhered to accordingly. But a general preset style might require some tweaks. So the following hierarchy is used: Default configuration values are the base Style presets override default values (gallery, darkmode, etc.) Parameter values override style presets and defaults (highlightColor, width) ProMode and Color Map override everything Overrides should also be structured to only required the attributes to change. This keeps a user from having to create an entry for all possible attributes and instead only include the intended attributes to change.
Error handling - Clicking send on an email is action that can’t be taken back. Once that email goes to inboxes, it’s out there permanently. For this reason, all errors are handled gracefully. In the event an override value has an error, the attribute falls back through the hierarchy in reverse order to the first qualifying value it finds, landing at the default values as an endpoint. This will help to ensure that an email will always show up in an inbox in presentable form and that it won’t fail catastrophically. Color map versus calculated values - Color calculations are a real and useful thing, but they also come with a lot of exceptions to rules once a solution gets to a certain complexity. There are enough colors and combinations of uses that calculating colors becomes a large and difficult to maintain algorithm instead of a helpful calculation. The shorter and more direct route was to map colors coming in to intended colors used in the email. A secondary benefit to this approach is the ability to surface the color map to the end user to be edited to their liking. This provides absolute color control that is editable and predictable.
In the event a color changes on Coda’s side, the color map can be edited by the end user for coda’s value before a pack fix is even entered. This isn’t something to rely on, but in the event a color breaks the map, the end user has control to fix the issue and isn’t left at the mercy of pack update timing. If a color does get changed on Coda’s side, the pack will simply fall back to the color Coda sends if it doesn’t find a match in the color map. This is another graceful failure point that allows the email to still be received in good condition.
These are current best-case states due to the constraints listed above with no reliable workarounds.
Page icons are difficult to detect as a page icon and not a general image. They are also very difficult to display as a custom image icon next to a title in HTML email code. They come through as an image at the top left of the email, then the page title on the next line. Currently, the most predictable approach to take is hide the page title in Coda and only use page content. Buttons aren’t clickable over the full button area, only the text. This is due to differences and limitations in Outlook rendering and due to sending from Gmail. Google strips out MSO conditionals which cancels out typical button hacks used in marketing emails. Bottom body spacer shows up in all clients except Outlook Desktop. The current fix is to add more nested tables which adds code bulk, so I’m holding off at the moment to see if this is ever actually noticed during regular use. Multi image rows may render only the first image on the Outlook app on iPhone (observed on iOS 15). Diagnostic via Mailgun screenshot tool and not confirmed on a real device or on newer iPhone and iOS versions.
Italics for table conditional formatting is not sent in the HTML from Coda. All other text styles go through just fine and can be picked up, but italics doesn’t show up at all. When this is fixed on Coda’s side, italics should automatically pick up. Table Medium and Table Dark backgrounds have a light font color that gets applied to code blocks causing them to wash out and not be readable. The pack keeps code block background and font color standard and only changes the border color to avoid this issue and to avoid adding complexity of more color calculations and handling. Font highlights can wash out with font colors on some table conditional formatting with darker backgrounds. This is similar to the codeblock issue. This pack corrects these colors to allow for proper contrast. Blockquote styles have their font color change with table conditional formatting, but the left border stays as it was causing it to look a bit off. The pack changes the color on both consistently for a unified look. Horizontal rules keep their same color when table conditional formatting is applied causing them to look washed out a good bit of the time. The pack has the horizontal rule adopt the font color which provides for better contrast and a visible horizontal rule. A blank line sent in an HTML email causes the email client to fail and assume the content is plain text. This shows the code for the email as the content. The email pro pack clears all empty lines as a last step before returning the code to avoid this failure point. Coda does however like to introduce empty lines, I think due to the regular and soft linebreak change from a while ago. Adding a helper in the Gmail pack to clear empty lines might be a helpful safeguard. In the product .ToText() fixes this issue when it pops up, which usually happens when returning the code to Coda by writing it to a column with a button, then reading from that column. Images using the image formula inside a hyperlink formula come through with HTML entities instead of the actual code. Hyperlink(Image()) comes through as <img ...>. The pack has a utility function that corrects for this.
Email HTML will never be perfect, but this pack intends to get as close as possible while staying true to Coda’s native experience.