docs/writing-css.md
Do you contribute changes to the AMP Runtime or add/modify AMP extensions? If you answer yes, then you should definitely read this document.
Do you write solutions on top of the amp library (ampstart/ABE), this may be a good read for you.
Specificity is the means by which browsers decide which CSS property values are the most relevant to an element and, therefore, will be applied. Specificity is based on the matching rules which are composed of different sorts of CSS selectors.
A selector is something which can identify/select an element or a group of elements and apply a list of properties to all the selected elements.
*
.favorite
ul#summer-drinks li.favorite
html body div#pagewrap ul#summer-drinks li.favorite
html > body div#pagewrap ul#summer-drinks > li.favorite
.ampstart-dropcap:first-letter
#summer-drinks::before
#summer-drinks::after
A selector can contain a class, id, pseudo elements, pseudo classes, :not(), and a combination of any of these and many more. For a comprehensive list of all CSS selectors, see https://developer.mozilla.org/en-US/docs/Web/CSS/Reference#Selectors.
In general, the order of the CSS selectors does not affect which rules get applied to your CSS, unless the selectors have the same specificity and apply to at least one element in common. Well-written CSS will work perfectly fine even when the selectors are re-ordered (for example, when reordering imports in a css file). In the real world, it's hard to achieve this, especially when we import third-party CSS (which can change at any time).
Visit the CSS Tricks: Specifics on CSS Specificity which explains specificity in very simple terms and shows how specificity is computed (it's very short and understandable).
To compute specificity, use this online calculator: https://specificity.keegan.st/
This is a bit trickier than creating a new selector (with completely new properties).
AMP is a versioned library that a lot of websites use. You might ask yourself, "But I am only changing CSS, how can this break AMP?". Well, you can! All it takes to break backwards compatibility is replacing a selector with another selector that has higher specificity.
!important
!important because this is ALWAYS
going to BREAK backwards compatibility (but could fix issues).!important during the first pass, and plan for it during design or
early implementation phases.When building extensions, you should always be aware of things that could cause FOUC (Flash Of Unstyled Content). This happens when (if) the extension is built very late and the extension’s CSS is dependent on DOM Structure changes (addition of a class or a tag or may be restructuring the DOM itself) that happens as a result of building the extension.
While it is not completely possible to avoid this, ideally it would be better to write selectors that are independent of BUILD outcomes. This ensures that there is no repaints (at least in the initial state).
One way to avoid re-layouts after BUILD is to separate out all the CSS properties into in and
out properties. "In" styles are unlikely to cause a lot of FOUC or any at all. "Out" styles may cause FOUC and
we should probably phrase it as "not allowed". A good example is padding vs margin of the
element itself. It's totally fine to change padding during build, but a huge no-no for margin.
Here are some examples of what not to do, using real examples.
The following CSS is an example of a bad selector:
amp-selector:not([disabled]) [option][selected]:not([disabled]) {
outline: solid 1 px rgba( 0 , 0 , 0 , 0.7);
}
Why is it bad?
The only good part about this CSS is that it completely avoids FOUC by targeting tags and attributes, and not depending on the build outcome.
Improving the CSS
Here is how we changed this CSS to make it better:
amp-selector [option][selected] {
outline: solid 1 px rgba( 0 , 0 , 0 , 0.7);
}
amp-selector [selected][disabled],
amp-selector[disabled] [selected] {
outline: none;
}
Fixing PR: https://github.com/ampproject/amphtml/commit/e12deb125bc0bed16d33481e0c
- .i-amphtml-accordion-header {
- cursor: pointer;
- background-color: #efefef;
- padding-right: 20 px;
- border: solid 1 px #dfdfdf;
- }
+ amp-accordion > section > :first-child {
+ cursor: pointer;
+ background-color: #efefef;
+ padding-right: 20 px;
+ border: solid 1 px #dfdfdf;
+}
The lesson learnt here is that even though the breaking CSS was a good change (it fixed the FOUC due to the class introduced at BUILD), it moved properties from a selector with LOWER specificity to HIGHER specificity, which breaks backward compatibility.