CSS • 2026-10-10

CSS Tutorial: From Selectors and Flexbox to Responsive Design

Understand CSS selectors, the cascade, the box model, units, typography, Flexbox, Grid, positioning, responsive layouts, animations, and practical styling patterns.

Understand CSS selectors, the cascade, the box model, units, typography, Flexbox, Grid, positioning, responsive layouts, animations, and practical styling patterns.

Developer workspace with code on a screen
Build your programming knowledge one practical example at a time.

What Is CSS?

CSS (Cascading Style Sheets) controls how HTML content looks: colors, fonts, spacing, borders, and layout. HTML says what the content is; CSS says how it should be presented.

Three ways to add CSS

  • External stylesheet: a .css file linked from the page. Best for maintainability and caching.
  • Internal stylesheet: a <style> block in the head. Fine for small demos.
  • Inline styles: a style attribute on one element. Hard to maintain and hard to override.

Anatomy of a rule

  • The selector chooses which elements to style.
  • A declaration block in braces holds one or more declarations.
  • Each declaration is a property, a colon, a value, and a semicolon.

Linking a stylesheet and writing a rule

/* In HTML: <link rel="stylesheet" href="styles.css"> */

/* selector  { property: value; } */
h1 {
  color: #1d3557;
  font-size: 2rem;
  margin-bottom: 0.5rem;
}

Selectors

Selectors target elements. Learning a handful of them covers most real-world styling.

  • Type: p, h2, a select every element of that type.
  • Class: .card selects elements with class="card" and is reusable.
  • ID: #header selects one unique element; prefer classes for styling.
  • Attribute: [type="email"] selects by attribute value.
  • Pseudo-class: :hover, :focus-visible, :first-child, :not() select by state or position.
  • Pseudo-element: ::before, ::after, ::first-line style generated or partial content.
  • Combinators: descendant (nav a), child (ul > li), adjacent sibling (h2 + p), general sibling (h2 ~ p).

Common selector patterns

