Web Awesome (ViUR)

Font Awesome
Try SSR Server-side rendering (SSR) generates component HTML on the server before the page loads, improving SEO and initial load time. Use the switch to see Web Awesome components render with and without SSR.
Search this website ⌘KCtrl+K Light Dark System Docs Select Color Scheme Default Awesome Shoelace Active Brutalist Glossy Matter Mellow Playful Premium Tailspin Docs Select Theme View Project on GitHub Star Project on GitHub
Start Components Docs Help
Web Awesome Font Awesome Build Awesome
Search this site… /
Try SSR Server-side rendering (SSR) generates component HTML on the server before the page loads, improving SEO and initial load time. Use the switch to see Web Awesome components render with and without SSR.
Light Dark System Docs Select Color Scheme Default Awesome Shoelace Active Brutalist Glossy Matter Mellow Playful Premium Tailspin Docs Select Theme

Getting Started

  • Installation
  • Usage
  • Forms
  • Localization
  • Frameworks
  • Using with AI
  • Figma Design Kit ProThis requires access to Web Awesome Pro
  • Server Rendering

Resources

  • Accessibility
  • Browser Support
  • Contributing
  • Patterns ProPatterns require access to Web Awesome Pro
  • Migrating from Shoelace
  • Visual Tests
  • Changelog
  • Help & Support

Theming & Utilities

  • Overview
  • Built-in Themes
  • Color Palettes
  • Design Tokens
  • Customizing & Theming
  • CSS Utilities

ViUR Components

  • Alert
  • Combobox
  • Pagination
  • Table Wrapper

Actions

  • Button
  • Button Group
  • Copy Button
  • Dropdown
    • Dropdown Item

Forms

  • Checkbox
  • Checkbox Group
  • Color Picker
  • Input
  • Known Date
  • Number Input
  • Radio Group
    • Radio
  • Rating
  • Select
    • Option
  • Slider
  • Switch
  • Textarea
  • Time Input
  • Data Grid Planned A Web Awesome Kickstarter stretch goal!

Layout

  • Accordion
    • Accordion Item
  • Card
  • Details
  • Dialog
  • Divider
  • Drawer
  • Page
  • Scroller
  • Split Panel

Navigation

  • Breadcrumb
    • Breadcrumb Item
  • Tab Group
    • Tab
    • Tab Panel
  • Tree
    • Tree Item

Feedback

  • Badge
  • Callout
  • Progress Bar
  • Progress Ring
  • Skeleton
  • Spinner
  • Tag
  • Tooltip

Media

  • Animated Image
  • Avatar
  • Carousel
    • Carousel Item
  • Comparison
  • Icon
  • Markdown
  • QR Code
  • Zoomable Frame

Data Viz

  • Advanced Usage

Helpers

  • Animation
  • Format Bytes
  • Format Date
  • Format Number
  • Include
  • Intersection Observer
  • Mutation Observer
  • Popover
  • Popup
  • Random Content
  • Relative Time
  • Resize Observer

Intersection Observer

  • Usage Examples
  • Adding Observable Content
  • Setting a Custom Root Element
  • Configuring Multiple Thresholds
  • Applying Classes on Intersect
  • Importing
  • Slots
  • Attributes & Properties
  • Events
On This Page...
  • Usage Examples
  • Adding Observable Content
  • Setting a Custom Root Element
  • Configuring Multiple Thresholds
  • Applying Classes on Intersect
  • Importing
  • Slots
  • Attributes & Properties
  • Events

Intersection Observer

<wa-intersection-observer>
Stable Helpers Since 2.0

Tracks immediate child elements and fires events as they move in and out of view. Useful for lazy loading, scroll-triggered animations, and viewport-aware interactions.

This component leverages the IntersectionObserver API to track when its direct children enter or leave a designated root element. The wa-intersect event fires whenever elements cross the visibility threshold.

Scroll to see the element intersect at 100% visibility
<div id="intersection__overview">
  <wa-intersection-observer threshold="1" intersect-class="visible">
    <div class="box"><wa-icon name="lightbulb"></wa-icon></div>
  </wa-intersection-observer>
</div>

<small>Scroll to see the element intersect at 100% visibility</small>

