defmodule AshuiWeb.DocsLive do use AshuiWeb, :live_view @impl true def mount(params, _session, socket) do section = Map.get(params, "section", "getting-started") component = Map.get(params, "component", nil) socket = socket |> assign(section: section, component: component) |> assign(rich_text_content: nil) |> assign(form: to_form(%{"content" => nil})) |> assign(sidebar_open: true) |> assign(sidebar_section_states: build_sidebar_section_states()) |> assign(:github_star_count, "2.4k") {:ok, socket} end @impl true def handle_params(params, _url, socket) do if Map.has_key?(params, "section") do section = Map.get(params, "section", "getting-started") component = Map.get(params, "component", nil) {:noreply, assign(socket, section: section, component: component)} else {:noreply, socket |> assign(section: "getting-started", component: nil) |> push_navigate(to: ~p"/docs/getting-started")} end end @impl true def handle_event("validate", %{"content" => content}, socket) do {:noreply, assign(socket, rich_text_content: content)} end @impl true def handle_event("validate", _params, socket) do {:noreply, socket} end @impl true def handle_event("toggle-sidebar", _params, socket) do current = Map.get(socket.assigns, :sidebar_open, true) {:noreply, assign(socket, sidebar_open: not current)} end @impl true def handle_event("toggle-nav-section", %{"section" => section}, socket) do states = socket.assigns[:sidebar_section_states] || %{} updated_states = Map.update(states, section, true, &(!&1)) {:noreply, assign(socket, sidebar_section_states: updated_states)} end @impl true def handle_event("expand-all-sections", _params, socket) do {:noreply, assign(socket, sidebar_section_states: build_sidebar_section_states(true))} end @impl true def handle_event("collapse-all-sections", _params, socket) do {:noreply, assign(socket, sidebar_section_states: build_sidebar_section_states(false))} end @impl true def handle_event("update-rich-text", %{"content" => content}, socket) do {:noreply, assign(socket, rich_text_content: content)} end @impl true def handle_event("carousel-prev", %{"id" => id}, socket) do current = Map.get(socket.assigns, :"#{id}_current_slide", 0) new_current = max(0, current - 1) {:noreply, assign(socket, :"#{id}_current_slide", new_current)} end @impl true def handle_event("carousel-next", %{"id" => id}, socket) do current = Map.get(socket.assigns, :"#{id}_current_slide", 0) {:noreply, assign(socket, :"#{id}_current_slide", current + 1)} end @impl true def handle_event("carousel-go-to", %{"id" => id, "index" => index}, socket) do {:noreply, assign(socket, :"#{id}_current_slide", String.to_integer(index))} end @impl true def handle_event("wizard-prev", %{"id" => id}, socket) do # Wizard navigation would need to be handled by updating step active states {:noreply, socket} end @impl true def handle_event("wizard-next", %{"id" => id}, socket) do # Wizard navigation would need to be handled by updating step active states {:noreply, socket} end @impl true def handle_event("toggle-filter-panel", %{"id" => id}, socket) do current = Map.get(socket.assigns, :"#{id}_collapsed", false) {:noreply, assign(socket, :"#{id}_collapsed", !current)} end @impl true def handle_event("filter-changed", _params, socket) do {:noreply, socket} end @impl true def handle_event("clear-filters", %{"id" => _id}, socket) do {:noreply, socket} end @impl true def handle_event("autocomplete-search", %{"id" => id, "value" => value}, socket) do # Filter options based on search value # In a real app, this would filter from server-side data {:noreply, assign(socket, :"#{id}_search_value", value)} end @impl true def handle_event("sortable-reorder", %{"id" => id, "order" => order}, socket) do # Handle reordering - in a real app, update the items list {:noreply, assign(socket, :"#{id}_order", order)} end @impl true def handle_event("chat-send", %{"id" => _id, "message" => _message}, socket) do # Handle chat message - in a real app, broadcast to channel {:noreply, socket} end @impl true def handle_event("chat-typing", %{"id" => _id}, socket) do # Handle typing indicator - in a real app, broadcast typing status {:noreply, socket} end @impl true def handle_event( "feed-action", %{"id" => _id, "item-id" => _item_id, "action" => _action}, socket ) do # Handle feed item action {:noreply, socket} end @impl true def handle_event("toggle-connection", %{"id" => _id}, socket) do # Handle connection toggle {:noreply, socket} end @impl true def handle_event("view-all-connections", %{"type" => _type}, socket) do # Handle view all connections {:noreply, socket} end @impl true def handle_event("toggle-drawer", %{"id" => id}, socket) do current_state = Map.get(socket.assigns, :"#{id}_open", false) {:noreply, assign(socket, :"#{id}_open", !current_state)} end @impl true def handle_event("close-drawer", %{"id" => id}, socket) do {:noreply, assign(socket, :"#{id}_open", false)} end @impl true def handle_event("open-lightbox", %{"id" => id, "index" => index}, socket) do images = Map.get(socket.assigns, :"#{id}_images", []) {:noreply, socket |> assign(:"#{id}_show", true) |> assign(:"#{id}_current_index", String.to_integer(index)) |> assign(:"#{id}_images", images)} end @impl true def handle_event("close-lightbox", %{"id" => id}, socket) do {:noreply, assign(socket, :"#{id}_show", false)} end @impl true def handle_event("lightbox-prev", %{"id" => id}, socket) do current_index = Map.get(socket.assigns, :"#{id}_current_index", 0) new_index = max(0, current_index - 1) {:noreply, assign(socket, :"#{id}_current_index", new_index)} end @impl true def handle_event("lightbox-next", %{"id" => id}, socket) do current_index = Map.get(socket.assigns, :"#{id}_current_index", 0) images = Map.get(socket.assigns, :"#{id}_images", []) new_index = min(length(images) - 1, current_index + 1) {:noreply, assign(socket, :"#{id}_current_index", new_index)} end @impl true def render(assigns) do ~H"""
<.link navigate={~p"/"} class="flex items-center gap-4 text-sm font-semibold uppercase tracking-[0.35em] text-white/90 transition hover:text-white" >
AshUI logo
AshUI
{@github_star_count} ⭐ Playground
<%= if @sidebar_open do %> <% end %>
{render_section(assigns)}
""" end defp navigation_sections_raw do [ {"Getting Started", [ %{label: "Introduction", path: "getting-started"}, %{label: "Installation", path: "installation"}, %{label: "Usage", path: "usage"} ]}, {"Layout & Navigation", [ %{label: "Navbar", path: "navbar"}, %{label: "Tabs", path: "tabs"}, %{label: "Breadcrumbs", path: "breadcrumbs"}, %{label: "Accordion", path: "accordion"}, %{label: "Grid", path: "grid"} ]}, {"Data Display", [ %{label: "Table", path: "table"}, %{label: "List", path: "list"}, %{label: "Badge", path: "badge"}, %{label: "Stats", path: "stats"}, %{label: "Avatar", path: "avatar"} ]}, {"Forms", [ %{label: "Input", path: "input"}, %{label: "Select", path: "select"}, %{label: "DatePicker", path: "datepicker"}, %{label: "Slider", path: "slider"}, %{label: "File Upload", path: "file-upload"}, %{label: "Rich Text Editor", path: "rich-text-editor"}, %{label: "Multi-Select", path: "multi-select"}, %{label: "Rating", path: "rating"}, %{label: "Password Strength Meter", path: "password-strength-meter"}, %{label: "Tag Input", path: "tag-input"}, %{label: "Signature Pad", path: "signature-pad"}, %{label: "Color Picker", path: "color-picker"}, %{label: "Conditional Form", path: "conditional-form"} ]}, {"Feedback", [ %{label: "Alert", path: "alert"}, %{label: "Modal", path: "modal"}, %{label: "Toast", path: "toast"}, %{label: "Progress", path: "progress"} ]}, {"Utilities", [ %{label: "Button", path: "button"}, %{label: "Card", path: "card"}, %{label: "Stepper", path: "stepper"} ]}, {"Layout & Utilities", [ %{label: "Carousel", path: "carousel"}, %{label: "Wizard", path: "wizard"}, %{label: "Resizable Panels", path: "resizable-panels"}, %{label: "Filter Panel", path: "filter-panel"}, %{label: "Skeleton", path: "skeleton"} ]}, {"Interactive", [ %{label: "Autocomplete", path: "autocomplete"}, %{label: "Sortable List", path: "sortable-list"}, %{label: "Live Chat", path: "live-chat"}, %{label: "Live Feed", path: "live-feed"}, %{label: "Countdown", path: "countdown"}, %{label: "Kanban Board", path: "kanban"}, %{label: "Gantt Chart", path: "gantt"}, %{label: "Live Data Table", path: "live-data-table"} ]}, {"User & Social", [ %{label: "User Profile Card", path: "user-profile-card"}, %{label: "Avatar Group", path: "avatar-group"}, %{label: "Connections Widget", path: "connections-widget"}, %{label: "Badge Card", path: "badge-card"} ]}, {"Accessibility & Theming", [ %{label: "Theme Switch", path: "theme-switch"}, %{label: "Alert", path: "alert"}, %{label: "Theme Config", path: "theme-config"} ]}, {"Miscellaneous", [ %{label: "Diff", path: "diff"}, %{label: "Kbd", path: "kbd"}, %{label: "Link", path: "link"}, %{label: "Hero", path: "hero"}, %{label: "Swap", path: "swap"}, %{label: "Dock", path: "dock"}, %{label: "Drawer", path: "drawer"}, %{label: "Mockup", path: "mockup"}, %{label: "Timeline", path: "timeline"} ]}, {"Visual & Presentation", [ %{label: "Lightbox", path: "lightbox"}, %{label: "Video Player", path: "video-player"}, %{label: "Animated Chart", path: "animated-chart"}, %{label: "Skeleton Layout", path: "skeleton-layout"}, %{label: "Masonry Grid", path: "masonry-grid"} ]}, {"Data & Analytics", [ %{label: "Chart", path: "chart"}, %{label: "Gauge", path: "gauge"}, %{label: "Heatmap", path: "heatmap"}, %{label: "Map", path: "map"} ]} ] end defp navigation_sections do navigation_sections_raw() |> Enum.map(fn {title, items} -> { title, items |> Enum.sort_by(&String.downcase(&1.label)) } end) |> Enum.sort_by(fn {title, _items} -> String.downcase(title) end) end defp build_sidebar_section_states(default \\ true) do navigation_sections() |> Enum.map(fn {title, _items} -> {title, default} end) |> Map.new() end defp render_section(assigns) do case assigns.section do "getting-started" -> render_getting_started(assigns) "installation" -> render_installation(assigns) "usage" -> render_usage(assigns) "button" -> render_button_docs(assigns) "input" -> render_input_docs(assigns) "card" -> render_card_docs(assigns) "alert" -> render_alert_docs(assigns) "select" -> render_select_docs(assigns) "textarea" -> render_textarea_docs(assigns) "checkbox" -> render_checkbox_docs(assigns) "progress" -> render_progress_docs(assigns) "navbar" -> render_navbar_docs(assigns) "tabs" -> render_tabs_docs(assigns) "badge" -> render_badge_docs(assigns) "modal" -> render_modal_docs(assigns) "table" -> render_table_docs(assigns) "accordion" -> render_accordion_docs(assigns) "breadcrumbs" -> render_breadcrumbs_docs(assigns) "grid" -> render_grid_docs(assigns) "list" -> render_list_docs(assigns) "stats" -> render_stats_docs(assigns) "avatar" -> render_avatar_docs(assigns) "datepicker" -> render_datepicker_docs(assigns) "slider" -> render_slider_docs(assigns) "toast" -> render_toast_docs(assigns) "stepper" -> render_stepper_docs(assigns) "chart" -> render_chart_docs(assigns) "gauge" -> render_gauge_docs(assigns) "heatmap" -> render_heatmap_docs(assigns) "map" -> render_map_docs(assigns) "file-upload" -> render_file_upload_docs(assigns) "rich-text-editor" -> render_rich_text_editor_docs(assigns) "multi-select" -> render_multi_select_docs(assigns) "rating" -> render_rating_docs(assigns) "password-strength-meter" -> render_password_strength_meter_docs(assigns) "tag-input" -> render_tag_input_docs(assigns) "signature-pad" -> render_signature_pad_docs(assigns) "color-picker" -> render_color_picker_docs(assigns) "conditional-form" -> render_conditional_form_docs(assigns) "carousel" -> render_carousel_docs(assigns) "wizard" -> render_wizard_docs(assigns) "resizable-panels" -> render_resizable_panels_docs(assigns) "filter-panel" -> render_filter_panel_docs(assigns) "skeleton" -> render_skeleton_docs(assigns) "autocomplete" -> render_autocomplete_docs(assigns) "sortable-list" -> render_sortable_list_docs(assigns) "live-chat" -> render_live_chat_docs(assigns) "live-feed" -> render_live_feed_docs(assigns) "countdown" -> render_countdown_docs(assigns) "kanban" -> render_kanban_docs(assigns) "gantt" -> render_gantt_docs(assigns) "live-data-table" -> render_live_data_table_docs(assigns) "user-profile-card" -> render_user_profile_card_docs(assigns) "avatar-group" -> render_avatar_group_docs(assigns) "connections-widget" -> render_connections_widget_docs(assigns) "badge-card" -> render_badge_card_docs(assigns) "theme-switch" -> render_theme_switch_docs(assigns) "alert" -> render_alert_docs(assigns) "theme-config" -> render_theme_config_docs(assigns) "diff" -> render_diff_docs(assigns) "kbd" -> render_kbd_docs(assigns) "link" -> render_link_docs(assigns) "hero" -> render_hero_docs(assigns) "swap" -> render_swap_docs(assigns) "dock" -> render_dock_docs(assigns) "drawer" -> render_drawer_docs(assigns) "mockup" -> render_mockup_docs(assigns) "timeline" -> render_timeline_docs(assigns) "lightbox" -> render_lightbox_docs(assigns) "video-player" -> render_video_player_docs(assigns) "animated-chart" -> render_animated_chart_docs(assigns) "skeleton-layout" -> render_skeleton_layout_docs(assigns) "masonry-grid" -> render_masonry_grid_docs(assigns) _ -> render_coming_soon(assigns) end end defp content_wrapper_classes(section) when section in ["alert", "theme-config", "theme-switch", "accordion"] do "w-full px-4 sm:px-8 md:px-12 lg:px-16 xl:px-20 2xl:px-24 py-12" end defp content_wrapper_classes(_section) do "max-w-4xl mx-auto px-8 py-12 text-gray-900" end defp main_wrapper_classes(section) when section in ["alert", "theme-config", "theme-switch", "accordion"] do "flex-1 overflow-y-auto bg-slate-950" end defp main_wrapper_classes(_section) do "flex-1 overflow-y-auto bg-white" end defp render_getting_started(assigns) do ~H"""

