attr()

Baseline Widely available *

This feature is well established and works across many devices and browser versions. It’s been available across browsers since July 2015.

* Some parts of this feature may have varying levels of support.

Note: The attr() function can be used with any CSS property, but support for properties other than content is experimental.

The attr() CSS function is used to retrieve the value of an attribute of the selected element and use it in a property value, similar to how the var() function substitutes a custom property value. It can also be used with pseudo-elements, in which case the attribute's value on the pseudo-element's originating element is returned.

Try it

Syntax

css
/* Basic usage */
attr(data-count)
attr(href)

/* With type */
attr(data-width px)
attr(data-size rem)
attr(data-name string)
attr(id type(<custom-ident>))
attr(data-count type(<number>))
attr(data-size type(<length> | <percentage>))

/* With fallback */
attr(data-count type(<number>), 0)
attr(data-width px, inherit)
attr(data-something, "default")

Parameters

The attr() function's syntax is as follows:

attr(<attr-name> <attr-type>? , <fallback-value>?)

The parameters are:

<attr-name>

The attribute name whose value should be retrieved from the selected HTML element(s).

<attr-type>

Specifies how the attribute value is parsed into a CSS value. This can be the string keyword, a type() function, or a CSS dimension unit. When omitted, it defaults to string.

  • The string keyword parses the value into a CSS string.

    css
    attr(data-name string, "stranger")
    
  • The type() function takes a <syntax> as its argument that specifies what data type to parse the value into. The <syntax> can be <angle>, <color>, <custom-ident>, <image>, <integer>, <length>, <length-percentage>, <number>, <percentage>, <resolution>, <string>, <time>, <transform-function>, or a combination thereof.

    css
    attr(id type(<custom-ident>), none)
    attr(data-count type(<number>), 0)
    

    To accept multiple types, list all allowed <syntax>es in the type() function, separated by a |.

    css
    attr(data-size type(<length> | <percentage>), 0px)
    

    For security reasons <url> is not allowed as a <syntax>.

  • The <attr-unit> identifier specifies the unit a numeric value should have (if any). It can be the % character (percentage) or a CSS distance unit such as px, rem, deg, s, etc.

    css
    attr(data-size rem)
    attr(data-width px, inherit)
    attr(data-rotation deg)
    
<fallback-value>

The value to be used if the specified attribute is missing or contains an invalid value.

Return value

The return value of attr() is the value of the HTML attribute whose name is <attr-name> parsed as the given <attr-type> or parsed as a CSS string.

When an <attr-type> is set, attr() will try to parse the attribute into that specified <attr-type> and return it. If the attribute cannot be parsed into the given <attr-type>, the <fallback-value> will be returned instead. When no <attr-type> is set, the attribute will be parsed into a CSS string.

If no <fallback-value> is set, the return value will default to an empty string when no <attr-type> is set or the guaranteed-invalid value when an <attr-type> is set.

Description

Limitations and security

The attr() function can reference attributes that were never intended by the page author to be used for styling, and might contain sensitive information (for example, a security token used by scripts on the page). In general, this is fine, but it can become a security threat when used in URLs. You therefore can't use attr() to dynamically construct URLs.

html
<!-- This won't work! -->
<span data-icon="https://example.org/icons/question-mark.svg">help</span>
<style>
  span[data-icon] {
    background-image: url(attr(data-icon));
  }
</style>

Values that use attr() get marked as "attr()-tained". Using an attr()-tainted value as or in a <url> makes a declaration become "invalid at computed value time" or IACVT for short

Backwards compatibility

Generally speaking, the modern attr() syntax is backwards-compatible because the old way of using it — without specifying an <attr-type> — behaves the same as before. Having attr(data-attr) in your code is the same as writing attr(data-attr type(<string>)) or the simpler attr(data-attr string)).

However, there are two edge cases where the modern attr() syntax behaves differently from the old syntax.

In the following snippet, browsers that don't support the modern attr() syntax will discard the second declaration because they cannot parse it. The result in those browsers is "Hello World".

html
<div text="Hello"></div>
css
div::before {
  content: attr(text) " World";
}
div::before {
  content: attr(text) 1px;
}

In browsers with support for the modern syntax, the output will be … nothing. Those browsers will successfully parse the second declaration but, because it is invalid content for the content property, the declaration becomes "invalid at computed value time" or IACVT for short.

To prevent this kind of situation, feature detection is recommended.

A second edge case is the following:

html
<div id="parent"><div id="child" data-attr="foo"></div></div>
css
#parent {
  --x: attr(data-attr);
}
#child::before {
  content: var(--x);
}

Browsers without support for modern syntax display the text "foo". In browsers with modern attr() support there is no output.

This is because attr() — similar to custom properties that use the var() function — get substituted at computed value time. With the modern behavior, --x first tries to read the data-attr attribute from the #parent element, which results in an empty string because there is no such attribute on #parent. That empty string then gets inherited by the #child element, resulting in a content: ; declaration being set.

To prevent this sort of situation, don't pass inherited attr() values onto children unless you explicitly want to.

Feature detection

You can feature detect support for modern attr() syntax using the @supports at-rule. In the test, try to assign advanced attr() to a (non-custom) CSS property.

For example:

css
@supports (x: attr(x type(*))) {
  /* Browser has modern attr() support */
}

@supports not (x: attr(x type(*))) {
  /* Browser does not have modern attr() support */
}

We can perform the same check in JavaScript with CSS.supports():

js
if (CSS.supports("x: attr(x type(*))")) {
  /* Browser has modern attr() support */
}

