← All stacks
HC

HTML & CSS

18 sections · 156 entries

Document Structure

DOCTYPE Declaration

syntax
<!DOCTYPE html>
example
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>My Page</title>
</head>
<body>
  <h1>Welcome</h1>
</body>
</html>

Note Always place this as the very first line. Without it, browsers fall into quirks mode and render inconsistently.

<html> Root Element

syntax
<html lang="language-code"> ... </html>
example
<html lang="en" dir="ltr">
  <!-- entire document goes here -->
</html>

Note The lang attribute is critical for screen readers and search engines. Use BCP 47 codes like "en", "fr", "es", "zh-Hans".

<head> Metadata Container

syntax
<head> ... </head>
example
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Product Dashboard</title>
  <link rel="stylesheet" href="styles.css">
</head>

Note The head element holds metadata, links to stylesheets, and scripts. Nothing inside head renders visually on the page.

Character Encoding

syntax
<meta charset="UTF-8">
example
<head>
  <meta charset="UTF-8">
</head>

Note Must appear within the first 1024 bytes of the document. UTF-8 handles virtually all global characters and emoji.

Viewport Meta Tag

syntax
<meta name="viewport" content="width=device-width, initial-scale=1.0">
example
<meta name="viewport" content="width=device-width, initial-scale=1.0">

Note Without this, mobile browsers render at a desktop width and then shrink the result. Essential for every responsive site.

<body> Content Container

syntax
<body> ... </body>
example
<body>
  <header>Site Header</header>
  <main>Primary Content</main>
  <footer>Site Footer</footer>
</body>

Note Only one body element per document. All visible page content lives here.

Linking a Stylesheet

syntax
<link rel="stylesheet" href="path/to/file.css">
example
<link rel="stylesheet" href="/css/main.css">
<link rel="stylesheet" href="https://fonts.example.com/sans.css" crossorigin>

Note Stylesheets in the head block rendering until they load. For non-critical CSS, consider loading asynchronously.

Adding Scripts

syntax
<script src="file.js" defer></script>
<script type="module" src="app.js"></script>
example
<script src="analytics.js" defer></script>
<script type="module" src="app.js"></script>

Note Use defer for classic scripts so they run after HTML parsing completes. Module scripts are deferred by default. Avoid placing scripts in the head without defer or async.

Text Elements

Headings h1-h6

syntax
<h1>Top Level</h1>
<h2>Sub Level</h2>
...
<h6>Deepest Level</h6>
example
<h1>Annual Report</h1>
<h2>Financial Summary</h2>
<h3>Revenue by Region</h3>

Note Use only one h1 per page for best SEO. Never skip heading levels (e.g., jumping from h1 to h4). Headings create a document outline used by assistive technology.

Paragraphs

syntax
<p>Text content here.</p>
example
<p>Our platform processes over two million transactions daily.</p>
<p>Each transaction is encrypted end-to-end for security.</p>

Note Browsers add default top and bottom margins to paragraphs. Reset or adjust with CSS if needed.

<span> Inline Container

syntax
<span class="name">inline content</span>
example
<p>Status: <span class="status-active">Active</span></p>

Note A span has no semantic meaning. Use it purely for styling a portion of text when no better semantic element exists.

Strong & Emphasis

syntax
<strong>important</strong>
<em>emphasized</em>
example
<p><strong>Warning:</strong> This action <em>cannot</em> be undone.</p>

Note strong conveys urgency or importance (renders bold). em conveys stress emphasis (renders italic). Prefer these over <b> and <i> for meaningful emphasis, since screen readers alter vocal tone for strong/em.

Blockquote

syntax
<blockquote cite="url">
  <p>Quoted text.</p>
</blockquote>
example
<blockquote cite="https://example.com/speech">
  <p>The only limit to our realization of tomorrow is our doubts of today.</p>
</blockquote>

Note The cite attribute provides the source URL but is not displayed by browsers. Add visible attribution separately if needed.

Preformatted Text & Code

syntax
<pre><code>code here</code></pre>
example
<pre><code>function greet(name) {
  return `Hello, ${name}!`;
}</code></pre>

<p>Use <code>Array.map()</code> to transform items.</p>

Note pre preserves all whitespace and line breaks exactly as written. Wrap code inside pre for multi-line blocks. Use code alone for inline code snippets.

Line Break & Horizontal Rule

syntax
<br>
<hr>
example
<p>123 Main Street<br>Suite 400<br>Springfield, IL 62704</p>
<hr>
<p>New section begins here.</p>

Note br is for meaningful line breaks like addresses or poetry, not for spacing. Use CSS margins or padding for spacing. hr represents a thematic shift between content blocks.

Small, Mark, Del, Ins

syntax
<small>fine print</small>
<mark>highlighted</mark>
<del>removed</del>
<ins>added</ins>
example
<p>Price: <del>$49.99</del> <ins>$29.99</ins></p>
<p>Search results for <mark>flexbox</mark></p>
<small>Terms and conditions apply.</small>

Note del and ins are ideal for showing edits or price changes. mark highlights search matches or key phrases. small is for side comments and legal text.

Images & Media

Basic Image

syntax
<img src="url" alt="description" width="w" height="h">
example
<img src="/images/team-photo.jpg" alt="Our engineering team at the 2025 offsite" width="800" height="450">

Note Always provide meaningful alt text for accessibility. Set explicit width and height to prevent layout shift while the image loads.

Figure & Caption

syntax
<figure>
  <img src="url" alt="description">
  <figcaption>Caption text</figcaption>
</figure>
example
<figure>
  <img src="/charts/revenue.png" alt="Bar chart showing 40% revenue growth year over year">
  <figcaption>Revenue growth from 2023 to 2025</figcaption>
</figure>

Note figure is for self-contained content that could be moved to an appendix without losing meaning. The figcaption provides a visible caption, while alt describes the image content itself.

Picture Element (Art Direction)

syntax
<picture>
  <source media="(condition)" srcset="image-url">
  <img src="fallback.jpg" alt="description">
</picture>
example
<picture>
  <source media="(min-width: 1024px)" srcset="/images/hero-wide.webp" type="image/webp">
  <source media="(min-width: 600px)" srcset="/images/hero-medium.webp" type="image/webp">
  <img src="/images/hero-small.jpg" alt="Mountain landscape at sunrise">
</picture>

Note The browser picks the first matching source. Always include the img fallback as the last child. Use picture for art direction (different crops at different sizes) or serving modern formats with fallbacks.

Responsive Images (srcset & sizes)

syntax
<img srcset="small.jpg 480w, medium.jpg 800w, large.jpg 1200w"
     sizes="(max-width: 600px) 100vw, 50vw"
     src="medium.jpg" alt="description">
example
<img
  srcset="/photos/product-480.jpg 480w,
         /photos/product-800.jpg 800w,
         /photos/product-1200.jpg 1200w"
  sizes="(max-width: 640px) 100vw,
         (max-width: 1024px) 50vw,
         33vw"
  src="/photos/product-800.jpg"
  alt="Wireless headphones in midnight blue"
>

Note The w descriptor tells the browser the intrinsic pixel width of each image. sizes tells it how wide the image will display at each breakpoint, so it can pick the best file. This is for resolution switching, not art direction.

Lazy Loading Images

syntax
<img src="url" alt="description" loading="lazy">
example
<img src="/gallery/photo-42.jpg" alt="Sunset over the canyon" loading="lazy" width="600" height="400">

Note loading="lazy" defers loading until the image nears the viewport. Do not lazy-load images that are visible on initial load (above the fold) since it slows their appearance. Use loading="eager" (the default) for hero images.

Video

syntax
<video src="url" controls width="w" height="h"></video>
example
<video controls width="720" height="405" poster="/thumbs/demo.jpg">
  <source src="/videos/demo.webm" type="video/webm">
  <source src="/videos/demo.mp4" type="video/mp4">
  Your browser does not support video playback.
</video>

Note Provide multiple source formats for compatibility. The poster attribute shows a thumbnail before playback. Avoid autoplay with sound since browsers block it and users dislike it.

Audio

syntax
<audio src="url" controls></audio>
example
<audio controls>
  <source src="/audio/podcast-ep12.ogg" type="audio/ogg">
  <source src="/audio/podcast-ep12.mp3" type="audio/mpeg">
  Audio playback is not supported in your browser.
</audio>

Note Like video, provide multiple source formats. The controls attribute gives users play, pause, and volume UI. Without controls, the element is invisible.

Decorative Images (Empty Alt)

syntax
<img src="url" alt="" role="presentation">
example
<img src="/images/decorative-divider.svg" alt="">

Note For purely decorative images that add no information, use an empty alt attribute. Screen readers will skip them entirely. Never omit the alt attribute since that causes readers to announce the file name.

Lists & Tables

Unordered List

syntax
<ul>
  <li>Item</li>
</ul>
example
<ul>
  <li>Set up development environment</li>
  <li>Configure database connection</li>
  <li>Deploy to staging server</li>
</ul>

Note Use unordered lists when the sequence of items does not matter. The default bullet style can be changed or removed with CSS list-style-type.

Ordered List

syntax
<ol start="n" type="1|a|A|i|I">
  <li>Item</li>
</ol>
example
<ol>
  <li>Preheat oven to 375 degrees</li>
  <li>Mix dry ingredients in a large bowl</li>
  <li>Add wet ingredients and stir until smooth</li>
  <li>Bake for 25 minutes</li>
