If, like me, you’re eager to use the latest CSS features, like container queries, you’re probably excited to dive in! But we also know it’s not in the Baseline yet… Thankfully, there are things we can do, such as using @supports to check for feature availability.
For container queries, there’s an additional strategy. By using min-width container queries rather than max-width ones, we increase the likelihood that our fallback experience remains usable! It’s a classic responsive design approach to think mobile-first, and in this case, it really helps.
.main{/* Mobile styles here, everyone will see this*/}@container(min-width: 600px){.main{/* Tablet styles here */}}@container(min-width: 800px){.main{/* Desktop styles here */}}
Currently, global support for container queries is at 93.7%. For the remaining users without support, this approach ensures they still get the mobile layout, even on larger screens. While it’s not perfect, it’s fully functional and ensures a better overall user experience. A neat trick!
Trick or treat, clip or hide?
Did you know that we can allow overflow in just one direction? With ‘overflow: hidden’, we always hide overflowing content along both the x and y axes, even if we use overflow-y. And ‘overflow: visible’ makes everything… visible. But now we have another friend in town. Meet clip!
overflow-x: clip;
overflow-y: clip;
Perfect, for example, if you want to hide content along the sides but still allow it to grow upwards and downwards!
Let's cheat the combinators
If you’re anything like me, you still have to look up some stuff from time to time, no matter how many times you’ve used it. One of those things for me is “combinators.”
A “combinator” is a special character that denotes the type of relationship between parts of a selector. And now, we have a cheat sheet!
space character: the descendent combinator matches a direct or nested child
>: the direct child combinator matches only top-level, un-nested children
+: the adjacent sibling combinator matches only the very next sibling
~: the general sibling combinator matches one or more siblings following the base selector
Reduced motion in life anyone?
If, like me, you have some fancy scroll animations on your site, this tip is for you. Such animations can cause discomfort for people with vestibular motion disorders. Animations like scaling or panning large objects can be vestibular motion triggers, which is why users can turn them off in the browser. But we need to listen for that preference in our code as well, which is easy to do with media queries.
@media(prefers-reduced-motion: no-preference){/* Turn on the animation */}
@media(prefers-reduced-motion: reduce){/* Turn off the animation */}
For a few years now, operating systems have allowed users to opt out of animations, typically within Accessibility settings. ‘no-preference’ is the default value, while ‘reduce’ indicates that the user has enabled reduced motion on their device.
Be kind to users and make the web more inclusive for everyone!
Want to save max() of time?
You know those little things you can spend tons of time on? Last week, I had one of those moments with a colleague. We spent too much time on it and then just accepted, “Okay, we didn’t know this.” But if I tell you, it might save you some time. We tried to do something like this.
div{width:max(120vh - 271px, 0);}
This doesn’t work at all. Can you spot the error?
div{width:max(120vh - 271px, 0px);}
It turns out that max() can’t calculate a value that has a unit alongside a unitless value. The browser then gets confused about the comparison. The same applies to the math functions calc(), min(), and clamp().
To save time, use units, folks!
A link needs its space
Ever used borders or pseudo-elements to customize your underlines for links? Yes, me too… But there’s a better way! The ‘text-underline-offset’ property allows you to control the distance between the text baseline and the underline.
a{text-underline-offset: 0.25em;}
You can use this offset to clear descenders, especially when links are grouped closely, such as in a bulleted list. I’d highly recommend adding it to your CSS reset.
Also, don’t miss that text-underline-offset has two nice friends, ‘text-decoration-color’ and ‘text-decoration-thickness’, if you get the urge to customize even more.
Words loves wrapping
We all know that text spread across a large screen can be hard to read. We want to limit the space that the text occupies. There are several ways to handle this, but I’ll show you one that I like a lot.
p{width:min(100%, 60ch);}
So, what does this actually do? It sets the width of the paragraph to take up 100% of the parent width until the width reaches 60 characters, at which point it stops growing. It will always pick the smaller value between 100% and 60ch. One advantage of this over using percentages, pixels (px), or viewport units (vw/vh) to represent width is that when you’re using relative units like em or rem for the font size, the width automatically adjusts as the font grows.
Smart right!?
A smoother transition?
Okay, this is kind of like magic, but I like it! We are talking about view transitions. In its simplest form, it opts in the current and destination documents to undergo a smooth transition between pages. With this one-liner in the base of our CSS, we can get a nicer feel when navigating the web on multi-page applications (MPA). When a user clicks a link, the click triggers the view transition, and we can see a nice cross-fade.
@view-transition{navigation: auto;}
So easy! You can, of course, combine this with keyframes and animations if you want to customize it. But more on that another time. The browser support isn’t widespread yet, but it currently works in Chrome, Edge, and Safari Technology Preview, with more support to come.
Disabled all over or just a bit?
When we disable buttons or other interactive elements, we usually rely on the disabled attribute in HTML, styling the element accordingly. But if we’re aiming for accessibility and inclusivity, there’s an issue, the disabled attribute makes the element non-focusable. That means users can’t reach it with the keyboard or assistive technologies. If it’s unreachable, how will they know it’s disabled?
A better, more inclusive approach is to use aria-disabled instead, which allows the element to stay focusable while still conveying its disabled state to assistive technologies.
Before:
button:disabled{color: grey;}
Now:
button[aria-disabled="true"]{color: grey;}
With aria-disabled, the element remains focusable and is properly announced as disabled (or dimmed) by assistive technologies. Meanwhile, visually, it still looks disabled, just like before. Better for everyone!
Animate to auto anyone?
I often highlight features that are part of the Baseline and supported across major browsers, but I have to tell you about one that’s coming soon, it’s a game-changer! Get ready to meet ‘interpolate-size’. This feature makes it possible to animate from 0 to… drumroll… ‘auto’!
Normally, to animate something like height, we have to use numeric values for both the starting and ending points. But with ‘interpolate-size’, and the allow-keywords value, we can now transition with intrinsic sizes like auto, min-content, and max-content.
:root{interpolate-size: allow-keywords;}.expandable{height: 0;transition: all 0.7s ease-in-out;}.expandable[open]{height:auto:}
This has been a long-standing challenge, and developers have been using JavaScript workarounds to achieve this. But now, from Chrome 129, it’s in the spec! Firefox and Safari are working on it too, so cross your fingers that it becomes part of the Baseline soon!
Who doesn't want a size adjust?
When we set up our preferred font-family in stylesheets, we usually include some fallback fonts. But have you ever checked if your fallback fonts have the same perceived font size? Fonts have an aspect value, which is the height of lowercase letters (like “x”) relative to uppercase letters. This aspect ratio can vary between fonts. Luckily, browsers can use this value to automatically adjust the font size when switching to a fallback font. Pretty smart, right?
To make this happen, we use the font-size-adjust property!
And here’s even better news: As of Chrome version 127, font-size-adjust is now supported across all major browsers and is part of the new Baseline.
Do you know the stylish ARIA?
When working to make the web more accessible and inclusive, we often add ARIA attributes to our components. But did you know that you can easily use those same attributes when styling your components?
nav button[aria-expanded="true"] svg{transform:rotate(180deg);}
button[aria-disabled="true"]{color: grey;}
With this approach, we don’t need extra CSS classes to handle states like ‘expanded’ or ‘disabled’. The examples above show how useful it can be, and there are plenty more possibilities. Once you start experimenting, you’ll find even more ways to take advantage of it, so give it a go!
Making stylish lists accessible
You know those HTML lists with the plain look and that little dot as a default bullet point? Sure, they’re not that bad, but we often use this CSS snippet to get a clean slate when we want to style our lists.
ul{list-style: none;}
But did you know that by doing this, you also remove the semantic meaning of the list in older versions of Safari? The list will no longer be detected as a list in the accessibility tree. But don’t worry, we have a solution, role to the rescue!
<ulrole="list"><li></li><li></li></ul>
With the help of ‘role’ we can style our lists however we want without losing support for assistive technologies. Simple!
CSS goes typesafe?
Did you know that CSS can, in a way, be typesafe and that we can use it effectively with custom properties? Now you do! Here’s the standard way to initialize a custom property. Always hotpink…
:root{--highlightColor: hotpink;}
A custom property initialized with ‘@property’ also includes the syntax type and sets inheritance, going beyond just name and value.
The benefit of this approach is that with the standard property, you expect that property to contain a color as a value. If someone updates that property to have a value other than a color, any use of the property would fail. With ‘@property’, we define a fallback color and a type. If a non-color value is used, the color will always fall back to the initial fallback value. Color is just one of many types supported.
Using ‘@property’ also enables animations of properties that were previously impossible to transition, like gradients. Neat, right!? And it’s now in Baseline!
Vertically align block children?
I don’t know how many ways we’ve tried to center children in a parent element. But guess what? We now have another method that can help us vertically align all the good stuff inside. This one works without needing to use flexbox or grid. Just take a block element and use ‘align-content’.
.container{align-content: center;}
You can also use keywords like ‘start’ and ‘end’ as well. What a one-liner, right?
Console friends and family
I know you always ship excellent code, but let’s use our imagination and think of a scenario where things go really bad and you find a tricky bug in your JavaScript code. What do you do? You start debugging. Typically, you set breakpoints or use our old friend ‘console.log()’. But did you know ‘console.log()’ has some quite smart siblings as well?
‘console.table()’ takes an array or object and logs the data as a table, with one element or property on each row. ‘console.dir()’ on the other hand displays a list of the properties and their values in the given object. The last one is ‘console.count()’, and can be placed in a function to keep track of how many times that particulary function has been called.
Using these can make your debugging process much smoother, try it!
Wrap your word around it
You know when you make a nice design, write some text, try it out and everything looks really neat? And then, in the real world, someone uses really long words and everything breaks? Wrapping to the rescue!
Do you know the difference between the two? They are similar in many ways, and you can use both of them for line-breaking controls, but using ‘overflow-wrap’ will wrap the entire overflowing word to its own line if it can fit in a single line without overflowing its container. The browser will break the word only if it cannot place it on a new line without overflowing. In my opinion, this one should be a part of your reset CSS.
‘word-break’ will break the overflowing word between two characters even if placing it on a new line would negate the need for a word break. This one can be useful in other cases, for example if you have a long copied and pasted URL. Or if you want to play around with a word and have a narrow parent container, getting every letter stacked under each other.
Same same but different!
Everything from the @starting-style
You know when you want to make a smooth entry at a party, but it’s hard to get the right feeling and you stumble? We have the same issue in CSS when we want to transition an element from ‘display: none’ onto a page. It isn’t easy. But what if I told you that we soon have an exciting solution on all major browsers? It’s called @starting-style.
Using @starting-style, we can specify styles for the element before it’s drawn to the page. They transition on page load, when they’re injected into the page, or when the display is toggled between ‘none’ and something else. In this example, it targets every element with the * selector and sets the starting style to opacity: 0, while also specifying a transition on opacity to ease-in. The result is that all elements fade in.Normally, keyframes could be used for this, but with @starting-style, we can transition.
I like it!
Meet our friends cqw and cqh
You know those companies that have abbreviations for everything that you can hardly spell or pronounce? Well, guess what? We have the same in CSS! Allow me to introduce you to the siblings ‘cqw’ and ‘cqh’. ‘cqw’ stands for a percentage of the width of the container query and ‘cqh’ naturally represents a percentage of the container’s height. Take a look at this CSS.
This means that we have a container with a child in it, and both the padding and the font-size will be 3% of the container’s width. This opens up for a very versatile responsive layout where we can place the child in a wide main section where it stands out distinctly, or in a small sidebar where the font and padding will adjust themselves to align well with the surroundings.
We have the possibilities to make stuff really responsive. We just have to learn the tools!
Have vmin and vmax up your sleeve?
When we’re delving into units, I have to tell you about the pair ‘vmin’ and ‘vmax’. The ‘vmin’ represents the smallest in percentage between the viewport width and height, while ‘vmax’ represents the largest between the two. This can come in handy when transitioning between portrait and landscape orientations. Imagine you have a grid, and with ‘vmax’ you can automatically adjust gap sizes to fit narrow viewport widths.
Let’s throw in the unit ‘ch’ as well, which approximately equals 40 characters in width. It’s equal to the width of the character ‘0’.
Play around with it, and I’m sure you find more use cases!
A new kind of cap?
Loads has changed in the world of CSS and one area that’s developed is CSS Units. One of the newcomers is a relative font unit called ‘cap’. Let’s dig deeper! It’s all about measuring the distance from the baseline to the top of capital or uppercase letters, typically the height of letters with flat tops. A neat use case for this one? Say you want to align an icon next to your text and make sure they’re both the same height and size.
Look at this:
<p>
Good Vibes Only
<svg>SVG icon in here</svg></p>
p{font-size: 2rem;}svg{height: 1cap;width: auto;}
What height will the icon have? It’ll match the height of the font exactly, and the icon will always automatically align with the baseline of the font. No matter if the font grows or gets smaller due to new CSS or browser settings.
Kind of nice!
Higher than high
Imagine you’ve designed your web and have gone through it all in your favorite browser. Everything looks really good and you’re done. Then someone tests the same things on a real mobile device and the browsers toolbox is covering some of your content. Doh!
We all know we need to take into account the size of the screen and probably you’re using vh/vw for this. The problem with this is that the browser’s toolbar height is not taken into account on mobile devices. But luckily we have a nice solution with some other units.
body{/*Small viewport,
takes the address bar and the toolbar into consideration*/height: 100svh;widht: 100svw;}
body{/*Large viewport,
doesn’t take the address bar and the toolbar into consideration*/height: 100lvh;widht: 100lvw;}
body{/*Dynamic viewport,
adapts its value when the toolbars are visible or/and when they are not.*/height: 100dvh;widht: 100dvw;}
They are all supported by the major browers, but one thing to know is that these don’t take the scrollbar into account. So you might want to use one unit value over the other. Go for it!
More attributes is fun!
Sometimes CSS is more competent than you might have thought about. One of those insight is that we have a lot of CSS functions and don’t always need to depend on javascript to do cool stuff. Just look at this one.
What does it really do? The ‘attr’ function gets the data value from the selected element and uses it in the stylesheet to show it and make it lovely pink. Works really well in both ‘::before’ and ‘::after’ pseudo elements for ‘content’. Support for other properties then ‘content’ is still experimental, but I bet you can come up with fun stuff with this one.
Avoid parent scrolling
Okay, time for a useful scrolling tool to have in the toolbox, especially if you have a smaller area on your page with elements that should be scrolled independently from the rest of the page. However, you might have encountered a frustrating issue where the scroll interaction passes to the parent element once it runs out of elements. But we have this one to the rescue:
article{overscroll-behavior: contain;}
With ‘overscroll-behavior: contain’ scrolling is isolated to the contained element, preventing it from traveling to the parent. This is a nice trick especially for scrollable sidebars on longer pages. But I bet you can come up with more cases where it can come in handy.
Ever copy-paste?
You know how we often find ourselves double or triple clicking, and then clicking again because we didn’t get it right, just to highlight a piece of text for copy-pasting? Do you also know we can help our users to do this? This little trick can make it so much easier.
p{user-select: all;}
This single line of code makes all the text within the ‘p’ element highlight with just one click, allowing to copy right away. ‘user-select’ can be applied to different elements such as ‘p’ or ‘span’, or even to a parent if it makes sense to easily select a longer piece of information for copying.
Neat, right!?
Pretty words
Sometimes, the magic lies in the tiny details. In an earlier beat , I showed ‘text-wrap: balance’ as a great way to manage headings. But have you heard about its ‘pretty’ counterpart?
The ‘pretty’ option can be applied to entire blocks of text, although its effect is noticeable primarily on the last four lines. Its main purpose is to prevent orphans—a single word—appearing alone on a line at the end of the text.
p{text-wrap: pretty;}
This subtle change provides a solution to avoid hacky ways to handle those words that looks visually out of place.
Right now it only has support in Chomium but it won’t negatively affect the experience if someone is not in a supported browser. So use it! It will add some visual balance to the page for those in a browser where supported.
Combinding different media
Okay, we have taked about media queries before but here is another one. Media Queries Level 4 introduces ways to combine media queries with ‘and’, ‘not’ and ‘or’. The first exemples below can be read like device with no hover capability and the second one is device with no hover or on a device were the viewport is in landscape mode.
You know media queries? Same old, same old… or not? Media Queries Level 4 spec includes some new stuff including syntax improvements with a range type. This is suppose to make media queries less verbose when using it with propeties like width or height.
A new way of thinking about it. Stay tuned for more media query stuff!
Ever felt inert?
Lacking the ability or strength to move, is what ‘inert’ means according to a google search. Notably, in the frontend world, its meaning isn’t that far off. ‘inert’ is a boolean expression that can be used on a HTML element like this:
<dialoginert>...</dialog>
When used it tells the browser to ignore it. It also makes the browser ignore input events like click or focus. Used on a section it will ignore all the children in the section and elements like links or buttons can’t be reached. One useful usecase is when added to elements that are visually hidden or offscreen, like when collapsed or paginated. When ‘inert’ is added it gets removed from both the tab order and the accessibility tree.
This attribute is most effective when used on groups of elements. Otherwise, we also have the ‘disabled’ state in HTML and ‘:disabled’ in CSS to consider.
Nice right!?
Balance those beautiful words
Have you ever received a design from your UX/UI-designer where every word ends and aligns perfectly with the elements around it? But when you put it into code, it looks nothing like the original design?
As developers, we often lack information about the final size, font size, or language of a headline. All the necessary variables for effectively and aesthetically treating text wrapping are in the browser. However, now we have a solution to help balance text.
‘text-wrap’ is used to control how text wraps within its container, and ‘balance’ enables automatic balancing of text lines across multiple columns, resulting in a visually appealing and balanced layout. It’s a small detail with a nice visual impact.
I absolutely love this feature!
All those children
You’re familiar with the nth-child selector, right? But did you also know about the more advanced nth-child selection with the keyword ‘of’?
As you probably know, the regular nth-child works like this: If you use :nth-child(2) on the ‘.super’ class, the browser selects the element that has the class ‘.super’ applied to it and is also the second child. However, using ‘of’ works slightly differently. The :nth-child(2 of .super) first filters all elements with the class ‘.super’ and then selects the second one from that list.
.super:nth-child(2){/* Select the element with the class .super that is also the 2nd child */background-color: hot-pink;}:nth-child(2 of .super){/* Select the 2nd child element that has the class .super applied to it */background-color: gold;}
A pretty nice addition to our nth-child toolbox if you ask me!
Old but new margins
Margins are not new in CSS. We have been using them for a long time, often with ‘margin’ as a shorthand for ‘margin-top’, ‘margin-bottom’, ‘margin-left’, or ‘margin-right’. However, do you also use ‘margin-block’ and ‘margin-inline’? As you might imagine ‘margin-block’ is equivalent to ‘margin-top’ and ‘margin-bottom’ while ‘margin-inline’ is the same as ‘margin-left’ and 'margin-right.
margin-block: 10px 20px;margin-inline: 2em, 4em;
Okay, is it just another words for the same thing then? Not really, because both ‘margin-block’ and ‘margin-inline’ adjust to the element’s writing mode, directionality, and text orientation. Take a look at this.
In the ‘div’ the text will be horizontally centered, and in the ‘vertically’ it will magically be centered vertically. This is just one example to give you an idea of the possibilities, can you see more?
Aspects of that ratio
What if I tell you that there is a property that adjusts the dimension of an element even if the parent container or the viewport changes? It’s called ‘aspect-ratio’. You can explicitly set the width or height of an element, and ‘aspect-ratio’ will use the dimension set to auto to maintain the specified width-to-height ratio.
If you set both width and height to specific measurements, that will override the aspect ratio, which is both intentional and useful.
Just a couple of tips: first, when used together with flexbox and grid, it works best without the default value, ‘align-items: stretch.’ And lastly, used with images together with ‘object-fit,’ it can work wonders.
A negative one
Did you know that you can use negative line numbers to position grid items with CSS Grid? It’s actually quite handy. As you probably know, you can place items along starting lines, counting from the left and spanning over a specified number of lines or columns. But what if you want to place items from the right? How can you do this in an easy way? You could count your items and place them according to the column in the grid, but that sounds like too much fuss. Instead, look at this.
This means that the child in the grid will always start at the last line of the grid, covering the last 6 columns.
Pretty nice, isn’t it?
Inset this one
If I say top, left, right, and bottom, I imagine that you’re thinking about absolutely positioned elements. But what if I say ‘inset’? This is what we usually do.
But we have a shorthand! ‘Inset’ is a shorthand for the four inset properties: top, right, bottom, and left all in one declaration. It works exactly the same way as margin and padding, and you can use it in the same way as well.
.container{position: absolute;inset: 0;}inset: 2em 4em 8em 0;/* top right bottom left */inset: 20% 10% 30%;/* top left/right bottom */inset: 0 10px;/* top/bottom left/right */inset: 24px;/* all edges = 20px */
Easy and clean!
The quickest centering?
Okay, how many times have you tried to find the easiest way to center a child element within its parent? We have multiple methods for achieving this, including margins, flexbox, text alignment, vertical align, tables, and so on. But have you ever tried CSS grid? It is a one-liner worth trying.
.parent{display: grid;place-items: center;}
You simply create a grid with one cell and center your child element(s) in it. Done!
Do you know - all?
Imagine that you’re about to style a button or a checkbox. Yes, I know, they are not pretty in default. You have cover up a lot of styling with new css. But what if I told you there is a simpler way?
all: unset;
This is the easiest way to, yes unset all properties of an element to it’s initial value. Or to it’s inherited values if they inherit by default. If you for example put it on a button it will look like plain text.