A common design pattern is to have something like a “card” element that has to be fully clickable. This is usually because it links to another page or triggers a JavaScript action.

The problem though, is that often, you end up with stuff that looks like this:

<div onClick="alert('Nope')">Please don’t ever do this.div>

You should absolutely never attach a click event to a

element, though, even if you sprinkle it with aria roles to “fake” a real button. Although this is technically possible, if assistive technology doesn’t support the aria roles in question, the user will just get

s and nothing else. Not cool.

In this article, we’re going to remedy this common crime by instead using the magic of CSS to give us the desired fully clickable element effect, while also using proper semantic elements and JavaScript and as an enhancement.

What we’re making permalink

We’re going to use the context of an

that when a button is clicked, a JavaScript alert fires. Here’s a CodePen demo:

See the Pen Semantic “break-out” button by Andy Bell (@andybelldesign) on CodePen.

To code along, you’re going to need a HTML file, a CSS file and a JavaScript file. I recommend that you use a service like CodePen that does all of this for you.

For the rest of this section, we’re going to be laying the foundations. If you’re in a rush, you can skip this section and use this starter CodePen instead.

Getting started with markup permalink

We need some HTML to work with, so add this:

<article class="box">

<h1>A semantic, breakout buttonh1>

<p>This whole box is clickable, but still uses a button element, correctly.p>

<button class="breakout-button" type="button">Say Hi ?button>


We’ve got an article, a heading, a paragraph and a button. Job done for HTML for now.

Base CSS styles permalink

Before we get into the proper CSS stuff, let’s set some base styles. Add this CSS:

body {

background: #efefef;

padding: 3rem 2rem;

line-height: 1.4;

font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial,

sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol';

font-size: 1.2rem;


h1 {

font-size: 2rem;

line-height: 1.1;

font-weight: 600;


.box {

color: #fff;

padding: 2rem;

max-width: 30rem;

background: #252525;

position: relative;

box-shadow: none;

transition: transform 300ms ease-in-out, box-shadow 400ms ease, background 100ms ease;




background: #111111;

box-shadow: 0 1rem 1rem rgba(0, 0, 0, 0.3);

transform: translateY(-0.5rem);


.box > * * {

margin-top: 1em;


Looking pretty decent, right? That’s it for our base styles.

Creating the breakout button component permalink

We’ve got base styles, so let’s focus all of our attention on the .breakout-button component.

Core component styles permalink

In your CSS, add the following:

.breakout-button {

font: inherit;

font-weight: 600;

padding: 0.6rem 2rem;

background: transparent;

color: currentColor;

border: 1px solid;

transition: background 100ms ease;

position: static;


Here, we have some simple styles for our button which make it looks nicer. A handy takeaway trick here is that because we are using font: inherit and color: currentColor, we get all of our text styles for free, using the cascade, which is by far my favourite aspect of CSS. Also notice that we are setting .breakout-button to be position: static. This is because we want our “breakout element” (coming up) to literally break out of the button!

Related to this is that the .box element has position: relative, which means that any child elements without a relative parent that have position: absolute will be contained in this .box. Because that’s what our breakout button will do as its sole purpose, you should always remember to make its containing parent behave like this. The position: static on the .breakout-button itself is a failsafe to make sure the “breakout element” isn’t ever contained to the .breakout-button element.

Right, let’s add some more code. Under the .breakout-button component style, add this CSS:



cursor: pointer;


We’ve set both the button and its ::before pseudo-element to have a pointer cursor. There’s some contention around wether a