CSS Basics — Styling the Web
Status: Active Last Updated: 2026-08-29 Category: Web — CSS Prerequisites: HTML basics Tags: css, selectors, specificity, box-model, flexbox, grid, responsive
Summary
CSS (Cascading Style Sheets) controls how HTML looks: colors, layout, fonts, spacing, animations. This article covers selectors, the box model, flexbox, grid, and responsive design.
What You'll Learn
- CSS selectors and specificity
- The box model
- Flexbox and CSS Grid layouts
- Responsive design with media queries
- CSS custom properties (variables)
Table of Contents
- Architectural Overview & Core Schema
- Deep Dive & Implementation
- Anti-Patterns & Common Pitfalls
- Independent Challenge
- Consolidation & Key Invariants
- Next Steps
1. Architectural Overview & Core Schema
Foundational theory, system mechanics, and high-level design.
Selectors and Specificity
CSS selectors target elements to style:
/* element selector */
h1 { color: red; }
/* class selector */
.button { background: blue; }
/* id selector */
#header { font-size: 24px; }
/* attribute selector */
input[type="text"] { border: 1px solid gray; }
/* pseudo-class */
a:hover { color: green; }
/* descendant */
nav a { color: white; }
Specificity determines which rule wins when multiple match:
- Inline styles: 1000
- ID selectors: 100
- Class/attribute/pseudo-class: 10
- Element/pseudo-element: 1
The higher specificity wins. !important overrides everything (use sparingly).
The Box Model
Every element is a rectangular box with four layers (from inside out):
+---------------------+
| margin | (space outside the border)
+---------------------+
| border | (line around the element)
+---------------------+
| padding | (space inside the border)
+---------------------+
| content | (the actual content)
+---------------------+
.box {
width: 200px; /* content width */
padding: 20px; /* inside the border */
border: 1px solid; /* line around */
margin: 10px; /* outside the border */
box-sizing: border-box; /* width includes padding+border */
}
box-sizing: border-box is almost always what you want — width: 200px becomes the visible width including padding and border.
2. Deep Dive & Implementation
Technical implementation, fully commented code blocks, and step-by-step logic.
Guided Checkpoint
Inspect specificity and computed styles in the browser:
// In DevTools console — list all computed styles for an element
const el = document.querySelector('.card');
const styles = getComputedStyle(el);
console.log('display:', styles.display);
console.log('width:', styles.width);
console.log('padding:', styles.padding);
console.log('margin:', styles.margin);
// Inspect the cascade in the Elements > Styles pane
// Click "Computed" tab to see resolved values
// Click any property to jump to its source rule
// In the console, show the element's inline styles
console.log('Inline styles:', el.style.cssText);
Deep Dive — Flexbox
Flexbox arranges items in one direction (row or column). Apply display: flex to the container. justify-content aligns along the main axis; align-items aligns along the cross axis. flex-grow, flex-shrink, flex-basis control item sizing. gap sets spacing between items without margins.
/* Card row with equal-width items */
.cards {
display: flex;
gap: 1rem;
justify-content: space-between;
align-items: stretch;
}
.card {
flex: 1 1 0; /* grow, shrink, basis — equal width */
min-width: 200px;
}
/* Centering content */
.centered {
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
}
/* Responsive wrapping */
.row {
display: flex;
flex-wrap: wrap;
gap: 1rem;
}
.row > * { flex: 1 1 300px; }
Deep Dive — CSS Grid
CSS Grid creates two-dimensional layouts (rows and columns). grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)) creates responsive columns without media queries. grid-area lets you name regions and place items by name.
/* Responsive auto-grid */
.grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
gap: 1.5rem;
}
/* Explicit grid */
.layout {
display: grid;
grid-template-areas:
"header header"
"sidebar main"
"footer footer";
grid-template-columns: 250px 1fr;
grid-template-rows: auto 1fr auto;
min-height: 100vh;
}
.layout header { grid-area: header; }
.layout aside { grid-area: sidebar; }
.layout main { grid-area: main; }
.layout footer { grid-area: footer; }
Deep Dive — Responsive Design
Responsive design adapts layout to viewport size. Start with mobile-first CSS (default styles, enhance with media queries). max-width on containers prevents over-stretching on large screens. clamp(min, preferred, max) smoothly scales values.
/* Mobile-first default */
.page { padding: 1rem; }
/* Tablet and up */
@media (min-width: 640px) {
.page { padding: 2rem; }
.sidebar { display: flex; }
}
/* Desktop */
@media (min-width: 1024px) {
.page { max-width: 1200px; margin: 0 auto; }
}
/* Fluid typography */
h1 { font-size: clamp(1.5rem, 4vw, 3rem); }
/* Fluid spacing */
.card { padding: clamp(0.75rem, 2vw, 2rem); }
Use relative units: rem for font sizes/accessibility, % or vw/vh for layout, em for component-local spacing. Avoid fixed pixel widths for containers.
Deep Dive — CSS Custom Properties
Custom properties (CSS variables) store values for reuse and theming. Defined on :root for global scope, overridable per component. Reference with var(--name, fallback).
:root {
--color-bg: #ffffff;
--color-text: #1a1a1a;
--color-accent: #3b82f6;
--space-sm: 0.5rem;
--space-md: 1rem;
--space-lg: 2rem;
--radius: 0.375rem;
--font-body: system-ui, sans-serif;
}
body {
background: var(--color-bg);
color: var(--color-text);
font-family: var(--font-body);
}
.btn {
background: var(--color-accent);
padding: var(--space-sm) var(--space-md);
border-radius: var(--radius);
color: white;
}
/* Dark theme override */
[data-theme="dark"] {
--color-bg: #0f172a;
--color-text: #e2e8f0;
}
Custom properties enable design tokens, runtime theming, and DRY stylesheets. Change --color-accent in one place and every .btn updates.
3. Anti-Patterns & Common Pitfalls
Documented failure modes and how to detect/prevent them.
Forgetting box-sizing: border-box
By default, CSS uses content-box: width: 200px sets the content width to 200px, and padding and border are added on top. A 200px wide box with 20px padding and a 1px border renders at 242px wide. This surprises developers and causes layout breakage when you add padding to fixed-width containers.
Fix: Set box-sizing: border-box globally:
*, *::before, *::after { box-sizing: border-box; }
With border-box, width is the total visible width including padding and border.
Specificity wars leading to !important abuse
When two rules conflict and neither can be overridden cleanly, developers reach for !important. Once one !important exists, another must be added to override it, and the cascade becomes unmanageable. Specificity should always win without !important.
Fix: Keep specificity low: prefer class selectors over ID selectors. Use a consistent naming convention (BEM, utility classes). Reserve !important only for utility overrides (e.g., .sr-only { position: absolute !important; }) and never for component styles.
Layout thrashing from reading and writing layout properties in a loop
Reading offsetWidth, getBoundingClientRect(), or getComputedStyle().width forces synchronous layout. Writing to a DOM property afterward forces another layout. A loop alternating reads and writes causes a reflow on every iteration.
Fix: Read all values first, store them, then write:
// BAD — reflow on each iteration
elements.forEach(el => {
const h = el.offsetHeight;
el.style.height = h * 2 + 'px'; // write triggers reflow
});
// GOOD — 1 reflow total
const heights = elements.map(el => el.offsetHeight);
requestAnimationFrame(() => elements.forEach((el, i) => el.style.height = heights[i] * 2 + 'px'));
Fixed pixel widths breaking responsive design
width: 1200px on a container looks correct on a desktop but causes horizontal scrolling on mobile. width: 100% with fixed padding also overflows the viewport. max-width is always safer for containers.
Fix:
.container { max-width: 1200px; width: 100%; margin: 0 auto; padding: 0 1rem; }
Always test at 375px (mobile) and 768px (tablet) viewport widths.
Using position: absolute for everything
Overusing position: absolute removes elements from the normal flow, making them hard to reason about. Adjacent content does not reflow around them, and overlapping z-index stacking becomes unpredictable.
Fix: Use flexbox or grid for layout first. Reserve position: absolute for overlays (modals, tooltips), badge overlays on cards, and elements that need to escape the flow intentionally.
Not scoping CSS or using overly generic class names
.button, .card, and .title are generic enough to collide across a large codebase. A class name collision means one CSS rule affects elements it shouldn't, causing invisible bugs.
Fix: Namespace with a component prefix (.card-header, .card-body, .card-footer via BEM, or .Card-header with CSS Modules). In projects using CSS Modules or CSS-in-JS, scoping is automatic.
4. Independent Challenge
A problem to solve without step-by-step guidance; promotes synthesis.
Build a responsive card grid layout from scratch without looking at the deep-dive code. Requirements:
- Use CSS Grid (
grid-template-columns: repeat(auto-fit, minmax(..., 1fr))) to create a grid that reflows from 1 column (mobile) to 2 columns (tablet) to 3+ columns (desktop) without media queries - Each card has: an image (use a placeholder via
https://picsum.photos/seed/{n}/400/200), a heading, a body paragraph, and a "Read more" button - All cards must be equal height in a row
- The card buttons must align to the bottom of the card regardless of how much text is in the body
- On mobile, cards stack vertically with no horizontal gap
- Add a dark-theme override (
[data-theme="dark"]) that inverts card background and text colors
Constraints: No flexbox (use CSS Grid only for the layout). No Tailwind. Pure CSS.
5. Consolidation & Key Invariants
Bullet-list summary of the must-remember rules.
- Always set
box-sizing: border-boxglobally —widthincludes padding and border; prevents layout surprises when padding is added. - Use
max-width(notwidth) on containers —max-width: 1200px; width: 100%handles both fluid and bounded layouts. - Flexbox and Grid are layout tools, not hacks — use them intentionally; avoid
position: absolutefor primary layout. - Batch DOM reads before DOM writes — interleaving layout reads and writes causes reflow on every iteration.
- Avoid
!important— it breaks the cascade and creates specificity wars; use lower-specificity selectors instead. - Use
remfor font sizes and spacing — respects user font-size preferences and improves accessibility.
6. Next Steps
Sequenced links to dependent lessons, deeper dives, or production guides.
- Practice flexbox with Flexbox Froggy or CSS-Tricks Flexbox Guide
- Build a responsive grid layout with CSS Grid Garden
- Read
kb/web/web-basics.mdfor the rendering pipeline - Review
kb/containers/docker-concepts.mdto understand deployment context
Change Log
- 2026-08-29: Migrated to 6-section canonical template (added sections 3, 4, 5; updated ToC; added Change Log entry)
- 2026-08-29: Expanded from stub to full entry-level lesson (all sections, code examples, Next Steps, Change Log)