CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-popperjs--core

Tooltip and popover positioning engine that automatically calculates optimal placement for UI elements with advanced positioning logic and overflow prevention

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

built-in-modifiers.mddocs/

Built-in Modifiers

Nine built-in modifiers that control positioning behavior including automatic flipping, overflow prevention, offsetting, arrow positioning, and style application.

Capabilities

flip Modifier

Flips the popper to the opposite placement when it would overflow the boundary.

interface FlipOptions {
  /** Check overflow on main axis */
  mainAxis: boolean;
  /** Check overflow on alternate axis */
  altAxis: boolean;
  /** Custom fallback placements to try in order */
  fallbackPlacements: Array<Placement>;
  /** Padding around boundary for overflow detection */
  padding: Padding;
  /** Boundary element(s) or boundary type */
  boundary: Boundary;
  /** Root boundary for overflow checking */
  rootBoundary: RootBoundary;
  /** Use alternate boundary context */
  altBoundary: boolean;
  /** Allow flipping to variation placements */
  flipVariations: boolean;
  /** Allowed auto placements when using 'auto' */
  allowedAutoPlacements: Array<Placement>;
}

type Boundary = Element | Array<Element> | 'clippingParents';
type RootBoundary = 'viewport' | 'document';
type Padding = number | { top?: number; right?: number; bottom?: number; left?: number };

Usage Examples:

import { createPopper } from '@popperjs/core';

// Enable flip with default options
const popper = createPopper(button, tooltip, {
  modifiers: [
    {
      name: 'flip',
      enabled: true,
    },
  ],
});

// Custom flip configuration
const popper = createPopper(button, tooltip, {
  modifiers: [
    {
      name: 'flip',
      options: {
        fallbackPlacements: ['top', 'right', 'bottom'],
        boundary: document.querySelector('.container'),
        padding: 5,
      },
    },
  ],
});

// Disable main axis flipping but allow alt axis
const popper = createPopper(button, tooltip, {
  modifiers: [
    {
      name: 'flip',
      options: {
        mainAxis: false,
        altAxis: true,
      },
    },
  ],
});

preventOverflow Modifier

Prevents the popper from overflowing its boundary by adjusting its position.

interface PreventOverflowOptions {
  /** Prevent overflow on main axis */
  mainAxis: boolean;
  /** Prevent overflow on alternate axis */
  altAxis: boolean;
  /** Boundary for overflow detection */
  boundary: Boundary;
  /** Root boundary context */
  rootBoundary: RootBoundary;
  /** Use alternate boundary context */
  altBoundary: boolean;
  /** Allow popper to overflow to stay near reference */
  tether: boolean;
  /** Offset when tether option activates */
  tetherOffset: TetherOffset;
  /** Padding around boundary */
  padding: Padding;
}

type TetherOffset = 
  | number 
  | { mainAxis: number; altAxis: number }
  | ((args: { popper: Rect; reference: Rect; placement: Placement }) => number | { mainAxis: number; altAxis: number });

Usage Examples:

// Basic overflow prevention
const popper = createPopper(button, tooltip, {
  modifiers: [
    {
      name: 'preventOverflow',
      options: {
        boundary: 'clippingParents',
        padding: 8,
      },
    },
  ],
});

// Tethering to keep popper near reference
const popper = createPopper(button, tooltip, {
  modifiers: [
    {
      name: 'preventOverflow',
      options: {
        tether: true,
        tetherOffset: 10,
      },
    },
  ],
});

// Prevent overflow only on main axis
const popper = createPopper(button, tooltip, {
  modifiers: [
    {
      name: 'preventOverflow',
      options: {
        mainAxis: true,
        altAxis: false,
      },
    },
  ],
});

offset Modifier

Applies offset to the popper position along main and alternate axes.

interface OffsetOptions {
  /** Offset specification as array [skidding, distance] or function */
  offset: OffsetsFunction | [number?, number?];
}

type OffsetsFunction = (args: {
  popper: Rect;
  reference: Rect;
  placement: Placement;
}) => [number?, number?];

Usage Examples:

// Static offset [skidding, distance]
const popper = createPopper(button, tooltip, {
  modifiers: [
    {
      name: 'offset',
      options: {
        offset: [0, 8], // 0 skidding, 8px distance
      },
    },
  ],
});

// Dynamic offset based on context
const popper = createPopper(button, tooltip, {
  modifiers: [
    {
      name: 'offset',
      options: {
        offset: ({ reference, popper, placement }) => {
          // Larger offset for larger references
          const distance = reference.width > 100 ? 12 : 6;
          return [0, distance];
        },
      },
    },
  ],
});

arrow Modifier

Positions an arrow element to point to the reference element.

interface ArrowOptions {
  /** Arrow element or selector */
  element: HTMLElement | string | null;
  /** Padding around arrow positioning */
  padding: Padding | ((args: { popper: Rect; reference: Rect; placement: Placement }) => Padding);
}

Usage Examples:

// Arrow with element reference
const arrowElement = tooltip.querySelector('.arrow');
const popper = createPopper(button, tooltip, {
  modifiers: [
    {
      name: 'arrow',
      options: {
        element: arrowElement,
        padding: 5,
      },
    },
  ],
});