</ol>

Note Use start to begin numbering at a specific value, and reversed attribute for countdown order. The type attribute changes markers (1, a, A, i, I).

Nested Lists

syntax
<ul>
  <li>Parent
    <ul>
      <li>Child</li>
    </ul>
  </li>
</ul>
example
<ul>
  <li>Frontend
    <ul>
      <li>HTML &amp; CSS</li>
      <li>JavaScript</li>
      <li>React</li>
    </ul>
  </li>
  <li>Backend
    <ul>
      <li>Node.js</li>
      <li>Python</li>
    </ul>
  </li>
</ul>

Note The nested list must be placed inside the parent li element, not after it. You can nest ul inside ol and vice versa.

Definition List

syntax
<dl>
  <dt>Term</dt>
  <dd>Definition</dd>
</dl>
example
<dl>
  <dt>Latency</dt>
  <dd>The time delay between a request and its response.</dd>
  <dt>Throughput</dt>
  <dd>The amount of data processed in a given time period.</dd>
</dl>

Note Ideal for glossaries, metadata key-value pairs, and FAQ sections. A single dt can have multiple dd elements for multiple definitions.

Basic Table

syntax
<table>
  <thead>
    <tr><th>Header</th></tr>
  </thead>
  <tbody>
    <tr><td>Data</td></tr>
  </tbody>
</table>
example
<table>
  <thead>
    <tr>
      <th>Product</th>
      <th>Price</th>
      <th>Stock</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Keyboard</td>
      <td>$89</td>
      <td>142</td>
    </tr>
    <tr>
      <td>Mouse</td>
      <td>$45</td>
      <td>308</td>
    </tr>
  </tbody>
</table>

Note Always use thead/tbody for proper structure. Use th for header cells and td for data cells. This helps screen readers navigate the table correctly.

Colspan & Rowspan

syntax
<td colspan="n">Spans n columns</td>
<td rowspan="n">Spans n rows</td>
example
<table>
  <thead>
    <tr>
      <th>Name</th>
      <th colspan="2">Contact Info</th>
    </tr>
    <tr>
      <th></th>
      <th>Email</th>
      <th>Phone</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td rowspan="2">Acme Corp</td>
      <td>sales@acme.com</td>
      <td>555-0100</td>
    </tr>
    <tr>
      <td>support@acme.com</td>
      <td>555-0101</td>
    </tr>
  </tbody>
</table>

Note When a cell spans multiple columns or rows, omit the corresponding td elements from those positions. Miscounting causes misaligned tables.

Table Caption & Footer

syntax
<table>
  <caption>Description</caption>
  <thead>...</thead>
  <tbody>...</tbody>
  <tfoot>
    <tr><td>Footer data</td></tr>
  </tfoot>
</table>
example
<table>
  <caption>Monthly Sales Figures (USD)</caption>
  <thead>
    <tr><th>Month</th><th>Revenue</th></tr>
  </thead>
  <tbody>
    <tr><td>January</td><td>$12,400</td></tr>
    <tr><td>February</td><td>$15,800</td></tr>
  </tbody>
  <tfoot>
    <tr><td>Total</td><td>$28,200</td></tr>
  </tfoot>
</table>

Note The caption element provides an accessible name for the table. It must be the first child of the table element. tfoot contains summary rows like totals.

Forms

Form Element

syntax
<form action="url" method="GET|POST">
  ...
</form>
example
<form action="/api/subscribe" method="POST">
  <label for="email">Email:</label>
  <input type="email" id="email" name="email" required>
  <button type="submit">Subscribe</button>
</form>

Note GET appends data to the URL (visible, cacheable, bookmarkable). POST sends data in the request body (for sensitive data and large payloads). Omitting method defaults to GET.

Text Inputs

syntax
<input type="text|email|password|url|search|tel" name="field" id="field">
example
<label for="username">Username:</label>
<input type="text" id="username" name="username" placeholder="Enter username" maxlength="30" required>

<label for="user-email">Email:</label>
<input type="email" id="user-email" name="email" autocomplete="email">

Note Different type values trigger different mobile keyboards and built-in validation. For example, type="email" shows an @ key on mobile and validates email format.

Number & Range Inputs

syntax
<input type="number" min="0" max="100" step="1">
<input type="range" min="0" max="100" value="50">
example
<label for="quantity">Quantity (1-10):</label>
<input type="number" id="quantity" name="quantity" min="1" max="10" step="1" value="1">

<label for="volume">Volume:</label>
<input type="range" id="volume" name="volume" min="0" max="100" value="50">

Note Number inputs show spinners on desktop and numeric keyboards on mobile. The step attribute controls increment size. Use step="any" for decimal values.

Date & Time Inputs

syntax
<input type="date|time|datetime-local|month|week">
example
<label for="start-date">Start Date:</label>
<input type="date" id="start-date" name="startDate" min="2026-01-01" max="2026-12-31">

<label for="meeting-time">Meeting Time:</label>
<input type="datetime-local" id="meeting-time" name="meetingTime">

Note Date picker appearance varies significantly across browsers and platforms. The value format is always ISO 8601 (YYYY-MM-DD) regardless of display format. Use min and max to constrain the selectable range.

Checkboxes & Radio Buttons

syntax
<input type="checkbox" name="field" value="val">
<input type="radio" name="group" value="val">
example
<fieldset>
  <legend>Notification Preferences</legend>
  <label><input type="checkbox" name="notify" value="email"> Email</label>
  <label><input type="checkbox" name="notify" value="sms"> SMS</label>
</fieldset>

<fieldset>
  <legend>Priority</legend>
  <label><input type="radio" name="priority" value="low"> Low</label>
  <label><input type="radio" name="priority" value="high"> High</label>
</fieldset>

Note Radio buttons with the same name attribute form an exclusive group where only one can be selected. Checkboxes allow multiple selections. Wrap each in a label for a clickable hit area.

Select Dropdown

syntax
<select name="field" id="field">
  <option value="val">Label</option>
</select>
example
<label for="country">Country:</label>
<select id="country" name="country">
  <option value="">Select a country</option>
  <optgroup label="North America">
    <option value="us">United States</option>
    <option value="ca">Canada</option>
    <option value="mx">Mexico</option>
  </optgroup>
  <optgroup label="Europe">
    <option value="uk">United Kingdom</option>
    <option value="de">Germany</option>
  </optgroup>
</select>

Note Use optgroup to categorize options. Add an empty first option as a placeholder. For multi-selection, add the multiple attribute, but consider checkboxes instead since multi-select is not intuitive for many users.

Textarea

syntax
<textarea name="field" rows="n" cols="n"></textarea>
example
<label for="message">Your Message:</label>
<textarea id="message" name="message" rows="5" cols="40" placeholder="Type your message here..." maxlength="500"></textarea>

Note Unlike input, the textarea value goes between the opening and closing tags, not in a value attribute. Use the CSS resize property to control whether users can resize it.

Labels

syntax
<label for="input-id">Text</label>
<input id="input-id">
example
<label for="fullname">Full Name</label>
<input type="text" id="fullname" name="fullname">

<!-- Or wrapping approach -->
<label>
  Phone Number
  <input type="tel" name="phone">
</label>

Note Every form input must have a label for accessibility. The for attribute must match the input's id. Alternatively, wrap the input inside the label element to create an implicit association.

Datalist (Autocomplete Suggestions)

syntax
<input list="list-id">
<datalist id="list-id">
  <option value="suggestion">
</datalist>
example
<label for="framework">Framework:</label>
<input type="text" id="framework" name="framework" list="frameworks">
<datalist id="frameworks">
  <option value="React">
  <option value="Vue">
  <option value="Angular">
  <option value="Svelte">
  <option value="Solid">
</datalist>

Note Unlike select, datalist allows free-text entry in addition to the suggestions. The user is not forced to pick from the list. Browser rendering of suggestions varies.

Form Validation Attributes

syntax
required | minlength="n" | maxlength="n" | pattern="regex" | min="n" | max="n"
example
<input type="text" name="zipcode" required pattern="[0-9]{5}" title="Five digit zip code">

<input type="password" name="password" required minlength="8" maxlength="128">

<input type="number" name="age" min="18" max="120">

Note Built-in validation runs on form submission and is bypassed by adding formnovalidate to the submit button or novalidate to the form. The title attribute provides a hint message shown when the pattern fails.

Semantic Elements

Header & Footer

syntax
<header>...</header>
<footer>...</footer>
example
<header>
  <h1>TechBlog</h1>
  <nav>
    <a href="/articles">Articles</a>
    <a href="/about">About</a>
  </nav>
</header>

<footer>
  <p>&copy; 2026 TechBlog. All rights reserved.</p>
</footer>

Note header and footer can be used for the entire page or within an article/section element. A page can have multiple headers and footers in different contexts.

<main> Primary Content

syntax
<main>...</main>
example
<body>
  <header>...</header>
  <main>
    <h1>Dashboard</h1>
    <section>...</section>
  </main>
  <footer>...</footer>
</body>

Note There must be only one visible main element per page. It should not be nested inside header, footer, nav, article, or aside. Skip-navigation links typically target main.

Section & Article

syntax
<section>...</section>
<article>...</article>
example
<main>
  <section>
    <h2>Latest Articles</h2>
    <article>
      <h3>Understanding Flexbox</h3>
      <p>A practical guide to flexible layouts...</p>
    </article>
    <article>
      <h3>Grid Layout Patterns</h3>
      <p>Common patterns for CSS Grid...</p>
    </article>
  </section>
