DEV SCRIPTS

Css Code FAQs

CSS FAQ — Basic to Advanced

CSS

60 Questions & Answers — Basic to Advanced, with Code Examples

What is CSS

CSS can be linked externally (a separate .css file via <link>), embedded internally (a <style> block in <head>), or written inline directly on an element’s style attribute. External stylesheets are preferred — they are cached by the browser and keep HTML clean.

html
<!-- 1. External (preferred) -->
<link rel="stylesheet" href="styles.css" />

<!-- 2. Internal -->
<style>
  h1 { color: navy; }
</style>

<!-- 3. Inline (highest specificity, hardest to override) -->
<h1 style="color: navy; font-size: 2rem;">Hello</h1>

The cascade resolves conflicts in this order: origin & importance (user-agent → author → author !important) → specificity (inline > ID > class/attr/pseudo-class > element) → source order (later rule wins). Understanding this chain prevents debugging by trial-and-error.

css
/* specificity: 0,0,1 — element selector */
p { color: black; }

/* specificity: 0,1,0 — class selector wins over element */
.intro { color: steelblue; }

/* specificity: 1,0,0 — ID always beats class */
#hero { color: crimson; }

/* !important overrides all — use as last resort */
p { color: hotpink !important; }

Text-related properties (color, font-size, line-height, font-family) inherit from parent to child automatically. Layout properties (margin, padding, border, width, background) do not inherit. Use inherit, initial, or unset to manually control the inherited value.

css
body {
  font-family: 'Inter', sans-serif; /* inherited by all descendants */
  color: #1E1B4B;                   /* inherited */
  background: #fafafa;              /* NOT inherited */
  padding: 1rem;                    /* NOT inherited */
}

/* Force inheritance on a non-inheriting property */
.card { border: inherit; }

/* Reset to browser default */
.reset { all: unset; }

px is fixed — always the same physical size regardless of context. rem is relative to the root <html> font size (typically 16px), making global scaling easy. em compounds from the nearest ancestor’s font size. % is relative to the parent’s dimension. Responsive designs prefer rem and % so text scales with user preferences.

css
html { font-size: 16px; }       /* 1rem = 16px */

h1   { font-size: 2rem;  }      /* 32px — scales if user changes root */
p    { font-size: 1rem;  }      /* 16px */
.btn { padding: .5em 1em; }     /* relative to the button's own font-size */

.container { width: 90%;        /* relative to parent width */
             max-width: 1200px; }

/* Common trick: 62.5% base makes 1rem = 10px */
html { font-size: 62.5%; }      /* 1rem = 10px, 1.6rem = 16px */

Each browser ships with a built-in user-agent stylesheet that adds margins, padding, and font sizes to elements differently. A reset (e.g., Eric Meyer’s) zeroes everything out. A normalizer (e.g., normalize.css) preserves useful defaults while harmonizing inconsistencies. Modern CSS includes a “cascade layer” approach to achieve this without third-party files.

css
/* Modern minimal reset */
*, *::before, *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

img, video { max-width: 100%; display: block; }

body {
  min-height: 100vh;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}

/* Remove list bullets only when list is in a nav */
nav ul { list-style: none; }

The browser parses HTML into a DOM tree and CSS into a CSSOM tree, then merges them into a Render Tree. Layout (Reflow) calculates each element’s size and position. Paint converts layout to pixel layers. Composite combines layers on the GPU. Triggering layout is expensive — prefer transform and opacity animations that only hit the composite stage.

css
/* Triggers Layout + Paint + Composite — expensive */
.bad-anim { transition: width 0.3s, margin 0.3s; }

/* Triggers Composite only — smooth 60fps */
.good-anim { transition: transform 0.3s, opacity 0.3s; }

/* Moving a box 200px right: */
.bad  { margin-left: 200px; }   /* layout recalculation */
.good { transform: translateX(200px); } /* GPU only */
Selectors & Specificity

Specificity is a three-column score (A, B, C): A = number of ID selectors, B = classes/attributes/pseudo-classes, C = elements/pseudo-elements. Inline styles trump all with an implicit (1,0,0,0). Columns never carry over — 11 classes do not beat 1 ID.

css
/* (0,0,1) */ p { }
/* (0,1,0) */ .card { }
/* (0,1,1) */ .card p { }
/* (0,2,1) */ .nav .link p { }
/* (1,0,0) */ #hero { }
/* (1,1,1) */ #hero .title span { }

/* :where() contributes 0 specificity */
/* (0,0,0) */ :where(.card) p { }

