CSS attr() Function

The CSS attr() function retrieves the value of an HTML attribute and uses it in a CSS declaration. It is commonly used with the content property to display attribute values, such as labels, descriptions, and tooltips. Modern CSS also allows typed attribute values to be used for properties such as width, color, and rotation, although browser support for these newer capabilities varies.

Syntax

attr(attribute-name)
attr(attribute-name, fallback)
attr(attribute-name type, fallback)

The attr() function accepts an attribute name, an optional value type, and an optional fallback.

For example:

.label::after {
  content: attr(data-label);
}

This retrieves the data-label attribute from the element and displays its value using the ::after pseudo-element.

A fallback can be provided:

.label::after {
  content: attr(data-label, "No label");
}

If the attribute is missing, the generated content displays No label.

Modern CSS also supports specifying an expected data type:

.box {
  width: attr(data-width type(<length>), 150px);
}

This reads a CSS length from the data-width attribute, using 150px if the attribute is missing or cannot be parsed as a length.

Parameters

The attr() function accepts the following components.

Parameter Description
attribute-name Required. The name of the attribute to retrieve from the element, such as title, data-label, or data-width.
type Optional. Specifies how the attribute value should be interpreted, using type(), raw-string, or a CSS unit.
fallback Optional. A value used when the attribute is missing or cannot be interpreted as the specified type.

Common examples include:

attr(data-label)
attr(data-label, "Unknown")
attr(data-label raw-string)
attr(data-count type(<number>), 0)
attr(data-width px, 100px)
attr(data-size type(<length>), 150px)
attr(data-color type(<color>), blue)
attr(data-angle deg, 0deg)

When no type is specified, the attribute value is treated as a CSS string.

Typed values require browsers that support the modern attr() syntax.

How It Works

The attr() function reads an attribute from the element receiving the CSS declaration.

Consider the following HTML:

<span class="status" data-state="Active">Account</span>

The attribute can be displayed using CSS:

.status::after {
  content: " - " attr(data-state);
}

The resulting text is:

Account - Active

The function follows these steps:

  1. Find the specified attribute on the selected element.
  2. Read its value.
  3. Interpret the value as a string or the specified CSS type.
  4. Substitute the value into the CSS declaration.
  5. Use the fallback when the attribute is missing or invalid for the requested type.

When used in a pseudo-element, attr() reads attributes from the originating HTML element.

If the attribute changes, the browser updates the resulting CSS value as needed.

The function does not change the HTML attribute itself.

Using attr() with content

The most established use of attr() is with the content property and the ::before or ::after pseudo-element.

For example:

<a href="/contact.html" data-label="Contact Page">Contact</a>

The attribute value can be displayed after the link:

a::after {
  content: " (" attr(data-label) ")";
}

The displayed result is:

Contact (Contact Page)

Another example displays a product code:

<span class="product-code" data-code="A102">Item</span>
.product-code::after {
  content: " - Code: " attr(data-code);
}

The displayed result is:

Item - Code: A102

Generated content is useful for visual labels and supplemental information. Important information should remain available in the HTML because generated content is not consistently announced by every screen reader.

Typed Attribute Values

Modern CSS extends attr() beyond strings by allowing attribute values to be parsed as specific CSS data types.

The type() function identifies the expected type.

Type Example Purpose
<number> attr(data-count type(<number>), 0) Read a numeric value.
<integer> attr(data-order type(<integer>), 1) Read a whole number.
<length> attr(data-width type(<length>), 100px) Read a length with a unit.
<percentage> attr(data-progress type(<percentage>), 0%) Read a percentage.
<color> attr(data-color type(<color>), blue) Read a CSS color.
<angle> attr(data-angle type(<angle>), 0deg) Read an angle.
raw-string attr(data-label raw-string) Read the attribute as literal text.

For example:

<div class="color-box" data-color="#0066cc">Color Example</div>
.color-box {
  background-color: attr(data-color type(<color>), #cccccc);
}

In a browser supporting typed attr(), the background color becomes #0066cc.

If the attribute contains an invalid color, the fallback #cccccc is used.

The type() notation is part of the modern CSS Values and Units specification and is not supported in every browser version.

Using CSS Units

The attr() function can also assign a CSS unit to a numeric attribute value.

For example:

<div class="size-box" data-width="200">Example</div>
.size-box {
  width: attr(data-width px, 100px);
}

The numeric attribute value 200 is interpreted as 200px.

Other units can be used:

width: attr(data-width rem, 10rem);
height: attr(data-height em, 5em);
transform: rotate(attr(data-angle deg, 0deg));
transition-duration: attr(data-time s, 1s);

The attribute must contain a suitable numeric value when a unit is specified.

For example, the attribute data-width="200px" is not appropriate for attr(data-width px) because the unit is already present.

Use type(<length>) when the attribute itself contains a complete length such as 200px or 12rem.

Unit-based attr() syntax requires modern browser support.

Fallback Values

The attr() function accepts an optional fallback value.

For a string attribute:

.label::after {
  content: attr(data-label, "Not available");
}

If data-label is missing, the generated content displays Not available.

An empty attribute is different from a missing attribute:

<span class="label" data-label="">Example</span>

In this case, attr() returns the empty string rather than the fallback.

Typed attributes can also use fallbacks:

.box {
  width: attr(data-width type(<length>), 150px);
}

The fallback is used when the attribute is missing or does not contain a valid length.

For example, data-width="large" cannot be parsed as a length, so the width becomes 150px.

Without an explicit fallback, a missing typed attribute can make the declaration invalid at computed-value time.

A fallback inside attr() does not provide compatibility with browsers that do not recognize the modern syntax. For those browsers, use a separate supported declaration or another fallback strategy.

Example

The following example uses attr() to display labels stored in HTML data-label attributes.

Each box uses the same CSS rule, but its generated label comes from its own HTML attribute.

HTML:

<div class="attr-demo">
  <div class="attr-item" data-label="HTML">Markup</div>
  <div class="attr-item" data-label="CSS">Styling</div>
  <div class="attr-item" data-label="JavaScript">Interactivity</div>
</div>

CSS:

.attr-demo {
  display: grid;
  gap: 12px;
}
.attr-item {
  padding: 12px 16px;
  border: 1px solid #ddd;
  background-color: #f5f5f5;
}
.attr-item::before {
  content: attr(data-label) ": ";
  color: #0066cc;
  font-weight: bold;
}
Browser Result
Markup
Styling
Interactivity

The browser displays the attribute value before each box's text:

HTML: Markup
CSS: Styling
JavaScript: Interactivity

This example uses the established string-based form of attr(), which is supported across modern browsers.

Typed Attribute Example

The following example demonstrates modern typed attr() by reading a width and background color from HTML attributes.

HTML:

<div class="attr-typed-demo">
  <div class="attr-typed-box" data-width="120" data-color="#0066cc">120px</div>
  <div class="attr-typed-box" data-width="180" data-color="#008866">180px</div>
  <div class="attr-typed-box" data-width="240" data-color="#9933cc">240px</div>
</div>

CSS:

.attr-typed-demo {
  display: grid;
  gap: 12px;
}
.attr-typed-box {
  box-sizing: border-box;
  width: 120px;
  min-height: 44px;
  padding: 12px;
  background-color: #0066cc;
  color: #fff;
  text-align: center;
}
@supports (width: attr(data-width px)) {
  .attr-typed-box {
    width: attr(data-width px, 120px);
    background-color: attr(data-color type(<color>), #0066cc);
  }
}
Browser Result
120px
180px
240px

In browsers supporting typed attr(), the boxes use widths of 120px, 180px, and 240px, with colors supplied by their attributes.

Browsers without typed attribute support display the boxes using the standard fallback styles.

The @supports condition checks syntax support. It does not guarantee that every supported data type behaves identically across browsers.

attr() vs. var()

The attr() and var() functions both substitute values into CSS declarations, but they retrieve values from different sources.

Feature attr() var()
Value source HTML or XML attributes CSS custom properties
Example attr(data-label) var(--label)
Common use Generated text and attribute-driven styling Reusable CSS values and themes
Inheritance Reads the selected element's own attribute Custom properties normally inherit unless configured otherwise
Browser compatibility String use is established; typed use is newer Broad support for custom property substitution

For example, attr() reads a value from HTML:

<div data-label="Example">Content</div>
div::before {
  content: attr(data-label);
}

The var() function reads a CSS custom property:

div {
  --box-color: #0066cc;
  background-color: var(--box-color);
}

Use attr() when a value belongs to an HTML attribute. Use var() when a value is defined as a CSS custom property.

Limitations and Security

The attr() function has several important limitations.

  • It reads attributes from the selected element, not arbitrary elements elsewhere in the document.
  • When used in a pseudo-element, it reads attributes from the originating element.
  • String-based usage with content has broader browser support than typed usage in other properties.
  • Typed values must match the requested CSS data type.
  • A missing or invalid typed attribute can make a declaration invalid at computed-value time if no suitable fallback is supplied.
  • It cannot be used to construct URLs for properties such as background-image.
  • Generated content should not be the only source of important information.

For security reasons, attribute-derived values cannot be used to construct CSS URLs.

For example, this does not work:

.icon {
  background-image: url(attr(data-image));
}

The restriction prevents potentially sensitive attribute values from being inserted into external resource URLs.

Similarly, the following is not supported:

.icon {
  background-image: attr(data-image type(<url>));
}

The <url> type is prohibited in typed attr() expressions.

Use ordinary CSS URLs or another appropriate method when an image source needs to be selected.

Also, attr() does not automatically convert arbitrary strings into valid CSS values. The retrieved value must be compatible with the property receiving it.

Browser Support

The traditional attr() function used with the content property is Baseline Widely available and has been supported across major browsers for many years.

The modern typed attr() syntax, which allows attribute values to be used in properties such as width, color, and transform, has more limited browser support.

For maximum compatibility, use the traditional syntax for generated text:

.label::after {
  content: attr(data-label);
}

When using typed attributes, provide conventional CSS styles as a fallback:

.box {
  width: 150px;
}
@supports (width: attr(data-width px)) {
  .box {
    width: attr(data-width px, 150px);
  }
}

Browsers that do not recognize the typed syntax retain the standard width declaration.

Support for individual typed values and related features may differ between browser versions.

For current compatibility information, visit Can I Use? and search for CSS attr() or advanced attr() support.

Specifications

The attr() function is defined in the CSS Values and Units Module Level 5 .

The specification defines how attribute values are retrieved, parsed, substituted, and handled when missing or invalid.

It also defines restrictions on using attribute-derived values in CSS URLs.

Related features include the content property, ::before and ::after pseudo-elements, CSS custom properties, and the var() function.