Closed brandonmcconnell closed 7 months ago
I like to use display: grid
for <table>
markup. Being able to write generic css that handles most of the built-in table attributes would be nice, e.g. rowspan
colspan
...
Here's another use case: images with a spaced out gradient border
that looks extracted from the image.
The markup for an image would be just
<figure>
<img src='meow.jpg' alt='fluffy black cat'/>
</figure>
Then in the CSS, this would do the trick for the image with the spaced out "gradient" (actually very blurred) border:
img {
border: solid 1em;
border-image: filter(src(attr(src)), blur(99px)) 10%;
padding: 1em; /* space between image and border */
width: 12em;
aspect-ratio: 1;
object-fit: cover
}
No browser currently supports this and it's not just because of attr()
. src()
doesn't seem to be in any browser at the moment, while filter()
is only supported in Safari (has been supported there for over 8 years at this point, but no other browser followed).
The current cross-browser solution feels needlessly complicated.
First, when I generate the HTML, add a --url
custom property that uses the same image url from the src
attribute.:
- let url = 'meow.jpg';
figure
img(src=url style=`--url: url(${url})` alt='fluffy black cat')
Then in the CSS, I need all of this:
figure {
display: grid;
width: 16em;
img, &::after {
grid-area: 1/ 1;
border: solid $b hsla(0, 0%, 0%, .001)
}
img {
box-sizing: border-box;
border-image: var(--url) 10%;
padding: $b;
width:100%;
aspect-ratio: 1;
object-fit: cover
}
&::after {
backdrop-filter: blur(99px);
mask:
linear-gradient(red 0 0) padding-box exclude,
linear-gradient(red 0 0);
content: ''
}
}
And this doesn't even include the -webkit-
-prefixed version for mask
, which is still needed at this point (live demo).
With
attr()
, we can achieve this more concisely, using attribute values directly:<!-- here, the styles will inherit from the attributes dynamically, so no need to set the explicit values twice --> <progress min="0" max="100" value="50"></progress> <style> progress { /* I'm using `attr()` directly in this calculation, but these values could also be abstracted to CSS custom properties for ease of reuse */ --percentage: calc((attr(value number) - attr(min number)) / (attr(max number) - attr(min number)) * 100%); background: linear-gradient(to right, green var(--percentage), white var(--percentage)); } </style>
I'm intrigued! This looks very good in theory and I'd love to be able to use something like this for inputs! For example, for input[type=range]
, this would eliminate the need for JS as we wouldn't need to rely on a JS-updated custom property for the value, like I'm doing in this demo.
But one thing that confuses me is that I don't see the value
attribute for such an input
changing in DevTools as I drag the thumb. The actual value (that I update the --val
custom property with) does change, but not the number value in the value
attribute. So would attr(value)
actually work, or would it just be always the same value set initially in the value
attribute?
If attr(value)
could work in calc()
and could get updated on dragging the thumb, that would be wonderful. Because I currently use the --val
attribute to compute quite a few things, for example the position of a tooltip value display and the number value shown in that tooltip. And since --val
can only be updated via JS, no JS means no tooltip value display.
@thebabydino Solid feedback all around. Examples like that greatly help to express this feature like this. Thank you!!
Re the value
attribute updating, I've run into that as well a number if times and have a couple other proposals to address that—
[css-values-5] value()
function
This proposal introduces a CSS function for incorporating the live value of an input, exactly as you've put it here
[css-properties-values-api] add support for scope-global variables
This proposal introduces a completely new way to work with CSS custom properties — globally.
A feature like this would be essential for exposing attributes as well as the value
property of descendant elements that cannot otherwise expose them upwards. I'm considering this feature a dependency of my other value()
proposal.
I would certainly appreciate any support you'd show to either of those proposals as well. Thank you 🙂
In addition I would add, inline styles aren't very good for security and CSP perspective.
@thebabydino
This looks very good in theory and I'd love to be able to use something like this for inputs! For example, for input[type=range]
This was my first thought, too. Range is a pain to style, currently: https://codepen.io/webstrand/pen/bGLXdPR
So would
attr(value)
actually work, or would it just be always the same value set initially in thevalue
attribute?
Traditionally, <input>
elements don't update their attribute when their value property changes. Changing this behavior would be a breaking change as there's quite a lot of things that reuse inputEl.getAttribute("value")
to restore fields to their initial state. For instance <button type="reset">
looks at the attribute value to determine what to reset to.
I'm not sure I'd be a fan of adding special cases for attr(value)
though.
@webstrand I agree; I don't think we should introduce any special cases using attr()
to retrieve dynamic values like the live value
prop of an input.
If you're interested, take a peek at the other two proposals I mentioned, namely this one: [css-values-5] value()
function
Re the `value` attribute updating, I've run into that as well a number if times and have a couple other proposals to address that— 1. [[css-values-5] `value()` function](https://github.com/w3c/csswg-drafts/issues/7869) This proposal introduces a CSS function for incorporating the live value of an input, exactly as you've put it here 2. [[css-properties-values-api] add support for scope-global variables](https://github.com/w3c/csswg-drafts/issues/7866) This proposal introduces a completely new way to work with CSS custom properties — globally.A feature like this would be essential for exposing attributes as well as the `value` property of descendant elements that cannot otherwise expose them upwards. I'm considering this feature a dependency of my other `value()` proposal.(expand/collapse explainer)
Hypothetically, you could use `@property` to set up a custom property with a new `global` option on its `@property` config set to `true`. When the custom property is set up this way, it only ever has one value — that with the highest specificity. This way, we can set global values using states such as a checkbox or an input value and access it anywhere else in that scope. For example, you could create a dark mode toggle checkbox somewhere on the page, and without needing to resort to some top-level `:has()` (not inherently bad, but an alternative), you could have that checkbox's `checked` state update the global value naturally, like this: ```css @property --color-bg { syntax: ""; inherits: true; initial-value: white; /* ⬜️ */ global: true; } @property --color-text { syntax: " "; inherits: true; initial-value: black; /* ⬛️ */ global: true; } #darkmode:checked { --color-bg: black; /* ⬛️ */ --color-text: white; /* ⬜️ */ } #page { background-color: var(--color-bg); /* ⬛️ */ color: var(--color-text); /* ⬜️ */ } ```
Another concrete example, an icon component that should be sized in rem
s to scale with the text, and also use mask-image
to allow it to inherit the text color, so dual potential usage of attr
in one class. Currently we do something like this
.icon {
width: var(--_height);
height: var(--_height);
&[height='64'] {
--_height: 4rem;
}
&[height='48'] {
--_height: 3rem;
}
&[height='40'] {
--_height: 2.5rem;
}
&[height='32'] {
--_height: 2rem;
}
&[height='24'] {
--_height: 1.5rem;
}
&[height='16'] {
--_height: 1rem;
}
background-color: currentColor;
mask-image: var(--icon-url);
}
@jacobrask Great example. I've done something similar for custom progress bars:
<div class="progress-wrapper">
<progress min=0 max=100 value=75></progress>
</div>
progress[min][max][value] {
.progress-wrapper:has(&) {
--percentage: calc((var(--value) - var(--min)) / (var(--max) - var(--min)) * 100%);
height: 100vh;
width: 100vw;
background: linear-gradient(to right, red var(--percentage), white var(--percentage));
}
.progress-wrapper:has(&) & {
display: none;
}
/* min */
.progress-wrapper:has(&[min="0"]) { --min: 0; } /* ... */
.progress-wrapper:has(&[min="100"]) { --min: 100; }
/* max */
.progress-wrapper:has(&[max="0"]) { --max: 0; } /* ... */
.progress-wrapper:has(&[max="100"]) { --max: 100; }
/* value */
.progress-wrapper:has(&[value="0"]) { --value: 0; } /* ... */
.progress-wrapper:has(&[value="100"]) { --value: 100; }
}
Thank you for proposing attr()
support extended capabilities for inclusion in Interop 2024.
We wanted to let you know that this proposal was not selected to be part of Interop this year.
As of the deadline, the specifications for attr()
support extended capabilities were not yet complete enough to allow interoperable implementations. To make progress on this area in the future, it will first be necessary to ensure that the feature has a clear specification in a standards track document.
For an overview of our process, see proposal selection. Thank you again for contributing to Interop 2024!
Posted on behalf of the Interop team.
@bkardell What features from the official specification are not complete enough to move forward with this feature? The spec is complete and has been for years.
This feature has been suggested for the past few consecutive years via interop and otherwise.
More notably, this issue garnered more attention and praise and engagement than any other interop issue proposed, which I believe should be weighed heavily when considering proposed features for interop.
One of the primary functions of proposing issues for interop is to give the community the ability to vote on which issues they desire to see implemented in the web platform most. The results clearly show that this is a top priority if not THE top priority.
@brandonmcconnell you're right that there's a lot of developer demand for this, that is very clear. The issue with the spec isn't really that it's "not yet complete enough to allow interoperable implementations", but that there are security issues that at least the Chrome team believes are serious enough to need solving before shipping. That is the topic of https://github.com/w3c/csswg-drafts/issues/5092, and we now have a sketch of a solution that might work. I am going to follow up on that work so that the security issues can be resolved. (I did take a look while we were evaluating proposals too, but there just wasn't enough time to fix it there and then.)
@foolip (cc @tabatkins) Given the recent progress and resolutions in https://github.com/w3c/csswg-drafts/issues/5092, and the exceeding demand on this interop proposal the past two consecutive years (#86 & #521), could we reconsider including it in this year's interop effort, as the blocker is now removed?
We can't, as part of Interop 2024. Interop choices and tests are carefully chosen so that we have measurable goals that don't significantly change during the year... But don't be too disheartened by that answer: It doesn't mean teams can't all prioritize it independently -- in my opinion, another benefit of the Interop project is just that it gets us collectively thinking/talking about priorities... So, it's totally possible if the situation regarding specs is resolved, that it might get interoperable support in the same year without actually being a part of the Interop project.
@bkardell Thanks, that's understandable 🙂
Description
The attr() CSS function is used to retrieve the value of an attribute of the selected element and use it in the stylesheet. It can also be used on pseudo-elements, in which case the value of the attribute on the pseudo-element's originating element is returned. (MDN)
Specification
CSS Values and Units Module Level 5
# attr-notation
Open Issues
Tests
The below list of tests was generated using
wpt-find
(wpt-find -mlc 'attr('
)Current Implementations
Standards Positions
Browser bug reports
Developer discussions
Some posts in support of
attr()
:X/Twitter
StackOverflow
If you search X/Twitter for "CSS attr" you will see plenty of posts from people excited about and/or curious about the future features of CSS
attr()
. You'd also see many posts echoing one opinion calling theattr()
useless, especially in comparison to CSS custom properties.I'd like to disagree with that sentiment, but… he's right. As the
attr()
function stands today,attr()
is really only useful when trying to pull plain text from attributes to display using a pseudo-element.It's mostly useless, but it doesn't have to be.
With these newer features,
attr()
would be exceedingly useful, not taking the place of CSS custom properties by any means but rather bridging the gap between markup values and CSS values. This would stand as a very productive separation of concerns.Polls & Surveys
No response
Existing Usage
No response
Workarounds
The primary workaround for this is to use CSS custom properties, but this has several drawbacks and only lives in the markup in the form of inline styles. One of the greatest advantages of
attr()
is the ability to utilize the values set on the markup.To achieve this with CSS custom properties, one would need to apply those values twice, once to the attributes for actual markup functionality and again as a CSS custom property for CSS usage. Example:
With
attr()
, we can achieve this more concisely, using attribute values directly:Accessibility Impact
Tightly coupled code can lead to inconsistencies due to human/developer-error when updates are made. The DRY coding principle would reasonably apply here.
Privacy Impact
This issue focuses on security concerns using
attr()
, but there are already good ideas re how to circumvent those issues, the primary of which would be whitelisting: https://github.com/w3c/csswg-drafts/issues/5092Other
No response