Utility-first CSS mit

Puzzle Tech Kafi #Frontend

26.08.2020, Mathis Hofer

Context
How it works
Conclusion

Context

CSS Framework Approaches

Vanilla
CSS

Utility CSS Frameworks

Component CSS Frameworks

Full control

From scratch

Quick results

Customize

The "write from scratch" approach:
Vanilla CSS

Write custom styles and classes

Own conventions

No framework

Optional: Preprocessor (LESS, SASS)

The "works out of the box" approach:
Component CSS Frameworks

Predefined component and utility classes

Use library and add new styles reactively

Quick results

"Everything looks the same"

Heavy theme customizing/CSS overriding

User downloads huge library

Component CSS Frameworks

The "middleway" approach:
Utility CSS Frameworks

No prestyled component classes

Low-level utility classes

Build own components

Create a well thought-out library proactively

Write less (or no) CSS code

Utility CSS Frameworks

Influences

Atomic CSS/Functional CSS

TACHYONS

Atomic CSS

Atomic CSS is the approach to CSS architecture that favors small, single-purpose classes with names based on visual function.

Source: Let’s Define Exactly What Atomic CSS is

Atomic CSS (cont. 1)

Challenging CSS Best Practices,

Thierry Koblentz (Smashing Magazine, 2013)

Atomic CSS (cont. 2)

Styling via markup

Avoid CSS redundancy

Stop CSS growing

No contextual or descendant selectors

Styles “sandboxed” to attached nodes

Move modules around without losing styles

No dead styles

CSS Bloat ➡️ HTML Bloat

Not about banning “semantic” class names

How it* works

*

The Framework

https://github.com/tailwindlabs/tailwindcss

 MIT License

First alpha in 2017

First stable in 2019

Implemented as PostCSS plugin

Directives to be used in CSS files

Configuration File

Breakpoints

Fonts

Color palette

Spacings

Variants

Plugins

Purging

# Create minimal tailwind.config.js
npx tailwindcss init

# Include all defaults
npx tailwindcss init --full

Generate with CLI:

Inject Styles

/* Injects tailwind base styles
   & normalize.css */
@tailwind base;

/* Injects tailwind component classes */
@tailwind components;

/* Add custom component classes here */

/* Injects tailwind utility classes */
@tailwind utilities;

/* Add custom utility classes here */

Build

npx tailwindcss build styles.css -o output.css

*only necessary after CSS changes or configuration modifications

// postcss.config.js
module.exports = {
  plugins: [
    // ...
    require('tailwindcss'),
    require('autoprefixer'),
    // ...
  ]
}

*

Preprocessing

PostCSS plugins:

Autoprefixer

Build-time imports: postcss-import

Nesting: postcss-nested, postcss-nesting

Variables: postcss-custom-properties

Future CSS features: postcss-preset-env

Example

Example: Traditional CSS

<div class="chat-notification">
  <div class="chat-notification-logo-wrapper">
    <img class="chat-notification-logo" src="/img/logo.svg" alt="ChitChat Logo">
  </div>
  <div class="chat-notification-content">
    <h4 class="chat-notification-title">ChitChat</h4>
    <p class="chat-notification-message">You have a new message!</p>
  </div>
</div>
.chat-notification {
  display: flex;
  max-width: 24rem;
  margin: 0 auto;
  padding: 1.5rem;
  border-radius: 0.5rem;
  background-color: #fff;
  box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
}
.chat-notification-logo-wrapper {
  flex-shrink: 0;
}
.chat-notification-logo {
  height: 3rem;
  width: 3rem;
}
.chat-notification-content {
  margin-left: 1.5rem;
  padding-top: 0.25rem;
}
.chat-notification-title {
  color: #1a202c;
  font-size: 1.25rem;
  line-height: 1.25;
}
.chat-notification-message {
  color: #718096;
  font-size: 1rem;
  line-height: 1.5;
}

Example: Tailwind

<div class="max-w-sm mx-auto flex p-6 bg-white rounded-lg shadow-xl">
  <div class="flex-shrink-0">
    <img class="h-12 w-12" src="/img/logo.svg" alt="ChitChat Logo">
  </div>
  <div class="ml-6 pt-1">
    <h4 class="text-xl text-gray-900 leading-tight">ChitChat</h4>
    <p class="text-base text-gray-600 leading-normal">You have a new message!</p>
  </div>
</div>

Source:

https://github.com/gojutin/tailwindcss-cheatsheet

Variants

Pseudo-class variants:

Responsive variants:

hover:
focus:
active:
group-hover:
group-focus:
focus-within:
focus-visible:
motion-safe:
motion-reduce:
disabled:
visited:
checked:
first:
last:
odd:
even:
sm:
md:
lg:
xl:

➡️ Prefix utility classes with variant(s)

➡️ Some combinations are not generated per default!

Variants (cont.)

<button class="bg-blue-400 hover:bg-blue-300">
  Pseudo-class
</button>

<button class="md:text-lg xl:text-xl">
  Responsive: Font size
</button>

<button class="hidden lg:inline-block">
  Responsive: Only visible on large screens
</button>

<button class="lg:hover:bg-red-300">
  Combined
</button>

Responsive Design

Mobile-first breakpoints (min-width)

Customizable in tailwind.config.js

sm
640px

md

768px

lg

1024px

xl

1280px

Extract Components

<button class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">
  Button
</button>

Problem:

Utility-first != Utility-only

Extract Components (cont. 1)

Primary choice:

Don't (avoid premature abstraction)

 

Secondary choice:

Template partial (backend)
or JavaScript component (frontend)

 

Tertiary choice:

Component class with @apply directive

Extract Components (cont. 2)

<button class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">
  Button
</button>
<button class="btn-blue">
  Button
</button>
@tailwind base;
@tailwind components;

/* Custom components */
.btn-blue {
  @apply bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded;
}

@tailwind utilities;

Extracted:

Optimizing for Production

Per default the generated CSS is > 1MB

PurgeCSS strips out unused utility classes
(simple Regex based approach)

// tailwind.config.js
module.exports = {
  purge: ['src/**/*.html'],
  ...
}
# Build without unused classes
NODE_ENV=production tailwindcss \
  build styles.css \
  -o output.css

Enough theory...

Conclusion

+

Focus: markup/style in one place

Super high quality documentation and screencasts

Lot of traction

?

How about more complex components (e.g. datepicker)?

Custom component's accessibility

Mental-Shifts

Choose abstractions wisely

Work with JavaScript components/partials

Work with predefined (gradual) spaces/sizes/colors

Background images ➡️ <img>

Design while you write (ideal for prototyping)

Let's change the methodology of how we write CSS!

Questions?

Slides:

slides.com/hupf/tailwind/

 

References:

Official Tailwind Docs

Screencasts by Adam Wathan

Challenging CSS Best Practices (Thierry Koblentz, Smashing Magazine)

Let’s Define Exactly What Atomic CSS is (John Polacek, CSS-Tricks)

In Defense of Utility-First CSS (Sarah Dayan, dotCSS 2019)

Why I Don't Like Tailwind CSS (Chris Hawkes)

 

Made with Slides.com