> All in One 586: December 2025

Ads

Wednesday, December 31, 2025

Partly Cloudy today!



With a high of F and a low of 32F. Currently, it's 34F and Clear outside.

Current wind speeds: 9 from the Southwest

Pollen: 0

Sunrise: December 31, 2025 at 08:11PM

Sunset: January 1, 2026 at 05:38AM

UV index: 0

Humidity: 46%

via https://ift.tt/pJdhK8M

January 1, 2026 at 10:02AM

What’s !important #2: Conditional View Transitions, CSS/SVG Text Effects, the Best of CSS Bluesky, and More

2026 is almost upon us. I know we’re all itching to see the clock strike midnight (cue The Final Countdown by Europe), but not without recapping the best CSS-related things that happened over the last two weeks!

Conditional view transitions

Chris Coyier showed us how to trigger different view transitions based on the URL (or any conditional JavaScript logic, really). However, Bramus mentioned in the comments that navigation matching is coming to CSS, so we might not need to rely on JavaScript for that part for very long. I’m keeping my fingers crossed for 2026!

Annotating design system components for accessibility

Geri Reid carefully explained how to annotate design system components for accessibility, giving solid HTML-based examples. I especially love the part about defining elements, ARIA, markup, keyboard navigation, zoom, and user preferences as accessibility tokens.

A pagination component with annotations explaining the semantic HTML markup and ARIA labels.
Source: zeroheight.

Firefox to become a “modern AI browser” to the delight of absolutely no one

Firefox has been a bit of a rollercoaster lately. It started with an interview on The Verge, where new CEO Anthony Enzor-DeMeo said that Firefox “will evolve into a modern AI browser.” With only 2.3% of the browser market share, I suppose it was inevitable that Firefox would try to shake things up, but users don’t appear to want this at all. To stem the bleeding though, they’ve announced an AI kill switch.

And that’s how I met your Mo- …Waterfox, which is a no-AI fork of Firefox.

Creating ‘toon text with CSS and SVG

Andy Clarke demonstrated how to create the text effects of classic ‘toons using CSS and SVG. I hadn’t heard of the paint-order CSS property before this, so that part is pretty darn cool.

A comparison of Andy Clarke’s ‘Hum Sweet Hum’ text effect (left) and the Augie Doggie ‘toon version of the same text effect (right).
Source: Smashing Magazine.

6 CSS features that you should learn in…2025

That’s right, 2025! Adam Argyle wrote about the CSS features that you should learn in 2025 at the beginning of the year. Well, it’s almost 2026 now, so how did you do? I’ll be honest — I barely even touched the surface of @property.

Great CSS posts on Bluesky

Let’s be honest, social media isn’t a fun place to hang out anymore, but I’ll make the exception for Bluesky. I rarely see stuff that’s not relevant to my interests, I actually talk to people on there, there’s a large dev community on there, and of course CSS-Tricks is on Bluesky too, as is the CSS-Tricks team and many of our authors.

So since browser updates typically ship at the beginning of the month, here are some cool posts by the Bluesky CSS community instead:

Temani Afif demonstrated a very interesting combination of anchor positioning, attr(), container queries, and shape(), which you can play around with (probably requires Chrome):

💡 CSS Tip!Two circles, one arrow, and CSS magic. 🪄A cool demo packed with modern features (anchor positioning, attr(), container queries, shape(), and more!) 🤩css-tip.com/connected-ci…The shape and position of the arrow are fully controlled using CSS (Yes, there is a collision detection).

CSS by T. Afif (@css-only.dev) 2025-12-16T11:21:17.632Z

Stephen Margheim listed the various reasons why you should namespace your semantic classes (e.g., rename .btn to .ui-button):

If you're going to write semantic CSS classes in 2025, don't call them .btn or .card.Use a ui-* prefix: ui-button, ui-input, ui-badge.Here's why naming matters more than you think…

Stephen Margheim (@fractaledmind.bsky.social) 2025-12-25T11:02:03.013434Z

Similar to Andy Clarke’s ‘toon text explorations, Ana Tudor’s displacement map text effects are rather astonishing as well, although you’ll most likely need Chrome for cutting-edge stuff like this:

I personally love displacement maps.When they work, they can do really cool stuff, not just shrinking/ expansion, but also rotation (codepen.io/thebabydino/…), 3D effects (codepen.io/thebabydino/…), the bubbling lens effect (codepen.io/thebabydino/…).None of these duplicate/ split text.

Ana Tudor (@anatudor.bsky.social) 2025-12-26T11:41:39.033Z

There are more displacement map text effects in Ana’s feed.

George Black said:

Writing CSS for my site that uses modern features only available in Chrome or Safari, but not both. Eventual consistency — eventually it’ll look right in one browser… right?

Actually, yes! Most likely. For those that don’t know, the Interop Project is a collaboration between Chrome, Firefox, and Safari where they make certain features work consistently across their browsers. You can even vote on the features! Keen to learn more? Read what Chrome and WebKit have said about Interop 2026.

Writing CSS for my site that uses modern features only available in Chrome or Safari, but not both.Eventual consistency — eventually it’ll look right in one browser… right?

George Black (@george.black) 2025-12-28T21:14:06.209Z

Finally, a massive shout-out to our very own Sunkanmi Fafowora for saying what we’re all thinking:

Thank you to all who work very hard to expand and push the boundaries of what we can do with CSS. You’ve made my work and the work of millions of others so much easier. You’re not unnoticed, and I do hope you’re having an amazing day.

Thank you to all who work very hard to expand and push the boundaries of what we can do with CSS. You've made my work and the work of millions of others so much easier.You're not unnoticed, and I do hope you're having an amazing day.💛

Sunkanmi Fafowora (@sunkanmifafowora.bsky.social) 2025-12-17T12:26:42.271Z

On that note, the CSS-Tricks team wishes you a very happy new year. May your websites be free of bugs and work in all (stable) browsers!

