|$ curl https://forge-ai.dev/api/markdown?path=docs/css
$cat docs/css-getting-started.md
updated May 25, 2026·36 min read·published

CSS Getting Started

CSSBeginnerFundamentalsBeginner
Introduction

Cascading Style Sheets (CSS) is the language used to describe the presentation of a document written in HTML or XML. CSS controls the layout, colors, fonts, spacing, and visual appearance of web pages. It is one of the three core technologies of the web, alongside HTML and JavaScript.

CSS separates content from presentation, allowing you to apply styles across multiple pages from a single stylesheet. This separation improves maintainability, accessibility, and enables powerful responsive design patterns.

Modern CSS has evolved far beyond simple color and font properties. It now includes features like custom properties (variables), Grid, Flexbox, container queries, animations, and color functions like oklch() and color-mix(). The CSS Object Model (CSSOM) works alongside the DOM to represent styles in a structured, programmable way.

index.html
HTML
1<!DOCTYPE html>
2<html lang="en">
3<head>
4 <meta charset="UTF-8" />
5 <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6 <title>CSS Example</title>
7 <link rel="stylesheet" href="styles.css" />
8</head>
9<body>
10 <h1>Hello, CSS!</h1>
11 <p>This paragraph is styled with an external stylesheet.</p>
12</body>
13</html>
styles.css
CSS
1/* External stylesheet: styles.css */
2h1 {
3 color: #00FF41;
4 font-family: system-ui, sans-serif;
5 text-align: center;
6}
7
8p {
9 font-size: 1.125rem;
10 line-height: 1.6;
11 color: #A0A0A0;
12 max-width: 65ch;
13 margin: 0 auto;
14}

info

Always link your CSS in the <head> using a <link> tag. Inline styles via the style attribute work but should be avoided for maintainability.
How CSS Works

Understanding how a browser processes CSS is essential for debugging and writing performant stylesheets. The rendering pipeline follows six distinct stages:

1Parse

The browser parses the HTML and constructs the DOM (Document Object Model). When it encounters a <link> or <style> tag, it fetches and parses the CSS to build the CSSOM (CSS Object Model). CSS parsing works right-to-left on selectors for performance.

2Cascade

The browser applies the cascade algorithm to resolve conflicting declarations. It considers specificity, importance, source order, and origin (user agent, user, author) to determine which styles win.

3Compute Values

The browser resolves all CSS values through four stages: declared values (author styles), cascaded values (after cascade wins), specified values (defaults if cascaded value is missing), computed values (resolving relative units like em/rem to px), used values (final values after layout), and actual values (browser-constrained values).

4Layout

The browser calculates the geometry of each element: its position and size within the viewport. This involves running the layout algorithm determined by the element's display property (block, inline, flex, grid, etc.).

5Paint

The browser fills in pixels: colors, backgrounds, borders, shadows, text, and images. Each element is painted onto one or more layers. Properties like transform and opacity are compositor-only and skip paint, making them fast to animate.

6Composite

Individual painted layers are composited together into the final image displayed on screen. This stage is GPU-accelerated. Promoting elements to their own layer (via will-change or transform: translateZ(0)) can improve performance.

best practice

For high-performance animations, prefer transform and opacity. These properties only trigger compositing (step 6), skipping layout and paint entirely. Animating width, height, or top/left triggers layout and paint on every frame.
preview
CSS Syntax

CSS is composed of rule sets (also called rule blocks) and at-rules. A rule set consists of a selector and a declaration block. Each declaration pairs a property with a value.

Rule Set Structure

syntax.css
CSS
1selector {
2 property: value;
3 property: value;
4}
5/* ↑ */
6/* declaration block */

A concrete example:

rule-set-example.css
CSS
1/* Selector: targets all <p> elements */
2p {
3 /* Declaration 1 */
4 color: #E0E0E0;
5
6 /* Declaration 2 */
7 font-size: 1rem;
8
9 /* Declaration 3 */
10 line-height: 1.6;
11}
12
13/* Multiple selectors can be grouped */
14h1, h2, h3 {
15 font-family: "Inter", system-ui, sans-serif;
16 font-weight: 600;
17}

At-Rules

At-rules start with @ and instruct CSS how to behave. Common at-rules include:

At-RulePurpose
@importImport an external stylesheet
@mediaConditional styles based on media features
@font-faceDefine custom fonts
@keyframesDefine animation sequences
@propertyRegister typed custom properties
@containerContainer query conditions
@layerControl cascade layer order
@scopeLimit selector scope
at-rules.css
CSS
1@import url("reset.css");
2
3@font-face {
4 font-family: "Geist";
5 src: url("fonts/Geist.woff2") format("woff2");
6}
7
8@media (max-width: 768px) {
9 body {
10 font-size: 0.875rem;
11 }
12}
13
14@keyframes fadeIn {
15 from { opacity: 0; transform: translateY(8px); }
16 to { opacity: 1; transform: translateY(0); }
17}
18
19@property --brand-color {
20 syntax: "<color>";
21 inherits: false;
22 initial-value: #00FF41;
23}

warning

Using @import blocks parallel CSS downloads and delays rendering. Prefer <link> tags in HTML for stylesheets.
Selectors

Selectors define which elements a set of styles applies to. CSS offers a rich selector syntax that allows precise element targeting.

Basic Selectors

SelectorExampleDescription
Typeh1Matches all <h1> elements
Class.cardMatches elements with class="card"
ID#headerMatches element with id="header" (use sparingly)
Attribute[type="text"]Matches elements with a specific attribute
Universal*Matches all elements
basic-selectors.css
CSS
1/* Type selector */
2article { padding: 2rem; }
3
4/* Class selector */
5.card {
6 background: #0D0D0D;
7 border-radius: 8px;
8}
9
10/* ID selector — avoid when possible, prefer classes */
11#main-nav { position: sticky; top: 0; }
12
13/* Attribute selectors */
14[disabled] { opacity: 0.5; cursor: not-allowed; }
15[data-type="primary"] { background: #00FF41; }
16[href^="https"] { color: #00FF41; }
17[class*="btn-"] { font-weight: 500; }
18
19/* Universal selector */
20* { box-sizing: border-box; }

Combinators

Combinators express relationships between selectors:

CombinatorSymbolExampleDescription
Descendant(space)article p<p> anywhere inside <article>
Child>article > p<p> as a direct child of <article>
Adjacent sibling+h2 + p<p> immediately after <h2>
General sibling~h2 ~ p<p> anywhere after <h2> (same parent)
Column||col || td<td> that belongs to a column
combinators.css
CSS
1/* Descendant — any nested level */
2article p { color: #A0A0A0; }
3
4/* Child — only direct children */
5nav > ul { display: flex; gap: 1rem; }
6
7/* Adjacent sibling — immediately after */
8h2 + p { margin-top: 0; font-size: 1.125rem; }
9
10/* General sibling — any after */
11h2 ~ p { color: #808080; }
12
13/* Chaining combinators */
14article.content > section > p:first-of-type {
15 font-size: 1.25rem;
16 font-weight: 500;
17}

Pseudo-Classes

Pseudo-classes select elements based on their state or position:

1/* Dynamic user state */2button:hover { background: #4338CA; }3input:focus { outline: 2px solid #00FF41; }4a:active { color: #00FF41; }5a:visited { color: #888; }6 7/* Position-based */8li:first-child { font-weight: bold; }9li:last-child { border-bottom: none; }10li:nth-child(odd) { background: #0D0D0D; }11li:nth-child(3n+1) { color: #00FF41; }12 13/* Functional pseudo-classes */14button:not(.primary) { opacity: 0.8; }15:is(h1, h2, h3) { line-height: 1.3; }16:where(.card, .panel) { padding: 1.5rem; }17form:has(input:invalid) { border-color: #EF4444; }18 19/* Form state */20input:checked { accent-color: #00FF41; }21input:disabled { opacity: 0.4; }22input:required { border-color: #00FF41; }

Pseudo-Elements

Pseudo-elements style specific parts of an element. They use the double-colon :: syntax:

pseudo-elements.css
CSS
1/* Generated content */
2.element::before {
3 content: "→ ";
4 color: #00FF41;
5}
6
7.element::after {
8 content: "";
9 display: block;
10 width: 100%;
11 height: 2px;
12 background: linear-gradient(90deg, #00FF41, transparent);
13}
14
15/* Typography pseudo-elements */
16p::first-line {
17 font-size: 1.25em;
18 font-weight: 600;
19 color: #E0E0E0;
20}
21
22p::first-letter {
23 font-size: 3em;
24 float: left;
25 line-height: 0.8;
26 margin-right: 0.25rem;
27}
28
29/* Selection styling */
30::selection {
31 background: #00FF41;
32 color: #0A0A0A;
33}
34
35/* Placeholder */
36input::placeholder {
37 color: #525252;
38 font-style: italic;
39}
🔥

pro tip

Use ::before and ::after for decorative content only. Screen readers may not announce generated content, so never use them for meaningful text.
Cascade & Specificity

The cascade is the algorithm that resolves conflicting CSS declarations. When two rules apply to the same element, the browser uses specificity, importance, and source order to determine which wins.

Specificity Hierarchy

Specificity is calculated as a four-part value (inline, ID, class, element). Each selector type contributes to one of these categories:

LevelSelector TypeExampleWeight
AInline stylesstyle="..."1, 0, 0, 0
BIDs#header0, 1, 0, 0
CClasses, attributes, pseudo-classes.card [type] :hover0, 0, 1, 0
DElements and pseudo-elementsdiv ::before0, 0, 0, 1

Specificity Calculator Examples

SelectorInlineIDClassElementTotal
p00010,0,0,1
.card00100,0,1,0
#header .nav a01110,1,1,1
ul li:first-child00120,0,1,2
style="color: red;"10001,0,0,0
#app .sidebar .nav a:hover01310,1,3,1

The !important Exception

Adding !important to a declaration elevates it above all normal declarations, regardless of specificity. Use it sparingly — it breaks the cascade and makes debugging difficult.

specificity.css
CSS
1/* Without !important */
2#header .title { color: red; } /* specificity: 0,1,1,0 → wins */
3.title { color: blue; } /* specificity: 0,0,1,0 → loses */
4
5/* With !important */
6.title { color: blue !important; } /* overrides the above ID selector */
7#header .title { color: red; } /* loses to !important */
8
9/* Cascade order tips */
10/* 1. !important wins over everything */
11/* 2. Higher specificity wins */
12/* 3. If specificity is equal, the last declaration wins */

warning

Never use !important in application stylesheets. It creates maintenance nightmares. The only acceptable uses are utility overrides and user-agent resets.

Cascade Layers

The @layer at-rule gives you explicit control over cascade order, regardless of specificity or source order:

cascade-layers.css
CSS
1/* Define layer order — first defined = lowest priority */
2@layer reset, base, components, utilities;
3
4/* Styles in later layers override earlier layers */
5@layer reset {
6 h1 { margin: 0; }
7}
8
9@layer components {
10 .card { padding: 1.5rem; }
11}
12
13@layer utilities {
14 .p-4 { padding: 1rem !important; }
15}
16
17/* Unlayered styles always win over layered styles */
Inheritance & Initial Values

Some CSS properties inherit from their parent element, while others do not. Understanding inheritance is key to writing concise, maintainable stylesheets.

Inherited vs Non-Inherited

InheritedNot Inherited
colormargin
font-familypadding
font-sizeborder
line-heightdisplay
text-alignwidth / height
visibilitybackground
cursorposition
letter-spacingbox-shadow

The Cascade Keywords

Every CSS property accepts these universal keywords for controlling inheritance behavior:

KeywordBehavior
inheritForces the property to inherit from its parent (even if normally non-inherited)
initialResets the property to its CSS specification default value
unsetActs as inherit if the property is normally inherited, otherwise as initial
revertResets to the user-agent stylesheet default (or the cascade's natural state)
revert-layerResets to the value from a previous cascade layer
inheritance.css
CSS
1/* Inheritance in action */
2body {
3 color: #E0E0E0; /* inherited by all text elements */
4 font-family: system-ui; /* inherited */
5}
6
7/* Force inheritance on a non-inherited property */
8button {
9 border: inherit; /* normally not inherited */
10 background: inherit;
11}
12
13/* Reset to initial value */
14.reset-box {
15 all: initial; /* resets ALL properties to initial values */
16}
17
18/* Use unset for resetting */
19.link-list {
20 margin: unset; /* margin is not inherited → acts as initial (0) */
21 color: unset; /* color is inherited → acts as inherit */
22}
23
24/* Revert to browser default */
25.no-style {
26 all: revert;
27}
🔥

pro tip

Use all: initial as a hard reset for isolated components. This ensures no external styles leak in. Use all: unset when you want a soft reset that preserves inheritance for text properties.
Box Model

Every element in CSS is rendered as a rectangular box. The box model describes how the content, padding, border, and margin areas interact to determine an element's total size.

box-model.html
MARGIN
BORDER
PADDING
CONTENT

Content-Box vs Border-Box

The box-sizing property changes how width and height are calculated:

Box Modelwidth IncludesTotal Width Formula
content-boxContent onlywidth + padding + border + margin
border-boxContent + padding + borderwidth + margin
box-model.css
CSS
1/* The universal reset — always use border-box */
2*, *::before, *::after {
3 box-sizing: border-box;
4}
5
6/* content-box (default) — width excludes padding/border */
7.content-box {
8 box-sizing: content-box;
9 width: 200px;
10 padding: 20px;
11 border: 2px solid #00FF41;
12 /* total width = 200 + 40 + 4 = 244px */
13}
14
15/* border-box — width includes padding/border */
16.border-box {
17 box-sizing: border-box;
18 width: 200px;
19 padding: 20px;
20 border: 2px solid #00FF41;
21 /* total width = 200px (content area = 200 - 40 - 4 = 156px) */
22}

Margin Collapse

Vertical margins between adjacent block elements collapse into a single margin equal to the larger of the two margins. This only applies to vertical (margin-top, margin-bottom) margins in the same block formatting context.

margin-collapse.css
CSS
1/* Margin collapse example */
2.box-a { margin-bottom: 40px; }
3.box-b { margin-top: 30px; }
4/* The gap between box-a and box-b is 40px (not 70px) */
5
6/* Margin collapse does NOT apply to: */
7/* - Flex children */
8/* - Grid children */
9/* - Elements with overflow: hidden/auto/scroll */
10/* - Absolutely positioned elements */
11/* - Inline-block elements */

Outline vs Border

Outlines are similar to borders but do NOT take up space in the box model. They are drawn outside the border edge and do not affect layout. Use outlines for focus indicators and debugging.

outline-vs-border.css
CSS
1/* Border — takes up space, part of box model */
2.card {
3 border: 2px solid #00FF41;
4 padding: 16px; /* content pushed inward */
5}
6
7/* Outline — does NOT take up space */
8.card:focus-within {
9 outline: 2px solid #00FF41;
10 outline-offset: 2px; /* drawn outside the border */
11 /* no layout shift */
12}
13
14/* Outline for debugging element bounds */
15.debug * {
16 outline: 1px solid rgba(255, 0, 0, 0.3) !important;
17}
Display Types

The display property determines how an element behaves in the layout. It controls whether the element generates a box, and how that box interacts with surrounding elements.

ValueBehaviorWidth DefaultHeight DefaultMargin
blockFills available width, starts on new line100% of parentContent heightAll sides respected
inlineFlows in text content, no width/height controlContent widthContent heightHorizontal only
inline-blockInline flow but respects width/height/marginContent widthContent heightAll sides
noneRemoved from layout entirely (not visible)
contentsBox disappears, children inherit parent's layout
flow-rootBlock-level, creates new BFC (contains floats)100% of parentContent heightAll sides
flexBlock-level flex container100% of parentContent heightAll sides
gridBlock-level grid container100% of parentContent heightAll sides
preview

info

Use display: contentsto remove an element's box from the layout tree while keeping its children. This is useful for semantic wrappers that shouldn't affect styling or for Flex/Grid items where the wrapper interferes with alignment.
Units

CSS offers a wide range of units for sizing elements, text, spacing, and more. Choosing the right unit makes your design responsive and accessible.

Unit Comparison Table

UnitTypeRelative ToBest For
pxAbsoluteDevice pixel (1px = 1/96th of 1in)Borders, shadows, fine details
emRelativeParent element's font-sizePadding, margins relative to text size
remRelativeRoot element's font-size (usually 16px)Font sizes, spacing (accessible scaling)
%RelativeParent element's same propertyWidths, heights, font-size
vwViewport1% of viewport widthFull-width sections, hero text
vhViewport1% of viewport heightFull-height sections, overlays
vminViewport1% of the smaller viewport dimensionResponsive square elements
vmaxViewport1% of the larger viewport dimensionResponsive hero sections
chRelativeWidth of the "0" character in the fontMeasure / line length (max-width: 65ch)
exRelativeHeight of the "x" characterInline vertical metrics
lhRelativeElement's own line-heightVertical rhythm
rlhRelativeRoot element's line-heightGlobal vertical rhythm
capRelativeCapital letter height of the fontTypography alignment
dvhViewportDynamic viewport height (adjusts for mobile UI)Mobile full-height sections
svhViewportSmallest viewport heightSafe minimum heights
lvhViewportLargest viewport heightMaximum height expectations

Unit Usage Examples

units.css
CSS
1/* --- Typography: use rem for accessibility --- */
2html { font-size: 16px; } /* 1rem = 16px default */
3body { font-size: 1rem; }
4h1 { font-size: 2.5rem; } /* 40px */
5h2 { font-size: 2rem; } /* 32px */
6p { font-size: 1rem; } /* 16px */
7small { font-size: 0.875rem; } /* 14px */
8
9/* --- Spacing: use rem consistently --- */
10.card {
11 padding: 1.5rem; /* 24px */
12 margin-bottom: 2rem; /* 32px */
13 border-radius: 0.5rem; /* 8px */
14 gap: 0.75rem; /* 12px */
15}
16
17/* --- Line length: use ch for readable text --- */
18.article-content {
19 max-width: 65ch; /* ~65 characters per line */
20}
21
22/* --- Layout: use viewport units --- */
23.hero {
24 width: 100vw; /* full viewport width */
25 height: 100dvh; /* dynamic viewport height */
26}
27
28/* --- Responsive font with clamp --- */
29.responsive-text {
30 font-size: clamp(1rem, 2.5vw, 3rem);
31 /* minimum 1rem, preferred 2.5vw, maximum 3rem */
32}
33
34/* --- Avoid em for font-size in complex components --- */
35.component {
36 font-size: 0.875em; /* compounds with nesting! */
37 /* Prefer rem to avoid compounding */

best practice

Use rem for font sizes and spacing to respect user browser settings. Use ch for line lengths (optimal readability: 60–75 characters). Use dvh for mobile full-height layouts instead of 100vh to avoid the mobile browser chrome overlap issue.
Colors

CSS offers multiple ways to specify colors, from simple named colors to advanced color spaces like OKLCH and color-mix().

MethodSyntaxExampleNotes
Namedrebeccapurple #639148 named colors
Hex#rgb / #rrggbb / #rrggbbaa #00FF416-digit with alpha optional
rgb/rgbargb(r g b / a) rgb(99 102 241 / 0.8)Modern space-separated syntax
hsl/hslahsl(h s% l% / a) hsl(120 100% 50%)Intuitive hue wheel
oklchoklch(l c h / a) oklch(0.87 0.26 145)Perceptually uniform, best for gradients
color-mixcolor-mix(in srgb, a pct, b pct) mix(#0f0, #66f)Blend two colors
currentColorcurrentColorInherits from color propertyGreat for SVG and borders
colors.css
CSS
1/* Named colors */
2.element { color: rebeccapurple; }
3.error { color: red; } /* readable but imprecise */
4
5/* Hexadecimal — most common */
6.primary { color: #00FF41; } /* green */
7.muted { color: #808080; } /* gray */
8.overlay { background: #0A0A0A; } /* dark bg */
9.translucent { background: #00FF4180; } /* 50% alpha */
10
11/* Modern rgb() syntax — space separated, no commas */
12.card {
13 background: rgb(12 12 20 / 0.95);
14 border-color: rgb(30 30 48 / 1);
15}
16
17/* HSL — intuitive color manipulation */
18.accent {
19 color: hsl(120 100% 50%); /* pure green */
20}
21.dimmed {
22 color: hsl(120 100% 50% / 0.5); /* 50% transparent green */
23}
24
25/* OKLCH — perceptually uniform, best for design systems */
26:root {
27 --brand: oklch(0.87 0.26 145);
28 --brand-light: oklch(0.92 0.18 145);
29 --brand-dark: oklch(0.65 0.30 145);
30}
31
32/* color-mix() — blend colors in real time */
33.btn-primary {
34 background: var(--brand);
35}
36.btn-primary:hover {
37 background: color-mix(in srgb, var(--brand), black 20%);
38}
39
40/* currentColor — automatically matches text color */
41.icon {
42 fill: currentColor; /* inherits from text color */
43 stroke: currentColor;
44}
45.button .icon {
46 color: white; /* icon inherits white */
47}
preview
🔥

pro tip

Use OKLCH for design system colors. It is perceptually uniform, meaning a 10% lightness change looks equally significant to the human eye at any point in the color space. HSL is not perceptually uniform — the same saturation value produces very different visual intensity for different hues.
Backgrounds & Borders

CSS provides powerful tools for decorating elements with backgrounds, gradients, borders, and shadows.

Background Shorthand

backgrounds.css
CSS
1/* Background shorthand — order: color image position/size repeat attachment origin clip */
2.hero {
3 background: #0A0A0A url("bg.jpg") center / cover no-repeat fixed;
4}
5
6/* Equivalent longhand */
7.hero {
8 background-color: #0A0A0A;
9 background-image: url("bg.jpg");
10 background-position: center;
11 background-size: cover;
12 background-repeat: no-repeat;
13 background-attachment: fixed;
14}
15
16/* Multiple backgrounds (stacked front-to-back) */
17.card {
18 background:
19 linear-gradient(135deg, rgb(99 102 241 / 0.1), transparent),
20 radial-gradient(circle at top right, rgb(0 255 65 / 0.05), transparent),
21 #0A0A0A;
22}
23
24/* Gradients */
25.linear-gradient {
26 background: linear-gradient(90deg, #00FF41, #00FF41, #EC4899);
27}
28
29.radial-gradient {
30 background: radial-gradient(circle, #00FF41, #0A0A0A);
31}
32
33.conic-gradient {
34 background: conic-gradient(from 0deg, #00FF41, #00FF41, #EC4899, #00FF41);
35}
preview

Border & Border-Radius

borders.css
CSS
1/* Border shorthand */
2.card {
3 border: 2px solid #222222;
4}
5
6/* Border-radius — can be asymmetrical */
7.card {
8 border-radius: 8px; /* uniform */
9 border-radius: 8px 16px; /* top-left+bot-right / top-right+bot-left */
10 border-radius: 8px 4px 12px 6px; /* TL TR BR BL (clockwise) */
11 border-radius: 50%; /* circle */
12}
13
14/* Border-image */
15.bordered {
16 border: 8px solid transparent;
17 border-image: linear-gradient(135deg, #00FF41, #00FF41) 1;
18}
19
20/* Box-shadow syntax: x y blur spread color inset */
21.card-shadow {
22 box-shadow:
23 0 1px 3px rgb(0 0 0 / 0.3),
24 0 4px 12px rgb(0 0 0 / 0.15); /* layered shadows */
25}
26
27/* Inner shadow */
28.card-inset {
29 box-shadow: inset 0 2px 4px rgb(0 0 0 / 0.3);
30}

best practice

Use multiple box-shadow layers to create realistic, rich shadows. A single blur value often looks flat. Combine a tight, opaque shadow with a wider, more transparent one for depth.
CSS Custom Properties

Custom properties (often called CSS variables) allow you to define reusable values. They cascade, can be overridden, and are accessible via JavaScript.

Defining and Using Custom Properties

custom-properties.css
CSS
1/* Define on :root for global scope */
2:root {
3 --brand: #00FF41;
4 --brand-dim: color-mix(in srgb, var(--brand), black 30%);
5 --surface: #0A0A0A;
6 --text: #E0E0E0;
7 --text-muted: #808080;
8 --border: #222222;
9 --radius-sm: 0.375rem;
10 --radius-md: 0.5rem;
11 --radius-lg: 0.75rem;
12 --space-xs: 0.25rem;
13 --space-sm: 0.5rem;
14 --space-md: 1rem;
15 --space-lg: 1.5rem;
16 --space-xl: 2rem;
17}
18
19/* Use with var() */
20.card {
21 background: var(--surface);
22 color: var(--text);
23 border: 1px solid var(--border);
24 border-radius: var(--radius-md);
25 padding: var(--space-lg);
26}
27
28/* Fallback value if property is not defined */
29.element {
30 color: var(--undefined-var, #00FF41); /* falls back to #00FF41 */
31 background: var(--surface, #0A0A0A); /* uses definition or fallback */
32}
33
34/* Scoped overrides */
35.dark-card {
36 --surface: #05050A;
37 --border: #2A2A3E;
38 background: var(--surface);
39}

Typed Custom Properties with @property

The @property at-rule registers a custom property with a defined syntax, allowing the browser to type-check and — crucially — animate it:

typed-properties.css
CSS
1/* Register a typed custom property */
2@property --brand-color {
3 syntax: "<color>";
4 inherits: false;
5 initial-value: #00FF41;
6}
7
8@property --rotation {
9 syntax: "<angle>";
10 inherits: false;
11 initial-value: 0deg;
12}
13
14@property --spacing {
15 syntax: "<length>";
16 inherits: true;
17 initial-value: 1rem;
18}
19
20/* Now we can animate these properties! */
21@keyframes rotate-brand {
22 from { --rotation: 0deg; }
23 to { --rotation: 360deg; }
24}
25
26.box {
27 --brand-color: #00FF41;
28 --rotation: 0deg;
29 background: var(--brand-color);
30 transform: rotate(var(--rotation));
31 transition: --brand-color 0.3s ease;
32}
33
34.box:hover {
35 --brand-color: #00FF41;
36 --rotation: 180deg;
37}
🔥

pro tip

Register custom properties with @property when you need to animate them. Without registration, the browser cannot interpolate between values during transitions or keyframe animations.

Accessing Custom Properties from JavaScript

theme.js
JavaScript
1// Read a custom property
2const root = document.documentElement;
3const brand = getComputedStyle(root)
4 .getPropertyValue("--brand")
5 .trim(); // "#00FF41"
6
7// Set a custom property
8root.style.setProperty("--brand", "#00FF41");
9
10// Use in dynamic theming
11function setTheme(theme) {
12 const themes = {
13 dark: {
14 "--surface": "#0A0A0A",
15 "--text": "#E0E0E0",
16 },
17 light: {
18 "--surface": "#FFFFFF",
19 "--text": "#1A1A1A",
20 },
21 };
22 const vars = themes[theme];
23 Object.entries(vars).forEach(([key, val]) => {
24 root.style.setProperty(key, val);
25 });
26}
Media Queries

Media queries allow you to apply CSS conditionally based on device characteristics like viewport size, resolution, color scheme, and user preferences.

Syntax & Logical Operators

media-queries.css
CSS
1/* Basic syntax */
2@media (max-width: 768px) {
3 .sidebar { display: none; }
4}
5
6/* Logical operators */
7@media (min-width: 640px) and (max-width: 1024px) {
8 /* AND: both conditions must match */
9 .grid { grid-template-columns: 1fr 1fr; }
10}
11
12@media (max-width: 480px), (orientation: landscape) {
13 /* OR: either condition matches */
14 .nav { flex-direction: column; }
15}
16
17@media not (hover: hover) {
18 /* NOT: negates the condition */
19 .tooltip { display: none; }
20}
21
22/* Chaining media features */
23@media (min-width: 768px) and (hover: hover) and (prefers-reduced-motion: no-preference) {
24 .card:hover {
25 transform: scale(1.02);
26 transition: transform 0.3s ease;
27 }
28}

Common Breakpoints

NameMin WidthTarget Devices
sm640pxLarge phones, small tablets
md768pxTablets (portrait)
lg1024pxTablets (landscape), small desktops
xl1280pxDesktop monitors
2xl1536pxLarge screens, ultrawide

Preference Queries

preference-queries.css
CSS
1/* Dark/light mode */
2@media (prefers-color-scheme: dark) {
3 :root {
4 --surface: #0A0A0A;
5 --text: #E0E0E0;
6 }
7}
8
9@media (prefers-color-scheme: light) {
10 :root {
11 --surface: #FFFFFF;
12 --text: #1A1A1A;
13 }
14}
15
16/* Reduced motion */
17@media (prefers-reduced-motion: reduce) {
18 *, *::before, *::after {
19 animation-duration: 0.01ms !important;
20 animation-iteration-count: 1 !important;
21 transition-duration: 0.01ms !important;
22 }
23}
24
25/* Reduced transparency */
26@media (prefers-reduced-transparency: reduce) {
27 .glass {
28 background: var(--surface);
29 backdrop-filter: none;
30 }
31}
32
33/* High contrast */
34@media (prefers-contrast: high) {
35 .card {
36 border-width: 2px;
37 }
38}
39
40/* Reduced data usage */
41@media (prefers-reduced-data: reduce) {
42 .hero {
43 background-image: none;
44 }
45}

warning

Always provide a prefers-reduced-motion: reduce query. Some users have vestibular disorders where animations can cause nausea or dizziness. Respect their preference by minimizing motion.

Container Queries

Container queries allow you to style elements based on their parent container's size, rather than the viewport. This is ideal for reusable components that need to adapt to various contexts.

container-queries.css
CSS
1/* Define a containment context */
2.card-container {
3 container-type: inline-size;
4 container-name: card;
5}
6
7/* Query based on container width */
8@container card (min-width: 400px) {
9 .card {
10 display: grid;
11 grid-template-columns: 200px 1fr;
12 }
13}
14
15@container card (max-width: 399px) {
16 .card {
17 display: flex;
18 flex-direction: column;
19 }
20}
21
22/* Style queries — check if container has a certain style */
23@container card style(--variant: featured) {
24 .card-title { font-size: 1.5rem; }
25}
Best Practices

Naming Conventions

Consistent naming conventions make CSS maintainable at scale. Two popular approaches are BEM and utility-first:

naming-conventions.css
CSS
1/* BEM (Block Element Modifier) */
2.block {} /* component name */
3.block__element {} /* child of block */
4.block--modifier {} /* variant of block */
5
6/* Example */
7.card {}
8.card__title {}
9.card__description {}
10.card--featured {}
11.card--compact {}
12
13/* Utility-first (e.g., Tailwind-style) */
14.flex { display: flex; }
15.p-4 { padding: 1rem; }
16.text-center { text-align: center; }
17.bg-brand { background: var(--brand); }
18
19/* Hybrid approach — combine components + utilities */
20.card {
21 /* component styles */
22 background: var(--surface);
23 border-radius: var(--radius-md);
24}
25.card.featured {
26 /* modifier with utility class */
27 border-color: var(--brand);
28}

Performance Best Practices

Use border-box globally — prevents unexpected sizing math
Prefer class selectors over ID selectors — lower specificity is easier to override
Avoid !important — it breaks the cascade and makes debugging miserable
Use shorthand properties (background, margin, font) — reduces file size
Minimize nesting depth in preprocessors — keep specificity flat
Use transform and opacity for animations — they only trigger compositing
Lazy-load non-critical CSS with media queries or @import conditionals
Use content-visibility: auto for long pages — skips rendering off-screen sections
Prefer logical properties (margin-inline-start over margin-left) for internationalization
Use will-change sparingly — only on elements that WILL change, never as optimization

Organization Strategy

structure.css
CSS
1/* Suggested file structure for projects */
2styles/
3├── reset.css /* CSS reset / normalize */
4├── tokens.css /* Custom properties (colors, spacing, fonts) */
5├── base.css /* Element selectors (body, headings, links) */
6├── layout.css /* Grid, containers, page structure */
7├── components/ /* Component-specific styles */
8│ ├── card.css
9│ ├── button.css
10│ └── nav.css
11├── utilities.css /* Utility classes */
12└── overrides.css /* Debug, print, fallback overrides */
13
14/* Within a component file, organize by: */
15/* 1. Container / layout */
16/* 2. Children */
17/* 3. States (:hover, :active, .is-active) */
18/* 4. Variants (--featured, --compact) */
19/* 5. Media queries (component-scoped) */

best practice

Use CSS custom properties for your design tokens (colors, spacing, typography). This centralizes values, makes theming trivial, and reduces the chance of inconsistencies. A single source of truth is better than 47 different hex values spread across 30 files.

Common Pitfalls to Avoid

✗ Overly Specific Selectors

div#main-content .container ul li a.link { color: blue; }

This has specificity 0,1,2,4 — it cannot be overridden without !important.

✓ Flat, Low-Specificity Selectors

.link-primary { color: var(--brand); }

A single class is easy to override and compose.

✗ Magic Numbers

margin-top: -3px; /* hack to align with sibling */

Magic numbers break when content or fonts change. Use proper layout techniques instead.

✓ Intentional, Meaningful Values

display: flex; align-items: center;

Flexbox and Grid provide robust, non-fragile alignment solutions.

CSS Object Model (CSSOM)

The CSSOM is a map of all CSS selectors and their computed styles for each element in the DOM. The browser constructs the CSSOM alongside the DOM during the parsing phase. Understanding the CSSOM helps you write efficient selectors and diagnose rendering performance.

cssom.js
JavaScript
1// Access computed styles
2const element = document.querySelector(".card");
3const styles = getComputedStyle(element);
4
5console.log(styles.color); // "rgb(224, 224, 224)"
6console.log(styles.getPropertyValue("--brand")); // "#00FF41"
7
8// StyleSheet API
9const sheet = document.styleSheets[0];
10const rules = sheet.cssRules;
11
12for (const rule of rules) {
13 if (rule instanceof CSSStyleRule) {
14 console.log(rule.selectorText); // ".card"
15 console.log(rule.style.color); // "#E0E0E0"
16 }
17}

info

The getComputedStyle() method triggers a synchronous style recalc, which can be expensive if called many times. Cache the result if you need multiple values from the same element.
$Blueprint — Engineering Documentation·Section ID: CSS-01·Revision: 1.0