The Art of CSS
# Simple, not easy
- CSS syntax is relatively simple to learn, or at least lookup
- but its semantics is not easy
- let's review the basics: cascade, inheritance, and custom properties
# Glossary Reminder
selector { /* declarations block */
property : value; /* declaration */
another : value; /* declaration */
} # The cascade
- property values of an element can be defined by multiple declaration blocks
- declaration blocks can have different specificity
- more specific declarations have precedence
- if same specificity, use source order: last one have precedence
# CSS Cascade Level 0001
- HTML is annotated with elements and pseudo elements
- carry semantic value
- style on these
- gives a low fidelity look-and-feel
# CSS Cascade Level 0010
- annotate further with classes, pseudo-classes, and attributes
ARIA attributes especially, increases semantic richness- style on these
- gives a medium fidelity look-and-feel
# CSS Cascade Level 0100
- annotate further with IDs
- no semantic value outside of application
- style on these
- gives a high fidelity look-and-feel
# CSS Cascade Level 1000
- annotate with inline styles
- no semantic value
- completes the look-and-feel
# Example: constructive interference
- properties cascade constructively from multiple selector blocks, to build the final style
p { font-style : italic; }
.foo { font-family : serif; }
#bar { background : blue; } <p class="foo" id="bar" style="color:tomato;">
italic, serif, blue background, tomato text
</p> # Example: destructive interference
- or destructively, cancelling each other out
p { color : red; }
.foo { color : green; }
#bar { color : blue; } <p class="foo" id="bar" style="color:tomato;">
tomato text
</p> # Example: finding a balance
- or even both constructive and destructive
/* level 0001 */
p { color: red; }
/* level 0002 */
p + p { background: green; color: blue; } <p>red text</p>
<p>
green background (constructive),
blue text (destructive)
</p> # Stylesheet Origin
- origins of stylesheets, with increasing precedence
- User Agent: reasonable default look and feel
- User: custom settings (eg.
stylish ) - Author: from website designers
- Author
!important - User
!important - User Agent
!important(rare) - users have ultimate control over how they want to view sites
- eg: adblocking, or removing annoying headers/footers
# Inheritance
- DOM has hierarchical structure (thus inheritance)
- elements can inherit undefined styles from ancestors
- some like
colorinherit by default, others likedisplaydo not - force/change inheritance with:
- a new value (what we normally do)
inherit: to use DOM parent's valueinitial: according to CSS specificationsunset: equivalent toinheritif normally inherited,initialif normally not
# Example: Single Responsibility
- single responsibility principle applied to selectors
- declaration blocks should be readable on its own
- easy to reason without looking at output
- use composition to build an element's style
- responsibility can be based on hierarchical roles
.as-child { /* eg: position: absolute; */ }
.as-self { /* eg: color: tomato; */ }
.as-parent { /* eg: display: flex; */ }
.as-peer { /* eg: margin: 1em; */ } <div class="as-child as-self as-parent as-peer">
content
</div> # Example: CSS Hierarchy
- describe hierarchy in CSS to reduce HTML verbiage
- sibling relationships too
.foo { /* eg: display: flex; */ }
.foo > * { /* eg: flex: 1 0 50%; */ } <div class="foo">
<div>content</div>
<div>content</div>
<div>content</div>
</div> # CSS and HTML are co-dependent
- interchangeably shift responsibility between CSS and HTML
- cannot write one without thinking about other
# Example: complexity in CSS
- CSS selectors can be complex and contain many declarations per block
- while HTML has barely any annotations
div > p {
font-style : italic;
font-family : serif;
background : blue;
color : tomato
} <div>
<p>
italic, serif, blue background, tomato text
</p>
</div> # Example: complexity in HTML
- CSS can be really simple (from
Atomic CSS , or see alsoTailwind CSS ) - while HTML is heavily annotated with CSS classes
.Fs(i) { font-style : italic; }
.Ff(serif) { font-family : serif; }
.Bg(blue) { background : blue; }
.C(tomato) { color : tomato } <div>
<p class="Fs(i) Ff(serif) Bg(blue) C(tomato)">
italic, serif, blue background, tomato text
</p>
</div> - find balance such that both documents are semantic and DRY
# Custom properties
- defined just like any other property
- obeys same cascade rules
- inherited by default
- can provide an optional fallback
# Example: dynamic styling
- data-driven styling from HTML
- last example looks like inline-styles, but …
.foo {
color: var(--foo-color, tomato);
}
.foo--modified {
--foo-color: firebrick;
}
#foo-target {
--foo-color: orchid;
} <p class="foo">
tomato text (fallback to default)
</p>
<p class="foo foo--modified">
firebrick text, specificity 10
</p>
<p class="foo" id="foo-target">
orchid text, specificity 100
</p>
<p class="foo" style="--foo-color: seagreen;">
seagreen text, specificity 1000
</p> # Example: Property dependencies
- when a property depends on another: the value is computed once per element, and the computed value inherited
- much more powerful than inline-styles; becomes function-like
- graceful fallback to
unsetin unsupported browsers
.foo {
--foo-background: linear-gradient(
var(--foo-angle),
seagreen,
tomato
);
background: var(--foo-background);
} <p class="foo">no background (no default, "unset")</p>
<p class="foo" style="--foo-angle: 10deg">dynamic</p>
<p class="foo" style="--foo-angle: 20deg">dynamic</p>
<p class="foo" style="--foo-angle: 30deg">dynamic</p> # The Art of CSS
- CSS is meant for designing consistent design systems
- global first, decrease scope (increase specificity) as required
- developers tend to focus on components, result in per-component styles
- local first, increase scope as required
- but CSS + HTML are co-dependent documents
- should be co-authored, shift responsibility accordingly to increase semantic-ness
# Summary
- embrace the cascade; apply common case, then exceptions
- embrace the DOM inheritance hierarchy and sibling relationships; describe it in the CSS to reduce HTML verbiage
- embrace composition of (usually hierarchical) roles to build styles
- embrace the dynamic nature of custom properties; use it for data-driven documents