Using just semantic CSS Pseudo-Classes you can help define important states for form elements that ensure the user provides the correct data without frustration.
There are other state pseudo-classes that are pretty helpful, but they're primarily to deal with forms. Let's go ahead and open up the form.html and the form.css files. I'm going to go ahead and back out in the browser and go to the form state pseudo-classes.
Here, I've got a basic form with a couple inputs. You can see in the HTML, I've actually separated them out into field sets. You don't have to do that. I just did that so that I can demonstrate a couple of interesting things with forms.
Here, in the CSS, I went ahead and took off some of the default styling on those field sets. They draw a border, they get a little bit padded and have some margin. It's a little bit of an odd layout for what I'm trying to do, so I just went ahead and removed that.
Then here, I set a default styling for anything that's got the text class, which I added onto my text elements. The first pseudo-class is the focus state. You can see here I've took off the outline, because, by default, it shows this blue outline whenever you have one of these fields focused.
I added a shadow instead, so it looks like it's popping out a little bit. This is pretty good. It looks fine except you'll see here this circle. The box shadow is actually forming a box around it, so it doesn't really fit the circle, but it works for demonstration purposes.
The next one, I've got here a disabled state on the field set. That's because, with field sets, I can disable them. Which means that I can apply styles not just to the input itself, but also to the label. So the city is currently disabled, which means I can change the opacity to the whole thing.
If I did it to just the input, I'd end up with some solid text, and it may not be easily distinguishable that this one is not enabled. So I took the opacity down to 0.5 or 50 percent. I wrote here in the comment, "The opposite of the disabled state is the enabled state."
So if you want to specifically write some styles for just things that are enabled, you could do it that way. Here, I've got a input colon checked. This means anything that is in a check state, and this applies to checkboxes and radio buttons.
I've got an additional modifier here to change the styling of the label after it. So if you click on these different ones, you'll see that the label actually turns italic. Then I've got a pseudo-class for invalid. The opposite of that, I can also say, is input valid.
But, you see the border color turns red. If I look up here, I've got an input for a zip, like a zip code. I can put in a custom regular expression that'll match a zip code. Right now it is required and the pattern is not being met, so it's going to be red, because that's the way I styled it for invalid.
But if I type in a valid zip code, it is no longer invalid, so that invalid pseudo-class no longer applies to it. These patterns can either be custom patterns, like this regular expression for the zip code, or in the case of this input, I've actually got email. Because I say it's of type email, I don't need to type out a pattern for it. There's already a pattern for the email field.
As I start to type it in, it will recognize that it's not valid. So whenever it doesn't match the pattern, that border turns red. Then when it does match it, it goes back to the default which is gray. You can see this last pseudo-class is actually for elements that are required.
If I look in the input, you can see that I have the required attribute so a zip code is required. That is also why, by default, if I don't have anything in there, it is invalid. But the email, if I don't have anything in there because it's optional, it's still valid because I don't have to type in anything.