</main>

Note An article is a self-contained piece of content that could be independently distributed (blog post, comment, product card). A section is a thematic grouping of content. Both should generally include a heading.

<aside> Sidebar Content

syntax
<aside>...</aside>
example
<main>
  <article>
    <h2>Web Performance Guide</h2>
    <p>Core Web Vitals measure real-world user experience...</p>
    <aside>
      <h3>Related Resources</h3>
      <ul>
        <li><a href="/caching">Caching Strategies</a></li>
        <li><a href="/images">Image Optimization</a></li>
      </ul>
    </aside>
  </article>
</main>

Note aside represents content tangentially related to its parent. When used at the page level it acts as a sidebar. Inside an article, it represents a pullquote or supplementary info.

Details & Summary (Disclosure)

syntax
<details>
  <summary>Visible title</summary>
  Hidden content
</details>
example
<details>
  <summary>How do I reset my password?</summary>
  <p>Go to the login page and click "Forgot Password". Enter your email address and we will send you a reset link within 5 minutes.</p>
</details>

<details open>
  <summary>System Requirements</summary>
  <ul>
    <li>64-bit operating system</li>
    <li>4 GB RAM minimum</li>
  </ul>
</details>

Note This creates a native collapsible section with no JavaScript needed. The open attribute shows the content expanded by default. Style the summary's marker with ::marker or list-style.

<dialog> Modal

syntax
<dialog id="myDialog">
  Content
  <button>Close</button>
</dialog>
example
<dialog id="confirmDialog">
  <h2>Confirm Deletion</h2>
  <p>This will permanently remove the item. Continue?</p>
  <form method="dialog">
    <button value="cancel">Cancel</button>
    <button value="confirm">Delete</button>
  </form>
</dialog>

<!-- Open with: document.getElementById('confirmDialog').showModal() -->

Note Use showModal() to open with a backdrop that traps focus (accessible). Use show() for a non-modal popup. The form method="dialog" approach closes the dialog and sets its returnValue to the button's value.

<template> Reusable Fragment

syntax
<template id="tmpl">
  <!-- inert markup -->
</template>
example
<template id="card-template">
  <div class="card">
    <h3 class="card-title"></h3>
    <p class="card-body"></p>
  </div>
</template>

<!-- Clone in JS:
const tmpl = document.getElementById('card-template');
const clone = tmpl.content.cloneNode(true);
clone.querySelector('.card-title').textContent = 'New Card';
document.body.appendChild(clone);
-->

Note Content inside template is not rendered, not visible, and not queried by selectors. It serves as a blueprint for JavaScript-generated elements. Clone its content with cloneNode(true).

<time> Machine-Readable Dates

syntax
<time datetime="YYYY-MM-DD">display text</time>
example
<p>Published on <time datetime="2026-03-15">March 15, 2026</time></p>
<p>Event starts at <time datetime="2026-04-10T09:00">9:00 AM, April 10</time></p>

Note The datetime attribute provides a machine-readable format for search engines and tools, while the visible text can use any human-friendly format.

Meta & SEO

Page Title

syntax
<title>Page Title - Site Name</title>
example
<head>
  <title>Pricing Plans - CloudDash</title>
</head>

Note The title appears in browser tabs, bookmarks, and search engine results. Keep it under 60 characters. Place the specific page name before the site name for better scannability.

Meta Description

syntax
<meta name="description" content="...">
example
<meta name="description" content="Compare our Starter, Pro, and Enterprise plans. Start free and scale as your team grows.">

Note Search engines display this as the page snippet. Keep it between 120-160 characters. Each page should have a unique description that summarizes its specific content.

Open Graph Tags (Social Sharing)

syntax
<meta property="og:title" content="...">
<meta property="og:description" content="...">
<meta property="og:image" content="...">
<meta property="og:url" content="...">
example
<meta property="og:title" content="CloudDash Pricing">
<meta property="og:description" content="Plans starting at $0/month. No credit card required.">
<meta property="og:image" content="https://clouddash.example.com/images/og-pricing.png">
<meta property="og:url" content="https://clouddash.example.com/pricing">
<meta property="og:type" content="website">

Note Open Graph controls how your page appears when shared on social platforms. The og:image should be at least 1200x630 pixels. Test with platform-specific debugging tools after deploying.

Canonical URL

syntax
<link rel="canonical" href="preferred-url">
example
<link rel="canonical" href="https://example.com/blog/flexbox-guide">

Note Use canonical when the same content is accessible at multiple URLs (with/without www, query parameters, etc.). It tells search engines which URL to index and rank.

Robots Meta Tag

syntax
<meta name="robots" content="index, follow">
<meta name="robots" content="noindex, nofollow">
example
<meta name="robots" content="noindex, nofollow">
<!-- Use on staging, admin, or private pages -->

<meta name="robots" content="index, follow">
<!-- Default behavior, but explicit is fine -->

Note noindex prevents a page from appearing in search results. nofollow tells crawlers not to follow links on the page. For specific bots, use their name instead of robots (e.g., googlebot).

Favicon

syntax
<link rel="icon" href="/favicon.ico" sizes="any">
<link rel="icon" href="/icon.svg" type="image/svg+xml">
example
<link rel="icon" href="/favicon.ico" sizes="32x32">
<link rel="icon" href="/icon.svg" type="image/svg+xml">
<link rel="apple-touch-icon" href="/apple-touch-icon.png">

Note Modern browsers support SVG favicons which scale perfectly. Include the .ico fallback for older browsers. apple-touch-icon is used when users add your site to their iOS home screen.

Structured Data (JSON-LD)

syntax
<script type="application/ld+json">
{ "@context": "https://schema.org", ... }
</script>
example
<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "Article",
  "headline": "Understanding CSS Grid",
  "author": {
    "@type": "Person",
    "name": "Jamie Chen"
  },
  "datePublished": "2026-03-20",
  "image": "https://example.com/images/grid-article.jpg"
}
</script>

Note JSON-LD structured data helps search engines understand your content and may trigger rich results (star ratings, recipe cards, event details). Place it in the head or body. Validate with search engine testing tools.

Theme Color

syntax
<meta name="theme-color" content="#hexcolor">
example
<meta name="theme-color" content="#1a1a2e">
<meta name="theme-color" content="#f5f5f5" media="(prefers-color-scheme: light)">
<meta name="theme-color" content="#1a1a2e" media="(prefers-color-scheme: dark)">

Note Controls the browser toolbar color on mobile devices. Use the media attribute to set different colors for light and dark system themes.

CSS Selectors

Element, Class & ID Selectors

syntax
element { }
.class { }
#id { }
example
p {
  line-height: 1.6;
}

.alert {
  padding: 12px 16px;
  border-radius: 6px;
}

#main-header {
  position: sticky;
  top: 0;
}

Note Prefer classes over IDs for styling since IDs have much higher specificity and cannot be reused. IDs are fine for JavaScript hooks and anchor targets.

Attribute Selectors

syntax
[attr]           /* has attribute */
[attr="value"]    /* exact match */
[attr^="prefix"]  /* starts with */
[attr$="suffix"]  /* ends with */
[attr*="part"]    /* contains */
example
a[target="_blank"]::after {
  content: " \2197";
}

input[type="email"] {
  padding-left: 32px;
}

a[href$=".pdf"] {
  color: #c0392b;
}

Note Attribute selectors are case-sensitive by default. Add i before the closing bracket for case-insensitive matching: [attr="value" i].

State Pseudo-Classes

syntax
:hover | :focus | :active | :focus-visible | :focus-within | :disabled | :checked
example
.button:hover {
  background-color: #2563eb;
  transform: translateY(-1px);
}

.button:active {
  transform: translateY(0);
}

input:focus-visible {
  outline: 2px solid #3b82f6;
  outline-offset: 2px;
}

input:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

Note Use :focus-visible instead of :focus for keyboard-only focus rings. It avoids showing outlines on mouse clicks while keeping them for keyboard navigation.

Structural Pseudo-Classes

syntax
:first-child | :last-child | :nth-child(n) | :nth-of-type(n) | :only-child | :empty
example
li:first-child {
  border-top: none;
}

li:last-child {
  border-bottom: none;
}

tr:nth-child(even) {
  background-color: #f8f9fa;
}

.grid-item:nth-child(3n) {
  margin-right: 0;
}

Note nth-child counts all siblings regardless of type. nth-of-type counts only siblings of the same tag. Common patterns: odd, even, 3n, 3n+1.

Pseudo-Elements

syntax
::before | ::after | ::first-line | ::first-letter | ::placeholder | ::selection | ::marker
example
.required-field::after {
  content: " *";
  color: #ef4444;
}

.fancy-quote::first-letter {
  font-size: 3em;
  float: left;
  line-height: 1;
  margin-right: 8px;
}

::selection {
  background-color: #bfdbfe;
  color: #1e3a5f;
}

Note Pseudo-elements use double colons (::) by convention. ::before and ::after require the content property. They are not in the DOM so they cannot be selected by users or read reliably by all screen readers.

Combinators

syntax
A B   /* descendant */
A > B /* direct child */
A + B /* adjacent sibling */
A ~ B /* general sibling */
example
/* All paragraphs inside .content */
.content p {
  margin-bottom: 1em;
}