See you in 2026!


What’s !important #2: Conditional View Transitions, CSS/SVG Text Effects, the Best of CSS Bluesky, and More originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.



from CSS-Tricks https://ift.tt/VDZj8uh
via IFTTT

Tuesday, December 30, 2025

Clear today!



With a high of F and a low of 24F. Currently, it's 29F and Clear outside.

Current wind speeds: 6 from the West

Pollen: 0

Sunrise: December 30, 2025 at 08:10PM

Sunset: December 31, 2025 at 05:38AM

UV index: 0

Humidity: 51%

via https://ift.tt/i6Q3cbY

December 31, 2025 at 10:02AM

Monday, December 29, 2025

Clear today!



With a high of F and a low of 19F. Currently, it's 20F and Clear outside.

Current wind speeds: 8 from the Southwest

Pollen: 0

Sunrise: December 29, 2025 at 08:10PM

Sunset: December 30, 2025 at 05:37AM

UV index: 0

Humidity: 54%

via https://ift.tt/4kbLynK

December 30, 2025 at 10:02AM

Sunday, December 28, 2025

Clear today!



With a high of F and a low of 10F. Currently, it's 14F and Clear outside.

Current wind speeds: 6 from the Northwest

Pollen: 0

Sunrise: December 28, 2025 at 08:10PM

Sunset: December 29, 2025 at 05:36AM

UV index: 0

Humidity: 66%

via https://ift.tt/GpsO9lq

December 29, 2025 at 10:02AM

Saturday, December 27, 2025

Snow Showers/Wind Late today!



With a high of F and a low of 17F. Currently, it's 45F and Clear outside.

Current wind speeds: 10 from the Northwest

Pollen: 0

Sunrise: December 27, 2025 at 08:10PM

Sunset: December 28, 2025 at 05:35AM

UV index: 0

Humidity: 47%

via https://ift.tt/MvOoDQi

December 28, 2025 at 10:02AM

Friday, December 26, 2025

Partly Cloudy today!



With a high of F and a low of 38F. Currently, it's 45F and Clear outside.

Current wind speeds: 13 from the Southwest

Pollen: 0

Sunrise: December 26, 2025 at 08:09PM

Sunset: December 27, 2025 at 05:35AM

UV index: 0

Humidity: 41%

via https://ift.tt/QeFTAlo

December 27, 2025 at 10:02AM

Thursday, December 25, 2025

Mostly Clear today!



With a high of F and a low of 36F. Currently, it's 44F and Clear outside.

Current wind speeds: 9 from the West

Pollen: 0

Sunrise: December 25, 2025 at 08:09PM

Sunset: December 26, 2025 at 05:34AM

UV index: 0

Humidity: 43%

via https://ift.tt/kqHcGw2

December 26, 2025 at 10:02AM

Wednesday, December 24, 2025

Mostly Clear today!



With a high of F and a low of 39F. Currently, it's 45F and Clear outside.

Current wind speeds: 6 from the Southeast

Pollen: 0

Sunrise: December 24, 2025 at 08:09PM

Sunset: December 25, 2025 at 05:33AM

UV index: 0

Humidity: 34%

via https://ift.tt/Rv4SomM

December 25, 2025 at 10:02AM

Tuesday, December 23, 2025

Cloudy today!



With a high of F and a low of 42F. Currently, it's 45F and Clear outside.

Current wind speeds: 10 from the South

Pollen: 0

Sunrise: December 23, 2025 at 08:08PM

Sunset: December 24, 2025 at 05:33AM

UV index: 0

Humidity: 45%

via https://ift.tt/XQfnUeV

December 24, 2025 at 10:02AM

Thank You (2025 Edition)

If I had to whittle down my favorite thing about CSS-Tricks to one thing — and like Tom Petty hits, there’s a lot of ’em — it’s that we take moments like this to hit Pause and simply write like humans. We’re a super technical site that gets into super technical web dev jargon and yet we’re just a set of people trying to learn together with other people and be better at what we do, whether that’s design, development, accessibility, performance, or any specialty flavor from the wide array of front-end responsibilities.

This is my tenth year here at CSS-Tricks. I remember when CSS-Tricks turned 10. It felt really old (in the best possible way) back then. So imagine how I feel about my age today.

What I’m ultimately trying to say is Thank You. Because of real people like you and the small team of folks who contribute here, I get to make this my day job. It’s the best job I’ve had in my life and it’s only possible because you keep showing up each day to read, learn, share, and discuss all-things-front-end (and a little CSS, of course) with us.

Thank you, thank youthank you!

Allow me a chance to share some highlights from 2025 — a year with a bunch of milestones and (let’s say) interesting twists.

Overall Site Traffic

Jumping straight to it: there were 20 million unique views in 2025. This is a huge drop-off from last year’s 26 million… and you can literally see the cliff in July when Google added AI summaries to the top of search results. So, yeah, we’re down an alarming 23% for the year, but the real month-over-month impact is more like 30%. Ouch.

It hurts — and I’m always quick to blame myself — but is also consistent with other sites I work with and what I’ve been hearing from other publishers in this space as we’ve compared battle notes.

I hate saying “it is what it is” but we really are in the midst of a new reality in digital publishing. I’m still trying to wrap my head around it because, obviously, eyeballs pay the bills around here and we’re going to have to adapt. There’s still plenty of reasons to make websites today. Making them sustainable, though? That’s getting harder, even if new CSS features are making the development way more fun.

Which brings me full circle to the start of this post: Thank you for showing up. It means the world to us and to me personally. The first and best way you can support CSS-Tricks is to continue showing up.

Publishing By The Numbers

Looking at a little history of how many articles we’ve published by year:

  • 2020: 1,183 articles
  • 2021: 890 articles (site acquired by DigitalOcean)
  • 2022: 390 articles
  • 2023: 0 articles (site paused)
  • 2024: 153 articles (site resumed in late June)

