Google Search is increasingly dominated by excessive ads and AI-driven results, making it harder for users to find organic, relevant content. As a result, the search engine is losing its original value, with many turning to social media and alternative platforms for discovery.
Unsplash’s decision to feature two search bars on its homepage adds unnecessary complexity and confusion, diluting the user experience. A single, streamlined search bar would provide clearer navigation and a more intuitive interface for users.
The videos from Smashing Magazine’s recent event on accessibility were just posted the other day. I was invited to host the panel discussion with the speakers, including a couple of personal heroes of mine, Stéphanie Walter and Sarah Fossheim. But I was just as stoked to meet Kardo Ayoub who shared his deeply personal story as a designer with a major visual impairment.
I’ll drop the video here:
I’ll be the first to admit that I had to hold back my emotions as Kardo detailed what led to his impairment, the shock that came of it, and how he has beaten the odds to not only be an effective designer, but a real leader in the industry. It’s well worth watching his full presentation, which is also available on YouTube alongside the full presentations from Stéphanie and Sarah.
Spring into action this season with new tools to boost your workflows, facilitate creative thinking, and help you be a more efficient designer. This roundup is packed with new goodies that can help you with daily tasks and more long-term projects. Enjoy! Beatoven Projects that need background music can be a challenge when it comes […]
In the last article, we created a CSS-only star rating component using the CSS mask and border-image properties, as well as the newly enhanced attr() function. We ended with CSS code that we can easily adjust to create component variations, including a heart rating and volume control.
This second article will study a different approach that gives us more flexibility. Instead of the border-image trick we used in the first article, we will rely on scroll-driven animations!
Here is the same star rating component with the new implementation. And since we’re treading in experimental territory, you’ll want to view this in Chrome 115+ while we wait for Safari and Firefox support:
CodePen Embed Fallback
Do you spot the difference between this and the final demo in the first article? This time, I am updating the color of the stars based on how many of them are selected — something we cannot do using the border-image trick!
I highly recommend you read the first article before jumping into this second part if you missed it, as I will be referring to concepts and techniques that we explored over there.
One more time: At the time of writing, only Chrome 115+ and Edge 115+ fully support the features we will be using in this article, so please use either one of those as you follow along.
Why scroll-driven animations?
You might be wondering why we’re talking about scroll-driven animation when there’s nothing to scroll to in the star rating component. Scrolling? Animation? But we have nothing to scroll or animate! It’s even more confusing when you read the MDN explainer for scroll-driven animations:
It allows you to animate property values based on a progression along a scroll-based timeline instead of the default time-based document timeline. This means that you can animate an element by scrolling a scrollable element, rather than just by the passing of time.
But if you keep reading you will see that we have two types of scroll-based timelines: scroll progress timelines and view progress timelines. In our case, we are going to use the second one; a view progress timeline, and here is how MDN describes it:
You progress this timeline based on the change in visibility of an element (known as the subject) inside a scroller. The visibility of the subject inside the scroller is tracked as a percentage of progress — by default, the timeline is at 0% when the subject is first visible at one edge of the scroller, and 100% when it reaches the opposite edge.
Things start to make more sense if we consider the thumb element as the subject and the input element as the scroller. After all, the thumb moves within the input area, so its visibility changes. We can track that movement as a percentage of progress and convert it to a value we can use to style the input element. We are essentially going to implement the equivalent of document.querySelector("input").value in JavaScript but with vanilla CSS!
The implementation
Now that we have an idea of how this works, let’s see how everything translates into code.
I know, this is a lot of strange syntax! But we will dissect each line and you will see that it’s not all that complex at the end of the day.
The subject and the scroller
We start by defining the subject, i.e. the thumb element, and for this we use the view-timeline shorthand property. From the MDN page, we can read:
The view-timelineCSSshorthand property is used to define a named view progress timeline, which is progressed through based on the change in visibility of an element (known as the subject) inside a scrollable element (scroller). view-timeline is set on the subject.
I think it’s self-explanatory. The view timeline name is --val and the axis is inline since we’re working along the horizontal x-axis.
Next, we define the scroller, i.e. the input element, and for this, we use overflow: hidden (or overflow: auto). This part is the easiest but also the one you will forget the most so let me insist on this: don’t forget to define overflow on the scroller!
I insist on this because your code will work fine without defining overflow, but the values won’t be good. The reason is that the scroller exists but will be defined by the browser (depending on your page structure and your CSS) and most of the time it’s not the one you want. So let me repeat it another time: remember theoverflowproperty!
The animation
Next up, we create an animation that animates the --val variable between the input’s min and max values. Like we did in the first article, we are using the newly-enhanced attr() function to get those values. See that? The “animation” part of the scroll-driven animation, an animation we link to the view timeline we defined on the subject using animation-timeline. And to be able to animate a variable we register it using @property.
Note the use of timeline-scope which is another tricky feature that’s easy to overlook. By default, named view timelines are scoped to the element where they are defined and its descendant. In our case, the input is a parent element of the thumb so it cannot access the named view timeline. To overcome this, we increase the scope using timeline-scope. Again, from MDN:
timeline-scope is given the name of a timeline defined on a descendant element; this causes the scope of the timeline to be increased to the element that timeline-scope is set on and any of its descendants. In other words, that element and any of its descendant elements can now be controlled using that timeline.
Never forget about this! Sometimes everything is correctly defined but nothing is working because you forget about the scope.
There’s something else you might be wondering:
Why are the keyframes values inverted? Why is the min is set to 100% and the max set to 0%?
To understand this, let’s first take the following example where you can scroll the container horizontally to reveal a red circle inside of it.
CodePen Embed Fallback
Initially, the red circle is hidden on the right side. Once we start scrolling, it appears from the right side, then disappears to the left as you continue scrolling towards the right. We scroll from left to right but our actual movement is from right to left.
In our case, we don’t have any scrolling since our subject (the thumb) will not overflow the scroller (the input) but the main logic is the same. The starting point is the right side and the ending point is the left side. In other words, the animation starts when the thumb is on the right side (the input’s max value) and will end when it’s on the left side (the input’s min value).
The animation range
The last piece of the puzzle is the following important line of code:
animation-range: entry 100% exit 0%;
By default, the animation starts when the subject starts to enter the scroller from the right and ends when the subject has completely exited the scroller from the left. This is not good because, as we said, the thumb will not overflow the scroller, so it will never reach the start and the end of the animation.
To rectify this we use the animation-range property to make the start of the animation when the subject has completely entered the scroller from the right (entry 100%) and the end of the animation when the subject starts to exit the scroller from the left (exit 0%).
To summarize, the thumb element will move within input’s area and that movement is used to control the progress of an animation that animates a variable between the input’s min and max attribute values. We have our replacement for document.querySelector("input").value in JavaScript!
What’s going on with all the --val instances everywhere? Is it the same thing each time?
I am deliberately using the same --val everywhere to confuse you a little and push you to try to understand what is going on. We usually use the dashed ident (--) notation to define custom properties (also called CSS variables) that we later call with var(). This is still true but that same notation can be used to name other things as well.
In our examples we have three different things named --val:
The variable that is animated and registered using @property. It contains the selected value and is used to style the input.
The named view timeline defined by view-timeline and used by animation-timeline.
The keyframes named --val and called by animation.
Here is the same code written with different names for more clarity:
All that we have done up to now is get the selected value of the input range — which is honestly about 90% of the work we need to do. What remains is some basic styles and code taken from what we made in the first article.
If we omit the code from the previous section and the code from the previous article here is what we are left with:
We make the thumb invisible and we define a gradient on the main element to color in the stars. No surprise here, but the gradient uses the same --val variable that contains the selected value to inform how much is colored in.
When, for example, you select three stars, the --val variable will equal 3 and the color stop of the first color will equal 3*100%/5 , or 60%, meaning three stars are colored in. That same color is also dynamic as I am using the hsl() function where the first argument (the hue) is a function of --val as well.
Here is the full demo, which you will want to open in Chrome 115+ at the time I’m writing this:
CodePen Embed Fallback
And guess what? This implementation works with half stars as well without the need to change the CSS. All you have to do is update the input’s attributes to work in half increments. Remember, we’re yanking these values out of HTML into CSS using attr(), which reads the attributes and returns them to us.
<input type="range" min=".5" step=".5" max="5">
CodePen Embed Fallback
That’s it! We have our rating star component that you can easily control by adjusting the attributes.
So, should I use border-image or a scroll-driven animation?
If we look past the browser support factor, I consider this version better than the border-image approach we used in the first article. The border-image version is simpler and does the job pretty well, but it’s limited in what it can do. While our goal is to create a star rating component, it’s good to be able to do more and be able to style an input range as you want.
With scroll-driven animations, we have more flexibility since the idea is to first get the value of the input and then use it to style the element. I know it’s not easy to grasp but don’t worry about that. You will face scroll-driven animations more often in the future and it will become more familiar with time. This example will look easy to you in good time.
Worth noting, that the code used to get the value is a generic code that you can easily reuse even if you are not going to style the input itself. Getting the value of the input is independent of styling it.
Many techniques are involved to create that demo and one of them is using scroll-driven animations to get the input value and show it inside the tooltip!
This one is a bit crazy but it illustrates how far we go with styling an input range! So, even if your goal is not to create a star rating component, there are a lot of use cases where such a technique can be really useful.
Conclusion
I hope you enjoyed this brief two-part series. In addition to a star rating component made with minimal code, we have explored a lot of cool and modern features, including the attr() function, CSS mask, and scroll-driven animations. It’s still early to adopt all of these features in production because of browser support, but it’s a good time to explore them and see what can be done soon using only CSS.
Grumpy Cat, famous for her permanent scowl, captured the internet’s heart with her relatable and sarcastic meme captions. Her rise to fame turned her into a beloved cultural symbol of frustration and humor that still resonates today.
A few months ago I wrote about what it means to stay gold — to hold on to the best parts of ourselves, our communities, and the American Dream itself. But staying gold isn’t passive. It takes work. It takes action. It takes hard conversations that ask us to confront where we’ve been, where we are, and who we want to be.
That’s why I’m incredibly honored to be joining Alexander Vindman in giving a talk at the historic Cooper Union Great Hall 14 days from now. I greatly admire the way Colonel Vindman was willing to put everything on the line to defend the ideals of democracy and the American Dream.
The American Dream is, at its core, the promise that hard work, fairness, and opportunity can lead to a better future. But in 2025, that promise feels like a question: How can we build on our dream so that it works for everyone?
Alexander and I will explore this in our joint talk through the lens of democracy, community, and economic mobility. We come from very different backgrounds, but we strongly share the belief that everyone’s American Dream is worth fighting for.
Alexander Vindman has lived many lifetimes of standing up for what’s right. He was born in the Soviet Union and immigrated to the U.S. as a child, growing up in Brooklyn before enlisting in the U.S. Army. Over the next 21 years, he served with distinction, earning a Purple Heart for injuries sustained in Iraq and eventually rising to Director of European Affairs for the National Security Council. When asked to choose between looking the other way or upholding the values he swore to protect, he chose correctly. That decision cost him his career but never his integrity. I have a lot to learn about what civic duty truly means from Alex.
I build things on the Internet, like Stack Overflow and Discourse. I write on the internet, on this blog. I’ve spent years thinking about how people interact online, how communities work (or don’t), and how we create digital spaces that encourage fairness, participation, and constructive discourse. Spaces that result in artifacts for the common good, like local parks, where everyone can enjoy them together. Whether you’re running a country or running a forum, the same rules seem to apply: people need clear expectations, fair systems, strong boundaries, and a shared sense of purpose.
This is the part of Stay Gold I couldn’t tell you about, not yet, because I was working so hard to figure it out. How do you make long-term structural change that creates opportunity for everyone? It is an incredibly complex problem. But if we focus our efforts in a particular area, I believe we can change a lot of things in this country. Maybe not everything, but something foundational to the next part of our history as a country: how to move beyond individual generosity and toward systems that create security, dignity, and possibility for all.
I can’t promise easy answers, but what I can promise is an honest, unfiltered conversation about how we move forward, with specifics. Colonel Vindman brings the perspective of someone who embodied American ideals, and I bring the experience of building self-governing digital communities that scale, which turned out to be far more relevant to the future of democracy than I ever would have dreamed possible.
Imagine what we can do if Alex and I work together. Imagine what we could do if we all worked together.
Digg is making a comeback under its original founder, Kevin Rose, and Reddit co-founder Alexis Ohanian, with a renewed focus on AI-driven content curation and community engagement.
Meta’s switch back to the blue Messenger logo has sparked confusion, reflecting the company’s ongoing identity crisis. Zuckerberg’s constant flip-flopping only adds to the uncertainty.
Icons may look sleek, but they often create confusion and increase cognitive load, especially in complex applications. Text labels, on the other hand, provide clear, unambiguous communication, making them a far superior choice for usability and accessibility in design.