Company Workshop
towards a cascade-aligned programming
workshops.oddbird.net/kc24/conventions/
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.
Conventions won’t save us
Conventions can develop over time
CSS Systems, 2008
YUI, Blueprint, 960gs, etc…
<div class=" column col6of12 last " >
<div class="
column col6of12 last
" >
CSS frameworks decrease the maintainability of code.
— Natalie Downe, CSS Systems
(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…
Most of the work is already done for you with global and high-level styles.
— Andy Bell, CUBE CSS
article
section
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
h2
button
/* 1 IDs, 2 attributes, 2 element type */ /* specificity: 1,2,2 */ form#contact button[type='submit']:active { border-color: seaGreen; }
border-color
1, 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
hidden
*
type
.class
[attributes]
#ID
.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 {}
p {}
.text-center {}
@layer settings { … } @layer tools { … } @layer generic { … } @layer elements { … } @layer objects { … } @layer components { … } @layer overrides { … }
@layer
{ … }
<name>
@layer reset { audio[controls] { display: block; } [hidden] { display: none !important; } }
@import url(…)
layer
;
layer(<name>)
settings
tools
generic
elements
objects
components
overrides
Utilities
Components
Themes
Frameworks
Resets
@layer reset { /* least powerful */ } @layer default { /* … */ } @layer theme { /* … */ } @layer components { /* more powerful */ } /* unlayered styles: most powerful */
,
<etc>
Only compared inside a layer
/* 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
<feature>
<tool>
Can it handle colors and lengths?
color
length
(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 to cannot 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
Be the browser’s mentor, not its micromanager.
— Andy Bell
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()
.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
Navigate slides using the arrow-keys.