This year? We’re looking at 255 articles. Considering there are 261 weekdays in 2025, that’s nearly an article per day… and we’re not done yet with the year. In fact, if we count this post and what’s left on the publishing calendar, we’re looking at exactly 261 articles for the year. It’s sorta like we’re your weekday companion at work!

The CSS-Tricks publishing schedule for December 2025 in calendar format in Notion.
The CSS-Tricks publishing schedule is always cookin’.

That includes the largest infusion to the Almanac in a year, perhaps ever? We added 101 Almanac entries as of today (one for each Dalmatian) and will add three more by the end of the year. The Almanac is my favorite part of the site. Sure, you can get great CSS documentation from somewhere like MDN, but I think the time and effort we put into explaining CSS features like one developer talking to another sets us apart. Where else are you going to enjoy learning about a trigonometry function like atan2(), right?

Let’s Keep Talking About the Almanac

…because that’s the area we invested most of our time and energy. You may remember that the Almanac has historically been a spot to learn about CSS properties and pseudos. Last year, though, we stuffed the Almanac’s mouth with a fistful of steroids and gave it new sections so that we’re covering all the CSS features we possibly can, including functions, selectors, and at-rules.

If you can believe it, I’m pretty sure we’ve added every single at-rule this year. And we started the year with a big ol’ zero CSS functions and are ending with a whopping 64 of them.

We also spent time making the Almanac a little easier to navigate. For example, now you can get high-level information about any feature without having to click through to the full page:

And we label experimental and shorthand properties:

And you can dig into the constituent properties for each shorthand:

Things wouldn’t be complete without a little dash of modern CSS. We sprinkled in a little scroll-driven animation action for good measure:

The Team

I introduced y’all to Juan Diego Rodriguez and Ryan Trimble last year as regular contributors. They play a big role and I don’t know what I’d do without them. They’re my second and third pair of eyes anytime I’m unsure of something and, let’s face it, that’s often.

But now we have a fourth pair of eyes! Danny Schwarz has been a long-time contributor and he’s stepping up to help us stay on top of timely things. CSS is moving faster than I can remember and it’s easy for things to slip under the radar even when your day job is tracking this stuff. Danny has the eyes of a hawk and has started reporting what he finds in what we’re calling What’s !Important. The first edition went out just the other day and we’ll keep that up on a bi-weekly basis for now.

In between editions, Danny publishes a feed of Quick Hits that you can follow for even more web platform news and happenings.

It takes a village, my friends!

Goal Review

Time to check in on the things we set out to accomplish last year and did… or didn’t.

Publish 1-2 new guides. Yes! We released CSS Color Functions and CSS Counters guides this past year. We actually nailed the goal back in June. I thought we might actually exceed the goal, but things settled down with all the work we put into the next goal.

Fill in the Almanac. No need to rehash all the work. Last year at this time, I said, “We’ve only got a few pages in the at-rules and functions sections that we recently created and could use all the help we can get.” I never imagined we’d end the year with 104 new pages, including all of the at-rules. I can’t thank the likes of Juan Diego, Danny, Sunkanmi Fafowora, John Rhea, and Gabriel Shoyombo enough for going above and beyond to make this one happen. Again, I’m incredibly proud of this treasure trove of CSS documentation and believe it’s a core part of what CSS-Tricks is.

🚫 Restart the newsletter. Nope on this one, and not for lack of effort. The truth is there are administrative hurdles behind the scenes preventing it from happening. We’ll get there though! It’s a piece of CSS-Tricks that I miss so dang much. Perhaps that’s the next evolution of What’s !Important.

🔶 More guest authors. I’ll give this one a passing grade. We welcomed a handful of new faces, including long-time friends who wrote with us for the first time, like Andy Clarke, Mat Marquis, Jeff Bridgforth, Declan Childlow, Blackle Mori, Christian Sabourin, Sladjana Stojanovic, and Darshan Siroya. Ideally, I’d like to see a fresh new set of faces publishing with us every month because that’s the sort of diversity that makes this community rich. We’re always accepting guest authors!

2026 Goals

RECOVER ALL THE LOST TRAFFIC! Just kidding, that’s only mildly in our control. We’ll continue the daily mission of serving you fresh, fun, and educational front-end goodness. While I’d like to tackle traffic woes head-on, the best plan will always be showing up and delivering the goods as best we can. In fact, we’ll carry over this year’s goals into the brand-new year. No need to shake up the recipe.

If I was given a magic lamp with one wish for the next year, I’d wish for full courses to be added to the site. I run a beginning level HTML/CSS online course separately and love how students interact with the lessons differently than the average reader interacts with a standalone tutorial or article. It’d be a moonshot to get something like that into CSS-Tricks in the next year, but I wouldn’t turn my nose up if the opportunity came up.

Again, Thank You!

Special thanks to DigitalOcean! It’s their backing that keeps this engine running, from hosting to finances and even helpful encouragement along the way. They have every right to do anything they want with this site and yet they choose to operate it like an independent publication. They’re not jamming their products and services down anyone’s throat, dictating the editorial direction of things, or constantly breathing down our necks. They’re good stewards and deserve a big collective high five!

A special shout out to Roxie Elliott for being the go-between on just about everything you can imagine. Her behind-the-scenes help has been incredibly valuable.

Like I said, it’s been an interesting year. Some ups, some downs, but plenty to be thankful for heading into what will be this site’s 19th birthday come July 2026. Nineteen years. Blockbuster still had a site then!

Thank you, Chris Coyier, for writing that very first blog post.


Thank You (2025 Edition) originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.



from CSS-Tricks https://ift.tt/60OkKzj
via IFTTT

Monday, December 22, 2025

Partly Cloudy today!



With a high of F and a low of 36F. Currently, it's 44F and Clear outside.

Current wind speeds: 8 from the West

Pollen: 0

