CSS logoCSSBEGINNER

CSS

Complete CSS fundamentals reference covering selectors, box model, typography, colors, positioning, variables, and modern CSS features

12 min read
cssselectorsbox-modelpositioningvariablescustom-propertiestypographycolorsresponsive
Loading your progress

Selectors & Specificity

Target elements with selectors and understand the cascade

Selectors

All the ways to target HTML elements with CSS

css
/* Element, class, ID */
p { }
.card { }
#header { }

/* Combinators */
.nav a { }        /* descendant */
.nav > li { }     /* direct child */
h2 + p { }        /* adjacent sibling */
h2 ~ p { }        /* general sibling */

/* Attribute */
[type="email"] { }
[href^="https"] { }
💡 :has() is a game-changer — it lets you style a parent based on its children
⚡ Use :is() to group selectors without repeating shared styles; :where() is the same but with 0 specificity
📌 Prefer :focus-visible over :focus for keyboard-only focus rings (better UX)
🟢 See also: CSS Flexbox, CSS Grid, and CSS Animations sheets for layout and motion
selectorscombinatorspseudo

How CSS determines which styles win when rules conflict

css
/* Specificity (low to high):
   Element     → 0-0-1   (p, div, h1)
   Class       → 0-1-0   (.card, :hover, [attr])
   ID          → 1-0-0   (#header)
   Inline      → wins over all
   !important  → overrides everything
*/

/* Cascade order (lowest to highest):
   1. Browser defaults
   2. External/internal stylesheets
   3. Inline styles
   4. !important rules
*/
💡 Specificity is scored as ID-CLASS-ELEMENT — the higher score wins, left to right
⚡ :where() has 0 specificity — useful for defaults that should be easy to override
📌 Avoid !important — it breaks the cascade and makes debugging painful
🟢 @layer gives you control over cascade order without fighting specificity wars
specificitycascadeinheritance

Box Model

How elements are sized with content, padding, border, and margin

Content, padding, border, margin, and how box-sizing changes the calculation

css
.box {
  width: 300px;
  padding: 20px;
  border: 2px solid #ccc;
  margin: 10px;
}

/* border-box: width INCLUDES padding + border */
*, *::before, *::after {
  box-sizing: border-box;
}
💡 Always use box-sizing: border-box globally — it makes width actually mean total width
⚡ margin-inline: auto centers a block element horizontally (works in all modern browsers)
📌 Vertical margins collapse — two 20px margins between elements becomes 20px, not 40px
🟢 aspect-ratio: 16/9 maintains proportions without the old padding-top hack
box-modelpaddingmarginsizing

Common margin and padding shorthand patterns and spacing techniques

css
/* Shorthand: Top Right Bottom Left (clockwise) */
margin: 10px 20px 15px 5px;
padding: 1rem 2rem;     /* Vertical | Horizontal */

/* Logical properties (LTR/RTL aware) */
margin-inline: auto;     /* Center horizontally */
padding-block: 1rem;     /* Top + bottom */
💡 display: grid; place-items: center is the shortest way to center anything
⚡ gap works in both flex and grid — replaces margin hacks between items
📌 margin-inline: auto centers horizontally; margin: auto in grid/flex centers both axes
🟢 Build a spacing scale with CSS variables for consistent design system spacing
spacingmargincenteringgap

Typography

Font properties, text styling, and web fonts

Fonts & Text

Font properties, text alignment, spacing, and web font loading

css
body {
  font-family: system-ui, -apple-system, sans-serif;
  font-size: 16px;
  line-height: 1.6;
  color: #333;
}

