What are you trying? Are you happy with it?
(as a point of reference)
Your mileage will vary
Doesn’t mean we all need to
I think they are all pretty neat and I see the point…
— Chris Coyier
But I’m a bunch of years into this and still generally… write the selector I need and then style with that.
— Chris Coyier
Conventions won’t save us
Conventions can develop over time
CSS Systems, 2008
YUI, Blueprint, 960gs, etc…
<div class="
column col6of12 last
" >
(this was before RWD & Media Queries)
(which may provide sizing)
(the variable axis for blocks)
Object Oriented CSS, 2009
A CSS “object” is a repeating visual pattern, that can be abstracted into an independent snippet of HTML, CSS, and possibly JavaScript.
— Nicole Sullivan, OOCSS
Avoid even unique component styles
(Unique components, without unique CSS)
width
height
SMACSS, BEM, ITCSS, CUBE, etc…
article
& section
Elements
Building with accessible semantics from the get-go can give you expressive, meaningful style hooks for free.
— Ben Myers, Style with Stateful, Semantic Selectors
/* expressive semantics */
form:has(:invalid) [type="submit"] { … }
/* some name we came up with? */
.form__btn--submit--invalid { … }
a[href*="://"] { /* external links */ }
a[href^="https:"] { /* secure links */ }
a[href$=".pdf"] { /* pdf links */ }
button[aria-pressed=true] { /* pressed buttons */ }
img:not([alt]) { /* images without alt text */ }
.flow > * + * {
margin-block-start: var(--flow-space, 1em);
}
No “intervention by the author”
* { /* universal */ }
p { /* type */ }
.summary { /* attribute */ }
#call-to-action { /* id */ }
* { /* 'universal' */ }
button, div, span {
/* 'type' (element) */ }
::before, ::after, ::part() {
/* 'pseudo-element' */ }
.action, .summary {
/* 'class' */ }
:hover, :user-invalid {
/* 'pseudo-class' */ }
[type=“submit”] {
/* 'attribute' */ }
#send { /* 'id' */ }
h2:has(> button[aria-expanded="false"]) + div {
display: none;
}
div
that comes after an h2
button
child, that is not expanded)/* 1 IDs, 2 attributes, 2 element type */
/* specificity: 1,2,2 */
form#contact button[type='submit']:active {
border-color: seaGreen;
}
border-color
declaration has a selector1, 2, 2
[
1
,0,2]
vs [0,3,2] vs [0,2,3]
[0,
3
,2]
vs [0,
2
,3]
Especially “At Scale”
/* …default table styles… */
table[rules=cols i] > tfoot > tr > td,
table[rules=cols i] > tfoot > tr > th,
table[rules=all i] > tfoot > tr > td,
table[rules=all i] > tfoot > tr > th {
border-color: black;
}
[hidden] { display: none; }
display:none
on the hidden
attribute*
type
s.class
es & [attributes]
#ID
s.block .element.modifier { /* 3 */ }
.block__element--modifier { /* 1 */ }
.🤬-bootstrap {
font-weight: bold !important;
}
demo:
(still meaningful and expressive)
Inverted Triangle CSS, 2014
From p {}
through .text-center {}
@layer settings { … }
@layer tools { … }
@layer generic { … }
@layer elements { … }
@layer objects { … }
@layer components { … }
@layer overrides { … }
@layer
{ … }
@layer
<name>
{ … }
@layer reset {
audio[controls] { display: block; }
[hidden] { display: none !important; }
}
@import url(…)
layer
;
@import url(…)
layer(<name>)
;
@layer
settings
{ … }
@layer
tools
{ … }
@layer
generic
{ … }
@layer
elements
{ … }
@layer
objects
{ … }
@layer
components
{ … }
@layer
overrides
{ … }
@layer
Utilities
{ … }
@layer
Components
{ … }
@layer
Themes
{ … }
@layer
Frameworks
{ … }
@layer
Resets
{ … }
@layer reset { /* least powerful */ }
@layer default { /* … */ }
@layer theme { /* … */ }
@layer components { /* more powerful */ }
/* unlayered styles: most powerful */
@layer
<name>
,
<name>
,
<etc>
;
Only compared inside a layer
demo:
/* establish layer order */
@layer one, two, three;
/* add code to layers as needed */
@import url(two.css) layer(two);
@layer three { … }
@layer one { … }
@layer two { … }
@layer components {
@layer state { … }
}
/* access nested layers */
@layer components.state { … }
/* system.css */
@layer theme { … }
@layer components { … }
@import url(system.css) layer(system);
@layer system.theme { … }
@layer system.components { … }
@import url(system.css) layer(system);
@layer system.theme { … }
@layer system.components { … }
@layer system.custom { … }
@import url(bootstrap.css) layer(bootstrap.theirs);
@layer bootstrap.ours {
/* anything here will override bootstrap */
}
@layer components {
@layer defaults, themes, state;
}
my recommendations
/* add as the first styles */
@layer reset, framework, components, utilities;
<style>/* keep this before linked styles */
@layer reset, framework, components, utilities;
</style>
<link rel="stylesheet" href="…">
<link rel="stylesheet" href="…">
<style>
@layer reset, framework, components, utilities;
@import url(…) layer(reset);
@import url(…) layer(framework);
</style>
*Including inter-org dependencies
@import url(bootstrap.css) layer(bootstrap.vendor);
@layer bootstrap.overrides {
/* anything here will override bootstrap */
}
<template>…</template>
<style>
@layer components {
/* … */
}
</style>
*the styles will be hidden from older browsers
Can it handle color
s and length
s?
(otherwise ‘tools’ become obstacles)
<div class="Bgc(#0280ae.5) C(#fff) P(20px)">...</div>
<body class="bg-green black-70 pa4">
<h1 class="f1">...</h1>
</body>
<div class="md:flex">
<div class="md:flex-shrink-0">
<img class="rounded-lg md:w-56" src="..." alt="">
</div>
<div class="mt-4 md:mt-0 md:ml-6">
<div class="uppercase tracking-wide text-sm text-indigo-600 font-bold">...</div>
</div>
</div>
¯\_
(ツ)
_/¯
When you… reduce the amount of time you spend writing and editing CSS… you must instead spend more time changing HTML classes…
— Nicolas Gallagher
.btn {
@apply font-bold py-2 px-4 rounded;
}
.btn-blue {
@apply bg-blue-500 text-white;
}
.btn-blue:hover {
@apply bg-blue-600;
}
Websites
don’t need tocannot possibly look the same in every browser.
— The web, paraphrased
The fact we can control a paper page is really a limitation of that medium.
— John Allsopp, A Dao of Web Design
Define some constraints. Let the language work out the details.
— Keith J Grant
CSS Systems, OOCSS, ITCSS, CUBE…
Use custom elements, attributes, ids, pseudos…
.card.big { /* classes */ }
.card--big { /* BEM */ }
/* Custom element with attributes */
my-card[data-size="big"] { /* … */ }
(with or without @layer
)
Styles should be reusable…
/* .page header { … } */
.page__header { … }
header.masthead { … }
/* Runs the risk of becoming out of date; not very maintainable. */
.blue { color: blue; }
/* Depends on location in order to be rendered properly. */
.header span { color: blue; }
/* Too specific; limits our ability to reuse. */
.header-color { color: blue; }
/* Nicely abstracted, very portable, doesn’t risk becoming out of date. */
.highlight-color { color: blue; }
a { text-decoration: underline; }
nav a { text-decoration: none; }
a { text-decoration: var(--link-decoration, underline); }
nav { --link-decoration: none; }
a:not([data-link]) {
text-decoration: var(--link-decoration, underline);
}
nav {
--link-decoration: none;
}
:not()
to exclude selectors.wrapper {
margin-inline: auto;
padding-inline: 1rem;
max-width: 60rem;
}
[hidden] { display: none !important; }
[visually-hidden]:not(:focus-within) {
clip: rect(0 0 0 0);
clip-path: inset(50%);
height: 1px;
overflow: hidden;
position: absolute;
white-space: nowrap;
width: 1px;
}
.bg-primary { background: #ff00ff; }
.bg-secondary { background: #ffbf81; }
[data-bg=primary] { background: #ff00ff; }
[data-bg=secondary] { background: #ffbf81; }
[data-bg] { background: var(--canvas-color); }
[data-bg=primary] { --canvas-color: #ff00ff; }
[data-bg=secondary] { --canvas-color: #ffbf81; }
[style*='--canvas-color:'] {
background: var(--canvas-color);
}
CSS is unlike anything else… designed for the realities of a flexible, multilingual, multi-device web.
— Rachel Andrew