Sunrise: December 22, 2025 at 08:08PM

Sunset: December 23, 2025 at 05:32AM

UV index: 0

Humidity: 45%

via https://ift.tt/216UvAe

December 23, 2025 at 10:02AM

text-decoration-inset is Like Padding for Text Decorations

The text-decoration-inset CSS property solves a problem that we’ve had since the beginning of the web, which is that text decorations such as underlines extend beyond the first and last characters (to the edges of the content box, to be specific), resulting in vertical misalignment.

A default blue link zoomed in to the spacing between the text underline and border box, showing that the underline extends all the way to the border box.

I say it’s a problem “we’ve” had rather sheepishly because maybe you, like some users, don’t actually care. But if you’re a funny bunny like me (I think “designer” is the technical term) then it most likely drives you crazy.

That being said, it’s not a problem that I’ve tried to fix because the juice just isn’t worth the squeeze. The best fix is probably text-decoration: none and ::after with a custom background, but this can be a bit finicky and I’d rather use all of the features that come with native text decorations, such as text-decoration-thickness, text-underline-position (which enables us to change the position of the underline relative to the font’s internal metrics; the baseline, for example), and text-underline-offset (which determines the offset from that position).

So, how does text-decoration-inset work? Well, if I trim an underline just enough for it to vertically align with the text, I wind up with this instead (this only works in Firefox 146, by the way):

A default blue link zoomed in to the spacing between the text underline and border box, showing that the underline does not extend all the way to the border box.

However, you can actually trim the decorations as much as you want, which enables us to create some really cool ones and even transition or animate them. Let’s take a quick look, shall we?

text-decoration-inset basic usage

text-decoration-inset, formerly text-decoration-trim, enables us to clip from the ends of the underline or whatever text-decoration-line is computed. This is the syntax:

text-decoration-inset: <left-inset> <right-inset>;

Yes, this means that we can set different inset values for the left and right sides.

These values must be <length>s, but we can use relative lengths such as em units, which are relative to the computed font-size. So, if the font-size changes, the insets scale with it. For example, in the demo above, 0.076em (which is what I’ve set as the left inset) means 7.6% of the computed font-size, and that’s the value that makes the left inset align with the left stem of the letter “N” and other left stems. This value was determined by trial and error, but it only needs to be determined once for each font.

If that first letter was, say, W? Yeah, then the inset wouldn’t align, so it’s not a perfect solution. I’d say that it’s suitable for when you know what the content will be.

Maybe the W3C will come up with a solution for vertically aligning text decorations as well as multiple lines of text both accurately and automatically. Until then, this is still a cool solution that enables us to create perfectly aligned effects like this (this demo uses an overline and an underline, and a whole ‘lotta text-decoration-thickness of course):

Animating text-decoration-inset

text-decoration-inset is more interesting when we start to think about transitions/animations. We often animate underlines, or should I say faux ::after underlines, but with text-decoration-inset we can do it natively. In the example below I multiply the insets by ten on :hover. Nothing too crazy, but remember that we can only use <length> values, so try to use em units, or at least test the text with different font sizes.

Again, Firefox 146+ required at the moment:

a {
  transition: 300ms;
  text-decoration-inset: 0.046em 0.009em;

  &:hover {
    text-decoration-inset: calc(0.046em * 10);
  }
}

Getting a bit more ambitious now, this next demo leverages a CSS @keyframes animation to create that shooting star underline effect. How it works is that we push the left inset all the way to the other side — but <length>s only, remember? We can’t use 100% here, so instead I’ve determined that the width of the element is 4.5em and used that as the value instead (the more precise we are, the better the animation or transition). Check the code comments for a full explanation:

a {
  /*
    The value at the start and end of the
    animation, as well as the default value
  */
  text-decoration-inset: 0.046em 0.009em;

  &:hover {
    animation: 1s next-level;
  }
}

@keyframes next-level {
  /* By half-way through the animation... */
  50% {
    /*
      ...the left inset has shifted 4.5em, 
      which is the full width of the element
    */
    text-decoration-inset: 4.5em 0.009em;

    /* It’s faded out as well */
    text-decoration-color: transparent;
  }

  /* Immediately after that... */
  50.999% {
    /* ...both insets are set to the left */
    text-decoration-inset: 0.046em 4.5em;
  }

  /* Then it animates back to the default value */
}

Overall, text-decoration-inset is a nice feature. It isn’t without its flaws, but no feature ever is. Personally, anything that helps me to refine a detail natively is very much welcome, and with text-decoration-inset we’re able to refine two — the text decoration alignment (relative to the text) and the text decoration transition or animation.


text-decoration-inset is Like Padding for Text Decorations originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.



from CSS-Tricks https://ift.tt/1wkpfP4
via IFTTT

Sunday, December 21, 2025

Partly Cloudy today!



With a high of F and a low of 36F. Currently, it's 39F and Clear outside.

Current wind speeds: 15 from the Southwest

Pollen: 0

Sunrise: December 21, 2025 at 08:07PM

Sunset: December 22, 2025 at 05:32AM

UV index: 0

Humidity: 50%

via https://ift.tt/3yjNGh7

December 22, 2025 at 10:02AM

Saturday, December 20, 2025

Partly Cloudy today!



With a high of F and a low of 25F. Currently, it's 27F and Clear outside.

Current wind speeds: 9 from the Southwest

Pollen: 0

Sunrise: December 20, 2025 at 08:07PM

Sunset: December 21, 2025 at 05:31AM

UV index: 0

Humidity: 72%

via https://ift.tt/onmhyid

December 21, 2025 at 10:02AM

Friday, December 19, 2025

Partly Cloudy today!



With a high of F and a low of 28F. Currently, it's 38F and Clear outside.

Current wind speeds: 8 from the West

Pollen: 0

Sunrise: December 19, 2025 at 08:06PM

Sunset: December 20, 2025 at 05:31AM