Getting Started

AshUI is a modular, reusable UI library for Phoenix LiveView applications.

Features

  • ✅ LiveView-first: Components work natively in .heex templates
  • ✅ Composable & Modular: Each component is independent and reusable
  • ✅ Minimal JavaScript: Reactive behavior handled by Phoenix assigns
  • ✅ Theme-ready: Light/dark mode support
  • ✅ Developer-friendly: Semantic naming, minimal boilerplate

Quick Example


            <.button variant={:primary}>Click me</.button>
            <.input type="text" label="Name" />
            <.alert variant={:success}>Success!</.alert>
          
""" end defp render_installation(assigns) do ~H"""

Installation

Add to mix.exs


            def deps do
              [
                {:ashui, path: "../ashui"}
              ]
            end
          

Import Components

In your lib/your_app_web.ex file, add imports:


            import Ashui.Components.Button
            import Ashui.Components.Input
            import Ashui.Components.Alert
            # ... more components
          
""" end defp render_usage(assigns) do ~H"""

Usage

Basic Usage

Once imported, use components in your LiveView templates:


            defmodule MyAppWeb.UserLive do
              use MyAppWeb, :live_view

              def render(assigns) do
                ~H"""
                <.button variant={:primary} phx-click="save">Save</.button>
                <.input type="text" label="Name" field={@form[:name]} />
                """
              end
            end
          
""" end defp render_button_docs(assigns) do ~H"""

Button

Button component with multiple variants and states.

Examples

Primary Secondary Ghost Danger Success

    <.button variant={:primary}>Primary</.button>
    <.button variant={:secondary}>Secondary</.button>
    <.button variant={:ghost}>Ghost</.button>
    

Props

Prop Type Default Description
variant :primary | :secondary | :ghost | :danger | :success :primary Button style variant
size :sm | :md | :lg | :xl :md Button size
disabled boolean false Disable the button
loading boolean false Show loading spinner
""" end defp render_input_docs(assigns) do ~H"""

Input

Input field component with validation states and helper text.

Examples


    <.input type="text" label="Name" placeholder="Enter your name" />
    <.input type="email" label="Email" helper="We'll never share your email" />
    <.input type="password" label="Password" error="Password is required" />
            
""" end defp render_card_docs(assigns) do ~H"""

Card

Card component for content containers with customizable padding and shadows.

Examples

Default Card

This card has default padding and shadow.

No Padding

Custom padding with larger shadow.


    <.card>
      <h3>Card Title</h3>
      <p>Card content</p>
    </.card>
            

Props

Prop Type Default Description
padded boolean true Add padding to card
shadow :none | :sm | :md | :lg | :xl :md Shadow size
""" end defp render_select_docs(assigns) do ~H"""

Select

Select dropdown component with options and validation states.

Examples


    <.select label="Country">
      <option value="">Select a country</option>
      <option value="us">United States</option>
      <option value="uk">United Kingdom</option>
    </.select>
            
""" end defp render_textarea_docs(assigns) do ~H"""

Textarea

Textarea component for multi-line text input with validation.

Examples


    <.textarea label="Message" placeholder="Enter your message" rows={4} />
    <.textarea label="Description" helper="Maximum 500 characters" rows={6} />
            
""" end defp render_checkbox_docs(assigns) do ~H"""

Checkbox

Checkbox component with label and validation support.

Examples


    <.checkbox label="Accept terms" />
    <.checkbox label="Subscribe" checked={true} />
    <.checkbox label="Required" required={true} />
            
""" end defp render_progress_docs(assigns) do ~H"""

Progress

Progress bar component for showing completion status.

Examples


    <.progress value={75} max={100} />
    <.progress value={50} variant={:success} />
    <.progress value={90} variant={:error} show_label={true} />
            

Props

Prop Type Default Description
value integer required Current progress value
max integer 100 Maximum value
variant :primary | :success | :warning | :error :primary Progress bar color variant
show_label boolean false Show percentage label
""" end defp render_navbar_docs(assigns) do ~H"""

Navbar

Responsive navbar component with mobile menu support and customizable navigation items.

Basic Example

<:item href="/">Home <:item href="/about">About <:item href="/contact">Contact

    <.navbar brand="MyApp">
      <:item href="/">Home</:item>
      <:item href="/about">About</:item>
      <:item href="/contact">Contact</:item>
    </.navbar>
            

With Active State

<:item href="/" active={true}>Home <:item href="/about" active={false}>About <:item href="/contact" active={false}>Contact

    <.navbar brand="MyApp">
      <:item href="/" active={true}>Home</:item>
      <:item href="/about" active={false}>About</:item>
      <:item href="/contact" active={false}>Contact</:item>
    </.navbar>
            

With Actions Slot

<:item href="/">Home <:item href="/about">About <:actions> Sign In

    <.navbar brand="MyApp">
      <:item href="/">Home</:item>
      <:item href="/about">About</:item>
      <:actions>
        <.button variant={:primary} size={:sm}>Sign In</.button>
      </:actions>
    </.navbar>
            

Props

Prop Type Default Description
brand string "Brand" Brand name displayed in navbar
class string "" Additional CSS classes
item (slot) slot - Navigation items. Each item accepts:
  • href - Navigation path (required)
  • active - Boolean to mark active item (optional)
actions (slot) slot - Content displayed on the right side of the navbar (e.g., buttons, user menu)

Mobile Menu

The navbar automatically includes a mobile menu toggle button. To handle the mobile menu toggle, add a LiveView event handler:


    def handle_event("toggle-mobile-menu", _params, socket) do
      {:noreply, assign(socket, :mobile_menu_open, !socket.assigns[:mobile_menu_open])}
    end
            

Note: You'll need to add JavaScript or LiveView logic to show/hide the mobile menu based on the state.

""" end defp render_tabs_docs(assigns) do ~H"""

Tabs

Tabs component for navigation between different content sections.

Examples

<:tab id="tab-1" label="Tab 1" active={true}>Content for tab 1 <:tab id="tab-2" label="Tab 2" active={false}>Content for tab 2 <:tab id="tab-3" label="Tab 3" active={false}>Content for tab 3

    <.tabs>
      <:tab id="tab-1" label="Tab 1" active={true}>Content 1</:tab>
      <:tab id="tab-2" label="Tab 2">Content 2</:tab>
    </.tabs>
            

Props

