In the world of web development, exploring advanced features of style languages like CSS can lead to pleasant discoveries. Among these, the attr() attribute emerges as an elegant and practical option that adds dynamism and customization to our layouts. In this article, we will examine in detail the use of this property, its functionalities and provide practical examples of how it can be employed in real scenarios.
How attr() works
The attr() attribute acts as a placeholder within the content property. For example, consider the following code:
.content::before {
content: attr(data-info);
}This allows us to insert the value of the data-info attribute inside the :before or :after selector. If we have an HTML element like this:
<div class="content" data-info="artCode">our first article</div>
<style>
.content::before {
content: attr(data-info);
}
</style>When we apply the previous CSS style, the text "Quote block:" will be displayed before the content of the div element.
We can also concatenate the attribute attr() with text:
.content::before{
content: \"developed by \" attr(data-info);
}This will combine the text "Element type: " with the value of the data-info attribute, generating a combined output.
Value dynamism
A strength of the attr() attribute is its ability to reflect changes in attribute values in real-time. This means that if the attribute value changes dynamically through scripts, the text generated via attr() updates accordingly, making it therefore sensitive to value changes of our container's attributes.
This makes it particularly useful in complex web applications where data is passed and manipulated through HTML attributes.
Limitations and considerations
However, it is important to note that the attr() attribute has some limitations. For example, it is not possible to use it to directly define property values like background-image or width.
It is exclusively bound to the content property for text generation within :before or :after pseudo-elements.
/* Invalid use cases */
.social-icon {
background-image: url(attr(data-icon-url));
}
img {
width: attr(width);
}
.container {
--main-color: attr(data-main-color);
}Use case: responsive tables
It very often happens to find yourself struggling with table responsiveness, especially when you have tables with multiple columns that make viewing on mobile devices difficult. In this case the attr() property comes to our rescue, let's see how!
Let's consider the following table:
<table>
<tr>
<th>Position</th>
<th>Name</th>
<th>Team</th>
<th>Appearances</th>
<th>Goals Scored</th>
</tr>
<tr>
<td data-cell=\"Position\">1</td>
<td data-cell=\"Name\">Lionel Messi</td>
<td data-cell=\"Team\">Paris Saint-Germain</td>
<td data-cell=\"Appearances\">25</td>
<td data-cell=\"Goals Scored\">30</td>
</tr>
<tr>
<td data-cell=\"Position\">2</td>
<td data-cell=\"Name\">Cristiano Ronaldo</td>
<td data-cell=\"Team\">Manchester United</td>
<td data-cell=\"Appearances\">23</td>
<td data-cell=\"Goals Scored\">25</td>
</tr>
<tr>
<td data-cell=\"Position\">3</td>
<td data-cell=\"Name\">Robert Lewandowski</td>
<td data-cell=\"Team\">Bayern Munich</td>
<td data-cell=\"Appearances\">24</td>
<td data-cell=\"Goals Scored\">28</td>
</tr>
</table>This is a simple table of a football scorers ranking. The particularity is that a data-cell="" attribute is added inside each individual cell where the reference column of the data is reported.
To manage responsiveness and readability even in mobile version, you just need to add some style in a media query; without having to revolutionize our layout:
@media screen and (max-width: 600px) {
th {
display: none;
}
td {
display: block;
width:100%;
}
td::before {
content: attr(data-cell) \': \';
font-weight: bold;
}
}First we hide the header of the table with a display: none.
Then we set a display: block to all td so they occupy all the available space on a single row.
As a last step we insert in a :before the value of the data we are displaying inside each individual cell (having previously hidden the header).
With 3 simple CSS modifications we have therefore made the table readable from all devices with the use of the attr() property!
Here is the complete example:
<!DOCTYPE html>
<html lang=\"en\">
<head>
<meta charset=\"UTF-8\">
<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">
<title>Football Players Ranking</title>
<link href=\"https://fonts.googleapis.com/css2?family=Inter:wght@400;700&display=swap\" rel=\"stylesheet\">
<style>
* {
box-sizing: border-box;
}
body {
font-family: 'Inter', sans-serif;
color: #ffffff;
margin: 0;
padding: 0;
}
h2 {
text-align: center;
color: #ffffff;
margin-top: 30px;
}
table {
border-collapse: collapse;
width: 95%;
max-width: 900px;
margin: 20px auto;
background-color: #2e2e2e;
}
th, td {
border: 1px solid #3e3e3e;
text-align: left;
padding: 12px;
}
th {
background-color: #3e3e3e;
color: #ffffff;
font-weight: bold;
}
tr:nth-child(odd) {
background-color: #3e3e3e;
}
tr:hover {
background-color: #4e4e4e;
}
@media screen and (max-width: 600px) {
th{
display: none;
}
td{
display: block;
width:100%;
}
td::before{
content: attr(data-cell) ': ';
font-weight: bold;
}
}
</style>
</head>
<body>
<table>
<tr>
<th>Position</th>
<th>Name</th>
<th>Team</th>
<th>Appearances</th>
<th>Goals Scored</th>
</tr>
<tr>
<td data-cell=\"Position\">1</td>
<td data-cell=\"Name\">Lionel Messi</td>
<td data-cell=\"Team\">Paris Saint-Germain</td>
<td data-cell=\"Appearances\">25</td>
<td data-cell=\"Goals Scored\">30</td>
</tr>
<tr>
<td data-cell=\"Position\">2</td>
<td data-cell=\"Name\">Cristiano Ronaldo</td>
<td data-cell=\"Team\">Manchester United</td>
<td data-cell=\"Appearances\">23</td>
<td data-cell=\"Goals Scored\">25</td>
</tr>
<tr>
<td data-cell=\"Position\">3</td>
<td data-cell=\"Name\">Robert Lewandowski</td>
<td data-cell=\"Team\">Bayern Munich</td>
<td data-cell=\"Appearances\">24</td>
<td data-cell=\"Goals Scored\">28</td>
</tr>
</table>
</body>
</html>Use case: multilingual texts in content:''
Another use case that could be useful in more complex projects: we are inside a multilingual page and we need to insert text inside a content via CSS, just like in this case:
<div className=\"header__switch\">
<button
className=\"header__switch-theme\"
onClick={handleDarkMode}
aria-label=\"Change theme\"
></button>
</div>.header__switch-theme::before {
content: attr(aria-label);
font-size: 0.7rem;
line-height: 1;
position: absolute;
top: -15px;
left: 0;
right: 0;
text-align: center;
}In the current context, we have a button that allows you to set the page theme (light/dark).
To clarify the function of this button, it is necessary to insert a title above it. I decided to do it via CSS, using absolute positioning and the aria-label attribute as part of the content.
This approach simplifies the management of page language translations, since the text to be translated is not separated in a distinct stylesheet, but is integrated directly into the HTML structure of the page, while being used within the CSS content property.
Will you try using it in any of your projects?
