Tailwind CSS - The Modern Utility-First Front-End Framework (No Slides, Please)
This document has been autogenerated from slides.
modern, utility-first styling for the web
- Front-End Frameworks
- Tailwind Overview
- Installation
- Utility-First Styling
- Separation of Concerns?
- Responsive Design
- Flex & Grid Layout
- Customization
- Interactive Components with Alpine.js
- Conclusions
Front-End Frameworks
Concept
Front-end frameworks, of which Tailwind CSS is one, are toolkits that help web developers create the front-end - the visible user interfaces of web pages and applications.
-
They do so by offering readymade styles (written in CSS) that the web developer can apply to their own HTML elements just by adding class names.
-
They are generally created in such a way that the web designer need not know how they work, but simply must know how to apply them properly.
-
The most successful front-end frameworks are those that offer the most commonly-used styles in the easiest-to-use way.
Two flavors
Front-end frameworks generally fall into one of two categories.
-
Component frameworks - like the older Bootstrap framework - give you large, pre-designed components (buttons, navigation bars, cards, etc.) that all look a certain way out of the box.
-
Utility frameworks - like Tailwind CSS - give you many tiny single-purpose classes (one for color, one for padding, one for font size, etc.) that you combine together to build any design you want.
-
Utility frameworks have become the more popular choice in recent years because they offer more design flexibility without writing custom CSS.
Tailwind Overview
Evolution
Tailwind CSS was first released by Adam Wathan in 2017 as a reaction to the limitations of component-based frameworks like Bootstrap.
-
It is maintained by Tailwind Labs and a large community of open-source contributors.
-
It is now one of the most widely used CSS frameworks, popular with both beginners and professional developers.
-
Tailwind CSS is given away free under the MIT open-source license.
Concept
Tailwind CSS is a free collection of small, single-purpose CSS classes for building websites and web applications. It contains …
-
a built-in CSS reset (called Preflight) that removes the browsers’ inconsistent default styles so that every project starts from the same clean baseline.
-
hundreds of utility classes for things like color, spacing, typography, borders, shadows, and more - each class does exactly one small thing.
-
responsive design built in - every utility class can be applied only at certain screen sizes by adding a prefix like
md:orlg:. -
layout utilities for flexbox and CSS grid, making it easy to position elements without writing any custom CSS.
-
state utilities so you can change styles when the user hovers, focuses, or clicks on an element, with prefixes like
hover:andfocus:.
Installation
The easy way: the Play CDN
Unlike many modern tools, Tailwind CSS can be added to a web page with a single line of code - no terminal commands, no installations, no setup files.
- Add the following
scripttag to theheadelement of your HTML document.
<script src="https://cdn.tailwindcss.com"></script>
-
That’s it. All of Tailwind’s classes are now available to use anywhere in your HTML.
-
This approach is called the Play CDN. A CDN (Content Delivery Network) is a cloud-based hosting environment that delivers files quickly from the nearest data center.
-
The Play CDN is officially supported by Tailwind for learning, prototyping, and small personal projects. Larger professional projects typically use a more advanced setup, but you do not need to worry about that yet.
A complete starter file
Here is a complete, working HTML file that uses Tailwind CSS:
<!DOCTYPE html>
<html>
<head>
<title>My Tailwind Page</title>
<!-- Link to the Tailwind CSS Play CDN -->
<script src="https://cdn.tailwindcss.com"></script>
</head>
<body>
<h1 class="text-3xl font-bold text-blue-600">Hello, Tailwind!</h1>
</body>
</html>
- The
text-3xlclass makes the text large,font-boldmakes it bold, andtext-blue-600makes it blue. No CSS file required.
Customization
You can still write your own CSS code alongside Tailwind whenever you want.
- After the link to the Tailwind script in the code, add a link to your own custom CSS file.
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" type="text/css" href="css/custom.css" />
-
Write any custom CSS rules in your
custom.cssfile. They will work alongside Tailwind’s utility classes. -
However, with Tailwind you will find that you rarely need to write custom CSS at all - the utility classes can handle most of what you want to do.
Utility-First Styling
Concept
Instead of writing CSS in a separate stylesheet, with Tailwind you style elements directly by adding utility classes to them in your HTML.
- Each class does one small thing. For example:
p-4adds padding of1remon all sides. (See CSS sizing units for whatremmeans.)bg-yellow-200sets the background color to a light yellow.text-centercenters the text.rounded-lggives the element large rounded corners.
- You combine these classes together to build any design you want.
<button class="bg-blue-500 text-white p-4 rounded-lg">Click me!</button>
- This creates a blue button with white text, comfortable padding, and rounded corners - all without writing a single line of custom CSS.
Common utilities
A small sampling of the most useful Tailwind utility classes:
-
Color -
text-red-500,bg-green-200,border-gray-400 -
Spacing -
p-4(padding),m-2(margin),px-6(padding left+right),mt-8(margin top) -
Typography -
text-sm,text-xl,font-bold,italic,underline -
Borders -
border,border-2,border-blue-500,rounded,rounded-full -
Sizing -
w-full(full width),w-1/2(half width),h-screen(full screen height, equivalent to100vh- see CSS sizing units) -
The Tailwind documentation is searchable and contains a complete reference for every class.
Separation of Concerns?
The traditional principle
In the traditional approach to web architecture, we are taught that the three core front-end technologies each have a distinct purpose:
-
HTML controls the content of the page.
-
CSS controls the presentation of that content.
-
JavaScript controls the behavior of the page.
-
Traditionally, each of these “concerns” lives in its own separate file, or at least in its own separate block of code. Changing the look of a page should mean editing only the CSS file, not the HTML.
-
This idea is called separation of concerns, and for decades it has been considered a best practice in software engineering.
Tailwind appears to break the rule
At first glance, Tailwind looks like it violates the separation of concerns principle.
<!-- Traditional approach -->
<button class="primary-action">Sign up</button>
<style>
.primary-action {
background-color: blue;
color: white;
padding: 1rem;
border-radius: 0.5rem;
}
</style>
<!-- Tailwind approach -->
<button class="bg-blue-500 text-white p-4 rounded-lg">Sign up</button>
- In the Tailwind version, the styling lives inside the HTML, mixed in with the content. Many traditionalists find this objectionable.
Tailwind’s counter-argument
The creators of Tailwind argue that the traditional view misunderstands what “separation of concerns” really means.
-
They observe that, in practice, HTML and its visual styling are not actually independent of each other.
-
When you change a button from a row of icons to a vertical list, you almost always have to change both the HTML structure and its CSS together.
-
If two things always change together, they are not really separate concerns - they are one concern split across two files.
-
True separation of concerns, the argument goes, is about decoupling things that change independently - not about which file each line of code lives in.
A different kind of separation
Tailwind proposes a different boundary line between concerns:
-
The content (the words, links, and images) is one concern - it lives in HTML.
-
The appearance of a particular component (this button, this card, this navbar) is another concern - and it lives alongside the HTML for that component.
-
The design system itself (what counts as “blue-500”, what spacing values are allowed) is a third concern - it lives in Tailwind’s configuration.
-
By colocating a component’s HTML and styling, you can read, copy, or change a whole component in one place - without hunting through a separate CSS file.
Honest tradeoffs
Neither approach is universally “correct.” Each has real benefits and real costs.
Advantages of utility-first (Tailwind):
- A component’s look and structure are visible together.
- Changing one component cannot accidentally break another, because there are no shared CSS class names.
- Less custom CSS is written overall, so production CSS files are very small.
Advantages of separated CSS (traditional):
- HTML stays clean and easy to read, especially for non-programmers.
- Repeated styles only need to be written once in a shared class.
-
The visual design can be redone without touching the HTML at all.
- Reasonable, experienced developers disagree about which approach is better, and both are widely used in industry today.
Responsive Design
Concept
Tailwind makes responsive design easy by letting you add a prefix to any utility class so that it only applies at certain browser widths.
The prefixes correspond to the following breakpoints:
-
sm:applies at browser widths >=640px (Small Devices) -
md:applies at browser widths >=768px (Medium Devices) -
lg:applies at browser widths >=1024px (Large Devices) -
xl:applies at browser widths >=1280px (Extra Large Devices) -
2xl:applies at browser widths >=1536px (Extra Extra Large Devices) -
A class with no prefix applies at all browser widths, including the smallest ones.
Usage
For example, the following heading will be small on phones, larger on tablets, and largest on desktops.
<h1 class="text-xl md:text-3xl lg:text-5xl">
This heading grows with the screen!
</h1>
-
The approach is called mobile-first - you write the styles for the smallest screens first, then add prefixed versions that override them at larger sizes.
-
This pattern works for any utility class - color, spacing, layout, sizing, and more.
<div class="bg-red-200 md:bg-green-200 lg:bg-blue-200">
Background color changes with the screen size.
</div>
Flex & Grid Layout
Concept
Tailwind provides utility classes for both flexbox and CSS grid - the two modern ways to lay out elements on a page.
-
You apply layout classes to a container element, and the children inside it are arranged automatically.
-
Like all utility classes, layout classes can be combined with responsive prefixes to create different layouts at different screen sizes.
Flexbox
Flexbox is great for arranging elements in a single row or column.
<div class="flex gap-4">
<article class="flex-1">First article</article>
<article class="flex-1">Second article</article>
</div>
-
flexturns thedivinto a flex container, so its children sit side by side. -
gap-4adds space between the children. -
flex-1tells each article to take up an equal share of the available width. -
To stack them vertically on small screens but side-by-side on medium screens and larger, use a responsive prefix:
<div class="flex flex-col md:flex-row gap-4">
<article class="flex-1">First article</article>
<article class="flex-1">Second article</article>
</div>
Grid
For more complex layouts with rows and columns, use Tailwind’s grid utilities.
<div class="grid grid-cols-12 gap-4">
<article class="col-span-6">First article</article>
<article class="col-span-6">Second article</article>
</div>
-
gridturns the container into a CSS grid. -
grid-cols-12divides the container into 12 equal columns - just like Bootstrap’s grid. -
col-span-6makes each article take up 6 of those 12 columns - i.e. half the page. -
And, of course, you can change column spans at different breakpoints:
<div class="grid grid-cols-12 gap-4">
<article class="col-span-12 md:col-span-6">First article</article>
<article class="col-span-12 md:col-span-6">Second article</article>
</div>
Centering content
A common task is centering content horizontally on the page. Tailwind makes this easy:
<body>
<div class="max-w-4xl mx-auto p-4">
<h1>My page content</h1>
<p>Everything inside this div is centered with a maximum width.</p>
</div>
</body>
-
max-w-4xlsets a maximum width so the content does not stretch too wide on large screens. -
mx-autosets the left and right margins toauto, which centers the element horizontally. -
p-4adds a little padding so the content does not touch the edges of the screen.
Interactivity & Customization
State prefixes
Tailwind makes it easy to change an element’s style when the user interacts with it, again by using a prefix on a utility class.
-
hover:applies the style when the user hovers their mouse over the element. -
focus:applies when the element is focused (e.g. a clicked input field). -
active:applies when the element is being actively clicked.
<button class="bg-blue-500 hover:bg-blue-700 text-white p-4 rounded">
Hover over me!
</button>
- This button is blue normally, and turns a darker blue when the user hovers over it - no Javascript required.
Going beyond the defaults
Tailwind comes with sensible default colors, spacing values, and font sizes - but you can use any value you like by putting it in square brackets.
<div class="bg-[#abcdef] w-[37px] text-[22px]">
Custom values for color, width, and font size.
</div>
-
This is called an arbitrary value. It lets you escape the defaults whenever needed. Any CSS sizing unit (
px,rem,vw,%, etc.) can be used inside the brackets. -
For most projects, however, the default values are designed to look good together and you will rarely need to use arbitrary values.
Interactive Components with Alpine.js
Concept
Tailwind itself ships no JavaScript - it is purely a CSS framework. To build rich interactive components like modals, dropdowns, accordions, and carousels, the Tailwind community standardly pairs Tailwind with Alpine.js.
-
Alpine.js is a tiny (~15 KB) JavaScript framework designed to be sprinkled directly into your HTML - the same colocation philosophy as Tailwind’s utility classes.
-
The simple
hover:andfocus:interactivity customization still applies for things that respond directly to the mouse or keyboard. -
Alpine is for behaviors that need to remember state - for example, “is this menu currently open?”, “which slide is the carousel showing?”, or “has the user dismissed this dialog?”.
Installation
Just like Tailwind, Alpine.js can be added to a web page with a single line of code - no terminal commands, no installations, no setup files.
- Add the following
scripttag to theheadelement of your HTML document, alongside the Tailwind script.
<!DOCTYPE html>
<html>
<head>
<title>My Tailwind + Alpine Page</title>
<script src="https://cdn.tailwindcss.com"></script>
<script defer src="https://unpkg.com/alpinejs"></script>
</head>
<body>
<!-- Your interactive components go here -->
</body>
</html>
- The
deferattribute on the Alpine script tells the browser to wait until the page’s HTML has finished loading before running Alpine - this is important.
Alpine basics
Just three core “directives” (special HTML attributes) are enough to build most interactive components:
-
x-data="{ ... }"declares a small piece of state on an element. The state is a JavaScript object, written in curly braces. -
x-show="..."shows or hides an element based on a state expression. If the expression is true, the element appears; if false, it disappears. -
@click="..."(a shorthand forx-on:click) runs a small JavaScript expression when the element is clicked. -
A few other helpful directives you will see in the examples that follow:
@mouseenterand@mouseleave- run code when the mouse enters or leaves an element.x-transition- fade things in and out smoothly when they show or hide.:src,:class, etc. - shorthand forx-bind:src,x-bind:class, used to make HTML attributes reactive to state.
Example: Dropdown menu
A button that toggles a dropdown menu open and closed.
<div x-data="{ open: false }" class="relative inline-block">
<button
@click="open = !open"
class="bg-blue-500 text-white px-4 py-2 rounded"
>
Menu
</button>
<div
x-show="open"
@click.outside="open = false"
class="absolute mt-2 bg-white shadow-lg rounded p-2"
>
<a href="#" class="block px-4 py-2 hover:bg-gray-100">Home</a>
<a href="#" class="block px-4 py-2 hover:bg-gray-100">About</a>
<a href="#" class="block px-4 py-2 hover:bg-gray-100">Contact</a>
</div>
</div>
-
The button flips
openbetweentrueandfalseeach time it is clicked. -
The
@click.outsidemodifier is a convenience: it automatically closes the menu when the user clicks anywhere else on the page.
Example: Modal dialog
A pop-up dialog with a dimmed backdrop.
<div x-data="{ open: false }">
<button @click="open = true" class="bg-blue-500 text-white px-4 py-2 rounded">
Open dialog
</button>
<div
x-show="open"
x-transition
class="fixed inset-0 bg-black/50 flex items-center justify-center"
>
<div class="bg-white p-6 rounded-lg max-w-md">
<h2 class="text-xl font-bold">Hello!</h2>
<p class="mt-2">This is a modal dialog.</p>
<button @click="open = false" class="mt-4 bg-gray-200 px-4 py-2 rounded">
Close
</button>
</div>
</div>
</div>
-
fixed inset-0makes the backdrop cover the entire window. -
bg-black/50is a semi-transparent black, dimming the page behind the dialog. -
x-transitionfades the dialog in and out instead of snapping it on and off.
Example: Tooltip
A small message that appears when the user hovers over an element.
<span x-data="{ hovered: false }" class="relative inline-block">
<button
@mouseenter="hovered = true"
@mouseleave="hovered = false"
class="bg-gray-200 px-3 py-1 rounded"
>
Hover me
</button>
<span
x-show="hovered"
class="absolute bottom-full mb-2 left-1/2 -translate-x-1/2
bg-black text-white text-sm px-2 py-1 rounded whitespace-nowrap"
>
Helpful hint!
</span>
</span>
- A pure-CSS tooltip is also possible with Tailwind’s
group-hover:modifier, but Alpine generalizes naturally to more complex tooltips - e.g. delayed appearance, click-to-dismiss, or content that depends on what the user has already done on the page.
Example: Accordion / collapsible
A section of content that can be expanded and collapsed - the equivalent of Bootstrap’s “collapse” component.
<div x-data="{ open: false }" class="border rounded">
<button @click="open = !open" class="w-full text-left p-4 font-bold">
Click to expand
</button>
<div x-show="open" class="p-4 border-t">
Hidden content revealed! You can put anything in here - paragraphs, lists,
images, even other Alpine components.
</div>
</div>
- This same pattern scales to a list of accordion items - just repeat the outer
divonce per item, each with its ownx-data.
Example: Image carousel
A slideshow of images with previous/next buttons.
<div
x-data="{
current: 0,
images: ['slide1.jpg', 'slide2.jpg', 'slide3.jpg']
}"
class="relative"
>
<img :src="images[current]" class="w-full rounded-lg" />
<button
@click="current = (current - 1 + images.length) % images.length"
class="absolute left-2 top-1/2 bg-white/80 px-3 py-1 rounded"
>
‹
</button>
<button
@click="current = (current + 1) % images.length"
class="absolute right-2 top-1/2 bg-white/80 px-3 py-1 rounded"
>
›
</button>
</div>
-
The state holds both which slide is currently shown (
current) and the list of available slides (images). -
The shorthand
:src(forx-bind:src) makes theimgtag’ssrcattribute automatically update whenevercurrentchanges. -
The
% images.lengthmath makes the slideshow wrap around from the last image back to the first one.
Example: Responsive navigation bar
A common site-wide pattern: a horizontal navigation bar that becomes a “hamburger” menu on small screens. This is the example that combines almost everything we have seen so far - utility classes, responsive prefixes, and Alpine state.
<nav x-data="{ menuOpen: false }" class="bg-gray-800 text-white">
<div class="flex items-center justify-between p-4">
<a href="/" class="font-bold text-xl">My Site</a>
<!-- the hamburger button - only visible BELOW the md breakpoint -->
<button @click="menuOpen = !menuOpen" class="md:hidden">☰</button>
<!-- the link group - hidden by default on small screens,
always shown as a flex row from md upward -->
<div
:class="menuOpen ? 'block' : 'hidden'"
class="md:!block absolute md:static top-16 left-0 right-0
bg-gray-800 md:bg-transparent p-4 md:p-0
md:flex md:gap-6"
>
<a href="#" class="block md:inline">Home</a>
<a href="#" class="block md:inline">About</a>
<a href="#" class="block md:inline">Contact</a>
</div>
</div>
</nav>
-
The key trick is the
md:hiddenandmd:flexpair of responsive prefixes - they toggle visibility based on screen width, independently of Alpine’s state. -
Alpine’s
menuOpenstate only controls the dropdown on small screens. On medium and larger screens,md:!blockoverrides Alpine’s choice and the links are always visible. (The!makes the utility important so it wins over thehiddenclass.) -
This pattern - “Alpine state for small screens, Tailwind responsive utilities for larger ones” - is how nearly every Tailwind-based site builds its main navigation.
Where to go from here
The examples above can be adapted into a wide range of interactive components, but Alpine.js offers many more features (lists, forms, animations) - explore the Alpine documentation for the full reference.
-
As a general plan of action, copy each example exactly from the documentation, make sure it works in your own page, and only then customize it.
-
If you would rather use a library of pre-built Tailwind components than build your own with Alpine, several free options exist:
- Flowbite - a Bootstrap-style component library built on Tailwind, also CDN-loadable.
- Preline UI - a similar free component library with a large catalog.
- Tailwind UI - the official, commercial collection from the Tailwind team.
Conclusions
You have now seen the joy of the Tailwind CSS front-end framework.
-
With a single script tag, you have access to a complete modern styling system.
-
By combining small utility classes, you can build any design you can imagine - responsive, interactive, and clean - without writing custom CSS.
-
Thank you. Bye.