a                    { color: #2457d6; }
.card                { padding: 1rem; }
#site-header         { position: sticky; top: 0; }
input[type="email"]  { border: 1px solid #94a3b8; }

nav a:hover,
nav a:focus-visible  { text-decoration: underline; }

ul > li:first-child  { font-weight: 700; }
li:not(.done)        { opacity: 1; }
h2 + p               { margin-top: 0; }

.btn::after          { content: " →"; }

The Cascade, Specificity, and Inheritance

When several rules target the same element, the cascade decides which one wins. Understanding it prevents most "why is my style not applying?" problems.

How the browser decides

  1. Importance and origin: !important and user or browser styles come first.
  2. Specificity: more specific selectors beat less specific ones (inline > ID > class/attribute/pseudo-class > element).
  3. Source order: if specificity ties, the later rule wins.

Inheritance

  • Text properties such as color, font-family, and line-height inherit by default.
  • Box properties such as margin, padding, and border do not.
  • Use inherit, initial, or unset to control inheritance explicitly.

Specificity in action

p            { color: black; }      /* specificity 0,0,1 */
.note        { color: green; }      /* 0,1,0 beats the rule above */
p.note       { color: blue; }       /* 0,1,1 beats .note */
#intro       { color: red; }        /* 1,0,0 beats classes */

/* Prefer low-specificity, class-based selectors and avoid !important. */

Units, Colors, and Custom Properties

Choosing units

  • px for precise details such as borders and shadows.
  • rem is relative to the root font size and is excellent for spacing and type.
  • em is relative to the current element's font size.
  • %, vw, and vh are relative to the parent or viewport.
  • clamp(min, preferred, max) creates fluid values that never get too small or too large.

Custom properties (CSS variables)

Custom properties store reusable values and make theming simple, including dark mode.

Design tokens with CSS variables and dark mode

:root {
  --color-bg: #f8fafc;
  --color-text: #0f172a;
  --color-brand: #2457d6;
  --space-2: 0.5rem;
  --space-4: 1rem;
  --radius: 0.75rem;
}

@media (prefers-color-scheme: dark) {
  :root {
    --color-bg: #0b1220;
    --color-text: #e2e8f0;
  }
}

body {
  background: var(--color-bg);
  color: var(--color-text);
  font-size: clamp(1rem, 0.95rem + 0.3vw, 1.125rem);
}

.button {
  background: var(--color-brand);
  padding: var(--space-2) var(--space-4);
  border-radius: var(--radius);
}

The Box Model

Every element is a rectangular box made of content, padding, border, and margin.

  • Content: the text or media itself.
  • Padding: space between content and border.
  • Border: the edge around the padding.
  • Margin: space outside the border, separating elements.

Setting box-sizing: border-box makes the declared width include padding and border, which makes layouts far more predictable.

Border-box reset and box properties

*,
*::before,
*::after {
  box-sizing: border-box;
}

.card {
  width: 100%;
  max-width: 24rem;
  padding: 1.5rem;
  border: 1px solid #d9e0ea;
  margin-inline: auto;        /* centers a block horizontally */
  margin-block: 2rem;
}

Typography and Text Styling

  • Use a system font stack for speed or load web fonts with font-display: swap.
  • Set line-height to around 1.5 for body text.
  • Keep line length between roughly 45 and 75 characters using max-width: 65ch.
  • Use text-wrap: balance for headings to avoid awkward line breaks.
  • Check color contrast; body text should meet at least a 4.5:1 ratio.

Readable typography defaults

body {
  font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  line-height: 1.6;
}

h1, h2, h3 {
  line-height: 1.2;
  text-wrap: balance;
}

article p {
  max-width: 65ch;
}

a {
  text-underline-offset: 0.2em;
}

Flexbox: One-Dimensional Layout

Flexbox lays out items along one main axis. It is ideal for navigation bars, toolbars, and centering.

Key properties

  • display: flex turns an element into a flex container.
  • flex-direction sets the main axis: row or column.
  • justify-content aligns items along the main axis.
  • align-items aligns items along the cross axis.
  • gap adds space between items.
  • flex: 1 lets an item grow to fill free space; flex-wrap: wrap allows wrapping.

A responsive navigation bar with Flexbox

.navbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: 0.75rem 1.5rem;
}

.navbar ul {
  display: flex;
  gap: 1rem;
  list-style: none;
  margin: 0;
  padding: 0;
}

.navbar .spacer { flex: 1; }

/* Perfect centering */
.center {
  display: flex;
  justify-content: center;
  align-items: center;
  min-height: 100vh;
}

CSS Grid: Two-Dimensional Layout

Grid controls rows and columns together. It is the best tool for page layouts and card galleries.

  • grid-template-columns defines the columns; fr units share free space.
  • repeat(auto-fit, minmax(16rem, 1fr)) creates a responsive grid without media queries.
  • grid-template-areas lets you name regions of a page layout.
  • gap sets the spacing between rows and columns.
  • Flexbox and Grid work together; they are not competing systems.

Responsive card grid and a named-area page layout

.card-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 16rem), 1fr));
  gap: 1rem;
}

.page {
  display: grid;
  grid-template-columns: 16rem 1fr;
  grid-template-areas:
    "header  header"
    "sidebar main"
    "footer  footer";
  min-height: 100vh;
}
.page > header  { grid-area: header; }
.page > aside   { grid-area: sidebar; }
.page > main    { grid-area: main; }
.page > footer  { grid-area: footer; }

@media (max-width: 40rem) {
  .page {
    grid-template-columns: 1fr;
    grid-template-areas: "header" "main" "sidebar" "footer";
  }
}

Positioning and Stacking

  • static is the default normal flow.
  • relative offsets an element from its normal spot and becomes an anchor for absolute children.
  • absolute removes the element from flow and positions it against the nearest positioned ancestor.
  • fixed positions against the viewport.
  • sticky behaves like relative until a scroll threshold, then sticks.
  • z-index controls stacking, but only works on positioned elements or flex/grid items.

Sticky header and a badge positioned on a card

.site-header {
  position: sticky;
  top: 0;
  z-index: 10;
  background: white;
}

.card { position: relative; }

.card .badge {
  position: absolute;
  top: 0.75rem;
  right: 0.75rem;
  padding: 0.15rem 0.5rem;
  border-radius: 999px;
  background: #e11d48;
  color: white;
  font-size: 0.75rem;
}

Responsive Design

Responsive design adapts a page to different screens. Start with a layout that works on narrow screens, then add breakpoints when the content needs them.

