MoreRSS

site iconAdam ArgyleModify

Web design & development tips & tricks: CSS, JS, HTML, Design, & UX
Please copy the RSS to your reader, or quickly subscribe to:

Inoreader Feedly Follow Feedbin Local Reader

Rss preview of Blog of Adam Argyle

Anchor Interpolated Morph (AIM)

2026-01-24 01:04:10

A ditto pokemon holding an anchor on an island

It's more natural when animations start near the trigger point, this CSS technique makes it easy. AIM animates from any element, and to any element.

Thanks to anchor(), anchor-size(), @starting-style, and interpolate-size.

All together they can create an anchor interpolated morph: an interruptible contextual transition from where a user invoked it.

That's a popover=hint + interestfor,
no JS CSS transition. Try it

This is a very powerful and capable feature combination.

Similar results are visually possible with child elements using their parent as the anchor, but:

  1. What if they're not ancestors?
  2. What if one is in the :top-layer?
  3. What if you want semantic HTML and a healthy accessibility OM?

AIM is the remedy.

FLIP #

This technique is inspired by Paul Lewis's coined animation technique called FLIP (First, Last, Invert, Play).

The gist of FLIP is to use JS to calculate the difference in positions/size/whatever of an element, before and after some DOM change.

AIM #

The gist of AIM is to use CSS to access properties from where another element is (first), to where natural render is (last), and let transition handle invert and play.

First: anchor(), anchor-size() & @starting-style
Last: interpolate-size: allow-keywords & auto

The roles where JS was accessing properties, measuring natural sizes, and calculating the differences… are now handled by CSS. Definitely not 100% like FLIP 😅 but hopefully you see the connection.

Here's a less designed example (so there's less CSS to sift through) that's a <dialog> element:

Try it. You can uncomment a top-left position example too!

How it works #

1: The anchor-name property is used to identify the element that the element is morphing from.

button {
  anchor-name: --⚓︎-morph; 
}

Can you believe this one line of CSS enables any other element to observe the height, width, and position of this element 🤯

Want more about that emoji naming convention?

2: Link up the dialog with that anchor.

dialog {
  position-anchor: --⚓︎-morph;

  /* adjust position as desired */
  left: anchor(left);
  top: anchor(top);
}

3: Enable transitioning to auto width/height.

dialog {
  interpolate-size: allow-keywords;
}

4: Enable transitions for all properties you want in the morph.

dialog {
  @media (prefers-reduced-motion: no-preference) {
    transition:
      display var(--_speed) allow-discrete,
      overlay var(--_speed) allow-discrete,
      height  var(--_speed) var(--ease-3),
      width   var(--_speed) var(--ease-3),
      left    var(--_speed) var(--ease-3),
      top     var(--_speed) var(--ease-3);
  }
}

5: Now we can use @starting-style to apply the styles from the anchor to the dialog before it opens. I call this "enter stage from" styles, and you can apply as many of the anchor values as you want.

I specify all corners here and setup a mask system to allow the dialog to transition to fit it's content, from the anchor size, without distorting the dialog contents.

dialog {
  @starting-style {
    &[open] {
      left: anchor(left);
      top: anchor(top);
      right: anchor(right);
      bottom: anchor(bottom);
      width: anchor-size(width);
      height: anchor-size(height);
    }
  }
}

Notice anchor-size() for height and width, this combined with height and width auto and interpolate-size: allow-keywords will allow the dialog to transition to its natural size from the anchor with a nice reveal.

And for exit out on the dialog, it's similar as enter stage in my example but you could easily transition a different way or to a different element.

dialog {
  &:not([open]) {    
    left: anchor(left);
    top: anchor(top);
    right: anchor(right);
    bottom: anchor(bottom);
    width: anchor-size(width);
    height: anchor-size(height);
  }
}

That's it for a dialog!

See the Pen by Adam Argyle (@argyleink) on CodePen.

AIM can be used for:

  • Dialogs
  • Popovers
  • Tooltips
  • Page preview
  • Fullscreen takeover (go big!)
  • Quick actions
  • Disclosures
  • Submenus
  • Hovercards
  • Particles? add some CSS random()
  • Other overlay components?

Here's the popover from the video at the beginning. It's using all the above techniques, plus some extras, precise timings, easings and intentionality.

See the Pen by Adam Argyle (@argyleink) on CodePen.

View Transitions #

What about view transitions and morphing, don't they do that too?

Yep! They do, and can do some neat morphing tricks. Here's a view transitions demo I called "anything to anything".

See the Pen by Adam Argyle (@argyleink) on CodePen.

But… that requires JS, is always a straight line and is not elegantly interruptible.

"always straight lines"… once you notice it you can't unsee it. View transitions can't curve or swing to a new destination.

Here's some slides I made using the Astro Morphull repo I made that use no JS view transitions so content morphs: https://cascadiajs-2025.netlify.app.

Spatial Continuity #

Traditional transitions often appear out of thin air or fade in from a generic center point. By leveraging anchor() and @starting-style, we can let CSS tell a story about where an element came from.

We're telling a story about where an element came from and why it’s there.

Go forth and make more natural transitions that feel spatially contextual. Stop letting your elements "teleport" into view, start letting them grow, stretch, and evolve directly from the components that triggered them.

Your users (and their muscle memory) will thank you. Speaking of, don't forget to gate the motion behind a prefers-reduced-motion media query (test the dialog and popovers).

Hot tip: You could easily morph to a different element than the invoking element, anchor() doesnt care.

Summary checklist for AIM:

  1. Set the Anchor:
    anchor-name on the invoking element to create a spatial reference.
  2. Set "Enter From" and "Exit To" styles with anchor():
    Use @starting-style to declare the entry and exit properties relative to the anchor.
  3. Transition:
    Orchestrate transition properties to allow the browser to interpolate the path, shapes and colors between the anchor point and the final layout.

