PhiaUi.Components.Carousel (phia_ui v0.1.17)

Copy Markdown View Source

Carousel component with touch swipe, button navigation, and keyboard support.

Follows the shadcn/ui Carousel anatomy adapted for Phoenix LiveView. The PhiaCarousel vanilla-JS hook manages CSS transform-based sliding, touch/swipe detection, keyboard navigation (arrow keys), and optional loop behaviour. Zero npm dependencies.

When to use

  • Hero banner with rotating promotional images
  • Product image gallery (multiple photos of the same item)
  • Onboarding walkthrough (step-by-step screens)
  • Testimonial rotator
  • Vertical announcement ticker

Anatomy

ComponentElementPurpose
carousel/1divRoot container — mounts the PhiaCarousel hook
carousel_content/1divFlex track that holds all slides side-by-side
carousel_item/1divIndividual slide (min-w-full shrink-0)
carousel_previous/1buttonNavigate to the previous slide
carousel_next/1buttonNavigate to the next slide
<.carousel id="hero-banner">
  <.carousel_content>
    <.carousel_item>
      <img src="/images/promo-1.jpg" alt="Summer sale" class="w-full object-cover" />
    </.carousel_item>
    <.carousel_item>
      <img src="/images/promo-2.jpg" alt="New arrivals" class="w-full object-cover" />
    </.carousel_item>
    <.carousel_item>
      <img src="/images/promo-3.jpg" alt="Free shipping" class="w-full object-cover" />
    </.carousel_item>
  </.carousel_content>
  <.carousel_previous />
  <.carousel_next />
</.carousel>
<.carousel id="announcements" orientation="vertical" loop={true}>
  <.carousel_content>
    <.carousel_item class="h-12 flex items-center">
      Maintenance window scheduled for Sunday 2 AM UTC
    </.carousel_item>
    <.carousel_item class="h-12 flex items-center">
      v2.4.0 released  see the changelog
    </.carousel_item>
  </.carousel_content>
  <.carousel_previous />
  <.carousel_next />
</.carousel>
<.carousel id="product-gallery">
  <.carousel_content>
    <.carousel_item :for={img <- @product_images}>
      <img src={img.url} alt={img.alt} class="w-full rounded-lg object-cover" />
    </.carousel_item>
  </.carousel_content>
  <.carousel_previous />
  <.carousel_next />
  <:indicators>
    <button
      :for={{_img, i} <- Enum.with_index(@product_images)}
      data-index={i}
      class="h-2 w-2 rounded-full bg-muted data-[active]:bg-primary"
    />
  </:indicators>
</.carousel>

Accessibility

  • The root element has role="region" and aria-label="carousel"
  • Each slide has role="group" and aria-roledescription="slide"
  • Navigation buttons have aria-label="Previous slide" / "Next slide"
  • The root is focusable (tabindex="0") so arrow-key navigation works without a mouse

Hook setup

mix phia.add carousel   # copies hooks/carousel.js to your project

# app.js
import PhiaCarousel from "./hooks/carousel"
let liveSocket = new LiveSocket("/live", Socket, {
  hooks: { PhiaCarousel }
})

Summary

Functions

Renders the carousel root container.

Renders the carousel track container.

Renders an individual carousel slide.

Renders the next-slide navigation button.

Renders the previous-slide navigation button.

Functions

carousel(assigns)

Renders the carousel root container.

Mounts the PhiaCarousel hook and communicates configuration via data-* attributes. The root is focusable (tabindex="0") so keyboard users can navigate slides with arrow keys without any additional setup.

role="region" + aria-label="carousel" ensures the landmark is announced correctly by screen readers.

Attributes

  • id (:string) - Unique carousel ID. The PhiaCarousel hook uses this to identify the instance and store its current slide index in hook state.

    Defaults to "carousel".

  • orientation (:string) - Carousel slide direction:

    • "horizontal" — slides left/right (default, most common)
    • "vertical" — slides up/down (e.g. announcement ticker) The hook reads data-orientation to set the correct transform axis.

    Defaults to "horizontal". Must be one of "horizontal", or "vertical".

  • loop (:boolean) - When true, navigating past the last slide wraps to the first (and navigating before the first wraps to the last). The hook reads data-loop.

    Defaults to false.

  • class (:string) - Additional CSS classes for the root div. Defaults to nil.

  • Global attributes are accepted. HTML attributes forwarded to the root div.

Slots

  • inner_block (required) - carousel_content/1 and navigation sub-components.
  • indicators - Optional dot indicator buttons rendered at the bottom of the carousel. The PhiaCarousel hook can toggle a data-active attribute on the active indicator. Use data-[active]:bg-primary Tailwind variant to style it.