Raccourci clavier : Ctrl + K
Commencer

Infobulle

Une courte description de l'élément sous le pointeur, ou sous le focus clavier. C'est une description et jamais un conteneur : rien à l'intérieur ne peut être manipulé.

Utilisation

vue
<script setup lang="ts">
import { VButton, VTooltip } from 'vectis-ui'
</script>

<template>
  <VTooltip text="Copy to the clipboard">
    <template #default="{ triggerProps }">
      <VButton variant="outline" tone="neutral" v-bind="triggerProps">Copy</VButton>
    </template>
  </VTooltip>
</template>

Exemples

Placements

placement propose huit côtés, dont quatre alignés sur un bord du déclencheur plutôt que centrés dessus. Il nomme une préférence : le navigateur déplace le panneau quand le côté demandé manque de place.

vue
<script setup lang="ts">
import { VButton, VTooltip, type TooltipPlacement } from 'vectis-ui'

const placements: TooltipPlacement[] = [
  'top',
  'top-start',
  'top-end',
  'bottom',
  'bottom-start',
  'bottom-end',
  'left',
  'right',
]
</script>

<template>
  <!-- Eight sides, four of them aligned to an edge of the trigger rather than centred
       on it. The placement names a preference and not a position: with no room on that
       side the browser moves the panel by itself. -->
  <div class="row">
    <VTooltip
      v-for="placement in placements"
      :key="placement"
      :text="`Placed ${placement}`"
      :placement="placement"
    >
      <template #default="{ triggerProps }">
        <VButton variant="outline" tone="neutral" v-bind="triggerProps">{{ placement }}</VButton>
      </template>
    </VTooltip>
  </div>
</template>

<style scoped>
.row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--vectis-space-6) var(--vectis-space-4);
  /* Room above and below for the panels, which are drawn outside this box. */
  padding-block: var(--vectis-space-8);
}
</style>

Au bord de l'écran

Faute de place du côté demandé, le panneau prend le côté opposé. C'est du CSS pur, sans mesure ni observateur.

vue
<script setup lang="ts">
import { VButton, VTooltip } from 'vectis-ui'
</script>

<template>
  <!-- The flip is pure CSS and costs no JavaScript: the panel names the side it would
       rather be on, and the browser falls back to the opposite one when the viewport
       has no room there. Scroll these buttons up against the top and the bottom of the
       window to see it happen. -->
  <div class="demo">
    <VTooltip text="Asks for the top, takes the bottom near the edge" placement="top">
      <template #default="{ triggerProps }">
        <VButton variant="outline" tone="neutral" v-bind="triggerProps"> Prefers the top </VButton>
      </template>
    </VTooltip>

    <VTooltip text="Asks for the bottom, takes the top near the edge" placement="bottom">
      <template #default="{ triggerProps }">
        <VButton variant="outline" tone="neutral" v-bind="triggerProps">
          Prefers the bottom
        </VButton>
      </template>
    </VTooltip>
  </div>
</template>

<style scoped>
.demo {
  display: flex;
  flex-wrap: wrap;
  gap: var(--vectis-space-4);
}
</style>

Ouverture et fermeture

delay est le temps pendant lequel le pointeur doit reposer sur le déclencheur avant que l'infobulle apparaisse, 300ms par défaut, et 0 supprime l'attente. Le focus clavier l'ouvre aussitôt, Échap la ferme sans retirer le focus, et appuyer sur le déclencheur la ferme aussi.

vue
<script setup lang="ts">
import { VButton, VTooltip } from 'vectis-ui'
</script>

<template>
  <div class="demo">
    <!-- The pointer has to rest on the trigger before the tooltip appears, 300ms by
         default, so passing over something on the way somewhere else opens nothing.
         Keyboard focus opens it at once instead: the intent is not in doubt there. -->
    <VTooltip text="Appears after the default wait">
      <template #default="{ triggerProps }">
        <VButton variant="outline" tone="neutral" v-bind="triggerProps">300ms</VButton>
      </template>
    </VTooltip>

    <VTooltip text="Appears after a longer wait" :delay="800">
      <template #default="{ triggerProps }">
        <VButton variant="outline" tone="neutral" v-bind="triggerProps">800ms</VButton>
      </template>
    </VTooltip>

    <!-- A delay of 0 disables the wait entirely. -->
    <VTooltip text="Appears at once" :delay="0">
      <template #default="{ triggerProps }">
        <VButton variant="outline" tone="neutral" v-bind="triggerProps">No wait</VButton>
      </template>
    </VTooltip>

    <!-- Tab to any of these and the tooltip opens immediately; Escape closes it
         without taking the focus away, and pressing the trigger closes it too. -->
  </div>
</template>

<style scoped>
.demo {
  display: flex;
  flex-wrap: wrap;
  gap: var(--vectis-space-4);
}
</style>

Décrire, pas nommer

L'infobulle pose aria-describedby sur son déclencheur, qui garde son propre nom accessible : un bouton icône porte toujours son label. Une tape n'ouvre rien, ce que dit l'infobulle doit donc exister ailleurs aussi.

