The soon-Baseline sibling-index() function for animations, CSS and the 2026 FIFA World Cup, use cases for the infinity keyword, container "stuck" queries, and more.
The writing-mode CSS property sets whether lines of text are laid out horizontally or vertically, and the direction in which blocks and lines progress.
.element {
writing-mode: vertical-rl;
}
This is most useful in languages such as Chinese, Japanese or …
The pointer-events property controls whether an element can become the target of pointer events like clicks, hover states, and other pointer-based events. In other words, it lets you decide whether the browser should treat an element as interactive when the pointer is over it.
.no-pointer-events {
pointer-events: none;
}
To understand how the property works, it helps to know what the browser does before it fires a pointer event. First, it has to determine which element is under the pointer. This process is known as hit-testing.
Normally, the browser chooses the topmost element under the pointer. But if that element has pointer-events set to none, the browser skips it and continues looking for the next eligible element underneath.
Once you think about pointer-events this way, most of its behavior starts to make sense. Rather than disabling events, it simply changes which element (or, in the case of SVG, which part of an element) becomes the event target in the first place.
Syntax
pointer-events: auto | bounding-box | visiblePainted | visibleFill | visibleStroke | visible | painted | fill | stroke | all | none;
Initial: auto
Applies to: All elements. In SVG, it applies to container elements, graphics elements, and the element.
Besides the standard CSS global values shown above, pointer-events defines eleven keyword values. You’ll use auto and none with both HTML and SVG elements, while the other nine are SVG-only and provide finer control over which parts of a graphic can receive pointer events.
auto: The default value. The element behaves normally and can receive pointer events. In SVG, this behaves the same as visiblePainted.
none: The element itself can’t become the target of pointer events, meaning it can’t be clicked or hovered. Instead, the browser targets whatever is underneath it.
SVG-only values
visiblePainted: The element only receives pointer events when it’s visible (visibility: visible) and the pointer is over a painted part of the graphic. In other words, it’s over a filled area (fill is not none) or a stroked edge (stroke is not none).
visibleFill: The element only receives pointer events when it’s visible and the pointer is over its fill, regardless of the value of the fill property, meaning it can even be set to none.
visibleStroke: The element only receives pointer events when it’s visible and the pointer is over its stroke, regardless of the value of the stroke property.
visible: The element only receives pointer events when it’s visible and the pointer is over either its fill or stroke, regardless of the values of the fill and stroke properties.
painted: The element only receives pointer events when the pointer is over a painted part of the graphic (its fill or stroke), regardless of the value of the visibility property.
fill: The element only receives pointer events when the pointer is over its fill, regardless of the values of the fill or visibility properties.
stroke: The element only receives pointer events when the pointer is over its stroke, regardless of the values of the stroke or visibility properties.
bounding-box: The element receives pointer events anywhere inside its bounding box—the smallest rectangle that completely surrounds it—regardless of its shape, even if parts of that area aren’t painted.
all: The element receives pointer events when the pointer is over either its fill or stroke, regardless of the values of the fill, stroke, or visibility properties.
Children can opt back in
One thing that’s easy to miss is that pointer-events is an inherited property. Setting pointer-events to none on a parent means its children inherit that value as well. However, any child can override the inherited value by setting pointer-events back to auto (or another valid value).
In this example, the parent ignores pointer events, but the child can still become their target.
A common use case is a modal. You might use a full-page container to center the modal, but that container also covers the entire viewport. Without changing its pointer-events value, it prevents pointer events from reaching the elements underneath it. Setting pointer-events to none on the container fixes that, but because the property is inherited, you’ll also need to restore the modal itself with pointer-events set to auto.
In the following demo, when the pointer-events property is specified as none you can interact with the background buttons even though they are behind the overlay.
Event propagation still works
The pointer-events property only determines which element becomes the event target. It doesn’t change how events travel through the DOM afterward.
For example, if a child with pointer-events: auto is clicked inside a parent with pointer-events: none, the child still becomes event.target. From there, the event follows its normal capture and bubble phases, so event listeners attached to the parent still run.
In other words, pointer-events affects target selection, not event propagation.
In the following demo, you can see how the parent with pointer-events: none can still receive click, pointerenter, and pointerleave when you click or move the pointer into or out of its interactive child.
pointer-events doesn’t disable an element
The pointer-events property only prevents the element from becoming the target of pointer events. Therefore, the element can still receive keyboard focus with the Tab key, and users can continue interacting with it using the keyboard if it’s otherwise focusable.
If you need to disable a native form control, use the disabled attribute instead. And if your goal is to make an entire section of the page completely non-interactive—including pointer input, keyboard focus, and the accessibility tree—the inert attribute is a better choice.
pointer-events doesn’t prevent text selection
Setting the pointer-events property to none doesn’t stop users from selecting text. For example, users can still select the text by pressing Ctrl/Cmd + A on the keyboard.
That’s because text selection isn’t determined by whether an element can become the target of pointer events.
If your goal is to prevent text from being selected, use the user-select property instead.
.avoid-user-selection {
user-select: none;
}
Try selecting the text in each block below:
Demo
When building a navigation menu, a common pattern is to hide a submenu by setting its opacity to 0 and then make it visible when the user hovers over its parent menu item. The problem is that the submenu is still there, so it can still receive pointer events even though you can’t see it.
Below, you can see two identical menus. One hides its submenu using only opacity, while the other also uses pointer-events. Hover over the hidden submenu area and try interacting with the content behind it to see how pointer-events prevents invisible elements from getting in the way.
Also, to better understand how each SVG value of this property works, pick one from the dropdown and move your pointer around the ring: over its filled band, inside its hollow center, and over the empty corners of the dashed bounding box. Notice how the interactive area changes with each value.
Similar to last time, What’s !important #15 is pretty stacked — read all about boundary-aware CSS, making grid lanes accessible, creating time-based web designs, fixing full-bleed CSS, improving customizable <select>, new web platform features, and more.
Using view() for boundary-aware CSS
Preethi Sam very expertly walked us through the concept of boundary-aware CSS. Using view(), Preethi was able to create a range of really useful effects, some of which I’ll share below:
I also wrote a little something for the Master.dev blog, about making interactive elements invisible but accessible. It sounds like more work is needed to make it fully accessible, and that a new value for the hidden attribute could be the answer.
David Bushell showed us how to fix full-bleed CSS. That is, how to make something span the entire horizontal viewport (without any overflow — that’s the tricky part!) despite being nested within another element. I just love to see old problems being solved with modern CSS (hint: it’s container query units).
And that’s why Declan Chidlow created FixCSS, which… well… fixes CSS in accordance with that. Now we can finally swap border-radius (the Ethan Tremblay of CSS) for corner-radius. All is right with the world again.
How to improve customizable <select>
Jake Archibald demonstrated how to improve the UX of customizable <select>s, addressing some sizing-related issues. It’s a must-read for anybody thinking about using customizable <select>.
By “new” I mean anything from still-not-fully-supported-yet to recently released to upcoming. Either way, here’s Bramus and Una Kravets at Google I/O 2026 with 35 web platform features that you might not have heard of yet or might want to be reminded of:
So, we recently got the new shape() function (now Baseline!) as well as the corner-shape property. What else could we possibly need as far as making shapes in CSS? Let me tell you: the border-shape property!
shape()? corner-shape? border-shape?! Where did all these come from?
If you are not a CSS shape fanatic like me, you probably missed these features when they came out, so let’s give them brief, formal introductions, starting with…
Speaking about SVG, you can express any SVG shape using shape(). Said differently, you can convert any SVG shape into a CSS shape and, guess what, I made a converter that does exactly that!
As far as corner-shape goes, it’s a property that works in conjunction with border-radius. As its name suggests, it allows you to control the shape of an element’s corner using predefined keywords.
But is this the corner-shape property really useful or even needed? Except for the squircle value, most of the shapes can already be created using clip-path or mask. But what corner-shape does that these others can’t is easily add borders and other decorations to those shapes!
corner-shape will not only shape the corners, but it also supports other properties like border and box-shadow, allowing them to follow the shape rather than the element’s box. This is a game-changer because we all know that adding borders to shapes is a nightmare.
Support is still not great (Chromium-only as I’m writing this), but it’s a good time to explore it and get an overview of its potential.
Enter border-shape!
Shaping corners is good, but it’s still fairly limited as far as what we can do with it. Like, what about shaping the whole element instead? That’s what border-shape will do. It accepts the same values as clip-path, including the new shape() function.
So, it has the same job as clip-path? What’s new?
Like with corner-shape, most decorative properties such as border, box-shadow, and outline follow the shape.
clip-path (and mask) will clip/mask the whole element, including the decorations, so having borders is a big NO. That’s a major problem for creating CSS shapes.
The border-shape property is here to solve this issue. Instead of clipping the element, it “shapes” the element, allowing its decorations to follow that shape. In other words, putting borders on CSS shapes will become child’s play!
Not to mention that border-shape is also very easy to use. If you are familiar with clip-path, then you practically have nothing new to learn. Simply replace one property with another, and you are done.
I invite you again to learn more about the shape() function because it’s the value that makes border-shape really powerful. The two really go hand-in-hand.
Now that you know the basic use of the property, shall we start the fun stuff? I are here to push the limits and show you what is possible using border-shape.
Note: Support is limited to Chrome-only for now so check out the next demos using Chrome.
Border-Only Shapes
As I said, the first major advantage here is adding borders to shapes that follow the actual shape, which also means the ability to create border-only shapes. All you have to do is write the following code:
Most of the shapes are already available in my CSS Shapes collection, so really, making border-only shapes is something you can do with a simple copy/paste. Even some of my online generators are already configured to provide border-only versions of complex shapes, like blobs, wavy lines, and fancy frames, among many, many others.
Cutout Shapes
Let’s take the previous code and update it as follows:
You keep the shape code you had, and you add inset(0) at the beginning. Yes, you can have two shape values inside border-shape and the result will be as follows:
The border-shape property accepts either a single <basic-shape> or two <basic-shape>s:
Single <basic-shape> (Stroke mode): The border is rendered as a stroke along the shape’s path, with the stroke width determined by the relevant side’s computed border width. This mode is useful for creating outlined shapes.
Two <basic-shape>s (Fill mode) The border is rendered as the area between the two paths. The first shape defines the outer boundary, and the second shape defines the inner boundary. This mode provides precise control over the border region’s geometry.
Using a basic rectangle as the outer shape (inset(0)), I was able to easily transform the border-only version into a cutout version!
Do you want the shape inside a circle? Easy!
All I did is replace inset(0) (a rectangle) with circle() (a circle).
But we can do this with a simple border-radius: 50%, right?
No! When working with border-shape, the border-radius is ignored, which is kind of logical since we no longer have corners to round — the element is now shaped. This is not a big deal since we can literally create any kind of shape that replaces the use of border-radius, as shown in the previous example.
Perhaps this is nothing surprising considering what we have learned so far. We define two shapes and the border is drawn inside the area between them. What’s new here is that we are dealing with content and, as you can see, it overlaps the border.
border-shape shapes the whole element, including the element’s decoration, but any content inside remains unchanged and follows the initial rectangle shape. That’s not new behavior since the same happens even even with classic border-radius. And the content will overflow unless we decide to hide it using overflow hidden.
In this case, I won’t consider the overflow property as I want the content to overflow and be placed on top of the border decoration.
I made the circle smaller and the rectangle bigger. Do you see where I am going? By making the radius of the circle 0 and the rectangle bigger, I create a breakout background effect!
The element remains centered, but its background (that we are simulating using a border!) extends to the edge of the screen.
And this is not limited to using two shape values. Even with the one shape value, we can achieve a breakout decoration (which is typically difficult) if you consider any value that gets you out of the element’s boundary.
Here is a demo with many examples that I’ll let you explore:
Partial Decorations
Let’s piggy-back off that last demo to try different decorations.
This time, instead of extending outside the element boundary, I am staying within it to create partial decoration. In other words, we no longer have boundaries. border-shape has no limit. The only limit is your imagination!
I am mainly relying on the shape() function to create the decoration, so do yourself a favor and explore this feature by reading my four-article series! If you master shape(), you can easily produce complex decorations using border-shape.
Shape Animation
Yes, border-shape can be animated, and we can have different kinds of animations. With the two-value syntax, we can animate the border-width value to create a reveal effect:
Hover the below and see the result:
Cool, right? We can also apply this to image elements as well:
We can also animate the shape values to create more complex animations. Here is a demo of a bouncing hover effect applied to blob shapes (from my article “Making Complex CSS Shapes Using shape().”
I take the same code, replace clip-path with border-shape, and tada!
And why not add a subtle animation to those previous decorations? Hover the titles and the text to see what happens:
Want More? Let’s Go!
Here is a hand-drawn underline that slides between the menu items on hover. Straight lines are boring!
And why not an electric frame around your content? Don’t worry, it’s safe for touch screens.
Let’s connect circles with a straight line that bends when the circles get closer and stretches when they get farther. Drag the circles in the demo below and see the magic in play:
Conclusion
I think it’s clear now why I am calling this new property “powerful.” In addition to making it easy to create CSS shapes, it lets us add fancy decorations, cool animations, and more!
I didn’t go into fine detail with most of the demos as I wanted to keep this a light article showing the potential of border-shape. Now that you know we can create crazy stuff with it, stay tuned for more elaborate articles!
Don’t forget to bookmark my CSS Shapes collection and my online generators. I have already updated many of the shapes using shape(), and I am in the process of also including the border-shape version.