/* Only direct children */
.nav > li {
  display: inline-block;
}

/* Paragraph right after an h2 */
h2 + p {
  font-size: 1.125em;
}

/* All paragraphs after an h2 */
h2 ~ p {
  color: #374151;
}

Note The descendant selector (space) matches at any depth. The child selector (>) matches only direct children. Adjacent (+) matches only the immediately following sibling.

:has() Relational Selector

syntax
parent:has(child) { }
example
/* Style a card only if it contains an image */
.card:has(img) {
  padding: 0;
}

/* Style a form group with an invalid input */
.form-group:has(input:invalid) {
  border-left: 3px solid #ef4444;
}

/* Style a heading followed by a subtitle */
h2:has(+ .subtitle) {
  margin-bottom: 4px;
}

Note :has() is often called the parent selector. It selects an element based on whether it contains or is followed by a matching element. Fully supported in modern browsers as of 2024.

:is(), :where(), :not()

syntax
:is(selector-list) { }   /* takes highest specificity in list */
:where(selector-list) { } /* zero specificity */
:not(selector) { }
example
/* Apply to h1, h2, or h3 inside .article */
.article :is(h1, h2, h3) {
  color: #1e293b;
}

/* Reset styles with zero specificity (easy to override) */
:where(.prose) p {
  margin-bottom: 1em;
}

/* All inputs except submit buttons */
input:not([type="submit"]) {
  border: 1px solid #d1d5db;
}

Note :is() and :where() take a selector list and simplify complex selectors. The key difference: :is() takes the specificity of its most specific argument, while :where() always has zero specificity. :not() can now accept complex selectors.

Specificity Rules

syntax
/* Specificity: (ID, Class, Element) */
/* #nav .item a  =>  (1, 1, 1) */
/* .menu a       =>  (0, 1, 1) */
/* a             =>  (0, 0, 1) */
example
/* Specificity (0, 0, 1) - easily overridden */
p { color: gray; }

/* Specificity (0, 1, 0) - overrides above */
.highlight { color: blue; }

/* Specificity (1, 0, 0) - overrides above */
#title { color: red; }

/* Inline styles beat all normal selectors */
/* !important overrides everything (avoid it) */

Note When two rules conflict, the one with higher specificity wins. If equal, the last one in source order wins. Avoid !important except for utility classes. Use @layer to manage specificity across libraries.

Box Model

Box Sizing

syntax
box-sizing: content-box | border-box;
example
*, *::before, *::after {
  box-sizing: border-box;
}

/* With border-box: a 300px wide element stays 300px
   even with padding and border added */
.card {
  width: 300px;
  padding: 20px;
  border: 1px solid #e5e7eb;
  /* Total width: still 300px */
}

Note The universal border-box reset is considered best practice. Without it, padding and borders are added on top of the width, making sizing unpredictable.

Margin

syntax
margin: top right bottom left;
margin: vertical horizontal;
margin: all;
example
.section {
  margin: 40px 0;
}

.container {
  margin: 0 auto; /* center horizontally */
  max-width: 1200px;
}

.spaced-item {
  margin-top: 16px;
  margin-bottom: 16px;
}

Note Vertical margins collapse: when two vertical margins touch, only the larger one applies (not their sum). Margins on flex and grid children do not collapse. Use margin: 0 auto on a block element with a set width to center it.

Padding

syntax
padding: top right bottom left;
padding: vertical horizontal;
padding: all;
example
.button {
  padding: 10px 24px;
}

.card {
  padding: 24px;
}

.nav-link {
  padding-block: 8px;
  padding-inline: 16px;
}

Note Unlike margins, padding never collapses. Padding is inside the element's border, so background colors and images extend into the padded area. Use padding-block and padding-inline for writing-mode-aware spacing.

Border

syntax
border: width style color;
border-radius: value;
example
.input {
  border: 1px solid #d1d5db;
  border-radius: 6px;
}

.avatar {
  border: 3px solid #3b82f6;
  border-radius: 50%; /* perfect circle */
}

.alert {
  border-left: 4px solid #f59e0b;
  border-radius: 0;
}

Note Border styles include solid, dashed, dotted, double, groove, ridge, inset, outset, and none. Use border-radius: 50% on a square element to create a circle. Individual sides can be styled separately.

Width & Height

syntax
width: value; height: value;
min-width | max-width | min-height | max-height
example
.container {
  width: 100%;
  max-width: 1200px;
}

.sidebar {
  width: 280px;
  min-height: 100vh;
}

.avatar {
  width: 48px;
  height: 48px;
  aspect-ratio: 1;
}

Note Avoid setting fixed heights on text containers since content length varies. Use min-height instead. The aspect-ratio property maintains proportions without needing both width and height.

Overflow

syntax
overflow: visible | hidden | scroll | auto | clip;
overflow-x: value;
overflow-y: value;
example
.card-body {
  max-height: 200px;
  overflow-y: auto;
}

.truncate {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.no-scroll {
  overflow: clip;
}

Note overflow: auto only shows scrollbars when content actually overflows, while scroll always shows them. overflow: clip is like hidden but does not create a scroll container, preventing programmatic scrolling. Combine overflow: hidden with text-overflow: ellipsis for truncated text.

Outline

syntax
outline: width style color;
outline-offset: value;
example
.button:focus-visible {
  outline: 2px solid #3b82f6;
  outline-offset: 2px;
}

.card:focus-within {
  outline: 2px solid #8b5cf6;
  outline-offset: 0;
}

Note Unlike border, outline does not take up space in the layout and does not affect element sizing. It can overlap adjacent elements. outline-offset pushes the outline away from the element edge. Never remove focus outlines without providing an alternative indicator.

Display: Block, Inline, Inline-Block, None

syntax
display: block | inline | inline-block | none;
example
.nav-link {
  display: inline-block;
  padding: 8px 16px;
}

.hidden {
  display: none; /* removes from layout and accessibility tree */
}

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
}

Note block takes full width, starts on a new line, accepts all box model properties. inline only takes content width, ignores vertical margin/padding. inline-block is inline but respects all box model properties. display: none removes the element entirely. For screen-reader-only content, use the visually-hidden pattern instead.

Flexbox

Flex Container Basics

syntax
display: flex;
flex-direction: row | column | row-reverse | column-reverse;
example
.navbar {
  display: flex;
  flex-direction: row;
}

.sidebar {
  display: flex;
  flex-direction: column;
}

Note Setting display: flex turns direct children into flex items. The default direction is row (horizontal). Items shrink to fit by default but do not wrap.

Justify Content (Main Axis)

syntax
justify-content: flex-start | center | flex-end | space-between | space-around | space-evenly;
example
/* Push items to opposite ends */
.header {
  display: flex;
  justify-content: space-between;
}

/* Center items horizontally */
.hero {
  display: flex;
  justify-content: center;
}

Note justify-content distributes items along the main axis (horizontal in row, vertical in column). space-between puts the first and last items at the edges. space-evenly distributes equal space around every item including edges.

Align Items (Cross Axis)

syntax
align-items: stretch | flex-start | center | flex-end | baseline;
example
/* Vertically center items in a row */
.toolbar {
  display: flex;
  align-items: center;
  height: 64px;
}

/* Align text baselines across different font sizes */
.price-row {
  display: flex;
  align-items: baseline;
}

Note align-items controls alignment on the cross axis (vertical in row, horizontal in column). The default is stretch, which makes items fill the container height. Use baseline to align items by their text baseline regardless of size.

Flex Wrap

syntax
flex-wrap: nowrap | wrap | wrap-reverse;
example
.tag-list {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}

.tag {
  padding: 4px 12px;
  background: #e5e7eb;
  border-radius: 16px;
}

Note By default, flex items squeeze onto one line (nowrap). With wrap, items flow to the next line when they run out of space. This is essential for responsive layouts without media queries.

Gap

syntax
gap: value;
row-gap: value;
column-gap: value;
example
.card-grid {
  display: flex;
  flex-wrap: wrap;
  gap: 24px;
}

.button-group {
  display: flex;
  gap: 8px;
}

Note gap creates space between flex items without adding margins to outer edges. It works in both flexbox and grid. This eliminates the need for margin hacks on the first or last child.

Flex Grow, Shrink, Basis

syntax
flex: grow shrink basis;
flex-grow: number;
flex-shrink: number;
flex-basis: size;
example
.sidebar {
  flex: 0 0 280px; /* fixed width, no grow or shrink */
}

.main-content {
  flex: 1; /* grow to fill remaining space */
}

.equal-columns > * {
  flex: 1 1 0%; /* equal width columns */
}

Note flex: 1 is shorthand for flex: 1 1 0%, meaning the item can grow and shrink and starts from zero base size. flex-basis sets the initial size before growing/shrinking. Using 0% basis makes items share space equally.

Align Self (Individual Override)

syntax
align-self: auto | flex-start | center | flex-end | stretch | baseline;
example
.container {
  display: flex;
  align-items: flex-start;
}

.push-bottom {
  align-self: flex-end;
}

.centered-item {
  align-self: center;
}

Note align-self overrides the container's align-items value for a single flex item. It applies on the cross axis. There is no justify-self in flexbox since the main axis is controlled by the container.

Order

syntax
order: integer;
example
.sidebar {
  order: -1; /* moves before items with default order 0 */
}

