🚀 Dynamic Content Successfully Loaded

This content was loaded via AJAX as plain HTML. The component below uses the same event listener as the root component - no additional initialization needed!

📊 Current Architecture Overview

Document (10 Total Event Listeners)
→ 3 Root Tab Components (6 listeners)
→ 4 Remaining listeners are handling this Example Page
→ → Dynamic Tab Component (You are here - using existing listeners)
→ → → Next Level (Click "Try Ynfiniti" to "Load More Tabs")

Single Listener Architecture

YaiTabs uses YpsilonEventHandler's DOM Event Scoping System for efficient event handling:

  • Event delegation - One listener handles all components regardless of nesting depth
  • Automatic scope isolation - Each component operates independently
  • Zero initialization required - Components work immediately after DOM insertion
  • Consistent O(1) performance - Scaling remains constant regardless of nesting complexity

Initialization Workflow

  1. Page loads with [data-yai-tabs] components
  2. If autoDisambiguate: true, makes identical data-open/data-tab values unique
  3. Root components receive event listeners (click + keydown per component)
  4. Dynamic content uses existing listeners - no re-initialization needed

⚡ Performance Advantages

Traditional Approach

  • Each component needs its own listener
  • Memory usage grows with component count
  • Initialization required for dynamic content
  • Nested components create complex cleanup requirements
  • Memory bloat with deep nesting

YaiTabs Approach

  • Single listener handles all components
  • Constant memory usage (only 2 listeners per root component)
  • No initialization needed for dynamic content
  • Automatic cleanup through DOM scoping only root components have listeners (In this Example, we have 3 root components)
  • O(1) scaling regardless of component count

Deep Nesting Demo (copy&pasted)

Below we nest some top-nav with some other navs left and right.

Animations

Switch between fade, slide-*, zoom, or flip via data-behavior. Keep transitions short (150–300ms) for snappy feel.

For content that shifts layout, consider overflow gating to avoid jank.

URL State & Deep Links

  • Each container uses a unique data-ref-path key.
  • On open, the route map updates and writes to the hash.
  • On hashchange, containers reconcile their state (no loops: guarded writes).

Use data-history-mode="replace" to keep Back-stack clean on frequent tabbing.

API Basics
  • data-default marks the initial tab.
  • data-open sets the target panel ID.
  • data-nav controls nav position: top/bottom/left/right.

Initialize only once per container; the component is idempotent by design.

Async Content

When loading remote HTML:

  • Set aria-busy="true" before fetch; reset to false after.
  • Optionally set aria-live="polite" for subtle SR announcements.
  • Re-run accessibility setup on nested content if it includes new tab containers.

Consider caching for quick back-navigation.

Troubleshooting
  • Duplicate listeners? Ensure only one init per container file & no legacy helpers.
  • Stale hash? Remove the key when closing a tab or fallback to default.
  • Focus lost? Keep one tabindex="0" per tablist (roving tabindex).
Pre-Ship Checklist
  • Unique data-ref-path per container
  • Visible :focus-visible ring for all interactive elements
  • aria-orientation consistent with computed layout
  • Reduced-motion guard on heavy transitions
Design Notes
  • Prefer subtle elevation for active tabs and maintain a consistent hit-area (min-height ~40px).
  • Use semantic color tokens for easy theming.
Changelog (Excerpt)
  • 3.1: URL state for nested tabs; orientation via computed layout.
  • 3.0: Single-listener event model; idempotent accessibility setup.
  • 2.x: Roving tabindex + APG keyboard model.
A new Reality
<!--
Ca. 350 clicks
Reached level: 55
-->
<div data-yai-tabs data-nesting="55" />
Local metrics

Largest Contentful Paint (LCP) 0.16 s
Your local LCP value of 0.16 s is good. LCP elem. p

Cumulative Layout Shift (CLS) 0.17
Your local CLS value of 0.17 needs improvement.

Worst cluster 2 shifts Interaction to Next Paint (INP) 1,192 ms
Your local INP value of 1,192 ms is poor.

Reduced Motion

Respect user preferences with a simple CSS gate:

@media (prefers-reduced-motion: reduce) {
    [data-yai-tabs] [data-tab] {
        transition: none;
        animation: none;
    }
}

Keep parity in UX by retaining visual state changes without movement.

🎯 Experience Infinite Nesting

Click the "Load More Tabs" button below to experience truly infinite nesting. Each level will load the same already nested HTML tab component dynamically and insert it into the next hierarchy level. Just inserting the HTML transforms each insertion into a scoped standalone component. This components get never registered, only processed to adjust ARIA attributes. They can be deleted the same way they were injected, just llike that.

Demonstrates delay attributes: 500ms pre-fetch + 500ms post-fetch delay

💡 Key Insight: Nested tab components require 0 additional listeners - they reuse the existing root component listeners through YpsilonEventHandler's DOM event scoping system.

⚙️ Configuration Options

Core Configuration

const tabs = new YaiTabs({
  // Allow closing tabs
  closable: true,
  // Default tab index if none specified
  openDefault: 50,
  // Automatic ARIA attributes
  autoAccessibility: true,
  // Auto-focus first tab
  autoFocus: false,
  // Auto-resolve ID conflicts
  autoDisambiguate: true,
});

Lifecycle Hooks

// Hook system for custom loading behavior
callbacks: {
  // When loading state should be applied
  setLoading: null,
  // When loading state should be removed
  removeLoading: null,
  // When content is ready for animation
  contentReady: null,
  // After everything completes
  afterLoad: null,
}

Event Handling Options

events: {
  enableStats: true,
  autoTargetResolution: true,
  actionableAttributes: ['data-tab-action'],
  actionableTags: [],
}

HTML Delay Attributes

// Pre-fetch delay (before request)
data-delay="1000"
// Post-fetch delay (after content loads)
data-post-delay="500"
// Minimum loading time (prevents flicker)
data-min-loading="800"