Modal
dialog要素でモーダルを表示するコンポーネントです。
Overview
↓
Preview
<Modal.OpenBtn modalId="modal-01" className="-bd -px:15 -py:5 -bdrs:10">Open Modal 01</Modal.OpenBtn>
<Modal.Root id="modal-01" aria-labelledby="modal-01-title" p="30"> <Modal.Inner layout="stack" pos="relative" max-sz="m" mx="auto" p="35" bdrs="30" bxsh="30"> <Modal.CloseBtn modalId="modal-01" autofocus pos="absolute" t="0" r="0" z="1" fz="xl" p="10" m="10" /> <Modal.Body layout="stack" g="30" util="trimAll"> <h2 className="-fz:l -fw:bold" id="modal-01-title">Modal Title</h2> <p>Lorem ipsum dolor sit amet. Consectetur adipiscing elit, sed do eiusmod tempor Incididunt ut. Labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut.</p> </Modal.Body> </Modal.Inner></Modal.Root><button class="-bd -px:15 -py:5 -bdrs:10 b--modal_openBtn set--plain -hov:-o" type="button" aria-haspopup="dialog" data-modal-open="modal-01">Open Modal 01</button>
<dialog class="b--modal set--plain -p:30" id="modal-01" aria-labelledby="modal-01-title"> <div class="b--modal_inner l--stack -pos:relative -max-sz:m -mx:auto -p:35 -bdrs:30 -bxsh:30"> <button class="b--modal_closeBtn set--plain -hov:-o -pos:absolute -t:0 -r:0 -z:1 -fz:xl -p:10 -m:10" type="button" data-modal-close="modal-01" autofocus> <svg class="a--icon" aria-hidden="true">...</svg> <span class="u--srOnly">Close</span> </button> <div class="b--modal_body l--stack u--trimAll -g:30"> <h2 class="-fz:l -fw:bold" id="modal-01-title">Modal Title</h2> <p>Lorem ipsum dolor sit amet. Consectetur adipiscing elit, sed do eiusmod tempor Incididunt ut. Labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut.</p> </div> </div></dialog><Modal.Root>にidをつけ、<Modal.OpenBtn> / <Modal.CloseBtn>に同じmodalIdを指定することで、そのモーダルの開閉ボタンになります。
- Modalは
dialogを使用しています。display値を変更しないように注意してください。 ::backdropはアニメーションに問題があるため、dialogをスクリーン幅いっぱいにして背景色をつけて運用する前提で設計しています。- スクリーンリーダーにモーダルの名前が伝わるよう、
<Modal.Root>にはタイトル要素(h2など)を指すaria-labelledbyを指定してください。視覚的なタイトルを持たないモーダルでは、代わりにaria-labelを指定します。
Styles
Modal のベーススタイルは、以下のCSSで定義されています。
_style.css
/*
* dialog で実装.
* Memo: ::backdrop のアニメーションはFirefoxで動かない
*/
@layer lism-block {
.b--modal {
--flow: 0 !important; /* flow直下にきても影響しないように */
--duration: var(--modal-duration, 0.3s);
width: 100%;
height: 100%;
max-width: 100%;
max-height: 100%;
overflow: unset;
background: var(--backdrop-bg, rgb(0 0 0 / 0.5));
backdrop-filter: var(--modal-blur, blur(4px));
transition-duration: var(--duration);
transition-property: opacity;
}
.b--modal::backdrop {
background: none;
}
.b--modal[open] {
display: flex;
flex-direction: column;
justify-content: center;
}
.b--modal_inner {
--offset: 0 0; /* アニメーション用 */
background-color: var(--base);
transition: translate var(--duration);
max-height: 100%;
}
.b--modal_openBtn,
.b--modal_closeBtn {
display: inline-flex;
align-items: center;
}
.b--modal:not([data-is-open]) {
opacity: 0;
}
.b--modal:not([data-is-open]) > .b--modal_inner {
translate: var(--offset);
}
/* 減速設定時はアニメーションを無効化(duration プロップ由来の inline style よりも優先させるため !important) */
@media (prefers-reduced-motion: reduce) {
.b--modal {
--duration: 0s !important;
}
}
} ソースコード全体は GitHub で公開しています。
How to use
@lism-css/ui パッケージでModalとして提供しています。
Import
import { Modal } from '@lism-css/ui/react'; import { Modal } from '@lism-css/ui/astro'; <link href="https://cdn.jsdelivr.net/npm/@lism-css/ui@0.30.0/dist/style.css" rel="stylesheet" /> <script type="module" src="https://cdn.jsdelivr.net/npm/@lism-css/ui@0.30.0/dist/scripts/modal.js"></script> <Modal.Root>, <Modal.Inner>, <Modal.Body>, <Modal.OpenBtn>, <Modal.CloseBtn>が利用できます。
Props
| プロパティ | 説明 |
|---|---|
id |
Modal(b--modal)にはid属性が必要です。 |
<Modal.Root> duration |
モーダルの開閉アニメーションの時間を指定します。--duration変数として出力されます。 |
<Modal.Inner> layout |
__innerのレイアウト用コンポーネントを指定できます。 |
<Modal.Inner> offset |
__innerの非表示時の位置をずらすためのオフセット値 |
<Modal.OpenBtn> modalId |
data-modal-openとして出力されます。モーダルを開くトリガー要素にて、モーダルのidを指定します。 |
<Modal.CloseBtn> modalId |
data-modal-closeとして出力されます。モーダルを閉じるトリガー要素にて、モーダルのidを指定します。 |
<Modal.CloseBtn> icon |
子要素を渡さない場合に表示されるアイコンを指定します。(デフォルト: x) |
<Modal.CloseBtn> srText |
アイコン表示時にスクリーンリーダー向けに出力されるテキストを指定します。デフォルトはCloseのため、日本語サイトではsrText="閉じる"の指定を推奨します。 |
Examples
<Modal.Body>をスクロール可能にする場合は、ov-y="auto"を指定します。常に見せておきたいコンテンツがあれば、その前後に配置します。
コンテンツが長い場合の例
↓
Modal使用例<Modal.OpenBtn modalId="modal-02" className="-bd -px:15 -py:5 -bdrs:10">Open Modal 02</Modal.OpenBtn>
<Modal.Root id="modal-02" aria-labelledby="modal-02-title" isContainer px="30" py="50"> <Modal.Inner layout="stack" max-sz="s" mx="auto" bdrs="20" bxsh="40"> <Flex ai="center" jc="between" py="15" bd-b> <Inline as="h2" id="modal-02-title" fz="l" fw="bold" hl="s" ms="30"> <Fragment>Modal Header</Fragment> </Inline> <Modal.CloseBtn modalId="modal-02" autofocus fz="xl" p="10" mx="15" /> </Flex> <Modal.Body layout="flow" px="30" py="20" ov-y="auto"> <DummyText /> <Box ar="16/9" bgc="base-2" bd /> <DummyText length="l" /> <DummyText length="s" /> <DummyText length="m" /> <Box ar="16/9" bgc="base-2" bd /> <DummyText length="xl" /> </Modal.Body> <Flex jc="end" px="30" py="15" bd-t> <Modal.CloseBtn modalId="modal-02" bgc="text" c="base" hl="s" px="15" py="10" bd="none" bdrs="10"> <Fragment>Cancel</Fragment> </Modal.CloseBtn> </Flex> </Modal.Inner></Modal.Root><button class="-bd -px:15 -py:5 -bdrs:10 b--modal_openBtn set--plain -hov:-o" type="button" aria-haspopup="dialog" data-modal-open="modal-02">Open Modal 02</button>
<dialog class="b--modal set--plain is--container -px:30 -py:50" id="modal-02" aria-labelledby="modal-02-title"> <div class="b--modal_inner l--stack -max-sz:s -mx:auto -bdrs:20 -bxsh:40"> <div class="l--flex -ai:center -jc:between -py:15 -bd-b"> <h2 class="-fz:l -fw:bold -hl:s -ms:30" id="modal-02-title">Modal Header</h2> <button class="b--modal_closeBtn set--plain -hov:-o -fz:xl -p:10 -mx:15" type="button" data-modal-close="modal-02" autofocus> <svg class="a--icon" aria-hidden="true">...</svg> <span class="u--srOnly">Close</span> </button> </div> <div class="b--modal_body l--flow -px:30 -py:20 -ov-y:auto"> <p>...Contents...</p> <div class="l--box -ar:16/9 -bgc:base-2 -bd"></div> <p>...Contents...</p> <p>...Contents...</p> <p>...Contents...</p> <div class="l--box -ar:16/9 -bgc:base-2 -bd"></div> <p>...Contents...</p> </div> <div class="l--flex -jc:end -px:30 -py:15 -bd-t"> <button class="b--modal_closeBtn set--plain -hov:-o -bgc:text -c:base -hl:s -px:15 -py:10 -bd:none -bdrs:10" type="button" data-modal-close="modal-02">Cancel</button> </div> </div></dialog>ドロワーメニューの作成例
モーダル内の同一ページへのリンク(#idなど)をクリックすると、リンク先へ移動しつつモーダルが閉じます。
↓
ドロワーメニューの作成例
import { MenuIcon } from '@lism-css/icons/react';
<Modal.OpenBtn modalId="modal-03" g="5" fz="s"> <Icon icon={MenuIcon} fz="l" /><span>MENU</span></Modal.OpenBtn>
<Modal.Root id="modal-03" aria-label="メニュー"> <Modal.Inner layout="stack" max-w="24rem" h="100%" bxsh="40" offset="-100px 0"> <Flex bd-b ai="center" jc="between" p="20"> <Inline fw="bold">MENU</Inline> <Modal.CloseBtn modalId="modal-03" autofocus fz="xl" p="5" /> </Flex> <Modal.Body ov-y="auto"> <nav aria-label="メインメニュー"> <NavMenu.Root bd-b itemP="1em"> <NavMenu.Item> <NavMenu.Link href="#menu-link01">Menu item 1</NavMenu.Link> </NavMenu.Item> <NavMenu.Item> <NavMenu.Link href="#menu-link02">Menu item 2</NavMenu.Link> </NavMenu.Item> <NavMenu.Item> <NavMenu.Link href="#menu-link03">Menu item 3</NavMenu.Link> </NavMenu.Item> <NavMenu.Item> <NavMenu.Link href="#menu-link04">Menu item 4</NavMenu.Link> </NavMenu.Item> </NavMenu.Root> </nav> </Modal.Body> </Modal.Inner></Modal.Root><button class="b--modal_openBtn set--plain -hov:-o -g:5 -fz:s" type="button" aria-haspopup="dialog" data-modal-open="modal-03"> <svg class="a--icon -fz:l" aria-hidden="true">...</svg> <span>MENU</span></button>
<dialog class="b--modal set--plain" id="modal-03" aria-label="メニュー"> <div class="b--modal_inner l--stack -max-w -h:100% -bxsh:40" style="--offset:-100px 0;--max-w:24rem"> <div class="l--flex -bd-b -ai:center -jc:between -p:20"> <span class="-fw:bold">MENU</span> <button class="b--modal_closeBtn set--plain -hov:-o -fz:xl -p:5" type="button" data-modal-close="modal-03" autofocus> <svg class="a--icon" aria-hidden="true">...</svg> <span class="u--srOnly">Close</span> </button> </div> <div class="b--modal_body -ov-y:auto"> <nav aria-label="メインメニュー"> <ul class="b--navMenu -bd-b" style="--item-p:1em"> <li class="b--navMenu_item"> <a class="b--navMenu_link -hov:-bgc" href="#menu-link01">Menu item 1</a> </li> <li class="b--navMenu_item"> <a class="b--navMenu_link -hov:-bgc" href="#menu-link02">Menu item 2</a> </li> <li class="b--navMenu_item"> <a class="b--navMenu_link -hov:-bgc" href="#menu-link03">Menu item 3</a> </li> <li class="b--navMenu_item"> <a class="b--navMenu_link -hov:-bgc" href="#menu-link04">Menu item 4</a> </li> </ul> </nav> </div> </div></dialog>