Skip to main content
Photography of Alvaro Montoro being a doofus
Alvaro Montoro

Honorary Tortoise

screenshot of a login form

Building an interactive form: Final stylings

html css webdev tutorial

Our form is almost complete. It was done since step 1, we are just completing it by adding nice things to make it look better. And we are almost there (I promise.)

This last step is going to be completely CSS, and just to add some styles to clean the look and feel of our form. Nothing fancy, but in my opinion, something necessary.


First, we are going to fix the outlines. We will have outlines, just not the default ones that look different from browser to browser and that are kind of ugly, to be honest:

Default outline on Chrome

Instead, we will replace them with a dashed outline slightly separated from the input border, that will match the active color design:

New outline in all browsers

A similar outline will be applied on focus to the submit button and the "forgot password" link. The only element that is special is the checkbox because the outline is applied to the label after it.

input[type="checkbox"]:focus + span::before {
  outline: 1px dashed hsl(var(--fgColorH), calc(var(--fgColorS) * 2), calc(var(--fgColorL) * 1.15));
  outline-offset: 2px;

Input effects

All the inputs have a focus event handler done via JavaScript. Just in case JS is disabled, we are going to add a fallback in CSS using focus-within, which is not fully supported.

As we are using classes for the JS-handled focus event, we just need to add the selectors with :focus-within to the list and we will be done. For example:

label:focus-within span,
label.state-focus span {
  color: hsl(var(--fgColorH), calc(var(--fgColorS) * 2), calc(var(--fgColorL) * 1.15));

label input:focus,
label:focus-within input,
label:focus-within span.checkbox-label::before,
label.state-focus input,
label.state-focus span.checkbox-label::before {
  border-color: hsl(var(--fgColorH), calc(var(--fgColorS) * 2), calc(var(--fgColorL) * 1.15));

label:focus-within input[type="checkbox"]:checked + span.checkbox-label::after,
label.state-focus input[type="checkbox"]:checked + span.checkbox-label::after {
  background: hsl(var(--fgColorH), calc(var(--fgColorS) * 2), calc(var(--fgColorL) * 1.15));

We also want to handle the hover effect. Right now, when hovering the mouse over the input fields, nothing happens. We want to highlight to the users that they are over an interactive field. We can do that by changing the color of the label text and input border to the main color (take into account that the active state is lighter).

label:hover span {
  color: hsl(var(--fgColorH), var(--fgColorS), var(--fgColorL));

label:hover input,
label:hover span.checkbox-label::before {
  border-color: hsl(var(--fgColorH), var(--fgColorS), var(--fgColorL));

label:hover input[type="checkbox"]:checked + span.checkbox-label::after {
  background: hsl(var(--fgColorH), var(--fgColorS), var(--fgColorL));

One important thing to remember: as the styles for hover and active have the same specificity, the ones that appear later in the code will prevail. Place the :hover styles on top of the :active/:focus-within styles.

Button effects

Last, but not least, the submit button has styles only for two states: when it's at rest and on focus (the outline). We should handle the styles for at least a couple more states: hover and active.

  • For hover, we will make it a little bit darker; and
  • for active we will make it a little bit lighter and brighter.

The CSS is simple and straightforward:

input[type="submit"]:hover {
  background: hsl(var(--fgColorH), var(--fgColorS), calc(var(--fgColorL) * 0.85));

input[type="submit"]:active {
  background: hsl(var(--fgColorH), calc(var(--fgColorS) * 2), calc(var(--fgColorL) * 1.15));

And with those final touches, our form is complete. Here is a demo on JSFiddle:

Article originally published on