slides.oddbird.net/workshops/23-smashing-de/7-conventions/

Organizing Conventions

slides.oddbird.net/workshops/23-smashing-de/7-conventions/

@ Smashing Conf Freiburg

Describe how we Write Consistent Code

Describe how we Write Organized Code

Describe how we Write Maintainable Code

CSS Mental Model
Selector Naming
Code Organization

Headshot of Chris Coyier

I think they are all pretty neat and I see the point, but I’m a bunch of years into this and still generally…

– Chris Coyier, @chriscoyier

Headshot of Chris Coyier

write the selector I need and then style with that.

– Chris Coyier, @chriscoyier

Headshot of Chris Coyier

write the selector I need and then style with that.

– Chris Coyier, @chriscoyier

Headshot of Eric Meyer

Once you become experienced, you can break the rules in creative ways.

– Eric Meyer, Structural Naming (2004)

Just because InstaSalesBook Did It

Doesn’t mean you need to

Headshot of Una Kravets

We often hear “Are you using OOCSS or BEM?” but these things are not mutually exclusive. In fact, most of the time, we’re… mixing them up, customizing them to suit our own needs.

– Una Kravets, Atomic OOBEMITSCSS

WWW parsers should ignore tags which they do not understand, and ignore attributes which they do not understand of tags which they do understand.

WWW Project

Support visual and non-visual media.

– Håkon Lie

Provide hints that the browser may or may not use.

– Håkon Lie

Ignored Selectors

:has(p) {
background: rebeccapurple;
}

Ignored Decalarations

body {
color: oklch(320deg 50% 50%);
}

Websites don’t need to cannot possibly look the same in every browser.

– Me, again

These differences Are a Feature of The Web

We don’t control styles We Hint & Suggest

The 🌊 Cascade Relies on Semantic Clues

Layers of… 🎯 Selector Specificity

  1. Inline style="…"
  2. Unique #IDs
  3. Reusable .classes & [attributes]
  4. Element types
  5. Universal *

Specificity provides A Name-Layering Convention

Use low specificity To Describe Systems

Use high specificity To Describe Details

.button {
background: darkgreen;
}

.button-submit {
background: rebeccapurple;
}

Source Order Is Meaningful

Not safe to shuffle

Sort code From General to Specific

Link States

a:link {color: blue;}        /* specificity = 1,1 */
a:visited {color: purple;} /* specificity = 1,1 */
a:hover {color: magenta;} /* specificity = 1,1 */
a:active {color: red;} /* specificity = 1,1 */
main {
/* display: block; */
display: flex;
display: grid;
}
/* … general … */
@media () {}
@supports () {}

Page Structures

#header {}
#nav {}
#main {}
#footer {}

HTML5

header {}
nav {}
main {}
footer {}

~2006-2010… Major CSS Libraries

YUI, Blueprint, 960gs, etc…

Central Class Library

<div class=" column col6of12 last " >

Can be Massive

Solve Every Problem for Every Project

CSS frameworks decrease the maintainability of code.

– Natalie Downe

  • High quality code
  • To tight deadlines
  • For handover to external developers
  1. Elements grouped by Type
  2. Classes grouped by Effect
  3. IDs grouped by Component

1… General Styles

  • body styles
  • reset
  • links
  • headings
  • other elements, tags

2… Helper Styles

  • forms
  • notifications
  • errors
  • consistant items with normally just one class

3… Page Structure

  • header
  • footer
  • generic layout blocks
  • skeleton including page furniture

4… Page Components

“most of your styles will be in here”

5… Overrides

avoid as much as possible

Fixed widths Only On Containers

(usually in ems)

Be Afraid Of Heights

(the variable axis for text)

Don’t Overly Sandbox

Styles should be reusable

div#page div.teaser ul.products li p.name

Don’t re-engineer Seperate Solutions

Develop in browsers simultaneously

How do you scale CSS For Thousands of Pages?

New pages should Rarely Require New CSS

Components Share Patterns

Avoid even unique component styles

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 limiting A Class To One Tag

.text is better than p.text or div.text

Be Flexible

  • Container sets width \
  • Content sets height

"Focus on What Not Where"

(from CSS Systems)

<div class="media">
<img class="fixedMedia" src="myImg.png" />
<div class="text">...</div>
</div>
<figure class="media">
<img class="fixedMedia" src="myImg.png" />
<figcaption class="text">...</figcaption>
</figure>
<li class="media">
<img class="fixedMedia" src="myImg.png" />
<p class="text">...</p>
</li>
<article class="media">
<img class="fixedMedia" src="myImg.png" />
<header class="text">...</header>
</article>

Extend Objects By Applying Multiple Classes

<div class="media media_reverse">
<img class="fixedMedia" src="myImg.png" />
<div class="text">...</div>
</div>

Mix & match Container & Content Objects

To create unique components, without unique CSS

Mentions Missing Scope

We’ll come back to that…

2009-Present Systems/Objects Repackaged

SMACSS, BEM, Atomic Design, ITCSS, etc…

Block Standalone Entity

Meaningful on its own

Element Always Part Of A Block

No standalone meaning

Modifier Change Appearance Or Behavior

A flag on a block or element

Not Size or Complexity Self-Sufficiency & Belonging

Selector Naming

.block { }
.block--modifier { }
.block__element { }
.block__element--modifier { }

Always Flat Specificty

No .combined.selectors

Modifier Exception

.block--modifier .block__element { }
/* Internal & Frame */
.button {
text-align: center;
border: 1px solid black;
}

Parent block provides External Positioning

/* External */
.header__button {
margin: 30px;
}

Open/Closed Principle

Open to modifier classes, closed to direct changes

/* ⚠ Not This */
.header .button {
margin: 30px;
}

2011, Jonathan Snook SMACSS

Scalable and Modular Architecture for CSS

Organized on Category

  1. Base (element type)
  2. Layout (use #id)
  3. Module (use .classes)
  4. State (use !important)
  5. Theme (i.e. “skin”)

Layout Styles

#article { float: left; }
.l-flipped #article { float: right; }
.l-fixed #article { width: 600px; }

Related elements within a module use the base name as a prefix.

– Jonathan Snook, SMACSS

State Types

.is-collapsed, .msg.is-error, .is-hidden

/* Callout Module */
.callout { }

/* Callout Module with State */
.callout.is-collapsed { }
// in module-name.css
.mod { border: 1px solid; }

// in theme.css
.mod { border-color: blue; }
/* 6 generations */
body.article > #main > #content > #intro > p > b { }
body.article #intro b { }

2013, Brad Frost Atomic Web Design

A design process based on composition

Low/High Specificity What Selector Weight?

Generic to Explicit What Desired Impact?

From p {} through .text-center {}

/* 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; }

2015 BEMIT

Extending the BEM syntax for ITCSS

NameSpaces

  • Components » .c-<name>
  • Objects » .o-<name>
  • Utilities » .u-<name>
  • States » .is-<name>/.has-<name>
  • Media » .<name>@<media>
<div class="o-media@md  c-user  c-user--premium">
<img src="" alt="" class="o-media__img@md c-user__photo c-avatar" />
<p class="o-media__body@md c-user__bio">...</p>
</div>
<button custom-attribute="anything">...</button>

Attribute Variations

[am-Button~='large']

<a am-Button>Normal button</a>
<a am-Button='large'>Large button</a>
<a am-Button='rounded'>Rounded button</a>
<a am-Button='large rounded'>Large rounded button</a>

Choosing A Prefix

  • data-* Provides JavaScript API
  • *-* Ensures Future-Proofing
  • [data-attr]Presence (even if empty)
  • [data-attr="..."]Exact match
  • [data-attr*="..."]Any match
  • [data-attr~="..."]Space-delimited (like classes)
  • [data-attr|="..."]Hyphen-delimited
  • [data-attr^="..."]Starts with…
  • [data-attr$="..."]Ends with…
  • [data-attr="..." i|s]Case sensativity

Using JS Frameworks Single File Components

Not just Is-Inside-Of

(no lower boundary)

But specifically Is-Owned-By

(clear lower boundaries)

<article-component>
<h2>...</h2>

<sub-component />

<footer>...</footer>
</article-component>
<article-component data-scope='article'>
<h2 data-scope='article'>...</h2>

<sub-component data-scope='article' data-scope='sub'>
<!-- internals of sub only have scope='sub' -->
</sub-component>

<footer data-scope='article'>...</footer>
</article-component>
<style scoped>
article { }
h2 { }
footer { }
</style>
article[data-scope=article] { }
h2[data-scope=article] { }
footer[data-scope=article] { }
<article data-scope="article instance-id">
h2[data-scope~=article] {
/* all instances */
}
h2[data-scope~=instance-id] {
/* unique instance */
}

Emotion

render(
<button css={css`
padding: 32px;
background-color: hotpink;
font-size: 24px;
border-radius: 4px;
&:hover {
color:
${color};
}
`
} >...</button>
)
<button class="css-1h3ivcl">...</button>

