Skip to main content
Ben Nadel at cf.Objective() 2013 (Bloomington, MN) with: Will Belden
Ben Nadel at cf.Objective() 2013 (Bloomington, MN) with: Will Belden ( @CaptainPalapa )

Accessibility And Styled Anchor Links vs. Styled Buttons In Angular 7.2.15

By on

After reading Accessibility For Everyone by Laura Kalbag, I've become more aware of the accessibility short-comings in my application interfaces. Of particular note is the fact that Anchor Links aren't keyboard accessible; at least, not by default. Yesterday, I demonstrated how to make anchor links keyboard accessible in Angular; but, the exercise in augmentation begs the question: is the anchor link even the right choice of Element? According to Marcy Sutton, probably not. Anchor links are for "resource links" whereas Buttons are for triggering discrete actions. And, as it turns out, Buttons happen to be much more accessible by default. As such, I wanted to take a quick look at styling Buttons in an Angular 7.2.15 application.

Run this demo in my JavaScript Demos project on GitHub.

View this code in my JavaScript Demos project on GitHub.

Marcy Sutton has a good explanation of the Button in her blog post; but, to sum up some of the key points, the Button element provides a number of benefits over the Anchor link element (in certain situations):

  • Buttons are naturally keyboard accessible without any additional effort, HTML markup, or Angular directives.

  • Buttons can be triggered with both the Enter key and the Spacebar key.

  • Buttons are more semantically correct when they are used to trigger a discrete action, like deleting an item, opening a pop-up content area, or changing the state of a toggle (which makes a difference in how Screen Readers announce them).

All of this makes me think that a good portion of the Anchor links in my current Angular applications should actually but Button elements. As such, I wanted to put together a quick demo to showcase the fact that a tags and button tags are both highly modifiable from a CSS stand-point.

To demonstrate, I have a simple App component that lists two sets of interactive elements: one set uses the a tag; the other set uses the button tag:

<p>
	<a href="#">Native Href link</a> (experiment control)
</p>

<p>
	Styled <code>&lt;A&gt;</code> elements:
</p>

<div class="actions">
	<a (click)="logClick( 'Download item one' )">
		<svg aria-hidden="true">
			<use xlink:href="#sprite-button-icon" />
		</svg>
		Download One
	</a>
	<a (click)="logClick( 'Download item two' )">
		<svg aria-hidden="true">
			<use xlink:href="#sprite-button-icon" />
		</svg>
		Download Two
	</a>
	<a (click)="logClick( 'Download item three' )">
		<svg aria-hidden="true">
			<use xlink:href="#sprite-button-icon" />
		</svg>
		Download Three
	</a>
</div>

<p>
	Styled <code>&lt;BUTTON&gt;</code> elements:
</p>

<div class="actions">
	<button (click)="logClick( 'Download item one' )">
		<svg aria-hidden="true">
			<use xlink:href="#sprite-button-icon" />
		</svg>
		Download One
	</button>
	<button (click)="logClick( 'Download item two' )">
		<svg aria-hidden="true">
			<use xlink:href="#sprite-button-icon" />
		</svg>
		Download Two
	</button>
	<button (click)="logClick( 'Download item three' )">
		<svg aria-hidden="true">
			<use xlink:href="#sprite-button-icon" />
		</svg>
		Download Three
	</button>
</div>

<!--
	NOTE: Usually, the SVG spite is placed high in the document. However, for this demo,
	I am placing it low down just to get it out of the way. It still seems to function
	as you would hope.
-->
<svg style="display: none ;">
	<symbol id="sprite-button-icon" viewBox="0 0 24 16" aria-labelledby="sprite-button-icon-label">
		<title id="sprite-button-icon-label">
			Download Icon
		</title>
		<path d="M19.4,6 C18.7,2.6 15.7,0 12,0 C9.1,0 6.6,1.6 5.4,4 C2.3,4.4 0,6.9 0,10 C0,13.3 2.7,16 6,16 L19,16 C21.8,16 24,13.8 24,11 C24,8.4 21.9,6.2 19.4,6 L19.4,6 Z M17,9 L12,14 L7,9 L10,9 L10,5 L14,5 L14,9 L17,9 L17,9 Z" />
	</symbol>
</svg>

As you can see, the a and button elements are essentially identical. They all have a (click) event-handler (function not show in this demo). And, they all include an SVG icon. And, when we render them, they look exactly the same. The main difference is that the button elements are much more accessible:

Demonstration that Button elements can be styled and are highly accessible in Angular 7.2.15.

The button element does require a tiny bit more CSS, particularly around Font style; but, as you can see in the following LESS CSS, both the a tag and the button tag are equally flexible:

:host {
	display: block ;
	font-size: 18px ;
}

.actions {
	display: flex ;
	font-family: monospace ;
	margin: 20px 0px 30px 0px ;

	a,
	button {
		align-items: center ;
		background-color: #f0f0f0 ;
		border: 1px solid #e0e0e0 ;
		border-radius: 5px 5px 5px 5px ;
		color: #ff3366 ;
		cursor: pointer ;
		display: flex ;
		font-family: inherit ; // Needed for Button.
		font-size: inherit ; // Needed for Button.
		margin-right: 10px ;
		padding: 10px 15px 10px 12px ;

		// Only CHROME seems to show a nice beefy outline by default; as such, in order
		// to get the focus styling more consistent, I am going to override the outline
		// and then use a box-shadow. This works the same in the major desktop browsers.
		&:focus,
		&:active {
			outline: none ;
			box-shadow: 0px 0px 2px 4px #ff3366 ;
		}
	}

	svg {
		fill: #ff3366 ;
		height: 16px ;
		margin-right: 13px ;
		width: 24px ;
	}
}

As you can see, it really takes no additional effort to style a button element. This may not have been true years ago; but, it is certainly true today in all modern browsers.

Now, to be clear, I am very much an accessibility novice. At this point, I'm just trying to become more aware of my own knowledge gaps; and, I'm starting to baby-step towards a more holistic understanding of my users. So, I won't get into any deeper explanation of semantic HTML or element selection. But, I will certainly be trying to use the button element in more places within my Angular applications, especially when the goal of the UI (user interface) interaction is to trigger a non-routable, discrete action. I believe this will naturally create a more accessible and intuitive experience.

ASIDE: Part of creating a "more intuitive" experience is not removingthe native focus and active indicators. For years, I've been told by designers (and by my own biased aesthetic) to remove the "outline" that shows up around an active element. Going forward, however, I will be trying to champion such accessible interface features, not trying to suppressing them.

Want to use code from this post? Check out the license.

Reader Comments

I believe in love. I believe in compassion. I believe in human rights. I believe that we can afford to give more of these gifts to the world around us because it costs us nothing to be decent and kind and understanding. And, I want you to know that when you land on this site, you are accepted for who you are, no matter how you identify, what truths you live, or whatever kind of goofy shit makes you feel alive! Rock on with your bad self!
Ben Nadel