CSSTailwind CSS Introduction

Tailwind CSS Introduction

Tailwind is a utility-first CSS framework: instead of writing a custom class per component (.card, .card-title) and defining its properties in a stylesheet, you compose small, single-purpose utility classes directly in markup (flex, p-4, text-lg). Every utility maps to a small, fixed set of CSS declarations, so the framework is really a very large, consistent vocabulary layered on top of ordinary CSS you already know.

Utility-first philosophy

The pitch is that naming things is hard and stylesheets rot — a .card class quietly grows overrides over years until nobody trusts changing it. Utility classes sidestep both problems: there is nothing to name, and a class like p-4 means exactly one thing everywhere in the codebase, forever.

HTML
<!-- Traditional CSS -->
<div class="card">
  <h2 class="card-title">Plan: Pro</h2>
  <p class="card-price">$19/mo</p>
</div>

<!-- Tailwind utility-first -->
<div class="rounded-lg border border-slate-200 p-6 shadow-sm">
  <h2 class="text-xl font-semibold">Plan: Pro</h2>
  <p class="mt-2 text-2xl font-bold text-blue-600">$19/mo</p>
</div>
Setup with Vite or Next.js

HTML
npm install tailwindcss @tailwindcss/vite   # Vite (Tailwind v4)
# or, for Next.js:
npm install tailwindcss @tailwindcss/postcss postcss

SCSS
/* globals.css — Tailwind v4: one import, no config file required to start */
@import "tailwindcss";
Note
Tailwind v4 dropped the old tailwind.config.js + content glob setup in favor of automatic content detection and CSS-native configuration — see the CSS-first config section below.
Core utilities mapped to CSS you already know

Utility

Equivalent CSS

flex

display: flex

grid grid-cols-3

display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));

p-4 / px-4 / pt-4

padding: 1rem / horizontal / top only

gap-4

gap: 1rem

text-lg font-semibold

font-size: 1.125rem; font-weight: 600;

rounded-lg

border-radius: 0.5rem

shadow-sm

A predefined small box-shadow

w-full max-w-md

width: 100%; max-width: 28rem;

Numeric scale utilities (p-4, gap-6, w-64) map onto a consistent spacing scale rather than arbitrary pixel values — this is the same idea as the design-token spacing scales built by hand with Sass maps (see Sass Control Directives), just pre-built and shipped as classes.

Responsive prefixes

Breakpoint prefixes turn any utility into a media-query-scoped one — mobile-first, applying at that breakpoint and above.

HTML
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4">
  <!-- 1 column on mobile, 2 from the sm breakpoint up, 4 from lg up -->
</div>

Prefix

Min-width (default scale)

sm:

40rem (640px)

md:

48rem (768px)

lg:

64rem (1024px)

xl:

80rem (1280px)

2xl:

96rem (1536px)

State variants

HTML
<button class="bg-blue-600 hover:bg-blue-700 focus-visible:ring-2 disabled:opacity-50">
  Save
</button>

hover:, focus:, focus-visible:, disabled:, dark:, and dozens more variant prefixes compose with any utility — hover:bg-blue-700 compiles to a :hover rule, dark:text-white to a rule scoped under a dark-mode selector or media query.

@apply and when not to reach for it

@apply lets you fold a group of utilities back into a single custom class, for the cases where a component is reused often enough that repeating the same utility string everywhere becomes noise.

SCSS
.btn-primary {
  @apply rounded-lg bg-blue-600 px-4 py-2 font-semibold text-white hover:bg-blue-700;
}
Note
Reach for @apply sparingly. Used heavily, it just recreates the "one class with many properties, hard to trace" model Tailwind exists to avoid — a JS/JSX component wrapping the raw utility classes is usually the better way to deduplicate repeated markup in component-based frameworks like React.
Config and theme tokens

Tailwind v4 moved configuration into CSS itself via the @theme directive, replacing the old JavaScript tailwind.config.js for most projects.

SCSS
/* globals.css */
@import "tailwindcss";

@theme {
  --color-brand: oklch(0.6 0.2 260);
  --spacing-18: 4.5rem;
  --font-display: "Cal Sans", sans-serif;
}

Every custom property defined inside @theme automatically becomes both a CSS variable (usable anywhere as var(--color-brand)) and a new utility (bg-brand, p-18, font-display) — one declaration, two ways to use it.

Honest pros and cons

Pros

Cons

No naming fatigue — nothing new to invent per component

Markup gets visually noisy, especially for complex responsive/state combinations

Styles are colocated with markup — easy to see exactly what a specific element looks like

Learning the utility vocabulary and scale is its own upfront cost

A constrained design-token scale discourages one-off magic numbers

Genuinely custom, one-off visual designs can still require dropping to plain CSS anyway

Small final CSS bundle — only used utility combinations are generated

Diffs in code review show utility soup rather than a semantic class name, which some teams find harder to review

Tip
Tailwind and semantic CSS are not mutually exclusive — many teams use Tailwind for layout/spacing/typography utilities while still writing a handful of named component classes (or React components) for truly reusable, complex UI, applying @apply or plain CSS only where it earns its keep.