UV index: 0

Humidity: 45%

via https://ift.tt/uMEcbNA

December 20, 2025 at 10:02AM

Masonry Layout is Now grid-lanes

We’ve talked a lot about Masonry layout here on CSS-Tricks, so there’s no need to remind you of the two-sided discussions about how to approach it, the idea of using it as a new unifying layout concept, or alternative approaches for making it work today.

Here’s what you do need to know: it’s going to be display: grid-lanes.

A series of tall images arranged in five columns.

The earliest talks concerning masonry began in 2017 when Rachel Andrew expressed some concerns about how to make a Pinterest masonry layout. Rachel said it felt like the right approach should be a flexbox kind of thing, but this wasn’t fully achievable with flexbox, as items would rather flow from top-to-bottom rather than across each row:

Currently the closest you can get with CSS to this type of layout is to use multi-col however, the items then flow top to bottom rather than across the rows.

This feels more like a behaviour of flexbox than grid, as the solution is very much based on the size of the items. Opening this in order to record the feature request/use case for future discussion.

And so, the talks surrounding masonry layout in CSS began within the W3C. In that same GitHub thread, you’ll read that Tab Atkins-Bittner shared a “hacky” way to go about it with CSS Grid.

You can have Grid do Masonry if you know the heights of the items ahead of time. It’s a bit hacky, but you set the grid container to grid: auto-flow dense 1px / <column widths here>;, then set each item to grid-row: span <pixel height as integer>; (for example, if the item is 50px tall, use grid-row: span 50;).

I’m a sucker for clever CSS hacks, but you know what else I’m a sucker for? A working CSS solution that makes it easier for beginners to use. And no, unfortunately, we cannot use grid-template-rows or grid-template-columns, as Nate Green suggested in the same thread:

.figure-list {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  grid-template-rows: flow;
}

Atkins-Bittner replied:

There’s no “simple” way to adapt Grid into Masonry – anything would involve non-trivial edits to the layout algorithm. Packery, in particular, really needs things to have a defined width.

Okay, that’s fine. I’m sure you’d be thinking as well, “why not create a new display property for it?” Well, Rachel added that:

The thing is we can’t create a brand new value of display for every distinct design pattern.

Mind you, all these discussions were happening as far back as 2017, meaning masonry has been an elusive goal for quite some time.

Just to make sure you’re with me, this is an example image of the kind of layout masonry looks like (thanks to Michael Richins for this):

A series of nine green boxes arranged in three columns, each box with a large black number labeling their order in the layout.

One thing I would like is that prior to the announcement of masonry display type, there were solutions/hacks discussed, like this one from Andy Barefoot, but nothing truly was decided as the main go-to feature for CSS masonry. Hey, there was even a super cool Masonry library from David DeSandro, and he gave pretty useful advice to W3C and browser vendors when writing the spec and implementing the masonry layout, like on how loading images would work, multi-column spanning items, filling gaps, retaining horizontal order.

Check out the Masonry.js docs for more information about the object options.

This thread helped, but it still didn’t lead to a conclusive statement about what the masonry syntax will look like. In fact, we had code suggestions, like from Dan Tonon:

.flex-container {
  display: flex;
  flex-direction: column;
  flex-block-count: 2; 
  flex-block-flow: cross; 
}

This introduces two new flexbox properties called flex-block-count and flex-block-flow which allows us to control items similarly (with column-count) and control the flow of items, respectively. However, this wouldn’t fly as Michael Richins pointed out:

This will still not work for children of different height…

This thread was not going anywhere, so discussions ended in April 2020 with a GitHub user with the name Nic787 referencing and agreeing with an earlier probable solution for masonry using float instead of grid.

I think you have a good point. Currently, float is becoming old and flexbox allow a lot of things to be done, but this masonry layout is lacking in both.

… Sometimes you can have lot of small images in a row, so making it left to right can’t work all the time. Masonry is left to right like always, but in a way it’s easier for the user to access informations.

Fortunately, discussions still continued in many threads, and the requests for the masonry display property flooded W3C. Another notable GitHub thread that surfaced on the masonry debate is, well, the Masonry Syntax Debate in November 2024, but that also didn’t lead to much at that point. In fact, Jen Simmons noted in the meeting that:

Personally disappointed that we’re not making more progress. We’ve been having this argument for 5 years.

Five years of back and forth on how exactly the naming and structure would be. Do we use grid? Do we use a brand new property? Do we create separate properties just for masonry? We couldn’t really decide on that.

One thing I like to get behind is a comment from our very own Juan Diego, stating how solutions aren’t “just use grid” or create a new property, but that we could get rid of the grid- prefix altogether and settle for an option that caters for both grid and masonry:

…it should be possible to use a new unprefixed template-areas property for masonry and grid, regardless of the formatting context..

Fortunately, something good came out of that thread because another meeting was held on January 31st of 2025, and guess what? It was resolved to reuse grid templating and placement properties for masonry layout.

The CSS Working Group just discussed [css-grid-3][masonry] Masonry Syntax Debate, and agreed to the following: RESOLVED: Re-use grid templating and placement properties for masonry layout.

Wait. Why am I announcing about masonry syntax properties? What about the main syntax itself? Isn’t that why we’re here? Awesome, you’re still with me. So let me show you the exact thread where the masonry syntax war ended.

It’s (almost) here!

This brings us to the latest announcement that setting grid-lanes on the display property activates a masonry layout.

.masonry {
  display: grid-lanes;
}

It was tough to get here. Deciding on the keyword alone took years. Just look at all of the names that were considered leading up to grid-lanes:

  • collapsed-grid or grid collapse
  • grid-stack or stacked-grid
  • grid-pack or packed-grid or grid pack
  • compact-grid or compact grid
  • grid-masonry or masonry-grid
  • grid-flex or flex-grid
  • grid single-axis (masonry grid) vs. grid dual-axis (normal grid)
  • grid stack (masonry grid) vs. grid grid (normal grid)
  • staggered-grid or grid staggered
  • uneven-grid
  • semi-grid
  • lane-grid or grid-lanes
  • axial-grid