vue
<script setup lang="ts">
import { VIconButton, VTooltip } from 'vectis-ui'
import { close, description, search } from 'vectis-ui/icons'
</script>

<template>
  <div class="demo">
    <!-- The tooltip DESCRIBES and never names: it sets `aria-describedby` on the
         trigger, and the trigger keeps its own accessible name. An icon button
         therefore still carries its `label`, and the tooltip is the visual
         confirmation of it rather than a replacement. -->
    <VTooltip text="Search everything">
      <template #default="{ triggerProps }">
        <VIconButton
          v-bind="triggerProps"
          :icon="search"
          label="Search"
          variant="outline"
          tone="neutral"
        />
      </template>
    </VTooltip>

    <VTooltip text="Export the current view as a CSV file">
      <template #default="{ triggerProps }">
        <VIconButton
          v-bind="triggerProps"
          :icon="description"
          label="Export"
          variant="outline"
          tone="neutral"
        />
      </template>
    </VTooltip>

    <!-- A tap opens nothing, there being no hover on a touch screen and no room for a
         panel standing over what the tap just opened. So whatever the tooltip says has
         to exist somewhere else too: a visible label, a field hint, a panel. -->
    <VTooltip text="Discard the draft">
      <template #default="{ triggerProps }">
        <VIconButton
          v-bind="triggerProps"
          :icon="close"
          label="Discard"
          variant="outline"
          tone="danger"
        />
      </template>
    </VTooltip>
  </div>
</template>

<style scoped>
.demo {
  display: flex;
  flex-wrap: wrap;
  gap: var(--vectis-space-4);
}
</style>

Contenu riche

Le slot #content l'emporte sur la prop text quand les deux sont donnés. Il doit rester non interactif : rien à l'intérieur n'est atteignable au clavier, et la description est aplatie en texte simple pour un lecteur d'écran.

vue
<script setup lang="ts">
import { VButton, VHotkeys, VTooltip, VTypography } from 'vectis-ui'
</script>

<template>
  <!-- The slot wins over the `text` prop when both are given. It has to stay
       non-interactive: the tooltip closes as soon as the pointer leaves the trigger,
       so a link inside could never be reached, and the description is flattened to
       plain text for a screen reader anyway. Anything one can act on belongs in a
       panel that stays open. -->
  <VTooltip placement="bottom-start">
    <template #default="{ triggerProps }">
      <VButton variant="outline" tone="neutral" v-bind="triggerProps">Search</VButton>
    </template>

    <template #content>
      <div class="content">
        <VTypography variant="label" as="strong">Global search</VTypography>
        <span>Looks through projects, files and people.</span>
        <!-- The panel is an inverse surface, dark in both themes, and VHotkeys paints
             itself from the colour it inherits: the caps follow it with nothing to
             set. -->
        <span class="shortcut">
          Shortcut
          <VHotkeys keys="mod+k" variant="outline" />
        </span>
      </div>
    </template>
  </VTooltip>
</template>

<style scoped>
.content {
  display: grid;
  gap: var(--vectis-space-1);
  padding-block: var(--vectis-space-1);
}
.shortcut {
  display: flex;
  align-items: center;
  gap: var(--vectis-space-2);
}
</style>

API

Props

PropTypeDéfaut
textstringaucune
Ce que dit l'infobulle. Le slot #content la remplace quand les deux sont donnés.
placementTooltipPlacement'top' | 'top-start' | 'top-end' | 'bottom' | 'bottom-start' | 'bottom-end' | 'left' | 'left-start' | 'left-end' | 'right' | 'right-start' | 'right-end''top'
De quel côté de l'élément l'infobulle apparaît. Le navigateur la bascule de lui-même du côté opposé quand la place manque.
delaynumber300
Combien de temps le pointeur doit rester sur l'élément avant que l'infobulle apparaisse, en millisecondes. Le focus clavier l'ouvre immédiatement, l'intention n'y faisant aucun doute, et un délai de 0 supprime entièrement l'attente.

Slots

SlotType
default{ triggerProps: TooltipTriggerProps; }
L'élément que l'infobulle décrit. Liez les triggerProps qu'il reçoit dessus, ce qui relie les deux pour les technologies d'assistance, et assurez-vous qu'il puisse prendre le focus, sans quoi les utilisateurs au clavier ne verront jamais l'infobulle.
content{}
Un contenu plus riche qu'une simple chaîne : une mise en forme, un raccourci clavier, une icône. Il doit rester non interactif : la description est aplatie en texte brut pour les lecteurs d'écran, et rien à l'intérieur n'est atteignable au clavier. Un contenu manipulable appartient à un panneau qui reste ouvert, comme VMenu.

Types

Les types que les tables ci-dessus nomment, écrits comme la librairie les déclare. Ceux qui portent export s'importent depuis vectis-ui pour typer votre propre code ; les autres décrivent la forme de ce qu'un slot fournit.

export type TooltipTriggerProps = {
  'aria-describedby': string
}

Variables CSS

TokenValeur
--vectis-control-size-tooltip-max18rem