Android style-form labels with pure CSS

Week 234 was posted by Charanjit Chana on 2022-04-18.

I've seen the way Android presents form labels replicated in many ways, mostly involving JavaScript. I've tried my hand at recreating with pure CSS but it means butchering the HTML so that the labels are after the inputs and styled correctly. By placing the label after the inputs, you can use the following CSS selector to target them while using flexbox to re-order the properties on the page. Fine for users, sounds terrible for accessibility.

input + label {
    // default labelĀ CSS 

input:focus + label {
    // label CSS for when the input has focus.

Thanks to :has(), we no longer need to rely on JavaScript or sketchy HTML markup. CSS can finally handle the presentation for this use case like it's supposed to.

If you think of the :has() selector as a parent selector, then you can apply the styles to it's children if those children exist. Click into the fields in the demo below to see the labels become more prominent.

The above demo will only work in Safari at the time of writing.

The CSS isn't really as clean as it was in the example code above, but I'd much rather a bit more CSS than having to deal with JavaScript.

li label {
    box-sizing: border-box; // helps with positioning
    display: block; // label will appear above the input, instead of before it
    height: calc(var(--base) * 2.5); // helps make the animation smoother
    transition: font-size var(--timing),
                padding var(--timing),
                color var(--timing);
li:has(label + input) label { // default, unfocused styles
    color: var(--unfocused);
    font-size: calc(var(--base) * 1.5);
    padding: calc(var(--base) / 2) 0 0;
li:has(label + input:focus) label { // focused styles
    color: var(--focused);
    font-size: calc(var(--base) * 1.75);
    padding: calc(var(--base) / 4) 0 0;

I don't know if it's a Safari bug, which is the only browser you can test in right now, but I couldn't get the labels to use the focused style when not empty. It might be that my selectors were not right but I tried separately and got nothing.

Over all though, :has() cannot come to all browers soon enough. I've been playing with a pure CSS collapsible menu and will share details on it soon.

Tags: css, development, has-css

Tweet WhatsApp Keep Upvote Digg Tumblr Pin Blogger LinkedIn Skype LiveJournal Like