<style>
  /* Container styles */
  #intersection__overview {
    display: flex;
    flex-direction: column;
    gap: 2rem;
    height: 300px;
    border: solid 2px var(--wa-color-surface-border);
    padding: 1rem;
    overflow-y: auto;

    /* Spacers to demonstrate scrolling */
    &::before {
      content: '';
      height: 260px;
      flex-shrink: 0;
    }

    &::after {
      content: '';
      height: 260px;
      flex-shrink: 0;
    }

    /* Box styles */
    .box {
      flex-shrink: 0;
      width: 120px;
      height: 120px;
      background-color: var(--wa-color-neutral-fill-normal);
      color: var(--wa-color-neutral-10);
      display: flex;
      align-items: center;
      justify-content: center;
      margin-inline: auto;
      transition: all 50ms cubic-bezier(0.68, -0.55, 0.265, 1.55);

      wa-icon {
        font-size: 3rem;
        stroke-width: 1px;
      }

      &.visible {
        background-color: var(--wa-color-brand-60);
        color: white;
      }
    }

    + small {
      display: block;
      text-align: center;
      margin-block-start: 1rem;
    }
  }
</style>

Keep in mind that only direct children of the host element are monitored. Nested elements won't trigger intersection events.

Usage Examples

Link to This Section

Adding Observable Content

Link to This Section

The intersection observer tracks only its direct children. The component uses display: contents styling, which makes it seamless to integrate with flex and grid layouts from a parent container.

<div style="display: flex; flex-direction: column;">
  <wa-intersection-observer>
    <div class="box">Box 1</div>
    <div class="box">Box 2</div>
    <div class="box">Box 3</div>
  </wa-intersection-observer>
</div>

The component tracks elements as they enter and exit the root element (viewport by default) and emits the wa-intersect event on state changes. The event provides event.detail.entry, an IntersectionObserverEntry object with intersection details.

You can identify the triggering element through entry.target. Check entry.isIntersecting to determine if an element is entering or exiting the viewport.

observer.addEventListener('wa-intersect', event => {
  const entry = event.detail.entry;

  if (entry.isIntersecting) {
    console.log('Element entered viewport:', entry.target);
  } else {
    console.log('Element left viewport:', entry.target);
  }
});

Setting a Custom Root Element

Link to This Section

You can observe intersections within a specific container by assigning the root attribute to the root element's ID. Apply rootMargin with the root-margin attribute to expand or contract the observation area.

<div id="scroll-container">
  <wa-intersection-observer root="scroll-container" root-margin="50px 0px"> ... </wa-intersection-observer>
</div>

Configuring Multiple Thresholds

Link to This Section

Track different visibility percentages by providing multiple threshold values as a space-separated list.

<wa-intersection-observer threshold="0 0.25 0.5 0.75 1"> ... </wa-intersection-observer>

Applying Classes on Intersect

Link to This Section

The intersect-class attribute automatically toggles the specified class on direct children when they become visible. This enables pure CSS styling without JavaScript event handlers.

Fade In
Slide In
Scale & Rotate
Bounce
Scroll to see elements transition at 50% visibility
<div id="intersection__classes">
  <wa-intersection-observer threshold="0.5" intersect-class="visible" root="intersection__classes">
    <div class="box fade">Fade In</div>
    <div class="box slide">Slide In</div>
    <div class="box scale">Scale & Rotate</div>
    <div class="box bounce">Bounce</div>
  </wa-intersection-observer>
</div>

<small>Scroll to see elements transition at 50% visibility</small>