JS doesn’t understand Lengths & Colors &c.

2015, Yahoo! Atomic CSS

<div class="Bgc(#0280ae.5) C(#fff) P(20px)">...</div>
<div class="Pos(a) Bgc(brandColor) W(columnWidth) H(90px)">...</div>

Tachyons

<body class="bg-green black-70 pa4">
<h1 class="f1">...</h1>
</body>

Tailwind CSS

<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>
.btn {
  @apply font-bold py-2 px-4 rounded;
}
.btn-blue {
  @apply bg-blue-500 text-white;
}
.btn-blue:hover {
  @apply bg-blue-600;
}

I don’t Get It ¯\_(ツ)_/¯

When you… reduce the amount of time you spend writing and editing CSS… you must instead spend more time changing HTML classes

– Nicolas Gallagher

2020, Andy Bell CUBE CSS

Composition Utility Block Exception

Adjusting OOCSS/BEM to De-Emphasize Blocks

Most of the work is already done for you with global and high-level styles.

– Andy Bell

Relies onIntelligent” Selectors

No “intervention by the author”

.flow > * + * {
margin-top: var(--flow-space, 1em);
}
.wrapper {
margin-inline: auto;
padding-inline: 1rem;
max-width: 60rem;
}
.bg-primary { background: #ff00ff; }
.bg-secondary { background: #ffbf81; }

.color-primary { color: #ff00ff; }
.color-secondary { color: #ffbf81; }
.card[data-state='reversed'] {
display: flex;
flex-direction: column-reverse;
}

Class Grouping

<article
class="[ card ] [ section box ] [ bg-base color-primary ]"
data-state="reversed"
></article>

At OddBird We Mix & Match

Ideas from Systems, OOCSS, ITCSS, AMCSS, CUBE

Getting Started

my recommendations, your milage may vary

/* first & easy to find */
@layer defaults, components, utilities;
<style>/* keep this before linked styles */
@layer defaults, components, utilities;
</style>
<link rel="stylesheet" href="">

Then Layer Dependencies

resets, design systems, frameworks, libraries…

Clearly define… Each Tool’s Priority

Clearly define… Tool-Specific Overrides

@import url(bootstrap.css) layer(bootstrap.vendor);

@layer bootstrap.overrides {
/* anything here will override bootstrap */
}

For flexibility Layer Everything

Once we use un-layered styles, that’s as high as we can go

/* Vue example */
<template></template>
<script></script>

<style>
@layer components {
/* all our component styles */
}
</style>

Good Tools Let Us Use CSS Features

(otherwise ‘tools’ become obstacle)

¯\_()_/¯

You do you (no gate-keeping)

Encourages Nuanced & Explicit Priorities

More layers Using Custom Properties

(with layered fallbacks)

a:link,
a:visited
{
color: var(--theme-link, var(--link-focus, var(--global-link)));
}

a:hover,
a:focus,
a:active
{
--link-focus: var(--theme-focus, var(--global-focus));
}

Define some constraints. Let the language work out the details.

– Keith J Grant

Use Built-In Attributes When Available

(We ❤ “intelligent” selectors)

is-collapsed

[aria-expanded] & [aria-pressed]

Use Utility Classes Where Meaningful & Common

.visually-hidden | .elide

DRY Code Don’t Repeat Yourself

.warning { border-color: red; }

DSfP Don’t Stretch for Patterns

.bc-r { border-color: red; }

[data-theme] {
background-color: var(--theme-bg, var(--global-bg, transparent));
color: var(--theme-color, var(--global-color, inherit));
}
[data-theme='light'] {
--theme-bg: var(--neutral-light);
--theme-color: var(--neutral-dark);
--theme-link: var(--primary-dark);
}

[data-theme='dark'] {
--theme-bg: var(--neutral-dark);
--theme-color: var(--neutral-light);
--theme-link: var(--primary-light);
}

Attribute-namespace For Related Classes

.is-success | .is-error [data-msg="success | error"]

A Wider 🌊 Cascade Allows More Nuance in Layering

W3C is working to Update & Extend The Cascade

@layer reset {
*, ::before, ::after { box-sizing: border-box; }
html { line-sizing: normal; }
body { margin: 0; }
}

@layer base { }
@layer components { }
@layer utilities { }

The Cascade is Useful

To guide our conventions

CSS is unlike anything else… designed for the realities of a flexible, multilingual, multi-device web.

– Rachel Andrew