Hacker Newsnew | past | comments | ask | show | jobs | submitlogin
Styling form states without JavaScript (webkit.org)
87 points by tosh on Nov 17, 2022 | hide | past | favorite | 35 comments


These are all handy, but I’ve never met a designer who’s content with the default behavior of input validation and have always been required to smooth out the interactions with JavaScript.

In some cases, I actually agree that the default behavior is rough. For example, type a single character in the email input in the example in the article and notice that the field immediately transitions to the error state even though you’re still typing.

It’s not a friendly experience—at best it’s pedantic—and I’ve never met a designer who hasn’t requested that the error state only be used when the user removes focus from an input. You could perhaps use a sequence of pseudo classes, but in practice, it’s typically much harder to follow that logic in CSS vs JS.


> For example, type a single character in the email input in the example in the article and notice that the field immediately transitions to the error state even though you’re still typing.

The solution for this is :user-invalid. Firefox has had it for a year and a half, WebKit has https://github.com/WebKit/WebKit/pull/5958 hopefully landing soon, Chromium has no activity on https://bugs.chromium.org/p/chromium/issues/detail?id=115606.... For fresh development these days, I would recommend using :user-invalid and polyfilling, rather than implementing your own very similar semantics in your own way in JS.

See https://developer.mozilla.org/en-US/docs/Web/CSS/:user-inval... for a demo.

(:user-invalid as an alternative to :invalid joins the list of presentational nuance pseudoclasses that allow you to match what browsers themselves do in their default styling, joining :focus-visible which is an alternative to :focus.)


> I would recommend using :user-invalid and polyfilling

Is there a polyfill you can recommend?


No idea, sorry. I haven’t had cause to want this sort of thing in the last couple of years (or I’d have done it myself if no one else has). It’s not unlikely, in fact, that no one has actually written a suitable polyfill yet, of similar nature to :focus-visible polyfills. But if one wants to implement such a thing, :focus-visible polyfills are a decent starting place to look at and think about.


Can't you combine with :not(:focus) ? That should trigger the CSS only after the field is left.

Or use a transition to show the error a few seconds later than the input happened.


You want the error to still be there if the user focuses on the input again, though. And you also want to error to appear immediately when the user blurs the input - you don't want them to be three fields ahead when you finally tell them about their first error!

In general I agree that we should be looking for CSS/built-in solutions to these sorts of input state problems, but this is one of those cases where you either use JavaScript, or accept the accessibility hit. (Bearing in mind that JavaScript itself can be an accessibility hit in its own right.)


I don't think it's much harder to follow that logic. You just replace `input:invalid` with `input:invalid:not(:focus)`.


Not quite, right? When the user blurs out of the field and it's marked as invalid, when the focus goes back to the input, we'd probably want to keep the field marked as invalid.


Personally I would be aiming for consistency / simplicity. There's so many good ways to solve this at a design level without introducing a multi-phased state system. But that's me.


But that's the thing: many JavaScript form validation libraries already do implement the state machine and present a consistent and simple API for the developer to use. It would be nice if this built-in stuff could fully replace JavaScript form validation libraries, but it's just not there yet.


Totally get that. And I think if those are the conventions you're designing form interactivity with then, absolutely, you probably need a javascript library to validate and step your user through form submission.

My point is that this should be the exception, not the rule. I certainly don't think this level of abstracted complexity is necessary, or even better, in most scenarios. I'm talking at a design or implementation level.


Many people are significantly less technically savvy than you might expect. Every form really needs all of these conventions if you want to make your software as widely usable as possible (which is all consumer software at least, or should be).

So in fact I think it is the rule, not the exception.

Unfortunately you have to do a dumb JS dance to get these reasonable affordances. Hard to blame people for not reimplementing all this shit even if it’s critically useful for many folks.


I'll have to respectfully disagree. Not with your first sentence, that's true enough, but everything else.


Looking forward to broad support for :has() ... infinite number of good potential use cases. The grid layout example is probably the most interesting here, though the syntax will certainly also make forms more flexible with less structural complexity.

That said, personally I put a bit of effort into achieving (dynamic) ends without javascript (despite being a js dev) and find form styling, with it's intrinsic state, one of the less-problematic things to style. In fact, I tend to use input elements in a bunch of unconventional places as state machines in place of js. Hamburger menus, overlays (prior to the <dialog> tag), dark mode toggle, etc. have all been prime candidates for "previous-sibling" (or "parent-previous-sibling") input treatment.