Prop Type Default Description
id string "tabs" Unique identifier for tabs container
tab (slot) slot required Tab items with id, label, and active attributes
""" end defp render_badge_docs(assigns) do ~H"""

Badge

Badge component for status indicators and labels.

Examples

Default Primary Success Warning Error Info
Small Medium Large

    <.badge variant={:success}>Active</.badge>
    <.badge variant={:warning} size={:lg}>Pending</.badge>
            

Props

Prop Type Default Description
variant :default | :primary | :success | :warning | :error | :info :default Badge color variant
size :sm | :md | :lg :md Badge size
""" end defp render_modal_docs(assigns) do ~H"""

Modal

Modal dialog component for overlays and confirmations.

Examples

Modal requires LiveView state management. See code example below.

Open Modal

    <.modal id="confirm-modal" show={@show_modal}>
      <:title>Confirm Action</:title>
      <p>Are you sure you want to proceed?</p>
      <:footer>
        <.button variant={:secondary} phx-click="close-modal">Cancel</.button>
        <.button variant={:primary} phx-click="confirm">Confirm</.button>
      </:footer>
    </.modal>
            

Props

Prop Type Default Description
id string required Unique identifier for modal
show boolean false Control modal visibility
size :sm | :md | :lg | :xl :md Modal size
title (slot) slot - Modal title
footer (slot) slot - Modal footer actions
""" end defp render_table_docs(assigns) do ~H"""

Table

Data table component with sorting and pagination support.

Examples

<:col label="Name" field={:name} /> <:col label="Email" field={:email} /> <:col label="Status" field={:status} />

    <.table rows={@users}>
      <:col label="Name" field={:name} />
      <:col label="Email" field={:email} />
      <:col label="Actions">
        <.button size="sm">Edit</.button>
      </:col>
    </.table>
            

Props

Prop Type Default Description
rows list [] List of row data (maps)
col (slot) slot required Column definitions with label and optional field
""" end defp render_accordion_docs(assigns) do ~H"""
Accordion Layered Content

Accordion

Collapsible sections with buttery-smooth transitions that keep dense information organised. Perfect for FAQs, advanced settings, or progressive disclosure flows.

Interactive Example

Compose rich content within each item and decide which panels open by default for a guided story.

Auto Collapse
<:item id="accordion-design" title="Design strategy" open={true}>

Focus on progressive disclosure to keep your surfaces calm. Use accordions to tuck away secondary controls while keeping essential actions visible.

<:item id="accordion-content" title="Content patterns" open={false}>
  • Pair concise titles with short supporting copy.
  • Ensure interactive elements sit inside the panel, not in the summary.
  • Use icons or badges sparingly for additional hierarchy.
<:item id="accordion-motion" title="Micro-interactions" open={false}>

The component ships with subtle easing to avoid jarring jumps. Extend with Tailwind transitions for deeper motion stories.

    
    <.accordion class="space-y-3">
    <:item id="accordion-design" title="Design strategy" open={true}>
    ...
    </:item>
    <:item id="accordion-content" title="Content patterns" open={false}>
    ...
    </:item>
    </.accordion>
    
            

Usage Notes

Small heuristics to keep accordion experiences intuitive and accessible.

Guidelines
  • Keep summary rows concise. Long titles wrap to two lines max for ideal readability.
  • Default one key panel to open when the section loads so users see a preview of the content style.
  • Wrap complex layouts inside the panel body using flex/grid utilities to maintain rhythm.

Props

Drive the accordion with expressive slot attributes while inheriting accessible semantics out of the box.

Prop Type Default Description
item (slot) slot required Accordion rows with id, title, and optional open.
""" end defp render_breadcrumbs_docs(assigns) do ~H"""

Breadcrumbs

Breadcrumb navigation component for showing page hierarchy.

Examples

<:item href="/">Home <:item href="/products">Products <:item active={true}>Current Page

    <.breadcrumbs>
      <:item href="/">Home</:item>
      <:item href="/products">Products</:item>
      <:item active={true}>Current Page</:item>
    </.breadcrumbs>
            

Props

Prop Type Default Description
item (slot) slot required Breadcrumb items with href and optional active attribute
""" end defp render_grid_docs(assigns) do ~H"""

Grid

Grid layout component for responsive layouts using CSS Grid.

Basic Example

Item 1
Item 2
Item 3
Item 4
Item 5
Item 6

    <.grid cols={3} gap={4}>
      <div>Item 1</div>
      <div>Item 2</div>
      <div>Item 3</div>
    </.grid>
            

Responsive Grid

Item 1
Item 2
Item 3
Item 4

    <.grid cols={1} cols_md={2} cols_lg={4} gap={4}>
      <div>Item 1</div>
      <div>Item 2</div>
      <div>Item 3</div>
      <div>Item 4</div>
    </.grid>
            

This grid will show 1 column on mobile, 2 columns on medium screens, and 4 columns on large screens.

With Cards

Card 1

Content for card 1

Card 2

Content for card 2


    <.grid cols={1} cols_md={2} gap={6}>
      <.card>
        <h3>Card 1</h3>
        <p>Content</p>
      </.card>
      <.card>
        <h3>Card 2</h3>
        <p>Content</p>
      </.card>
    </.grid>
            

Props

Prop Type Default Description
cols integer 1 Number of columns (base/default)
cols_sm integer - Number of columns on small screens (≥640px)
cols_md integer - Number of columns on medium screens (≥768px)
cols_lg integer - Number of columns on large screens (≥1024px)
cols_xl integer - Number of columns on extra large screens (≥1280px)
gap integer 4 Gap between grid items (Tailwind gap value: 0-12)
class string "" Additional CSS classes
""" end defp render_list_docs(assigns) do ~H"""

List

Data list component for displaying structured information with titles and content.

Basic Example

<:item title="Name">John Doe <:item title="Email">john@example.com <:item title="Role">Administrator <:item title="Status">Active

    <.list>
      <:item title="Name">John Doe</:item>
      <:item title="Email">john@example.com</:item>
      <:item title="Role">Administrator</:item>
    </.list>
            

With Dynamic Content

<:item title="Post Title">Getting Started with Phoenix LiveView <:item title="Views">1,234 <:item title="Published">January 15, 2024 <:item title="Author">Jane Smith

    <.list>
      <:item title="Post Title">{@post.title}</:item>
      <:item title="Views">{@post.views}</:item>
      <:item title="Published">{@post.published_at}</:item>
    </.list>
            

With Badges

<:item title="Status"> Active <:item title="Priority"> High <:item title="Category"> Feature

    <.list>
      <:item title="Status">
        <.badge variant={:success}>Active</.badge>
      </:item>
      <:item title="Priority">
        <.badge variant={:warning}>High</.badge>
      </:item>
    </.list>
            

Props

Prop Type Default Description
item (slot) slot required List items. Each item accepts:
  • title - Title/label for the item (required)
""" end defp render_stats_docs(assigns) do ~H"""

Stats

Stats component for displaying statistics and metrics in a clean, organized layout.

Basic Example

<:stat title="Total Users" value="1,234" desc="21% more than last month" /> <:stat title="Revenue" value="$45,678" desc="12% increase" /> <:stat title="Orders" value="567" desc="8% decrease" />

    <.stats>
      <:stat title="Total Users" value="1,234" desc="21% more than last month" />
      <:stat title="Revenue" value="$45,678" desc="12% increase" />
      <:stat title="Orders" value="567" desc="8% decrease" />
    </.stats>
            

Vertical Layout

<:stat title="Page Views" value="89,400" desc="21% more than last month" /> <:stat title="Unique Visitors" value="12,345" desc="5% increase" />

    <.stats horizontal={false}>
      <:stat title="Page Views" value="89,400" desc="21% more than last month" />
      <:stat title="Unique Visitors" value="12,345" desc="5% increase" />
    </.stats>
            

With Dynamic Content

<:stat title="Active Users" value="2,456" desc="Last 30 days" /> <:stat title="Total Sales" value="$123,456" desc="This month" />

    <.stats>
      <:stat title="Active Users" value={@active_users} desc="Last 30 days" />
      <:stat title="Total Sales" value={@total_sales} desc="This month" />
    </.stats>
            

Props

Prop Type Default Description
horizontal boolean true Layout stats horizontally or vertically
class string "" Additional CSS classes
stat (slot) slot required Stat items. Each stat accepts:
  • title - Stat title/label
  • value - Main stat value
  • desc - Description or change indicator
""" end defp render_avatar_docs(assigns) do ~H"""

Avatar

Avatar component for displaying user profile pictures, initials, or custom content.

Basic Examples


    <.avatar name="John Doe" />
    <.avatar name="Jane Smith" />
    <.avatar name="AB" />
            

Sizes


    <.avatar name="XS" size={:xs} />
    <.avatar name="SM" size={:sm} />
    <.avatar name="MD" size={:md} />
    <.avatar name="LG" size={:lg} />
    <.avatar name="XL" size={:xl} />
            

With Image


    <.avatar src="/images/user.jpg" name="User Name" />
    <.avatar src="/images/user.jpg" name="User Name" size={:lg} />
            

Online Status


    <.avatar name="User" online={true} />
    <.avatar name="User" online={false} />
    <.avatar name="User" />
            

Custom Content

👤

    <.avatar>
      <.icon name="hero-user" class="w-6 h-6" />
    </.avatar>
    <.avatar>
      <span>👤</span>
    </.avatar>
            

Props

Prop Type Default Description
src string nil Image source URL. If provided, displays image instead of initials
name string nil Name for generating initials (first letter of each word, max 2 characters)
size :xs | :sm | :md | :lg | :xl :md Avatar size
online boolean nil Show online (true) or offline (false) status indicator. nil hides indicator
class string "" Additional CSS classes
""" end defp render_datepicker_docs(assigns) do ~H"""

DatePicker

DatePicker component for selecting dates, times, and date-time values with validation support.

Basic Date Picker


    <.datepicker label="Birth Date" field={@form[:birth_date]} />
    <.datepicker label="Start Date" value="2024-01-15" />
            

Date-Time Picker


    <.datepicker label="Appointment" type={:datetime_local} field={@form[:appointment]} />
    <.datepicker label="Event Time" type={:time} field={@form[:time]} />
            

With Date Range


    <.datepicker label="Start Date" min="2024-01-01" max="2024-12-31" />
    <.datepicker label="End Date" min="2024-01-01" max="2024-12-31" />
            

With Validation


    <.datepicker label="Required Date" required={true} field={@form[:date]} />
    <.datepicker label="Date with Error" error="This field is required" />
    <.datepicker label="Date with Helper" helper="Select a date in the future" />
            

