Vue3 × TypeScript でミニマルなモーダルを公開した話とライブラリ公開でハマったこと
目次
Vue3 × TypeScript でミニマルなモーダルコンポーネントをライブラリとして公開してみました。
どんなモーダル?
必要最低限の機能を備えたミニマルなモーダルです。必要最低限といってもアクセシビリティには以下の点で配慮しています。
- モーダル要素に role 属性、aria-modal 属性、aria-labelledby 属性、aria-describedby 属性が付与されている
- モーダルを開くと、モーダル内の最初のフォーカス可能な要素に自動でフォーカスされる
- モーダルが開いている間、モーダル以外の要素に aria-hidden 属性が付与される
- モーダルが開いている間、モーダル以外の要素のスクロールが無効化される
- モーダルが開いている間、モーダル内でフォーカスがトラップされる
- Esc キーを押下すると、モーダルが閉じる
- モーダルの外側をクリックすると、モーダルが閉じる
- モーダルを閉じると、モーダルが開く前にフォーカスされていた要素にフォーカスが戻る
使い方は、Github の README.md にて記載しているので、下記から御覧ください。
なんでつくったの?
Vue.js で React Modal のように使えるシンプルなモーダルコンポーネントが欲しかったからです。
React でモーダルコンポーネントを実装する場合は React Modal というライブラリが有名かと思いますが、Vue.js でモーダルコンポーネントを実装しようとした場合に、これだ!というライブラリを見つけることができませんでした。
特に、モーダルが開いている間でも、モーダルの後ろ側の要素にフォーカスがあたってしまったり、アクセシビリティ的な観点が考慮されたものが少なかったです。
ライブラリ公開でハマったこと
ライブラリ公開後、テストのために他の環境でライブラリをインストールしたのですが、CSS ファイルを読み込むことができませんでした。
原因としては、npm にライブラリを公開するときの設定で、CSSファイルが公開されないようになっていたためでした。具体的には、package.json の files
フィールド及び、exports
フィールドを適切に設定する必要がありました。
files フィールド
package.json の files
フィールドは、npm ライブラリがインストールされたときに、どのファイルをnode_modulesにコピーするか を設定します。そのため、公開したいファイル及びディレクトリが、files
フィールドに指定されていない場合、import することができません。
今回は、公開したい css ディレクトリが、 files フィールドに指定されていなかったことが原因で、CSS ファイルの読み込みに失敗していました。以下のように css ディレクトリを files フィールドに指定することで、ライブラリをインストールしたときに css ディレクトリが node_modules にコピーされるようになります。
{
"files": [
"dist",
"css",
"types"
],
}
package.json や LICENSE ファイルは、files フィールドに指定しなくても良いの?と思われるかもしれませんが、これらのファイルは、files
フィールドの設定にかかわらず node_modules にコピーされます。また、 .git ディレクトリや、 package-lock.json などインストール先に影響を与えないファイルは自動で無視されます。
exports フィールド
package.json の exports
フィールドは、Node.js v12.7.0 から利用できるようになったフィールドで、main
フィールドと同じようにエントリーポイントを定義する役割を持ち、パッケージとしてのインターフェースを定義できるものです。
exports フィールドを指定した場合、指定したエントリーポイント以外はモジュールとして非公開となり、読み込めなくなります。 exports フィールドを指定することで、パッケージとしてのインターフェースが明確に定義され、提供側と利用側それぞれが提供・利用するものを安全にやりとりできるようになることがメリットになります。
今回、CSS が読み込めなかったもう一つの原因は、 exports
フィールドにて、CSS のエントリーポイントを公開してなかったことが原因でした。そのため、以下のように exports フィールドを指定することで、公開したライブラリから CSS ファイルを import できるようになりました。
"exports": {
".": {
"import": "./path/to/entry.js",
"require": "./path/to/entry.umd.js"
},
"./css": "./path/to/entry.css"
},
まとめ
久しぶりにライブラリを公開して、exports
フィールドの指定を初めて行ったので結構戸惑いました。package.json はいろいろな指定ができてややこしいですね。。。