/* Reorder on mobile */
@media (max-width: 768px) {
  .hero-image { order: -1; }
  .hero-text  { order: 0; }
}

Note The default order is 0. Lower values appear first. This changes visual order only, not tab order or screen reader order, so use sparingly to avoid accessibility confusion.

Center Anything with Flexbox

syntax
display: flex;
justify-content: center;
align-items: center;
example
/* Center a single element both horizontally and vertically */
.page-center {
  display: flex;
  justify-content: center;
  align-items: center;
  min-height: 100vh;
}

/* Shorter alternative using place properties */
.quick-center {
  display: flex;
  place-content: center;
  place-items: center;
}

Note This three-line pattern is the simplest way to center anything vertically and horizontally. Add min-height: 100vh to center within the full viewport.

Flex Spacer Pattern (Push Items Apart)

syntax
margin-left: auto; /* or margin-right: auto */
example
/* Push the last item to the far right */
.navbar {
  display: flex;
  align-items: center;
  gap: 16px;
}

.navbar .user-menu {
  margin-left: auto;
}

Note Auto margins in flexbox consume all available space in that direction. This pushes elements apart without justify-content: space-between, which is useful when you need only one item pushed to the end.

CSS Grid

Grid Container Basics

syntax
display: grid;
grid-template-columns: values;
grid-template-rows: values;
example
.layout {
  display: grid;
  grid-template-columns: 250px 1fr;
  grid-template-rows: auto 1fr auto;
  min-height: 100vh;
}

Note display: grid turns direct children into grid items. Unlike flexbox, grid controls both columns and rows simultaneously, making it ideal for two-dimensional layouts.

Fractional Unit (fr)

syntax
grid-template-columns: 1fr 2fr 1fr;
example
/* Three columns: 25% / 50% / 25% */
.layout {
  display: grid;
  grid-template-columns: 1fr 2fr 1fr;
  gap: 24px;
}

/* Fixed sidebar + flexible main */
.page {
  display: grid;
  grid-template-columns: 300px 1fr;
}

Note The fr unit distributes remaining free space proportionally. 1fr 2fr 1fr means the middle column gets twice the space of the side columns. Fixed sizes (px, rem) are allocated first, then fr units share what is left.

Repeat Function

syntax
grid-template-columns: repeat(count, size);
example
/* 4 equal columns */
.grid-4 {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 16px;
}

/* Pattern: 100px then 1fr, repeated 3 times */
.dashboard {
  grid-template-columns: repeat(3, 100px 1fr);
}

Note repeat() avoids writing the same value many times. You can repeat patterns too: repeat(3, 1fr 2fr) produces six columns alternating between 1fr and 2fr.

Auto-Fill & Auto-Fit

syntax
grid-template-columns: repeat(auto-fill, minmax(min, max));
grid-template-columns: repeat(auto-fit, minmax(min, max));
example
/* Cards that auto-wrap, minimum 280px each */
.card-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: 24px;
}

Note auto-fill creates as many tracks as fit, leaving empty tracks if there are fewer items. auto-fit collapses empty tracks, letting items stretch to fill the row. For most card grids, auto-fit is what you want.

Minmax Function

syntax
minmax(minimum, maximum)
example
.layout {
  display: grid;
  grid-template-columns: minmax(200px, 300px) 1fr minmax(200px, 300px);
}

/* Sidebar that shrinks on small screens */
.page {
  grid-template-columns: minmax(150px, 250px) 1fr;
}

Note minmax() sets a size range for grid tracks. Common pattern: minmax(0, 1fr) prevents grid blowout when content is wider than the column. The default minimum for fr units is min-content, not 0.

Named Grid Areas

syntax
grid-template-areas:
  "header  header"
  "sidebar main"
  "footer  footer";
grid-area: name;
example
.page {
  display: grid;
  grid-template-columns: 250px 1fr;
  grid-template-rows: auto 1fr auto;
  grid-template-areas:
    "header  header"
    "sidebar content"
    "footer  footer";
  min-height: 100vh;
}

.page-header  { grid-area: header; }
.page-sidebar { grid-area: sidebar; }
.page-content { grid-area: content; }
.page-footer  { grid-area: footer; }

Note Named areas provide a visual map of your layout right in the CSS. Use a period (.) for empty cells. Each area name must form a rectangle. Changing the template in a media query rearranges the entire layout.

Grid Placement & Spanning

syntax
grid-column: start / end;
grid-row: start / end;
grid-column: span n;
example
.feature-card {
  grid-column: 1 / 3; /* spans columns 1 and 2 */
}

.tall-card {
  grid-row: span 2; /* spans 2 rows */
}

.full-width {
  grid-column: 1 / -1; /* spans all columns */
}

Note Grid lines are numbered starting at 1. Negative numbers count from the end (-1 is the last line). span n is a shorthand that avoids hardcoding line numbers.

Grid Gap

syntax
gap: row-gap column-gap;
row-gap: value;
column-gap: value;
example
.grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 24px;
}

.asymmetric {
  row-gap: 32px;
  column-gap: 16px;
}

Note The gap property works identically in grid and flexbox. It replaces the older grid-gap property. Gap only creates space between items, never on the outer edges.

Grid Alignment

syntax
justify-items: start | center | end | stretch;
align-items: start | center | end | stretch;
place-items: align justify;
example
.grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  place-items: center; /* center in both axes */
}

/* Individual item */
.special-item {
  justify-self: end;
  align-self: start;
}

Note In grid, justify controls the inline (horizontal) axis and align controls the block (vertical) axis. place-items is shorthand for both. Unlike flexbox, grid supports justify-self for individual items.

Subgrid

syntax
grid-template-columns: subgrid;
grid-template-rows: subgrid;
example
.card-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 24px;
}

.card {
  display: grid;
  grid-template-rows: subgrid;
  grid-row: span 3; /* participate in 3 parent rows */
}

/* Now all card titles, bodies, and footers align across cards */

Note Subgrid lets a nested grid item inherit its parent's track sizing. This is essential for aligning content across sibling cards (like matching title heights). The child must span the relevant parent tracks.

Typography

Font Family

syntax
font-family: "Font Name", fallback, generic;
example
body {
  font-family: "Inter", "Segoe UI", system-ui, sans-serif;
}

code, pre {
  font-family: "JetBrains Mono", "Fira Code", ui-monospace, monospace;
}

Note Always end the stack with a generic family (sans-serif, serif, monospace, cursive, system-ui). The system-ui keyword uses the operating system's default font. Quotes are required when font names contain spaces.

Font Size

syntax
font-size: value; /* px, rem, em, %, clamp() */
example
html {
  font-size: 16px; /* base for rem units */
}

body {
  font-size: 1rem;
}

h1 {
  font-size: clamp(1.75rem, 4vw, 3rem);
}

.small-text {
  font-size: 0.875rem; /* 14px at 16px base */
}

Note Use rem for consistent sizing relative to the root. em is relative to the parent's font size which compounds in nested elements. clamp() creates fluid typography that scales between a minimum and maximum.

Font Weight

syntax
font-weight: 100-900 | normal | bold | lighter | bolder;
example
.heading {
  font-weight: 700;
}

.body-text {
  font-weight: 400;
}

.light-text {
  font-weight: 300;
}

.semibold {
  font-weight: 600;
}

Note 100=Thin, 200=ExtraLight, 300=Light, 400=Normal, 500=Medium, 600=SemiBold, 700=Bold, 800=ExtraBold, 900=Black. The font file must include the requested weight or the browser will fake bold, which looks poor.

Line Height

syntax
line-height: number | length | percentage;
example
body {
  line-height: 1.6;
}

h1 {
  line-height: 1.2;
}

.compact {
  line-height: 1.4;
}

Note Use unitless numbers (like 1.6) rather than fixed units so the line height scales proportionally with font size. Body text typically reads best between 1.5 and 1.7. Headings look better tighter at 1.1 to 1.3.

Text Align

syntax
text-align: left | center | right | justify | start | end;
example
.hero-heading {
  text-align: center;
}

.article-body {
  text-align: start; /* respects writing direction */
}

.price {
  text-align: right;
}

Note Prefer start and end over left and right for internationalization support (RTL languages). Avoid text-align: justify for body text on the web since it creates uneven word spacing without hyphenation.

Text Decoration

syntax
text-decoration: line style color thickness;
text-underline-offset: value;
example
a {
  text-decoration: underline;
  text-underline-offset: 3px;
  text-decoration-thickness: 2px;
}

a:hover {
  text-decoration-color: #3b82f6;
}

.no-underline {
  text-decoration: none;
}

Note Use text-underline-offset to move underlines away from the text baseline for better readability. text-decoration-skip-ink: auto (the default in most browsers) makes underlines skip descenders cleanly.

Text Transform

syntax
text-transform: uppercase | lowercase | capitalize | none;
example
.label {
  text-transform: uppercase;
  letter-spacing: 0.05em;
  font-size: 0.75rem;
}

.name {
  text-transform: capitalize;
}

Note When using uppercase, add letter-spacing (0.04em to 0.1em) to improve readability. Keep actual content in normal case in the HTML since text-transform is purely visual and screen readers may spell out uppercase letters.

Letter & Word Spacing

syntax
letter-spacing: value;
word-spacing: value;
example
.tracking-wide {
  letter-spacing: 0.05em;
}

.tracking-tight {
  letter-spacing: -0.02em;
}

.spaced-words {
  word-spacing: 0.1em;
}