Props

Prop Type Default Description
type :date | :datetime_local | :time | :month | :week :date Input type for date/time selection
field Phoenix.HTML.FormField - Form field struct from to_form/2
label string nil Label text displayed above the input
value string nil Default date/time value (ISO format)
min string nil Minimum selectable date/time (ISO format)
max string nil Maximum selectable date/time (ISO format)
error string nil Error message to display
helper string nil Helper text displayed below the input
required boolean false Mark field as required
disabled boolean false Disable the datepicker
""" end defp render_slider_docs(assigns) do ~H"""

Slider

Slider component for selecting a numeric value within a range with visual feedback.

Basic Slider


              <.slider label="Volume" value={50} />
              <.slider label="Brightness" value={75} />
              <.slider label="Opacity" value={25} />
            

Custom Range


              <.slider label="Temperature (°C)" value={20} min={-10} max={40} />
              <.slider label="Price Range ($)" value={500} min={0} max={1000} step={50} />
              <.slider label="Rating" value={3} min={1} max={5} step={1} />
            

Without Value Display


              <.slider label="Volume" value={50} show_value={false} />
              <.slider label="Speed" value={60} show_value={false} />
            

With Validation


              <.slider label="Required Value" value={50} error="This field is required" />
              <.slider label="Value with Helper" value={30} helper="Adjust the slider to set your preferred value" />
              <.slider label="Disabled Slider" value={50} disabled={true} />
            

With Form Field

When used with a form field, the slider integrates with Phoenix forms:


              <.form for={@form} phx-change="validate" phx-submit="save">
                <.slider label="Volume" field={@form[:volume]} />
                <.button type="submit">Save</.button>
              </.form>
            

Props

Prop Type Default Description
field Phoenix.HTML.FormField - Form field struct from to_form/2
label string nil Label text displayed above the slider
value integer 50 Current slider value
min integer 0 Minimum selectable value
max integer 100 Maximum selectable value
step integer 1 Step increment for slider value
error string nil Error message to display
helper string nil Helper text displayed below the slider
disabled boolean false Disable the slider
show_value boolean true Show current value next to the slider
""" end defp render_toast_docs(assigns) do ~H"""

Toast

Toast component for displaying temporary notifications that appear at fixed positions on the screen.

Basic Toast

Toasts are fixed position notifications. Here are examples of different variants:

Operation successful! Something went wrong Information message Warning message

              <.toast variant={:success}>Operation successful!</.toast>
              <.toast variant={:error}>Something went wrong</.toast>
              <.toast variant={:info}>Information message</.toast>
              <.toast variant={:warning}>Warning message</.toast>
            

Positions

Toasts can be positioned at different locations on the screen:

Top Start Top Center Bottom End

              <.toast variant={:info} position={:top_start}>Top Start</.toast>
              <.toast variant={:info} position={:top_center}>Top Center</.toast>
              <.toast variant={:info} position={:top_end}>Top End</.toast>
              <.toast variant={:info} position={:bottom_start}>Bottom Start</.toast>
              <.toast variant={:info} position={:bottom_center}>Bottom Center</.toast>
              <.toast variant={:info} position={:bottom_end}>Bottom End</.toast>
            

Auto-Dismiss

Toasts can automatically dismiss after a specified duration:

This toast will auto-dismiss in 3 seconds This toast won't auto-dismiss (duration={0})

              <.toast variant={:success} duration={3000}>Auto-dismiss in 3 seconds</.toast>
              <.toast variant={:info} duration={0}>No auto-dismiss</.toast>
            

Non-Dismissible

Toasts can be made non-dismissible by setting dismissible={false}:

This toast cannot be dismissed manually

              <.toast variant={:warning} dismissible={false}>Non-dismissible toast</.toast>
            

With LiveView

Toasts work seamlessly with LiveView. You can show/hide them using JS commands:


              # In your LiveView
              def handle_event("show-success", _params, socket) do
                {:noreply, socket}
              end

              # In your template
              <button phx-click="show-success" phx-click-js={JS.show(to: "#toast-success", transition: "fade-in-scale")}>
                Show Success Toast
              </button>

              <.toast id="toast-success" variant={:success}>
                Operation completed successfully!
              </.toast>
            

Props

Prop Type Default Description
id string auto-generated Unique ID for the toast (required for JS.show/hide)
variant :info | :success | :warning | :error :info Visual variant of the toast
position :top_start | :top_center | :top_end | :middle_start | :middle_center | :middle_end | :bottom_start | :bottom_center | :bottom_end :top_end Position of the toast on the screen
dismissible boolean true Show close button to manually dismiss
duration integer 5000 Auto-dismiss duration in milliseconds (0 to disable)
class string "" Additional CSS classes
""" end defp render_stepper_docs(assigns) do ~H"""

Stepper

Stepper component for displaying multi-step processes and progress indicators.

Basic Stepper

<:step label="Step 1" status={:completed} /> <:step label="Step 2" status={:active} /> <:step label="Step 3" status={:pending} /> <:step label="Step 4" status={:pending} />

              <.stepper>
                <:step label="Step 1" status={:completed} />
                <:step label="Step 2" status={:active} />
                <:step label="Step 3" status={:pending} />
                <:step label="Step 4" status={:pending} />
              </.stepper>
            

Vertical Stepper

<:step label="Account Setup" status={:completed} /> <:step label="Profile Information" status={:completed} /> <:step label="Preferences" status={:active} /> <:step label="Review" status={:pending} />

              <.stepper orientation={:vertical}>
                <:step label="Account Setup" status={:completed} />
                <:step label="Profile Information" status={:completed} />
                <:step label="Preferences" status={:active} />
                <:step label="Review" status={:pending} />
              </.stepper>
            

With Icons

<:step label="Cart" status={:completed} icon="hero-shopping-cart" /> <:step label="Shipping" status={:completed} icon="hero-truck" /> <:step label="Payment" status={:active} icon="hero-credit-card" /> <:step label="Review" status={:pending} icon="hero-check-circle" />

              <.stepper>
                <:step label="Cart" status={:completed} icon="hero-shopping-cart" />
                <:step label="Shipping" status={:completed} icon="hero-truck" />
                <:step label="Payment" status={:active} icon="hero-credit-card" />
                <:step label="Review" status={:pending} icon="hero-check-circle" />
              </.stepper>
            

With Variants

<:step label="Success" status={:completed} variant={:success} /> <:step label="Warning" status={:active} variant={:warning} /> <:step label="Error" status={:pending} variant={:error} />

              <.stepper>
                <:step label="Success" status={:completed} variant={:success} />
                <:step label="Warning" status={:active} variant={:warning} />
                <:step label="Error" status={:pending} variant={:error} />
              </.stepper>
            

Step Status

Pending (default):

<:step label="Pending Step" status={:pending} />

Active:

<:step label="Active Step" status={:active} />

Completed:

<:step label="Completed Step" status={:completed} />

              <:step label="Pending Step" status={:pending} />
              <:step label="Active Step" status={:active} />
              <:step label="Completed Step" status={:completed} />
            

Props

Prop Type Default Description
orientation :horizontal | :vertical :horizontal Layout orientation of the stepper
class string "" Additional CSS classes
Step Slot Props
label string nil Label text for the step
status :pending | :active | :completed :pending Status of the step
variant :primary | :secondary | :accent | :info | :success | :warning | :error :primary Color variant for active/completed steps
icon string nil Heroicon name (e.g., "hero-check-circle")
""" end defp render_chart_docs(assigns) do ~H"""

Chart

Chart component for displaying various types of data visualizations including Line, Bar, Pie, Donut, Area, Stacked, and Sparkline charts.

Line Chart


              <.chart type={:line} data={[10, 20, 30, 40, 50, 45, 35]} labels={["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul"]} />
            

Bar Chart


              <.chart type={:bar} data={[20, 35, 25, 45, 30]} labels={["Q1", "Q2", "Q3", "Q4", "Q5"]} />
            

Pie Chart


              <.chart type={:pie} data={[30, 25, 20, 15, 10]} labels={["A", "B", "C", "D", "E"]} />
            

Donut Chart


              <.chart type={:donut} data={[40, 30, 20, 10]} labels={["Desktop", "Mobile", "Tablet", "Other"]} />
            

Area Chart


              <.chart type={:area} data={[15, 25, 35, 30, 40, 35, 45]} />
            

Sparkline Chart


              <.chart type={:sparkline} data={[10, 15, 12, 18, 14, 20, 16]} width={200} height={50} />
            

Props

Prop Type Default Description
type :line | :bar | :pie | :donut | :area | :stacked | :sparkline - Chart type
data list - Array of numeric data points
labels list [] Labels for data points
width integer 400 Chart width in pixels
height integer 300 Chart height in pixels
colors list nil Custom color array (hex codes)
title string nil Chart title
""" end defp render_gauge_docs(assigns) do ~H"""

Gauge

Gauge component for displaying KPI metrics and progress indicators with circular progress visualization.

Basic Gauge


              <.gauge value={75.0} max={100.0} label="CPU Usage" />
              <.gauge value={60.0} max={100.0} label="Memory" />
            

Variants


              <.gauge value={80.0} variant={:success} label="Success" />
              <.gauge value={65.0} variant={:warning} label="Warning" />
              <.gauge value={50.0} variant={:error} label="Error" />
            

Sizes


              <.gauge value={70.0} size={:sm} label="Small" />
              <.gauge value={70.0} size={:md} label="Medium" />
              <.gauge value={70.0} size={:lg} label="Large" />
            

Props

Prop Type Default Description
value float - Current value
max float 100.0 Maximum value
label string nil Label text below gauge
variant :primary | :success | :warning | :error :primary Color variant
size :sm | :md | :lg :md Gauge size
""" end defp render_heatmap_docs(assigns) do sample_data = %{ "2024-01-01" => 5, "2024-01-02" => 10, "2024-01-03" => 15, "2024-01-04" => 20, "2024-01-05" => 25, "2024-01-06" => 30, "2024-01-07" => 35 } assigns = assign(assigns, :sample_data, sample_data) ~H"""

Heatmap

Heatmap component for displaying data density and user interactions using color intensity visualization.

Basic Heatmap


              <.heatmap data={%{"2024-01-01" => 5, "2024-01-02" => 10, "2024-01-03" => 15}} />
            

Color Schemes

Blue (default):

Green:

Red:

Purple:


              <.heatmap data={heatmap_data} color_scheme={:blue} />
              <.heatmap data={heatmap_data} color_scheme={:green} />
              <.heatmap data={heatmap_data} color_scheme={:red} />
              <.heatmap data={heatmap_data} color_scheme={:purple} />
            

