Web Componentsとフレームワークを組み合わせると、<a>が使いにくい
便利なフレームワークのルータライブラリと衝突する Anchor Tag
コンポーネントの中に<a>
を入れたい時、例えばテキストリンクを Web Component にしたいとする。
<my-link href="https://">テキストリンクだよ</my-link>
Web Component でリンクの挙動を一括で指定できるからとっても便利。
<my-link href="https://">
#shadow-root
<style></style>
<a href="https://" class="css-in-shadow-dom">
<slot></slot>
</a>
</my-link>
しかし View フレームワークと Web Component を組み合わせる場合、フレームワーク内での繊維にはフレームワークが指定したアンカータグを使わなければならないこともある。実際に表示されるのはどちらも<a>
だとしても、元のコード上では指定されたタグを使う必要がある。
Nuxt ではnuxt-link
だし、Next.js ではnext-link
、Vue-Router ではrouter-link
となる。
Shadow DOM の中にnuxt-link
を置いてもそれはレンダリングの際に置換はされないし、<a>
タグを強制するかもしくは Web Component によるリンクの提供を諦めるしかないというのが現状である。そもそも Web Component なのだから特定のフレームワークの中だけで機能するようなデザインにするわけにもいかない。かなしいね。
解決法
基本的に、フレームワークと組み合わせる時は<a>
を Custom Element の中に入れる設計にはできないことは上述の通り。以下はそれをどのように解決、というよりは付き合っていくかという方法になる。
ちなみにどちらも<a>
タグはコンポーネントの外に置くことになるので、 attributes はプロダクトコード側で管理するしかできなくて悲しい。
<a>
を期待する
1. 子要素に<my-link>
<a href="https://">テキストリンク</a>
</my-link>
この方法で良いことは::slotted(a)
という具合に<a>
のスタイル整形も Web Component の Shadow DOM 内のの CSS によって可能なことだ。
ただ、::slotted
はとても弱いセレクターなので、グローバル CSS でa {}
のスタイルが当てられるだけで簡単に上書きできてしまう。上書きできないような仕組みかもしくは、子要素の<a>
に何も付けないでね!という注意書きが必要だ。
<a>
を期待する
2. 親要素に<a href="https://">
<my-link>テキストリンク</my-link>
</a>
この場合<my-link>
が出来るのは自身のテキストスタイルを整形する程度である。<a>
のスタイルは別途グローバルな CSS で指定しなければいけない。なんで君 Web Component になったの...?
ただ、<my-link>
内部のスタイルは1つ目よりは強固になるので、上書きされにくい CSS を求めるなら<a>
のスタイルを極限まで無にした上でこちらのコンポーネントに:hover
や:active
などを付けるという 構造で頑張る方が良いかもしれない。
3. フレームワークのコードも提供する
<my-link>
<nuxt-link class="my-link-inner" :to="https://">テキストリンク</nuxt-link>
</my-link>
これを更にフレームワークのコンポーネントで wrap して
<MyLink href="https://">テキストリンク</MyLink>
こうする。コンポーネントの二度漬けやで。
<a>
を使う
4. もしくは、Web Component である場合はフレームワークのアンカータグの使用を諦めてもらう。 リンクをクリックするとページを読み込み直す。あるがままの挙動に帰るのです。
もちろんリンクタグの使用に制限があるのはプロダクト内遷移のみで、Web サイト外もしくはフレームワーク外への遷移は<a>
で良いです。
何で困るのか
汎用的な一つの便利な Web Component を提供する場合はこの問題はおそらく起こらないと思わる。プロダクトもそれがスペシャルな要素であることを認識してくれたのなら、フレームワークが指定するアンカータグを使用する必要もないでしょう。
私は今 Design System というものを Web Component で実装する立場なのでこういうものを作らなければいけないんですが、使うフレームワークが決まっているならその中の規則に沿ってコンポーネントを作ればいいと思います。