Skip to content

Instantly share code, notes, and snippets.

@tak-dcxi
Created January 14, 2021 23:38
Show Gist options
  • Save tak-dcxi/078c40e601acd4a40320d2648c8f7239 to your computer and use it in GitHub Desktop.
Save tak-dcxi/078c40e601acd4a40320d2648c8f7239 to your computer and use it in GitHub Desktop.
Drawer Menu Pattern 1
<div class="website">
<header id="js-website-header" class="website__header">
<div class="global-header">
<div class="global-header__title">
<p class="logo">My Drawer Menu</p>
</div>
<div class="global-header__button">
<button id="js-menu-button" class="menu-button" type="button" aria-label="メニュー">
<span class="menu-button__icon"></span>
</button>
</div>
</div>
<!-- /.navbar -->
<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" tabindex="-1">メニュー</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>
<div class="drawer-menu__overlay"></div>
</nav>
<!-- /.modal-menu -->
</header>
<!-- /.website__header -->
<main class="website__main" data-inert-target>
<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">メニュー本体とボタンの紐付けを行う(aria-controls)</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>
<!-- /.article -->
</main>
<!-- /.website__main -->
</div>
<!-- /.website -->
window.addEventListener("DOMContentLoaded", () => {
const menuContent = document.getElementById("js-menu-content");
const menuButton = document.getElementById("js-menu-button");
new ModalMenu(menuContent, menuButton);
new PrepareCurrent(menuContent);
});
class ModalMenu {
constructor(menu, button, options) {
this.modalMenu = menu;
if (!this.modalMenu)
throw TypeError("指定したモーダルメニュー本体のセレクタが見つかりません");
this.menuButton = button;
if (!this.menuButton)
throw TypeError("指定したメニューボタンのセレクタが見つかりません");
const defaultOptions = {
closeTriggerSelector: "[data-menu-close-trigger]", // ボタンとは別にクリック時にメニューを閉じるトリガーとなるセレクタ
inertTarget: "[data-inert-target]", // 開いている時はこのセレクタを読み上げ対象外にする
labelSelector: "#js-menu-label", // 支援技術向けのメニュー本体のラベルとなるidセレクタ
hideLabelSelector: true, //ニューのラベルとなる要素を視覚的に非表示にするか
clickLinkToClose: true, // メニュー内のリンクをクリック時にメニューを閉じるか
toggleDuration: 500 // メニューの遷移時間
};
const mergedOptions = Object.assign(defaultOptions, options);
this.options = mergedOptions;
/**
* ボタンとは別にメニューを閉じるトリガーとなるセレクタの設定
* メニュー内のリンクをクリック時にメニューを閉じる設定をしている場合はメニュー内のa要素をクリック時にもメニューを閉じる
*/
this.closeTrigger = this.options.clickLinkToClose
? document.querySelectorAll(
`${this.options.closeTriggerSelector}, #${this.modalMenu.getAttribute(
"id"
)} a`
)
: document.querySelectorAll(this.options.closeTriggerSelector);
/**
* メニューのラベルとなるセレクタの設定
*/
this.label = document.querySelector(this.options.labelSelector);
if (!this.label) throw TypeError("ラベルとなる見出し要素の指定は必須です");
/**
* メニューオープン時に読み上げおよびに対象外にするセレクタの設定
*/
this.inertTarget = document.querySelectorAll(this.options.inertTarget);
if (!this.inertTarget.length)
throw TypeError("inert対象のセレクタの指定は必須です");
/**
* 状態格納で使用する変数
*/
this.isExpanded = false; // メニューの開閉状態を格納する
this.scrollAmount; // メニューオープン時のスクロール量を格納する
/**
* 初期化時に属性を付与する
*/
this.prepareAttributes();
/**
* イベントリスナーの設定
*/
attachEvent(this.menuButton, "click", this.handleButtonClick.bind(this));
this.closeTrigger.forEach((trigger) => {
attachEvent(trigger, "click", this.handleCloseTriggerClick.bind(this));
});
attachEvent(document, "keyup", this.handleKeyup.bind(this));
}
prepareAttributes() {
this.modalMenu.setAttribute("inert", "");
this.modalMenu.style.display = "none";
this.menuButton.setAttribute(
"aria-controls",
this.modalMenu.getAttribute("id")
);
this.menuButton.setAttribute("aria-expanded", false);
this.menuButton.setAttribute("aria-haspopup", true);
this.label.setAttribute("tabindex", "-1");
document.documentElement.setAttribute("data-modal-menu-opened", false);
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.modalMenu.setAttribute("aria-labelledby", labelId);
}
handleButtonClick(event) {
event.preventDefault;
if (!this.isExpanded) {
this.open();
} else {
this.close();
}
}
handleCloseTriggerClick(event) {
event.preventDefault;
this.close();
}
handleKeyup(event) {
const pressEsc = event.keyCode === 27;
if (this.isExpanded && pressEsc) {
event.preventDefault;
this.close();
}
}
open() {
this.modalMenu.removeAttribute("inert");
this.modalMenu.style.display = "";
this.menuButton.setAttribute("aria-expanded", true);
document.documentElement.setAttribute("data-modal-menu-opened", true);
this.backfaceFixed(true);
this.inertTarget.forEach((element) => {
element.setAttribute("inert", "");
});
setTimeout(() => {
this.label.focus();
}, 100);
this.isExpanded = true;
}
close() {
this.modalMenu.setAttribute("inert", "");
this.menuButton.setAttribute("aria-expanded", false);
this.menuButton.setAttribute("inert", "");
document.documentElement.setAttribute("data-modal-menu-opened", false);
this.backfaceFixed();
this.inertTarget.forEach((element) => {
element.removeAttribute("inert");
});
new Promise((resolve) => {
setTimeout(() => {
this.modalMenu.style.display = "none";
this.menuButton.removeAttribute("inert");
resolve();
}, this.options.toggleDuration);
}).then(() => {
this.menuButton.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>
/**
* Base
*/
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;
--button-size: 40px;
}
*,
*::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;
}
.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;
}
/**
* Menu Button Component
*/
.menu-button {
appearance: none;
background-color: var(--base-color);
border: none;
border-radius: 50%;
box-shadow: 0 0 12px rgba(0, 0, 0, 0.15);
cursor: pointer;
height: 50px;
padding: 0;
position: relative;
transition: background-color 0.3s;
width: 50px;
&.focus-visible {
background-color: var(--gray-color-primary);
}
@media (hover) {
&:hover {
background-color: var(--gray-color-primary);
}
}
@media screen and (min-width: 1200px) {
height: 60px;
width: 60px;
}
}
.menu-button__icon {
background-color: var(--white-color-primary);
bottom: 0;
height: 2px;
left: 0;
margin: auto;
position: absolute;
right: 0;
top: 0;
transition: background-color 0.3s;
width: 16px;
&::before,
&::after {
background-color: var(--white-color-primary);
content: "";
display: inline-block;
height: 100%;
left: 0;
position: absolute;
top: 0;
transition: transform 0.3s;
width: 100%;
}
.menu-button[aria-expanded="false"] & {
&::before {
transform: translateY(-5px);
}
&::after {
transform: translateY(5px);
}
}
.menu-button[aria-expanded="true"] & {
background-color: transparent;
&::before {
background-color: var(--white-color-primary);
transform: translateY(0) rotate(45deg);
}
&::after {
background-color: var(--white-color-primary);
transform: translateY(0) rotate(-45deg);
}
}
}
/**
* Website Layout
*/
.website__header {
position: relative;
z-index: 1;
}
.website__main {
overflow-x: hidden;
padding: 108px 5%;
@media screen and (min-width: 768px) {
padding: 120px 5%;
}
@media screen and (min-width: 1200px) {
padding: 160px 5%;
}
}
/**
* global-header
*/
.global-header {
align-items: center;
display: flex;
justify-content: space-between;
left: 0;
padding: 0 5%;
position: fixed;
top: 20px;
width: 100vw;
z-index: 2;
@media screen and (min-width: 768px) {
top: 32px;
}
@media screen and (min-width: 1200px) {
top: 40px;
}
}
/**
* Modal Menu Component
*/
.drawer-menu {
height: 100%;
left: 0;
overflow: hidden;
position: fixed;
top: 0;
width: 100%;
z-index: 1;
}
.drawer-menu__overlay {
animation-duration: var(--menu-toggle-duration);
animation-fill-mode: forwards;
background-color: rgba(0, 0, 0, 0.9);
height: 100%;
left: 0;
position: absolute;
top: 0;
width: 100%;
z-index: -1;
[data-modal-menu-opened="true"] & {
animation-name: menu-overlay-appeared;
}
[data-modal-menu-opened="false"] & {
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 {
display: flex;
height: 100%;
align-items: center;
justify-content: center;
flex-direction: column;
margin: auto;
overflow: hidden;
padding: 80px 0;
}
.drawer-menu__list {
list-style: none;
margin: 0;
overflow-x: hidden;
overflow-y: auto;
padding: 0;
}
.drawer-menu__item {
position: relative;
& + & {
margin-top: 2em;
}
[data-modal-menu-opened="true"] & {
animation: menu-item-appeared-first;
animation-delay: calc(var(--menu-toggle-duration) - 0.2s);
animation-duration: 0.5s;
animation-fill-mode: both;
animation-timing-function: cubic-bezier(0.19, 1, 0.22, 1);
&::before {
animation-delay: calc(var(--menu-toggle-duration) + 0.3s);
animation-duration: var(--menu-toggle-duration);
animation-fill-mode: both;
animation-name: menu-item-appeared-second;
animation-timing-function: cubic-bezier(0.19, 1, 0.22, 1);
background-color: var(--gray-color-secondary);
content: "";
display: inline-block;
height: 100%;
left: 0;
position: absolute;
top: 0;
transform-origin: bottom;
width: 100%;
z-index: 1;
}
}
[data-modal-menu-opened="false"] & {
animation-duration: var(--menu-toggle-duration);
animation-fill-mode: forwards;
animation-name: menu-item-appeared-leaved;
}
}
@keyframes menu-item-appeared-first {
0% {
transform: translate3d(-100%, 0, 0);
}
100% {
transform: translate3d(0, 0, 0);
}
}
@keyframes menu-item-appeared-second {
0% {
transform: scale3d(1, 1, 1);
}
100% {
transform: scale3d(1, 0, 1);
}
}
@keyframes menu-item-appeared-leaved {
0% {
opacity: 1;
}
100% {
opacity: 0;
}
}
.drawer-menu__link {
align-items: center;
color: inherit;
display: block;
justify-content: space-between;
letter-spacing: 0.01em;
padding: 0 1em;
position: relative;
text-decoration: none;
transition: transform 0.3s;
&[aria-current] {
opacity: 0.5;
}
&:not([aria-current]):focus {
transform: translateX(8px);
}
@media (hover) {
&:not([aria-current]):hover {
transform: translateX(8px);
}
}
}
.drawer-menu__en-label {
color: var(--gray-color-secondary);
display: block;
font-family: "Montserrat", sans-serif;
font-size: 2em;
font-weight: 500;
text-transform: uppercase;
}
.drawer-menu__jp-label {
color: var(--gray-color-tertiary);
display: block;
}
.drawer-menu__close-button {
border-bottom: var(--gray-color-secondary) 1px solid;
order: -1;
text-align: right;
}
/**
* article module
*/
.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;
}
}
}
/**
* component
*/
.logo {
font-family: "Lobster", cursive;
font-size: 20px; // 拡大しないようにあえてのpx指定
white-space: nowrap;
}
<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