Props

Prop Type Default Description
data map - Map of keys (dates/labels) to numeric values
color_scheme :blue | :green | :red | :purple :blue Color scheme for intensity visualization
""" end defp render_map_docs(assigns) do ~H"""

Map

Interactive map component for displaying locations and events. Uses OpenStreetMap tiles for rendering.

Basic Map


              <.map latitude={37.7749} longitude={-122.4194} zoom={13} />
            

With Markers


              <.map
                latitude={40.7128}
                longitude={-74.0060}
                zoom={12}
                markers={[
                  %{lat: 40.7128, lng: -74.0060, label: "New York"},
                  %{lat: 40.7589, lng: -73.9851, label: "Times Square"}
                ]}
              />
            

Custom Size


              <.map latitude={51.5074} longitude={-0.1278} zoom={10} width="600px" height="300px" />
            

Props

Prop Type Default Description
latitude float - Center latitude coordinate
longitude float - Center longitude coordinate
zoom integer 13 Zoom level (1-18)
markers list [] List of marker maps with :lat, :lng, and optional :label
width string "100%" Map width (CSS value)
height string "400px" Map height (CSS value)

Note

The map component requires a JavaScript hook (MapHook) for full interactivity. For production use, consider integrating with Mapbox, Google Maps, or Leaflet.js for enhanced features.

""" end defp render_file_upload_docs(assigns) do ~H"""

File Upload

File upload component with drag & drop support and live progress tracking. Uses Phoenix LiveView's built-in file upload functionality.

Basic File Upload

Note: This component requires LiveView upload configuration in your LiveView module.

File upload component preview

Configure uploads in your LiveView to see the full component


              <.file_upload uploads={@uploads.avatar} label="Upload Avatar" />
            

Multiple Files


              <.file_upload uploads={@uploads.documents} accept=".pdf,.doc" max_entries={5} />
            

LiveView Setup


              def mount(_params, _session, socket) do
                {:ok,
                 socket
                 |> allow_upload(:avatar, accept: ~w(.jpg .jpeg .png), max_entries: 1, max_file_size: 10_000_000)}
              end

              def handle_event("validate", _params, socket) do
                {:noreply, socket}
              end

              def handle_event("cancel-upload", %{"ref" => ref}, socket) do
                {:noreply, cancel_upload(socket, :avatar, ref)}
              end
            

Props

Prop Type Default Description
uploads map - Uploads map from socket (e.g., @uploads.avatar)
accept string "*/*" Accepted file types (e.g., "image/*", ".pdf,.doc")
max_entries integer 1 Maximum number of files
max_file_size integer 10485760 Maximum file size in bytes (default: 10MB)
drag_label string "Drag and drop files here..." Custom drag & drop label text
""" end defp render_rich_text_editor_docs(assigns) do ~H"""

Rich Text Editor

Rich text editor / WYSIWYG component for blogs, comments, and content editing with formatting controls.

Interactive Example

Try the editor below - use the toolbar buttons or keyboard shortcuts to format your text!

<%= if @rich_text_content do %>

Current HTML Output:

{@rich_text_content}
<% end %>

              <.rich_text_editor field={@form[:content]} label="Content" />
            

Features

Text Formatting

  • Bold (Ctrl+B)
  • Italic (Ctrl+I)
  • Underline (Ctrl+U)
  • Strikethrough

Headings

  • Heading 1 (H1)
  • Heading 2 (H2)
  • Heading 3 (H3)
  • Paragraph (P)

Lists & Alignment

  • Bullet Lists
  • Numbered Lists
  • Text Alignment (Left, Center, Right, Justify)
  • Indentation (Tab / Shift+Tab)

Special Features

  • Links (Ctrl+K)
  • Blockquotes
  • Code Blocks
  • Undo/Redo (Ctrl+Z / Ctrl+Y)
  • Clear Formatting

With Initial Content


              <.rich_text_editor
                field={@form[:content]}
                value={"<h2>Welcome</h2><p>This is <strong>pre-filled</strong> content!</p>"}
                label="Pre-filled Editor"
              />
            

Keyboard Shortcuts

Formatting

  • Ctrl+B - Bold
  • Ctrl+I - Italic
  • Ctrl+U - Underline
  • Ctrl+K - Insert Link

Editing

  • Ctrl+Z - Undo
  • Ctrl+Y - Redo
  • Tab - Indent
  • Shift+Tab - Outdent

Note

The rich text editor uses a JavaScript hook (RichTextEditorHook) to sync content with the hidden input field for form submission.

All formatting is handled client-side using the browser's document.execCommand API. The HTML content is automatically synced to a hidden input field for form submission.

Props

Prop Type Default Description
field Phoenix.HTML.FormField - Form field struct
label string nil Label text
placeholder string "Start typing..." Placeholder text
value string nil Initial HTML content
""" end defp render_multi_select_docs(assigns) do ~H"""

Multi-Select

Multi-select component with chips/tags display for selecting multiple items from a list.

Basic Multi-Select

Select options...
<.icon name="hero-chevron-down" class="w-5 h-5 text-gray-400" />

              <.multi_select field={@form[:tags]} options={["Option 1", "Option 2", "Option 3"]} />
            

With Selected Values

Option 1 Option 2
<.icon name="hero-chevron-down" class="w-5 h-5 text-gray-400" />

              <.multi_select field={@form[:categories]} options={categories} label="Categories" />
            

With Label-Value Pairs


              <.multi_select
                field={@form[:tags]}
                options={[
                  {"Frontend", "frontend"},
                  {"Backend", "backend"},
                  {"DevOps", "devops"}
                ]}
              />
            

Note

The multi-select component requires a JavaScript hook (MultiSelectHook) for dropdown toggle functionality. Selected values are submitted as an array in the form params.

Props

Prop Type Default Description
field Phoenix.HTML.FormField - Form field struct
options list - List of options (strings or label-value tuples)
placeholder string "Select options..." Placeholder text when no items selected
""" end defp render_rating_docs(assigns) do ~H"""

Rating

Rating / Star input component for reviews and feedback with interactive star selection.

Basic Rating

<%= for i <- 1..5 do %> <% end %> 3/5

              <.rating field={@form[:rating]} />
            

Sizes

Small:

<%= for i <- 1..5 do %> <.icon name="hero-star" class="w-4 h-4 text-gray-300" /> <% end %>

Medium (default):

<%= for i <- 1..5 do %> <.icon name="hero-star" class="w-6 h-6 text-gray-300" /> <% end %>

Large:

<%= for i <- 1..5 do %> <.icon name="hero-star" class="w-8 h-8 text-gray-300" /> <% end %>

              <.rating field={@form[:score]} size={:sm} />
              <.rating field={@form[:score]} size={:md} />
              <.rating field={@form[:score]} size={:lg} />
            

Readonly Rating

<%= for i <- 1..5 do %>
<%= if i <= 4 do %> <.icon name="hero-star" class="w-6 h-6 text-yellow-400 fill-yellow-400" /> <% else %> <.icon name="hero-star" class="w-6 h-6 text-gray-300" /> <% end %>
<% end %> 4/5

              <.rating value={4} readonly={true} />
            

Custom Max Rating


              <.rating field={@form[:score]} max={10} />
            

Note

The rating component requires a JavaScript hook (RatingHook) for interactive star selection.

Props

Prop Type Default Description
field Phoenix.HTML.FormField nil Form field struct (optional)
value integer 0 Current rating value
max integer 5 Maximum rating (number of stars)
size :sm | :md | :lg :md Star size
readonly boolean false Display-only mode (no interaction)
""" end defp render_password_strength_meter_docs(assigns) do ~H"""

Password Strength Meter

Password input component with live strength validation and suggestions.

Basic Usage


    <.password_strength_meter id="password-input" field={@form[:password]} />
            

With Suggestions


    <.password_strength_meter id="signup-password" field={@form[:password]} show_suggestions={true} />
            
""" end defp render_tag_input_docs(assigns) do ~H"""

Tag Input

Tag / Chips input component for adding and removing multiple tags dynamically.

Basic Usage


    <.tag_input id="tags-input" tags={@tags} />
            

With Max Tags


    <.tag_input id="skills-input" tags={@skills} placeholder="Add skills..." max_tags={5} />
            
""" end defp render_signature_pad_docs(assigns) do ~H"""

Signature Pad

Signature pad / drawing canvas component for approvals or annotations.

Basic Usage


    <.signature_pad id="signature-pad" width={400} height={200} />
            

Custom Size


    <.signature_pad id="approval-signature" width={600} height={300} clear_label="Clear" />
            
""" end defp render_color_picker_docs(assigns) do ~H"""

Color Picker

Color picker / palette selector component for design dashboards or admin apps.

Basic Usage


    <.color_picker id="primary-color" value="#3b82f6" />
            

With Palette


    <.color_picker id="theme-color" value="#000000" show_palette={true} />
            
""" end defp render_conditional_form_docs(assigns) do ~H"""

Conditional Form

Conditional / dynamic form component where fields appear/disappear based on user input.

Basic Usage

<:field name="user_type" type="select" options={["individual", "business"]} /> <:conditional_field show_if={%{"user_type" => "business"}}>

    <.conditional_form id="dynamic-form">
      <:field name="user_type" type="select" options={["individual", "business"]} />
      <:conditional_field show_if={%{"user_type" => "business"}}>
        <div class="mb-4">
          <label for="company_name-input">Company Name</label>
          <input type="text" id="company_name-input" name="company_name" />
        </div>
      </:conditional_field>
    </.conditional_form>
            
""" end defp render_carousel_docs(assigns) do ~H"""

Carousel

Carousel component for displaying images, testimonials, or content cards in a sliding carousel with navigation controls.

Basic Carousel

<:slide>

Slide 1

First slide content

<:slide>

Slide 2

Second slide content

<:slide>

Slide 3

Third slide content


              <.carousel id="my-carousel">
                <:slide>
                  <div class="p-8">Slide 1</div>
                </:slide>
                <:slide>
                  <div class="p-8">Slide 2</div>
                </:slide>
              </.carousel>
            

With Autoplay


              <.carousel id="autoplay-carousel" autoplay={true} interval={3000} />
            

Props

Prop Type Default Description
id string - Unique ID for the carousel
autoplay boolean false Automatically advance slides
interval integer 5000 Autoplay interval in milliseconds
show_indicators boolean true Show slide indicators
show_arrows boolean true Show navigation arrows
""" end defp render_wizard_docs(assigns) do ~H"""

Wizard

Wizard component for multi-step forms or flows with navigation, progress tracking, and validation support.

Basic Wizard

