Back to Blog
    Web Development

    CSS in 2026: Anchor Positioning, Scroll-Driven Animations, @starting-style & More

    Mortex Solutions TeamFebruary 16, 202616 min read
    CSS in 2026: Anchor Positioning, Scroll-Driven Animations, @starting-style & More

    CSS in 2026 is more powerful than ever, with features that previously required JavaScript now built natively into the language. From anchor positioning to scroll-driven animations, here's everything you need to know.

    CSS code on screen

    🎨 The Modern CSS Feature Set

    • Anchor Positioning — Position elements relative to any other element
    • Scroll-Driven Animations — Animate based on scroll position, no JS needed
    • @starting-style — Animate elements on first render
    • Container Queries — Style based on parent size, not viewport
    • :has() Selector — The "parent selector" CSS always needed
    • View Transitions API — Smooth page transitions natively

    1. Anchor Positioning

    Position tooltips, popovers, and dropdowns relative to any element — no JavaScript positioning libraries needed.

    /* Define the anchor */
    .trigger-button {
      anchor-name: --my-anchor;
    }
    
    /* Position relative to the anchor */
    .tooltip {
      position: fixed;
      position-anchor: --my-anchor;
    
      /* Place below the anchor, centred horizontally */
      top: anchor(bottom);
      left: anchor(center);
      translate: -50% 8px;
    
      /* Auto-flip if it overflows the viewport */
      position-try-fallbacks: flip-block, flip-inline;
    }
    
    /* Works with any element pair — no wrapper needed! */

    2. Scroll-Driven Animations

    Create parallax effects, progress indicators, and reveal animations driven purely by scroll — zero JavaScript.

    /* Progress bar that fills as you scroll the page */
    .reading-progress {
      position: fixed;
      top: 0;
      left: 0;
      width: 100%;
      height: 4px;
      background: linear-gradient(to right, #3b82f6, #8b5cf6);
      transform-origin: left;
      animation: grow-progress linear;
      animation-timeline: scroll();
    }
    
    @keyframes grow-progress {
      from { transform: scaleX(0); }
      to   { transform: scaleX(1); }
    }
    
    /* Fade-in elements as they scroll into view */
    .reveal-on-scroll {
      animation: fade-in linear both;
      animation-timeline: view();
      animation-range: entry 0% entry 100%;
    }
    
    @keyframes fade-in {
      from { opacity: 0; transform: translateY(40px); }
      to   { opacity: 1; transform: translateY(0); }
    }

    3. @starting-style — Entry Animations

    Animate elements when they first appear in the DOM — perfect for dialogs, toasts, and dynamically added content.

    /* Dialog with entry animation */
    dialog[open] {
      opacity: 1;
      transform: scale(1);
      transition: opacity 0.3s, transform 0.3s, display 0.3s allow-discrete;
    
      @starting-style {
        opacity: 0;
        transform: scale(0.9);
      }
    }
    
    /* Toast notification that slides in */
    .toast {
      translate: 0 0;
      opacity: 1;
      transition: translate 0.4s ease-out, opacity 0.4s;
    
      @starting-style {
        translate: 100% 0;
        opacity: 0;
      }
    }

    4. Container Queries

    Style components based on their parent's size, not the viewport. This is the holy grail for component-based design.

    /* Define a container */
    .card-wrapper {
      container-type: inline-size;
      container-name: card;
    }
    
    /* Style based on container width */
    @container card (min-width: 400px) {
      .card {
        display: grid;
        grid-template-columns: 200px 1fr;
        gap: 1rem;
      }
    
      .card-image {
        aspect-ratio: 1;
      }
    }
    
    @container card (max-width: 399px) {
      .card {
        display: flex;
        flex-direction: column;
      }
    
      .card-image {
        aspect-ratio: 16/9;
      }
    }

    5. The :has() Selector

    Select parents based on their children. This was CSS's most requested feature for over a decade.

    /* Style a form group that contains an invalid input */
    .form-group:has(input:invalid) {
      border-color: red;
    }
    
    .form-group:has(input:invalid) .error-message {
      display: block;
    }
    
    /* Style a card differently if it has an image */
    .card:has(img) {
      grid-template-rows: 200px auto;
    }
    
    .card:not(:has(img)) {
      padding-top: 2rem;
    }
    
    /* Dark mode toggle without JS */
    html:has(#dark-mode:checked) {
      color-scheme: dark;
      --bg: #0f172a;
      --text: #e2e8f0;
    }

    6. View Transitions API

    /* Smooth page transitions — works with MPA and SPA */
    @view-transition {
      navigation: auto;
    }
    
    /* Customise the transition animation */
    ::view-transition-old(root) {
      animation: slide-out 0.3s ease-in;
    }
    
    ::view-transition-new(root) {
      animation: slide-in 0.3s ease-out;
    }
    
    @keyframes slide-out {
      to { transform: translateX(-100%); opacity: 0; }
    }
    
    @keyframes slide-in {
      from { transform: translateX(100%); opacity: 0; }
    }
    
    /* Named transitions for specific elements */
    .hero-image {
      view-transition-name: hero;
    }
    /* The hero image morphs smoothly between pages! */
    🎨 Key Takeaway: CSS in 2026 can do what previously required JavaScript libraries — scroll animations, anchor positioning, entry animations, and page transitions. Less JS = faster pages. Start using these features today with progressive enhancement.