1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

コンポーネントを汚さない、操作ガイドコンポーネントの作り方(Vue 3 + Tailwind CSS)

Last updated at Posted at 2025-12-05

アプリの使い方を説明する操作ガイドを実装したいです。
本物のコンポーネントの見た目を使いながら、特定の要素をハイライトし、ツールチップで操作説明を表示するイメージです。

(↓ フォームコンポーネントに操作ガイドを表示する例)

image.png

ここで実装上の悩みがあります。

  • ガイド内の見た目は対象コンポーネントと同じにしたい
    (コンポーネントを再利用したい)
  • しかし、対象コンポーネントにはガイド用のコードを追加したくない
    (コンポーネントの責務を増やしたくない)

そんなときに操作ガイドを外からセレクタで後付けするアプローチが有効かもしれません。

実装方針

コンポーネントの外側にオーバーレイコンテナを設置し、そこにハイライトとツールチップを描画します。

  1. 対象のコンポーネントをそのまま表示する
  2. セレクタでターゲット要素を指定する
  3. Tailwind クラスを付与してハイライトする
  4. ツールチップをターゲット要素のまわりに配置する

開発環境:

  • Vue3
  • Tailwind CSS

操作ガイド部分の実装:

<!-- オーバーレイ -->
<GuideOverlay>
  <!-- フォームコンポーネント(操作ガイド用の実装なし) -->
  <MyForm />

  <!-- ハイライト・ツールチップ -->
  <template #guides>
    <GuideSpot
      selector=".name-input"
      highlight-class="ring-4 ring-rose-500 bg-rose-100"
      placement="right"
    >
      <div class="flex items-center gap-2">
        <div class="text-rose-500 text-3xl"></div>
        <div class="bg-rose-500 text-white px-4 py-3 rounded-xl shadow-xl">
          フルネームで入力してください
        </div>
      </div>
    </GuideSpot>

    <GuideSpot
      selector=".submit-btn"
      highlight-class="ring-4 ring-amber-500"
      placement="bottom"
    >
      <div class="flex flex-col items-center gap-1">
        <div class="text-amber-500 text-3xl"></div>
        <div class="bg-amber-500 text-white px-5 py-3 rounded-xl shadow-xl">
          入力が終わったら送信をクリックしてください
        </div>
      </div>
    </GuideSpot>
  </template>
</GuideOverlay>

対象コンポーネント(MyForm)には手を加えずに、外からセレクタで指定してハイライトとツールチップを追加します。

実装方法

2 つのコンポーネントで構成されています。

1. GuideOverlay(コンテナ)

コンテナの参照を provide で子コンポーネントに共有します。

GuideOverlay.vue
<script setup lang="ts">
import { ref, provide } from 'vue';

const containerRef = ref<HTMLElement | null>(null);

provide('guideContainer', containerRef);
</script>

<template>
  <div ref="containerRef" class="relative">
    <div class="pointer-events-none">
      <slot />
    </div>
    <slot name="guides" />
  </div>
</template>

2. GuideSpot(ハイライト&ツールチップ)

セレクタから要素を見つけ、クラスを付与し、ツールチップを配置します。
ツールチップの内容は slot にすることで内容を柔軟にカスタムできます。

GuideSpot.vue
<script setup lang="ts">
import { ref, inject, onMounted, onUnmounted, type Ref } from 'vue';
import { useFloating, offset, flip, autoUpdate, type Placement } from '@floating-ui/vue';

const props = withDefaults(defineProps<{
  selector: string;
  highlightClass?: string;
  placement?: Placement;
}>(), {
  placement: 'top',
});

const targetElement = ref<Element | null>(null);
const tooltipRef = ref<HTMLElement | null>(null);

const { floatingStyles } = useFloating(targetElement, tooltipRef, {
  placement: props.placement,
  middleware: [
    offset(12),
    flip(),
  ],
  whileElementsMounted: autoUpdate,
});

const containerRef = inject<Ref<HTMLElement | null>>('guideContainer');
const addedClasses = ref<string[]>([]);

onMounted(() => {
  const target = containerRef?.value?.querySelector(props.selector);
  if (target && props.highlightClass) {
    const classes = props.highlightClass.split(/\s+/).filter(Boolean);
    target.classList.add(...classes);
    addedClasses.value = classes;
  }
  targetElement.value = target ?? null;
});

onUnmounted(() => {
  if (targetElement.value && addedClasses.value.length > 0) {
    targetElement.value.classList.remove(...addedClasses.value);
  }
});
</script>

<template>
  <div
    v-if="targetElement"
    ref="tooltipRef"
    class="absolute z-50"
    :style="floatingStyles"
  >
    <slot />
  </div>
</template>

※ 要素の配置には floating-ui を利用しています。

ポイント

対象のコンポーネントは変更せずにそのまま表示するだけで OK です。
ガイド機能を外付けできており、対象のフォームコンポーネントをシンプルに保つことができます。

まとめ

操作ガイドを実装するために以下の方針を取りました。

  • セレクタで後付け … コンポーネントを汚さずに済む
  • クラス付与でハイライト ... Tailwind で自由にスタイリングできる
  • スロットを使った実装 ... 操作ガイドやツールチップの内容を柔軟にカスタマイズできる

もし何かの参考になれば幸いです。もっとよい方法があれば、ぜひ教えてください!

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?