Skip to content

Instantly share code, notes, and snippets.

@tak-dcxi
Created January 14, 2021 23:41
Show Gist options
  • Save tak-dcxi/58987e71b8b332bb704288e9780ed00d to your computer and use it in GitHub Desktop.
Save tak-dcxi/58987e71b8b332bb704288e9780ed00d to your computer and use it in GitHub Desktop.
Drawer Menu Pattern 2
<div class="wrapper" data-inert-target>
<header class="global-header">
<div class="global-header__container">
<div class="global-header__title">
<p class="logo">My Drawer Menu</p>
</div>
<div class="global-header__burger-button">
<button class="menu-button js-menu-open-trigger" type="button" aria-label="メニューを開く">
<span class="menu-button__icon -open"></span>
</button>
</div>
</div>
</header>
<main class="main-content">
<article class="article">
<h1 class="article__title">このドロワーメニューについて</h1>
<div class="article__sentence">
<p>ユーザビリティおよびにアクセシビリティを意識した自作ドロワーメニューです。</p>
</div>
<section class="article__section">
<h2 class="article__subtitle">自己チェック</h2>
<ul class="article__list">
<li class="article__list-item -checked">メニューが開いている時に背景のスクロールを無効にする</li>
<li class="article__list-item -checked">メニュー内のリンクをクリック時にメニューを閉じることができる(非同期遷移のサイトやページ内リンクをメニューに含む場合は必須)</li>
<li class="article__list-item -checked">支援技術向けのラベルをボタンとメニュー本体に付与する</li>
<li class="article__list-item -checked">裏側のコンテンツの読み上げを制御する</li>
<li class="article__list-item -checked">裏側のコンテンツを操作対象外にする(inert属性を利用)</li>
<li class="article__list-item -checked">メニュー本体をEsc操作で閉じられる</li>
<li class="article__list-item -checked">メニューを閉じた時にフォーカスをボタンに当てる</li>
<li class="article__list-item -checked">背景固定時のスクロールバー消失によるガタツキを防止する</li>
<li class="article__list-item -checked">durationの設定を反映したCSSカスタムプロパティを生成することで、JSのオプションで一括でアニメーションのdurationを変えられるようにする</li>
</ul>
</section>
<section class="article__section">
<h2 class="article__subtitle">課題点</h2>
<ul class="article__list">
<li class="article__list-item -not-checked">裏側のコンテンツの読み上げ制御はDOM依存のため要検討</li>
<li class="article__list-item -not-checked">自作するよりMicromodal.js使ったほうが良さそう</li>
</ul>
</section>
<section class="article__section">
<h2 class="article__subtitle">参考サイト</h2>
<ul class="article__list">
<li class="article__list-item -link"><a class="article__link" href="https://magazine.techcareer.jp/instacart-blog/technology-instacart-blog/6418/" target="_blank" rel="noopener noreferrer">アクセシブルなWebモーダルを作る | techcareer magazine</a></li>
<li class="article__list-item -link"><a class="article__link" href="https://www.w3.org/TR/wai-aria-practices-1.1/examples/dialog-modal/dialog.html" target="_blank" rel="noopener noreferrer">Modal Dialog Example | WAI-ARIA Authoring Practices 1.1</a></li>
<li class="article__list-item -link"><a class="article__link" href="https://bootstrap-guide.com/components/modal" target="_blank" rel="noopener noreferrer">モーダル~Bootstrap5設置ガイド</a></li>
<li class="article__list-item -link"><a class="article__link" href="https://standard.shiftbrain.com/blog/unavailable-inert-regions-and-inert-attribute" target="_blank" rel="noopener noreferrer">UIにおける見えるけど利用できない非活性な領域の実装とinert属性について - シフトブレイン/スタンダードデザインユニット</a></li>
</ul>
</section>
</article>
</main>
</div>
<nav id="js-menu-content" class="drawer-menu" style="display: none;">
<div class="drawer-menu__container">
<h2 id="js-menu-label" class="drawer-menu__headline">メニュー</h2>
<ul class="drawer-menu__list">
<li class="drawer-menu__item">
<a class="drawer-menu__link" href="#">
<span class="drawer-menu__en-label">Drawer Menu</span>
<span class="drawer-menu__jp-label">ドロワーメニュー</span>
</a>
</li>
<li class="drawer-menu__item">
<a class="drawer-menu__link" href="https://codepen.io/tak-dcxi/pen/VwZbwyg" target="_blank" rel="noreferrer noopener">
<span class="drawer-menu__en-label">Hero Header</span>
<span class="drawer-menu__jp-label">ヒーローヘッダー</span>
</a>
</li>
<li class="drawer-menu__item">
<a class="drawer-menu__link" href="https://codepen.io/tak-dcxi/pen/jOEVKEX" target="_blank" rel="noreferrer noopener">
<span class="drawer-menu__en-label">Observe Animation</span>
<span class="drawer-menu__jp-label">スクロール連動アニメーション</span>
</a>
</li>
<li class="drawer-menu__item">
<a class="drawer-menu__link" href="https://codepen.io/tak-dcxi/pen/yLaaJYj" target="_blank" rel="noreferrer noopener">
<span class="drawer-menu__en-label">Accordion</span>
<span class="drawer-menu__jp-label">アコーディオン</span>
</a>
</li>
<li class="drawer-menu__item">
<a class="drawer-menu__link" href="https://codepen.io/tak-dcxi/pen/vYEyrGJ" target="_blank" rel="noreferrer noopener">
<span class="drawer-menu__en-label">Tab</span>
<span class="drawer-menu__jp-label">タブコンポーネント</span>
</a>
</li>
<li class="drawer-menu__item">
<a class="drawer-menu__link" href="https://codepen.io/tak-dcxi/pen/vYXWPvZ" target="_blank" rel="noreferrer noopener">
<span class="drawer-menu__en-label">Form Template</span>
<span class="drawer-menu__jp-label">フォームテンプレート</span>
</a>
</li>
</ul>
<div class="drawer-menu__close-button">
<button class="menu-button js-menu-close-trigger" type="button" aria-label="メニューを閉じる">
<span class="menu-button__icon -close"></span>
</button>
</div>
</div>
<div class="drawer-menu__overlay js-menu-close-trigger"></div>
</nav>
window.addEventListener("DOMContentLoaded", () => {
const drawer = document.getElementById("js-menu-content");
new DrawerMenu(drawer);
new PrepareCurrent(drawer);
});
class DrawerMenu {
constructor(root, options) {
this.root = root;
if (!this.root) return;
const defaultOptions = {
openTriggerSelector: ".js-menu-open-trigger", // メニューを開く際のターゲットとなるセレクタ
closeTriggerSelector: ".js-menu-close-trigger", // メニューを閉じる際のターゲットとなるセレクタ
inertTargetSelector: "[data-inert-target]", // 開いている時はこのセレクタを読み上げ対象外にする。
labelSelector: "#js-menu-label", // 支援技術向けのメニュー本体のラベルとなるidセレクタ
hideLabelSelector: true, //ニューのラベルとなる要素を視覚的に非表示にするか
clickLinkToClose: true, // メニュー内のリンクをクリック時にメニューを閉じるか
toggleDuration: 500 // メニューの遷移時間
};
const mergedOptions = Object.assign(defaultOptions, options);
this.options = mergedOptions;
/**
* メニューを開くトリガーとなるセレクタの設定
*/
this.openTrigger = document.querySelectorAll(
this.options.openTriggerSelector
);
if (!this.openTrigger.length)
throw TypeError("メニューを開く要素が見つかりません");
/**
* メニューを閉じるトリガーとなるセレクタの設定
*/
this.closeTrigger = this.options.clickLinkToClose
? document.querySelectorAll(
`${this.options.closeTriggerSelector}, #${this.root.getAttribute(
"id"
)} a`
)
: document.querySelectorAll(this.options.closeTriggerSelector);
if (!this.closeTrigger.length)
throw TypeError("メニューを閉じる要素が見つかりません");
/**
* メニューのラベルとなるセレクタの設定
*/
this.label = document.querySelector(this.options.labelSelector);
if (!this.label) throw TypeError("ラベルとなる見出し要素の指定は必須です");
/**
* メニューが開かれている際に読み上げ対象外とするセレクタの指定
*/
this.inertTarget = document.querySelectorAll(
this.options.inertTargetSelector
);
if (!this.inertTarget.length)
throw TypeError("inert対象のセレクタの指定は必須です");
/**
* 状態を格納する変数
*/
this.isExpanded = false; // メニューの開閉状態を格納する
this.openButton; // どのトリガーでメニューが開かれたかを格納する
this.scrollAmount; // メニューが開かれた際のスクロール量を格納する
/**
* 初期化時に属性を付与
*/
this.prepareAttributes();
/**
* イベントハンドラの設定
*/
this.openTrigger.forEach((trigger) => {
attachEvent(trigger, "click", this.handleOpenTriggerClick.bind(this));
});
this.closeTrigger.forEach((trigger) => {
attachEvent(trigger, "click", this.handleCloseTriggerClick.bind(this));
});
attachEvent(document, "keyup", this.handleKeyup.bind(this));
}
prepareAttributes() {
this.root.setAttribute("inert", "");
this.root.style.display = "none";
this.openTrigger.forEach((trigger) => {
trigger.setAttribute("aria-haspopup", true);
});
this.label.setAttribute("tabindex", "-1");
document.documentElement.style.setProperty(
"--menu-toggle-duration",
`${this.options.toggleDuration}ms`
);
/**
* ラベル対象にidが含まれていない場合はid属性を付与
*/
if (!this.label.hasAttribute("id")) {
const randomId = "aria-label-" + Math.random().toString(32).substring(2);
this.label.setAttribute("id", randomId);
}
const labelId = this.label.getAttribute("id");
/**
* ラベル対象を非表示にする設定をしている場合の処理
*/
if (this.options.hideLabelSelector) {
const styles = {
border: "0",
clip: "rect(0 0 0 0)",
clipPath: "inset(50%)",
height: "1px",
margin: "-1px",
overflow: "hidden",
padding: "0",
position: "absolute",
whiteSpace: "nowrap",
width: "1px"
};
Object.keys(styles).forEach((key) => {
document.querySelector(this.options.labelSelector).style[key] =
styles[key];
});
}
this.root.setAttribute("aria-labelledby", labelId);
}
handleOpenTriggerClick(event) {
event.preventDefault;
this.open();
}
handleCloseTriggerClick(event) {
event.preventDefault;
this.close();
}
handleKeyup(event) {
const pressEsc = event.keyCode === 27;
if (this.isExpanded && pressEsc) {
event.preventDefault;
this.close();
}
}
open() {
this.root.removeAttribute("inert");
this.root.style.display = "";
this.backfaceFixed(true);
this.inertTarget.forEach((target) => {
target.setAttribute("inert", "");
});
this.openButton = document.activeElement;
setTimeout(() => {
this.label.focus();
}, 100);
this.isExpanded = true;
}
close() {
this.root.setAttribute("inert", "");
this.backfaceFixed();
this.openTrigger.forEach((trigger) => {
trigger.setAttribute("inert", "");
});
this.inertTarget.forEach((target) => {
target.removeAttribute("inert");
});
new Promise((resolve) => {
setTimeout(() => {
this.root.style.display = "none";
this.openTrigger.forEach((trigger) => {
trigger.removeAttribute("inert");
});
resolve();
}, this.options.toggleDuration);
}).then(() => {
this.openButton.focus();
});
this.isExpanded = false;
}
backfaceFixed(fixed) {
/**
* 表示されているスクロールバーとの差分を計測し、背面固定時はその差分body要素にボーダーを生成する
*/
const scrollbarWidth = window.innerWidth - document.body.clientWidth;
document.body.style.borderRight = fixed
? `${scrollbarWidth}px solid transparent`
: "";
/**
* 背面固定する対象を決定する
*/
const scrollingElement = () => {
const browser = window.navigator.userAgent.toLowerCase();
// document.scrollingElementが有効なブラウザの場合
if ("scrollingElement" in document) return document.scrollingElement;
// document.scrollingElementが無効なiOSの場合はbody要素を
if (browser.indexOf("webkit") > 0) return document.body;
// その他(IEとか)はhtml要素を
return document.documentElement;
};
/**
* 背面固定時に変数にスクロール量を格納
*/
if (fixed) this.scrollAmount = scrollingElement().scrollTop;
/**
* CSSで背面を固定
*/
const styles = {
height: "100vh",
left: "0",
overflow: "hidden",
position: "fixed",
top: `${this.scrollAmount * -1}px`,
width: "100vw"
};
Object.keys(styles).forEach((key) => {
scrollingElement().style[key] = fixed ? styles[key] : "";
});
/**
* 背面固定解除時に元の位置にスクロールする
*/
if (!fixed) window.scrollTo(0, this.scrollAmount);
}
}
function attachEvent(element, event, handler, options) {
element.addEventListener(event, handler, options);
return {
unsubscribe() {
element.removeEventListener(event, handler);
}
};
}
class PrepareCurrent {
constructor(root, options) {
this.root = root;
if (!this.root) return;
const defaultOptions = {
level: 1,
ignoreSelector: ".js-ignore-current"
};
const mergedOptions = Object.assign(defaultOptions, options);
this.options = mergedOptions;
this.targets = Array.from(
this.root.querySelectorAll(`a:not(${this.options.ignoreSelector})`)
);
this.targets.map((target) => {
this.setCurrentLocation(target, this.options.level);
this.setCurrentPage(target);
});
}
setCurrentLocation(target, level) {
const paths = this.getRootAbsolutePath(
this.convertHrefToRootAbsolutePath(target)
);
const matchedPath = paths.map((path, index) => {
if (index <= level - 1) {
return `${path}/`;
}
return null;
});
const directories = this.getRootAbsolutePath(window.location.pathname);
const matchedDirectory = directories.map((directory, index) => {
if (index <= level - 1) {
return `${directory}/`;
}
return null;
});
const directory = `/${matchedDirectory.join("")}`;
const href = `/${matchedPath.join("")}`;
if (href === directory && href !== "//") {
target.setAttribute("aria-current", "location");
}
}
setCurrentPage(target) {
const path = this.convertHrefToRootAbsolutePath(target);
const locationPathname = window.location.pathname.replace(
/index\.html$/,
""
);
if (path === locationPathname) {
target.setAttribute("aria-current", "page");
}
}
convertHrefToRootAbsolutePath(target) {
const absolutePath =
target instanceof HTMLElement ? target.href : target.href.baseVal;
const protocolHost = `${window.location.protocol}//${window.location.host}`;
return absolutePath.replace(protocolHost, "").replace(/index\.html$/, "");
}
getRootAbsolutePath(absolutePath) {
return absolutePath
.replace(/\\/g, "/")
.replace(/\/[^/]*$/, "")
.slice(1)
.split("/");
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/stickyfill/2.1.0/stickyfill.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/focus-visible@5.2.0/dist/focus-visible.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/wicg-inert@3.1.0/dist/inert.min.js"></script>
// z-indexをmap化して管理する
$z-index: (global-header, drawer-menu);
@function z-index($module) {
@return index($z-index, $module);
}
html {
box-sizing: border-box;
font-size: 75%;
-webkit-font-smoothing: subpixel-antialiased;
-moz-osx-font-smoothing: auto;
@media screen and (min-width: 768px) {
font-size: 87.5%;
}
@media screen and (min-width: 1200px) {
font-size: 100%;
}
}
:root {
--base-color: #434a56;
--white-color-primary: #f7f8f8;
--gray-color-primary: #676f79;
--gray-color-secondary: #e2e2e2;
--gray-color-tertiary: #aaa;
--active-color: #006e9b;
--header-height: 52px;
@media screen and (min-width: 768px) {
--header-height: 56px;
}
@media screen and (min-width: 1200px) {
--header-height: 60px;
}
}
*,
*::before,
*::after {
box-sizing: inherit;
margin: 0;
}
body {
background-color: var(--white-color-primary);
color: var(--base-color);
font-family: "Helvetica Neue", "Segoe UI", "Hiragino Sans",
"Hiragino Kaku Gothic ProN", Meiryo, sans-serif;
font-size: 1em;
line-height: 1.5;
}
.menu-button {
appearance: none;
background-color: var(--base-color);
border: none;
box-shadow: 0 0 12px rgba(0, 0, 0, 0.15);
cursor: pointer;
height: var(--header-height);
padding: 0;
position: relative;
transition: background-color 0.3s;
width: var(--header-height);
&.focus-visible {
background-color: var(--gray-color-primary);
}
@media (hover) {
&:hover {
background-color: var(--gray-color-primary);
}
}
}
.menu-button__icon {
background-color: var(--white-color-primary);
bottom: 0;
height: 2px;
left: 0;
margin: auto;
position: absolute;
right: 0;
top: 0;
width: 18px;
&::before,
&::after {
background-color: #f7f7f7;
content: "";
display: inline-block;
height: 100%;
left: 0;
position: absolute;
top: 0;
transition: transform 0.3s;
width: 100%;
}
&.-open {
&::before {
transform: translateY(-6px);
}
&::after {
transform: translateY(6px);
}
}
&.-close {
background-color: transparent;
&::before {
background-color: #f7f7f7;
transform: translateY(0) rotate(45deg);
}
&::after {
background-color: #f7f7f7;
transform: translateY(0) rotate(-45deg);
}
}
}
.drawer-menu {
height: 100%;
left: 0;
overflow: hidden;
position: fixed;
top: 0;
width: 100%;
z-index: z-index(drawer-menu);
}
.drawer-menu__overlay {
animation-duration: var(--menu-toggle-duration);
animation-fill-mode: forwards;
background-color: rgba(0, 0, 0, 0.1);
height: 100%;
left: 0;
position: absolute;
top: 0;
width: 100%;
z-index: -1;
.drawer-menu:not([inert]) & {
animation-name: menu-overlay-appeared;
}
.drawer-menu[inert] & {
animation-name: menu-overlay-leaved;
}
}
@keyframes menu-overlay-appeared {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
@keyframes menu-overlay-leaved {
0% {
opacity: 1;
}
100% {
opacity: 0;
}
}
.drawer-menu__container {
animation-duration: var(--menu-toggle-duration);
animation-fill-mode: forwards;
background-color: var(--white-color-primary);
border-left: var(--gray-color-secondary) 1px solid;
box-shadow: 0 0 5px rgba(0, 0, 0, 0.1);
display: flex;
flex-direction: column;
height: 100%;
max-width: 440px;
min-width: 280px;
overflow: hidden;
position: absolute;
right: 0;
top: 0;
width: 80%;
.drawer-menu[inert] & {
animation-name: menu-container-leaved;
}
.drawer-menu:not([inert]) & {
animation-name: menu-container-appeared;
}
}
@keyframes menu-container-appeared {
0% {
transform: translateX(100%);
}
100% {
transform: translateX(0);
}
}
@keyframes menu-container-leaved {
0% {
transform: translateX(0);
}
100% {
transform: translateX(100%);
}
}
.drawer-menu__list {
flex: 1;
list-style: none;
margin: 0;
overflow-y: auto;
padding: 0;
}
.drawer-menu__item {
border-bottom: var(--gray-color-secondary) 1px dashed;
}
.drawer-menu__link {
align-items: center;
color: inherit;
display: block;
justify-content: space-between;
letter-spacing: 0.01em;
padding: 1em 2.5em 1em 2em;
position: relative;
text-decoration: none;
transition: background-color 0.3s;
&::after {
border-right: 2px solid var(--gray-color-tertiary);
border-top: 2px solid var(--gray-color-tertiary);
bottom: 0;
content: "";
display: inline-block;
height: max(8px, 0.5em);
margin: auto 0;
position: absolute;
right: 24px;
top: 0;
transform: rotate(45deg);
width: max(8px, 0.5em);
}
&:focus {
background-color: rgba(0, 0, 0, 0.05);
}
&[aria-current] {
background-color: rgba(0, 0, 0, 0.1);
&::after {
content: none;
}
}
@media (hover) {
&:hover {
background-color: rgba(0, 0, 0, 0.05);
}
}
}
.drawer-menu__en-label {
display: block;
font-family: "Montserrat", sans-serif;
font-weight: 500;
text-transform: uppercase;
}
.drawer-menu__jp-label {
color: var(--gray-color-tertiary);
display: block;
font-size: max(10px, 0.75em);
}
.drawer-menu__close-button {
border-bottom: var(--gray-color-secondary) 1px solid;
order: -1;
text-align: right;
}
.global-header {
background-color: var(--white-color-primary);
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
height: var(--header-height);
position: sticky;
top: 0;
z-index: z-index(global-header);
}
.global-header__container {
align-items: center;
display: flex;
justify-content: space-between;
&::before {
content: "";
display: inline-block;
width: var(--header-height);
}
}
.logo {
font-family: "Lobster", cursive;
font-size: 20px; // 拡大しないようにあえてのpx指定
white-space: nowrap;
}
.main-content {
overflow-x: hidden;
padding: 4.5em 5%;
}
.article {
font-size: 1rem;
margin: auto;
max-width: 1024px;
}
.article__title {
font-size: 1.778em;
font-weight: normal;
margin: 0;
}
.article__sentence {
line-height: 1.75;
margin-top: 1.5em;
& > p + p {
margin-top: 1em;
}
}
.article__section {
margin-top: 2em;
}
.article__subtitle {
border-bottom: 2px solid var(--gray-color-secondary);
font-size: 1.333em;
font-weight: normal;
padding: 0.2em 0;
}
.article__list {
display: table;
margin-top: 1.5em;
padding: 0;
}
.article__list-item {
display: flex;
& + & {
margin-top: 0.5em;
}
&::before {
background-position: center center;
background-repeat: no-repeat;
background-size: contain;
content: "";
display: inline-block;
flex-shrink: 0;
height: 1.5em;
margin-right: 0.5em;
vertical-align: middle;
width: 1.5em;
}
&.-checked {
&::before {
background-image: url("data:image/svg+xml;charset=utf8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%3E%20%3Cpath%20d%3D%22M9.86%2018a1%201%200%200%201-.73-.32l-4.86-5.17a1%201%200%201%201%201.46-1.37l4.12%204.39%208.41-9.2a1%201%200%201%201%201.48%201.34l-9.14%2010a1%201%200%200%201-.73.33z%22%20fill%3D%22%2344c08a%22%2F%3E%3C%2Fsvg%3E");
}
}
&.-not-checked {
&::before {
background-image: url("data:image/svg+xml;charset=utf8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%3E%20%3Cpath%20d%3D%22M13.41%2012l4.3-4.29a1%201%200%201%200-1.42-1.42L12%2010.59l-4.29-4.3a1%201%200%200%200-1.42%201.42l4.3%204.29-4.3%204.29a1%201%200%200%200%200%201.42%201%201%200%200%200%201.42%200l4.29-4.3%204.29%204.3a1%201%200%200%200%201.42%200%201%201%200%200%200%200-1.42z%22%20fill%3D%22%23f72f47%22%20%2F%3E%3C%2Fsvg%3E");
}
}
&.-link {
&::before {
background-image: url("data:image/svg+xml;charset=utf8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224px%22%20height%3D%2224px%22%20viewBox%3D%220%200%2024%2024%22%20stroke%3D%22%23676f79%22%20stroke-width%3D%221%22%20stroke-linecap%3D%22square%22%20stroke-linejoin%3D%22miter%22%20fill%3D%22none%22%3E%20%3Crect%20width%3D%2213%22%20height%3D%2213%22%20x%3D%223%22%20y%3D%223%22%20%2F%3E%20%3Cpolyline%20points%3D%2216%208%2021%208%2021%2021%208%2021%208%2016%22%20%2F%3E%3C%2Fsvg%3E");
}
}
}
.article__link {
color: var(--active-color);
@media (hover) {
&:hover {
opacity: 0.8;
text-decoration: none;
}
}
}
.js-focus-visible :focus:not(.focus-visible) {
outline: 0;
}
[inert] {
cursor: default;
pointer-events: none;
}
[inert],
[inert] * {
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
<link href="https://fonts.googleapis.com/css2?family=Lobster&amp;family=Montserrat:wght@500&amp;display=swap" rel="stylesheet" />
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment