Skip to content

Isotope Extension

The Isotope extension provides responsive grid layouts with filtering capabilities using isotope-layout and imagesloaded.

Features

  • Responsive Grid Layouts: Automatic item arrangement and resizing
  • Filtering System: Interactive filters with active state management
  • Image Loading Support: Integration with imagesloaded for proper layout timing
  • Hash-based Navigation: URL hash support for filter states
  • Lazy Loading Integration: Works with lazysizes for optimal performance
  • Dynamic Content: Support for asynchronously loaded content

Configuration

ts
export interface IsotopeExtensionOptions {
    /**
     * The Isotope wrapper element. Defaults to .isotope-wrapper
     */
    wrapper?: HTMLElement;
}

Usage

ts
import { RunThemeExtensions } from 'wly-statamic-theme-extensions';

RunThemeExtensions({
    isotope: {
        wrapper: document.querySelector('#my-grid') // Optional: specific element
    }
});

HTML Structure

Basic Grid Layout

html
<div class="isotope-wrapper">
    <div class="isotope-grid">
        <div class="isotope-item category-foo">Item 1</div>
        <div class="isotope-item category-bar">Item 2</div>
        <div class="isotope-item category-baz">Item 3</div>
        <div class="isotope-item category-foo category-bar">Item 4</div>
    </div>
</div>

Grid with Filters

html
<div class="isotope-wrapper">
    <!-- Filter controls -->
    <div class="isotope-filters">
        <button data-filter="*" class="active">All</button>
        <button data-filter=".category-foo">Category Foo</button>
        <button data-filter=".category-bar">Category Bar</button>
        <button data-filter=".category-baz">Category Baz</button>
    </div>
    
    <!-- Grid container -->
    <div class="isotope-grid">
        <div class="isotope-item category-foo">
            <h3>Foo Item 1</h3>
            <p>Content for foo category</p>
        </div>
        <div class="isotope-item category-bar">
            <h3>Bar Item 1</h3>
            <p>Content for bar category</p>
        </div>
        <div class="isotope-item category-baz">
            <h3>Baz Item 1</h3>
            <p>Content for baz category</p>
        </div>
        <div class="isotope-item category-foo category-bar">
            <h3>Multi-category Item</h3>
            <p>This item belongs to multiple categories</p>
        </div>
    </div>
</div>

Hash-based Navigation

Create linkable filters using href attributes:

html
<div class="isotope-filters">
    <a href="#all" data-filter="*" class="active">All</a>
    <a href="#portfolio" data-filter=".portfolio">Portfolio</a>
    <a href="#blog" data-filter=".blog">Blog</a>
    <a href="#news" data-filter=".news">News</a>
</div>

Features:

  • URLs update when filters are clicked (example.com/#portfolio)
  • Direct navigation to filtered views via URL
  • Browser back/forward button support
  • Hash-based initial filter on page load

Advanced Features

Dynamic Content Loading

For content loaded via AJAX or Vue components:

html
<div class="isotope-wrapper" data-change-listener="#content-trigger">
    <div class="isotope-grid">
        <!-- Initial content -->
    </div>
</div>

<!-- Trigger element that fires 'change' event -->
<div id="content-trigger"></div>
js
// After loading new content
document.getElementById('content-trigger').dispatchEvent(new Event('change'));

Async Initialization

For Vue components or other async content:

html
<div class="isotope-wrapper" data-init-message="grid-ready">
    <div class="isotope-grid">
        <!-- Content loaded by Vue component -->
    </div>
</div>
js
// From Vue component or async loader
window.postMessage('grid-ready', '*');

No-Filter Grid

Use isotope without filters for pure masonry layout:

html
<div class="isotope-wrapper">
    <!-- No .isotope-grid needed - wrapper becomes grid -->
    <div class="isotope-item">Item 1</div>
    <div class="isotope-item">Item 2</div>
    <div class="isotope-item">Item 3</div>
</div>

CSS Integration

Basic CSS structure for responsive grid:

css
.isotope-wrapper {
    width: 100%;
}

.isotope-grid {
    display: flex;
    flex-wrap: wrap;
}

.isotope-item {
    width: 33.333%;
    padding: 10px;
    box-sizing: border-box;
}

@media (max-width: 768px) {
    .isotope-item {
        width: 50%;
    }
}

@media (max-width: 480px) {
    .isotope-item {
        width: 100%;
    }
}

.isotope-filters button.active {
    background: #007cba;
    color: white;
}

Filter States

The extension automatically manages filter states:

  • Active Class: Applied to currently selected filter
  • History Management: Updates browser URL for hash-based filters
  • Initial State: Detects hash on page load or falls back to active filter
  • Event Handling: Click events with preventDefault and custom logic

Image Loading Integration

The extension integrates with imagesloaded to ensure proper layout:

  • Automatic Detection: Waits for images to load before arranging
  • Lazy Loading Support: Listens for 'lazyloaded' events
  • Layout Refresh: Triggers layout updates when images load
  • Dynamic Insertion: Handles new items with image content

Performance Considerations

  • Debounced Events: Filter changes are debounced for smooth performance
  • Opacity Animation: New items fade in with opacity transitions
  • Efficient Selectors: Uses specific selectors to minimize DOM queries
  • Event Delegation: Efficient event handling for multiple filters

Integration with Other Extensions

Works seamlessly with other theme extensions:

  • Lazy Loading: Automatic integration with lazysizes extension
  • Vue Components: Support for Vue-based content loading
  • Image Optimization: Works with any image loading strategy