Audio playback, image carousels, and server-side QR code generation.

Table of Contents


audio_player

HTML5 audio player with play/pause, scrubber, time display, and volume control. All state is managed by the PhiaAudioPlayer hook.

Hook: PhiaAudioPlayer Attrs: id, src (audio URL), title, cover_url

<%!-- Basic audio player --%>
<.audio_player
  id="podcast-player"
  src={@episode.audio_url}
  title={@episode.title}
/>

<%!-- With cover art --%>
<.audio_player
  id="track-player"
  src={@track.url}
  title={@track.name}
  cover_url={@track.cover_art}
/>

<%!-- Playlist of audio players --%>
<div class="space-y-3">
  <.audio_player
    :for={ep <- @episodes}
    id={"episode-#{ep.id}"}
    src={ep.audio_url}
    title={ep.title}
    cover_url={ep.cover_url}
  />
</div>
// app.js
import PhiaAudioPlayer from "./phia_hooks/audio_player"
// hooks: { PhiaAudioPlayer }

Use cases

  • Podcast episode lists
  • Music streaming interfaces
  • Audio message playback in chat
  • Voice memo previews

Touch-swipe, keyboard-navigable, looping image/content carousel.

Hook: PhiaCarousel Sub-components: carousel_content/1, carousel_item/1, carousel_previous/1, carousel_next/1 Attrs: id, loop (bool), orientation (horizontal/vertical)

<%!-- Image carousel --%>
<.carousel id="product-images" loop={true}>
  <.carousel_content>
    <.carousel_item :for={img <- @product_images}>
      <.aspect_ratio ratio="4/3" class="overflow-hidden rounded-lg">
        <img src={img.url} alt={img.alt} class="object-cover w-full h-full" />
      </.aspect_ratio>
    </.carousel_item>
  </.carousel_content>
  <.carousel_previous />
  <.carousel_next />
</.carousel>

<%!-- Card carousel --%>
<.carousel id="featured-posts" class="w-full">
  <.carousel_content class="-ml-4">
    <.carousel_item :for={post <- @featured_posts} class="pl-4 basis-1/3">
      <.card>
        <.card_header>
          <.card_title><%= post.title %></.card_title>
        </.card_header>
        <.card_content>
          <p class="text-sm text-muted-foreground"><%= post.excerpt %></p>
        </.card_content>
      </.card>
    </.carousel_item>
  </.carousel_content>
  <.carousel_previous />
  <.carousel_next />
</.carousel>

<%!-- Testimonials --%>
<.carousel id="testimonials" loop={true} class="max-w-2xl mx-auto">
  <.carousel_content>
    <.carousel_item :for={t <- @testimonials}>
      <.card class="text-center p-8">
        <blockquote class="text-lg italic">"<%= t.quote %>"</blockquote>
        <div class="mt-4 flex items-center justify-center gap-3">
          <.avatar size="sm"><.avatar_fallback name={t.author} /></.avatar>
          <div>
            <p class="font-medium text-sm"><%= t.author %></p>
            <p class="text-xs text-muted-foreground"><%= t.role %></p>
          </div>
        </div>
      </.card>
    </.carousel_item>
  </.carousel_content>
  <.carousel_previous />
  <.carousel_next />
</.carousel>
// app.js
import PhiaCarousel from "./phia_hooks/carousel"
// hooks: { PhiaCarousel }

qr_code

Server-side SVG QR code generation using eqrcode. No JavaScript required.

Attrs: value (string to encode), size (pixel width, default 200), class

Dependency: eqrcode is included as a transitive dependency via PhiaUI. No extra setup needed.

<%!-- URL QR code --%>
<.qr_code value="https://phiaui.dev" />

<%!-- Custom size --%>
<.qr_code value={@share_url} size={300} class="mx-auto" />

<%!-- With label --%>
<div class="flex flex-col items-center gap-2">
  <.qr_code value={@wifi_password} size={180} />
  <p class="text-xs text-muted-foreground">Scan to connect to WiFi</p>
</div>

<%!-- In a modal for sharing --%>
<.dialog id="share-qr">
  <:trigger>
    <.button variant="outline" size="sm">
      <.icon name="qr-code" size="sm" /> Share via QR
    </.button>
  </:trigger>
  <.dialog_content>
    <.dialog_header>
      <.dialog_title>Scan to Share</.dialog_title>
    </.dialog_header>
    <div class="flex justify-center py-4">
      <.qr_code value={@current_url} size={240} />
    </div>
  </.dialog_content>
</.dialog>

<%!-- For payment or crypto --%>
<.card class="w-fit mx-auto">
  <.card_header>
    <.card_title>Pay with Bitcoin</.card_title>
  </.card_header>
  <.card_content class="flex flex-col items-center gap-3">
    <.qr_code value={@btc_address} size={200} />
    <div class="flex items-center gap-2">
      <code class="text-xs bg-muted px-2 py-1 rounded truncate max-w-48"><%= @btc_address %></code>
      <.copy_button value={@btc_address} />
    </div>
  </.card_content>
</.card>

Use cases

  • WiFi credentials sharing
  • Payment addresses (crypto, Pix, etc.)
  • Contact cards / vCards
  • Event check-in codes
  • Share URLs on mobile
  • 2FA enrollment (TOTP secrets)

Back to README