CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-tether

A client-side library to make absolutely positioned elements attach to elements in the page efficiently.

Pending
Overview
Eval results
Files

constraints.mddocs/

Constraints and Boundaries

Advanced positioning constraints to keep elements within specified boundaries and handle viewport clipping. The constraint system prevents elements from being positioned outside visible areas and provides intelligent attachment point flipping.

Capabilities

Constraint Configuration

Define boundaries and behaviors for positioned elements.

interface ConstraintOptions {
  /** Boundary definition - can be a selector, element, or coordinate array */
  to: string | HTMLElement | Array<number>;
  /** How to handle constraint violations - flip attachments */
  attachment?: string;
  /** Whether and how to pin elements to boundaries */
  pin?: boolean | string | Array<string>;
  /** Custom CSS class for out-of-bounds state */
  outOfBoundsClass?: string;
  /** Custom CSS class for pinned state */
  pinnedClass?: string;
}

Usage in Tether Options:

import Tether from "tether";

const tether = new Tether({
  element: '.dropdown',
  target: '.trigger',
  attachment: 'top left',
  targetAttachment: 'bottom left',
  constraints: [
    {
      to: 'scrollParent',
      attachment: 'together',
      pin: true
    },
    {
      to: 'window',
      attachment: 'together',
      pin: ['top', 'left']
    }
  ]
});

Boundary Types

Different types of boundaries for constraint checking.

// String boundary identifiers
type BoundaryIdentifier = 
  | "scrollParent"    // First scrollable ancestor
  | "window"          // Browser viewport
  | string;           // CSS selector

// Element boundary
type ElementBoundary = HTMLElement;

// Coordinate boundary [left, top, right, bottom]
type CoordinateBoundary = [number, number, number, number];

Usage Examples:

// Constrain to viewport
{
  to: 'window',
  attachment: 'together',
  pin: true
}

// Constrain to scrollable parent
{
  to: 'scrollParent',
  attachment: 'both',
  pin: ['top', 'bottom']
}

// Constrain to specific element
{
  to: '.container',
  attachment: 'target',
  pin: true
}

// Constrain to custom coordinates
{
  to: [100, 50, 800, 600], // [left, top, right, bottom]
  attachment: 'together',
  pin: true
}

Attachment Flipping

Control how attachment points change when constraints are violated.

type AttachmentFlipBehavior = 
  | "target"      // Flip target attachment only
  | "element"     // Flip element attachment only
  | "both"        // Flip both attachments independently
  | "together";   // Flip both attachments as a unit

Attachment Behaviors:

  • "target": When element goes out of bounds, flip the target attachment
  • "element": When element goes out of bounds, flip the element attachment
  • "both": Flip target and element attachments independently based on bounds
  • "together": Flip both attachments together maintaining their relationship
// Flip target attachment when constrained
{
  to: 'window',
  attachment: 'target',
  pin: false
}

// Flip both attachments together
{
  to: 'scrollParent',
  attachment: 'together',
  pin: true
}

Pinning Behavior

Control how elements are pinned to constraint boundaries.

type PinConfiguration = 
  | boolean                    // Pin to all sides
  | string                     // Single side or comma-separated sides  
  | Array<string>;             // Array of sides

type PinSide = "top" | "bottom" | "left" | "right";

Usage Examples:

// Pin to all sides
{ pin: true }

// Pin to specific sides
{ pin: 'top,left' }
{ pin: ['top', 'left'] }

// No pinning (allow out-of-bounds)
{ pin: false }

Custom CSS Classes

Override default CSS classes for constraint states.

interface CustomConstraintClasses {
  /** Class applied when element is outside boundaries */
  outOfBoundsClass?: string;
  /** Class applied when element is pinned to boundaries */
  pinnedClass?: string;
}

Usage Example:

const tether = new Tether({
  element: '.menu',
  target: '.button',
  attachment: 'top left',
  targetAttachment: 'bottom left',
  constraints: [
    {
      to: 'window',
      attachment: 'together',
      pin: true,
      outOfBoundsClass: 'menu-overflow',
      pinnedClass: 'menu-pinned'
    }
  ]
});

Constraint Examples

Dropdown Menu Constraints

// Dropdown that flips when near viewport edges
const dropdown = new Tether({
  element: '.dropdown-menu',
  target: '.dropdown-trigger',
  attachment: 'top left',
  targetAttachment: 'bottom left',
  constraints: [
    {
      // Flip attachments together when hitting viewport
      to: 'window',
      attachment: 'together',
      pin: false
    },
    {
      // Pin to scrollable container
      to: 'scrollParent',
      pin: ['top', 'bottom', 'left', 'right']
    }
  ]
});

Tooltip Constraints

// Tooltip that stays within viewport
const tooltip = new Tether({
  element: '.tooltip',
  target: '.help-icon',
  attachment: 'bottom center',
  targetAttachment: 'top center',
  constraints: [
    {
      to: 'window',
      attachment: 'together',
      pin: ['top', 'left', 'right']
    }
  ]
});

Modal Dialog Constraints

// Modal that stays centered in viewport
const modal = new Tether({
  element: '.modal',
  target: 'body',
  attachment: 'middle center',
  targetAttachment: 'middle center',
  constraints: [
    {
      to: 'window',
      pin: ['top', 'left', 'right', 'bottom']
    }
  ]
});

CSS Classes Applied

The constraint system automatically applies CSS classes based on positioning state:

Default Classes

// Out of bounds classes
".tether-out-of-bounds"        // General out-of-bounds state
".tether-out-of-bounds-top"    // Out of bounds on top
".tether-out-of-bounds-bottom" // Out of bounds on bottom  
".tether-out-of-bounds-left"   // Out of bounds on left
".tether-out-of-bounds-right"  // Out of bounds on right

// Pinned classes
".tether-pinned"               // General pinned state
".tether-pinned-top"          // Pinned to top
".tether-pinned-bottom"       // Pinned to bottom
".tether-pinned-left"         // Pinned to left
".tether-pinned-right"        // Pinned to right

Custom Classes

When outOfBoundsClass or pinnedClass are specified, they replace the default classes:

// With custom classes
{
  outOfBoundsClass: 'menu-overflow',
  pinnedClass: 'menu-stuck'
}

// Applied classes become:
".menu-overflow"           // Instead of .tether-out-of-bounds
".menu-overflow-top"       // Instead of .tether-out-of-bounds-top
".menu-stuck"              // Instead of .tether-pinned
".menu-stuck-left"         // Instead of .tether-pinned-left

Additional CSS Classes

The Tether system also applies these additional classes:

// Marker classes (for debugging/development)
".tether-element-marker"   // Marker on positioned element
".tether-target-marker"    // Marker on target element
".tether-marker-dot"       // Visual dot within markers

Install with Tessl CLI

npx tessl i tessl/npm-tether

docs

constraints.md

core-positioning.md

event-system.md

index.md

optimization.md

tile.json