Description
The problem
CSS doesn't yet have a way to retrieve the value
property for an input/select/textarea/etc or to use/reuse those values in any useful way since we can't pass values up the cascade and form field elements generally (ever?) don't have descendant elements.
It's possible to set the value
attribute on a form field but not to use the updated value of that field once it's been changed via user input. often the case that when the value of that field changes, the attribute itself remains the same, standing more so as a reference to the field's initial value than an indicator of its "live" value.
In other words, without using JS, access to a field's value is impossible.
Description of the proposal
I propose adding a value()
function which returns the active or "live" value of any value-holding element, namely form fields. The syntax would be fairly similar to the existing & future attr()
syntax.
Background
As mentioned in "The problem" section above, since we can't pass values up the cascade and form field elements don't have descendant elements, in order for this function to be useful, it needs a way to expose these values in such a way that they can be re-used throughout the cascade, even in other areas not neighboring the fields themselves.
For this reason, this issue should be treated as a stacked issue atop #7866
Issue #7866 provides a way for any CSS values to be stored at a global scope and be re-used as needed.
Syntax
value([syntax?])
Usage
The single syntax argument would be optional, equipping a developer to set the intended syntax for the result, where leaving it empty would use the field's intrinsic syntax.
For example, using…
value(number)
orvalue()
oninput[type="number"]
orinput[type="range"]
would result in a number value, sincenumber
is the intrinsic syntax for those input typesvalue(string)
oninput[type="number"]
orinput[type="range"]
would result in the field's value but parsed as a string which could then be used anywhere a string could be used (e.g. thecontent
property of a pseudo-element)value(string)
orvalue()
on any of the following types would result in a string/text value, since that is the intrinsic and intended syntax for those input typesinput[type="text"]
input[type="email"]
input[type="tel"]
input[type="search"]
input[type="url"]
input[type="radio"]:checked
input[type="checkbox"]:checked
select > option:selected
textarea
value(number)
on any of these ☝🏼 however would result in the field's value parsed as a number. In any case where that numeric parsing fails, that declaration is ignored and skipped. When the pasring succeeds, the number returned can be used anywhere a number would normally be accepted.value(color)
orvalue()
oninput[type="color"]
would result in a color value, sincecolor
is the intrinsic syntax for that input types
Considerations
- When first tossing around this idea, I thought it might be more complicated to account for
select
,checkbox
, andradio
button values, but after giving it more thought, I think those should work as desired naturally. The key difference—as mentioned in the "Usage" examples above—is that a developer should store the value for the active (:checked
or:selected
) field itself, not just allselect
/checkbox
/radio
fields, like this:[type="checkbox"]:checked { --value: value(); } [type="radio"]:checked { --value: value(); } select > option:selected { --value: value(); }
- While attribute modifications can be tracked using
MutationObserver.observe
,value
changes would likely be more appropriately tracked using theinput
andchange
events. - There are many input types which don't (yet) have a CSS representation or equivalent syntax. It would be worth discussing that a bit deeper to see how/if those should be handled and evaluated.
A more complete example
How might this look in practice?
* different values for input's value attribute vs. property ┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┬┄┄┄┄┄┄┄┄┄┐
┆ ┆
html ━┓ ┆ ┆
┣━ head ┆ ┆
┗━ body ━┓ ┆ ┆
┣━ div ━┓ ┆ ┆
┃ ┣━ span ┆ ┆
┃ ┗━ div#progress ┆ ┆
┗━ form ━┓ ┆ ┆
┗━ input[type="range"][min=0][max=100][step=1][value=10]{value: 30}
@property --range-min {
syntax: "<number>";
inherits: true;
initial-value: 0;
global: true;
}
@property --range-max {
syntax: "<number>";
inherits: true;
initial-value: 0;
global: true;
}
@property --range-val {
syntax: "<number>";
inherits: true;
initial-value: 0;
global: true;
}
span::before {
content: "The current value, " var(--range-val) ", is between " var(--range-min) " and " var(--range-max);
}
div#progress {
--offset-max: var(--range-max) - var(--range-min);
--offset-val: var(--range-val) - var(--range-min);
--stop: calc((var(--offset-val) / var(--offset-max)) * 100%);
background: linear-gradient(to right, black var(--stop), white var(--stop));
}
form > input[type="range"]#range {
--range-min: attr(min number);
--range-max: attr(max number);
--range-val: value();
}
This example demonstrates how you might use value()
paired with attr()
and global variables to take a range input's value, min, and max values and create a label and makeshift progress bar using them, all done using only CSS.
This is just one example, and many more could be made, and I have no doubt I'll create several more as I continue to iterate on this concept and work toward a viable implementation.