CSS Syntax and Rule Anatomy

CSS is a declarative language: you describe what the result should look like, and the browser works out how to get there. A stylesheet is a sequence of rules, and every rule has the same two-part shape. Master this shape and the rest of the language is vocabulary.

What CSS is

A declarative language, not a programming one

In JavaScript you write steps that run in order. In CSS you write constraints: "paragraphs are dark gray", "the navigation sits in a row". There is no control flow to manage — the browser takes every rule that matches an element and resolves conflicts using the cascade (the subject of the next lesson). This is also why CSS is merciless about typos: a declaration is either valid and applied, or invalid and silently dropped.

Why presentation is a separate language

HTML answers "what is this content?", CSS answers "how should it look?". Keeping the two apart is not pedantry — it is what makes one stylesheet restyle a thousand pages, what lets a media query reformat the same markup for a phone, and what allows a user stylesheet or browser setting to override yours for accessibility. When markup carries its presentation inline, none of those are possible.

How the browser processes a stylesheet

The browser parses your CSS into rules, collects every declaration matching each element, sorts them with the cascade, and computes a final value per property. Two consequences matter daily: the browser does not stop at the first error (it recovers and keeps parsing), and a rule cannot reach into another rule — there is no execution, only patterns.

Anatomy of a rule

The selector and the declaration block

Every rule pairs a selector (which elements?) with a declaration block (what should they look like?). Each declaration is a property: value pair terminated by a semicolon.

/* One complete rule: selector + declaration block.
   The selector reads left to right: "any p element". */
p {
  color: #e2e8f0;         /* property: color, value: a hex color */
  line-height: 1.6;       /* unitless numbers are valid line heights */
  margin-block-end: 1rem; /* logical property: spacing after the block */
}

/* The final semicolon is optional...
body { color: #e2e8f0 }
...but always write it. Omitting it makes the next
pasted-in line silently merge into this value. */

Comments — and what they must explain

CSS has exactly one comment syntax: /* ... */, which may span lines but does not nest. A useful comment explains why the rule exists, not what the syntax already says. "Removes the list margin" adds nothing; "the nav list is laid out by flexbox, so the default margin breaks the alignment" teaches the next reader something.

Invalid declarations are dropped, not fatal

If a value makes no sense — color: 12px; — the browser discards that one declaration and continues. This is a feature: it is the mechanism behind progressive enhancement, where modern declarations are layered after fallbacks (used deliberately in the Modern CSS lesson). But it also means a typo never announces itself: the dev-tools Styles pane shows dropped declarations struck through — check it when a style "does nothing".

Where styles live

Three locations, three use cases

LocationHowUse caseRecommendation
External file <link rel="stylesheet" href="styles.css"> Real sites: styles cached once, reused on every page Default choice
Internal <style> A <style> element in the <head> Single-file demos and first-load critical CSS Fine for demos and small pages
Inline style attribute <p style="color: red"> Debugging a computed value; some JS-driven positioning Avoid in authored markup — it wins the cascade and cannot be overridden by class

Both can pull a stylesheet into a page, but they behave differently. <link> is discovered in parallel with the HTML download; @import is discovered only after the CSS containing it has been fetched and parsed, serializing your requests. Prefer link.

<!-- Parallel discovery: preferred -->
<link rel="stylesheet" href="/css/base.css">
<link rel="stylesheet" href="/css/components.css">
/* base.css — the second import waits for this file to download and parse first */
@import url("components.css");   /* works, but serializes the waterfall */

At-rules

Conditional at-rules: @media and @supports

At-rules (statements starting with @) instruct the browser rather than style an element. The two you will meet first are conditional — their block applies only when the condition holds:

/* Applies only when the viewport is at least 768px wide (Responsive Design lesson) */
@media (min-width: 768px) {
  .layout { grid-template-columns: 1fr 1fr; }
}

/* Applies only when the browser implements the feature (progressive enhancement) */
@supports (display: grid) {
  .gallery { display: grid; gap: 1rem; }
}

Defining at-rules: @font-face, @keyframes, @layer

Other at-rules define something for later use: @font-face registers a web font, @keyframes defines an animation (the Animations lesson), and @layer declares a cascade layer (the Architecture lesson). You only need to recognize the shape for now: @name followed by a block or parameters.

Error recovery: what happens when CSS is wrong

A bad declaration kills only itself

.notice {
  color: #f59e0b;
  colr: #ef4444;   /* typo: unknown property, this line alone is dropped */
  padding: 1rem;   /* still applied */
}

A bad selector kills the whole rule

/* One bad selector in the list invalidates the ENTIRE rule -
   the browser cannot know which part you meant to keep. */
.notice, h2..typo {
  color: #ef4444;   /* never applied, not even to .notice */
}

This is the most common surprise for beginners who build a comma-separated selector list with one malformed entry. It is also why the :is() pseudo-class matters: a forgiving selector list inside :is() only drops the invalid entry (covered in Selectors).

Why this matters in practice

Error recovery is why CSS never throws, and why "my style isn't applying" is always one of three things: the selector doesn't match, the declaration was dropped, or another declaration won the cascade. Check them in that order — dev tools shows all three in the Styles pane.

Practice: write your first stylesheet

The task

Create a folder with two files, index.html and styles.css, link them, and style this markup using only what this lesson covered — selectors plus declarations:

<article id="intro" class="card">
  <h2 class="card-title">CSS Practice</h2>
  <p>Write a clean first stylesheet.</p>
  <p class="muted">Small text, comfortable spacing.</p>
</article>

The checklist

  • Target the article by its ID, the two paragraphs by type, and the small text by its class.
  • Give every declaration a trailing semicolon, even the last one.
  • Add one comment per rule that says why the rule exists.
  • Then deliberately break it: misspell a property and a selector, reload, and confirm the dev-tools Styles pane shows exactly what this lesson predicted.