h1 { font-size: 2.5rem; font-weight: 700; }
.muted { color: #6b7280; }
.center { text-align: center; }
💡 Use rem for font sizes (relative to root) — predictable and accessible when users change browser defaults
⚡ clamp(min, preferred, max) makes fluid typography easy — no media queries needed
📌 font-display: swap prevents invisible text while web fonts load (FOUT over FOIT)
🟢 text-wrap: balance evens out heading line lengths — great for narrow containers
typographyfontstext

Style ordered/unordered lists, custom markers, and CSS counters

css
/* Remove default list styles */
ul { list-style: none; padding: 0; }

/* Custom markers */
li::marker { color: #3b82f6; font-size: 1.2em; }
ul { list-style-type: "→ "; }
💡 list-style: none + padding: 0 is the standard reset for navigation menus
⚡ ::marker pseudo-element lets you style bullet/number color and size independently
📌 CSS counters auto-number without using <ol> — great for steps, figures, and headings
🟢 Custom string list-style-type accepts any string: emojis, arrows, check marks
listsmarkerscounters

Colors & Backgrounds

Color formats, gradients, and background properties

Color formats, opacity, gradients, and background sizing

css
/* Color formats */
color: #ff6600;
color: rgb(255, 102, 0);
color: hsl(24, 100%, 50%);
color: oklch(70% 0.2 45);

/* Background */
background: linear-gradient(to right, #3b82f6, #8b5cf6);
background-image: url("bg.jpg");
background-size: cover;
💡 oklch() produces perceptually even colors — better for design systems than hsl
⚡ Use rgb(0 0 0 / 50%) for transparent backgrounds — doesn't affect child elements like opacity does
📌 background-size: cover fills the element; contain fits the whole image inside
🟢 Layer a dark gradient over an image for readable text on photo backgrounds
colorsbackgroundsgradients

Gradients

Linear, radial, and conic gradients for backgrounds and text

css
/* Linear gradient */
background: linear-gradient(to right, #3b82f6, #8b5cf6);
background: linear-gradient(135deg, #f59e0b, #ef4444);

/* Radial gradient */
background: radial-gradient(circle, #3b82f6, #1e40af);

/* Conic gradient (pie chart effect) */
background: conic-gradient(red, yellow, lime, aqua, blue, red);
💡 Hard color stops at the same position create sharp lines — great for geometric patterns
⚡ Gradient text: background-clip: text + color: transparent reveals the gradient through text
📌 Conic gradients with border-radius: 50% create instant pie charts in pure CSS
🟢 repeating-linear-gradient creates stripes, checkerboards, and geometric patterns
gradientlinearradialconic

Positioning

Control element placement with position, z-index, and stacking

Static, relative, absolute, fixed, and sticky positioning

css
/* Relative (offset from normal position) */
.badge { position: relative; top: -5px; left: 10px; }

/* Absolute (relative to nearest positioned ancestor) */
.tooltip { position: absolute; top: 100%; left: 0; }

/* Fixed (relative to viewport) */
.navbar { position: fixed; top: 0; width: 100%; z-index: 50; }

/* Sticky (scrolls then sticks) */
.sidebar { position: sticky; top: 20px; }
💡 Absolute positioning needs a positioned ancestor (relative parent) — otherwise it uses the viewport
⚡ position: sticky is perfect for sidebars, table headers, and section headings
📌 z-index only works on positioned elements — static elements ignore it
🟢 inset: 0 is shorthand for top: 0; right: 0; bottom: 0; left: 0
positionz-indexstickyabsolute

Transforms

Move, rotate, scale, and skew elements with CSS transforms

css
transform: translateX(20px);
transform: rotate(45deg);
transform: scale(1.5);
transform: skew(10deg);

/* Combine multiple */
transform: translate(-50%, -50%) rotate(45deg) scale(1.2);
💡 Transforms are GPU-accelerated — translateX/Y and scale are very performant for animations
⚡ transform: translateY(-4px) on hover creates a subtle lift effect — better than changing margin
📌 Transform order matters — translate then rotate gives different results than rotate then translate
🟢 Use perspective on the parent for 3D effects, and backface-visibility: hidden for flip cards
transformrotatescaletranslate

Display & Visibility

Control how elements render and whether they are visible

Block, inline, none, visibility, and opacity

css
display: block;        /* Full width, new line */
display: inline;       /* Flows with text */
display: inline-block; /* Inline but accepts width/height */
display: none;         /* Removed from layout */
display: flex;         /* Flexbox container */
display: grid;         /* Grid container */

visibility: hidden;    /* Hidden but takes up space */
opacity: 0;            /* Transparent but interactive */
💡 display: none removes from layout AND accessibility — use .sr-only to hide visually but keep for screen readers
⚡ object-fit: cover on images is like background-size: cover but for img/video elements
📌 inline elements ignore width, height, and vertical margin — use inline-block if you need those
🟢 See the CSS Flexbox and CSS Grid sheets for complete layout system references
displayvisibilityhide

Control mouse cursor appearance and element interactivity

css
cursor: pointer;         /* Clickable hand */
cursor: not-allowed;     /* Disabled state */
cursor: grab;            /* Draggable */
pointer-events: none;    /* Click-through */
user-select: none;       /* Prevent text selection */
💡 pointer-events: none makes an element click-through — clicks pass to elements behind it
⚡ cursor: grab + cursor: grabbing on :active gives proper drag feedback
📌 user-select: none on buttons and UI chrome prevents accidental text selection
🟢 Combine opacity: 0.5 + cursor: not-allowed + pointer-events: none for disabled states
cursorpointer-eventsuser-select

Borders, Shadows & Effects

Borders, rounded corners, shadows, and visual effects

Border styles, rounded corners, box shadows, and filters

css
.card {
  border: 1px solid #e5e7eb;
  border-radius: 12px;
  box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}

.circle { border-radius: 50%; }
.pill { border-radius: 9999px; }
💡 box-shadow: 0 0 0 3px color creates a ring effect — great for focus states
⚡ backdrop-filter: blur() creates frosted glass effects — combine with semi-transparent background
📌 outline doesn't take up space or affect layout — border does
🟢 filter: drop-shadow follows the element shape (including transparency) — box-shadow is always rectangular
bordershadowradiusfilter

Accessible focus indicators for keyboard navigation

css
/* Custom focus ring */
button:focus-visible {
  outline: 2px solid #3b82f6;
  outline-offset: 2px;
}

/* Remove default, add custom */
button:focus { outline: none; }
button:focus-visible {
  box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.4);
}
💡 Use :focus-visible instead of :focus — it only shows the ring for keyboard users, not mouse clicks
⚡ :focus-within styles a parent when ANY child inside it has focus — perfect for form groups
📌 Never remove focus outlines without providing an alternative — keyboard users need them
🟢 outline-offset creates a gap between the element and the focus ring for a cleaner look
focusoutlineaccessibility

Custom Properties & Functions

CSS variables and built-in math functions

Define reusable values with custom properties and use calc, clamp, min, max

css
:root {
  --color-primary: #3b82f6;
  --spacing: 1rem;
  --radius: 8px;
}

.btn {
  background: var(--color-primary);
  padding: var(--spacing);
  border-radius: var(--radius);
}

.container {
  width: min(100% - 2rem, 1200px);
}
💡 CSS variables cascade and inherit — override them in any scope for easy theming
⚡ clamp(min, preferred, max) replaces min-width + max-width + media queries in one line
📌 var(--name, fallback) provides a default value if the variable is not defined
🟢 Use @media (prefers-color-scheme: dark) with variable overrides for system dark mode
variablescustom-propertiescalcclamp

Implement dark mode with CSS variables and media queries

css
:root {
  --bg: #ffffff;
  --text: #1f2937;
  --primary: #3b82f6;
}

@media (prefers-color-scheme: dark) {
  :root {
    --bg: #111827;
    --text: #f9fafb;
    --primary: #60a5fa;
  }
}

body { background: var(--bg); color: var(--text); }
💡 Override CSS variables in @media (prefers-color-scheme: dark) — one change, everything updates
⚡ color-scheme: light dark on :root makes browser UI (scrollbars, inputs) match your theme
📌 Support both system detection and manual toggle — store the choice in localStorage
🟢 Slightly dim images in dark mode with filter: brightness(0.9) to reduce eye strain
dark-modethemingvariables

Transitions

Smooth property changes on hover, focus, and state changes

CSS Transitions

Animate property changes smoothly between states

css
.btn {
  background: #3b82f6;
  transition: background 200ms ease;
}
.btn:hover {
  background: #2563eb;
}

/* Shorthand: property duration timing-function delay */
transition: all 300ms ease-in-out;
transition: transform 200ms ease, opacity 200ms ease;
💡 Transition individual properties instead of "all" — better performance and control
⚡ Only transition cheap properties: transform, opacity, color, background, box-shadow
📌 Avoid transitioning width/height/top/left — they trigger layout recalculation (janky)
🟢 See the CSS Animations sheet for keyframes, complex sequences, and timing deep-dives
transitionhoveranimation

Media Queries & Responsive

Adapt layouts for different screen sizes and user preferences

Media Queries

Responsive breakpoints, user preferences, and container queries

css
/* Mobile-first breakpoints */
@media (min-width: 640px)  { /* sm */ }
@media (min-width: 768px)  { /* md */ }
@media (min-width: 1024px) { /* lg */ }
@media (min-width: 1280px) { /* xl */ }

/* Dark mode */
@media (prefers-color-scheme: dark) { }

/* Reduced motion */
@media (prefers-reduced-motion: reduce) { }
💡 Mobile-first (min-width) is the standard — start small, add complexity for larger screens
⚡ Container queries let components respond to their container size, not the viewport
📌 Always respect prefers-reduced-motion — disable animations for users who need it
🟢 See the Screen Sizes & Responsive Breakpoints sheet for device dimensions and patterns
media-queriesresponsivecontainer-queries

Style components based on their container size, not the viewport

css
.card-wrapper {
  container-type: inline-size;
}

@container (min-width: 400px) {
  .card { display: flex; gap: 1rem; }
}

@container (min-width: 600px) {
  .card { font-size: 1.25rem; }
}
💡 Container queries let components adapt to where they are placed, not just viewport size
⚡ The same .card component can have different layouts in a sidebar vs main content area
📌 Set container-type: inline-size on the parent — then @container queries check ITS width
🟢 Container query units (cqi, cqb) are like vw/vh but relative to the container
container-queriesresponsivecomponents

Modern CSS Features

CSS nesting, :has(), logical properties, and other recent additions

Modern CSS features supported in all major browsers

css
/* CSS Nesting (no preprocessor needed) */
.card {
  padding: 1rem;
  & h2 { font-size: 1.5rem; }
  &:hover { box-shadow: 0 4px 12px rgba(0,0,0,0.15); }
  & .badge { background: green; }
}

/* :has() parent selector */
.card:has(img) { padding: 0; }
💡 CSS nesting is native now — same syntax as Sass/SCSS without the preprocessor
⚡ :has() is the most powerful CSS addition in years — style any ancestor based on descendants
📌 Logical properties (inline/block) make layouts work automatically for RTL languages
🟢 accent-color instantly themes checkboxes, radios, ranges, and progress bars
nestinghaslogicalmodern

Drive @keyframes from scroll position or element visibility — no JS required.

css
/* Animate as the page scrolls (root scroller) */
@keyframes progress {
  to { width: 100%; }
}
.progress-bar {
  animation: progress linear;
  animation-timeline: scroll();    /* drive from page scroll */
}

/* Animate based on element entering the viewport */
@keyframes fade-in {
  from { opacity: 0; transform: translateY(40px); }
  to   { opacity: 1; transform: translateY(0); }
}
.card {
  animation: fade-in linear both;
  animation-timeline: view();      /* drive from element's own view progress */
  animation-range: entry 0% cover 30%;
}
💡 animation-timeline: scroll() drives from a scroller; view() drives from element visibility
⚡ animation-range controls WHEN in the timeline the animation plays (entry / cover / exit)
⚠️ Firefox still needs a flag (as of 2026); use @supports (animation-timeline: scroll()) as a fallback
🔥 Always pair with @media (prefers-reduced-motion: reduce)

Native animated transitions between DOM states or page navigations.

css
/* === Same-document transitions (Baseline: all modern browsers) === */
/* JS: trigger a transition */
// document.startViewTransition(() => updateDOM())

/* CSS: opt elements into the named animation */
.card {
  view-transition-name: card;   /* unique per element */
}

/* Customize the auto-generated animation */
::view-transition-old(card),
::view-transition-new(card) {
  animation-duration: 400ms;
}

/* === Cross-document (MPA) view transitions === */
@view-transition {
  navigation: auto;             /* enable on same-origin nav */
}
💡 startViewTransition() captures before/after states and morphs between them
⚡ Each element with view-transition-name gets its own animated pair
📌 MPA view transitions need @view-transition { navigation: auto; } on BOTH pages
🔥 Default animation is a crossfade — override with ::view-transition-old/new

Position elements relative to other elements — no JS-based popper needed.

css
/* Step 1: name an anchor */
.button {
  anchor-name: --trigger;
}

/* Step 2: position something relative to that anchor */
.tooltip {
  position: absolute;
  position-anchor: --trigger;

  /* Place below the anchor, centered */
  top: anchor(bottom);
  left: anchor(center);
  translate: -50%;
}

/* Or use the high-level shorthand */
.popover {
  position: absolute;
  position-anchor: --trigger;
  position-area: bottom span-all;     /* below, full width */
}
💡 anchor-name declares the source; position-anchor + anchor() places relative to it
⚡ position-area is the easy mode — pick a region (bottom center, top left, etc.)
📌 position-try-fallbacks auto-flips to avoid overflow — kills the need for floating-ui
🔥 Pairs beautifully with native <dialog> and popover="" for tooltips/menus