Note Use em units for letter-spacing so it scales with font size. Tight letter-spacing works well for large headings. Wide letter-spacing suits small uppercase labels.

Word Break & Overflow Wrap

syntax
word-break: normal | break-all | keep-all;
overflow-wrap: normal | break-word | anywhere;
example
.comment {
  overflow-wrap: break-word;
}

.data-cell {
  word-break: break-all;
}

.cjk-text {
  word-break: keep-all;
}

Note overflow-wrap: break-word only breaks long words that would overflow their container. word-break: break-all breaks between any characters, even mid-word. Use overflow-wrap for most cases and word-break: break-all only for data like hashes or URLs.

Text Wrap Balance & Pretty

syntax
text-wrap: balance | pretty | stable;
example
h1, h2, h3 {
  text-wrap: balance;
}

.article-body {
  text-wrap: pretty;
}

Note text-wrap: balance distributes text evenly across lines, preventing headings with one orphaned word on the last line. text-wrap: pretty focuses on avoiding orphans in body text. balance has a limit of a few lines for performance reasons.

Loading Web Fonts

syntax
@font-face {
  font-family: "Name";
  src: url("path.woff2") format("woff2");
  font-weight: 400;
  font-display: swap;
}
example
@font-face {
  font-family: "CustomSans";
  src: url("/fonts/custom-sans-regular.woff2") format("woff2");
  font-weight: 400;
  font-display: swap;
}

@font-face {
  font-family: "CustomSans";
  src: url("/fonts/custom-sans-bold.woff2") format("woff2");
  font-weight: 700;
  font-display: swap;
}

body {
  font-family: "CustomSans", sans-serif;
}

Note Use font-display: swap to show fallback text while the font loads, preventing invisible text. WOFF2 offers the best compression. Preload critical fonts with <link rel="preload" as="font" crossorigin>.

Colors & Backgrounds

Color Formats

