You wrote a few rules. You saved the file. You refreshed the browser — and somehow a plain HTML document turned into a designed page. That moment is the whole point of CSS, but the mechanism behind it is often a mystery for beginners.
CSS is not a script that runs line by line. It is a stylesheet: a set of rules that the browser reads, matches against the document, and turns into pixels. This guide walks through exactly how that happens — from the anatomy of a rule to the cascade that resolves conflicts, to the box model that gives every element its size, to the rendering pipeline that produces what you finally see.
The Short Answer
Every time the browser renders a page, it does the same thing with CSS:
Each step depends on the one before it. If the HTML never parses, CSS has nothing to match. If the cascade cannot decide a winner, the browser falls back to a default. If layout fails, nothing gets painted.
Understanding this pipeline is what separates beginners who fight CSS from developers who work with it. When a rule “does not work,” the answer is almost always hiding somewhere in this flow.
Think of CSS as a voting system. Every declaration is a vote for how an element should look. The cascade counts the votes, applies tie-breakers, and delivers a single winning value per property. No ambiguity survives the process.
Anatomy of a CSS Rule
Before anything else, you need to be able to read a CSS rule. Every rule has the same four parts, in the same order:
The four parts
- Selector
-
Describes which elements the rule targets. In the example,
.cardtargets every element with the classcard. - Declaration block
- Everything between the curly braces. It contains one or more declarations.
- Property
-
The aspect of the element being changed, such as
color,font-size, ormargin. Properties are predefined by the CSS specification. - Value
-
What the property is being set to, such as
#0891b2,1.5rem, orauto.
Each declaration ends with a semicolon, and the whole block is wrapped in curly braces. A rule can contain as many declarations as you need:
.card {
background: #ffffff;
border-radius: 12px;
padding: 24px;
box-shadow: 0 4px 16px rgba(15, 23, 42, 0.08);
}
Read it left to right: for every element with the class card, set the
background to white, round the corners to 12 pixels, add 24 pixels of internal padding,
and add a soft shadow.
A missing semicolon is one of the most common beginner mistakes. The browser will silently discard the entire declaration — and often the next one too — because it cannot tell where one property ends and the next begins.
How CSS Reaches the Page
For CSS to do anything, it has to reach the HTML document. There are three ways to deliver it, and each has different implications.
1. External stylesheet (recommended)
A separate .css file linked from the HTML head:
<head>
<link rel="stylesheet" href="styles.css">
</head>
This is the standard approach. The CSS file is downloaded once, cached by the browser, and reused across every page on the site. It keeps presentation separate from structure and is by far the easiest to maintain.
2. Internal style block
Rules written directly inside a <style> element in the HTML head:
<head>
<style>
.card { border-radius: 12px; }
</style>
</head>
This keeps everything in one file, which is convenient for small demos or pages with unique styling. It does not scale well because the styles cannot be shared or cached across pages.
3. Inline styles (avoid)
Rules applied directly to an element with the style attribute:
<div class="card" style="border-radius: 12px;">
Content
</div>
Inline styles have the highest specificity short of !important, which makes
them extremely hard to override. They cannot be reused, cannot be cached, and mix
presentation back into the markup. Use them only for values that must be computed at
runtime.
When multiple stylesheets or style blocks apply, the browser honors them in document order. An external stylesheet linked after an internal style block overrides it at equal specificity. Getting the order wrong is a common source of “why is this not applying?”
How Selectors Match
Selectors are the pattern-matching engine of CSS. The browser reads every selector and tests it against every element in the document. When a selector matches, the declarations inside its block become candidates for that element.
The main selector types
| Selector | Example | Matches |
|---|---|---|
| Type | p |
Every <p> element |
| Class | .card |
Every element with class="card" |
| ID | #hero |
The single element with id="hero" |
| Attribute | [type="email"] |
Inputs with a matching type attribute |
| Descendant | nav a |
Links anywhere inside a <nav> |
| Child | ul > li |
List items that are direct children of a list |
| Pseudo-class | a:hover |
Links while the pointer is over them |
| Pseudo-element | p::first-line |
The first line of every paragraph |
Selectors combine
Selectors are rarely used alone. They can be chained, combined with commas, or nested with combinators:
/* Comma: two separate rules */
h1, h2 { color: #0f172a; }
/* Chained: element that also has the class */
button.primary { background: #0891b2; }
/* Descendant combinator */
.card a { text-decoration: none; }
/* Child combinator */
.card > a { font-weight: 600; }
/* Adjacent sibling */
h2 + p { margin-top: 0; }
Each combinator expresses a different structural relationship. The descendant combinator
( ) matches at any depth. The child combinator (>) matches
only direct children. Understanding the difference helps you write selectors that are
both precise and resilient.
Browsers evaluate selectors from right to left. For .card a, the browser
first finds every <a>, then checks whether any ancestor has the
class card. This is why very deep descendant chains are slower — the
browser has to walk up the tree for each candidate.
The Cascade: Who Wins
The “C” in CSS stands for cascading. When more than one rule sets the same property on the same element, the cascade decides which declaration wins. It considers four factors, in this order:
- Origin and importance — user agent, user, and author styles, plus any
!importantflags. - Specificity — a score based on the selectors involved.
- Source order — the position of the rule in the stylesheet.
- Inline styles — style attributes beat external rules at equal importance.
Origin and importance
There are three sources of CSS, and the cascade ranks them:
- User agent styles — the browser’s built-in defaults.
- User styles — custom styles set by the reader, often for accessibility.
- Author styles — everything you write.
Author styles override user agent styles by default, which is why your rules beat the
browser’s defaults. The exception is !important, which flips the order —
important user agent styles beat important author styles, and important user styles beat
everything. This is intentional: it lets users override website styling for accessibility
reasons.
!important should be a last resort. Once it is in place, it can only be
overridden by another !important with equal or higher specificity, which
leads to escalating complexity. If you find yourself reaching for it often, the real
problem is probably specificity in your selectors.
Specificity: How Strong Is a Selector
Specificity is a score assigned to a selector. When two rules of the same origin and importance set the same property, the one with the higher specificity wins.
The score is usually described as three numbers, written like (a, b, c):
- a — the number of ID selectors.
- b — the number of class, attribute, and pseudo-class selectors.
- c — the number of type and pseudo-element selectors.
Compare the scores from left to right. A single ID beats any number of classes. A single class beats any number of type selectors. Only when all three are equal does source order decide.
A worked example
Suppose two rules both set the color of a paragraph:
p { color: #0f172a; }
.card p { color: #0891b2; }
The first selector has specificity (0, 0, 1). The second has
(0, 1, 1). Because the second is more specific, the paragraph inside a
.card becomes teal — regardless of which rule appears first in the file.
Aim for single-class selectors wherever possible. They have a uniform, predictable specificity, which means conflicts are resolved by source order rather than by an escalating arms race. Frameworks like BEM and utility systems exist largely to keep specificity flat.
The !important exception
A declaration marked with !important outranks all normal declarations of
the same origin, regardless of specificity. It sits at the top of the cascade for that
origin. Use it only to override third-party CSS you cannot edit, or as a deliberate
escape hatch — never as a first tool.
Inheritance: What Passes Down
Not every property has to be set explicitly on every element. Some properties inherit — they take their value from the parent element unless overridden.
The rule is intuitive: properties that describe text inherit, and properties that describe layout do not.
Text and typography
color, font-family, font-size, font-weight, line-height, letter-spacing, text-align.
These flow from parent to child automatically.
Layout and box
margin, padding, border, width, height, display, position.
These apply only to the element they are set on.
This is why setting font-family on body styles the whole page:
every text node inside inherits it. And it is why setting margin on
body affects only body itself, not the elements inside it.
Controlling inheritance
You can explicitly control inheritance with three keywords:
.btn {
border: inherit; /* take the parent's border */
color: initial; /* reset to the CSS default */
margin: unset; /* inherit if inherited, else initial */
}
These keywords are especially useful when you want to reset an element’s styling without losing inherited context.
Setting * { box-sizing: border-box; } is a common reset. Note that
* does not break inheritance — it just applies box-sizing to every
element individually, and because box-sizing is not inherited, this is the correct
approach.
The Box Model: How Size Is Computed
Every element the browser renders is a rectangular box. That box is made of four nested layers, and understanding them is essential to controlling layout.
Sizing: content-box vs border-box
By default, width and height refer only to the content area.
Padding and border are added on top, which makes elements wider than you might expect:
.box {
width: 300px;
padding: 20px;
border: 5px solid;
/* Total rendered width: 300 + 40 + 10 = 350px */
}
Setting box-sizing: border-box changes this. The declared width now
includes padding and border, so the element is exactly as wide as you specified:
* { box-sizing: border-box; }
.box {
width: 300px;
padding: 20px;
border: 5px solid;
/* Total rendered width: 300px */
}
Almost every modern project sets border-box globally, because it makes
layout math dramatically simpler.
Every browser’s developer tools show a colored diagram of an element’s box model. Inspecting this panel is the fastest way to understand why an element is bigger or smaller than you expected.
The Rendering Pipeline
Once the cascade has produced a final set of values for every element, the browser goes to work turning that into pixels. This happens in four stages.
1. Style calculation
The browser takes the DOM tree from HTML and the CSSOM tree from CSS, walks them together, and computes the final style for every element. This is where the cascade, specificity, and inheritance all finish their work. The result is a new tree — sometimes called the render tree — where each node carries its resolved styles.
2. Layout
In the layout phase, the browser calculates the geometry of every box. It determines the width, height, and position of each element based on its styles, its content, and its siblings. This is where the box model, Flexbox, Grid, and normal flow all take effect.
Layout is expensive. Changing properties that trigger layout — width,
height, margin, font-size, display —
forces the browser to redo this work.
3. Paint
Once the geometry is known, the browser paints pixels. It fills in backgrounds, draws borders and text, renders images, and applies shadows and gradients. The result is a series of painted layers, often on the GPU.
Properties that trigger only paint — color, background-color,
box-shadow — are cheaper than layout, but still not free.
4. Composite
Finally, the browser combines all the painted layers onto the screen. Some properties,
such as transform and opacity, can be handled entirely in this
phase. That is why animating with transform and opacity is far
smoother than animating with top and left.
For smooth animations, stick to transform and opacity. These
skip layout and paint and can often run entirely on the GPU. Anything else — animating
width, height, or top — will trigger layout on
every frame and cause jank.
How Layout Actually Works
Layout is where most of the complexity of CSS lives. Elements are positioned by one of
several systems, and the system in use is determined by the display
property and related rules.
Normal flow
By default, elements follow normal flow. Block elements stack vertically, each taking the full width of their parent. Inline elements flow with the text, wrapping as needed. This is the baseline layout system, and it handles simple documents perfectly.
Flexbox
Flexbox lays out items in a single row or column, distributing space and aligning along two axes. It is ideal for navigation bars, toolbars, card rows, and any case where you want items to size themselves within a container.
.nav {
display: flex;
justify-content: space-between;
align-items: center;
gap: 16px;
}
Grid
Grid lays out items in two dimensions simultaneously — rows and columns — with explicit control over placement and sizing. It is ideal for full-page layouts, dashboards, and any design where both dimensions matter at once.
.layout {
display: grid;
grid-template-columns: 240px 1fr;
grid-template-rows: auto 1fr auto;
gap: 24px;
min-height: 100vh;
}
Positioning
For cases that do not fit flow, Flexbox, or Grid, the position property
takes an element out of the normal flow and places it manually. Values include
relative, absolute, fixed, and
sticky.
Modern layout should reach for Flexbox and Grid first. Positioning is powerful but removes elements from the flow, which causes overlap, layout fragility, and accessibility issues. Use it sparingly, for overlays and floating UI only.
How Responsive Design Fits In
The browser does not apply CSS once. It re-evaluates certain rules whenever the environment changes — window size, device orientation, user preferences. The mechanism is the media query.
.card {
padding: 16px;
}
@media (min-width: 768px) {
.card {
padding: 32px;
}
}
The rule inside the media query applies only when the viewport is at least 768 pixels wide. On smaller screens, the base rule applies. This is the same cascade — media queries just add or remove candidates based on the current environment.
Relative units
Responsive design also depends on units that scale with the environment rather than fixed pixel values:
rem— relative to the root font size. Scales with user settings.em— relative to the current element’s font size.%— relative to the parent’s size.vwandvh— relative to the viewport.ch— relative to the width of the “0” character in the current font.
Using rem for typography and spacing means everything scales gracefully with
the user’s preferred text size — a critical accessibility win.
Write your base styles for the smallest screen, then add media queries with
min-width for larger sizes. This keeps the default styling simple and
ensures the mobile experience is always the most optimized.
Custom Properties: Values That Flow
CSS custom properties — often called CSS variables — let you store a value under a name and reuse it anywhere in the stylesheet.
:root {
--color-primary: #0891b2;
--color-accent: #7c3aed;
--space-md: 1rem;
--radius: 12px;
}
.btn {
background: var(--color-primary);
padding: var(--space-md) calc(var(--space-md) * 1.5);
border-radius: var(--radius);
}
Unlike preprocessor variables, CSS custom properties are live. They inherit, they can be changed at runtime, and they can be scoped to any element or selector.
Why they matter
- Theming — swap a whole color scheme by redefining a few variables.
- Consistency — one definition, many uses, no drift.
- Responsive values — change a variable inside a media query and every rule that uses it updates.
- JavaScript integration — read and set variables from scripts without touching individual properties.
.dark-theme {
--color-primary: #22d3ee;
--color-accent: #a855f7;
}
Because custom properties inherit, adding the dark-theme class to
body instantly re-themes every descendant.
Sass variables are resolved at compile time and disappear from the output. CSS custom properties remain in the stylesheet and are evaluated by the browser. That makes them dynamic — the browser can change them at runtime, and CSS can respond to those changes.
Why Your CSS Is Not Applying
This is the question every beginner asks. Almost every instance falls into one of a handful of categories.
1. Specificity conflict
Another rule with a higher specificity is overriding yours. Check the DevTools styles panel — the winning rule is shown, and overridden declarations are crossed out.
2. Typo or invalid value
Browsers silently ignore invalid declarations. A misspelled property name,
colour instead of color, or an invalid value such as
font-size: 14 without a unit, will simply be dropped.
3. Missing semicolon
If a declaration is missing its semicolon, the browser may merge it with the next one, causing both to fail. This is one of the most common typos.
4. Selector does not match
The element may not actually have the class or ID the selector is targeting. Typos in class names are frequent, and CSS fails silently when a selector matches nothing.
5. Element not in the DOM yet
If styles are added dynamically through JavaScript before the element exists, the rules will not apply. Wait for the element to be present before styling it.
Right-click any element and choose “Inspect.” The styles panel shows every rule that matched the element, in order of specificity, with overridden declarations struck through. This is the single most useful debugging tool in CSS.
Common Myths About How CSS Works
- “CSS runs top to bottom.” CSS is not a script. The browser reads it in order, but the cascade decides which rules win based on origin, importance, and specificity — not on which line came first.
- “Later rules always override earlier ones.” Source order only matters when specificity is equal. A later rule with lower specificity loses to an earlier, more specific one.
- “The browser applies CSS once.” The browser re-runs style calculation, layout, and paint whenever the DOM, styles, or environment change. This is what makes responsive and interactive pages possible.
-
“You can style HTML with HTML.” Old presentational tags like
<font>are deprecated and cannot build modern layouts. Presentation belongs in CSS. - “!important is a fix.” It is not. It is a last resort that makes the cascade harder to reason about and usually signals a specificity problem upstream.
- “CSS is deterministic.” It is — but the determinism comes from the cascade rules, not from source order. Two identical rules produce the same result every time; two conflicting ones are resolved by a fixed set of tie-breakers.
When a rule does not seem to apply, do not guess. Inspect the element and read the cascade. Every mystery in CSS has a defined answer — it is just a matter of finding the rule that won.
Best Practices
These habits keep your CSS predictable as the project grows.
Start with a reset
Normalize or reset margin, padding, and box-sizing at the top of the stylesheet.
Keep specificity low
Prefer single classes. Avoid IDs for styling, and avoid deep descendant chains.
Name by purpose
.card and .alert survive redesigns. .red-box does not.
Use border-box everywhere
* { box-sizing: border-box; } removes an entire class of sizing bugs.
Prefer relative units
rem, em, %, and ch scale with user settings.
Animate cheaply
Use transform and opacity. Avoid animating layout properties.
Use custom properties
Define colors, spacing, and radii once. Reference them everywhere.
Design mobile-first
Write base styles for small screens and enhance upward with min-width.
Validate your CSS
The W3C CSS Validator catches errors browsers silently ignore.
CSS works by matching, cascading, laying out, and painting. Master those four steps, and every “why is this not working?” becomes a question you can answer.
Frequently Asked Questions
1. How does CSS work in simple terms?
CSS works by matching selectors to elements in the HTML document. For each match, the browser collects the declarations, resolves conflicts using the cascade and specificity, and then applies the winning values to build the page’s visual tree. The browser then lays out and paints that tree to produce what you see on screen.
2. What is the cascade in CSS?
The cascade is the algorithm that decides which declaration wins when multiple rules set the same property on the same element. It considers origin (user agent, author, user), importance, specificity, and source order. Later and more specific declarations override earlier or less specific ones.
3. What is specificity in CSS?
Specificity is a score assigned to each selector that determines how strongly it competes in the cascade. Inline styles beat IDs, IDs beat classes and attributes, and classes beat element selectors. When specificity is equal, source order decides.
4. How does the browser apply CSS to a page?
The browser parses HTML into a DOM tree and CSS into a CSSOM tree. It combines them into a render tree, computes the final styles for each element, lays out each box with its size and position, paints the pixels, and composites them onto the screen. This happens every time the page changes.
5. What is the box model in CSS?
Every element is rendered as a rectangular box made of four layers: the content area, padding, border, and margin. Together, they determine the element’s total size and how much space it takes up in the layout.
6. What is inheritance in CSS?
Inheritance is how some CSS properties pass down from a parent element to its
children automatically. Text-related properties such as color,
font-family, and line-height inherit. Layout properties
like margin, padding, and border do not.
7. Why do some CSS rules not apply?
The most common reasons are specificity conflicts, an invalid property value, a typo in the selector or property name, a missing semicolon on the previous declaration, or a parent element with a rule that overrides the child. The browser’s DevTools show which rules won and why.
8. Does CSS run in a specific order?
Yes. The browser parses and applies CSS in a defined order: first user agent styles, then user styles, then author styles. Within each origin, importance and specificity determine the winner. Media queries are evaluated based on the current environment.
9. What does “cascading” actually mean?
“Cascading” describes how multiple stylesheets and rules flow together into a single set of winning declarations for each element. It is the mechanism that allows defaults, frameworks, and your own styles to coexist without stepping on each other blindly.
10. How does CSS handle responsive layouts?
Through media queries, relative units, and flexible layout systems like Flexbox and Grid. Media queries apply different rules at different viewport sizes. Relative units adapt to user settings and screen size. Flexbox and Grid reflow content automatically within whatever space is available.
11. What are CSS custom properties?
CSS custom properties, also called CSS variables, are values stored under a name that
begins with two dashes, such as --color-primary. They can be read
anywhere in the stylesheet with the var() function. They inherit, they
update at runtime, and they make themes and design tokens easier to manage.
12. How long does it take to learn how CSS works?
The basics of selectors, the box model, and colors take a few weeks. The cascade, specificity, and layout with Flexbox and Grid take a few months of regular practice to feel natural. Truly mastering CSS is an ongoing process, because the language continues to grow.
Key Takeaways
- A CSS rule is a selector plus a block of property-value declarations.
- CSS reaches the page through external files, internal blocks, or inline styles.
- The cascade decides which declaration wins based on origin, importance, specificity, and source order.
- Specificity ranks IDs above classes, and classes above element selectors.
- Some properties inherit; most layout properties do not.
- Every element is a box made of content, padding, border, and margin.
- The browser computes styles, lays out boxes, paints pixels, and composites layers.
- Flexbox and Grid are the modern layout systems; positioning is for special cases.
- Media queries and relative units make layouts responsive.
- Custom properties store reusable values and make theming easy.
- Most “CSS is not working” problems are specificity conflicts or typos.
- DevTools show exactly which rule won — always inspect before guessing.
What to Learn Next
Now that you understand how CSS works under the hood, the natural next step is to go deeper on the parts that matter most. Start with CSS Selectors Explained and The CSS Box Model.
From there, explore CSS Specificity and the Cascade, Inheritance in CSS, and CSS Units Explained. When you are ready to build layouts, read CSS Flexbox Guide and CSS Grid Guide, then move on to Responsive Web Design.
If you want to understand how CSS fits with the rest of the browser, read How Web Browsers Work and HTML vs CSS.
Practice Challenge
The fastest way to internalize how CSS works is to build a page and then deliberately break it, so you can see the cascade in action. This exercise takes about twenty minutes.
- Create
index.htmlandstyles.cssin the same folder. - Write HTML with a heading, a paragraph, a list, and a button. Give each element a class.
- Link the stylesheet from the HTML head using
<link rel="stylesheet">. - Add a global reset at the top of the stylesheet:
* { box-sizing: border-box; margin: 0; padding: 0; } - Define two custom properties in
:root— one for a primary color and one for spacing. - Style the heading with the primary color, using
var()to reference the custom property. - Give the button a padding that uses the spacing variable multiplied by two.
- Now deliberately introduce a specificity conflict: add a rule for
h1and another for.page-title, both setting different colors. Confirm the class wins. - Open DevTools, inspect the heading, and confirm the losing rule is crossed out.
- Add a media query that changes the heading size at
min-width: 768px, and resize the window to see it switch. - Finally, set
border-boxand then remove it, observing how the button’s total size changes.
When you finish, you will have seen the cascade, specificity, inheritance, the box model, custom properties, and media queries all working together in your own code. Once you have felt those mechanisms in action, CSS stops being a list of properties to memorize and becomes a system you can reason about.