or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

advanced-middleware.mdbasic-middleware.mdcore-positioning.mdindex.mdoverflow-detection.md
tile.json

basic-middleware.mddocs/

Basic Middleware

Essential middleware for common positioning scenarios including offset adjustments, flip behavior, and shift behavior to keep floating elements in view.

Capabilities

Offset

Modifies the placement by translating the floating element along specified axes. Useful for adding gaps between reference and floating elements.

/**
 * Modifies the placement by translating the floating element along the
 * specified axes. A number (shorthand for mainAxis or distance), or an axes 
 * configuration object may be passed.
 * 
 * @param options - Offset configuration
 * @returns Middleware object
 */
function offset(options?: OffsetOptions): Middleware;

type OffsetOptions = number | {
  /** Distance along the side of the floating element (gutter/margin) */
  mainAxis?: number;
  /** Distance along the alignment of the floating element (skidding) */
  crossAxis?: number;
  /** Same as crossAxis but for aligned placements only, inverts end alignment */
  alignmentAxis?: number | null;
} | Derivable<number | {
  mainAxis?: number;
  crossAxis?: number;
  alignmentAxis?: number | null;
}>;

Usage Examples:

import { computePosition, offset } from "@floating-ui/core";

// Simple gap of 10px
await computePosition(reference, floating, {
  middleware: [offset(10)],
  platform: domPlatform
});

// Different axes
await computePosition(reference, floating, {
  middleware: [
    offset({
      mainAxis: 10,    // 10px gap from reference
      crossAxis: 5,    // 5px skidding along alignment
      alignmentAxis: -2 // 2px toward opposite edge for aligned placements
    })
  ],
  platform: domPlatform
});

// Dynamic offset based on state
await computePosition(reference, floating, {
  middleware: [
    offset(({ rects }) => ({
      mainAxis: rects.floating.height / 2
    }))
  ],
  platform: domPlatform
});

Flip

Optimizes visibility by flipping the placement to keep it in view when the preferred placement would overflow the clipping boundary. Alternative to autoPlacement.

/**
 * Optimizes the visibility of the floating element by flipping the placement
 * in order to keep it in view when the preferred placement(s) will overflow 
 * the clipping boundary.
 * 
 * @param options - Flip configuration
 * @returns Middleware object
 */
function flip(options?: FlipOptions): Middleware;

interface FlipOptions extends DetectOverflowOptions {
  /** Whether to check main axis overflow for flipping */
  mainAxis?: boolean;
  /** Whether to check cross axis overflow for flipping */
  crossAxis?: boolean | "alignment";
  /** Placements to try if preferred placement doesn't fit */
  fallbackPlacements?: Array<Placement>;
  /** Strategy when no placements fit */
  fallbackStrategy?: "bestFit" | "initialPlacement";
  /** Whether to allow fallback to perpendicular axis */
  fallbackAxisSideDirection?: "none" | "start" | "end";
  /** Whether to flip to opposite alignments if they fit better */
  flipAlignment?: boolean;
}

Usage Examples:

import { computePosition, flip } from "@floating-ui/core";

// Basic flipping
await computePosition(reference, floating, {
  placement: "top",
  middleware: [flip()], // Will try "bottom" if "top" overflows
  platform: domPlatform
});

// Custom fallback sequence
await computePosition(reference, floating, {
  placement: "top",
  middleware: [
    flip({
      fallbackPlacements: ["bottom", "left", "right"]
    })
  ],
  platform: domPlatform
});

// Only check main axis, allow perpendicular fallback
await computePosition(reference, floating, {
  placement: "top",
  middleware: [
    flip({
      mainAxis: true,
      crossAxis: false,
      fallbackAxisSideDirection: "start"
    })
  ],
  platform: domPlatform
});

Shift

Optimizes visibility by shifting the floating element to keep it in view when it would overflow the clipping boundary. Works along the alignment axis.

/**
 * Optimizes the visibility of the floating element by shifting it in order to
 * keep it in view when it will overflow the clipping boundary.
 * 
 * @param options - Shift configuration
 * @returns Middleware object
 */
function shift(options?: ShiftOptions): Middleware;

