GitHub

Tooltip

Hover and focus tooltip. Wrap a trigger in the default slot and set text. It shows after delay ms (default 200) and hides on leave or blur.

Stable wc import '@esa/ecology/esa-tooltip'; Angular usage

Preview

<esa-tooltip text="Tooltip above" position="above">
  <button>Hover me</button>
</esa-tooltip>
import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import '@esa/ecology/esa-tooltip';

@Component({
  selector: 'app-tooltip-demo',
  standalone: true,
  schemas: [CUSTOM_ELEMENTS_SCHEMA],
  template: `
    <esa-tooltip text="Tooltip above" position="above">
      <button>Hover me</button>
    </esa-tooltip>
  `,
})
export class TooltipDemo {}

Positions

<esa-tooltip text="Tooltip above" position="above">…</esa-tooltip>
<esa-tooltip text="Tooltip below" position="below">…</esa-tooltip>
<esa-tooltip text="Left side" position="left">…</esa-tooltip>
<esa-tooltip text="Right side" position="right">…</esa-tooltip>
import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import '@esa/ecology/esa-tooltip';

@Component({
  selector: 'app-tooltip-demo',
  standalone: true,
  schemas: [CUSTOM_ELEMENTS_SCHEMA],
  template: `
    <esa-tooltip text="Tooltip above" position="above">…</esa-tooltip>
    <esa-tooltip text="Tooltip below" position="below">…</esa-tooltip>
    <esa-tooltip text="Left side" position="left">…</esa-tooltip>
    <esa-tooltip text="Right side" position="right">…</esa-tooltip>
  `,
})
export class TooltipDemo {}

Focusable trigger

Any focusable element works — the tooltip shows on focusin as well as hover, so keyboard users get it too.

<esa-tooltip text="Shows on focus too" position="above">
  <a href="#">Tab to me</a>
</esa-tooltip>
import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import '@esa/ecology/esa-tooltip';

@Component({
  selector: 'app-tooltip-demo',
  standalone: true,
  schemas: [CUSTOM_ELEMENTS_SCHEMA],
  template: `
    <esa-tooltip text="Shows on focus too" position="above">
      <a href="#">Tab to me</a>
    </esa-tooltip>
  `,
})
export class TooltipDemo {}

API

PropTypeDefaultDescription
text string '' Tooltip label. The tooltip does not render when empty.
position 'above' | 'below' | 'left' | 'right' 'above' Side the tooltip appears on.
align 'center' | 'start' | 'end' 'center'
delay number 200 Milliseconds to wait before showing on hover/focus.
open boolean false Reflected open state.

Accessibility

  • The bubble is role="tooltip" and is pointer-events: none so it never intercepts clicks.
  • Shows on both hover and focus, so keyboard users see the same hint as pointer users.
  • Tooltips supplement — they should not be the only place critical information lives.

Theming surface

Every public token this component reads, extracted from its source at build time, split by blast radius: the hooks that belong to this component, then the shared and system tokens it consumes alongside everything else. The Resolves to column walks each token's real lineage — component → semantic → primitive → raw value — so you can see exactly where re-pointing it lands. A spoke re-skins by overriding these in its theme-<slug>.css; component internals are never edited. No hook for what you need? File it with /request-lego.

Type roles it names

This component does not set a size, weight, face or tracking of its own — it names a composite, and the composite carries all five. A composite is a class rather than a token, so none of it appears in the tables below; re-theme text by re-pointing the token family, not by editing the component.

RoleRe-pointReach
.typography-label-md
  • --typography-label-md-font-family 'DM Sans', sans-serif via --typography-font-family-sans → --font-family-dm-sans
  • --typography-label-md-font-size clamp(0.75rem, 0.66rem + 0.44vw, 0.9375rem) via --font-size-200
  • --typography-label-md-font-weight 500 via --typography-font-weight-medium → --font-weight-500
  • --typography-label-md-letter-spacing 0.01em via --letter-spacing-default
  • --typography-label-md-line-height 1.6 via --line-height-normal
+33 more components esa-app-bar, esa-breadcrumbs, esa-button-group, esa-button-toggle, esa-card, esa-checkbox-group, esa-color-picker, esa-combobox, esa-command-palette, esa-confirm-dialog, esa-danger-zone, esa-date-picker, esa-dialog, esa-empty-state, esa-entity-search, esa-file-upload, esa-filter-container, esa-form-field, esa-header-nav, esa-input-tag, esa-link-column, esa-page-header, esa-pillbox, esa-popover, esa-progress-bar, esa-radio-group, esa-range-slider, esa-select, esa-side-dialog, esa-sidebar-nav, esa-stat, esa-text-field, esa-textarea
.typography-microcopy-xs-subtle
  • --typography-microcopy-xs-subtle-font-family 'DM Sans', sans-serif via --typography-font-family-sans → --font-family-dm-sans
  • --typography-microcopy-xs-subtle-font-size clamp(0.625rem, 0.56rem + 0.32vw, 0.75rem) via --font-size-100
  • --typography-microcopy-xs-subtle-font-weight 350 via --typography-font-weight-regular → --font-weight-350
  • --typography-microcopy-xs-subtle-letter-spacing 0.01em via --letter-spacing-default
  • --typography-microcopy-xs-subtle-line-height 1 via --line-height-none
+7 more components esa-combobox, esa-date-picker, esa-filter-dropdown, esa-filter-pills, esa-input-tag, esa-select, esa-text-field

Wired to this component

No other component reads these — re-point them and esa-tooltip is the only thing that moves.

Token Tier Resolves to
--tooltip-max-width component 240px

Shared tokens it reads

Owned by a family or by the semantic layer. Still overridable — but scope the override (esa-foo { --form-border-color: … }) unless you mean to move everything that reads it. Reach says what else that is.

Token Reach Resolves to
--animation-enter semantic every component 150ms via --duration-150
--color-background-default-knockout semantic every component #202020 via --color-gray-12
--color-content-default-knockout semantic every component #fcfcfc via --color-gray-1
--elevation-4 semantic every component 0 via --shadow-offset-x
--radius-sm semantic every component 0.25rem via --radius-100
--z-tooltip semantic every component 600
--spacing-150 primitive every component 0.375rem
--spacing-250 primitive every component 0.625rem