// Arrow with CSS selector
const popper = createPopper(button, tooltip, {
  modifiers: [
    {
      name: 'arrow',
      options: {
        element: '.arrow',
        padding: { left: 10, right: 10 },
      },
    },
  ],
});

// Dynamic arrow padding
const popper = createPopper(button, tooltip, {
  modifiers: [
    {
      name: 'arrow',
      options: {
        element: '.arrow',
        padding: ({ placement }) => placement.startsWith('top') ? 8 : 4,
      },
    },
  ],
});

computeStyles Modifier

Computes CSS styles for positioning the popper element.

interface ComputeStylesOptions {
  /** GPU acceleration using transform3d */
  gpuAcceleration: boolean;
  /** Adaptive positioning that adjusts for device pixel ratio */
  adaptive: boolean;
  /** Round pixel values to avoid blurry rendering */
  roundOffsets: boolean;
}

Usage Examples:

// Disable GPU acceleration
const popper = createPopper(button, tooltip, {
  modifiers: [
    {
      name: 'computeStyles',
      options: {
        gpuAcceleration: false,
      },
    },
  ],
});

// Configure pixel rounding
const popper = createPopper(button, tooltip, {
  modifiers: [
    {
      name: 'computeStyles',
      options: {
        roundOffsets: true,
        adaptive: false,
      },
    },
  ],
});

applyStyles Modifier

Applies computed styles to the popper and arrow elements.

// applyStyles has no configurable options
// It automatically applies styles from computeStyles modifier

Usage Examples:

// applyStyles is included by default in full createPopper
// Disable if you want to handle styles manually
const popper = createPopper(button, tooltip, {
  modifiers: [
    {
      name: 'applyStyles',
      enabled: false,
    },
  ],
});

// Access computed styles from state instead
const styles = popper.state.styles.popper;
Object.assign(tooltip.style, styles);

eventListeners Modifier

Manages scroll and resize event listeners for automatic position updates.

interface EventListenersOptions {
  /** Listen for scroll events */
  scroll: boolean;
  /** Listen for resize events */
  resize: boolean;
}

Usage Examples:

// Disable automatic resize handling
const popper = createPopper(button, tooltip, {
  modifiers: [
    {
      name: 'eventListeners',
      options: {
        resize: false,
        scroll: true,
      },
    },
  ],
});

// Disable all automatic event handling
const popper = createPopper(button, tooltip, {
  modifiers: [
    {
      name: 'eventListeners',
      enabled: false,
    },
  ],
});

hide Modifier

Detects when the reference element is not visible and hides the popper.

// hide modifier has no configurable options
// It sets data-popper-reference-hidden and data-popper-escaped attributes

Usage Examples:

// hide modifier is included by default
// Access visibility state from modifiersData
const hideData = popper.state.modifiersData.hide;
if (hideData?.isReferenceHidden) {
  console.log('Reference element is not visible');
}

// Style based on visibility attributes
const css = `
  [data-popper-reference-hidden] {
    visibility: hidden;
    pointer-events: none;
  }
  
  [data-popper-escaped] {
    opacity: 0.5;
  }
`;

popperOffsets Modifier

Computes the basic positioning offsets for the popper element.

// popperOffsets modifier has no configurable options
// It computes the base x, y coordinates for popper positioning

Usage Examples:

// popperOffsets is a core modifier required by most others
// Access computed offsets from state
const offsets = popper.state.modifiersData.popperOffsets;
console.log('Popper position:', offsets.x, offsets.y);

// This modifier should not be disabled as it's foundational

Modifier System

Modifier Interface

Structure that all modifiers must follow.

interface Modifier<Name, Options> {
  /** Unique name identifier */
  name: Name;
  /** Whether the modifier is enabled */
  enabled: boolean;
  /** Execution phase during update cycle */
  phase: ModifierPhases;
  /** Required modifier names that must run before this one */
  requires?: Array<string>;
  /** Optional modifier names that should run before if present */
  requiresIfExists?: Array<string>;
  /** Main modifier function executed during updates */
  fn: (args: ModifierArguments<Options>) => State | void;
  /** Effect function for setup/cleanup (runs once) */
  effect?: (args: ModifierArguments<Options>) => (() => void) | void;
  /** Default options for the modifier */
  options?: Partial<Options>;
  /** Static data for the modifier */
  data?: any;
}

interface ModifierArguments<Options> {
  state: State;
  instance: Instance;
  options: Partial<Options>;
  name: string;
}

type ModifierPhases = 
  | 'beforeRead' | 'read' | 'afterRead'
  | 'beforeMain' | 'main' | 'afterMain'
  | 'beforeWrite' | 'write' | 'afterWrite';

Usage Examples:

// Custom modifier example
const customModifier = {
  name: 'customOffset',
  enabled: true,
  phase: 'main',
  requires: ['popperOffsets'],
  fn({ state }) {
    // Apply custom offset logic
    state.modifiersData.popperOffsets.y += 10;
  },
};

const popper = createPopper(button, tooltip, {
  modifiers: [customModifier],
});

Install with Tessl CLI

npx tessl i tessl/npm-popperjs--core

docs

built-in-modifiers.md

core-positioning.md

index.md

variants-tree-shaking.md

virtual-elements.md

tile.json