Upon page load, an overlay message box will slide-in to the viewport. This can be used for promotions, call-to-actions, email newsletter sign up forms, etc.
It includes a few data attributes that allow you to modify the message’s behavior:
data-position
This allows you to decide whether the message should slide-in from the right or left side of the viewport.right
, left
, center
(center must use fade
for data-close-transition)
data-close-transition
This allows you to decide how the message should disappear when the user closes it.fade
, slide
data-top
When data-position is set to either right
or left
then this allows you to decide how many pixels from the top of the viewport the message should appear. It will automatically take the header
element into consideration, if it has the ID of #site-header
(Takes any integer)
As per my last post, this example also includes the HTML CSS for a simple header, navigation menu, and masthead photo slideshow section — although the slideshow is non-functional in this case.
This example also includes Javascript for setting the masthead photo slideshow section’s photo to take up the entire available height of the viewport so long as that is <=960px.
I think I’ll be using the header/nav/slideshow stuff as a sandbox layout for other posts in the future. It seems to be working well for that!
Update as of 2023-02-12: I’ve added two new functions. A timer function which lets you set X number of seconds before the overlay appears. And a scroll function which lets you set a percentage of the page that the user has to scroll before the banner appears. One of these functions must be triggered in order for the overlay to appear, and once one of them has triggered then the other will no longer be able to trigger afterwards. These new functions are not documented on this page but you can view this version at this CoedPen link. I also removed the messageTopValue function because it was not needed for the particular project using this variation of the code, but it can easily be added back.:
JavaScript
// Created by Alex Winter
// Last Modified: 2022-07-29
const header = document.querySelector('#site-header');
const headerContainer = document.querySelector('#site-header .header-container');
const headerLogo = document.querySelector('.header-logo a');
const masthead = document.querySelector('.masthead-slideshow .glide');
// ternary operator
// if the element is null (undefined and doesnt exist in DOM) then set its offsetHeight value to 0
const headerHeight = header != null ? header.offsetHeight : 0; // get header height
// define viewport height
let viewportHeight = window.innerHeight;
// **************************************************
// Masthead Slideshow Height
// **************************************************
const setMastheadHeight = () => {
// calculate masthead height
let mastheadHeight = viewportHeight - headerHeight;
// apply masthead height value to masthead element
if (masthead) {
masthead.style.height = mastheadHeight + 'px';
};
};
setMastheadHeight();
// **************************************************
// Message
// **************************************************
const message = () => {
// get the required elements
const message = document.querySelector('#message')
const close = document.querySelector('#message .close')
// guard clause to make sure the '#message' element exists on the page
if (message == null) {
return
}
// get the width of the message element
const messageWidth = message.offsetWidth
// get the 'data-top' value and add it to the height of the header
// then apply this value to be the 'top' CSS value for the #message element
const messageTopValue = () => {
const messageDataTop = message.dataset.top != null ? message.dataset.top : 0
const messageTop = parseInt(messageDataTop, 10)
message.style.top = headerHeight + messageTop + 'px'
}
// sets the message element's default position
const messagePlacement = (placement) => {
message.style[placement] = ('-' + messageWidth + 'px')
}
// moves the message element in to place
const messageDirection = (placement) => {
setTimeout(() => {
message.style[placement] = '0px'
}, 1000)
}
// apply the 'center' class
const messageCenter = () => {
setTimeout(() => {
message.classList.add('position-center-visible')
}, 1000)
}
// function that sets the message element to 'display:none;' when needed
// IMPORTANT: duration should be equal to the transition-duration CSS property set for '#message' in your SCSS file
const messageHide = () => {
setTimeout(() => {
message.style.display = 'none'
}, 1000)
}
// the message element can slide in from either "left" or "right"
// IMPORTANT: sliding from right is the default, but you can slide from left by editing the 'data-position- attribute within the '#message' element
const messageStart = () => {
if (message.dataset.position == 'center') {
messageCenter()
return
}
// set the 'top' value for the message element
messageTopValue()
// default to 'right' if 'left' or 'right' aren't set,
if (message.dataset.position !== ('left' || 'right')) {
messagePlacement('right')
messageDirection('right')
return
}
// otherwise use the value entered in 'data-position'
messagePlacement(message.dataset.position)
messageDirection(message.dataset.position)
}
messageStart()
// close the overlay
// IMPORTANT: sliding to right is the default, but you can slide to left or fade out by setting the 'data-close-transition' attribute within the '#message' element
const messageClose = () => {
// 'fade' transition
const messageCloseFade = () => {
message.style.opacity = '0'
messageHide()
}
// click the 'close' button
close?.addEventListener('click', () => {
// if 'data-position' is not set, default to 'right'
if (message.dataset.position == null) {
messagePlacement('right')
messageHide()
return
}
// if 'data-close-transition' is set to 'slide', use the value entered in 'data-close-transition'
if (message.dataset.closeTransition == 'slide') {
messagePlacement(message.dataset.position)
messageHide()
return
}
// otherwise default to 'fade'
messageCloseFade()
})
}
messageClose()
}
message()
HTML
<header id="site-header">
<div class="header-container">
<div class="header-logo">
<a href="/">
<svg viewBox="0 0 138 26" fill="none" stroke="lightblue" stroke-width="2.3" stroke-linecap="round" stroke-linejoin="round" title="CodePen"><path d="M15 8a7 7 0 100 10m7-8.7L33 2l11 7.3v7.4L33 24l-11-7.3zm0 0l11 7.4 11-7.4m0 7.4L33 9.3l-11 7.4M33 2v7.3m0 7.4V24M52 6h5a7 7 0 010 14h-5zm28 0h-9v14h9m-9-7h6m11 1h6a4 4 0 000-8h-6v14m26-14h-9v14h9m-9-7h6m11 7V6l11 14V6"></path></svg>
</a>
</div>
<div class="header-nav">
<nav class="nav-traditional" aria-label="Navigation Menu">
<ul>
<li class="nav-item standard"><a href="/" class="nav-link">About</a></li>
<li class="nav-item standard"><a href="/" class="nav-link">Services</a></li>
<li class="nav-item standard"><a href="/" class="nav-link">Portfolio</a></li>
<li class="nav-item standard"><a href="/" class="nav-link">Blog</a></li>
<li class="nav-item standard"><a href="/" class="nav-link">Contact</a></li>
</ul>
</nav>
</div>
</div>
</header>
<!--
data-position: right, left, center (center must use 'fade' for 'data-close-transition')
data-close-transition: slide, fade
data-top: any integer
-->
<div id="message" class="text-center" data-position="right" data-close-transition="slide" data-top="40" role="complementary" aria-label="Important Message">
<h2>Schedule a Free In-Home Consultation</h2>
<p><a href="/" class="button">Schedule Consultation</a></p>
<button class="close" aria-label="Close the important message"><i class="fa-solid fa-xmark"></i></button>
</div>
<section id="site-masthead" role="complementary" aria-label="Photo Slideshow">
<div class="masthead-slideshow">
<div class="glide">
<img src="https://images.unsplash.com/photo-1628783629868-19fb7eb52e2a" alt="">
</div>
</div>
</section>
<main>
<section>
<h1>Site Content</h1>
<p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Distinctio mollitia nostrum, praesentium provident officia odit possimus commodi blanditiis fugiat animi recusandae, necessitatibus assumenda cumque vel, molestias quo. Tempore nobis quos perspiciatis dignissimos recusandae id. Qui, veritatis. Eaque dolore adipisci dolores necessitatibus ducimus magni, inventore nobis, cumque maxime nam eius quae sint! Accusamus, enim! Placeat asperiores mollitia fugiat, consectetur illo provident nam dolores blanditiis non nulla nemo dignissimos praesentium voluptas, fuga vero voluptatum velit voluptate totam ad commodi. Iure beatae magnam exercitationem quod tenetur id. Eveniet error vel maxime adipisci quasi dolores repudiandae. Beatae repudiandae assumenda recusandae eius consequuntur itaque ut?</p>
</section>
</main>
CSS
body {
margin: 0;
background-color: #000;
color: #fff;
font-family: 'Monterrat', sans-serif;
}
h1 {
margin: 0;
font-size: 3.5rem;
line-height: 1.75;
}
p {
font-size: 1.5rem;
line-height: 1.75;
}
.text-center {
text-align: center;
}
// **************************************************
// Button
// **************************************************
%button {
display: inline-block;
padding-block: 0.75em;
padding-inline: 1.1em;
line-height: 1;
background-color: #247593;
border: 2px solid #247593;
// border-radius: 0.25em;
color: #fff;
font-size: 1.1rem;
font-weight: 400;
font-style: normal;
text-align: center;
text-decoration: none;
// text-transform: uppercase;
transition: all 0.5s;
&:hover,
&:focus {
background-color: #fff;
color: #247593;
}
}
.button {
@extend %button;
}
// **************************************************
// Header
// **************************************************
header#site-header {
position: sticky;
top: 0;
width: 100%;
z-index: 10000;
background-color: #323233;
}
// **************************************************
// Header Container
// **************************************************
.header-container {
display: flex;
flex-direction: row;
justify-content: space-between;
gap: 2em;
max-width: 1920px;
margin-inline: auto;
padding-block: 2.25em;
padding-inline: 1em;
@media (max-width: 1360px) {
flex-direction: column;
gap: 0;
padding-block: 1em;
}
}
// **************************************************
// Header Logo
// **************************************************
.header-logo {
flex-grow: 0;
flex-shrink: 1;
flex-basis: auto;
display: flex;
justify-content: center;
flex-direction: column;
align-items: center;
a {
width: 420px;
@media (max-width: 1360px) {
width: 210px;
}
}
img {
display: block;
}
}
// **************************************************
// Header Nav
// **************************************************
.header-nav {
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
}
.nav-traditional {
margin: 0;
padding: 0;
opacity: 1;
display: block;
>ul {
display: flex;
gap: 0.25em;
margin: 0;
padding: 0;
list-style: none;
@media(max-width: 540px) {
gap: 0;
}
>li {
position: relative;
>a {
display: block;
padding-block: 0.6em;
padding-inline: 1.2em;
color: #fff;
font-size: 1.4rem;
font-family: 'Montserrat', sans-serif;
line-height: 1;
font-weight: 400;
text-decoration: none;
@media (max-width: 1360px) {
font-size: 1rem;
}
@media(max-width: 540px) {
padding-inline: 0.75em
}
}
&.standard {
>a {
transition: all 0.5s;
&:hover,
&:focus{
background-color: #5A5F73;
color: #fff;
}
}
}
}
}
}
// **************************************************
// Message
// **************************************************
#message {
position: fixed;
inset: auto;
right: -9999px; // ensures the element is not visible during page load
z-index: 9999;
width: 100%;
max-width: 350px;
padding-block: 2.75em 2.6em;
padding-inline: 1em;
background: #fff;
border: 2px solid #8082bd;
box-shadow: 1px 1px 5px rgba(0, 0, 0, 0.5);
transition-property: all;
transition-duration: 1000ms;
&[data-position='center'] {
top: 50%;
right: 50%;
transform: translate(50%, -50%);
opacity: 0; // ensures the element is not visible during page load
}
&.position-center-visible {
opacity: 1;
}
h2 {
margin: 0;
margin-bottom: 0.6em;
color: #000;
font-size: 1.5rem;
line-height: 1.2;
font-weight: 500;
}
p {
margin: 0;
}
// set padding and font-size accordingly to ensure this element is always at least 44x44 pixels to comply with WCAG 2.1 guidelines
.close {
position: absolute;
inset: 0 auto auto 0;
padding-block: 0.29em;
padding-inline: 0.48em;
background-color: transparent;
border: 0;
font-size: 1.7rem;
line-height: 1;
color: #c4c4c4;
cursor: pointer;
&:hover,
&:focus {
color: #525252;
}
}
}
// **************************************************
// Masthead Slideshow
// **************************************************
#site-masthead {
position: relative;
background-color: #000;
}
.masthead-slideshow {
.glide {
max-height: 960px;
}
img {
display: block;
object-fit: cover;
width: 100%;
height: 100%;
max-width: none;
}
}
// **************************************************
// Main
// **************************************************
main {
padding-block: 5em;
padding-inline: 1em;
section {
width: min(100%, 1920px);
margin-inline: auto;
}
}