CSS can help improve your HTML⁉ - Ep 2: buttons and links.
Tags: html, css, a11y, tutorial
Published: 2021-12-15 13:07:06

Part 2 of using CSS to make our HTML better!

In this second part of this series we will be exploring how we can use CSS to ensure (and enforce) that we are using `<button>` and `<a>` elements correctly...you would be surprised how many developers get this wrong!

If you missed part one you may need to have a quick read of that so you get the key concepts as I won't be repeating them in detail here:

{% post https://dev.to/inhuofficial/using-css-to-prevent-html-errors-ep-1-alt-attributes-11o8 %}

All caught up? Great!

Before we start part 2, it is worth mentioning (reiterating) that this concept relies on two style sheets.

One for production (and development) and one for development **only** (the one containing error messages).

In the fiddles I have indicated which styles you would use in your **production** stylesheet and which to use in your **development** stylesheet!

Right, with that reminder out of the way, let's begin!

## A button is a button!
I know I keep saying it and I sound like a broken record but, a button is a button!

{% post https://dev.to/inhuofficial/they-just-won-t-learn-a-button-is-a-lt-button-gt-accessibility-rants-k1b %}

See, I told you I keep saying it!

But what if we could guide our developers towards this logical conclusion using CSS?

Well first thing is first, we can make it so our `.btn` class **only** works on a `<button>`.

``` css
button.btn{}
```

Try and use the `.btn` class on anything else and it just won't work!

but we can do better than that!

What if we can also give our team a little bit of a warning to let them know they have used the wrong element?

``` css
.btn:not(button){/* wrong element error highlighting */}
```

### Meaningful warning and error messages
Now while adding outlines to stuff that is wrong is great, it isn't very informative.

Luckily we can fix that also!

By using `:after` we can insert an error message

So we end up with a selector to add an outline and another selector to add a warning that is the same but using the `:after` pseudo selector.

By utilising the `content` property we can add a custom error message!

``` css
.btn:not(button) /* outlines */
.btn:not(button):after /* error message */

```


### Example for buttons
In this example we have a real button and a fake button made with a `<div>`. 

Notice how the real button works and the fake button has no styling as well as an error message!

{% codepen https://codepen.io/inhuofficial/pen/BawWjKg default-tab=result, html, css %}

### Taking it one step further!
I would argue that for most sites we can actually go one step further and completely do away with the `.btn` class on our buttons!

This helps with consistency, looks neater and enforces our policy of `<button>`s for buttons!

At this point we no longer even need an error check as we can only get styling on a `<button>`!

We can do this by using the element as the selector `button{}`! 

``` css
button{
    padding: 0.75rem;
    background-color: #333;
    color: #fff;
    margin: 2rem 0;
    border: 0;
    border-radius: 0.3rem;
    width: 100%;
}
```

However this may have one unintended consequence...

### That's great, but we often style anchors like buttons!
And that is a valid point and nowadays, you are right!

However we can recycle our selector from the first part so we can have a `.btn` class **only** on anchors (`<a>`)!

``` css
button,
a.btn{/* our styles for buttons! */}
```

This does mean we have to reintroduce our error checks though, but this time to ensure that we only use the `.btn` class on an anchor!

This raises an interesting question, do we want an error to show if we have a `.btn` class on a `<button>`?

If we **do** then our error selector just needs a subtle change

``` css
.btn:not(a){}
.btn:not(a):after{}
```

This will add our error messages to `<button class="btn"`, which might be confusing.

As it won't do any harm having the `.btn` class on a `<button>` (as we have no valid selector for it anyway) I would think it better to not show error messages in this instance.

Yet again the power of CSS makes this relatively simple, we can add multiple items within our `:not` selector!

``` css
.btn:not(a,button){}
.btn:not(a,button):after{}
```

This is saying:-
- select all items with the `.btn` class
- that are not (`:not()`)
- an anchor (`a`)
- OR a button `,button` (comma seperated).

### Example including anchors
Bringing everything together we end up with the following:

{% codepen https://codepen.io/inhuofficial/pen/VwMperq default-tab=result, html, css %}

Looks great, but we can go **even** further!

### But we only want valid anchors
Now that we have allowed the `.btn` class to be added to anchors we have opened up another problem!

The dreaded `<a href="#"` or `<a href="javascript: void()`!

This anti-pattern has not been needed since HTML5 (you did read my rant on buttons I linked earlier didn't you?).

So how can we fix this one?

Well in part 1 we used attribute selectors `[attr]` and once again they can come to our rescue!

As this is a site wide error (it should never be used!) we can ignore the `.btn` selector and just focus on anchors!

The first one is easier to solve, we select any anchors with a `href="#"` (and while we are at it an empty `href` or missing `href` too!)

``` css
a:not([href]), /* A missing href attribute! */
a[href=""], /* an empty (or null) href */
a[href="#"] /* the anti pattern! */
```
 
That covers most of them, we just need to highlight the JavaScript anti-pattern!

Yet again we covered this in part 1, we can use the "begins with" notation:

``` css
a[href^="javascript:" i]
```

Remember the "i" at the end to make it case insensitive.

We used the "begins with" `^=` syntax for this example as it doesn't have to be `javascript: void()`, this way we can capture all JavaScript `href`s no matter what function they call!

### Example for valid anchors!
Utilising the same system of using `:after` we can add messages to our anchors as well!

{% codepen https://codepen.io/inhuofficial/pen/wvrJMQM default-tab=result, html, css %}

**Note:** We can make this much better by having separate message depending on whether the problem is a missing `href`, bad `href` or the use of JavaScript. I haven't done that in the example but you would do it as follows:

``` css
a:not([href]):after{content: "missing href attr"}
a[href="#"]:after{content: "anti-pattern on href detected"}
a[href=""]:after{content: "empty or null href"}
a[href^="javascript:" i]:after{content: "using javascript in href anti-pattern detected"}
```

### Additional things to mention
As pointed out by @ashleyjsheridan it is perfectly valid to have an  anchor without a `href`.

As such where I marked anchors with missing or empty `href` attributes as errors this was a mistake on my part. 

Instead these should be downgraded to "warnings" just to pick up on mistakes.

Additionally this then means that if you use `<a name="sectionName">` for whatever reason it will only be a warning (although this is quite a dated way to do things as you can just give an element an `id` attribute and link to that instead).

Overall it has no bearing though as the **development** style sheet will not impact production, might just be a little annoying though if you do edge case stuff like dynamically adding a `href` to an existing anchor.

Additionally they mention:
> Might also be worth considering the 3 <input> button types: button, submit, and reset.

This is an excellent point, so to add those you would update the selector to target:
 
``` css
button,
a.btn,
input[type="button"],
input[type="submit"],
input[type="reset"]{
  /* can use the same default styling and overwrite with additional styles to differentiate as needed */
} 
```

If you need to use those.

## Conclusion
A lot of what we learned in part 1 has carried over to part 2. 

It is just that our selectors have become a little more complex.

But hopefully you can see how this allows us to enforce business style guides once again (having or not requiring a `.btn` class on `<button>` elements for example). 

In the next part we are going to focus on document structure best practices and introduce some WAI-ARIA attributes and some tips and tricks with those!

## Thanks for Reading!
Thanks for reading, I hope you enjoyed it!

In fact to show my appreciation please do have a virtual ❤ and 🦄 to show how much it means to me!

If you enjoyed this and want something just a *little* (OK a **lot** longer I have written) why not read my monster accessibility tips article:

{% post https://dev.to/inhuofficial/101-digital-accessibility-tips-and-tricks-4728 %}

Have a great week!