Get affordable and hassle-free WordPress hosting plans with Cloudways — start your free trial today.
The CSS attr()
function enables you to pass the value of an HTML attribute over to CSS. It’s not a “new” feature, but it has been upgraded in recent times, making it significantly more useful.
Here’s a quick example pulling a data-attribute called data-color
from the HTML and using its value as the element’s color in CSS:
<div class="element" data-color="#f8a100">...</div>
.element {
color: attr(data-color type(<color>));
}
This is another example, this time taking a data-font-size
attribute value from HTML and using it to set the element’s font size in CSS:
<div class="element" data-font-size="20">...</div>
.element {
font-size: attr(data-font-size px);
}
Starting from Chrome 133 (more browsers are working on this as well), you can use the attr()
function with any CSS property, whereas in the past it was only supported by the content
property. The other big news is that attr()
can now parse values into a range of data types, whereas before they were always parsed as strings.
As you might have surmised, this enables us to do stuff with the imported values instead of just outputting them as strings on the content
property.
Syntax
attr(<attr-name> <attr-type>?, <fallback-value>?)
In plain English, we specify which HTML attribute we want to fetch, say what type of data it is, then provide an optional fallback value that can be used if that attribute doesn’t exist for some reason. Let’s take a look at a basic use case to see how this comes together.
Basic usage
Before Chrome 133, we would use attr()
with the content
property. For example, we could use it to read the value of an attribute as a string, such as the URL in a link’s href
attribute:
<a href="https://example.com/">...</a>
…and then output that string on the element’s content
property to display the URL:
a:empty::before {
content: attr(href);
}
We still can do that! But now we can use the attr()
function with any property, not just content
:
<p data-font-size="20">...</p>
p {
font-size: attr(data-font-size);
}
However, this doesn’t work as intended because font-size
doesn’t accept unitless values, so right after the attribute’s name, we need to specify what sort of unit it is. For example, we can convert that 20
into 20px
by specifying the px
unit:
p {
font-size: attr(data-font-size px);
}
We can also declare the unit in the HTML:
<p data-font-size="20px">...</p>
However, when passed into attr()
, this value isn’t actually twenty of anything but rather the phrase “20px” as a string. To “parse” it as a 20px
numeric value, we need to specify its data type using the type()
function in the declaration:
p {
font-size: attr(data-font-size type(<length>));
}
We can also specify a fallback value, which will be used if the attribute doesn’t exist in the HTML. For example, let’s say that we have a paragraph element that lacks any sort of attribute:
<p>...</p><!-- Attribute missing -->
We can still use attr()
, this time accounting for missing attributes by setting a fallback value after declaring the type()
:
p {
/* Fails when attribute is missing in HTML */
font-size: attr(data-font-size);
/* Defaults to 16px */
font-size: attr(data-font-size type(<length>), 16px);
}
These would also fail:
p {
font-size: attr(data-font-size px, 16);
font-size: attr(data-font-size type(<percentage>), 200);
}
…because the fallback must be a valid value. In both cases, there is no unit specified on the fallback value, leaving the browser clueless as far as what it is setting. Instead, we need explicitly declare the unit when setting the fallback value:
div {
font-size: attr(data-font-size, 16px);
font-size: attr(data-font-size, 200%);
}
attr()
data types
CSS As we’ve established so far, by default, attr()
parses the return value as a string. If that’s not what you’re looking for, then you’ll need to specify which data type you need. This is the full list of available data types:
Data type | Example | Notes |
---|---|---|
<string> (the default) | content: attr(href <string>); or just content: attr(href); | The return value will be parsed as plain text |
<angle> | transform: rotate(attr(data-rotation type(<angle>))); | |
<color> | color: attr(data-color type(<color>)); | |
<custom-ident> | animation-name: attr(data-animation-name type(<custom-ident>)); | |
<image> | background-image: attr(data-linear-gradient type(<image>)); | |
<integer> | column-count: attr(data-column-count type(<integer>)); | An integer can be a positive or negative whole number |
<number> | opacity: attr(data-opacity type(<number>)); | A number can be an integer (a positive or negative whole number) or a number with a fractional component (e.g., 0.5 ) |
<length> | width: attr(data-width type(<length>)); | |
<percentage> | width: attr(data-width type(<percentage>)); | |
<length-percentage> | width: attr(data-width type(<length-percentage>)); | For properties that accept length and percentage values, but you don’t necessarily know which one it’ll be |
<resolution> | N/A | I could not think of any instance or example where a resolution unit would be declared on a property. |
<time> | animation-duration: attr(data-duration type(<time>)); | |
<transform-function> | transform: attr(data-transform-function <transform-function>); |
URLs aren’t welcome here (mostly)
For security reasons, URLs only work with attr()
when outputted as plain text (<string>
) by the content
property:
<a href="https://example.com/"></a>
a:empty::before {
content: attr(href); /* Missing anchor text = output href */
}
We can’t use them as a <url>
data type:
<div data-image="https://example.com/image.svg">...</div>
div {
/* Does not work! */
background-image: url(attr(data-image type(<url>)));
}
This is a shame, because this would be quite useful. Typically when we have image URLs in our templating logic, we can’t put them into external CSS, which isn’t ideal for specificity or separation of concerns. But alas, I don’t think attr()
will support the <url>
data type either now or in the future.
Example: Setting font size and color
Earlier, we looked at the following example:
p {
font-size: attr(data-font-size type(<length>));
}
The following is a demo that uses the same technique. Try updating the data-font-size
attribute value to see how the font size adjusts accordingly. The demo only works in Chrome 133+ at the time of writing.
For bonus points, try changing the data-color
attribute in the HTML to any valid CSS color value to see the text color update.
Example: Star ratings
I have two methods of showing a star rating using attr()
; a simpler method that uses a CSS Grid layout — that comes with some caveats — and a trickier method that uses calc-size()
but works more smoothly.
The CSS Grid version:
The key part is that we have an inline-grid
where each star goes into a column (grid-template-columns
) of no particular width (auto
). We use the repeat()
function to define how many columns, which of course is where we put the attr(data-rating type(<number>))
. “Spare” stars get wrapped underneath, but we don’t see them because they’re cut off by a fixed height and overflow: hidden
.
The biggest caveat is that the repeat()
function of grid-template-columns
only accepts an integer, which means that we can’t do fractional stars. Still, this is a cool example of how we can use the return value of attr()
within a property instead of being the whole value.
The calc-size()
version:
In this version there’s no wrapping and no fixed height
. Instead, the stars are on a single flex row and cut off using width
and overflow: hidden
.
Let’s start with the calc()
function. In the example above, the “2.25” rating is divided by the maximum rating of 5
, which equals 0.45
. This 0.45
is then multiplied by the container’s width, which is interpolated from min-content
and represented by size
using calc-size()
, leaving us with a fractional rating. Perfect — we almost accomplished the entire thing in a one-liner!
Browser support
The CSS attr()
function has been supported by all browsers for many years, but only when used with a <string>
type on the CSS content
property.
The upgraded version of attr()
that supports more data types for use on additional properties only offers limited browser support at the time of writing.
In the meantime, we can detect support for it and use it conditionally using @supports
:
@supports (x: attr(x type(*))) {
/* Advanced attr() supported */
}
@supports not (x: attr(x type(*))) {
/* Advanced attr() not supported */
}
The same thing in JavaScript:
if (CSS.supports("x: attr(x type(*))")) {
/* Advanced attr() supported */
}
if (!CSS.supports("x: attr(x type(*))")) {
/* Advanced attr() not supported */
}
Specification
The attr()
function is defined in the CSS Values and Units Module Level 5 specification, which is currently in Working Draft status. That means the information can change between now and the time it becomes adopted as a formal Candidate Recommendation for browsers to implement.
More information
- CSS
attr()
gets an upgrade (Chrome for Developers) - A more powerful CSS
attr()
function (Fully Stacked) - The
attr()
function in CSS now supports types (Amit Merchant)