I wouldn’t have minded staggered-grid since that’s what it is in React Native. In fact, Kevin Powell agreed. That said, I am perfectly okay with grid-lanes. It will take some time for browsers to implement it, as discussions surrounding the shorthand adjustments for masonry are still going on, with nothing much said on the issue just yet.

What about Item Flow?

Oh yeah, remember that? It’s still in the works, and I expect that we’ll see it after a proper grid-lanes implementation starts making the rounds. There’s a nice hypothetical example from Fantasai illustrating how it might look if added to the spec today:

item-flow: <item-direction> || <item-wrap> || <item-pack> || <item-slack>

/* shorthand for */
item-direction: row | column | row-reverse | column-reverse
item-wrap: wrap | wrap-reverse
item-pack: normal | dense || collapse
item-slack: <length-percentage>

In use, it would look something along the lines of:

.masonry {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(14rem, 1fr));
  item-flow: collapse; /* == item-flow: row collapse 1em; */
  gap: 1rem;
}

So, what’s next?

After many back-and-forths, masonry grid-lanes is here! But where exactly?

Well, this is the awkward part. The truth is we’ll still have to wait for browsers to implement grid-lanes. How long may it take? I really can’t say, but take into consideration that all major browsers already have implemented some type of masonry layout behind flags or previews, but with different syntaxes:

So, good news all around! All that’s left is to wait. If you want to track grid-lanes progress, here is a good link pool for each browser from Patrick Brosset. And, if you can’t wait for the official masonry implementation, Zell Liew has an approach for using it today, with minimal JavaScript.


Masonry Layout is Now grid-lanes originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.



from CSS-Tricks https://ift.tt/P90si2O
via IFTTT

Thursday, December 18, 2025

Mostly Clear today!



With a high of F and a low of 22F. Currently, it's 22F and Clear outside.

Current wind speeds: 7 from the South

Pollen: 0

Sunrise: December 18, 2025 at 08:06PM

Sunset: December 19, 2025 at 05:30AM

UV index: 0

Humidity: 47%

via https://ift.tt/kuDvGTX

December 19, 2025 at 10:02AM

Search CSS-Tricks Raycast Extension

I have to search for articles often on this site. And I’d say searching this site is Pretty OK™ in general. Making content searchable on a site with 18+ years of published content is gonna be challenging no matter what, and the Jetpack Search tool we use is darned good considering it’s a drop-in solution.

At the same time, it’s a bit heavy-handed when all I really need is a quick URL I can copy-paste into some other article I’m working on. What would be cool is a search straight from my local machine which is totally possible by fetching the content from the WordPress REST API that runs under the hood of this site.

That’s exactly what Jelte Lagendijk built for Raycast, a “shortcut-all the-things” app for macOS and Windows. It’s a little extension where you simply type and a get a solid set of real-time results.

Desktop window with a search field partially typing a query for anchor positioning and results listed below it.

Click any item and get the high-level summary with a path to open it up in the browser.

Desktop window with a list of search results for anchor positioning and one item selected showing a preview of the article's title, date, summary, and featured image.

Search CSS-Tricks Raycast Extension originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.



from CSS-Tricks https://ift.tt/hGwDx1b
via IFTTT

Wednesday, December 17, 2025

Partly Cloudy/Wind today!



With a high of F and a low of 29F. Currently, it's 48F and Showers in the Vicinity outside.

Current wind speeds: 29 from the Northwest

Pollen: 0

Sunrise: December 17, 2025 at 08:05PM

Sunset: December 18, 2025 at 05:30AM

UV index: 0

Humidity: 47%

via https://ift.tt/bO6NGBk

December 18, 2025 at 10:02AM

Responsive List of Avatars Using Modern CSS (Part 2)

Ready for the second part? If you recall, last time we worked on a responsive list of overlapping avatar images featuring a cut-out between them.

Two rows of circular avatar images. The images overlap with one another. The first row has eight images; the second row has six images.

We are still creating a responsive list of avatars, but this time it will be a circular list.

Showing two examples of circular avatar images arranged in a circle. The first example has eight images. The second example has six images.

This design is less common than the horizontal list, but it’s still a good exercise to explore new CSS tricks.

Let’s start with a demo. You can resize it and see how the images behave, and also hover them to get a cool reveal effect.

The following demo is currently limited to Chrome and Edge, but will work in other browsers as the sibling-index() and sibling-count() functions gain broader support. You can track Firefox support in Ticket #1953973 and WebKit’s position in Issue #471.

We will rely on the same HTML structure and CSS base as the example we covered in Part 1: a list of images inside a container with mask-ed cutouts. This time, however, the positions will be different.

Responsive List of Avatars Using Modern CSS

  1. Horizontal Lists
  2. Circular Lists (You are here!)

Placing Images Around a Circle

There are several techniques for placing images around a circle. I will start with my favorite one, which is less known but uses a simple code that relies on the CSS offset property.

.container {
  display: grid;
}
.container img {
  grid-area: 1/1;
  offset: circle(180px) calc(100%*sibling-index()/sibling-count()) 0deg;
}

The code doesn’t look super intuitive, but its logic is fairly straightforward. The offset property is a shorthand, so let’s write it the longhand way to see how breaks down:

offset-path: circle(180px);
offset-distance: calc(100%*sibling-index()/sibling-count());
offset-rotate: 0deg;

We define a path to be a circle with a radius of 180px. All the images will “follow” that path, but will initially be on top of each other. We need to adjust their distance to change their position along the path (i.e., the circle). That’s where offset-distance comes into play, which we combine with the sibling-index() and sibling-count() functions to create code that works with any number of elements instead of working with exact numbers.