The responsive toolkit

  • Viewport meta tag in the HTML head.
  • Fluid widths using %, fr, min(), max(), and clamp().
  • Flexible media with max-width: 100%; height: auto.
  • Media queries to change layout at breakpoints.
  • Container queries to style a component based on its own container's width.
  • Mobile-first CSS using min-width queries.

Mobile-first media queries and a container query

img, video { max-width: 100%; height: auto; }

.layout { display: grid; gap: 1rem; }

@media (min-width: 48rem) {
  .layout { grid-template-columns: 2fr 1fr; }
}

@media (min-width: 64rem) {
  .layout { grid-template-columns: 3fr 1fr; }
}

/* Container query: respond to the card's own width */
.card-wrap { container-type: inline-size; }

@container (min-width: 28rem) {
  .card { display: grid; grid-template-columns: 8rem 1fr; gap: 1rem; }
}

Transitions, Animations, and Motion Preferences

  • transition animates a property change smoothly, such as on hover.
  • @keyframes with animation creates multi-step animations.
  • Animate transform and opacity for the best performance.
  • Respect users who prefer less motion with prefers-reduced-motion.

Hover transition and a reduced-motion-safe animation

.card {
  transition: transform 160ms ease, box-shadow 160ms ease;
}
.card:hover {
  transform: translateY(-2px);
  box-shadow: 0 0.5rem 1.5rem rgb(0 0 0 / 10%);
}

@keyframes fade-in {
  from { opacity: 0; transform: translateY(8px); }
  to   { opacity: 1; transform: none; }
}
.hero { animation: fade-in 400ms ease both; }

@media (prefers-reduced-motion: reduce) {
  * { animation: none !important; transition: none !important; }
}

CSS Best Practices

  • Use a consistent naming convention such as BEM (.card__title, .card--featured).
  • Keep selectors short and low in specificity; avoid !important.
  • Define colors, spacing, and radii as custom properties.
  • Prefer rem, gap, and logical properties (margin-inline) for flexible layouts.
  • Always keep a visible :focus-visible style.
  • Organize files by purpose: reset, tokens, layout, components, utilities.
  • Test at several widths and with browser zoom at 200%.

Complete Example: Responsive Card Layout

Link this stylesheet to an HTML page containing a .card-grid with several .card children.

Responsive card layout

*,
*::before,
*::after {
  box-sizing: border-box;
}

body {
  margin: 0;
  font-family: system-ui, sans-serif;
  line-height: 1.6;
  color: #202938;
  background: #f5f7fb;
}

.card-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 16rem), 1fr));
  gap: 1rem;
  padding: 1.5rem;
}

.card {
  padding: 1.25rem;
  border: 1px solid #d9e0ea;
  border-radius: 0.75rem;
  background: white;
  box-shadow: 0 0.25rem 1rem rgb(0 0 0 / 5%);
}

.card img {
  display: block;
  width: 100%;
  height: auto;
  border-radius: 0.5rem;
}

.button {
  display: inline-block;
  padding: 0.65rem 1rem;
  border-radius: 0.4rem;
  background: #2457d6;
  color: white;
  text-decoration: none;
}
.button:focus-visible {
  outline: 3px solid #93b4ff;
  outline-offset: 2px;
}

@media (prefers-reduced-motion: no-preference) {
  .card { transition: transform 160ms ease; }
  .card:hover { transform: translateY(-2px); }
}

Frequently Asked Questions

What is the difference between Flexbox and Grid?

Flexbox is mainly one-dimensional and arranges items along a row or column. Grid handles rows and columns together. Choose based on the layout, and combine them when useful.

Should I use pixels or rem?

Pixels are useful for precise details like borders; rem units scale with the root font size and are often better for typography and spacing because they respect user preferences.

What is a media query?

A media query applies CSS conditionally, for example when the viewport is wider than a chosen breakpoint or when the user prefers dark mode or reduced motion.

Why is my CSS not applying?

Usually a more specific selector or a later rule is winning, the selector does not match the element, or the stylesheet is not linked correctly. Inspect the element in browser DevTools to see which rules apply.

What does box-sizing: border-box do?

It makes an element's declared width and height include padding and border, so adding padding does not make the box larger than you set.

When should I use CSS variables?

Use them for values you repeat or want to change in one place, such as colors, spacing, and radii. They also make theming and dark mode easy.

← Back to All Articles