<:step id="step1" title="Personal Info" active={true}>

Step 1: Personal Information

Enter your personal details here.

<:step id="step2" title="Account Details">

Step 2: Account Details

Set up your account information.

<:step id="step3" title="Review">

Step 3: Review

Review and confirm your information.


              <.wizard id="signup-wizard">
                <:step id="step1" title="Personal Info" active={true}>
                  <p>Step 1 content</p>
                </:step>
                <:step id="step2" title="Account Details">
                  <p>Step 2 content</p>
                </:step>
              </.wizard>
            

Props

Prop Type Default Description
id string - Unique ID for the wizard
step.id string - Unique ID for each step
step.title string - Step title
step.active boolean false Whether the step is currently active
step.completed boolean false Whether the step is completed
""" end defp render_resizable_panels_docs(assigns) do ~H"""

Resizable Panels

Resizable panels component for split layouts like IDEs or dashboards. Users can drag to resize panels horizontally or vertically.

Horizontal Panels

<:panel min_width="150px" default_width="300px">

Left Panel

Drag the divider to resize

<:panel>

Right Panel

Resizable content area


              <.resizable_panels id="split-layout" orientation={:horizontal}>
                <:panel min_width="200px" default_width="300px">
                  <div class="p-4">Left Panel</div>
                </:panel>
                <:panel>
                  <div class="p-4">Right Panel</div>
                </:panel>
              </.resizable_panels>
            

Props

Prop Type Default Description
id string - Unique ID for the panels
orientation :horizontal | :vertical :horizontal Layout orientation
panel.min_width string "100px" Minimum panel width
panel.default_width string nil Initial panel width
""" end defp render_filter_panel_docs(assigns) do ~H"""

Filter Panel

Filter panel component for faceted search and real-time filtering of data tables with collapsible sections.

Basic Filter Panel

<:filter label="Category" field="category" type={:checkbox}> <:option field="category" value="electronics" label="Electronics" count={42} /> <:option field="category" value="clothing" label="Clothing" count={28} /> <:option field="category" value="books" label="Books" count={15} /> <:filter label="Price Range" field="price" type={:radio}> <:option field="price" value="0-50" label="$0 - $50" /> <:option field="price" value="50-100" label="$50 - $100" /> <:option field="price" value="100+" label="$100+" />

              <.filter_panel id="product-filters">
                <:filter label="Category" field="category">
                </:filter>
                <:option field="category" value="electronics" label="Electronics" count={42} />
                <:option field="category" value="clothing" label="Clothing" count={28} />
                <:filter label="Price Range" field="price" type={:radio}>
                </:filter>
                <:option field="price" value="0-50" label="$0 - $50" />
              </.filter_panel>
            

Props

Prop Type Default Description
id string - Unique ID for the filter panel
title string "Filters" Panel title
collapsible boolean true Allow collapsing the panel
filter.type :checkbox | :radio | :range :checkbox Filter input type
""" end defp render_skeleton_docs(assigns) do ~H"""

Skeleton

Skeleton component for loading placeholders during async data loading. Provides visual feedback while content is being fetched.

Text Skeletons


              <.skeleton variant={:text} size={:md} />
              <.skeleton variant={:text} lines={3} />
            

Circle Skeletons


              <.skeleton variant={:circle} size={:md} />
            

Custom Dimensions


              <.skeleton variant={:rect} width="200px" height="100px" />
            

Props

Prop Type Default Description
variant :text | :circle | :rect :text Skeleton shape variant
size :sm | :md | :lg :md Size for text and circle variants
lines integer 1 Number of lines for text variant
width string nil Custom width (e.g., "200px")
height string nil Custom height (e.g., "100px")
""" end defp render_skeleton_layout_docs(assigns) do ~H"""

Skeleton Layout

Full-page skeleton layouts for dashboard, article, and profile loading states.

Dashboard Skeleton

Article & Profile

Usage


    <.skeleton_layout variant={:dashboard} />
    <.skeleton_layout variant={:article} class="mt-6" />
            
""" end defp render_autocomplete_docs(assigns) do sample_options = [ "Apple", "Banana", "Cherry", "Date", "Elderberry", "Fig", "Grape", "Honeydew" ] assigns = assign(assigns, :sample_options, sample_options) ~H"""

Autocomplete

Autocomplete component with live search and suggestion list while typing. Provides keyboard navigation and debounced search.

Basic Autocomplete


              <.autocomplete id="search-users" field={@form[:user]} options={users} />
            

With Form Field


              <.autocomplete id="product-search" field={@form[:product]} options={products} min_chars={2} />
            

With Custom Options


              <.autocomplete
                id="user-search"
                options={[{"John Doe", "1"}, {"Jane Smith", "2"}]}
                max_results={5}
              />
            

Props

Prop Type Default Description
id string - Unique ID for the autocomplete
field Phoenix.HTML.FormField nil Form field struct (optional)
options list - List of options (strings or label-value tuples)
min_chars integer 1 Minimum characters before showing suggestions
max_results integer 10 Maximum number of suggestions to show
""" end defp render_sortable_list_docs(assigns) do sample_items = [ %{id: 1, title: "First Item", description: "Drag me around"}, %{id: 2, title: "Second Item", description: "Rearrange as needed"}, %{id: 3, title: "Third Item", description: "Drop anywhere"}, %{id: 4, title: "Fourth Item", description: "Order is preserved"} ] assigns = assign(assigns, :sample_items, sample_items) ~H"""

Sortable List

Sortable list component for drag & drop reordering of items. Users can visually rearrange items by dragging.

Basic Sortable List

<:item :let={item}>

{item.title}

{item.description}

<.icon name="hero-bars-3" class="w-5 h-5 text-gray-400" />

              <.sortable_list id="todo-list" items={@todos}>
                <:item :let={todo}>
                  <div class="p-4 border rounded">
                    <%= todo.title %>
                  </div>
                </:item>
              </.sortable_list>
            

Custom Item ID Key


              <.sortable_list id="items" items={@items} item_id={:uuid}>
                <:item :let={item}>
                  <%= item.name %>
                </:item>
              </.sortable_list>
            

Note

The sortable list component requires a JavaScript hook (SortableListHook) for drag & drop functionality. The order is automatically tracked in a hidden input field.

Props

Prop Type Default Description
id string - Unique ID for the sortable list
items list - List of items to display
item_id atom :id Key to use as unique identifier for items
""" end defp render_live_chat_docs(assigns) do sample_messages = [ %{ id: 1, user_id: "user1", user_name: "Alice", text: "Hello! How are you?", timestamp: DateTime.utc_now() }, %{ id: 2, user_id: "user2", user_name: "Bob", text: "I'm doing great, thanks!", timestamp: DateTime.utc_now() }, %{ id: 3, user_id: "user1", user_name: "Alice", text: "That's wonderful to hear!", timestamp: DateTime.utc_now() } ] assigns = assign(assigns, :sample_messages, sample_messages) ~H"""

Live Chat

Live chat component with typing indicators and real-time messaging. Supports message bubbles, user identification, and typing status.

Basic Live Chat


              <.live_chat id="chat-room" messages={@messages} current_user={@current_user} />
            

With Typing Indicators


              <.live_chat
                id="chat-room"
                messages={@messages}
                current_user={@current_user}
                typing_users={@typing_users}
              />
            

Props

Prop Type Default Description
id string - Unique ID for the chat
messages list [] List of message maps with :text, :user_id, :user_name, :timestamp
typing_users list [] List of users currently typing
""" end defp render_live_feed_docs(assigns) do sample_items = [ %{ id: 1, title: "New Order", content: "Order #1234 has been placed", timestamp: DateTime.utc_now(), action: "view", action_label: "View Order" }, %{ id: 2, title: "Payment Received", content: "Payment of $99.99 received", timestamp: DateTime.utc_now() }, %{ id: 3, title: "User Registration", content: "New user registered", timestamp: DateTime.utc_now(), action: "view", action_label: "View User" } ] assigns = assign(assigns, :sample_items, sample_items) ~H"""

Live Feed

Live feed component for scrollable real-time updates and notifications. Automatically scrolls to new items and tracks user scroll position.

Basic Live Feed


              <.live_feed id="notifications" items={@notifications} />
            

With Custom Height


              <.live_feed id="feed" items={@items} max_height="500px" auto_scroll={true} />
            

Props

Prop Type Default Description
id string - Unique ID for the feed
items list [] List of feed items with :title, :content, :timestamp, :avatar
auto_scroll boolean true Automatically scroll to new items
""" end defp render_countdown_docs(assigns) do future_date = DateTime.add(DateTime.utc_now(), 3600, :second) assigns = assign(assigns, :future_date, future_date) ~H"""

Countdown

Countdown timer component for events, offers, or games. Updates in real-time and dispatches events when complete.

Compact Format


              <.countdown id="sale-timer" target_date={~U[2024-12-31 23:59:59Z]} />
            

Detailed Format


              <.countdown id="event-timer" target_date={@event_date} format={:detailed} />
            

With Completion Event


              <.countdown
                id="timer"
                target_date={@target_date}
                on_complete="countdown-complete"
              />
            

Props

Prop Type Default Description
id string - Unique ID for the countdown
target_date DateTime | NaiveDateTime | string - Target date/time for countdown
format atom :compact Display format (:compact or :detailed)
on_complete string nil Event name to dispatch when countdown completes
""" end defp render_kanban_docs(assigns) do columns = [ %{ id: "todo", title: "To Do", cards: [ %{ id: "card-1", title: "Design homepage", description: "Create wireframes", tags: ["design", "ui"] }, %{id: "card-2", title: "Setup database", description: "Configure PostgreSQL"} ] }, %{ id: "in-progress", title: "In Progress", cards: [ %{ id: "card-3", title: "Implement auth", description: "Add user authentication", tags: ["backend"] } ] }, %{ id: "done", title: "Done", cards: [ %{id: "card-4", title: "Setup project", description: "Initialize Phoenix app"} ] } ] assigns = assign(assigns, :demo_columns, columns) ~H"""

Kanban Board

Kanban / Task Board component with draggable columns and cards for project management apps.

Basic Usage


    tasks = [
      %{
        id: "task-1",
        name: "Project Planning",
        start_date: ~D[2024-01-01],
        end_date: ~D[2024-01-05],
        progress: 30
      }
    ]

    <.gantt id="project-timeline" tasks={@tasks} />
            

With Custom Date Range


    <.gantt
      id="project-timeline"
      tasks={@tasks}
      start_date={~D[2024-01-01]}
      end_date={~D[2024-12-31]}
    />
            