For six elements, the values will be as follows:

100% x 1/6 = 16.67%
100% x 2/6 = 33.33%
100% x 3/6 = 50%
100% x 4/6 = 66,67%
100% x 5/6 = 83.33%
100% x 6/6 = 100%

This will place the elements evenly around the circle. To this, we add a rotation equal to 0deg using offset-rotate to keep the elements straight so they don’t rotate as they follow the circular path. From there, all we have to do is update the circle’s radius with the value we want.

That’s my preferred approach, but there is a second one that uses the transform property to combine two rotations with a translation:

.container {
  display: grid;
}
.container img {
  grid-area: 1/1;
  --_i: calc(1turn*sibling-index()/sibling-count());
  transform: rotate(calc(-1*var(--_i))) translate(180px) rotate(var(--_i));
}

The translation contains the circle radius value and the rotations use generic code that relies on the sibling-* functions the same way we did with offset-distance.

Even though I prefer the first approach, I will rely on the second one because it allows me to reuse the rotation angle in more places.

The Responsive Part

Similar to the horizontal responsive list from the last article, I will rely on container query units to define the radius of the circle and make the component responsive.

Diagram of eight circular avatar images arranged around a circle. A red dashed line indicates the size and radius of the larger circle.
.container {
  --s: 120px; /* image size */

  aspect-ratio: 1;
  container-type: inline-size;
}
.container img {
  width: var(--s);
  --_r: calc(50cqw - var(--s)/2);
  --_i: calc(1turn*sibling-index()/sibling-count());
  transform: rotate(calc(-1*var(--_i))) translate(var(--_r)) rotate(var(--_i));
}

Resize the container in the demo below and see how the images behave:

It’s responsive, but when the container gets bigger, the images are too spread out, and I don’t like that. It would be good to keep them as close as possible. In other words, we consider the smallest circle that contains all the images without overlap.

Remember what we did in the first part: we added a maximum boundary to the margin for a similar reason. We will do the same thing here:

--_r: min(50cqw - var(--s)/2, R);

I know you don’t want a boring geometry lesson, so I will skip it and give you the value of R:

S/(2 x sin(.5turn/N))

Written in CSS:

--_r: min(50cqw - var(--s)/2,var(--s)/(2*sin(.5turn/sibling-count())));

Now, when you make the container bigger, the images will stay close to each other, which is perfect:

Let’s introduce another variable for the gap between images (--g) and update the formula slightly to keep a small gap between the images.

.container {
  --s: 120px; /* image size */
  --g: 10px;  /* the gap */

  aspect-ratio: 1;
  container-type: inline-size;
}
.container img {
  width: var(--s);
  --_r: min(50cqw - var(--s)/2,(var(--s) + var(--g))/(2*sin(.5turn/sibling-count())));
  --_i: calc(1turn*sibling-index()/sibling-count());
  transform: rotate(calc(-1*var(--_i))) translate(var(--_r)) rotate(var(--_i));
}

The Cut-Out Effect

For this part, we will be using the same mask that we used in the last article:

mask: radial-gradient(50% 50% at X Y, #0000 calc(100% + var(--g)), #000);

With the horizontal list, the values of X and Y were quite simple. We didn’t have to define Y since its default value did the job, and the X value was either 150% + M or -50% - M, with M being the margin that controls the overlap. Seen differently, X and Y are the coordinates of the center point of the next or previous image in the list.

That’s still the case this time around, but the value is trickier to calculate:

Diagram of eight circular avatar images arranged around a circle. Two line segments identify an A segment in red and a B segment in green. The first segment points to the current image represented by i. The second segment points to the next image represented by i plus 1.

The idea is to start from the center of the current image (50% 50%) and move to the center of the next image (X and Y). I will first follow segment A to reach the center of the big circle and then follow segment B to reach the center of the next image.

This is the formula:

X = 50% - Ax + Bx
Y = 50% - Ay + By

Ax and Ay are the projections of the segment A on the X-axis and the Y-axis. We can use trigonometric functions to get the values.

Ax = r x sin(i);
Ay = r x cos(i);

The r represents the circle’s radius defined by the CSS variable --_r, and i represents the angle of rotation defined by the CSS variable --_i.

Same logic with the B segment:

Bx = r x sin(j);
By = r x cos(j);

The j is similar to i, but for the next image in the sequence, meaning we increment the index by 1. That gives us the following CSS calculations for each variable:

--_i: calc(1turn*sibling-index()/sibling-count());
--_j: calc(1turn*(sibling-index() + 1)/sibling-count());

And the final code with the mask:

.container {
  --s: 120px; /* image size */
  --g: 14px;  /* the gap */

  aspect-ratio: 1;
  container-type: inline-size;
}
.container img {
  width: var(--s);
  --_r: min(50cqw - var(--s)/2,(var(--s) + var(--g))/(2*sin(.5turn/sibling-count())));
  --_i: calc(1turn*sibling-index()/sibling-count());
  --_j: calc(1turn*(sibling-index() + 1)/sibling-count());
  transform: rotate(calc(-1*var(--_i))) translate(var(--_r)) rotate(var(--_i));
  mask: radial-gradient(50% 50% at
    calc(50% + var(--_r)*(cos(var(--_j)) - cos(var(--_i))))
    calc(50% + var(--_r)*(sin(var(--_i)) - sin(var(--_j)))),
      #0000 calc(100% + var(--g)), #000);
}

Cool, right? You might notice two different implementations for the cut-out. The formula I used previously considered the next image, but if we consider the previous image instead, the cut-out goes in another direction. So, rather than incrementing the index, we decrement instead and assign it to a .reverse class that we can use when we want the cut-out to go in the opposite direction:

.container img {
  --_j: calc(1turn*(sibling-index() + 1)/sibling-count());
}
.container.reverse img {
  --_j: calc(1turn*(sibling-index() - 1)/sibling-count());
}

The Animation Part

Similar to what we did in the last article, the goal of this animation is to remove the overlap when an image is hovered to fully reveal it. In the horizontal list, we simply set its margin property to 0, and we adjust the margin of the other images to prevent overflow.

This time, the logic is different. We will rotate all of the images except the hovered one until the hovered image is fully visible. The direction of the rotation will depend on the cut-out direction, of course.

Eight avatar images arranged around a circle. An arrow points to the same thing showing what happens when hovering over the avatar positioned at the top of the circle.

To rotate the image, we need to update the --_i variable, which is used as an argument for the rotate function. Let’s start with an arbitrary value for the rotation, say 20deg.

.container img {
  --_i: calc(1turn*sibling-index()/sibling-count());
}
.container:has(:hover) img {
  --_i: calc(1turn*sibling-index()/sibling-count() + 20deg);
}
.container.reverse:has(:hover) img {
  --_i: calc(1turn*sibling-index()/sibling-count() - 20deg);
}

Now, when an image is hovered, all of images rotate by 20deg. Try it out in the following demo.

Hmm, the images do indeed rotate, but the mask is not following along. Don’t forget that the mask considers the position of the next or previous image defined by --_j and the next/previous image is rotating — hence we need to also update the --_j variable when the hover happens.

.container img {
  --_i: calc(1turn*sibling-index()/sibling-count());
  --_j: calc(1turn*(sibling-index() + 1)/sibling-count());
}
.container.reverse img {
  --_j: calc(1turn*(sibling-index() - 1)/sibling-count());
}
.container:has(:hover) img {
  --_i: calc(1turn*sibling-index()/sibling-count() + 20deg);
  --_j: calc(1turn*(sibling-index() + 1)/sibling-count() + 20deg);
}
.container.reverse:has(:hover) img {
  --_i: calc(1turn*sibling-index()/sibling-count() - 20deg);
  --_j: calc(1turn*(sibling-index() - 1)/sibling-count() - 20deg);
}

That’s a lot of redundant code. Let’s optimize it a little by defining additional variables:

.container img {
  --_a: 20deg;

  --_i: calc(1turn*sibling-index()/sibling-count() + var(--_ii, 0deg));
  --_j: calc(1turn*(sibling-index() + 1)/sibling-count() + var(--_jj, 0deg));
}
.container.reverse img {
  --_i: calc(1turn*sibling-index()/sibling-count() - var(--_ii, 0deg));
  --_j: calc(1turn*(sibling-index() - 1)/sibling-count() - var(--_jj, 0deg));
}
.container:has(:hover) img {
  --_ii: var(--_a);
  --_jj: var(--_a);
}

Now the angle (--_a) is defined in one place, and I consider two intermediate variables to add an offset to the --_i and --_j variables.

The rotation of all the images is now perfect. Let’s disable the rotation of the hovered image:

.container img:hover {
  --_ii: 0deg;
  --_jj: 0deg;
}

Oops, the mask is off again! Do you see the issue?

We want to stop the hovered image from rotating while allowing the rest of the images to rotate. Therefore, the --_j variable of the hovered image needs to update since it’s linked to the next or previous image. So we should remove --_jj: 0deg and keep only --_ii: 0deg.

.container img:hover {
  --_ii: 0deg;
}

That’s a little better. We fixed the cut-out effect on the hovered image, but the overall effect is still not perfect. Let’s not forget that the hovered image is either the next or previous image of another image, and since it’s not rotating, another --_j variable needs to remain unchanged.

For the first list, it’s the variable of the previous image that should remain unchanged. For the second list, it’s the variable of the next image:

/* select previous element of hovered */
.container:not(.reverse) img:has(+ :hover),
/* select next element of hovered */
.container.reverse img:hover + * {
  --_jj: 0deg;
}

In case you are wondering how I knew to do this, well, I tried both ways and I picked the one that worked. It was either the code above or this:

.container:not(.reverse) img:hover + *,
.container.reverse img:has(+ :hover) {
  --_jj: 0deg;
}

We are getting closer! All the images behave correctly except for one in each list. Try hovering all of them to identify the culprit.

Can you figure out what we are missing? Think a moment about it.

Our list is circular, but the HTML code is not, so even if the first and last images are visually placed next to each other, in the code, they are not. We cannot link both of them using the adjacent sibling selector (+). We need two more selectors to cover those edge cases:

.container.reverse:has(:last-child:hover) img:first-child,
.container:not(.reverse):has(:first-child:hover) img:last-child {
  --_jj: 0deg;
}

Oof! We have fixed all the issues, and now our hover effect is great, but it’s still not perfect. Now, instead of using an arbitrary value for the rotation, we need to be accurate. We have to find the smallest value that removes the overlap while keeping the images as close as possible.

Showing the gap between two images at three different points. The first and third points are too close and too spaced out, respectively. The middle point is perfect with just enough space between the images.

We can get the value with some trigonometry. I’ll skip the geometry lesson again (we have enough headaches as it is!) and give you the value:

--_a: calc(2*asin((var(--s) + var(--g))/(2*var(--_r))) - 1turn/sibling-count());

Now we can say everything is perfect!

Conclusion

This one was a bit tough, right? Don’t worry if you got a bit lost with all the complex formulas. They are very specific to this example, so even if you have already forget about them, that’s fine. The goal was to explore some modern features and a few CSS tricks such as offset, mask, sibling-* functions, container query units, min()/max(), and more!

Responsive List of Avatars Using Modern CSS

  1. Horizontal Lists
  2. Circular Lists (You are here!)

Responsive List of Avatars Using Modern CSS (Part 2) originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.



from CSS-Tricks https://ift.tt/qSGUY83
via IFTTT

Mostly Cloudy/Wind today!

With a high of F and a low of 13F. Currently, it's 18F and Partly Cloudy outside. Current wind speeds: 13 from the North Pollen: 0...