CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-tippy-js

The complete tooltip, popover, dropdown, and menu solution for the web

Pending
Overview
Eval results
Files

singleton-tooltips.mddocs/

Singleton Tooltips

Singleton system for creating shared tooltips that can be displayed across multiple reference elements with smooth transitions and centralized management.

Capabilities

Create Singleton

Creates a single tooltip instance that can be shared across multiple reference elements, providing smooth transitions and consistent presentation.

/**
 * Creates a singleton tooltip that can be shared across multiple instances
 * @param tippyInstances - Array of existing tippy instances to control
 * @param optionalProps - Optional properties for the singleton
 * @returns Enhanced singleton instance with additional methods
 */
function createSingleton<TProps = Props>(
  tippyInstances: Instance[],
  optionalProps?: Partial<CreateSingletonProps<TProps>>
): CreateSingletonInstance<CreateSingletonProps<TProps>>;

type CreateSingletonProps<TProps = Props> = TProps & {
  /** Properties that can be overridden by individual instances */
  overrides: Array<keyof TProps>;
};

interface CreateSingletonInstance<TProps = CreateSingletonProps> extends Instance<TProps> {
  /** Update the controlled instances */
  setInstances(instances: Instance[]): void;
  /** Show tooltip for specific target */
  show(target?: ReferenceElement | Instance | number): void;
  /** Show tooltip for next instance in sequence */
  showNext(): void;
  /** Show tooltip for previous instance in sequence */
  showPrevious(): void;
}

Usage Examples:

import tippy, { createSingleton } from "tippy.js";

// Create individual instances (initially disabled)
const instances = [
  tippy('#button1', { content: 'Button 1 content' }),
  tippy('#button2', { content: 'Button 2 content' }),
  tippy('#button3', { content: 'Button 3 content' })
];

// Create singleton to control them
const singleton = createSingleton(instances, {
  delay: 500,
  placement: 'top',
  moveTransition: 'transform 0.2s ease-out',
  overrides: ['content', 'placement'] // Allow individual overrides
});

// Singleton automatically shows appropriate content when hovering over any button

Navigation Methods

Built-in navigation methods for programmatically controlling which tooltip is displayed.

interface CreateSingletonInstance<TProps = CreateSingletonProps> {
  /** Show tooltip for specific target by element, instance, or index */
  show(target?: ReferenceElement | Instance | number): void;
  /** Show tooltip for next instance in sequence */
  showNext(): void;
  /** Show tooltip for previous instance in sequence */
  showPrevious(): void;
}

Usage Examples:

import tippy, { createSingleton } from "tippy.js";

const instances = [
  tippy('#step1', { content: 'Step 1: Introduction' }),
  tippy('#step2', { content: 'Step 2: Configuration' }),
  tippy('#step3', { content: 'Step 3: Completion' })
];

const singleton = createSingleton(instances, {
  placement: 'bottom',
  theme: 'tutorial'
});

// Programmatic navigation
function startTutorial() {
  singleton.show(0); // Show first tooltip
}

function nextStep() {
  singleton.showNext(); // Move to next tooltip
}

function previousStep() {
  singleton.showPrevious(); // Move to previous tooltip
}

// Keyboard navigation
document.addEventListener('keydown', (e) => {
  if (e.key === 'ArrowRight') nextStep();
  if (e.key === 'ArrowLeft') previousStep();
  if (e.key === 'Escape') singleton.hide();
});

Instance Management

Dynamic management of controlled instances with the ability to add, remove, or replace instances.

interface CreateSingletonInstance<TProps = CreateSingletonProps> {
  /** Replace all controlled instances with new set */
  setInstances(instances: Instance[]): void;
}

Usage Examples:

import tippy, { createSingleton } from "tippy.js";

// Initial setup
let instances = [
  tippy('#item1', { content: 'Item 1' }),
  tippy('#item2', { content: 'Item 2' })
];

const singleton = createSingleton(instances);

// Add new instances dynamically
function addTooltipTarget(element, content) {
  const newInstance = tippy(element, { content });
  instances.push(newInstance);
  singleton.setInstances(instances); // Update singleton
}

// Remove instances
function removeTooltipTarget(element) {
  instances = instances.filter(instance => instance.reference !== element);
  singleton.setInstances(instances); // Update singleton
}

// Replace all instances
function updateAllTargets(newElements) {
  instances = newElements.map(el => tippy(el, { 
    content: el.getAttribute('data-tooltip') 
  }));
  singleton.setInstances(instances);
}

Property Overrides