/* :is() takes specificity of its most specific argument */
/* (1,0,0) */ :is(#hero, .card) { }

The descendant (space) matches any nested level. Child (>) matches only direct children. Adjacent sibling (+) matches the immediately following sibling. General sibling (~) matches all following siblings. Precise combinators reduce the need for extra classes.

css
/* Descendant — any depth */
.nav a { text-decoration: none; }

/* Child — only direct children */
.list > li { padding: .5rem 0; }

/* Adjacent sibling — h2 immediately after h1 */
h1 + h2 { margin-top: .25rem; font-size: 1.1rem; color: gray; }

/* General sibling — all .error after .field */
.field ~ .error { display: block; color: red; }

:nth-child(An+B) matches elements at calculated positions — 2n for even, 2n+1 for odd, 3n+1 for every third starting at 1. :not() excludes a selector. Combined, they eliminate class spam in HTML like class="odd" or class="last".

css
/* Zebra-stripe a table */
tr:nth-child(even)  { background: #f8f8ff; }
tr:nth-child(odd)   { background: #fff; }

/* Every third card starting at the first */
.card:nth-child(3n+1) { border-left: 4px solid purple; }

/* All links except those in the footer */
a:not(footer a) { color: royalblue; }

/* Last item — no bottom border */
li:not(:last-child) { border-bottom: 1px solid #eee; }

Pseudo-elements are generated content that exist in the render tree but not in the DOM — they can’t be selected with JavaScript. They require content: "" to render. Used for decorative elements (icons, underline effects, quotation marks) that don’t belong in HTML semantics.

css
/* Animated underline on hover */
.link { position: relative; text-decoration: none; }
.link::after {
  content: "";
  position: absolute;
  bottom: -2px; left: 0;
  width: 0; height: 2px;
  background: royalblue;
  transition: width .3s ease;
}
.link:hover::after { width: 100%; }

/* Quote decoration */
blockquote::before { content: "\201C"; font-size: 4rem; color: #ddd; }

/* Clearfix (legacy float clearing) */
.clearfix::after { content: ""; display: table; clear: both; }

:is() accepts a selector list and matches any element that matches one of the arguments, eliminating duplicate rules. Its specificity equals the most specific argument in the list — useful but something to track. :where() is the zero-specificity version for base styles.

css
/* Old way — repeated */
h1 a, h2 a, h3 a, h4 a { color: inherit; text-decoration: none; }

/* With :is() — one rule, same result */
:is(h1, h2, h3, h4) a { color: inherit; text-decoration: none; }

/* :is() with nested scoping */
:is(article, section) :is(h2, h3) { font-weight: 600; }

/* :where() — same match, zero specificity (great for resets) */
:where(ul, ol) { list-style: none; padding: 0; }

:has() matches an element if it contains a matching descendant — effectively a “parent selector.” Previously impossible in CSS, it enables conditional styling based on a child’s presence or state, eliminating many JS-toggled class hacks.

css
/* Card that contains an image gets different padding */
.card:has(img) { padding: 0; }
.card:not(:has(img)) { padding: 1.5rem; }

/* Form row that contains an invalid input turns red */
.form-row:has(:invalid) { background: #fff5f5; }

/* Nav with open dropdown gets overlay */
nav:has(.dropdown[open])::after {
  content: "";
  position: fixed; inset: 0;
  background: rgba(0,0,0,.3);
  z-index: 10;
}

Attribute selectors match elements based on the presence or value of an attribute, with pattern-matching options for prefix, suffix, and substring. They are powerful for styling links, inputs, and data attributes without adding classes.

css
[disabled]            { opacity: .5; }           /* has attribute */
[type="email"]        { background: #f0f8ff; }   /* exact value */
[href^="https"]       { color: green; }           /* starts with */
[href$=".pdf"]::after { content: " (PDF)"; }     /* ends with */
[href*="github"]      { font-weight: bold; }     /* contains */

/* Data attribute styling */
[data-theme="dark"]  { background: #1a1a1a; color: #fff; }

/* Input types without classes */
input[type="range"]  { accent-color: purple; }

!important jumps a declaration to the highest origin level, beating inline styles and ID selectors. Its presence creates a parallel specificity war — the most specific !important wins among all !important declarations. Justified only for utility classes (overriding third-party styles), accessibility overrides, or user stylesheets.

css
/* Utility class — must always apply regardless of component specificity */
.visually-hidden {
  position: absolute !important;
  width: 1px !important;
  height: 1px !important;
  overflow: hidden !important;
  clip: rect(0,0,0,0) !important;
  white-space: nowrap !important;
}

/* DON'T use it to "fix" specificity battles — fix the selector instead */
/* BAD: */ #sidebar .widget .title { color: red !important; }
/* GOOD: */ [data-widget-title] { color: red; }
Box Model & Display

Every element is a box composed of: contentpaddingbordermargin. In the default content-box model, width sets only the content area — padding and border are added on top. In border-box, width includes padding and border, making layout math straightforward.

css
/* content-box (default): total width = 300 + 40 + 4 = 344px */
.default {
  box-sizing: content-box;
  width: 300px;
  padding: 20px;    /* adds 40px */
  border: 2px solid; /* adds 4px */
}

/* border-box: total width = exactly 300px */
.better {
  box-sizing: border-box;
  width: 300px;
  padding: 20px;    /* included in 300px */
  border: 2px solid; /* included in 300px */
}

With border-box, setting width: 50% on two sibling elements always adds up to 100% regardless of padding or border. Without it, padding and border push the total beyond 100%, causing wrapping. Nearly every modern CSS reset applies border-box universally.

css
/* Universal border-box reset */
*, *::before, *::after { box-sizing: border-box; }

/* Now this works perfectly — two columns always 100% */
.col-left, .col-right {
  width: 50%;
  padding: 1rem;   /* included in 50%, no overflow */
  float: left;
}

/* content-box would break: 50% + padding*2 + border*2 > 100% */

Adjacent vertical margins between block elements merge into a single margin equal to the largest one. Collapsing is prevented when: elements are inside a flex or grid container, there is padding or border between parent and child, or the parent triggers a block formatting context (BFC) via overflow, display: flow-root, etc.

css
/* These collapse — the gap between them is 24px, not 40px */
h2 { margin-bottom: 24px; }
p  { margin-top: 16px; }

/* Prevent parent-child collapse */
.parent {
  overflow: hidden;   /* creates BFC — child margin stays inside */
  /* OR: padding-top: 1px; */
  /* OR: display: flow-root; */
}

/* No collapsing inside flex/grid containers */
.flex-parent { display: flex; flex-direction: column; }

block elements take up the full line width and stack vertically. inline elements flow with text — width, height, and vertical margin have no effect. inline-block sits in line like inline but respects width, height, and all margins — it is the classic “inline with sizing” hack before flexbox.

css
/* block — full width, starts new line */
div { display: block; }

/* inline — content width only, ignores width/height */
span { display: inline; }

/* inline-block — flows in line, but respects sizing */
.badge {
  display: inline-block;
  width: 24px;
  height: 24px;
  line-height: 24px;
  border-radius: 50%;
  text-align: center;
}

/* Modern: flex/grid replaced most inline-block use cases */

display: none removes the element from layout entirely — no space reserved. visibility: hidden hides the element but keeps its space. opacity: 0 makes it invisible but keeps space and still receives pointer events. Choosing the wrong one causes layout shifts or invisible but clickable elements.

css
.gone    { display: none; }          /* removed from flow, not accessible */
.ghost   { visibility: hidden; }    /* space preserved, not accessible */
.glass   { opacity: 0; }            /* invisible but STILL clickable! */

/* Accessible hide (screen reader visible, visually hidden) */
.sr-only {
  position: absolute;
  width: 1px; height: 1px;
  overflow: hidden;
  clip: rect(0,0,0,0);
  white-space: nowrap;
}

/* Animate to/from visible — can't transition display:none */
.fade { opacity: 0; transition: opacity .3s; visibility: hidden; }
.fade.show { opacity: 1; visibility: visible; }

overflow: visible (default) lets content spill out. hidden clips it. scroll always shows scrollbars. auto shows scrollbars only when needed. Any value other than visible creates a Block Formatting Context, which is why overflow: hidden clears floats and prevents margin collapse.

css
/* Truncate long text with ellipsis */
.truncate {
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
  max-width: 200px;
}

/* Scrollable panel */
.panel { overflow-y: auto; max-height: 400px; }

/* Clip image without hiding overflow on x-axis */
.hero { overflow: clip; }   /* newer: doesn't create scroll container */

/* Horizontal scroll for a code block */
pre { overflow-x: auto; }
Positioning & Stacking

position: relative with no offset applied leaves the element exactly in its normal-flow position but makes it a positioned ancestor. Any absolutely-positioned descendant will anchor to this element instead of scrolling up the DOM. This is the pattern behind every modal, tooltip, and dropdown overlay.

css
/* Parent — relative creates the anchor point */
.card {
  position: relative; /* no offset — stays in place */
}

/* Child — absolute positions relative to .card */
.badge {
  position: absolute;
  top: -8px;
  right: -8px;
  /* anchors to .card corner, not the viewport */
}

/* Without relative on .card, .badge would anchor to
   the nearest positioned ancestor or the viewport */

An absolutely-positioned element is removed from the normal flow — siblings act as if it doesn’t exist. It anchors to the nearest ancestor with a non-static position. If none exists it anchors to the initial containing block (the viewport). inset: 0 is a shorthand for covering the full containing block.

css
/* Overlay that fills its relative parent */
.overlay {
  position: absolute;
  inset: 0;  /* top: 0; right: 0; bottom: 0; left: 0 */
  background: rgba(0,0,0,.5);
}

/* Center inside relative parent */
.centered {
  position: absolute;
  top: 50%; left: 50%;
  transform: translate(-50%, -50%);
}

/* Fixed — anchors to viewport, ignores scroll */
.sticky-nav { position: fixed; top: 0; left: 0; width: 100%; }

A sticky element scrolls normally until it hits the threshold (e.g., top: 0), then sticks to that offset until its parent container scrolls out of view. It must have a scroll container ancestor and the parent must be taller than the sticky element — otherwise it appears not to stick.

css
/* Sticky table header */
thead th {
  position: sticky;
  top: 0;
  background: white;  /* must have background or content shows through */
  z-index: 1;
}

/* Sticky sidebar — sticks until its parent ends */
.sidebar {
  position: sticky;
  top: 1rem;
  align-self: start;   /* critical in flex/grid: prevents stretching */
}

/* Common bug: parent has overflow: hidden → sticky won't work */

z-index only works on positioned elements (non-static). More importantly, z-index comparisons happen within the same stacking context. An element with z-index: 9999 inside a stacking context that itself has a low z-index cannot appear above elements outside that context — no matter how high the number.

css
/* Parent creates stacking context — child is trapped inside it */
.parent {
  position: relative;
  z-index: 1;       /* creates stacking context */
  transform: translateZ(0); /* also creates stacking context! */
}

.child {
  position: absolute;
  z-index: 9999;    /* only competes within .parent's context */
}

/* Modal fix — move modal to body level or ensure ancestor z-index is high */
.modal-backdrop {
  position: fixed;
  inset: 0;
  z-index: 1000;
}

Stacking contexts are created by many properties beyond position + z-index: opacity < 1, transform, filter, will-change, isolation: isolate, contain: layout/paint, and more. This is why adding a CSS animation to a parent can unexpectedly trap a child modal behind other elements.

css
/* All of these create a stacking context: */
.a { opacity: 0.99; }                /* < 1 triggers it */
.b { transform: translateX(0); }     /* any transform */
.c { filter: blur(0px); }            /* any filter */
.d { will-change: transform; }
.e { isolation: isolate; }           /* explicit, clean */
.f { position: fixed; }

/* Use isolation: isolate intentionally to contain a component's z-index: */
.dialog-host { isolation: isolate; } /* internal z-index won't bleed out */

Horizontal centering a block: margin: 0 auto. Centering inline/text: text-align: center on the parent. Centering in a flex container: justify-content + align-items: center. Centering absolute elements: top: 50%; left: 50%; transform: translate(-50%,-50%). CSS 2024: align-content: center on a block container.

css
/* Block centering */
.box { width: 600px; margin: 0 auto; }

/* Flex centering — most common modern approach */
.parent {
  display: flex;
  justify-content: center;  /* horizontal */
  align-items: center;       /* vertical */
  min-height: 100vh;
}

/* Absolute centering */
.modal {
  position: absolute;
  top: 50%; left: 50%;
  transform: translate(-50%, -50%);
}

/* 2024: single property centering */
body { display: grid; place-items: center; min-height: 100vh; }
Flexbox

Flex turns direct children into flex items that are laid out along a main axis. Items shrink to content size by default (no longer block-width). The container distributes available space with justify-content (main axis) and aligns items with align-items (cross axis). Flex is one-dimensional — it manages a single row or column.

css
.nav {
  display: flex;
  align-items: center;        /* vertically center all items */
  gap: 1.5rem;                /* spacing without margins */
}

.nav .logo { margin-right: auto; } /* pushes rest to far right */

/* Key default behaviors:
   flex-direction: row      — horizontal main axis
   flex-wrap: nowrap        — single line
   align-items: stretch     — items fill cross-axis height
   flex-shrink: 1           — items shrink if needed */

flex-grow sets a item’s share of the remaining space after all items reach their flex-basis. A value of 1 on all items distributes leftover space equally. If one item has flex-grow: 2 and others have 1, it gets twice as much of the leftover space — not twice the total width.

css
/* Sidebar + main: sidebar fixed, main takes all remaining space */
.layout { display: flex; gap: 1rem; }
.sidebar { width: 240px; flex-shrink: 0; }   /* won't shrink */
.main    { flex-grow: 1; }                    /* takes all leftover */

/* Equal columns regardless of content */
.cols > * { flex: 1; }  /* flex-grow:1, flex-shrink:1, flex-basis:0 */

/* 2:1 ratio — first column gets twice the free space */
.wide  { flex-grow: 2; }
.narrow{ flex-grow: 1; }

When the total flex-basis of items exceeds the container, items shrink proportionally to their flex-shrink value multiplied by their flex-basis. An item with flex-shrink: 0 never shrinks. Setting it to 0 on icons and logos prevents them from squashing on small screens.

css
.toolbar { display: flex; gap: .5rem; }

/* Icon — never shrinks */
.toolbar .icon { flex-shrink: 0; width: 24px; height: 24px; }

/* Search input — shrinks freely */
.toolbar .search { flex-shrink: 1; flex-grow: 1; min-width: 0; }
/* min-width: 0 is critical — flex items default min-width is auto,
   which prevents shrinking below content size */

/* Label — shrinks twice as fast as the search bar */
.toolbar .label { flex-shrink: 2; }

justify-content controls distribution along the main axis (horizontal in row, vertical in column). align-items aligns items on the cross axis. In Grid, both properties control the entire row/column tracks. place-items is the shorthand for both.

css
/* Row flex — justify = horizontal, align = vertical */
.row { display: flex; justify-content: space-between; align-items: center; }

/* Column flex — axes flip */
.col { display: flex; flex-direction: column;
       justify-content: center;   /* now vertical */
       align-items: flex-start; } /* now horizontal */

/* Grid centering shorthand */
.centered-grid { display: grid; place-items: center; }

/* Values: flex-start | flex-end | center | space-between |
           space-around | space-evenly | stretch | baseline */
CSS Grid

grid-template-columns sets the number and size of columns. The fr (fraction) unit divides available space after fixed and percentage widths are placed — like flex-grow but for the entire row at once. 1fr 1fr 1fr is three equal columns; 250px 1fr is a fixed sidebar with a flexible main area.

css
/* Three equal columns */
.grid { display: grid; grid-template-columns: 1fr 1fr 1fr; }

/* Shorthand with repeat() */
.grid { grid-template-columns: repeat(3, 1fr); }

/* Fixed sidebar + flexible main */
.layout { grid-template-columns: 240px 1fr; }

/* Fixed sidebar + flexible main + fixed sidebar */
.three-col { grid-template-columns: 200px 1fr 200px; }

/* Mixed: auto sizes to content */
.mixed { grid-template-columns: auto 1fr auto; }

minmax(min, max) sets a size range for a grid track. The track grows to max when space is available and shrinks to min but never below it. Combining with auto-fill or auto-fit creates responsive grids that reflow without media queries.

css
/* Card grid: as many 250px+ columns as fit, up to 1fr */
.cards {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
  gap: 1.5rem;
}
/* auto-fill: keeps empty tracks  */
/* auto-fit: collapses empty tracks, remaining grow to fill */

/* Row heights: at least 100px, up to content */
.grid { grid-auto-rows: minmax(100px, auto); }

Items are placed on named or numbered grid lines. grid-column: 1 / 3 spans from line 1 to line 3 (two columns). span N is a shorthand meaning “span N tracks from current position.” Negative line numbers count from the end — 1 / -1 spans the full row.

css
.grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 1rem; }

/* Spans columns 1–3 (3 tracks wide) */
.hero     { grid-column: 1 / 4; }

/* Shorthand with span */
.featured { grid-column: span 2; grid-row: span 2; }

/* Full-width banner — works regardless of column count */
.banner   { grid-column: 1 / -1; }

/* Named lines: grid-template-columns: [start] 1fr [mid] 1fr [end] */
.sidebar  { grid-column: start / mid; }

Named areas let you draw the layout as ASCII art in CSS. Each string represents a row; identical names merge into one area. Items are placed by grid-area: name. This produces highly readable layout code and makes responsive reflow trivial — just redefine the areas at a breakpoint.

css
.page {
  display: grid;
  grid-template-columns: 220px 1fr;
  grid-template-rows: auto 1fr auto;
  grid-template-areas:
    "header  header"
    "sidebar main  "
    "footer  footer";
  min-height: 100vh;
}

header  { grid-area: header;  }
.sidebar{ grid-area: sidebar; }
main    { grid-area: main;    }
footer  { grid-area: footer;  }

/* Responsive: single column on mobile */
@media (max-width: 768px) {
  .page { grid-template-columns: 1fr;
          grid-template-areas: "header" "main" "sidebar" "footer"; }
}

Without subgrid, a grid item that itself has display: grid creates an independent grid — its children cannot align to the outer grid’s lines. subgrid inherits the parent’s track definitions so nested content aligns perfectly across cards or columns.

css
/* Parent grid with 3 rows: image, title, description */
.card-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-template-rows: auto auto 1fr;
  gap: 1rem;
}

/* Card spans all 3 parent rows, subgrid inherits them */
.card {
  display: grid;
  grid-row: span 3;
  grid-template-rows: subgrid;  /* inherits parent row sizes */
}

/* Now all card images/titles/descriptions align across columns */
.card img  { grid-row: 1; }
.card h3   { grid-row: 2; }
.card p    { grid-row: 3; align-self: start; }

Grid is two-dimensional (rows and columns simultaneously) — ideal for page-level layouts and card grids. Flexbox is one-dimensional — ideal for nav bars, toolbars, and component-level alignment. They complement each other: use Grid for the outer page shell and Flexbox for the internals of each component inside grid cells.

css
/* Grid: page layout (2D) */
.page { display: grid; grid-template-areas: "nav" "main" "footer"; }

/* Flex: nav internals (1D row) */
nav { display: flex; justify-content: space-between; align-items: center; }

/* Grid: card grid */
.cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); }

/* Flex: inside each card (1D column) */
.card { display: flex; flex-direction: column; }
.card .body { flex-grow: 1; }   /* push footer to bottom */
Responsive Design

Media queries apply CSS rules conditionally based on viewport characteristics. @media (width), (orientation), (prefers-color-scheme), and (hover) are all valid conditions. Multiple conditions combine with and. Modern syntax uses @media (width >= 768px) instead of min-width.

css
/* Mobile first — base styles apply to all; override upward */
.grid { grid-template-columns: 1fr; }

@media (min-width: 640px)  { .grid { grid-template-columns: 1fr 1fr; } }
@media (min-width: 1024px) { .grid { grid-template-columns: repeat(3, 1fr); } }

/* Modern range syntax */
@media (768px <= width < 1024px) { .sidebar { display: none; } }

/* Dark mode */
@media (prefers-color-scheme: dark) {
  :root { --bg: #1a1a2e; --text: #e0e0e0; }
}

Mobile-first writes base styles for small screens and adds overrides with min-width media queries as screens grow. Desktop-first does the opposite with max-width. Mobile-first is better: base CSS loads on all devices (small files for mobile), progressive enhancement, and aligns with how browsers parse stylesheets.

css
/* ✅ Mobile-first: starts simple, adds complexity */
.hero { font-size: 1.5rem; padding: 2rem 1rem; }

@media (min-width: 768px) {
  .hero { font-size: 2.5rem; padding: 5rem 2rem; }
}

/* ❌ Desktop-first: starts complex, subtracts (harder to maintain) */
.hero { font-size: 2.5rem; padding: 5rem 2rem; }

@media (max-width: 767px) {
  .hero { font-size: 1.5rem; padding: 2rem 1rem; }
}

vw/vh are relative to the viewport, not the parent — 50vw is always half the screen width regardless of nesting. 100vh causes the mobile address-bar bug (scrolls behind the bar). dvh (dynamic viewport height) adjusts as the browser bar hides/shows — the modern fix.

css
/* Full-height hero — old way, buggy on mobile */
.hero { height: 100vh; }

/* Full-height hero — correct on mobile (2022+) */
.hero { height: 100dvh; }

/* Fluid font based on viewport width */
h1 { font-size: 5vw; }   /* scales with viewport */

/* Prevent overflow on full-width elements */
.full { width: 100%; }     /* not 100vw — 100vw includes scrollbar width */

/* Useful for side-drawer width */
.drawer { width: min(320px, 80vw); }

clamp(min, preferred, max) sets a value that scales fluidly between a minimum and maximum. For typography, clamp(1rem, 2.5vw, 2rem) means: never smaller than 1rem, never larger than 2rem, scales proportionally in between. No breakpoints needed — the font just adapts.

css
/* Fluid type scale — no media queries */
h1 { font-size: clamp(1.75rem, 5vw, 3.5rem); }
h2 { font-size: clamp(1.35rem, 3.5vw, 2.5rem); }
p  { font-size: clamp(1rem, 1.5vw, 1.125rem); }

/* Fluid spacing */
.section { padding: clamp(2rem, 8vw, 6rem) clamp(1rem, 5vw, 3rem); }

/* Fluid max-width container */
.container { width: min(1200px, 90vw); margin: 0 auto; }

Container queries apply styles based on the size of a parent container, not the viewport. A card component can display in two columns when its container is wide and one column when narrow — regardless of screen size. This makes components truly reusable across different layout contexts.

css
/* Define the container */
.card-wrapper {
  container-type: inline-size;
  container-name: card;
}

/* Style based on container width, not viewport */
.card { display: block; }

@container card (min-width: 400px) {
  .card {
    display: flex;
    flex-direction: row;
    gap: 1rem;
  }
  .card img { width: 160px; flex-shrink: 0; }
}

Without the viewport meta tag, mobile browsers render at a virtual width (~980px) then scale down — CSS media queries see 980px, not the real device width. The tag tells the browser to use the actual device pixel width as the viewport width, making responsive CSS work as intended.

html
<!-- Always include this in <head> -->
<meta name="viewport" content="width=device-width, initial-scale=1.0" />

<!-- width=device-width: use actual screen width, not 980px virtual -->
<!-- initial-scale=1.0: don't zoom in/out on load -->

<!-- DO NOT add maximum-scale=1.0 — blocks user zoom (accessibility violation) -->
<!-- BAD: content="width=device-width, initial-scale=1, maximum-scale=1" -->
Transitions & Animations

transition interpolates a CSS property from its old value to its new value over a duration when a state changes (hover, focus, class toggle). It is defined on the default state — this ensures the animation plays both ways (in and out). Defining it only on :hover means only the hover-in animates.

css
/* Button — transition on default state plays both ways */
.btn {
  background: royalblue;
  transform: scale(1);
  transition: background .2s ease, transform .15s ease, box-shadow .2s ease;
}
.btn:hover {
  background: #1a56db;
  transform: scale(1.03);
  box-shadow: 0 4px 16px rgba(0,0,0,.2);
}

/* Multiple transitions shorthand */
.card { transition: all .3s ease; } /* convenient but may animate unintended properties */

Keyframes define intermediate states at percentage positions in the animation timeline. You can describe 0% → 50% → 100% with completely different values, creating non-linear effects like a bounce, pulse, or shake. The animation runs independently of DOM state changes.

css
/* Skeleton loading shimmer */
@keyframes shimmer {
  0%   { background-position: -200% 0; }
  100% { background-position:  200% 0; }
}

.skeleton {
  background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
  background-size: 200% 100%;
  animation: shimmer 1.4s infinite ease-in-out;
}

/* Bounce */
@keyframes bounce {
  0%, 100% { transform: translateY(0); }
  50%       { transform: translateY(-20px); }
}
.icon { animation: bounce 1s ease infinite; }

Timing functions control the rate of change over time: ease (slow-fast-slow), ease-in (slow start), ease-out (slow end), linear (constant). cubic-bezier(x1,y1,x2,y2) defines a custom curve — used by designers to match physical motion (material design uses cubic-bezier(0.4,0,0.2,1)).

css
/* Built-in */
.slide { transition: transform .3s ease-out; }        /* decelerates — feels natural */
.fade  { transition: opacity  .2s ease-in;  }         /* accelerates — good for exit */

/* Material Design standard easing */
.md    { transition: all .3s cubic-bezier(0.4, 0, 0.2, 1); }

/* Spring-like overshoot */
.spring { transition: transform .4s cubic-bezier(0.34, 1.56, 0.64, 1); }

/* CSS spring() — upcoming native API */
/* .future { transition: transform spring(1 200 20 0); } */

/* steps() for sprite animations */
.sprite { animation: walk 0.6s steps(6) infinite; }

transform and opacity are handled entirely on the compositor thread using the GPU — no layout or paint recalculation occurs. Animating width, height, top, or margin triggers a full Layout (reflow) every frame, blocking the main thread and dropping frames on complex pages.

css
/* ❌ Triggers layout every frame — jank on busy pages */
.bad  { transition: width .3s, margin-left .3s; }

/* ✅ GPU composited — always smooth */
.good { transition: transform .3s, opacity .3s; }

/* Slide in from left using transform, not margin */
@keyframes slideIn {
  from { transform: translateX(-100%); opacity: 0; }
  to   { transform: translateX(0);     opacity: 1; }
}

/* Promote to own layer proactively (use sparingly) */
.heavy-anim { will-change: transform; }

Some users have vestibular disorders or epilepsy that make motion harmful. The OS-level "reduce motion" setting triggers the prefers-reduced-motion: reduce media query. CSS animations should be disabled or replaced with instant transitions when this preference is active.

css
/* Global animation kill-switch */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
    scroll-behavior: auto !important;
  }
}

/* Or: keep subtle animations, remove motion-heavy ones */
@media (prefers-reduced-motion: reduce) {
  .parallax     { transform: none !important; }
  .auto-scroll  { animation: none; }
  .fade-in      { opacity: 1; transition: none; } /* instant instead of fade */
}

will-change tells the browser that a property will animate, allowing it to promote the element to a GPU compositing layer before the animation starts. Without it, promotion happens mid-animation causing a visual flash. Overuse creates memory pressure — apply it only to elements that actually animate.

css
/* Promote only when animation is imminent */
.menu { transition: transform .3s; }
.menu:hover,
.menu.open { will-change: transform; }   /* hint on state, not always */

/* Remove will-change after animation ends (via JS) */
.animated {
  will-change: transform;
  animation: slideIn .4s forwards;
}
/* After animation: element.style.willChange = 'auto'; */

/* Don't do this — promotes EVERY element */
* { will-change: transform; }  /* ❌ wastes GPU memory */
Modern CSS Features

Sass variables are compiled away at build time — they produce static CSS. CSS custom properties live in the browser and participate in the cascade and inheritance tree. They can be updated at runtime with JavaScript, overridden in media queries, scoped to a component, and read or written dynamically — none of which Sass variables can do.

css
/* Define in :root for global scope */
:root {
  --color-primary: #7B2FBE;
  --spacing-md: 1rem;
}

/* Use anywhere */
.btn { background: var(--color-primary); padding: var(--spacing-md); }

/* Override at component scope */
.danger-zone { --color-primary: #dc2626; }

/* Override in media query — impossible with Sass variables */
@media (prefers-color-scheme: dark) {
  :root { --color-primary: #a855f7; }
}

/* Read/write from JavaScript */
/* document.documentElement.style.setProperty('--spacing-md', '1.5rem'); */

calc() mixes units (e.g., 100% - 2rem). min() picks the smallest value — useful for max-width constraints. max() picks the largest — useful for ensuring a minimum. clamp(min, val, max) bounds a value between a floor and ceiling. Together they replace many media query breakpoints.

css
/* calc — mix units */
.sidebar { width: calc(300px - 2rem); }
.full    { height: calc(100vh - 64px); } /* 64px = fixed header */

/* min — never wider than 600px */
.card { width: min(600px, 100%); }

/* max — never smaller than 200px */
.col { width: max(200px, 33%); }

/* clamp — fluid font: min 1rem, prefer 4vw, max 2rem */
h1 { font-size: clamp(1rem, 4vw, 2rem); }

/* Combine with custom properties */
:root { --gutter: clamp(1rem, 3vw, 2rem); }
.grid { gap: var(--gutter); }

Cascade layers allow you to explicitly define the priority order of groups of rules. A rule in a higher-priority layer wins over a lower-priority layer regardless of specificity. This solves the "specificity war" problem with third-party CSS — you can slot framework styles into a low-priority layer and your overrides into a higher one.

css
/* Declare order: reset < base < components < utilities */
@layer reset, base, components, utilities;

@layer reset {
  * { margin: 0; padding: 0; box-sizing: border-box; }
}

@layer base {
  body { font-family: sans-serif; }
  a { color: royalblue; }
}

@layer components {
  .btn { padding: .5rem 1rem; border-radius: 6px; }
}

@layer utilities {
  /* These win regardless of component specificity */
  .mt-4 { margin-top: 1rem !important; }
}

Native CSS nesting (supported in all modern browsers since 2023) allows child selectors to be written inside parent rule blocks. The & symbol references the parent selector. Unlike Sass, native nesting is dynamic — nested rules participate in the living cascade rather than being compiled to flat CSS.

css
/* Native CSS nesting */
.card {
  background: white;
  border-radius: 12px;

  & h2 { font-size: 1.25rem; margin-bottom: .5rem; }

  & p  { color: #64748B; line-height: 1.7; }

  &:hover { box-shadow: 0 4px 20px rgba(0,0,0,.1); }

  & .badge { /* descendant .badge inside .card */
    display: inline-block;
    padding: .25rem .5rem;
    background: #EDE9FE;
  }

  @media (max-width: 600px) {
    padding: 1rem;  /* media queries can nest too */
  }
}

Physical properties (margin-left, padding-top) are fixed to physical directions. Logical properties map to the flow direction: inline is horizontal in LTR/RTL, block is vertical. In an RTL language, margin-inline-start automatically becomes right-side margin — zero extra CSS for RTL support.

css
/* Physical (breaks RTL) */
.old { margin-left: 1rem; padding-top: 2rem; border-right: 1px solid; }

/* Logical (works in any writing mode) */
.new {
  margin-inline-start: 1rem;  /* left in LTR, right in RTL */
  padding-block-start: 2rem;  /* top in horizontal, left in vertical */
  border-inline-end: 1px solid;
}

/* Shorthand */
.box {
  margin-inline: auto;        /* center horizontally in any mode */
  padding-block: 2rem;        /* top + bottom */
  inset-inline: 0;            /* left: 0; right: 0 */
}

oklch is a perceptually uniform color space — changing the lightness channel produces visually equal steps, unlike hsl where "50% lightness" looks different per hue. color-mix() blends two colors natively without Sass. Together they enable dynamic, mathematically consistent design systems.

css
:root {
  /* oklch(lightness chroma hue) */
  --brand: oklch(55% 0.2 280);           /* purple */
  --brand-light: oklch(90% 0.1 280);     /* visually balanced tint */
  --brand-dark:  oklch(35% 0.2 280);     /* visually balanced shade */
}

/* color-mix: 20% white into the brand color */
.tint { background: color-mix(in oklch, var(--brand) 80%, white); }

/* Automatic dark mode via relative color syntax */
.hover { background: oklch(from var(--brand) calc(l - 0.1) c h); }

BEM (Block__Element--Modifier) structures class names so every selector has specificity of exactly one class (0,1,0). There are no nested selectors, no IDs, no tag selectors in component rules. Components are completely portable — moving them to a different part of the DOM changes nothing because styles don't depend on context.

css
/* BEM: Block__Element--Modifier */

/* Block */
.card { background: white; border-radius: 12px; }

/* Element — belongs to the block */
.card__image { width: 100%; aspect-ratio: 16/9; object-fit: cover; }
.card__title { font-size: 1.2rem; font-weight: 600; }
.card__body  { padding: 1.25rem; }

/* Modifier — variant of block or element */
.card--featured { border: 2px solid royalblue; }
.card--dark     { background: #1e1b4b; color: white; }
.card__title--large { font-size: 1.75rem; }

contain: layout paint tells the browser that an element's internals don't affect anything outside it — enabling optimized layout recalculation. content-visibility: auto skips rendering off-screen content entirely, cutting initial paint time dramatically on long pages or infinite feeds.

css
/* contain: painting changes inside card don't trigger outer layout */
.card { contain: layout paint; }

/* content-visibility: skip render until near viewport */
.article-preview {
  content-visibility: auto;
  contain-intrinsic-size: 0 300px; /* reserved space to prevent scroll jumps */
}
/* On a 1000-article page, only ~5 visible articles are painted at load —
   page load time drops from ~4s to ~0.4s in real-world benchmarks */

:where() matches elements exactly like :is() but contributes zero specificity. Base styles written with :where() can be overridden by any single-class rule without specificity battles. Design systems use it for default styles that should always lose to component-level overrides.

css
/* Without :where — (0,1,1) specificity, may be hard to override */
.prose a:hover { text-decoration: underline; }

/* With :where — (0,0,0) specificity, anything overrides it */
:where(.prose) a:where(:hover) { text-decoration: underline; }

/* Design system base — zero specificity */
:where(h1, h2, h3, h4, h5, h6) {
  line-height: 1.2;
  font-weight: 700;
}
/* Component can now override with just a class selector (0,1,0) */
.hero-title { line-height: 1; font-weight: 800; }

Scroll snap locks scroll position to defined snap points after the user stops scrolling. The container sets scroll-snap-type; each item sets scroll-snap-align. No JavaScript event listeners, no IntersectionObserver hacks — the browser handles the snapping natively with physics-based deceleration.

css
/* Horizontal carousel */
.carousel {
  display: flex;
  overflow-x: auto;
  scroll-snap-type: x mandatory;   /* snap on horizontal axis, always */
  gap: 1rem;
  scroll-behavior: smooth;
  -ms-overflow-style: none;
  scrollbar-width: none;
}

.carousel-item {
  flex: 0 0 80%;                  /* each item = 80% of container */
  scroll-snap-align: start;
}

/* Full-page vertical scroll */
.page-sections { height: 100vh; overflow-y: scroll; scroll-snap-type: y mandatory; }
.section       { height: 100vh; scroll-snap-align: start; }

Before aspect-ratio, responsive video embeds required a wrapper with padding-top: 56.25% (9/16) and absolute-positioned inner elements. aspect-ratio directly sets the relationship between width and height — the browser calculates the other dimension automatically.

css
/* Old hack — needed a wrapper */
.video-wrapper { position: relative; padding-top: 56.25%; }
.video-wrapper iframe { position: absolute; inset: 0; width: 100%; height: 100%; }

/* Modern — direct */
iframe    { width: 100%; aspect-ratio: 16 / 9; }
img       { width: 100%; aspect-ratio: 4 / 3;  object-fit: cover; }
.square   { width: 100%; aspect-ratio: 1; }
.portrait { width: 200px; aspect-ratio: 3 / 4; }

/* Skeleton placeholder that holds exact space */
.img-placeholder { aspect-ratio: 16/9; background: #eee; }

@scope (CSS 2024) limits where rules apply by defining a root element and an optional lower boundary ("donut hole"). Styles inside the scope only match descendants of the root, preventing leakage without needing a Shadow DOM or deep BEM class hierarchies.

css
/* Scope all rules to inside .card */
@scope (.card) {
  h3   { font-size: 1.2rem; color: navy; }   /* only h3 inside .card */
  p    { line-height: 1.7; }
  a    { color: royalblue; }
}
/* h3 outside .card is unaffected */

/* Donut hole: scope to .card but exclude .card .footer */
@scope (.card) to (.footer) {
  p { color: #444; }  /* won't apply inside .card .footer */
}

/* :scope is the scoping root element */
@scope (.card) {
  :scope { border-radius: 12px; }  /* targets .card itself */
}
Prev
Next
Drag
Map
HTML Snippets Powered By : XYZScripts.com
Select the fields to be shown. Others will be hidden. Drag and drop to rearrange the order.
  • Image
  • SKU
  • Rating
  • Price
  • Stock
  • Availability
  • Add to cart
  • Description
  • Content
  • Weight
  • Dimensions
  • Additional information
Click outside to hide the comparison bar
Compare