HTML & CSS Guide

A complete frontend foundation โ€” semantic HTML, forms, the box model, selectors and specificity, flexbox, grid, responsive design, transitions and dark-mode theming. Every code block has a Copy button.

Copy button on each code block  ยท  โ— Beginner โ— Intermediate โ— Advanced

01 Introduction โ€” The Frontend Foundation

HTML (HyperText Markup Language) is the structure of a webpage โ€” headings, paragraphs, images, links, forms. CSS (Cascading Style Sheets) is the presentation โ€” color, layout, typography, animation, responsiveness. Together they are the two skills every frontend developer uses daily, and they are the foundation of every framework (React, Vue, Svelte all render HTML and style with CSS).

How the browser renders your page

Analogy: HTML is the skeleton and organs; CSS is the skin, clothing and posture. A page can technically work with no CSS at all, but it will look like a document from 1995.

Why this still matters in 2026

๐Ÿ’ก Tip: Learn layout (flexbox, grid) and the box model deeper than colors and shadows. Layout is what beginners struggle with; colors anyone can copy from a template.

02 Setup & Dev Tools Beginner

Your first file โ€” no build step needed

<!-- index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>My First Page</title>
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <h1>Hello, World!</h1>
  <p>This is my first HTML and CSS page.</p>
</body>
</html>
/* style.css */
body{
  font-family: system-ui, sans-serif;
  background: #0f172a;
  color: #f1f5f9;
  padding: 40px;
}
h1{ color: #f97316; }
โš  Always link CSS properly: <link rel="stylesheet" href="style.css"> lives in <head>. A <style> block inside <body> works but blocks rendering and is considered bad practice.

Live Server extension

Browser DevTools โ€” your superpower

Inspect Element
Right-click any element โ†’ Inspect. See the applied CSS
Device Toolbar
Ctrl+Shift+M โ€” simulate any phone/tablet size
Edit & Live
Change CSS in DevTools to test ideas instantly
Flexbox/Grid Overlay
Layout panel โ†’ visualize gaps and tracks
Console
Check for HTML validation and CSS errors
Rendering tab
Simulate prefers-color-scheme and contrast issues
๐Ÿ’ก Tip: In the Elements panel, the Styles tab shows crossed-out rules. A struck-through declaration means a more specific selector overrode it โ€” this instantly explains specificity bugs.

03 Document Structure & Core Tags Beginner

The anatomy of an HTML document

<!DOCTYPE html>          <!-- tells the browser to use modern standards mode -->
<html lang="en">      <!-- root element; lang is required for a11y -->
  <head>               <!-- metadata: not visible on the page -->
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Page Title</title>
    <link rel="stylesheet" href="style.css">
    <script src="app.js" defer></script>
  </head>
  <body>               <!-- everything the user actually sees -->
    <h1>Heading</h1>
    <p>Paragraph</p>
  </body>
</html>

Elements, tags, attributes, values

<a href="https://developer.mozilla.org" class="link" target="_blank" rel="noopener">MDN</a>

Void (self-closing) elements

These have no closing tag and no content:

Comments and entities

<!-- This is an HTML comment -->

<p>5 < 10 && 10 > 5</p>      <!-- escape < and > -->
<p>Tom &amp; Jerry          <!-- escape the ampersand itself -->
<p>&copy; 2026 &mdash; Laksh Guides</p>

The heading hierarchy rule

Headings are the outline of your page, not a way to pick font sizes. There must be exactly one <h1>, and levels must not skip.

Good practice: Use CSS (not <h3>) to change the size of a heading. If it looks small, style it small โ€” don't change its semantic level. Screen-reader users navigate pages by heading outline.
<strong>
Important text โ€” strong emphasis
<em>
Italic โ€” semantic emphasis
<mark>
Highlighted text
<small>
Fine print, side comments
<sub>/<sup>
Hโ‚‚O, xยฒ
<code>
Inline code snippet
<abbr>
CSS (title="Cascading Style Sheets")
<q>
Short inline quotation

04 Text, Links, Images & Tables Beginner

Lists

<ul>              <!-- unordered (bulleted) -->
  <li>Flexbox</li>
  <li>Grid</li>
</ul>

<ol type="A">        <!-- ordered (numbered); type="A" / "i" / "1" -->
  <li>Read HTML</li>
  <li>Read CSS</li>
</ol>

<dl>              <!-- description list -->
  <dt>API</dt>      <!-- term -->
  <dd>Application Programming Interface</dd>
</dl>

Links โ€” the essentials

<a href="https://developer.mozilla.org">MDN Web Docs</a>
<a href="page.html">Internal page</a>
<a href="#section">Jump to section</a>
<a href="mailto:hello@example.com">Email me</a>
<a href="tel:+919876543210">Call support</a>
<a href="report.pdf" download>Download the PDF</a>
<a href="https://example.com" target="_blank" rel="noopener noreferrer">Open in new tab</a>
โš  Security rule: target="_blank" gives the new page access to your window via window.opener. Always pair it with rel="noopener". Never use target="_blank" for internal navigation โ€” it breaks the back button.

Images

<img
  src="images/laksh.jpg"
  alt="Laksh working at a desk with two monitors"
  width="600"        <!-- reserve space, prevents layout shift -->
  height="400"
  loading="lazy"     <!-- defer offscreen images -->
  decoding="async"
>
Alt text rules: describe the content and purpose in one sentence for meaningful images (alt="Red bicycle leaning on a brick wall"). For purely decorative images use alt="" โ€” a screen reader then skips it. Never write alt="image" or alt="photo of".

Tables โ€” for tabular data only

<table>
  <caption>Browser market share, 2026</caption>
  <thead>
    <tr><th scope="col">Browser</th><th scope="col">Share</th></tr>
  </thead>
  <tbody>
    <tr><th scope="row">Chrome</th><td>66%</td></tr>
    <tr><th scope="row">Safari</th><td>18%</td></tr>
  </tbody>
  <tfoot>
    <tr><td>Total</td><td>100%</td></tr>
  </tfoot>
</table>
โš  Common mistake: never use a table for page layout. Screen readers announce every row and cell, so a layout table produces nonsense like "table, 3 columns, 12 rows". Layout is done with Grid and Flexbox.

05 Forms & Inputs Beginner

Forms are how users send data. A good form works even when JavaScript is disabled.

<form action="/signup" method="post">
  <label for="name">Full name</label>
  <input type="text" id="name" name="name" autocomplete="name" required>

  <label for="email">Email address</label>
  <input type="email" id="email" name="email" autocomplete="email" required>

  <label for="pw">Password</label>
  <input type="password" id="pw" name="pw" minlength="8" required>

  <label for="age">Age</label>
  <input type="number" id="age" name="age" min="18" max="100">

  <label for="birth">Date of birth</label>
  <input type="date" id="birth" name="birth">

  <label for="color">Favourite colour</label>
  <input type="color" id="color" name="color" value="#e34f26">

  <label>
    <input type="checkbox" name="terms" value="yes" required>
    I accept the terms and conditions
  </label>

  <label for="plan">Plan</label>
  <select id="plan" name="plan">
    <optgroup label="Personal">
      <option value="free">Free</option>
      <option value="pro" selected>Pro</option>
    </optgroup>
    <option value="team">Team</option>
  </select>

  <textarea id="bio" name="bio" rows="4" maxlength="500" placeholder="Tell us about yourself"></textarea>

  <button type="submit">Create account</button>
  <button type="reset">Reset</button>
</form>

Input types you'll actually use

text
Single-line free text
email
Validates an email format, mobile keyboard
password
Masked input
number
min, max, step spinners
tel
Phone number keypad
url
Absolute link
search
Shows a clear (ร—) button
date / time
Native date and time pickers
checkbox
One or many independent options
radio
Exactly one option from a group
range
Slider with min/max/step
file
File picker with accept="image/*"

The label is not optional

โš  Pitfalls: placeholder is not a label โ€” it disappears on typing and often fails contrast tests. Use autocomplete attributes so browsers and password managers can fill fields. Never log the name of a password field to the server.
๐Ÿ’ก Tip: Always include a submit button. Pressing Enter inside a text input submits the form only if there is exactly one submit button โ€” this is expected behaviour, not a bug.

06 Semantic Layout Intermediate

Semantic elements describe what a piece of content is, not how it looks. They give free SEO, accessibility and correct behaviour, and they make your code readable months later.

<body>
  <header>              <!-- site banner: logo, nav -->
    <nav aria-label="Main navigation">
      <ul>
        <li><a href="/" aria-current="page">Home</a></li>
        <li><a href="/guides">Guides</a></li>
        <li><a href="/about">About</a></li>
      </ul>
    </nav>
  </header>

  <main>                <!-- the unique content of this page -->
    <article>
      <header>
        <h1>Understanding Flexbox</h1>
        <p><time datetime="2026-08-14">14 Aug 2026</time></p>
      </header>
      <section>
        <h2>Main axis and cross axis</h2>
        <p>...</p>
      </section>
      <aside>Related links</aside>
      <footer>Posted by Laksh</footer>
    </article>
  </main>

  <aside>              <!-- complementary: sidebar, ads, TOC -->
  <footer>              <!-- site-wide footer -->
</body>

Choosing the right element

<div>
Generic container, no meaning
<span>
Inline generic, no meaning
<main>
Primary content โ€” only ONE per page
<section>
Thematic group, usually with a heading
<article>
Self-contained, reusable content
<nav>
Navigation links block
<aside>
Tangent content, sidebar
<header>/<footer>
Intro/outro of a section or page
<details>
Expandable FAQ โ€” no JS needed
<dialog>
Modal popup with built-in focus trap
<picture>
Art direction + responsive formats
โœ Try it

Take any page you admire, view its source (Ctrl+U), and identify which blocks are <header>, <main> and <footer>. Then guess how they built the layout โ€” Grid or Flexbox.

07 Accessibility & SEO Basics Intermediate

Accessibility (a11y) means your page works for keyboard users, screen-reader users, and users with low vision, colour blindness or motor impairments. It is a legal requirement for public sites (WCAG 2.2) and it is good engineering.

Keyboard first

ARIA โ€” use sparingly

<!-- Good: a real button needs nothing extra -->
<button>Open menu</button>

<!-- Necessary when you must use a div (e.g. a custom widget) -->
<div role="button" tabindex="0" aria-pressed="false"
     aria-label="Toggle navigation menu">โ˜ฐ</div>

<!-- Live region for dynamic messages -->
<p role="status" aria-live="polite">Form submitted successfully</p>

<!-- Hide decorative icons from screen readers -->
<span aria-hidden="true">โ˜…</span>
<span class="visually-hidden">5 out of 5 stars</span>
โš  Golden rule: "No ARIA is better than bad ARIA." ARIA only changes how assistive tech reads your HTML โ€” it does not add functionality. If you add role="button" you must also implement keyboard handlers yourself.

SEO essentials

<meta name="description" content="Learn HTML and CSS from scratch: semantic markup, flexbox, grid, responsive design, and theming with CSS variables.">
<meta property="og:type" content="website">
<meta property="og:title" content="HTML & CSS Guide">
<meta property="og:description" content="From document structure to CSS Grid โ€” the complete frontend foundation.">
<meta property="og:image" content="https://example.com/og.png">
Test your work: In Chrome DevTools โ†’ Lighthouse tab โ†’ run Accessibility and SEO audits. Also test with the keyboard only: unplug your mouse and tab through the whole page.

08 CSS Selectors Beginner

/* Universal + element */
*                  /* every element */
p                  /* every <p> */

/* Class and ID */
.card              /* any element with class="card" */
#header            /* the element with id="header" (unique) */

/* Compound (no space = same element) */
nav ul li.active    /* li that has class active, inside ul inside nav */
button.primary

/* Descendant / child / next sibling */
.card p            /* p anywhere inside .card */
.card > p         /* p that is a DIRECT child of .card */
.card + p           /* p immediately after .card */
.card ~ p           /* every p AFTER .card */

/* Attribute */
a[href^="https"]    /* external links */
input[type="text"]
a[target="_blank"]
[data-id="42"]

/* Pseudo-classes */
a:hover
a:focus-visible
li:nth-child(2)         /* 2nd li */
li:nth-child(odd)       /* 1st, 3rd, 5th */
li:not(.active)         /* except */
input:checked           /* checkbox/radio checked */
input:required
input:invalid
p::placeholder
div:has(h2)             /* div containing an h2 */
::selection            /* highlighted text */

/* Pseudo-elements */
p::before  { content: "โ†’ "; }
p::after   { content: ""; }
li::marker { color: #e34f26; }
::first-letter{ font-size:3rem; float:left; }

Combinators at a glance

Modern CSS worth knowing: :has(), nesting (.card{ &:hover{ ... } }), and @layer for managing cascade order. All are supported in current browsers.
๐Ÿ’ก Tip: Prefer classes over IDs and element selectors for styling. IDs are unique, so they carry very high specificity and are painful to override. Classes are reusable and predictable.

09 The Cascade, Specificity & Inheritance Intermediate

When two rules target the same element, the browser decides which wins using the cascade. The order is:

  1. Origin & importance โ€” user-agent styles < author styles < !important author < user !important
  2. @layer โ€” unlayered styles beat layered ones
  3. Specificity โ€” a numeric score per selector
  4. Source order โ€” if specificity ties, the last rule in the file wins

Specificity scoring (0, a, b, c)

Compare these โ€” the higher score wins even if it appears earlier:
p
(0,0,1)
.intro
(0,1,0)
nav ul li a
(0,0,4)
.card .title
(0,2,0)
a:hover
(0,2,0)
#header
(1,0,0) โ€” always wins
#header .nav-item
(1,1,0)
style="color:red"
(1,0,0,0) โ€” inline wins

Inheritance

Some properties inherit down the DOM tree by default โ€” color, font-family, line-height, text-align, visibility. Most others do not โ€” margin, padding, border, width.

/* Stop inheritance */
.card *{ all: unset; }        /* nuclear option โ€” use with care */
button{ font: inherit; }      /* forms don't inherit fonts by default */
โš  The classic specificity war: fighting !important or adding longer selectors to win usually means your architecture is wrong. Fix the root cause โ€” restructure the markup or introduce CSS custom properties instead of writing #main .card h3.title.
/* Modern solution: @layer makes order explicit โ€” no more specificity wars */
@layer reset, base, components, utilities;

@layer base{
  body{ font-family: system-ui, sans-serif; }
}
@layer components{
  .card{ padding: 20px; }   /* overrides base regardless of file order */
}
@layer utilities{
  .card{ padding: 40px; }   /* utilities always win */
}

10 The Box Model Beginner

Every element on a page is a rectangular box with four layers: content โ†’ padding โ†’ border โ†’ margin. Margin is outside the border; padding is inside it.

      โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ margin (transparent, outside) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
      โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ border โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”   โ”‚
      โ”‚  โ”‚  โ”Œโ”€โ”€โ”€โ”€ padding โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”‚   โ”‚
      โ”‚  โ”‚  โ”‚        CONTENT  (width ร— height)         โ”‚  โ”‚   โ”‚
      โ”‚  โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ”‚   โ”‚
      โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜   โ”‚
      โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
.box{
  width: 200px;
  height: 100px;
  padding: 20px;
  border: 5px solid #e34f26;
  margin: 30px;
}

/* Default: content-box โ†’ total width = 200 + 40 + 10 + 60 = 310px */

/* The fix almost everyone needs: */
*, *::before, *::after{ box-sizing: border-box; }
โš  The #1 beginner bug: with content-box (the default), width:200px; padding:20px renders 240px wide. Set box-sizing:border-box globally and width means the total size โ€” padding and border are subtracted automatically.

Margin collapse

Vertical margins of adjacent siblings collapse into one โ€” the bigger value wins, they do not add. A child's top margin can also collapse through its parent if the parent has no padding or border.

.a{ margin-bottom: 30px; }
.b{ margin-top: 20px; }   /* gap between them = 30px, NOT 50px */

Width, min and max

Horizontal centring (the classic): .container{ max-width: 1000px; margin-inline: auto; }. This only works for block elements โ€” for flex items use margin-inline: auto with a fixed width, or justify-content: center on the parent.

11 Colors, Fonts & Units Beginner

Color formats

#e34f26
Hex โ€” opaque. 6 digits, #fff shortcut works
#e34f2680
Hex with alpha (80 = 50%)
rgb(227 79 38)
Red green blue
rgba(227,79,38,.5)
RGB with alpha 0โ€“1
hsl(15 78% 52%)
Hue saturation lightness โ€” best for scaling themes
oklch(62% .19 40)
Perceptually uniform โ€” modern pick
transparent
Fully transparent, keeps transitions smooth
currentColor
Inherits the element's own color value
๐Ÿ’ก Tip: Build palettes in hsl() โ€” change one number (lightness) and you get perfect tints and shades for free: hsl(15 78% 52%), hsl(15 78% 42%), hsl(15 78% 92%).

Units

Typography that reads well

body{
  font-family: 'Inter', system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
  font-size: 1rem;         /* 16px default */
  line-height: 1.6;        /* 1.5โ€“1.8 for body text */
  color: #475569;
  max-width: 70ch;         /* readable line length */
  margin-inline: auto;
  padding-inline: 1.5rem;
}

h1{ font-size: clamp(1.8rem, 1.4rem + 2vw, 2.8rem); line-height: 1.15; letter-spacing: -0.02em; }
h2{ font-size: clamp(1.4rem, 1.2rem + 1vw, 1.8rem); }

.btn{
  font: inherit;           /* buttons don't inherit fonts by default */
  font-weight: 600;
  letter-spacing: 0.01em;
}
Line length matters: the ideal measure is 45โ€“75 characters per line. Set max-width: 70ch on prose instead of letting text run the full width of a 1920px monitor. Use text-wrap: balance on headings so lines break evenly.
โœ Try it

Build one paragraph of body text, then experiment: try line-height: 1.0 vs 1.8, and width 40ch vs 120ch. Read both. The difference will click instantly.

12 Flexbox Intermediate

Flexbox lays out items in one direction โ€” a row or a column. This is the single most-used layout tool on the web: nav bars, button groups, card rows, centering anything.

.container{
  display: flex;              /* row by default */
  flex-direction: row | row-reverse | column | column-reverse;
  flex-wrap: wrap | nowrap | wrap-reverse;
  justify-content: flex-start | center | flex-end | space-between | space-around | space-evenly;
  align-items: stretch | flex-start | center | flex-end | baseline;
  align-content: flex-start | center | space-between;  /* multi-line only */
  gap: 1rem;
}

Main axis vs cross axis

The one thing to remember: justify-content = main axis, align-items = cross axis. Flip flex-direction: column and the two swap roles โ€” this confuses everyone once.

Properties on the items

.item{
  flex-grow: 1;         /* share leftover space */
  flex-shrink: 1;       /* default: allowed to shrink */
  flex-basis: 200px;    /* starting size before free space */
  flex: 1 1 auto;       /* grow shrink basis shorthand */
  flex: 0 0 200px;      /* fixed width, never grow/shrink */
  align-self: flex-end; /* override align-items for one item */
  order: 2;             /* visual order only โ€” DOM order is unchanged */
  min-width: 0;         /* CRITICAL: allows shrinking below content size */
}

Pattern 1 โ€” perfect centring

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

Pattern 2 โ€” navbar with pushed-right links

.navbar{ display: flex; align-items: center; gap: 1.5rem; }
.navbar .brand{ margin-right: auto; }   /* pushes the rest to the right */
.navbar a{ text-decoration: none; color: #f1f5f9; }
.navbar a:hover{ color: #e34f26; }
.navbar a[aria-current="page"]{ color: #e34f26; font-weight: 600; }

Pattern 3 โ€” wrapping card row

.cards{
  display: flex;
  flex-wrap: wrap;
  gap: 1.25rem;
  justify-content: center;
}
.card{
  flex: 1 1 260px;      /* grow, shrink, basis 260px */
  max-width: 380px;
  padding: 1.5rem;
  border: 1px solid #334155;
  border-radius: 16px;
}
โš  Overflow trap: flex items default to min-width:auto, which means they refuse to shrink below their content's width. A long unbroken string overflows its container. Fix: min-width: 0 on the item (and overflow-wrap: anywhere on the text).
โœ Try it

Take your navbar and set flex-direction: column. Predict what breaks before you change it โ€” then verify. Doing this for all four direction values teaches the main/cross axis faster than any tutorial.

13 CSS Grid Intermediate

Grid lays items out in two dimensions at once โ€” rows and columns. Use it for page-level layout and card galleries; use Flexbox for one-dimensional component internals.

.grid{
  display: grid;
  grid-template-columns: repeat(3, 1fr);   /* 3 equal columns */
  grid-template-rows: auto 1fr auto;
  gap: 1.5rem;
  align-items: start;
  justify-items: stretch;
}

Track sizing

1fr
One share of leftover space
repeat(auto-fill, minmax(240px, 1fr))
Auto-responsive column count โ€” no media query needed
minmax(200px, 1fr)
Minimum size, then share the rest
min-content
As narrow as content allows
max-content
As wide as the content wants
fit-content(30ch)
Content width, capped at 30ch
auto
Uses available space, respects content
subgrid
Align child grid to parent tracks

Line-based placement

.layout{
  display: grid;
  grid-template-columns: 250px 1fr;
  grid-template-rows: auto 1fr auto;
  grid-template-areas:
    "header header"
    "sidebar main"
    "footer footer";
}
header { grid-area: header; }
aside  { grid-area: sidebar; }
main   { grid-area: main; }
footer { grid-area: footer; }
Why grid-template-areas is the best layout tool: the entire page structure becomes visible in the CSS as an ASCII diagram. Reflowing to mobile is a one-line change instead of a pile of overrides.

Spanning and alignment

.hero{
  grid-column: 1 / -1;      /* span all columns */
  grid-row: span 2;
}
.card{ align-self: start; justify-self: center; }
.grid{ place-items: center; }          /* align + justify shorthand */
.grid{ place-content: center; }        /* for the whole grid */

/* Dense packing โ€” fills holes left by spans */
.gallery{ grid-auto-flow: row dense; }

Pattern โ€” responsive gallery with zero media queries

.gallery{
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(240px, 100%), 1fr));
  gap: 1rem;
}
๐Ÿ’ก Tip: The min() wrapper is what stops a 240px minimum from overflowing a 320px phone. Add min(240px, 100%) to every auto-fill grid and it becomes bulletproof.
โœ Try it

Build a classic dashboard: header, sidebar, main content and footer using grid-template-areas. Then resize the window and confirm nothing breaks. This exact layout is what you will build in every framework tutorial.

14 Positioning Intermediate

static
Default. top/right/bottom/left are ignored
relative
Offset from its normal spot; still takes space in flow
absolute
Removed from flow, positioned vs nearest positioned ancestor
fixed
Locked to the viewport; scrolls with the page
sticky
Scrolls until a threshold, then sticks

Relative + absolute

.card{ position: relative; }        /* becomes the containing block */
.badge{
  position: absolute;
  top: -8px; right: -8px;
  background: #e34f26;
  color: white;
  border-radius: 999px;
  padding: 2px 8px;
}
โš  Critical: an absolute element is positioned relative to its nearest ancestor that has position set to anything other than static (or transform / filter / contain). If it lands in the wrong place, look for an unexpected position:relative up the tree.

Centring with absolute positioning

.overlay{
  position: absolute;
  inset: 0;                  /* top/right/bottom/left all 0 = fill parent */
  display: grid;
  place-items: center;       /* cleaner than top:50% + translate */
}

Sticky navbar

header{
  position: sticky;
  top: 0;
  z-index: 100;
  backdrop-filter: blur(12px);
  background: rgba(15, 23, 42, 0.85);
}

Fixed toast notification

.toast{
  position: fixed;
  bottom: 24px; right: 24px;
  z-index: 1000;
  padding: 14px 20px;
  border-radius: 12px;
  background: #10b981;
  color: #04140d;
  box-shadow: 0 10px 30px rgba(0,0,0,0.4);
}
Sticky gotcha: position: sticky fails if any ancestor has overflow: hidden, auto or scroll. Also, the element needs room to move within its parent โ€” it cannot leave its parent's box.
๐Ÿ’ก Tip: z-index only competes with other elements that share the same stacking context. One transform, filter or opacity < 1 creates a new stacking context and can trap children. When z-index "doesn't work", this is why.

15 Responsive Design Intermediate

Mobile-first is the default

Write the small-screen layout first, then progressively enhance. Your base CSS targets phones; media queries add complexity only where needed.

/* Base โ€” mobile (no query needed) */
.cards{ display: grid; gap: 1rem; }

/* Enhance at 640px */
@media (min-width: 40rem){
  .cards{ grid-template-columns: repeat(2, 1fr); }
}

/* Enhance at 1024px */
@media (min-width: 64rem){
  .cards{ grid-template-columns: repeat(4, 1fr); }
}

Media query features

min-width / max-width
Viewport width
min-height / max-height
Viewport height
orientation
portrait | landscape
prefers-color-scheme
dark | light
prefers-reduced-motion
Respect accessibility settings
prefers-contrast
more | less
hover / any-hover
Only style hover on real pointers
pointer: coarse
Touch devices โ€” enlarge tap targets

Fluid typography and spacing

h1{
  /* min | preferred | max โ€” scales smoothly with viewport */
  font-size: clamp(1.8rem, 1.2rem + 3vw, 3.5rem);
}

.container{
  width: min(100% - 2rem, 1200px);   /* fluid with a max */
  margin-inline: auto;
}

.grid{ gap: clamp(0.75rem, 2vw, 1.5rem); }
Avoid vw for font sizes on mobile โ€” Chrome zooms text when it exceeds 20px. Use clamp() with a rem minimum instead. Also remember 100vw includes the scrollbar width and causes horizontal overflow; 100% does not.

Respecting user preferences

@media (prefers-reduced-motion: reduce){
  *, *::before, *::after{
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}
โš  Touch targets: WCAG requires interactive elements to be at least 44 ร— 44px. Nav links are commonly 16px tall on desktop and unusable on a phone. Add padding and min-height: 44px in touch contexts.
๐Ÿ’ก Tip: Test in DevTools โ†’ Device Toolbar at 320px, 375px, 768px, 1024px and 1440px. If it survives all five with no horizontal scrollbar, your layout is solid.

16 Transitions, Transforms & Animation Advanced

Transitions โ€” animate a change

.btn{
  background: #e34f26;
  transition: background 0.25s ease, transform 0.25s ease, box-shadow 0.25s ease;
}
.btn:hover{
  background: #1572b6;
  transform: translateY(-2px);
  box-shadow: 0 8px 20px rgba(0,0,0,0.3);
}
.btn:active{ transform: translateY(0); }
Performance rule: animate only transform, opacity and filter. Animating width, height, top or margin forces the browser to recompute layout on every frame โ€” this causes jank.

Keyframe animations

@keyframes fade-up{
  0%   { opacity: 0; transform: translateY(30px); }
  100% { opacity: 1; transform: translateY(0); }
}

.card{
  opacity: 0;
  animation: fade-up 0.7s cubic-bezier(0.16, 1, 0.3, 1) both;
}
.card:nth-child(2){ animation-delay: 0.1s; }
.card:nth-child(3){ animation-delay: 0.2s; }

/* Infinite loading spinner */
@keyframes spin{ to{ transform: rotate(360deg); } }
.spinner{
  width: 36px; height: 36px;
  border: 3px solid rgba(255,255,255,0.2);
  border-top-color: #e34f26;
  border-radius: 50%;
  animation: spin 0.8s linear infinite;
}

Scroll-triggered reveal with no JavaScript

@supports (animation-timeline: view()){
  .card{
    animation: fade-up linear both;
    animation-timeline: view();
    animation-range: entry 0% cover 25%;
  }
}

Transforms cheat sheet

Timing functions

ease
Default โ€” gentle in and out
linear
Constant speed โ€” spinners only
ease-in
Slow start
ease-out
Slow end โ€” feels snappy
cubic-bezier(.16,1,.3,1)
Smooth deceleration โ€” feels premium
steps(4)
Choppy, frame-by-frame
โœ Try it

Build a hover card that lifts up, scales slightly and reveals a hidden description โ€” using only transform and opacity. Then check the Performance panel while hovering to confirm nothing forces reflow.

17 CSS Variables & Theming Advanced

Custom properties (--name) store values on an element and cascade exactly like normal properties. They are the foundation of every design system and the cleanest way to build dark mode.

:root{
  /* Brand palette โ€” the only values you ever change */
  --brand: #e34f26;
  --brand-dark: #c2410c;
  --accent: #1572b6;

  /* Semantic tokens */
  --bg: #07080e;
  --surface: rgba(18, 22, 40, 0.75);
  --text: #f1f5f9;
  --text-dim: #8892b0;
  --border: rgba(227, 79, 38, 0.15);

  /* Spacing scale */
  --space-1: 0.25rem;
  --space-2: 0.5rem;
  --space-3: 1rem;
  --space-4: 1.5rem;

  --radius: 14px;
  --shadow: 0 8px 32px rgba(0,0,0,0.5);
}

@media (prefers-color-scheme: light){
  :root{
    --bg: #f8fafc;
    --surface: rgba(255,255,255,0.85);
    --text: #0f172a;
    --text-dim: #475569;
  }
}

body{
  background: var(--bg);
  color: var(--text);
}
.card{
  padding: var(--space-4);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
  box-shadow: var(--shadow);
}

JavaScript-free dark mode toggle

<!-- inline in <head> โ€” prevents white flash on load -->
<script>
  const theme = localStorage.getItem('theme')
    || (matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light');
  document.documentElement.dataset.theme = theme;
</script>

:root[data-theme="light"]{
  --bg: #f8fafc;
  --surface: #ffffff;
  --text: #0f172a;
  --text-dim: #475569;
}
:root[data-theme="dark"]{
  --bg: #07080e;
  --surface: #121626;
  --text: #f1f5f9;
  --text-dim: #8892b0;
}

Runtime variables from JS

// JS reads the computed value
const brand = getComputedStyle(document.documentElement)
  .getPropertyValue('--brand').trim();   /* "#e34f26" */

// JS sets a variable (e.g. reading the user's accent preference)
document.documentElement.style.setProperty('--brand', '#1572b6');
๐Ÿ’ก Tip: Use var(--brand, #e34f26) to provide a fallback if the variable is ever missing. Note that custom properties are not typed โ€” --gap: 10 is invalid as a length, you must write --gap: 10px.
Pro tip: register properties with @property to give them a type and make them animatable โ€” this is how you animate custom properties without JavaScript.

18 Practice Projects Intermediate

Reading is not learning. Build these in order โ€” each one introduces exactly one new concept.

โ–ถ 1. Personal Profile Card Flexbox

Center a card vertically and horizontally. Add an avatar, name, role and three social links that highlight on hover. Make the avatar circle on hover with transform: scale().

.profile{
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.75rem;
  padding: 2rem;
  border-radius: 20px;
  background: var(--surface);
}
.avatar{
  width: 120px; height: 120px;
  border-radius: 50%;
  object-fit: cover;
  transition: transform 0.3s ease;
}
.profile:hover .avatar{ transform: scale(1.05) rotate(3deg); }
.links{ display: flex; gap: 1rem; }
โ–ถ 2. Responsive Navbar with Hamburger Media queries

Horizontal nav on desktop; on mobile it collapses into a โ˜ฐ button that toggles a vertical menu. Use aria-expanded and aria-label so it stays accessible.

โ–ถ 3. CSS Grid Photo Gallery Grid

Use auto-fill + minmax for automatic columns. Give the first image grid-column: span 2 and grid-row: span 2. Add a lightbox on click using position: fixed.

โ–ถ 4. Dashboard Layout grid-template-areas

Header, 240px sidebar, main area, footer. Below 900px, collapse to a single column with the sidebar hidden behind a toggle. This is the layout behind every admin panel you have ever used.

โ–ถ 5. Landing Page Full project

Hero with gradient text and a CSS-only animated background, a 3-card feature grid, a testimonials grid using auto-fit, an FAQ built with <details>, and a CTA section. Must be responsive, keyboard-navigable and score 95+ in Lighthouse.

โ–ถ 6. Themed Portfolio with Dark Mode CSS variables

Define a full token set in :root, then a toggle button that swaps data-theme on <html>. Persist the choice in localStorage and add a smooth transition: background-color 0.3s to body.

How to learn properly: build it without a tutorial first, then compare with a reference solution. The gap between your attempt and theirs is your learning. Rebuild from memory a week later.

19 Cheat Sheet & Best Practices Beginner

Reset you'll actually use

*, *::before, *::after{ box-sizing: border-box; margin: 0; padding: 0; }
body{ line-height: 1.6; -webkit-font-smoothing: antialiased; }
img, picture, video{ max-width: 100%; height: auto; display: block; }
input, button, textarea, select{ font: inherit; }
ul[class]{ list-style: none; }
:focus-visible{ outline: 2px solid #e34f26; outline-offset: 2px; }
@media (prefers-reduced-motion: reduce){
  *, *::before, *::after{ animation-duration: .01ms !important; transition-duration: .01ms !important; }
}

Layout decision tree

Ten rules to remember

  1. Semantic HTML first; CSS only for presentation
  2. One <h1> per page, never skip heading levels
  3. Always set box-sizing: border-box
  4. Use gap, not margin hacks โ€” no collapse surprises
  5. min-width: 0 on flex items that contain text
  6. Classes for styling, IDs for anchors and JavaScript hooks
  7. Units: rem for type and spacing, %/fr for sizes
  8. Mobile-first with min-width queries
  9. Animate only transform and opacity
  10. Test with the keyboard and in DevTools โ€” every time

Resources worth bookmarking

๐ŸŽฏ Next step: Learn enough JavaScript to change a class on click, and you can build interactive components without any framework. That is the whole frontend job in 90% of cases.