<style>
  /* Container styles */
  #intersection__classes {
    display: flex;
    flex-direction: column;
    gap: 2rem;
    height: 300px;
    border: solid 2px var(--wa-color-surface-border);
    padding: 1rem;
    overflow-y: auto;

    /* Spacers to demonstrate scrolling */
    &::before {
      content: '';
      height: 260px;
      flex-shrink: 0;
    }

    &::after {
      content: '';
      height: 260px;
      flex-shrink: 0;
    }

    + small {
      display: block;
      text-align: center;
      margin-block-start: 1rem;
    }

    /* Shared box styles */
    .box {
      flex-shrink: 0;
      width: 120px;
      height: 120px;
      display: flex;
      align-items: center;
      justify-content: center;
      text-align: center;
      color: white;
      opacity: 0;
      padding: 2rem;
      margin-inline: auto;

      /* Fade */
      &.fade {
        background: var(--wa-color-brand-fill-loud);
        color: var(--wa-color-brand-on-loud);
        transform: translateY(30px);
        transition: all 0.6s ease;

        &.visible {
          opacity: 1;
          transform: translateY(0);
        }
      }

      /* Slide */
      &.slide {
        background: var(--wa-color-brand-fill-loud);
        color: var(--wa-color-brand-on-loud);
        transform: translateX(-50px);
        transition: all 0.5s ease;

        &.visible {
          opacity: 1;
          transform: translateX(0);
        }
      }

      /* Scale */
      &.scale {
        background: var(--wa-color-brand-fill-loud);
        color: var(--wa-color-brand-on-loud);
        transform: scale(0.6) rotate(-15deg);
        transition: all 0.7s cubic-bezier(0.175, 0.885, 0.32, 1.275);

        &.visible {
          opacity: 1;
          transform: scale(1) rotate(0deg);
        }
      }

      /* Bounce In and Out */
      &.bounce {
        background: var(--wa-color-brand-fill-loud);
        color: var(--wa-color-brand-on-loud);
        opacity: 0;
        transform: scale(0.8);
        transition: none;

        &.visible {
          opacity: 1;
          transform: scale(1);
          animation: bounceIn 0.8s cubic-bezier(0.68, -0.55, 0.265, 1.55) forwards;
        }

        &:not(.visible) {
          animation: bounceOut 0.6s cubic-bezier(0.68, -0.55, 0.265, 1.55) forwards;
        }
      }
    }
  }

  @keyframes bounceIn {
    0% {
      transform: scale(0.8);
    }
    40% {
      transform: scale(1.08);
    }
    65% {
      transform: scale(0.98);
    }
    80% {
      transform: scale(1.02);
    }
    90% {
      transform: scale(0.99);
    }
    100% {
      transform: scale(1);
    }
  }

  @keyframes bounceOut {
    0% {
      transform: scale(1);
      opacity: 1;
    }
    20% {
      transform: scale(1.02);
      opacity: 1;
    }
    40% {
      transform: scale(0.98);
      opacity: 0.8;
    }
    60% {
      transform: scale(1.05);
      opacity: 0.6;
    }
    80% {
      transform: scale(0.95);
      opacity: 0.3;
    }
    100% {
      transform: scale(0.8);
      opacity: 0;
    }
  }
</style>

Importing

Link to This Section

If you're using the autoloader or a hosted project, components load on demand — no manual import needed. To cherry-pick a component manually, use one of the following snippets.

CDN npm Self-Hosted React

Import this component directly from the CDN:

import 'https://ka-f.webawesome.com/webawesome@0.17.0/components/intersection-observer/intersection-observer.js';

After installing Web Awesome via npm, import this component:

import '@awesome.me/webawesome/dist/components/intersection-observer/intersection-observer.js';

If you're self-hosting Web Awesome, import this component from your server:

import './webawesome/dist/components/intersection-observer/intersection-observer.js';

To import this component for React 18 or below, use the following code:

import WaIntersectionObserver from '@awesome.me/webawesome/dist/react/intersection-observer/index.js';

Slots

Link to This Section

Learn more about using slots.

Name Description
(default) Elements to track. Only immediate children of the host are monitored.

Attributes & Properties

Link to This Section

Learn more about attributes and properties.

Name Description Reflects
disabled
disabled
Deactivates the intersection observer functionality.
Type boolean
Default false
intersectClass
intersect-class
CSS class applied to elements during intersection. Automatically removed when elements leave the viewport, enabling pure CSS styling based on visibility state.
Type string
Default ''
once
once
If enabled, observation ceases after initial intersection.
Type boolean
Default false
root
root
Element ID to define the viewport boundaries for tracked targets.
Type string | null
Default null
rootMargin
root-margin
Offset space around the root boundary. Accepts values like CSS margin syntax.
Type string
Default '0px'
threshold
threshold
One or more space-separated values representing visibility percentages that trigger the observer callback.
Type string
Default '0'

Events

Link to This Section

Learn more about events.

Name Description
wa-intersect Fired when a tracked element begins or ceases intersecting.
Need a hand? Report a bug Ask for help
Go Make Something Awesome
Version 0.17.0 © Fonticons, Inc.
  • Terms
  • Privacy
  • Refunds
  • Core License
  • Pro License

Quick Links

  • Components
  • CSS Utilities
  • Theming
  • Using with AI
  • Changelog
  • Help & Support

Recent Searches

    D'oh! No results for “”

    Suggest on GitHub Ask on Discord
    Navigate Select
    Close Esc