Features

  • Draggable tasks to adjust dates
  • Resizable task bars
  • Dependency lines between tasks
  • Progress indicators
  • Customizable colors and styling
  • Auto-calculated date ranges
""" end defp render_gantt_docs(assigns) do tasks = [ %{ id: "gantt-task-1", name: "Project Kickoff", start_date: ~D[2024-01-02], end_date: ~D[2024-01-05], progress: 100, color: "blue" }, %{ id: "gantt-task-2", name: "Design Sprint", start_date: ~D[2024-01-06], end_date: ~D[2024-01-12], progress: 65, color: "purple", dependencies: ["gantt-task-1"] }, %{ id: "gantt-task-3", name: "Implementation", start_date: ~D[2024-01-13], end_date: ~D[2024-01-24], progress: 35, color: "emerald", dependencies: ["gantt-task-2"] }, %{ id: "gantt-task-4", name: "QA & Launch", start_date: ~D[2024-01-25], end_date: ~D[2024-01-31], progress: 10, color: "amber", dependencies: ["gantt-task-3"] } ] assigns = assigns |> assign(:demo_tasks, tasks) |> assign(:demo_start_date, ~D[2024-01-01]) |> assign(:demo_end_date, ~D[2024-02-05]) ~H"""

Gantt Chart

Interactive Gantt chart for project timelines with drag, resize, and dependency visuals.

Project Roadmap


    tasks = [
      %{
        id: "task-1",
        name: "Kickoff",
        start_date: ~D[2024-01-02],
        end_date: ~D[2024-01-05],
        progress: 100
      },
      %{
        id: "task-2",
        name: "Design Sprint",
        start_date: ~D[2024-01-06],
        end_date: ~D[2024-01-12],
        dependencies: ["task-1"]
      }
    ]

    <.gantt
      id="product-roadmap"
      tasks={@tasks}
      start_date={~D[2024-01-01]}
      end_date={~D[2024-01-31]}
    />
            

Highlights

  • Auto-calculated date range based on tasks with optional overrides.
  • Drag and resize hooks emit events for task updates.
  • Dependency lines with arrowheads for critical path visualization.
  • Slot support for custom task labels and interactive row content.
  • Configurable row and bar sizing for dense timelines.
""" end defp render_live_data_table_docs(assigns) do columns = [ %{key: "id", label: "ID", sortable: true, width: "80px"}, %{key: "name", label: "Name", sortable: true}, %{key: "email", label: "Email", sortable: true}, %{key: "role", label: "Role", sortable: true, width: "120px"}, %{key: "status", label: "Status", sortable: true, width: "100px"} ] # Generate sample data (large dataset for virtual scroll demo) rows = 1..100 |> Enum.map(fn i -> %{ id: i, name: "User #{i}", email: "user#{i}@example.com", role: if(rem(i, 3) == 0, do: "Admin", else: "User"), status: if(rem(i, 2) == 0, do: "Active", else: "Inactive") } end) assigns = assigns |> assign(:demo_columns, columns) |> assign(:demo_rows, rows) ~H"""

Live Data Table

Live Data Table component with virtual scrolling for handling huge datasets efficiently. Supports PubSub updates, sorting, filtering, and row selection.

Basic Usage


    columns = [
      %{key: "id", label: "ID", sortable: true},
      %{key: "name", label: "Name", sortable: true},
      %{key: "email", label: "Email", sortable: true}
    ]

    <.live_data_table
      id="users-table"
      rows={@users}
      columns={@columns}
      row_height={50}
      visible_rows={10}
    />
            

With Row Selection


    <.live_data_table
      id="users-table"
      rows={@users}
      columns={@columns}
      enable_selection={true}
      selected_rows={@selected_rows}
    />
            

Features

  • Virtual scrolling for huge datasets (10,000+ rows)
  • Column sorting (ascending/descending)
  • Real-time filtering/search
  • Row selection (single or multiple)
  • PubSub integration for live updates
  • Customizable row and column rendering
  • Responsive and accessible

Event Handlers

The component dispatches the following events to your LiveView:

  • filter-table - When filter value changes
  • sort-column - When column header is clicked
  • toggle-row-selection - When row checkbox is toggled
  • select-all-rows - When header checkbox is clicked
""" end defp render_user_profile_card_docs(assigns) do sample_actions = [ %{label: "Message", variant: :primary, on_click: "message-user"}, %{label: "Follow", variant: :outline, on_click: "follow-user"} ] assigns = assign(assigns, :sample_actions, sample_actions) ~H"""

User Profile Card

User profile card component with online/offline status, avatar, and user information. Perfect for displaying user details in a card format.

Basic Profile Card


              <.user_profile_card
                name="John Doe"
                email="john@example.com"
                avatar="/images/avatar.jpg"
                online={true}
              />
            

With Actions


              <.user_profile_card
                name="Jane Smith"
                actions={[
                  %{label: "Message", variant: :primary, on_click: "message-user"},
                  %{label: "Follow", variant: :outline, on_click: "follow-user"}
                ]}
              />
            

Props

Prop Type Default Description
name string - User's full name
online boolean false Online status indicator
avatar string nil URL to user's avatar image
""" end defp render_avatar_group_docs(assigns) do sample_users = [ %{name: "Alice", online: true}, %{name: "Bob", online: false}, %{name: "Charlie", online: true}, %{name: "Diana", online: true}, %{name: "Eve", online: false}, %{name: "Frank", online: true} ] assigns = assign(assigns, :sample_users, sample_users) ~H"""

Avatar Group

Avatar group component for displaying multiple users in a row with overlapping avatars. Supports online status indicators and overflow count.

Stacked Avatars


              <.avatar_group users={@users} max_visible={5} />
            

Different Sizes

Small

Medium

Large

Props

Prop Type Default Description
users list - List of user maps with :name, :avatar, :online
max_visible integer 5 Maximum number of avatars to show
size atom :md Avatar size (:sm, :md, :lg)
""" end defp render_connections_widget_docs(assigns) do demo_connections = [ %{ id: 1, name: "Ava Johnson", username: "ava.designs", avatar: "https://i.pravatar.cc/80?img=12", is_connected: true, mutual_count: 12, online: true }, %{ id: 2, name: "Mateo García", username: "mateo.codes", avatar: "https://i.pravatar.cc/80?img=32", is_connected: false, mutual_count: 6, online: false }, %{ id: 3, name: "Priya Patel", username: "priya.product", avatar: "https://i.pravatar.cc/80?img=45", is_connected: true, mutual_count: 18, online: true }, %{ id: 4, name: "Noah Williams", username: "noah.analytics", is_connected: false, mutual_count: 3, online: true } ] assigns = assigns |> assign(:demo_connections, demo_connections) |> assign(:demo_current_user, 99) ~H"""

Connections Widget

Social-style connections list with avatars, status indicators, mutual counts, and custom actions.

Followers Panel

<:action>

    <.connections_widget
      connections={@connections}
      current_user={@current_user_id}
      type={:followers}
      max_display={5}
    >
      <:action>
        <button class="btn btn-secondary">View All</button>
      </:action>
    </.connections_widget>
            

Highlights

  • Supports followers, following, or connections context via the type attribute.
  • Built-in mutual connection counts and online indicators.
  • Optional custom action slot for CTA buttons or dropdowns.
  • Responsive layout with graceful empty states when no data is available.
""" end defp render_badge_card_docs(assigns) do badges = [ %{ title: "First Steps", description: "Complete your onboarding checklist", icon: "hero-flag", unlocked: true, progress: 100, rarity: :common, points: 50, unlocked_at: ~D[2024-01-12] }, %{ title: "Collaboration Pro", description: "Invite five teammates to your workspace", icon: "hero-users", unlocked: false, progress: 60, rarity: :rare, points: 120 }, %{ title: "Ship It", description: "Launch your first project milestone", icon: "hero-rocket-launch", unlocked: false, progress: 25, rarity: :epic, points: 250 } ] assigns = assign(assigns, :demo_badges, badges) ~H"""

Badge Card

Gamified badge card for achievements, loyalty programs, and progress tracking.

Badge Showcase

<%= for badge <- @demo_badges do %> <% end %>

    <.badge_card
      title="Collaboration Pro"
      description="Invite five teammates"
      icon="hero-users"
      rarity={:rare}
      progress={60}
      points={120}
    />
            

Tips

  • Use the rarity attr to theme border and icon colors.
  • Show progress bar for locked achievements with progress.
  • Include points and unlocked_at for richer context.
""" end defp render_theme_switch_docs(assigns) do ~H"""
Theme Switch Dynamic Modes

Theme Switch

Accessible toggle powered by the `ThemeToggleHook`. Deliver effortless transitions between light and dark palettes with a single interactive element.

Sizes & Labels

Choose from multiple sizes and optionally surface accessibility labels to clarify mode changes.

Responsive
Labelled Variant
    
    <.theme_switch />
    <.theme_switch size={:lg} />
    <.theme_switch show_label={true} />
    
            

Usage Notes

Integrate the switch with minimal wiring and customise visuals using Tailwind utilities to match your brand aura.

Hook powered
  • Works out of the box with the bundled ThemeToggleHook defined in assets/js/app.js.
  • Set show_label for explicit light/dark annotations.
  • Extend styling with additional Tailwind classes through the class attribute for unique theming.
""" end defp render_alert_docs(assigns) do ~H"""
Alert Docs

Alert

ARIA-friendly alert component for displaying important messages to users. Supports multiple variants and dismissible alerts so teams can deliver timely, accessible feedback.

Alert Variants

Demonstrates each semantic variant and how it harmonises with the dark UI foundation.

Auto adaptive
    
    <.alert variant={:info} title="Information" message="This is an info alert" />
    <.alert variant={:success} title="Success" message="Operation successful" />
    <.alert variant={:warning} title="Warning" message="Please review" />
    <.alert variant={:error} title="Error" message="Something went wrong" />
    
            

Dismissible Alert

Enable dismissible alerts for temporary feedback that users can clear once acknowledged.

    
    <.alert variant={:info} dismissible={true} title="Alert" message="Message" />
    
            

Props

Configure alerts with a small, expressive API. Defaults are optimised for accessibility and theme coherence.

Prop Type Default Description
variant atom :info Alert variant (:info, :success, :warning, :error)
dismissible boolean false Allow dismissing the alert
role string "alert" ARIA role attribute
""" end defp render_theme_config_docs(assigns) do colors = Ashui.Theme.colors() spacing = Ashui.Theme.spacing_tokens() fonts = Ashui.Theme.fonts() assigns = assigns |> assign(:colors, colors) |> assign(:spacing, spacing) |> assign(:fonts, fonts) ~H"""
Theme Config

Theme Configuration

