')
- .attr('id', stickyId + '-sticky-wrapper')
- .addClass(o.wrapperClassName);
- stickyElement.wrapAll(wrapper);
-
- if (o.center) {
- stickyElement.parent().css({width:stickyElement.outerWidth(),marginLeft:"auto",marginRight:"auto"});
- }
-
- if (stickyElement.css("float") == "right") {
- stickyElement.css({"float":"none"}).parent().css({"float":"right"});
- }
-
- var stickyWrapper = stickyElement.parent();
- stickyWrapper.css('height', stickyElement.outerHeight());
- sticked.push({
- topSpacing: o.topSpacing,
- bottomSpacing: o.bottomSpacing,
- stickyElement: stickyElement,
- currentTop: null,
- stickyWrapper: stickyWrapper,
- className: o.className,
- getWidthFrom: o.getWidthFrom,
- responsiveWidth: o.responsiveWidth
- });
- });
- },
- update: scroller,
- unstick: function(options) {
- return this.each(function() {
- var unstickyElement = $(this);
-
- var removeIdx = -1;
- for (var i = 0; i < sticked.length; i++)
- {
- if (sticked[i].stickyElement.get(0) == unstickyElement.get(0))
- {
- removeIdx = i;
- }
- }
- if(removeIdx != -1)
- {
- sticked.splice(removeIdx,1);
- unstickyElement.unwrap();
- unstickyElement.removeAttr('style');
- }
- });
- }
- };
-
- // should be more efficient than using $window.scroll(scroller) and $window.resize(resizer):
- if (window.addEventListener) {
- window.addEventListener('scroll', scroller, false);
- window.addEventListener('resize', resizer, false);
- } else if (window.attachEvent) {
- window.attachEvent('onscroll', scroller);
- window.attachEvent('onresize', resizer);
- }
-
- $.fn.sticky = function(method) {
- if (methods[method]) {
- return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
- } else if (typeof method === 'object' || !method ) {
- return methods.init.apply( this, arguments );
- } else {
- $.error('Method ' + method + ' does not exist on jQuery.sticky');
- }
- };
-
- $.fn.unstick = function(method) {
- if (methods[method]) {
- return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
- } else if (typeof method === 'object' || !method ) {
- return methods.unstick.apply( this, arguments );
- } else {
- $.error('Method ' + method + ' does not exist on jQuery.sticky');
- }
-
- };
- $(function() {
- setTimeout(scroller, 0);
- });
-})(jQuery);
-$(document).ready(function(){
- $(".toc-padding").sticky({topSpacing:0});
- });
diff --git a/docs/assets/logo.png b/docs/assets/logo.png
deleted file mode 100644
index 4d1cc323..00000000
Binary files a/docs/assets/logo.png and /dev/null differ
diff --git a/docs/assets/phone-outline.png b/docs/assets/phone-outline.png
deleted file mode 100644
index 9cce1ad0..00000000
Binary files a/docs/assets/phone-outline.png and /dev/null differ
diff --git a/docs/assets/scrollIt.js b/docs/assets/scrollIt.js
deleted file mode 100644
index c38ec266..00000000
--- a/docs/assets/scrollIt.js
+++ /dev/null
@@ -1,152 +0,0 @@
-/**
- * ScrollIt
- * ScrollIt.js(scroll•it•dot•js) makes it easy to make long, vertically scrolling pages.
- *
- * Latest version: https://github.com/cmpolis/scrollIt.js
- *
- * License
- */
-(function($) {
- 'use strict';
-
- var pluginName = 'ScrollIt',
- pluginVersion = '1.0.3';
-
- /*
- * OPTIONS
- */
- var defaults = {
- upKey: 38,
- downKey: 40,
- easing: 'linear',
- scrollTime: 600,
- activeClass: 'active',
- onPageChange: null,
- topOffset : -50
- };
-
- $.scrollIt = function(options) {
-
- /*
- * DECLARATIONS
- */
- var settings = $.extend(defaults, options),
- active = 0,
- lastIndex = $('[data-scroll-index]:last').attr('data-scroll-index');
-
- /*
- * METHODS
- */
-
- /**
- * navigate
- *
- * sets up navigation animation
- */
- var navigate = function(ndx) {
- if(ndx < 0 || ndx > lastIndex) return;
-
- var targetTop = $('[data-scroll-index=' + ndx + ']').offset().top + settings.topOffset + 1;
- $('html,body').animate({
- scrollTop: targetTop,
- easing: settings.easing
- }, settings.scrollTime);
- };
-
- /**
- * doScroll
- *
- * runs navigation() when criteria are met
- */
- var doScroll = function (e) {
- var target = $(e.target).closest("[data-scroll-nav]").attr('data-scroll-nav') ||
- $(e.target).closest("[data-scroll-goto]").attr('data-scroll-goto');
- navigate(parseInt(target));
- };
-
- /**
- * keyNavigation
- *
- * sets up keyboard navigation behavior
- */
- var keyNavigation = function (e) {
- var key = e.which;
- if($('html,body').is(':animated') && (key == settings.upKey || key == settings.downKey)) {
- return false;
- }
- if(key == settings.upKey && active > 0) {
- navigate(parseInt(active) - 1);
- return false;
- } else if(key == settings.downKey && active < lastIndex) {
- navigate(parseInt(active) + 1);
- return false;
- }
- return true;
- };
-
- /**
- * updateActive
- *
- * sets the currently active item
- */
- var updateActive = function(ndx) {
- if(settings.onPageChange && ndx && (active != ndx)) settings.onPageChange(ndx);
-
- active = ndx;
- $('[data-scroll-nav]').removeClass(settings.activeClass);
- $('[data-scroll-nav=' + ndx + ']').addClass(settings.activeClass);
- };
-
- /**
- * watchActive
- *
- * watches currently active item and updates accordingly
- */
- var watchActive = function() {
- var winTop = $(window).scrollTop();
-
- var visible = $('[data-scroll-index]').filter(function(ndx, div) {
- return winTop >= $(div).offset().top + settings.topOffset &&
- winTop < $(div).offset().top + (settings.topOffset) + $(div).outerHeight()
- });
- var newActive = visible.first().attr('data-scroll-index');
- updateActive(newActive);
- };
-
- /*
- * runs methods
- */
- $(window).on('scroll',watchActive).scroll();
-
- $(window).on('keydown', keyNavigation);
-
- $('body').on('click','[data-scroll-nav], [data-scroll-goto]', function(e){
- e.preventDefault();
- doScroll(e);
- });
-
- };
-}(jQuery));
-
-$(function(){
- $.scrollIt();
-});
-
-// This is mainly for the two template links, since ScrollIt cannot cover them.
-// http://www.learningjquery.com/2007/10/improved-animated-scrolling-script-for-same-page-links
-$(document).ready(function(){
- $('.toc-a').click(function() {
- if (location.pathname.replace(/^\//,'') == this.pathname.replace(/^\//,'')
- && location.hostname == this.hostname) {
- var $target = $(this.hash);
- $target = $target.length && $target
- || $('[name=' + this.hash.slice(1) +']');
- if ($target.length) {
- var targetOffset = $target.offset().top;
- $('html,body')
- .animate({scrollTop: targetOffset}, 500);
- return false;
- }
- }
- });
-});
diff --git a/docs/assets/wireframe-default.png b/docs/assets/wireframe-default.png
deleted file mode 100644
index 7201bc39..00000000
Binary files a/docs/assets/wireframe-default.png and /dev/null differ
diff --git a/docs/assets/wireframe-fluid.png b/docs/assets/wireframe-fluid.png
deleted file mode 100644
index c1ec7af7..00000000
Binary files a/docs/assets/wireframe-fluid.png and /dev/null differ
diff --git a/docs/assets/wireframe-hybrid.png b/docs/assets/wireframe-hybrid.png
deleted file mode 100644
index e99b2851..00000000
Binary files a/docs/assets/wireframe-hybrid.png and /dev/null differ
diff --git a/docs/assets/wireframe-responsive.png b/docs/assets/wireframe-responsive.png
deleted file mode 100644
index 1f8af0c2..00000000
Binary files a/docs/assets/wireframe-responsive.png and /dev/null differ
diff --git a/docs/assets/wireframe-with-media-queries.png b/docs/assets/wireframe-with-media-queries.png
deleted file mode 100644
index 2be36d19..00000000
Binary files a/docs/assets/wireframe-with-media-queries.png and /dev/null differ
diff --git a/docs/assets/wireframe-without-media-queries.png b/docs/assets/wireframe-without-media-queries.png
deleted file mode 100644
index a16e93ad..00000000
Binary files a/docs/assets/wireframe-without-media-queries.png and /dev/null differ
diff --git a/docs/config.toml b/docs/config.toml
new file mode 100644
index 00000000..da30898a
--- /dev/null
+++ b/docs/config.toml
@@ -0,0 +1,11 @@
+# The URL the site will be built for
+base_url = "https://example.com"
+
+# Whether to automatically compile all Sass files in the sass directory
+compile_sass = true
+
+# Highlight all code blocks found
+highlight_code = true
+
+# Which theme to use for the code highlighting. (https://www.getgutenberg.io/documentation/getting-started/configuration/)
+highlight_theme = "cheerfully-light"
diff --git a/docs/content/best-practices.md b/docs/content/best-practices.md
new file mode 100644
index 00000000..f99b9341
--- /dev/null
+++ b/docs/content/best-practices.md
@@ -0,0 +1,57 @@
++++
+title = "Best Practices"
+description = "Emails don’t need to look the same in every email client, but there are some guidelines to make sure they render properly in email clients and are as accessible as possible."
+template = "default-template.html"
+[extra]
+page_id = "best-practices"
++++
+
+# Best Practices
+
+[Emails don’t need to look the same in every email client](http://doemailshavetolookthesameineveryclient.com/), Emails don’t need to look the same in every email client, but there are some guidelines to make sure they render properly in email clients and are as accessible as possible.
+
+## General rules and principles
+
+Cerberus strives to support email clients with low levels of HTML & CSS support (namely Microsoft Outlook, a [few versions of Gmail](https://emails.hteumeuleu.com/trying-to-make-sense-of-gmail-css-support-after-the-2016-update-53c15151063a), and a handful of [mostly non-US clients](https://emails.hteumeuleu.com/should-we-stop-inlining-styles-in-emails-8c3b64f0d407)). It’s safest to code emails like it’s 1999 (s)till).
+
+That means:
+
+- CSS2 instead of CSS3
+- `
`s instead of `
`s
+- Raster images (like PNGs) instead of vector (like SVGs)
+- Inline CSS instead of embedded styles or external stylesheets
+
+[Can I Email?](https://www.caniemail.com/) and [Campaign Monitor’s guide to CSS](https://www.campaignmonitor.com/css/) are good references for HTML & CSS support in email.
+
+## HTML and CSS
+
+1. **Use `
` when creating new tables.** This negates any unwanted spacing and borders and tells screen readers to skip over the table’s tags and move straight into the content.
+2. **When in doubt, nest another table.** For finer control of your HTML, nest tables when building emails.
+3. **Use padding for spacing in table cells.** Margins aren’t fully supported on tables and container elements.
+4. **Use margin for typography.** Margins *are* fully supported for headlines, paragraphs, and lists.
+5. **Use `align` for layout instead of `float`, `grid`, or `flexbox`.** Floats aren’t supported in Outlook and email clients don’t have good support for modern CSS layout properties in general.
+6. **HTML attributes are still relevant.** Most styling is done using CSS. But because some email clients use antiquated rendering engines, they tend to better understand HTML attributes like `align`, `valign`, `height`, and `width`.
+7. **Define color as `#ffffff` instead of `#fff` or `rgb(1,2,3)`.** Six-digit hex is supported in inline CSS as well as HTML attributes like `bgcolor` that are still supported in email.
+8. **Don’t forget about [preview text](https://stackoverflow.design/email/guidelines/faq#what-is-preview-text?).** We can specify the text that appears beneath subject lines in many email clients. If preview text is not included, this space will be populated by the email’s content.
+
+## Images
+
+1. **Save images as PNG, GIF, or JPG instead of SVG.** SVG has almost no support in email, no matter how it’s referenced (inline, Base64, `.svg`).
+2. Save images as @2x and scale them down using HTML attributes. Since SVG isn’t supported, a 20x20 raster image coded like `` displays crisply on high-definition screens.
+
+## Accessibility
+
+1. Include `role="presentation"` on all tables used for layout. This prevents screen readers from reading aloud the structure of each table cell.
+2. **Include `aria-hidden="true"` on presentational elements.** This prevents screen readers from reading aloud something that's not content.
+3. **Use HTML1 semantic tags whenever possible.** Tags like `
` and `` allow screen readers to quickly jump from section to section. Tags like `` and `` give text more importance.
+4. **Include an `alt` attribute on every image.** Be descriptive and use `alt` to help readers “see” the email if images aren’t displayed. Use an empty `alt=""` for images a screen reader should skip (eg. decorative images). Screenreaders will dictate the filename of images without an `alt` attribute (eg. "icon dash checkmark dot png"). [More on alt text](https://stackoverflow.design/content/examples/alt-text/).
+5. **Avoid “Click Here” or “Learn More” link copy.** It helps an email avoid spam filters and gives context about the link to folks using screen readers or dictation software.
+6. **Create a plain text version of every email.** It helps us avoid spam filters, some email clients don’t support HTML, and some people just prefer plain text. They also respond better to things like changing font size, family, and color, and work well with screen magnifiers.
+
+## Testing
+
+Tools to ensure emails look as they should when they’re sent.
+
+- [Litmus](https://www.litmus.com/) and [Email on Acid](https://www.emailonacid.com/) allow us to preview screenshots of our emails across 90+ email clients on multiple devices. Both have code editors built in (Eg. [Litmus Builder](https://litmus.com/email-builder)), which helps troubleshoot and fix bugs in actual email clients.
+- [Parcel](https://useparcel.com/) - A web-based code editor built specifically for email development.
+- [Putsmail](https://putsmail.com/) - Send yourself test emails.
diff --git a/docs/content/components.md b/docs/content/components.md
new file mode 100644
index 00000000..b9b6a7bc
--- /dev/null
+++ b/docs/content/components.md
@@ -0,0 +1,359 @@
++++
+title = "Components"
+description = "About John Doe, a good human"
+template = "default-template.html"
+[extra]
+page_id = "components"
++++
+
+# Components
+
+## Tables
+
+`
`s are still the most dependable way to create layouts for HTML emails.
+
+
Some email clients will collapse the spacer’s height if there’s no content.
+
style="font-size: 0px;line-height: 0px;"
Some clients will add additional space inherited from the ’s font-size and line-height.
+
+
+## Typography
+
+It’s safe and accessible to use semantic HTML tags like ``, `
`, and `
` for text in email just as we do for the web. The main difference in email is that [CSS should be written inline](https://stackoverflow.design/email/guidelines/faq#why-do-we-have-to-write-css-inline?) to specify intended styles (like the color of an anchor tag) and zero out unintended defaults (like the default margin around a `
Cerberus -
- Between mobile, Gmail, and Outlook, HTML email is a three-headed dog from hell.
-
-
-
-
Introduction
-
Coding regular emails is hard enough by itself. Making them responsive shouldn’t add to the headache. A few simple, but solid patterns are all that’s needed to optimize emails for small screens.
-
That’s what Cerberus is.
-
It’s just a few responsive email patterns that go a long way. The code blocks are compartmentalized so they may be used, removed, combined, and nested to build an email.
-
Each template contains code comments and has good support among popular email clients.
Cerberus is a small collection layout patterns for HTML email. The code is meant to be edited, adapted, and built upon.
-
The code is compartmentalized and annotated in an effort to explain what’s happening so you can add, edit, and remove code with some confidence.
-
This has been tested in all “popular” email clients, but not every email client out there. More on that here.
-
-
-
-
The Templates
-
-
-
Fluid Template
-
Good for simple layouts such as transactional and single column emails.
-
This template focuses on a fluid layout that sizes itself using percentage-based widths to shrink horizontally on narrow screens. This email layout does not reconfigure at different screen sizes.
-
If you want a basic template to handle rich text and images, this is a good baseline.
Good for more complicated, shape-shifting email layouts that work on some mobile clients.
-
This template uses media queries to reconfigure the layout for different screen sizes for email clients that support media queries. However, mobile clients that don’t support media queries or the <style> tag will display a shrunk version of the desktop layout instead. This applies to some versions of Gmail (still) and Yahoo, as well as a number of international email clients (more info on that here).
-
If you’re already comfortable with media queries, the learning curve is relatively low. If total device coverage isn’t required, you can create a responsive email the same way you create a responsive website.
This template uses a hybrid approach to reconfigure the layout for different screen sizes for email clients regardless of media query support. At its core, it uses max-width and min-width to impose rigid baselines (allowing some movement) and imposes a fixed, wide width for Outlook who is shackled to desktop anyway. Once a mobile-friendly baseline is set, media queries progressively enhance the email further in clients that support it.
-
If you have some email design experience, this template optimizes every popular email client. All the extra Outlook code can make these templates quite large and your maths have to be spot on for multi-column layouts.
Templates in the archived-versions folder are not currently being maintained and should be considered unsupported and deprecated. They are kept around for historical purposes. 💫
-
-
-
-
Client Support
-
Cerberus is tested in the most popular email clients as reported by Litmus and my own email campaigns. I’ve focused on the following clients:
-
-
-
Desktop
-
Outlook 2000/02/03/07/10/13/16 (Windows)
-
Windows 10 Mail
-
Outlook 2011/16 (Mac)
-
Apple Mail 9/10
-
Thunderbird
-
-
-
Web (Chrome, FF, IE)
-
Gmail (IMAP, Business
-
Outlook.com
-
Yahoo
-
Aol
-
Office 365 Web
-
Comcast
-
Web.de
-
GMX.de
-
freenet.de
-
T-Online.de
-
Mail.ru
-
Terra Mail
-
-
-
Mobile Apps
-
iOS Mail (iPhone 5 and up, iPad, iPad Mini)
-
Gmail (iOS+Android, all account types)
-
Google Inbox (iOS)
-
Mail (Android 6.0)
-
Yahoo (iOS)
-
Outlook (iOS)
-
Alto Mail (iOS)
-
-
-
When I say “tested”, I mean “email doesn’t fall apart”. I don’t mean "Everything is pixel perfect in Outlook" or “I found a way to make media queries work everywhere.” They don’t.
-
There are multiple versions of Gmail. Cerberus works pretty well in all of them. Rémi Parmentier created an excellent graphic explaining Gmail’s capabilities in this article.
-
Any client not listed above should be considered untested. If you feel I’ve left out a popular email client or can suggest a non-destabilizing fix for one, please submit an issue!
-
For what it’s worth, Litmus (who does test every email client) includes Cerberus in its Email Builder, so there’s that.
-
-
-
Known Issues
-
Not necessarily a bug with the code in this repo, but a few things that could trip you up.
-
-
Some ESPs don’t like URLs in HTML comments (example issue). In this case, you can remove the URLs or even the entire HTML comment.
-
-
-
-
A Word on CSS Inliners
-
I recommend against using a CSS inliners with Cerberus. Here’s why:
-
-
Cerberus is supposed to be simple and should not require a dependency like a CSS inliner.
-
The placement of Cerberus’s CSS is already optimized. The CSS in the <head> is meant only for email clients that parse CSS in this location. It doesn’t need to be inlined.
-
There are some CSS selectors like :hover that don’t inline so well and throw errors in some CSS inliners.
-
Inlining leads to code bloat. Not only does this impact download speeds, but some email clients like Gmail and iOS Outlook truncate messages after they exceed a certain file size.
-
-
I’m a fan of using snippets, available most code editors. I understand CSS inliners help many folks, but if you use one with Cerberus, please do so at your own risk. I recommend Lee Munroe’s CSS inliner and hear good things about Roadie.
-
-
-
Contributing
-
If you would like to help, please @reply me on Twitter or open an issue to discuss your idea. I’m forever interested in reducing the entropy of this code. I’m most interested in:
-
-
Fixing existing code that’s broken.
-
Reducing the amount of code in existing patterns.
-
Improving the documentation.
-
-
Please be mindful there are three templates that share a lot of code. Many changes apply to all three.
-
Please branch off the main branch instead of master.
-
-
-
Resources
-
Things that directly helped (and continue to help) build Cerberus.
Hello! I’m Ted Goas, the core author and maintainer. I’m a designer & front-end developer working on websites, web apps, and HTML emails. And I enjoy talking shop on Twitter.