Custom Media

2026-01-23 12:46:28

@custom-media working behind a flag in Firefox Nightly!

@custom-media --motionOK (prefers-reduced-motion: no-preference);

@media (--motionOK) {
  transition: transform .3s ease;
}

Lovely syntax. Open Props has ~45 ready to go, peep the spec, or visit MDN for more info.

Overscroll Effects On Nested Scrollers In All Browsers

2026-01-21 12:20:31

Finally… Chrome 145 joins WebKit and Firefox in supporting overscroll effects on nested scrollers. No more bonk UX, get the same soft edge bounce as the main page has always had!

❤️ Ty Robert!

WWW Ep227 Ai And Oath With Will Johnson

2026-01-15 08:00:00

some title

Ep #227
AI and Oath with Will Johnson

Robbie and I talk with Will Johnson, senior developer advocate at Auth0, about web dev, parenting, and internet culture.

whiskey.fm · youtube · spotify · apple

WWW Ep226 A Very Merry Descent Into Holiday Madness

2026-01-08 08:00:00

some title

Ep #226
A very merry descent into holiday madness

Robbie and I talk about holiday travel chaos, parenting stress, and health scares before diving into AI-assisted coding, Linux quirks, IDE trends, burnout in open source, and more.

whiskey.fm · youtube · spotify · apple

4 CSS Features Every Front-End Developer Should Know In 2026

2026-01-07 15:01:00

Punk graffiti and sticker style image saying 4 CSS features every front-end developer should know in 2026

2026; I think every front-end developer should know how to query scroll states, trim typographic whitespace, stagger with sibling-index(), and use type safe attr().

This is only some of the CSS that shipped in 2025 you need to know.


This post is a themed post!
Check out 2023, 2024 and 2025.

sibling-index() and sibling-count() #

Earlier this year these were just experiments, now they're available in stable Chrome and Safari!

They let you use an element's position relative to its siblings as values in calculations. For example, you can stagger elements with a transition delay based on their sibling-index().

A nice trick is to subtract 1 so the first element starts immediately:

li {
  transition: opacity .3s ease;
  transition-delay: calc((sibling-index() - 1) * 100ms);
}

Stagger enter stage animations easily by combining with @starting-style!

li {
  transition: opacity .3s ease;
  transition-delay: calc((sibling-index() - 1) * 100ms);

  @starting-style {
    opacity: 0;
  }
}

See the Pen by Adam Argyle (@argyleink) on CodePen.

You can rotate hues in oklch, automatically number elements, and all sorts of fun things.

Resources

@container scroll-state() #

These features fit nicely into progressive enhancement, similar to scroll driven animations, as they're enhancements rather than requirements. IMO at least.

Three states of a scroller are now queryable:
stuck, snapped, scrollable and scrolled.

To start, you need the element that's stuck, snapped, or scrollable to have container-type: scroll-state. Then, a child can query it with @container scroll-state().

An element cannot query itself, but its pseudo elements can!

stuck #

Perfectly know when position: sticky elements are stuck.

/* when .outer-navbar is stuck */
@container scroll-state(stuck) {
  .inner-navbar {
    box-shadow: var(--shadow-3);
  }
}

Use this to help users understand an element will now be overlaying their scroll content.

snapped #

Perfectly know when scroll-snap alignment is active.

/* when <li> parent is snapped */
@container scroll-state(snapped) {
  .box {
    scale: 1.1;
  }
}

/* or not snapped! */
@container not scroll-state(snapped) {
  .box figcaption {
    translate: 0 100%;
  }
}

Great for highlighting the item, or demoting every other item.

scrollable #

Perfectly know when content overflows its container, and in which direction(s).

@container scroll-state(scrollable) {
  .scroll-hint {
    opacity: 1;
  }
}

Use this to toggle hints, scroll indicators, or adjust padding to signal more content.

scrolled #

Perfectly know when content is scrolled in a direction.

@container scroll-state(scrolled: bottom) {
  translate: 0 -100%;
}

Use this for sticky headers or navbars that showy hidey based on scroll direction.

Resources

text-box #

text-box lets you slice half-leading right off a text box!

Web font rendering includes whitespace above and below glyphs for "safe spacing", but sometimes you want pixel-perfect alignment to baselines or x-heights.

Achieve the above image with:

h1 {
  text-box: trim-both cap alphabetic;
}

That one-liner trims spacing above cap height and below alphabetic baselines.

Learn more in my interactive notebook

Perfect for type and grid alignment nerds. I think it'll become the default.

Resources

typed attr() #

There's an advanced version of attr(); type-safe and more powerful.

It allows using HTML attributes directly in CSS with type checking and fallbacks.

Pass colors:

<div data-bg="white" data-fg="deeppink"></div>
.theme {
  background: attr(data-bg color, black);
  color: attr(data-fg color, white);
}

Pass numbers:

<div class="grid" data-columns="3">…</div>
.grid {
  --_columns: attr(data-columns number, 3);

  display: grid;
  grid-template-columns: repeat(var(--_columns), 1fr);
}

This creates a powerful bridge between HTML and CSS.

Here's a scroll snap example where CSS basically controls the enums and HTML must pass valid values to get the desired snap results:

<li scroll-snap="start"></li>
<li scroll-snap="center"></li>
<li scroll-snap="end"></li>
<li scroll-snap="nothing"></li>
[scroll-snap] {
  scroll-snap-align: attr(scroll-snap type(start | center | end));
}

The type() function validates attribute values against allowed keywords. Invalid values fall back gracefully.

Try it on Codepen

Resources

CSS rules.

Now let's laugh at this hilarious obvious AI generated nano banana image I made about us having our cake and eating it too.

Still can't do hands lol