Configurable theme system with color, spacing, and font tokens. Use the `Ashui.Theme` module to orchestrate consistent styling programmatically across your product.

Color Tokens

Curated palette engineered for luminous contrast on dark surfaces.

<%= for {name, value} <- @colors do %>
{name}
{value} Token
<% end %>
    
    # Get a color token
    Ashui.Theme.color(:primary)
    # => "oklch(70% 0.213 47.604)"

    # Get all colors
    Ashui.Theme.colors()
    
            

Spacing Tokens

Modular scale spacing mapped to responsive rhythm.

<%= for {name, value} <- @spacing do %>
{name}
{value}
<% end %>
    
    # Get a spacing token
    Ashui.Theme.spacing(:md)
    # => "1rem"

    # Get all spacing tokens
    Ashui.Theme.spacing_tokens()
    
            

Font Tokens

Typography stacks that deliver clarity across platforms.

<%= for {name, families} <- @fonts do %>
{name}
The quick brown fox jumps over the lazy dog
Stack {Enum.join(families, ", ")}
<% end %>
    
    # Get a font token
    Ashui.Theme.font(:sans)
    # => ["ui-sans-serif", "system-ui", "sans-serif"]

    # Get all fonts
    Ashui.Theme.fonts()
    
            

Theme Functions

Leverage helper functions to access tokens inside LiveViews, components, and modules.

Function Description
Ashui.Theme.color/1 Get a color token value
Ashui.Theme.spacing/1 Get a spacing token value
Ashui.Theme.font/1 Get a font token value
Ashui.Theme.current_theme/1 Get current theme from assigns
""" end defp render_diff_docs(assigns) do ~H"""

Diff

Diff component for side-by-side comparison of two items (text, code, etc.).

Text Diff


              <.diff left="Old content" right="New content" />
            

Code Diff

Props

Prop Type Default Description
left string - Left side content
right string - Right side content
type atom :text Content type (:text or :code)
""" end defp render_kbd_docs(assigns) do ~H"""

Kbd

Keyboard shortcut display component for showing keyboard combinations.

Basic Usage

Press Ctrl + S to save

Press Esc to close

Press Cmd + K to search


              Press <.kbd>Ctrl</.kbd> + <.kbd>S</.kbd> to save
            

Different Sizes

Small

Esc

Medium

Ctrl

Large

Enter

Props

Prop Type Default Description
size atom :md Key size (:sm, :md, :lg)
""" end defp render_link_docs(assigns) do ~H"""

Link

Styled link component with support for external links and LiveView `navigate`/`patch`.

Variants

Default link Primary link Secondary link Underline link

    <.link href="#">Default link</.link>
    <.link href="#" variant={:primary}>Primary</.link>
            

LiveView Navigation

Getting started Stay on page (patch) External link

    <.link navigate={~p"/dashboard"}>Dashboard</.link>
    <.link patch={~p"/docs?section=components"}>Components</.link>
    <.link href="https://ashui.dev" external={true}>AshUI</.link>
            
""" end defp render_hero_docs(assigns) do ~H"""

Hero

Flexible hero banner with title, subtitle, CTA, and optional background image.

Product Launch Hero


    <.hero
      title="Ship Better Products"
      subtitle="Plan, collaborate, and deliver features."
      cta="Get Started"
      cta_href={~p"/signup"}
      background="https://example.com/hero.jpg"
    />
            

Custom Content

Reserve Seat Explore Agenda
""" end defp render_swap_docs(assigns) do ~H"""

Swap

Toggle between two states using animated swap transitions.

Mode Toggle

<:on> <.icon name="hero-moon" class="w-4 h-4" /> Dark Mode <:off> <.icon name="hero-sun" class="w-4 h-4" /> Light Mode <:on> Sound On <:off> Sound Off

    <.swap id="theme-swap" active={@dark_mode}>
      <:on>Dark</:on>
      <:off>Light</:off>
    </.swap>
            
""" end defp render_dock_docs(assigns) do ~H"""

Dock

Dock / Bottom Navigation component for mobile-style bottom navigation bars.

Basic Dock

<:item icon="hero-home" label="Home" active={true} /> <:item icon="hero-user" label="Profile" /> <:item icon="hero-bell" label="Notifications" /> <:item icon="hero-cog-6-tooth" label="Settings" />

              <.dock id="main-dock">
                <:item icon="hero-home" label="Home" active={true} />
                <:item icon="hero-user" label="Profile" />
              </.dock>
            

Props

Prop Type Default Description
icon string - Icon name for dock item
active boolean false Active state
""" end defp render_drawer_docs(assigns) do drawer_open = Map.get(assigns, :drawer_docs_open, false) assigns = assign(assigns, :drawer_open, drawer_open) ~H"""

Drawer

Drawer sidebar component for off-canvas navigation.

Left Drawer

<:trigger> <:content>

              <.drawer id="nav-drawer" open={@drawer_open}>
                <:trigger>Open Menu</:trigger>
                <:content>
                  <nav>Navigation items</nav>
                </.drawer>
            

Props

Prop Type Default Description
side atom :left Drawer side (:left or :right)
overlay boolean true Show overlay backdrop
""" end defp render_mockup_docs(assigns) do ~H"""

Mockup

Mockup component for displaying phone, browser, or window mockups.

Phone Mockup

Phone Content


              <.mockup type={:phone}>
                <img src="/screenshot.png" />
              </.mockup>
            

Browser Mockup

Browser Content

This is a browser mockup

Window Mockup

Window content here

Props

Prop Type Default Description
type atom - Mockup type (:phone, :browser, :window)
url string nil URL for browser mockup
""" end defp render_timeline_docs(assigns) do sample_items = [ %{ title: "Project Started", date: DateTime.add(DateTime.utc_now(), -30, :day), description: "Initial project setup and planning" }, %{ title: "First Release", date: DateTime.add(DateTime.utc_now(), -15, :day), description: "Version 1.0 released to production" }, %{ title: "Major Update", date: DateTime.add(DateTime.utc_now(), -7, :day), description: "Added new features and improvements" }, %{title: "Current", date: DateTime.utc_now(), description: "Working on next version"} ] nested_items = [ %{ title: "Project Started", date: DateTime.add(DateTime.utc_now(), -30, :day), description: "Initial project setup", children: [ %{ title: "Repository Created", date: DateTime.add(DateTime.utc_now(), -30, :day), description: "GitHub repo initialized" }, %{ title: "Team Onboarded", date: DateTime.add(DateTime.utc_now(), -28, :day), description: "Team members added" } ] }, %{ title: "First Release", date: DateTime.add(DateTime.utc_now(), -15, :day), description: "Version 1.0 launched" }, %{ title: "Major Update", date: DateTime.add(DateTime.utc_now(), -7, :day), description: "Added new features", children: [ %{ title: "Feature A", date: DateTime.add(DateTime.utc_now(), -7, :day), description: "Implemented" }, %{ title: "Feature B", date: DateTime.add(DateTime.utc_now(), -5, :day), description: "Implemented" } ] } ] assigns = assigns |> assign(:sample_items, sample_items) |> assign(:nested_items, nested_items) ~H"""

Timeline

Timeline component for displaying chronological events or activities with support for nested/collapsible events.

Basic Timeline


              <.timeline id="events-timeline" items={@events} />
            

Nested Events with Collapsible


              <.timeline
                id="nested-timeline"
                items={@events}
                collapsible={true}
                default_expanded={true}
              />
            

Horizontal Timeline

""" end defp render_lightbox_docs(assigns) do sample_images = [ %{src: "/images/placeholder-1.jpg", alt: "Image 1"}, %{src: "/images/placeholder-2.jpg", alt: "Image 2"}, %{src: "/images/placeholder-3.jpg", alt: "Image 3"} ] lightbox_id = "docs-lightbox" lightbox_show = Map.get(assigns, :"#{lightbox_id}_show", false) lightbox_current_index = Map.get(assigns, :"#{lightbox_id}_current_index", 0) assigns = assigns |> assign(:sample_images, sample_images) |> assign(:lightbox_id, lightbox_id) |> assign(:lightbox_show, lightbox_show) |> assign(:lightbox_current_index, lightbox_current_index) |> assign(:"#{lightbox_id}_images", sample_images) ~H"""

Lightbox

Lightbox / Image Viewer component for full-screen image previews with navigation.

Basic Lightbox

<%= for {image, index} <- Enum.with_index(@sample_images) do %> <% end %>

              <.lightbox id="gallery-lightbox" images={@images} current_index={0} show={@show_lightbox} />
            

Props

Prop Type Default Description
images list - List of image URLs or maps with :src and optional :alt
current_index integer 0 Currently displayed image index
""" end defp render_video_player_docs(assigns) do ~H"""

Video Player

Video Player component with custom controls for embedded videos.

Basic Video Player


              <.video_player src="/videos/example.mp4" poster="/images/poster.jpg" />
            

Props

Prop Type Default Description
src string - Video source URL
autoplay boolean false Autoplay video
""" end defp render_animated_chart_docs(assigns) do sample_data = [10, 25, 30, 45, 50, 35, 40] sample_labels = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"] assigns = assigns |> assign(:sample_data, sample_data) |> assign(:sample_labels, sample_labels) ~H"""

Animated Chart

Animated Chart component with smooth transitions when data changes.

Animated Line Chart


              <.animated_chart type={:line} data={@chart_data} labels={@labels} />
            

Animated Bar Chart

Props

Prop Type Default Description
type atom - Chart type (:line, :bar, :area)
duration integer 1000 Animation duration in milliseconds
""" end defp render_masonry_grid_docs(assigns) do sample_items = [ %{id: 1, title: "Item 1", height: "h-48"}, %{id: 2, title: "Item 2", height: "h-64"}, %{id: 3, title: "Item 3", height: "h-32"}, %{id: 4, title: "Item 4", height: "h-56"}, %{id: 5, title: "Item 5", height: "h-40"}, %{id: 6, title: "Item 6", height: "h-72"} ] assigns = assign(assigns, :sample_items, sample_items) ~H"""

Masonry Grid

Masonry Grid / Pinterest Layout component for dynamic card layouts.

Basic Masonry Grid

<:item :let={item}>
{item.title}

              <.masonry_grid items={@items} columns={3} gap={4}>
                <:item :let={item}>
                  <div>{item.content}</div>
                </:item>
              </.masonry_grid>
            

Props

Prop Type Default Description
columns integer 3 Number of columns
gap integer 4 Gap between items (Tailwind spacing unit)
""" end defp render_coming_soon(assigns) do ~H"""

Coming Soon

This component documentation is being built. Check back soon!

""" end end