interface ShiftOptions extends DetectOverflowOptions {
  /** Whether to shift along the main axis */
  mainAxis?: boolean;
  /** Whether to shift along the cross axis */
  crossAxis?: boolean;
  /** Function that limits the shifting to prevent detachment */
  limiter?: {
    fn: (state: MiddlewareState) => Coords;
    options?: any;
  };
}

Usage Examples:

import { computePosition, shift, limitShift } from "@floating-ui/core";

// Basic shifting
await computePosition(reference, floating, {
  placement: "top",
  middleware: [shift()], // Shifts to stay within viewport
  platform: domPlatform
});

// Shift with padding
await computePosition(reference, floating, {
  placement: "top-start",
  middleware: [
    shift({
      padding: 8 // Keep 8px away from viewport edges
    })
  ],
  platform: domPlatform
});

// Shift both axes with limiter
await computePosition(reference, floating, {
  placement: "bottom",
  middleware: [
    shift({
      mainAxis: true,
      crossAxis: true,
      limiter: limitShift() // Prevents shifting too far from reference
    })
  ],
  platform: domPlatform
});

Limit Shift

Built-in limiter for the shift() middleware that prevents excessive shifting that could detach the floating element from its reference.

/**
 * Built-in limiter that will stop shift() at a certain point to prevent
 * the floating element from detaching from the reference element.
 * 
 * @param options - Limit shift configuration  
 * @returns Limiter object for use with shift middleware
 */
function limitShift(options?: LimitShiftOptions): {
  options: any;
  fn: (state: MiddlewareState) => Coords;
};

interface LimitShiftOptions {
  /** Offset when limiting starts */
  offset?: number | {
    mainAxis?: number;
    crossAxis?: number;
  } | ((state: MiddlewareState) => number | {
    mainAxis?: number;
    crossAxis?: number;
  });
  /** Whether to limit main axis shifting */
  mainAxis?: boolean;
  /** Whether to limit cross axis shifting */
  crossAxis?: boolean;
}

Usage Examples:

import { computePosition, shift, limitShift } from "@floating-ui/core";

// Basic limit shift
await computePosition(reference, floating, {
  middleware: [
    shift({
      limiter: limitShift()
    })
  ],
  platform: domPlatform
});

// Custom offset before limiting starts
await computePosition(reference, floating, {
  middleware: [
    shift({
      limiter: limitShift({
        offset: 10, // Allow 10px of shifting before limiting
        mainAxis: true,
        crossAxis: false
      })
    })
  ],
  platform: domPlatform
});

// Dynamic offset based on state
await computePosition(reference, floating, {
  middleware: [
    shift({
      limiter: limitShift({
        offset: ({ rects }) => ({
          mainAxis: rects.reference.width / 4,
          crossAxis: 0
        })
      })
    })
  ],
  platform: domPlatform
});

Common Patterns

Basic Tooltip Positioning

import { computePosition, offset, flip, shift } from "@floating-ui/core";

// Standard tooltip setup
const positionTooltip = async (reference, tooltip) => {
  const { x, y, placement } = await computePosition(reference, tooltip, {
    placement: "top",
    middleware: [
      offset(6),        // 6px gap
      flip(),           // Flip if doesn't fit
      shift({ padding: 8 }) // Shift with 8px padding
    ],
    platform: domPlatform
  });
  
  // Apply positioning
  Object.assign(tooltip.style, {
    left: `${x}px`,
    top: `${y}px`,
  });
};

Dropdown Positioning

import { computePosition, flip, shift, offset } from "@floating-ui/core";

// Dropdown with custom fallback strategy
const positionDropdown = async (trigger, dropdown) => {
  const { x, y, placement } = await computePosition(trigger, dropdown, {
    placement: "bottom-start",
    middleware: [
      offset(4),
      flip({
        fallbackPlacements: ["bottom-end", "top-start", "top-end"],
        fallbackStrategy: "bestFit"
      }),
      shift({ padding: 16 })
    ],
    platform: domPlatform
  });
  
  // Apply positioning
  Object.assign(dropdown.style, {
    left: `${x}px`,
    top: `${y}px`,
  });
};