if (!CSS.supports("x: attr(x type(*))")) {
  /* Browser does not have modern attr() support */
}

Formal syntax

<attr()> = 
attr( <attr-name> <attr-type>? , <declaration-value>? )

<attr-name> =
[ <ident-token>? '|' ]? <ident-token>

<attr-type> =
type( <syntax> ) |
string |
<attr-unit>

<syntax> =
'*' |
<syntax-component> [ <syntax-combinator> <syntax-component> ]* |
<syntax-string>

<syntax-component> =
<syntax-single-component> <syntax-multiplier>? |
'<' transform-list '>'

<syntax-combinator> =
'|'

<syntax-string> =
<string>

<syntax-single-component> =
'<' <syntax-type-name> '>' |
<ident>

<syntax-multiplier> =
'#' |
'+'

<syntax-type-name> =
angle |
color |
custom-ident |
image |
integer |
length |
length-percentage |
number |
percentage |
resolution |
string |
time |
url |
transform-function

Examples

content property

In this example, we prepend the value of the data-foo data-* global attribute to the contents of the <p> element.

HTML

html
<p data-foo="hello">world</p>

CSS

css
[data-foo]::before {
  content: attr(data-foo) " ";
}

Result

Using a fallback value

Experimental: This is an experimental technology
Check the Browser compatibility table carefully before using this in production.

In this example, we append the value of data-browser data-* global attribute to the <p> element. If the data-browser attribute is missing from the <p> element, we append the fallback value of "Unknown".

HTML

html
<p data-browser="Firefox">My favorite browser is:</p>
<p>Your favorite browser is:</p>

CSS

css
p::after {
  content: " " attr(data-browser, "Unknown");
  color: tomato;
}

Result

color value

Experimental: This is an experimental technology
Check the Browser compatibility table carefully before using this in production.

In this example, we set the CSS value of background-color to the value of the data-background data-* global attribute assigned to the <div> element.

HTML

html
<div class="background" data-background="lime">
  background expected to be red if your browser does not support advanced usage
  of attr()
</div>

CSS

css
.background {
  background-color: red;
}

.background[data-background] {
  background-color: attr(data-background type(<color>), red);
}

Result

Using dimension units

Experimental: This is an experimental technology
Check the Browser compatibility table carefully before using this in production.

In this example the data-rotation attribute is parsed into a deg unit, which specifies the element's rotation.

HTML

html
<div data-rotation="-3">I am rotated by -3 degrees</div>
<div data-rotation="2">And I by 2 degrees</div>
<div>And so am I, using the fallback value of 1.5deg</div>

CSS

css
div {
  width: fit-content;
  transform-origin: 50% 50%;
  rotate: attr(data-rotation deg, 1.5deg);
}

Result

Parsing attr() values as <custom-ident>s

Experimental: This is an experimental technology
Check the Browser compatibility table carefully before using this in production.

In this example, the values for the view-transition-name property are derived from the id attribute of the element. The attribute gets parsed into a <custom-ident>, which is what view-transition-name accepts as a value.

The resulting values for view-transition-name are card-1, card-2, card-3, etc.

HTML

The HTML contains four cards with different id attributes and a "Shuffle cards" <button>, which shuffles the cards.

html
<div class="cards">
  <div class="card" id="card-1">1</div>
  <div class="card" id="card-2">2</div>
  <div class="card" id="card-3">3</div>
  <div class="card" id="card-4">4</div>
</div>
<button>Shuffle cards</button>

CSS

The cards are laid out in a flex container:

css
.cards {
  display: flex;
  flex-direction: row;
  gap: 1em;
  padding: 1em;
}

On each card, the attr() function gets the id attribute and parses it into a <custom-ident>, which is used as the value for the view-transition-name property. When there is no id set on a card, the fallback value none is used instead.

css
.card {
  view-transition-name: attr(id type(<custom-ident>), none);
  view-transition-class: card;
}

JavaScript

When the <button> is pressed the cards are shuffled. This is done by randomizing the order of an array containing references to all the cards and then updating the order property of each card to its new array index position.

To animate each card to its new position, View Transitions are used. This is done by wrapping the order update in a call to document.startViewTransition.

js
const shuffle = (array) => {
  for (let i = array.length - 1; i >= 0; i--) {
    const j = Math.floor(Math.random() * (i + 1));
    [array[i], array[j]] = [array[j], array[i]];
  }
};

document.querySelector("button").addEventListener("click", (e) => {
  const $cards = Array.from(document.querySelectorAll(".card"));
  shuffle($cards);
  document.startViewTransition(() => {
    $cards.forEach(($card, i) => {
      $card.style.setProperty("order", i);
    });
  });
});

Result

Specifications

Specification
CSS Values and Units Module Level 5
# attr-notation

Browser compatibility

Report problems with this compatibility data on GitHub
desktopmobile
Chrome
Edge
Firefox
Opera
Safari
Chrome Android
Firefox for Android
Opera Android
Safari on iOS
Samsung Internet
WebView Android
WebView on iOS
attr()
<fallback>
<type-or-unit>
Experimental
<angle>
Experimental
<color>
Experimental
<frequency>
Experimental
<integer>
Experimental
<length>
Experimental
<number>
Experimental
<percentage>
Experimental
<time>
Experimental
<url>
Experimental

Legend

Tip: you can click/tap on a cell for more information.

Full support
Full support
In development. Supported in a pre-release version.
In development. Supported in a pre-release version.
No support
No support
Experimental. Expect behavior to change in the future.
See implementation notes.

See also