AMP in WordPress: CSS Limits

This post is the third in a series capturing knowledge gems gathered from the Official AMP plugin Support forums. The community asks interesting and important questions in the forum, and the engineering team at Google driving the development of the plugin, led by Weston Ruter, has provided equally cool answers worth spreading.

Support Question

I want to adopt an AMP-first content strategy for my site but my site has too much CSS!

Lots of people

WordPress AMP Plugin support forum

Background

Since AMP pages are just web pages, styling AMP pages is very similar to normal HTML pages: we need to use CSS! However, AMP imposes certain restrictions on CSS usage. Among them:

  • Only inline styles can be used on AMP pages (i.e. no external stylesheets): generally speaking, custom styles may be added to a document using a single <style amp-custom> tag in the head of the document.
  • The only exception to inline-only styles is custom fonts
  • The amount of custom CSS allowed is up to a maximum of 50KB of CSS per page.
  • AMP components come with default styles to make authoring responsive pages reasonably easy. These styles are defined in the amp.css.
  • AMP only allows transitions and animations of properties that can be GPU accelerated in common browsers. Currently these include: opacity, transform and -vendorPrefix-transfor
  • The !important qualifier is not allowed because it interferes with AMP static layout system

These restrictions exist for performance and usability:

  • Way too often web pages include one or more external stylesheets, which means that additional HTTP requests are needed to load them with a consequent impact on performance, especially in high-latency scenarios. With AMP, only one request (plus fonts) is needed to load the whole page
  • Way too often sites have huge and complicated CSS stylesheets, causing performance degradations due to CSS processing and execution times

AMP also provides enhanced responsive design capabilities by introducing features such as placeholders & fallbacks, advanced art direction via srcset and the layout attribute for better control over how your elements display. These capabilities make it very easy to make elements responsive—you just need to add a layout="responsive" attribute.

CSS Animations

In addition to the CSS that can be included in <style amp-custom>, we can also add the <style amp-keyframes> tag, which is allowed specifically for keyframes animations. The CSS in  amp-keyframes counts separately from the 50KB limit for amp-custom CSS, and it has a limit of upt to Keyframes allows 500KB.

This is great because keyframes rules are often bulky even for moderately complicated animation, and are often the reason why the size limit imposed on <style amp-custom> is exceeded. Putting the keyframes declarations at the bottom of the document in the <style amp-keyframes> makes it possible to exceed size limitations,  and since keyframes are not render-blocking, it also avoids blocking First Contentful Paint to parse them.
For more details on CSS animations in AMP check Triggering CSS animations & transitions.

The Right Way

Although AMP restrictions on CSS appear at first as very limiting and difficult to deal with, following good CSS hygiene and best practices gives us enough space to have all CSS we need on a per-page basis.

One of the main challenges we have observed in efforts transitioning sites to full AMP compatibility is dealing with a very large, legacy-ridden, scary CSS burden. Part of the reason for this is that CSS programming often is more an art than programming. Styling is often done via experimentation, as described by Nick Mertens:  “Try a few things to see the interactions between surrounding elements: change the display type, add/remove some margin, play around with flex, etc. Then you can start honing in on the changes required to get to the end result. Add a class here, create a modifier there, clean up, check again and done.”  This makes it easier to add new rules, or change existing ones, that deleting them. With time, CSS bloating proliferates.  Also, as sites change, and re-styling efforts happen under pressure, again it is easier to expand the CSS codebase, than maintaining only the CSS that is needed.

Independently of using AMP or not, we need to get our CSS house in order. If we use AMP, we are forced to maintain CSS tidiness, and that is a good thing. If we don’t use AMP, we need to be self disciplined (i.e. use performance budgets), and can take a look at AMP CSS practices and restrictions to shed light onto areas we need to pay attention to to make CSS more efficient in our sites.

AMP Plugin: CSS Handling

As discussed in one of our prior posts, The official AMP plugin for WordPress provides a great deal of support to enable AMP content creation. One of such areas of support is CSS processing: Styles Processing, CSS Tree Shaking, and Styles Concatenation.

Styles Processing

The AMP plugin integrates smoothly with the WordPress way of handling CSS via the enqueuing of stylesheets (wp_enqueue_style), and:

  • Gathering all stylesheets from <link>, <style>, and [style] elements
  • Parsing and processing stylesheets:
    • Update CSS selectors pointing to converted elements: e.g. img to amp-img
    • Transform !important qualifiers into rules with highly-specificity selectors
    • Remove disallowed @-rules and CSS properties
      (e.g. -moz-binding)
    • Gather CSS selectors from rules to pass them to tree shaking functionality
  • Tree shaking to remove the majority of style rules not relevant to a given AMP page
  • Serializing the parsed CSS into minified form
  • Caching processed CSS to amortize processing costs

All this functionality allows WordPress developers to use CSS in the standard way they are used to, get a little bit of a break while they get their CSS house in order, and stay on the CSS fast lane once they get there.

CSS Tree Shaking

One of the challenges developers face when trying to put their CSS house in order is finding out which CSS rules are actually used on any given page. Recent versions of Chrome’s DevTools provide support for assessing CSS code coverage, which is very useful for getting read of dead CSS rules. The AMP plugin provides support in this area via Tree Shaking: a set of checks and heuristics to eliminate as many CSS rules not used in a given generated AMP page as possible:

  • Remove selectors that mention non-existent class names, IDs, or elements
  • Account for class names from amp-bind expressions in [class] attributes.
  • Account for class names dynamically added by AMP (e.g. amp-viewer, amp-hidden, etc.).
  • Account for selectors for component-specific class names if the corresponding component is present on the document (e.g. amp-carousel-slide).
  • Limit evaluation of selectors to keep runtime performance in check
  • Remove all declaration blocks for which all associated selectors have been removed
  • Remove all @-rule elements for which all of its declaration blocks have been removed.

Styles Concatenation

After all styles processing has been completed, the plugin concatenates all the style rules that “survived” the process. This is done until the 50KB CSS size limit is reached. After reaching the limit, an excessive CSS validation error is raised.

To help developers determine how much CSS was included in the AMP page and from where, the plugin adds a comment to the page source before the style[custom] element specifying which stylesheets where included and which were excluded.

Upcoming CSS Capabilities

Upcoming updates to the plugin are bringing more advances and improvements to the CSS processing capabilities, including:

Prioritizing the processing stylesheets (PR: #2346): to increase the likelihood of including critical styles before the 50KB is reached. For example, the stylesheets of a theme should never be excluded to make space for the CSS of the admin bar. Similarly, print stylesheets should never be included at the expense of plugin stylesheets.

The general prioritization for stylesheet processing is as follows:

  1. Parent theme (non-print) stylesheets
  2. Child theme (non-print) stylesheets
  3. Core stylesheets used by themes (for blocks)
  4. Plugin (non-print) stylesheets
  5. Stylesheets from wp-includes
  6. Additional CSS from Customizer
  7. Styles added by blocks and widgets
  8. Dashicons
  9. Print stylesheets
  10. Admin bar CSS

Discontinue excluding excessive CSS by default in Native mode (Issue #2326): Currently when there is too much CSS, the first stylesheet that exceeds the 50KB limit is excluded from being included on the page. This leads to pages that appear broken. The work on  prioritizing the processing of stylesheets helps a lot (#2322), we should also consider whether we should exclude stylesheets by default. This is particularly important when activating AMP on a theme that has more than 50KB of CSS even without the admin bar, or when a certain combination of blocks causes more than 50KB of CSS to be added to the page after tree shaking.

Ignore admin bar for AMP validation purposes (Issue: #1921): The inclusion of the admin bar was included in AMP via #1205. However, the amount of admin bar CSS is so large (20KB) that it takes up about 40% of the total allowed CSS in AMP (50KB). For this reason it very frequently causes excessive_css validation errors when an authenticated user is browsing the frontend. As opposed to providing this option or trying to automatically disable the admin bar when there is too much CSS (#1800), we could instead just exclude admin bar CSS (including any styles that depend on admin-bar) from being considered in the 50KB budget.

Show notice on frontend when there is excessive CSS (Issue: #1801): In addition to automatically disabling the admin bar when there is excessive CSS (#1800), there should be an additional message displayed to the logged-in site administrator on the frontend when there is still excessive CSS.

Leave a Reply

Your email address will not be published. Required fields are marked *