StringMasonry Module

A lightweight, high-performance masonry layout engine. StringMasonry arranges child elements into optimal vertical columns, minimizing gaps and handling dynamic content like images and DOM mutations automatically. It uses transform for movement, ensuring 60fps animations during layout shuffles.

Activation primer: Add string="masonry" to the container element. The module will automatically position all direct children of that container.

HTML Attributes

AttributeTypeDefaultControlsPractical notes
string-masonry-colsbreakpoint-dimension2|640:3|1024:4Number of columns at different breakpointsFormat: val|bp:val|bp:val. E.g., 1|768:2|1200:3
string-masonry-gapbreakpoint-dimension16|640:24|1024:32Gutter size (px) between items and columnsFormat matches cols. Controls both horizontal and vertical spacing.
string-masonry-modestring (auto|manual)autoLayout triggersauto reacts to resize/mutations. manual waits for external events or prop updates.
string-masonry-position-timechild attribute0.6 (from default)Duration (s) for position transitionsAdd to child items, not the container.
string-masonry-position-easingchild attributecubic-bezier(0.25, 1, 0.5, 1)Easing curve for movementAdd to child items.
string-masonry-size-timechild attribute0.6 (from default)Duration (s) for width resizingrarely needed unless column count changes animate width.

Module Snapshot

  • Activation attribute: string="masonry"
  • Layout method: Absolute positioning via translate3d.
  • Responsive: Native breakpoint syntax for columns and gaps.
  • Dynamic: Auto-updates on window resize, image loads, and DOM node addition/removal.
  • Performance: Batches DOM reads/writes; animates layout changes with FLIP-like transitions.
  • Requirements: Children should be display: block (or similar) and will be forced to position: absolute.

Basic Usage

import StringTune, { StringMasonry } from '@fiddle-digital/string-tune';

const stringTune = StringTune.getInstance();
stringTune.use(StringMasonry);
stringTune.start(60);
<div class="grid" string="masonry" string-masonry-cols="1|768:2|1024:3" string-masonry-gap="20|1024:40">
  <div class="card">...</div>
  <div class="card">...</div>
  <div class="card">...</div>
</div>

Advanced: Manual Mode & Event Control

For applications with complex filtering or external state management (e.g., React/Vue lists), use manual mode to precisely control when the layout recalculates.

1. Set Manual Mode

<div string="masonry" string-id="product-grid" string-masonry-mode="manual">...</div>

2. Trigger Updates via Events

Use stringTune.emit() to send commands to specific masonry instances using their string-id.

ChannelPayloadDescription
masonry:update:<id>{ mode?: string, cols?: number, gap?: number }Updates settings and forces a layout shuffle.

Examples:

  • Force a layout refresh (e.g., after filter changes):
// Triggers a layout calculation using current DOM elements
stringTune.emit('masonry:update:product-grid', {});
  • Change columns dynamically:
// Switches to 4 columns and 20px gap, animating items to new positions
stringTune.emit('masonry:update:product-grid', {
  cols: 4,
  gap: 20,
});
  • Switch modes:
stringTune.emit('masonry:update:product-grid', { mode: 'auto' });

Lifecycle Events

Listen to these events to coordinate other animations or UI updates (like fading in a footer after the grid settles).

Channel PatternPayloadFired WhenUse Cases
masonry:shuffle:start{ object: StringObject }The grid detects a change and begins animating items.Disable pointer events, show loader.
masonry:shuffle:end{ object: StringObject }All items have reached their target positions and the container height is final.Enable pointer events, scroll to position, trigger "entry" animations.
resizetrueThe container height changed significantly.Global scroll refresh (handled automatically by StringTune).

Usage Example

// Log when the grid starts moving
stringTune.on('masonry:shuffle:start', ({ object }) => {
  if (object.id === 'product-grid') {
    console.log('Grid is reshuffling...');
    document.body.classList.add('is-animating');
  }
});

// React when it finishes
stringTune.on('masonry:shuffle:end', ({ object }) => {
  if (object.id === 'product-grid') {
    console.log('Grid settled. New height:', object.htmlElement.offsetHeight);
    document.body.classList.remove('is-animating');
  }
});

How It Works

  1. Grid Calculation: computes column width based on container width, column count, and gap settings.
  2. Positioning: iterates through children, placing each item into the shortest available column (top-down, left-to-right filling).
  3. Applies Transforms: calculates translate3d(x, y, 0) for each item.
  4. Container Height: updates the container's height to match the tallest column, ensuring proper document flow below the grid.
  5. Animation: if the layout changes (resize, filter, load), items smoothly interpolate to their new positions using the configured duration and easing.

Tips & Gotchas

  • Images: The module automatically monitors <img> tags inside items. You do not need external libraries like imagesLoaded; the layout will self-correct as images decode and render.
  • CSS: Do not manually set position: absolute or top/left on children in your CSS; the module forces these styles inline.
  • Performance: Transitions use transform and width (only if column width changes). For the smoothest 60fps experience, avoid adding heavy CSS effects (like box-shadow or blur) to the moving cards themselves during animation.