syntax
color: #rrggbb | #rgb | rgb(r g b) | hsl(h s l) | oklch(L C H);
example
.text-hex   { color: #1e293b; }
.text-rgb   { color: rgb(30 41 59); }
.text-hsl   { color: hsl(215 28% 17%); }
.text-oklch { color: oklch(0.35 0.05 260); }

/* With alpha (transparency) */
.overlay { background: rgb(0 0 0 / 0.5); }
.tint    { background: hsl(210 100% 50% / 0.1); }

Note Modern CSS uses space-separated values with a slash for alpha: rgb(255 0 0 / 0.5). The older comma syntax still works but is being phased out. oklch provides perceptually uniform colors, so adjusting lightness produces visually consistent results across hues.

currentColor Keyword

syntax
border-color: currentColor;
fill: currentColor;
example
.icon-button {
  color: #2563eb;
  border: 2px solid currentColor;
}

.icon-button svg {
  fill: currentColor;
}

.icon-button:hover {
  color: #1d4ed8;
  /* border and SVG automatically update */
}

Note currentColor refers to the element's computed color value. It keeps borders, SVG fills, shadows, and other properties in sync with the text color. Change color once and everything follows.

Gradients

syntax
background: linear-gradient(direction, color1, color2);
background: radial-gradient(shape, color1, color2);
background: conic-gradient(from angle, color1, color2);
example
.hero {
  background: linear-gradient(135deg, #667eea, #764ba2);
}

.spotlight {
  background: radial-gradient(circle at 30% 50%, #fbbf24, transparent 70%);
}

.pie-chart {
  background: conic-gradient(#3b82f6 0% 65%, #e5e7eb 65% 100%);
  border-radius: 50%;
}

Note Gradients are background-image values, not colors, so they cannot be used with the color property. You can layer multiple gradients separated by commas. Conic gradients are great for pie-chart-like visuals.

Background Image

syntax
background-image: url("path");
background-size: cover | contain | width height;
background-position: x y;
background-repeat: no-repeat | repeat;
example
.hero {
  background-image: url("/images/hero-bg.jpg");
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
}

/* Shorthand */
.banner {
  background: url("/images/pattern.svg") center / cover no-repeat;
}

Note background-size: cover fills the entire container (may crop). contain fits the whole image (may leave empty space). When using the background shorthand, the size must follow position with a slash separator.

Opacity

syntax
opacity: 0 to 1;
example
.disabled-card {
  opacity: 0.5;
  pointer-events: none;
}

.watermark {
  opacity: 0.15;
}

.fade-in {
  opacity: 0;
  transition: opacity 0.3s ease;
}
.fade-in.visible {
  opacity: 1;
}

Note opacity affects the entire element and all its children. For transparent backgrounds only, use rgba or hsla with alpha instead. An element with opacity less than 1 creates a new stacking context.

CSS Custom Properties (Variables)

syntax
:root {
  --name: value;
}
element {
  property: var(--name, fallback);
}
example
:root {
  --color-primary: #2563eb;
  --color-surface: #ffffff;
  --radius-md: 8px;
  --space-4: 16px;
}

.card {
  background: var(--color-surface);
  border-radius: var(--radius-md);
  padding: var(--space-4);
}

.button {
  background: var(--color-primary);
}

Note Custom properties cascade and inherit like any CSS property. Define theme tokens on :root for global access. They can be changed per element, making component theming straightforward. The fallback value in var() is used only if the variable is not defined.

Color Mix

syntax
color-mix(in colorspace, color1 percentage, color2 percentage)
example
.hover-darken {
  background: var(--color-primary);
}
.hover-darken:hover {
  background: color-mix(in oklch, var(--color-primary) 80%, black);
}

.tinted-surface {
  background: color-mix(in srgb, var(--color-primary) 10%, white);
}

Note color-mix() blends two colors in the specified color space. Use oklch for perceptually even mixing. This replaces the need for pre-computed hover shades. Specify percentages to control the blend ratio.

Box Shadow

syntax
box-shadow: offset-x offset-y blur spread color;
box-shadow: inset offset-x offset-y blur spread color;
example
.card {
  box-shadow: 0 1px 3px rgb(0 0 0 / 0.1), 0 1px 2px rgb(0 0 0 / 0.06);
}

.card-elevated {
  box-shadow: 0 10px 25px rgb(0 0 0 / 0.1);
}

.input:focus {
  box-shadow: 0 0 0 3px rgb(59 130 246 / 0.3);
}

Note Layer multiple shadows for realistic depth (soft large shadow + tight small shadow). Use box-shadow: 0 0 0 Npx color as a pseudo-border that does not affect layout. The inset keyword creates inner shadows.

Layout & Positioning

Position Relative

syntax
position: relative;
top | right | bottom | left: value;
example
.badge-container {
  position: relative;
}

.badge {
  position: absolute;
  top: -8px;
  right: -8px;
}

Note position: relative keeps the element in the normal flow but enables offset with top/right/bottom/left. Its main use is establishing a positioning context for absolutely positioned children.

Position Absolute

syntax
position: absolute;
top | right | bottom | left: value;
example
.tooltip {
  position: absolute;
  bottom: 100%;
  left: 50%;
  transform: translateX(-50%);
  padding: 6px 12px;
  background: #1f2937;
  color: white;
  border-radius: 4px;
  white-space: nowrap;
}

Note Absolute positioning removes the element from the document flow and positions it relative to the nearest positioned ancestor (one with position: relative, absolute, fixed, or sticky). If none exists, it positions relative to the viewport.

Position Fixed

syntax
position: fixed;
top | right | bottom | left: value;
example
.sticky-header {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 100;
  background: white;
  box-shadow: 0 1px 3px rgb(0 0 0 / 0.1);
}

.back-to-top {
  position: fixed;
  bottom: 24px;
  right: 24px;
}

Note Fixed elements are positioned relative to the viewport and stay in place during scrolling. They are removed from the document flow. A transform, filter, or perspective on any ancestor breaks fixed positioning and makes it relative to that ancestor instead.

Position Sticky

syntax
position: sticky;
top: value;
example
.table-header {
  position: sticky;
  top: 0;
  background: white;
  z-index: 10;
}

.section-label {
  position: sticky;
  top: 64px; /* below a fixed navbar */
}

Note Sticky is a hybrid: it behaves as relative until the scroll position reaches the threshold (top/bottom), then it sticks like fixed. The element must have a scrollable ancestor and the parent must have enough height for it to work. overflow: hidden on an ancestor can prevent sticking.

Z-Index & Stacking

syntax
z-index: integer;
example
.dropdown   { z-index: 10; }
.modal-bg   { z-index: 40; }
.modal      { z-index: 50; }
.toast      { z-index: 60; }

/* Isolate stacking context */
.component {
  isolation: isolate;
}

Note z-index only works on positioned elements (relative, absolute, fixed, sticky) and flex/grid children. Elements with opacity < 1, transform, or filter create new stacking contexts. Use isolation: isolate to explicitly create one without side effects.

Float & Clear

syntax
float: left | right | none;
clear: left | right | both;
example
/* Classic text wrapping around an image */
.article-image {
  float: left;
  margin: 0 16px 16px 0;
  max-width: 40%;
}

/* Clear floats */
.clearfix::after {
  content: "";
  display: table;
  clear: both;
}

Note Floats were once used for page layouts but flexbox and grid have replaced that use case. Floats are still useful for wrapping text around images. Use the clearfix hack or display: flow-root on the parent to contain floated children.

Centering Methods Summary

syntax
/* Horizontal block centering */
margin: 0 auto;
/* Flex centering */
display: flex; justify-content: center; align-items: center;
/* Grid centering */
display: grid; place-items: center;
/* Absolute centering */
position: absolute; inset: 0; margin: auto;
example
/* Method 1: margin auto (horizontal only) */
.container { margin: 0 auto; max-width: 800px; }

/* Method 2: flexbox (both axes) */
.flex-center { display: flex; justify-content: center; align-items: center; }

/* Method 3: grid (both axes, shortest) */
.grid-center { display: grid; place-items: center; }

/* Method 4: absolute (both axes, within parent) */
.abs-center { position: absolute; inset: 0; margin: auto; width: fit-content; height: fit-content; }

Note For most cases, the grid place-items: center approach is the shortest and most versatile. Use margin auto for simple block-level horizontal centering. The absolute method works when you need to center within a positioned parent.

Inset Shorthand

syntax
inset: top right bottom left;
inset: all;
example
/* Fill entire parent */
.overlay {
  position: absolute;
  inset: 0;
  background: rgb(0 0 0 / 0.5);
}

/* 20px from each edge */
.inset-box {
  position: absolute;
  inset: 20px;
}

Note inset is shorthand for top, right, bottom, and left. inset: 0 is equivalent to top: 0; right: 0; bottom: 0; left: 0. It follows the same clockwise shorthand pattern as margin and padding.

Responsive Design

Media Queries

syntax
@media (condition) { ... }
example
/* Mobile-first approach */
.grid {
  display: grid;
  grid-template-columns: 1fr;
}

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

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

Note Mobile-first (min-width) is the recommended approach: start with small screens and add complexity at wider breakpoints. Common breakpoints: 640px (small), 768px (medium), 1024px (large), 1280px (extra large).

Media Query Features

syntax
@media (prefers-color-scheme: dark) { }
@media (prefers-reduced-motion: reduce) { }
@media (hover: hover) { }
@media (orientation: portrait) { }
example
@media (prefers-color-scheme: dark) {
  :root {
    --bg: #0f172a;
    --text: #e2e8f0;
  }
}

@media (prefers-reduced-motion: reduce) {
  * {
    animation-duration: 0.01ms !important;
    transition-duration: 0.01ms !important;
  }
}

@media (hover: hover) {
  .card:hover { transform: translateY(-2px); }
}

Note prefers-color-scheme detects dark/light mode system preference. prefers-reduced-motion is critical for accessibility. hover: hover avoids applying hover styles on touch devices where hover is awkward.

Container Queries

syntax
.parent {
  container-type: inline-size;
  container-name: name;
}
@container name (condition) { ... }
example
.card-wrapper {
  container-type: inline-size;
}

.card {
  display: grid;
  grid-template-columns: 1fr;
}

@container (min-width: 400px) {
  .card {
    grid-template-columns: 150px 1fr;
  }
}

@container (min-width: 600px) {
  .card {
    grid-template-columns: 200px 1fr auto;
  }
}

Note Container queries respond to the size of a parent container rather than the viewport. This makes components truly self-contained. Set container-type on the parent to opt in. Use container-name when you need to target a specific ancestor.

Clamp Function

syntax
clamp(minimum, preferred, maximum)
example
.container {
  width: clamp(320px, 90%, 1200px);
  padding: clamp(16px, 4vw, 48px);
}

h1 {
  font-size: clamp(1.5rem, 2.5vw + 1rem, 3.5rem);
}

Note clamp() eliminates the need for many media queries. It sets a preferred value that flexes between a minimum and maximum. The preferred value usually includes a viewport unit so it scales fluidly. Ideal for typography and spacing.

Viewport Units

syntax
vw | vh | vmin | vmax | dvh | svh | lvh | dvw | svw | lvw
example
.hero {
  min-height: 100dvh; /* accounts for mobile browser chrome */
}

.full-width-banner {
  width: 100vw;
  margin-left: calc(50% - 50vw);
}

.square {
  width: min(80vw, 80vh);
  height: min(80vw, 80vh);
}

Note On mobile browsers, 100vh is taller than the visible area because it does not account for the URL bar. Use 100dvh (dynamic viewport height) which updates as the browser chrome appears and disappears. svh is the smallest possible viewport, lvh is the largest.

Min & Max Functions

syntax
min(value1, value2, ...)
max(value1, value2, ...)
example
.container {
  width: min(90%, 1200px);
  /* same as: width: 90%; max-width: 1200px; */
}

.sidebar {
  width: max(200px, 20%);
  /* at least 200px, grows with container */
}

.padding-responsive {
  padding: max(16px, 3vw);
}

Note min() picks the smallest value, acting like a max-width. max() picks the largest, acting like a min-width. You can mix units freely inside these functions. They often replace media queries for simple responsive adjustments.

Media Query Range Syntax

syntax
@media (width >= 768px) { }
@media (768px <= width <= 1024px) { }
example
@media (width >= 768px) {
  .sidebar { display: block; }
}

@media (768px <= width <= 1024px) {
  .content { padding: 24px; }
}

@media (width < 640px) {
  .desktop-only { display: none; }
}

Note Range syntax is more readable than the traditional min-width/max-width approach. It is supported in all modern browsers. You can use <, <=, >, and >= operators. This replaces awkward constructs like (max-width: 1023.98px).

Container Query Units

syntax
cqw | cqh | cqi | cqb | cqmin | cqmax
example
.card-wrapper {
  container-type: inline-size;
}

.card-title {
  font-size: clamp(1rem, 4cqi, 1.5rem);
}

.card-image {
  height: 30cqi;
}

Note Container query units (cqi for inline, cqb for block) are relative to the container's dimensions, not the viewport. 1cqi equals 1% of the container's inline size. They allow truly component-relative sizing.

Animations & Transitions

Transitions

syntax
transition: property duration timing-function delay;
transition: all 0.3s ease;
example
.button {
  background: #2563eb;
  transition: background-color 0.2s ease, transform 0.2s ease;
}

.button:hover {
  background: #1d4ed8;
  transform: translateY(-1px);
}

/* Transition specific properties for performance */
.card {
  transition: box-shadow 0.2s ease, transform 0.3s ease;
}

Note Prefer transitioning specific properties rather than using 'all' since transitioning everything can cause unexpected side effects and reduced performance. Stick to transform and opacity for the smoothest animations (they are GPU-accelerated).

Keyframe Animations

syntax
@keyframes name {
  from { ... }
  to { ... }
}

@keyframes name {
  0% { ... }
  50% { ... }
  100% { ... }
}
example
@keyframes fadeSlideIn {
  from {
    opacity: 0;
    transform: translateY(20px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

.card {
  animation: fadeSlideIn 0.4s ease-out;
}

Note Keyframe animations run independently from state changes unlike transitions which need a trigger. Define keyframes globally and apply them with the animation property. The animation runs once by default.

Animation Property

syntax
animation: name duration timing-function delay iteration-count direction fill-mode;
example
.spinner {
  animation: spin 1s linear infinite;
}

@keyframes spin {
  to { transform: rotate(360deg); }
}

.pulse {
  animation: pulse 2s ease-in-out infinite alternate;
}

@keyframes pulse {
  from { transform: scale(1); }
  to { transform: scale(1.05); }
}

Note Key values: infinite loops forever. alternate reverses direction each cycle. forwards (fill-mode) keeps the final keyframe state after the animation ends. Combine multiple animations with commas.

Transform

syntax
transform: translate() | rotate() | scale() | skew();
translate: x y;
rotate: angle;
scale: value;
example
.card:hover {
  transform: translateY(-4px) scale(1.02);
}

.rotated {
  rotate: 12deg;
}

.shifted {
  translate: 10px -5px;
}

.mirror {
  scale: -1 1; /* flip horizontally */
}

Note Modern CSS supports individual transform properties (translate, rotate, scale) that can be animated independently. The older transform property applies all transforms at once. Transforms do not affect document flow and are GPU-accelerated.

Timing Functions

syntax
transition-timing-function: ease | linear | ease-in | ease-out | ease-in-out | cubic-bezier(x1, y1, x2, y2);
example
.slide-in {
  transition: transform 0.3s ease-out;
}

.bounce {
  transition: transform 0.5s cubic-bezier(0.34, 1.56, 0.64, 1);
}

.smooth {
  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}

Note ease-out starts fast and slows down, ideal for enter animations. ease-in starts slow and speeds up, better for exit animations. Custom cubic-bezier values give you fine control. Values beyond 0-1 on the y-axis create overshoot/bounce effects.

View Transitions

syntax
/* Opt elements into the transition */
.element {
  view-transition-name: unique-name;
}

/* Customize the transition */
::view-transition-old(name) { ... }
::view-transition-new(name) { ... }
example
.page-header {
  view-transition-name: header;
}

.product-image {
  view-transition-name: product-hero;
}

::view-transition-old(product-hero) {
  animation: fadeOut 0.2s ease-out;
}

::view-transition-new(product-hero) {
  animation: fadeIn 0.3s ease-in;
}

/* Trigger in JS: document.startViewTransition(() => updateDOM()) */

Note View transitions animate between DOM states with a cross-fade by default. Assign view-transition-name to elements that should animate individually rather than cross-fading with the whole page. Each name must be unique on the page. Triggered via JavaScript's document.startViewTransition() API.

Smooth Scrolling

syntax
scroll-behavior: smooth | auto;
example
html {
  scroll-behavior: smooth;
}

@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }
}

Note Applies to scrolling triggered by anchor links and JavaScript scrollTo/scrollIntoView calls. Always disable smooth scrolling for users who prefer reduced motion. This does not affect user-initiated scroll (mouse wheel, trackpad).

Will-Change & Performance

syntax
will-change: property;
/* Remove after animation completes */
example
.animated-card {
  will-change: transform;
  transition: transform 0.3s ease;
}

/* Apply will-change only when needed */
.card-container:hover .animated-card {
  will-change: transform;
}

.animated-card:hover {
  transform: scale(1.05);
}

Note will-change hints to the browser to prepare for an animation, promoting the element to its own compositor layer. Overusing it wastes memory. Apply it only to elements that will actually animate and remove it after the animation finishes if possible.

Modern CSS

Native CSS Nesting

syntax
.parent {
  /* parent styles */
  .child {
    /* nested child styles */
  }
  &:hover {
    /* parent hover */
  }
}
example
.card {
  padding: 16px;
  border-radius: 8px;

  .card-title {
    font-size: 1.25rem;
    font-weight: 600;
  }

  .card-body {
    margin-top: 8px;
    color: #4b5563;
  }

  &:hover {
    box-shadow: 0 4px 12px rgb(0 0 0 / 0.1);
  }

  @media (min-width: 768px) {
    padding: 24px;
  }
}

Note Native CSS nesting works in all major browsers without a preprocessor. Use & for pseudo-classes and pseudo-elements. Media queries can be nested inside rules. You no longer need the & prefix for simple descendant selectors like .child.

Cascade Layers (@layer)

syntax
@layer layer-name { ... }
@layer base, components, utilities;
example
/* Declare layer order (lowest to highest priority) */
@layer reset, base, components, utilities;

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

@layer base {
  body { font-family: system-ui, sans-serif; line-height: 1.6; }
  a { color: #2563eb; }
}

@layer components {
  .button { padding: 8px 16px; border-radius: 6px; }
}

@layer utilities {
  .hidden { display: none; }
  .mt-4 { margin-top: 16px; }
}

Note Layers control the cascade order regardless of specificity or source order within each layer. Later layers in the declaration beat earlier ones. Unlayered styles beat all layered styles. This solves specificity wars with third-party CSS libraries.

@scope

syntax
@scope (.scope-root) to (.scope-limit) {
  /* styles apply between root and limit */
}
example
@scope (.card) to (.card-footer) {
  p {
    color: #374151;
    line-height: 1.6;
  }

  a {
    color: #2563eb;
    text-decoration: underline;
  }
}

/* The .card-footer and its contents are excluded */

Note @scope limits where styles apply by defining both a root and an optional lower boundary. Styles only match elements between the root and the limit, not inside the limit. This provides true component scoping without build tools.

@property (Typed Custom Properties)

syntax
@property --name {
  syntax: "<type>";
  inherits: true | false;
  initial-value: value;
}
example
@property --gradient-angle {
  syntax: "<angle>";
  inherits: false;
  initial-value: 0deg;
}

.gradient-box {
  --gradient-angle: 0deg;
  background: linear-gradient(var(--gradient-angle), #3b82f6, #8b5cf6);
  transition: --gradient-angle 0.5s ease;
}

.gradient-box:hover {
  --gradient-angle: 180deg;
}

Note @property registers a custom property with a specific type, enabling the browser to animate it. Without registration, custom properties are strings that cannot be transitioned. Supported types include <color>, <length>, <angle>, <number>, <percentage>.

Advanced :has() Patterns

syntax
/* Form validation */
.field:has(input:invalid) { }
/* State-driven layouts */
body:has(.modal[open]) { }
example
/* Dim page when modal is open */
body:has(dialog[open]) {
  overflow: hidden;
}

/* Change grid layout based on number of children */
.grid:has(> :nth-child(4)) {
  grid-template-columns: repeat(2, 1fr);
}

.grid:has(> :nth-child(7)) {
  grid-template-columns: repeat(3, 1fr);
}

/* Style label when sibling input is focused */
.field:has(input:focus) label {
  color: #2563eb;
}

Note :has() can check for any condition: child state, sibling state, attribute presence, or even count of children. It enables previously impossible CSS-only patterns like conditional layouts and form state styling without JavaScript.

Scroll-Driven Animations

syntax
animation-timeline: scroll() | view();
animation-range: start end;
example
/* Progress bar tied to page scroll */
.scroll-progress {
  position: fixed;
  top: 0;
  left: 0;
  height: 3px;
  background: #3b82f6;
  transform-origin: left;
  animation: scaleProgress linear;
  animation-timeline: scroll();
}

@keyframes scaleProgress {
  from { transform: scaleX(0); }
  to { transform: scaleX(1); }
}

/* Fade in when element enters viewport */
.reveal {
  animation: fadeIn linear;
  animation-timeline: view();
  animation-range: entry 0% entry 100%;
}

@keyframes fadeIn {
  from { opacity: 0; transform: translateY(30px); }
  to { opacity: 1; transform: translateY(0); }
}

Note scroll() ties animation progress to scroll position of a scroll container. view() ties it to the element's visibility in the viewport. animation-range controls which portion of the scroll/view timeline the animation occupies. No JavaScript needed for scroll-linked effects.

CSS Anchor Positioning

syntax
.anchor {
  anchor-name: --name;
}
.tooltip {
  position: absolute;
  position-anchor: --name;
  top: anchor(bottom);
  left: anchor(center);
}
example
.trigger {
  anchor-name: --trigger;
}

.popover {
  position: absolute;
  position-anchor: --trigger;
  bottom: anchor(top);
  left: anchor(center);
  translate: -50% 0;
  margin-bottom: 8px;

  /* Fallback if not enough space above */
  position-try-fallbacks: flip-block;
}

Note Anchor positioning lets you position elements relative to other elements anywhere in the DOM without a shared parent. position-try-fallbacks automatically repositions (flips) when the element would overflow the viewport. This replaces JavaScript-based tooltip/popover positioning libraries.

Light-Dark Function

syntax
color-scheme: light dark;
color: light-dark(light-value, dark-value);
example
:root {
  color-scheme: light dark;
}

body {
  background: light-dark(#ffffff, #0f172a);
  color: light-dark(#1e293b, #e2e8f0);
}

.card {
  background: light-dark(#f8fafc, #1e293b);
  border: 1px solid light-dark(#e2e8f0, #334155);
}

Note light-dark() provides dark mode support inline without media queries. It uses the color-scheme property to determine which value to use. Declare color-scheme: light dark on :root to enable it. This is much simpler than managing separate media query blocks for colors.

Popover API (HTML + CSS)

syntax
<button popovertarget="id">Toggle</button>
<div id="id" popover>Content</div>
example
<button popovertarget="menu">Open Menu</button>
<div id="menu" popover>
  <nav>
    <a href="/settings">Settings</a>
    <a href="/profile">Profile</a>
    <a href="/logout">Log Out</a>
  </nav>
</div>

<style>
[popover] {
  padding: 16px;
  border: 1px solid #e5e7eb;
  border-radius: 8px;
  box-shadow: 0 4px 12px rgb(0 0 0 / 0.15);
}

[popover]::backdrop {
  background: rgb(0 0 0 / 0.1);
}
</style>

Note The popover attribute creates a top-layer element that dismisses on outside click or Escape key, with no JavaScript required. It handles focus trapping and accessibility automatically. Style the ::backdrop pseudo-element for the overlay behind it.

@starting-style (Entry Animations)

syntax
@starting-style {
  .element {
    /* initial state before transition */
  }
}
example
dialog[open] {
  opacity: 1;
  transform: scale(1);
  transition: opacity 0.3s ease, transform 0.3s ease,
             display 0.3s ease allow-discrete;
}

@starting-style {
  dialog[open] {
    opacity: 0;
    transform: scale(0.95);
  }
}

/* Exit animation */
dialog:not([open]) {
  opacity: 0;
  transform: scale(0.95);
}

Note @starting-style defines the initial state for elements transitioning from display: none (or not yet rendered). Without it, browsers cannot animate elements that appear for the first time. Combined with allow-discrete on the display property, it enables pure CSS enter/exit animations.

Modern Text Wrapping

syntax
text-wrap: wrap | nowrap | balance | pretty | stable;
example
h1, h2, h3, h4 {
  text-wrap: balance;
}

article p {
  text-wrap: pretty;
}

.editable-field {
  text-wrap: stable;
}

Note balance evenly distributes text across lines (best for headings). pretty avoids orphaned words on the last line (best for paragraphs). stable prevents text from reflowing while the user is editing. These require no JavaScript and are progressive enhancements.

Relative Color Syntax

syntax
color: oklch(from var(--base) calc(l * factor) c h);
example
:root {
  --brand: oklch(0.6 0.15 250);
}

.button {
  background: var(--brand);
}

.button:hover {
  /* 20% darker, same chroma and hue */
  background: oklch(from var(--brand) calc(l - 0.1) c h);
}

.button-light {
  /* Much lighter variant */
  background: oklch(from var(--brand) 0.95 calc(c * 0.3) h);
}

Note Relative color syntax creates color variations from a base color by decomposing it into components and modifying them with calc(). This works in any color space (oklch, hsl, srgb). It is the most powerful approach for generating color palettes from a single token.