nekobatoken

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 はプロダクトコード側で管理するしかできなくて悲しい。

1. 子要素に<a>を期待する

<my-link>
  <a href="https://">テキストリンク</a>
</my-link>

この方法で良いことは::slotted(a)という具合に<a>のスタイル整形も Web Component の Shadow DOM 内のの CSS によって可能なことだ。

ただ、::slottedはとても弱いセレクターなので、グローバル CSS でa {}のスタイルが当てられるだけで簡単に上書きできてしまう。上書きできないような仕組みかもしくは、子要素の<a>に何も付けないでね!という注意書きが必要だ。

2. 親要素に<a>を期待する

<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>

こうする。コンポーネントの二度漬けやで。

4. <a>を使う

もしくは、Web Component である場合はフレームワークのアンカータグの使用を諦めてもらう。 リンクをクリックするとページを読み込み直す。あるがままの挙動に帰るのです。

もちろんリンクタグの使用に制限があるのはプロダクト内遷移のみで、Web サイト外もしくはフレームワーク外への遷移は<a>で良いです。

何で困るのか

汎用的な一つの便利な Web Component を提供する場合はこの問題はおそらく起こらないと思わる。プロダクトもそれがスペシャルな要素であることを認識してくれたのなら、フレームワークが指定するアンカータグを使用する必要もないでしょう。

私は今 Design System というものを Web Component で実装する立場なのでこういうものを作らなければいけないんですが、使うフレームワークが決まっているならその中の規則に沿ってコンポーネントを作ればいいと思います。