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-transform
- 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
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, than 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.
The official AMP plugin for WordPress provides a great deal of support for AMP content publishing, and of the main such areas is CSS processing.
Styles Processing #
The AMP plugin functionality related to CSS is integrated into the standard WordPress CSS handling workflows (e.g. enqueuing stylesheets via wp_enqueue_style), and makes it easy to coalesce all the CSS used on a page in the way that is expected by the AMP runtime. In general terms, the AMP plugin does the following tasks:
|Gather||Collect all stylesheets from <link> elements, <style> elements, and [style] attributes|
|Parse||– Update CSS selectors pointing to converted elements: e.g. img to amp-img|
– Transform !important qualifiers into rules with high-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-shake||If the resulting CSS is larger than AMP’s 75KB limit, as CSS tree-shaking algorithm is applied|
|Truncate||If after tree-shaking there is still more than 75KB, then any stylesheet that takes the total over 75KB will be omitted.|
|Prioritize||To mitigate the possible consequences of leaving critical CSS out, the plugin prioritizes the order of styles eligible for removal:|
– Parent theme (non-print) stylesheets
– Child theme (non-print) stylesheets
– Core stylesheets used by themes (for blocks)
– Plugin (non-print) stylesheets
– Stylesheets from wp-includes
– Additional CSS from Customizer
– Styles added by blocks and widgets
– Print stylesheets
– Admin bar CSS
|Transient storing||Parsed styles are minified, serialized, and stored in a transient, which are a simple and standardized way in WordPress for storing cached data in the database, temporarily by giving it a custom name and a timeframe after which it will expire and be deleted|
|AMP CSS||Construct the style[custom] element to be added to the |
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 are via Tree Shaking functionality: a set of checks and heuristics to eliminate as many CSS rules not used in a given generated AMP page as possible. Some of these checks and heuristics are:
The AMP plugin provides functionality to make it easier to render AMP pages without violating AMP CSS budget of max 75KB per page.
- Remove selectors that mention non-existent class names, IDs, elements, or attributes
- 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
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.
AMP 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 page elements display. These capabilities make it very easy to make elements responsive—you just need to add a layout=”responsive” attribute.
As part of the developer tools provided by the plugin (e.g. Validated URL dashboard), you can also get detailed information regarding the CSS state of any AMP URL.
First, the overall summary of the CSS processing is presented, including:
- Total CSS before tree-shaking and minification
- Total CSS after tree-shaking and minification
- Percentage of the CSS budget consumed
- Amount of excluded CSS
And then a detailed dissection of the CSS included in the page and the corresponding attribution to the components responsible for each part of the CSS.