<button class="o-btn" aria-expanded="false">Show dialogue</button>
<div class="o-dialogue" aria-hidden="true" tabindex="-1">
	<h2 class="o-dialogue__heading">Oh, hey there</h2>
	<p class="o-dialogue__summary">Remember, you can use your <kbd>esc</kbd> key to close me!</p>
	<button class="[ o-btn o-btn--small ] [ o-dialogue__btn ]" aria-expanded="false">Close window</button>
</div>
// Vars 
$primary: #34495e;
$primary--light: #527395;
$secondary: #d35400;
$radius: 3px;
$transition: all 250ms cubic-bezier(0.675, 0.420, 0.090, 0.870);

// Core
body {
	font-size: 16px;
	background: #f3f3f3;
	display: flex;
	justify-content: center;
	align-items: center;
	position: relative;
	font-family: 'Lato';
}

kbd {
	padding: 2px;
	border: 1px solid $secondary;
	border-radius: $radius;
}

// Button object 
.o-btn {
	display: inline-block;
	appearance: none;
	border: none;
	padding: 0;
	margin: 0;
	text-decoration: none;
	font-family: 'Lato';
	font-weight: 900;
	font-size: 1.3rem;
	line-height: 1;
	padding: 15px 25px 17px 25px;
	border-radius: $radius;
	background: $primary;
	color: #fff;
	cursor: pointer;
	
	&:hover,
	&:focus {
		background: $primary--light;
	}
	
	&:focus {
		outline: none;
		box-shadow: 0 0 0 3px $secondary;
	}
	
	// Modifiers
	&--small {
		font-size: 0.88rem;
		padding: 10px 15px;
	}
}

// Dialogue 
.o-dialogue {
	display: block;
	max-width: 400px;
	position: absolute;
	top: 50%;
	left: 50%;
	transform: translate3d(-50%, -30%, 0);
	background: #ffffff;
	padding: 20px;
	margin: 20px;
	border-radius: $radius;
	box-shadow: 0px 10px 20px rgba(0, 0, 0, 0.2);
	transition: $transition;
	opacity: 0;
	visibility: hidden;
	
	&__summary {
		padding-top: 10px;
		display: block;
	}
	
	&__btn {
		margin-top: 20px;
	}
	
	// Show the dialogue
	&[aria-hidden="false"] {
		opacity: 1;
		visibility: visible;
		transform: translate3d(-50%, -50%, 0);
	}
}
var hidden = true;
const buttonElems = [...document.querySelectorAll('.o-btn')];
const dialogueElem = document.querySelector('.o-dialogue');

const toggle = (state = !hidden) => {
	
	// Loop buttons and reverse expanded attribute
	buttonElems.map(buttonElem => {
		buttonElem.setAttribute('aria-expanded', (state ? 'false' : 'true'));
	});
	
	// Set the hidden attribute to the opposite of state
	dialogueElem.setAttribute('aria-hidden', (state ? 'true' : 'false'));

	// Focus the dialogue if it's visible
	if(!state) {
		dialogueElem.setAttribute('tab-index', '0');
		dialogueElem.focus();
	}
	else {
		dialogueElem.setAttribute('tab-index', '-1');
		
		// Focus the 'show dialogue' button
		buttonElems[0].focus();
	}
	
	// Set the global hidden flag to the state value
	hidden = state;
};

// Attach toggle function to button click events
buttonElems.map(buttonElem => {
	buttonElem.addEventListener('click', (evt) => {
		evt.preventDefault();
		
		toggle();
	});
});

// Bind the toggle function to the escape key
document.addEventListener('keyup', (evt) => {
    if(evt.keyCode === 27) {
		
		// Force the closure by passing custom state
        toggle(true);
    }
});
View Compiled

External CSS

  1. https://codepen.hankchizljaw.io/css/global.css
  2. https://fonts.googleapis.com/css?family=Fira+Mono|Lato:400,900

External JavaScript

This Pen doesn't use any external JavaScript resources.