Flexible override system allowing individual instances to customize specific properties while sharing common singleton behavior.

type CreateSingletonProps<TProps = Props> = TProps & {
  /** Array of property names that can be overridden by individual instances */
  overrides: Array<keyof TProps>;
};

Usage Examples:

import tippy, { createSingleton } from "tippy.js";

// Create instances with different placements and content
const instances = [
  tippy('#top-button', { 
    content: 'Top button', 
    placement: 'bottom' // Will override singleton
  }),
  tippy('#side-button', { 
    content: 'Side button', 
    placement: 'left' // Will override singleton
  }),
  tippy('#main-button', { 
    content: 'Main button'
    // Will use singleton placement
  })
];

const singleton = createSingleton(instances, {
  delay: 300,
  duration: 200,
  placement: 'top', // Default placement
  theme: 'custom',
  overrides: ['content', 'placement'], // Allow these to be overridden
  // delay, duration, theme cannot be overridden
});

// Individual instances can override content and placement
// but share delay, duration, and theme from singleton

Smooth Transitions

Built-in transition system for smooth movement between tooltip positions when switching targets.

// Singleton props for transition control
interface CreateSingletonProps {
  /** CSS transition for smooth movement between positions */
  moveTransition: string;
}

Usage Examples:

import tippy, { createSingleton } from "tippy.js";

const instances = [
  tippy('.nav-item', { content: 'Navigation item' })
];

// Custom transition effects
const smoothSingleton = createSingleton(instances, {
  moveTransition: 'transform 0.3s cubic-bezier(0.4, 0, 0.2, 1)',
  placement: 'bottom',
  offset: [0, 8]
});

// Different transition for different contexts
const quickSingleton = createSingleton(instances, {
  moveTransition: 'transform 0.1s ease-out', // Quick transitions
  delay: 100
});

// No transition (instant movement)
const instantSingleton = createSingleton(instances, {
  moveTransition: '', // Disable transitions
});

Advanced Singleton Patterns

Complex singleton usage patterns for specialized use cases.

Usage Examples:

import tippy, { createSingleton } from "tippy.js";

// Contextual help system
function createHelpSystem(containers) {
  const allInstances = [];
  
  containers.forEach(container => {
    const helpElements = container.querySelectorAll('[data-help]');
    const instances = Array.from(helpElements).map(el => 
      tippy(el, {
        content: el.dataset.help,
        placement: el.dataset.placement || 'top'
      })
    );
    allInstances.push(...instances);
  });

  return createSingleton(allInstances, {
    theme: 'help-system',
    delay: [600, 100],
    moveTransition: 'transform 0.2s ease-out',
    overrides: ['content', 'placement'],
    interactive: true,
    allowHTML: true
  });
}

// Image gallery with captions
function createGalleryTooltips(gallery) {
  const images = gallery.querySelectorAll('img[data-caption]');
  const instances = Array.from(images).map(img => 
    tippy(img, {
      content: `
        <div class="image-caption">
          <h4>${img.alt}</h4>
          <p>${img.dataset.caption}</p>
        </div>
      `,
      allowHTML: true,
      placement: 'bottom'
    })
  );

  return createSingleton(instances, {
    theme: 'gallery',
    moveTransition: 'all 0.3s ease',
    maxWidth: 300,
    overrides: ['content']
  });
}

// Keyboard-navigable tooltip tour
function createTooltipTour(steps) {
  const instances = steps.map((step, index) => 
    tippy(step.element, {
      content: `
        <div class="tour-step">
          <div class="step-number">Step ${index + 1} of ${steps.length}</div>
          <h3>${step.title}</h3>
          <p>${step.description}</p>
        </div>
      `,
      allowHTML: true,
      placement: step.placement || 'top'
    })
  );

  const tour = createSingleton(instances, {
    theme: 'tour',
    interactive: true,
    hideOnClick: false,
    trigger: 'manual',
    overrides: ['content', 'placement']
  });

  // Add navigation controls
  let currentStep = 0;
  
  return {
    start() {
      currentStep = 0;
      tour.show(0);
    },
    next() {
      if (currentStep < instances.length - 1) {
        currentStep++;
        tour.show(currentStep);
      }
    },
    previous() {
      if (currentStep > 0) {
        currentStep--;
        tour.show(currentStep);
      }
    },
    end() {
      tour.hide();
    }
  };
}

Install with Tessl CLI

npx tessl i tessl/npm-tippy-js

docs

core-tooltip.md

event-delegation.md

global-utilities.md

index.md

plugins.md

singleton-tooltips.md

tile.json