In the form examples provided, the <labels> being styled could easily be included "after" the <input> in code (regardless of where they appear visually) allowing them to be targeted based on state/focus of the input. Similarly the :focus-within vs :has(:focus-visible) consideration is interesting, but in reality not really a massive concern and certainly not a blocker. The fact that people have relied on JS for this stuff traditionally I think is the bigger concern for me. Here's to progress!

P.S. I mainly like the idea of low/no js from an academic perspective. But I do think it comes in handy in surprising situations, both when a random no-js purist stumbles across your site and is able to do something, but also when something breaks (naughty browser plugin, broken script, dodgy refactor) and people are still able to open your nav / close your popover / fill in your complaints form.


Yeah :has is the really interesting part in the demos here. Such a game changer for some things.


Am I the only one who giggles inside every time I see one of those 100s of named CSS colors being used? They have such cute names some of them. Anitquewhite, dodgerblue (boo!!), darkolivegreen, bloodstainred.

https://developer.mozilla.org/en-US/docs/Web/CSS/named-color


I enjoy using the CSS named colors for web design because it provides a limited set of options to choose from. It's easier to decide on the best choice from a limited set of options than it is to find the global maximum in the entire RGB space.

I'll also forever have a fondness for "cornflower blue" because it was the default fill color when you created a new C# Xbox 360 game using Microsoft XNA.


Don't forget about downright heartwarming entries like rebeccapurple: https://meyerweb.com/eric/thoughts/2014/06/19/rebeccapurple/


Perhaps there's an earlier origin, but that looks like the old X11 color list that was in /usr/lib/X11/rgb.txt

This file: https://github.com/freedesktop/xorg-rgb/blob/master/rgb.txt


That is indeed the list of colors from which the Level 3 CSS colors were adapted.


Great article that explains what all the fuss is about regarding the :has() “parent selector” that’s been 20 years in the making.


More like ‘relative selector’.


Why is Firefox lagging behind on :has support here?


They're still working out some kinks. Also, the other browsers may have launched a little too early while some weird spec corner cases were getting worked out.


Also, the other browsers may have launched a little too early while some weird spec corner cases were getting worked out.

All of that got worked out a while ago; you can read the CSSWG notes and check the bug trackers for WebKit, Firefox and Chrome.

It’s not that WebKit, which shipped :has() back in March, was too early but Mozilla had some difficulties with their implementation [1].

It took Google (and Igalia) until the end of August to ship their implementation in Chrome 105 [2].

Even though web developers have been asking for this for nearly 20 years, it only recently got figured out.

Based on Apple’s article [3] and Igalia's prototyping document [4], this is easily the most complex CSS selector that’s ever been implemented.

I would cut Mozilla some slack.

[1]: https://bugzilla.mozilla.org/show_bug.cgi?id=418039

[2]: https://developer.chrome.com/blog/new-in-chrome-105/

[3]: https://webkit.org/blog/13096/css-has-pseudo-class/

[4]: https://github.com/Igalia/explainers/blob/main/css/has/proto...


Perhaps because it is hidden behind a flag and could benefit from more reports first to get it stable?

https://caniuse.com/css-has :

> Supported in Firefox behind the `layout.css.has-selector.enabled` flag

see https://stackoverflow.com/q/73936048 etc.

NOTE: it looks like it's not ordinarily working even when switched on.


> NOTE: it looks like it's not ordinarily working even when switched on.

I find the :has() selector very useful in certain cases, but firefox doesn't support it yet and like you said, it doesn't work very well even when enabled. What I have done for Firefox is use the `@supports selector` query [1] and fallback to some equally functional but not as nicely styled UI with the hope that when Firefox supports it, it will automatically provide the nicer UI.

[1] https://developer.mozilla.org/en-US/docs/Web/CSS/@supports#f...


Yes, and thinking, that @supports selector is also suggested in one of the examples in the article:

  /* Warning message 
     about support for :has() */

  @supports selector(:has(img)) {
    body small {
      display: none;
    }
  }
And I sure love it when CSS performs according my preference, but I'd never complain about a UA not implementing or a user prefer her own style over agent or author.


Nice approach! If you ever blog or tweet about CSS I'd be stoked to read it.


This approach is called progressive enhancement [1]; it’s been a thing in web development for over a decade.

[1]: https://alistapart.com/article/understandingprogressiveenhan...


How long will it take before CSS is Turing-complete?


Reminds me of old IE days when you can eval() anything in CSS.


Nice how nothing on this site works without active JavaScript.


The content works perfectly well, it is the embedded code editors that don't run without JS. Pretty reasonable if you ask me.


Unfortunately, the <interactive code environment> tag hasn't been added to the HTML standard.




Consider applying for YC's Winter 2026 batch! Applications are open till Nov 10

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: