[1] https://stackoverflow.com/questions/5328883/how-do-i-style-t...
Annoyingly, Chrome used to allow styling of validation-messages using vendor-prefixed pseudoelement selectors, but they removed that functionality and never brought it back; I'll chuck this on the same pile as other arbitrary annoyances like "can we have a native HTML combo-box please" and "why is <select multiple> still a horribly unusable ctrl+click box instead of a checkbox-list?".
The problem is that it's quite tricky to correctly subscribe to the changes of this validity state. There are indeed some validity events being dispatched, but unfortunately not always. Updating form state programmatically (such as calling "form.reset()" or setting input value via "input.value = '...'") doesn't trigger these events.
I think this is a separate good topic for investigations and for suggestions to the web platform
The majority of the work in form validation is not in the validation of the data, but in the UX and interaction, display and update of state. There's no generic way to handle it, as it's very dependent on the app itself.
Keeping the browser smaller and cleaner, with less logic seems to be a better idea.
Said no designer ever.
At work our design team came up with buttons that are 10x10 pixels on my screen. They are used to change pages (like on mobile, but this is a desktop program), the scroll events are ignored by design, so you either click the tiny buttons (which are slightly darker gray than the dark background) or you simulate a finger swipe via drag and drop with the mouse. Yes they designed a touch GUI for a desktop application.
And this also assumes that the browser validations are in any way usable to begin with. I would argue that they are not, and would require some sane styling to become usable in the first place.
I have never worked for any company or organisation that believed this. Most clients will send you their branding guidelines before sending their feature requests. If they get to choose between adding a new feature, improving usability or making sure everything follows the branding, they will choose the branding every time.
This isn't an issue if everyone's an accessibly expert, otherwise we're sacrificing usability.
People like to complain about JavaScript on websites, but they often don't know in what a bleak world they would live.
"Using specific type attribute values, such as "email", "number", or "url""
These can significantly improve user experience on mobile by triggering the optimal keyboard.
A spinner control, that is.
Spinners always puzzled me, to be honest. There is obviously a need for a compact numeric input control that both displays the exact value and allows rough changes using the mouse. Witness knobs in DAWs—which don’t actually work the way you’d expect from their appearance, you’re supposed to grab and then drag in a linear fashion, possibly ending outside the knob’s screen bounds. Or consider the weird input thing Darktable uses—which at least doesn’t mislead you with false skeuomorphism, but takes a bit to figure out. Then there are the inputs used for color grading in DaVinci Resolve. And so on. And all of them are nonobvious. Spinners are obvious, but they are also needlessly fiddly to use with the mouse, and neither do they provide the at-a-glance readability of knobs et al.
I feel like humanity just hasn’t solved compact numeric inputs yet.
I rarely want those arrow buttons for numbers
It should be split into:
type=integer so the keyboard does not allow anything besides 0-9
type=decimal so the keyboard also allows decimal dot/comma and a fixed number of decimals!
type=float which allows for scientific notation, e.g. 1.2e42
> Browsers automatically provide validation to ensure that only text that matches the standard format for Internet email addresses is entered into the input box. Browsers use an algorithm equivalent to the following regular expression:
> /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/;
Which doesn't even allow all valid email addresses :|
No support for quoted local parts. No support for punycode domains. Disappointing.
Use <input type="text" inputmode="email"> instead to just get the email-optimized keyboard on phones without the misguided validation.
In most cases, GDS now use just 3 text fields for day, month and year, but recommend pickers for dates close to today's date e.g. for booking an appointment, because a picker is easier if you don't know the exact date without reference to a calendar.
Sadly they don't currently have a recommended picker, but there's a useful discussion of what has been tried here:
https://github.com/alphagov/govuk-design-system/discussions/...
I'm trying to picture a room full of <s>people</s> developers agreeing letters are numbers too!
Lets give the little people a slider but lets call it a range!
I really feel like they are trolling.
You start with a neat database table then you engage in an endless struggle trying to allow the user to edit a single row of it. It really feels like you are not suppose to do it. As if it was intended to be as annoying as possible.
E.g. any leading zeros get dropped out of phone number starting with a 0, very common.
And that means it's barely doable with normal inputs, the special ones support even less events.
There's your problem right there. Can you expand on the reasons? It seems like very bad practice for a website to provide it's own "keyboard" instead of using the system keyboard.
EDIT: which I agree with.
Native HTML validation has many flaws and visual customization is not my biggest concern to be honest (but it's the nail in the coffin).
E.g.:
- It's impossible to show multiple errors at once per field unless you concat strings ("You need a number. You need a symbol. Must be >10 chars.")
This is both bad UX and bad for accessibility (you cannot navigate concatenated strings on the accessibility tree). And this isn't even implementation dependent, it's the spec! You need multiple errors at once because playing whack-a-validation-error is not fun for users.
- It's browser-dependent which is bad because you can't control it and because the implementation is generally terrible (e.g. in Chrome it shows a popup on focus, which is not very accessible by itself because (1) it's a popup (2) that shows modally (3) and can't show all form errors at once).
Not using popups for important information is accessibility 101, but browsers cannot afford to do anything else without interfering with the actual document.
- You still need custom validation for form-wide errors (like errors that don't belong to a particular field, "Fields A and B are incompatible") so you might as well have a consistent validation story.
- It requires you to have hidden inputs to be consistent (-ish) if you have some custom input that doesn't fit any of the native input types -- this breaks accessibility too and fixing it is as hard as having your own accessibility-friendly validation story.
- The custom validity API is imperative and cumbersome to use. Not using custom validity is almost never an option unless you want terrible messages ("This field is invalid")
And many more.
HTML form validation is terrible.
The fact that something provided by the browser can fail accessibility requirements is definitely ironic. We're always taught that the motivation to "use the platform" and "follow semantics and semantic elements" is partly to satisfy the accessibility concerns.
I still think it's worth to leverage the native validation mechanisms.
* You don't have to use the native validity tooltips for the error messages. You can read directly from the input's ValidityState (input.validity) and render the message however you like and show multiple errors if you need to
* The browser can improve and you will benefit from using a standardized approach
The fact that "not using popups" supposedly breaks a11y sounds weird, though. But if you need to respond to an audit then this is the way you can go.
> Errors that do not belong to a particular field
These are indeed interesting! There are techniques to handle those, too. In my project I have a "HiddenValidationInput" component that renders an invisible readonly input that serves the purpose of rendering a custom error that doesn't belong to any other field. It's in fact quite a pleasure to use because you can just conditionally render it.
> The custom validity API is imperative and cumbersome to use
Absolutely agree on this one, and handling this is exactly what my article is about. And I really hope that this part will improve in time, too
Feels like I don't gain much from that. Native validation is very limited and the few cases that it covers are super simple to implement already. Am I missing something?
I'd rather have a `validateSomething` which returns a discriminated union of error causes than using `pattern` and just getting a boolean.
> In my project I have a "HiddenValidationInput" component
Yeah, that's an accessibility issue (and the UX for the common user is terrible too for multiple reasons).
> it's the spec
This one? https://html.spec.whatwg.org/multipage/dom.html#concept-elem...
I don't see anything that defines how validation messages should be presented (not even that they should be present)
We didn't discuss browser-specific issues in detail, but I edited some points in my original message that highlight some of the issues that I suspect make it a no-go for accessibility.
And also why browsers started separting these. It’s a checkbox and radio that should contain a text, not vice versa.
[1] https://a11ysupport.io/tests/html_label_element_implicit
https://developer.mozilla.org/en-US/docs/Web/API/HTMLObjectE...
It doesn't work in Firefox for Android.
The more websites use this, the more pressure Mozilla will feel to fix their browser. This isn't something like WebMIDI or whatever API Chrome or Safari implemented before standardising, it's part of the original HTML5 spec.
https://gs.statcounter.com/browser-market-share/mobile/japan
That's still not a lot, but above 1% is a decent threshold to decide to support a browser.
Recently I was trying to get a refund on Groupon because the company I'd bought a Groupon for was under new management that refused to honor my groupon.
The form had a stipulation "minimum of 15 words". Try as I might, I could not get the form to pass validate until I inspected the HTML.
<input pattern="^(\b\w+\b\s*){15,}$" required>
\w - word characters
\b - word boundaries
\s - white
Literally zero allowance for any sort of punctuation.E.g. the "repeat password" example is actually achievable without "setCustomValidity" by using the "pattern" attribute. For that, you would have to dynamically construct a RegEx out of the value from the first input. I didn't want to make the article too long by comparing the solutions, but the point is, with the "customValidity" you see how much more eloquent and easier to read the validation is. So a nicer API here makes all the difference
The "15 word minimum" constraint would look so much nicer as "value.split(/\s+/).length >= 15".
Frontend validation is only there to be helpful for the user, but if you can style it, or trigger it from the backend on submit, you have to implement your own styling anyway.
We try to use browsers standard features whenever possible. Despite looking into using the built-in validation, it's never been worthwhile. Too many gotchas, and we end up using a library to be able to easily support more complex checks anyway.
Furthermore, using a library opens up, in some cases, the possibility of sharing some of the validation code between front and backend.
In particular this article seems to work around one of the issues with `useLayoutEffect`. Not something that should be done lightly.
https://developer.mozilla.org/en-US/docs/Web/CSS/:user-inval...
If I'm making a wish list I'd also like to point a property at a handler function that accepted a `string` and returned a `string | null` (or at least a `nonempty string | empty string`) rather than using an onchange handler, but it is what it is.
I even remember when people started evangelizing client-side validation in the mid-2000s. Javascript was already a normal tool used in web apps by then, and most web developers would regularly be adding javascript to their apps.
Back then it was a bit of a pita as you had all sorts of gotchas with javascript memory leaks by registering change events on controls. I can't even remember the term now, but you could basically have a circular reference between a closure and a control and so it wouldn't cleanup properly.
Also, modern developers probably can't even begin to imagine how slow javascript was on IE6. A loop of a few 100 iterations could bring it to an unresponsive halt.
https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes...
It does not translate with the application but with browsers settings, it doesn’t style or fit any design. It looks differently on different browsers and it is really hard to explain to stakeholders “this is from browser I don’t have control over it”.
Wait, which is it? Does not match any style or design, or is it matching the browser's?
And if your application isn't consistent with the browser and OS settings, shouldn't you fix your application?
I truly believe a nicer API would motivate developers to use it, and if native validations satisfy the product requirement, their styling does become a lesser concern.
But surely styling is still important, but another great topic to write about is the fact that you actually can opt-in to showing native validity errors in a custom way!
I could see a case, if a user decides to somehow make a single website use a different language than the others. I guess it would be a browser's job to have specific languages for specific websites.
Explaining to people they should go and change their browser settings is major hassle.
You need to revalidate on the server, no matter what.
These validation at input can be super annoying when you're copy-pasting your strings from other sources, and want to edit them in textbox in-place. Especially if you're on your phone.
I've be frustrated by this on numerous websites, a lot.
Same is true for things like disabled ect https://developer.mozilla.org/en-US/docs/Glossary/Boolean/HT...
> The strings "true" and "false" are invalid values. To set the attribute to false, the attribute should be omitted altogether. Though modern browsers treat any string value as true, you should not rely on that behavior.
in other words required=false may still end up making the field required. FYI.
Maybe that is user friendly but for sure I don't like to see the backend bombarded with API calls each time an user types a letter.
Huge, huge, massive “no no”.
Likewise you still have to do sever side validation as any client side code can be modified, or you can just send payloads directly to the server. IMHO client side form validation is dangerous as it gives a false sense of security.
The total amount of traffic in both direction is pretty small, and the logic is simple, especially compared to lots of other things your server is typically doing.
You'd probably have to wait until the user moves on to the next field, if there's more, but that's also a little silly, as you'd force the user to go back to a previous field.
The problem with vanilla form validations are (1) they're so basic that it's table stakes for any library or framework in this space, even ChatGPT can do it well, (2) there's an enormous amount of other validation scenarios they don't cover, and (3) unless your validation is simple and doesn't require a validation library, now your logic is split between two places.
[1]: https://valibot.dev/guides/comparison/
[2]: https://zod.dev/?id=basic-usage
[3]: https://www.react-hook-form.com/get-started/#SchemaValidatio...
[4]: https://tanstack.com/form/latest/docs/framework/react/quick-...
Can you provide examples of those? Genuinely interested as I'm on a quest of creating a list of recipes that show that native form validation can be just as capable as custom validation libraries
The only downside: the user has to wait 300ms.
I used to think it doubled your workload to do both, but if you are using JS-free client-side validation, I think that the server can just return an HTTP error guilt-free for any invalid input that the browser will catch. It’s a pretty good compromise for format validation.
Indeed the most pleasant UX is to set an additional validation logic on the client side and I don’t think this should the go-to solution.
No, you can handle that on the server too, easily, since you can fill the form with the last known state upon page loading.
You've got tech stacks and cloud services that will put you within 20ms of 99% of your users.
Server side everything is still the safest possible bet you can make.
It is so easy for developers to think they have something better after a simple NPM install, until they test it on a slow (i.e. median) phone or watch a blind person try to use their application and then spend weeks trying to improve things. Given how common web forms are it’d be really nice if we had an Interop-style competition focused on making the out of the box experience better for normal people both for the controls integrated in browsers and the myriad of JavaScript widgets.
^(?=(.*[a-zA-Z]){4,})(?=.*[0-9!#$%]).+$
requires that these characters appear consecutively.Isn't that correct?
I have nothing against regex and the "pattern" attribute is the way to go for many cases, but having this is an alternative is also very nice:
const valid = value.length => 4 && isAlphanumeric(value); return ( <Input value={value} customValidity={valid ? 'at least 4 alpha characters' : ''} /> )
It's gone so far that some peole claim React (and probably other frameworks too) is "a language"; they apparently don't even know it's just a JavaScript library.
But dont remenber to also verify serverside. Anything clientside can have been fumbeled with. (Also kinda anoying to have to duplicate this tho)
Oh yes it is...
I once worked on a project where we had a tool that dynamically built client-side forms based on each customer's needs. After rolling it out for a big customer I discovered (after the original engineer responsible had already been fired for other reasons) that all of our dynamic forms had NO server side validation. none, zilch, nada.
We did have client side validation though, a good amount of it and nearly all of it was using native HTML features, and we had the whole form available server-side as well. So given that I was under a crunch, on form submission I loaded the HTML form server-side, manually iterated over all the elements and re-implemented the validation checks that were embedded in the HTML. Crazy times, but heck it was flexible!
> (Also kinda anoying to have to duplicate this tho)
Security and convenience are like space and time, you can't move one without transformation of the other.even re-fetch them from the server on each input change in the client
or just ditch the whole thing and do it in htmx :->
To take an extreme example, you could ensure that validation happens identically on both the back and front end by writing your own framework with that property. You could create a framework with no more security bugs than the next best alternative and while providing great UX. But writing a framework and shaking out the bugs is a huge lift.
So in practice you can't go all the way out on the third axis and it is approximately a dilemma. But if you're on the lookout for exceptions you may find an opportunity to cheat/curve bend (eg as suggested by other commenters, when using JS for the front and backend, you can use data modeling libraries like Zod to get most of the benefit for a fraction of the price of writing a framework).
<input required={true} />I was once discussing a third-party integration with a React developer. The integration required that our app POST a couple of fields to the third-party's site. I found that the developer was struggling with the integration and they were asking me questions about it when I said something to them along the lines of "It's just an HTML form, with a couple of hidden inputs that when submitted make a POST request to this URL" they said to me "Yeah, well HTML is kinda old, it's not really used anymore"...
I'm sure I've said plenty of stupid things when I was green but I hope no one remembers them like I remember this one. It lives rent free in my head.
In my personal experience, react allowed me to rely more on the native web platform APIs, not less, than other frameworks (at the time that I switched to react)
> I'm sure I've said plenty of stupid things when I was green but I hope no one remembers them like I remember this one. It lives rent free in my head.
I'm doing gigging while my product is gaining traction. Last week, I received this verbatim rejection for a PR review at a client, who's oldest developer is 27:
"No, we don't want all the logic in the same place. You must split the logic up"
This is also one that will take up valuable space in my head till the end of time :-(
(PS. Yes, I split the logic so that it was evenly balanced in 3 different programming languages, instead of having the entire logic in C# alone)
I don't think I understand how it would be "worse" for plain JS and HTML though. Would love to hear your thoughts.
Actually, there is one possible concern. When HTML is returned by the server includes inputs with set "custom-validity" attributes and this HTML gets open by a browser with no javascript enabled, this would make the input "stuck" in an invalid state. This is an important edge case to consider but I do believe there is a resolution that would satisfy everyone
The concepts are obviously easily translated to other component frameworks, but they also do apply to pure HTML and vanilla javascript.
The problem I am highlighting in the article is the absence of a declarative nature to the Custom Validity API, so I think it makes sense to use a declarative component approach in code examples
unfortunately you’re sort of back to square one if you want to implement warnings (ie suboptimal inputs but still workable).
Edit: While grasping the ergonomics produces euphoria like solving a complex puzzle it’s also a hint at the pain un-initiated colleagues will feel when tasked with maintaining the code.
The browser is programmable; at this point they should stop getting clever about adding built-in functionality and instead just expose better ways to use the browser as a dumb UI toolkit.
The browser should provide low-level capabilities and let developers build the high-level functionality from it.
This article ultimately supports this by showing us exactly how half-baked this particular browser feature happens to be.
But firstly, I would consider what you're missing when you abandon native validation
* On submit, the first field that needs attention gets focused automatically
* Error messages for semantic attributes are localized automatically by the browser
* The native message tooltip are actually nice and their placement is handled by the browser. They aren't the best fit for any design system, but for many products they are way nicer than custom implementations.
* Possible styling using CSS pseudo-classes such an ":invalid" or ":user-invalid"
In the end, I do think that the developers would benefit from a more explicitly exposed API for this. Just like we got the "new URL()" constructor, but earlier we had to create an anchor node, fill in its "href" attribute and then read the anchor properties to get to the parts of the parsed URL.
That is long solved.
> Error messages for semantic attributes are localized automatically by the browser
Assuming I want to use their error messages. I would prefer to provide my own, more detailed, error messages. Also what good is localized error messages in a non-localized form?
> The native message tooltip are actually nice and their placement is handled by the browser.
I definitely don't like the placement of the tooltip nor do I even like using tooltips for showing error messages. I much prefer permanently keeping the error message displayed under the field until the user edits the field.
I make some pretty large forms in my job with a lot of complex validation rules and non-technical users. We do validation way better than this.
(Consider Triptych [https://github.com/alexpetros/triptych] as the baseline for what browsers should have been providing since 2005 or so.)
Adding more high-level UI functionality is really in conflict with the idea of browser as platform. Other features, like WASM, are pushing the browser the other way.
Call spade a spade. Don’t call it heavily underused.
It's just that for the demos in the article it makes sense to show invalid states as soon as possible, but for a nicer UX in real apps you need to take into account "touched" and "submitted" states for the form and per input
For the demos I wanted the reader to know at a glance when our validation code takes effect and this obviously comes at a conflict with demoing a fully "real-world" behavior
Ideally constraints would also propagate from model definitions, so there could be a single source of truth for "what phone numbers do we accept". Some years back I tried to do this by parsing SQL table definitions, but never got far enough. Django does this, but it lacks pretty much any client-side validation support IIRC. (Or client-side anything, really...)
With Typescript, this is now my preferred way to build full-stack sites.
You still need to handle the "what happens next" part on each side, but at least your validation rules are shared.
No. Don't use the maxlength attribute.
https://adamsilver.io/blog/dont-use-the-maxlength-attribute-...
I also dislike the "character rejection" mechanisms, even though many people love it and products often ask to implement it.
To add to the possible solutions mentioned in your article, I'd add the "pattern" attribute. You can do something like this:
<input pattern=".{0,6}" />
This will allow input of any length, but show a warning then the value is longer than six characters.
Capturing input with frameworks is much simpler than pulling the values out of an event object, so I'd be okay with just using input and button elements.
Yet, that doesn't trigger the validation, so I end up wrapping it with a form element and using a submit button.
I run the validation manually using `reportValidity()` on `element.querySelector(...)` before passing it on to whatever.
I do miss when textboxes had inset and buttons had outset borders.
yeah i can mark a field as numeric... but then when a phone renders the number only input all popular keyboards will also leave out every single clipboard buttons and helpers. url where you can also search if you type a phrase? too bad you just lost all typing correcting and prediction. it's lame and hopeless.