Skip to main content
Photography of Alvaro Montoro being a doofus
Alvaro Montoro

Soccer Fan

New Values and Functions in CSS

The CSS Working Group released the first Public Working Draft for the CSS Values and Units Module Level 5. It describes the common values and units that CSS properties accept and the syntax used for them, and it comes with some interesting new features.

css webdev

On September 13, 2024, the CSS Working Group released the first Public Working Draft for the CSS Values and Units Module Level 5. It is an extension of the previous level that includes some interesting additions.

Things that were unimaginable not so long ago are making their way into the specs: random values, using attributes as values in any property, being able to use the order in calculations... It looks promising.

Many of these features have a common denominator: they simplify the CSS code. Things that before would require multiple rules or hacky solutions will be possible with one or two lines of CSS. As I said, it looks promising. This is a list of the new changes (more details below):

  • Changes to attr() function: so it can be used with any attribute and in any CSS property (not only on content).
  • calc-size() function: use intrinsic values such as auto or min-content in calculations.
  • New first-valid() function to avoid issues with custom properties with invalid values.
  • New *-mix() family of functions with a new notation for ratios.
  • New *-progress() family of functions to calculate the progress ratio between a range or within a media or container.
  • Randomization with new random() and random-item() functions, to return random values from a range or list (finally!)
  • New sibling-count() and sibling-index() functions that provide integer values to operate depending on the order and size.
  • New toggle() function for styling nested elements easily cycling over a list of values.
  • New functional notation for arguments with comma-separated lists of values, to avoid ambiguity with the comma separating the arguments.
  • New URL modifiers to provide more control over url() requests.
  • Extension of the position type to allow flow-relative values.

New Features and Updates

Changes to attr() function

Reading an attribute and using it in CSS is not new. That was already possible with attr(), but a common complaint was how limited the functionality was, only working with strings and in the content.

The attr() function will go through some updates, so any data attribute independent of its data type can be used in any property. It will be as simple as specifying the type and, if we want to, a fallback value just in case something doesn't go as expected.

This is a long-awaited update that will make a lot of developers happy.

Attr() function. The attr() function is extended so it can be used with any of the element's attributes, and with any CSS property (at the moment, it only works with content). It can take two optional parameters: a unit for the attribute and a fallback value. CSS Code snippet


Operations with intrinsic values using calc-size()

This module also introduces a new function able to safely operate with intrinsic values (auto, max-content, fit-content, etc.) This is a feature that will be especially helpful in transitions and animations.

It also adds new keywords (size) to provide more flexibility to the calculations, making it easier to work with the sizes.

Why have a whole new function when calc() is already there? As the document explains, there are backward compatibility and practical reasons why it was done this way (e.g., smooth interpolations in all cases, especially when operating in percentages.)

calc-size function. The calc() function works great with definite values, but cannot operate with intrinsic values (such as auto or fit-content). The new calc-size() function allows calculations with intrinsic values in a safe and well-defined way. CSS Code snippet


New first-valid() function

A new method is introduced: first-valid(). The idea is to pass a list of values to the function; they will be resolved, and the first valid one will be the one used. This will be especially useful when dealing with CSS custom properties (aka CSS variables).

One issue when operating with CSS variables is that, within a declaration, they are considered a valid value, even if the actual contained value isn't valid. Setting a fallback value won't help either, and a fallback declaration will be ignored, too.

With this method, we could simplify the code by consolidating all fallback declarations into a single one with first-valid().


New *-mix() family of functions

It also introduces a new function, mix(), that can be used to simplify the different *-mix functions. Do you want to mix colors? You could do something like color-mix(red 60%, blue) or a simpler mix(60%, red, blue) will do the trick too. And as we say colors, we could also mix lengths, transform functions, etc.

That notation is also extended to the other *-mix family of functions:

  • calc-mix()
  • color-mix()
  • cross-fade()
  • palette-mix()

If no easing function is specified in the progress parameter (the first one), linear will be applied by default.


New *-progress() family of functions

They represent the proportional progress of a given value from one starting value to another end value. The result is a number between 0 and 1 that can be used in operations, but it will be especially handy when combined with the *-mix family of functions previously described.

There are three functions in this family:

  • progress(): generic, for any math function.
  • media-progress(): for media features.
  • content-progress(): for container queries.


Randomization functions in CSS

Fun designs have some level of randomization, something that was missing in CSS. But this module introduces two new functions that return random values from a list (random-item()) or between a range (random()).

No more hacky tricks or dependencies on other languages to achieve this. The syntax is straightforward and powerful, too, with the possibility to calculate the random number by selector or element.


New sibling functions

Sometimes you may want to provide different styles depending on the order of the elements within a container. Unfortunately, counters cannot be used like that in CSS (I'll leave that rant for another day).

With the introduction of two new functions that return a number, making it possible to operate with them, this roadblock is removed:

  • sibling-count(): returns the number of siblings.
  • sibling-index(): returns the position/order of the element within the list of siblings.

No more need to set custom properties on each element or write individual selectors with nth-child.


New toggle() function

A convenient new way to define values in nested elements is introduced. The toggle() function sets values that the element and its descendants will cycle through, simplifying the code considerably. Forget about complex rules or redefinitions — everything will be in a single line of code.

For example, imagine that we have a list with four nested levels. We want the odd levels to have discs and the even levels to be squares. We could have some fun doing ul > li ul > li ul > li ul { ... } at different levels, or we can just do something like ul { list-style-type: disc, square; }. Boom! Done!

The only thing that is a bit concerning about this function is its name. Maybe it's just me, but the word "toggle" has "duality" connotations: on/off, yes/no — two values that switch/toggle between each other. The toggle() function can have as many parameters as you want, so it feels weird that it's named "toggle."


New functional notation for arguments

One thing you may have noticed is how some of the new functions (e.g., random() or toggle()) can take arguments that are comma-separated lists of values.

How can we distinguish one argument from the next in those cases? That is why there's a proposal for "comma upgrading" for functional notations. This means we can use a semicolon (;) instead of a comma (,) to separate parameters unambiguously.

For example, imagine that you want to have a random font-family on your page and specify different options:

  • Times, serif
  • Arial, sans-serif
  • Roboto, sans-serif

All those arguments are comma-separated lists of values. If we used a comma to separate the arguments, it would be a huge mess. But with the new notation, it is easy to identify where one argument ends and the next one begins:

.random-font {
  font-family: random-item(
                 Times, serif; 
                 Arial, sans-serif; 
                 Roboto, sans-serif
               );
}


Extension to the position type

CSS already has logical properties for margin, padding, and border — values that are relative to the text writing direction and may change from one language to another.

This is now introduced for the position type (not to be confused with the position property). Properties that indicate position (e.g., background-position, object-position, etc.) can specify values that will be relative to the text flow and direction.

The new values that can be used are:

  • x-start
  • x-end
  • y-start
  • y-end
  • block-start
  • block-end
  • inline-start
  • inline-end


Conclusion

It is still at an early stage, and things will change, but some of the new features and functionality included in the CSS Values and Units Module Level 5 look incredibly promising.

Some are long-awaited, too! Especially the possibility of using any attribute with any property. I recall seeing that option in the specs a long time ago. Hopefully, this is the push it needs to make it a reality.

Don't forget to check the CSS Values and Units Module Level 5 Working Draft for more details and information. If you have any questions or comments, log a ticket at their GitHub repo.

Happy (CSS) experimenting and coding!

Article originally published on