docs/components/layout.md

# Layout

26 layout components — page shell, structural grid/flex primitives, accordion, resizable panels, and scroll areas.

**Module**: `PhiaUi.Components.Layout`

```elixir
import PhiaUi.Components.Layout
```

---

## Table of Contents

**Page Structure**
- [shell](#shell) / [app_shell](#app_shell)
- [page_layout](#page_layout) / [split_layout](#split_layout)
- [page_header](#page_header)
- [fixed_bar](#fixed_bar) / [sticky](#sticky)

**Grid & Flex**
- [container](#container)
- [box](#box)
- [flex](#flex) / [stack](#stack) / [wrap](#wrap)
- [grid](#grid) / [simple_grid](#simple_grid)
- [masonry_grid](#masonry_grid)
- [center](#center)
- [spacer](#spacer)
- [section](#section)
- [media_object](#media_object)
- [description_list](#description_list)
- [nav_list](#nav_list)

**Panels**
- [accordion](#accordion)
- [collapsible](#collapsible)
- [resizable](#resizable)

**Utility**
- [scroll_area](#scroll_area)
- [aspect_ratio](#aspect_ratio)
- [separator / divider](#separator--divider)

---

## shell

Full-page app shell. Coordinates sidebar + topbar + main content area.

```heex
<.shell>
  <:sidebar>
    <.sidebar>
      <:brand><img src="/logo.svg" alt="App" class="h-7" /></:brand>
      <:nav_items>
        <.sidebar_item icon="home" href="/" active={@active == :home}>Dashboard</.sidebar_item>
        <.sidebar_item icon="users" href="/users">Users</.sidebar_item>
        <.sidebar_item icon="settings" href="/settings">Settings</.sidebar_item>
      </:nav_items>
    </.sidebar>
  </:sidebar>

  <:topbar>
    <.topbar>
      <:right>
        <.dark_mode_toggle />
        <.avatar size="sm"><.avatar_fallback name={@current_user.name} /></.avatar>
      </:right>
    </.topbar>
  </:topbar>

  <:main>
    <div class="p-6">
      <%= @inner_content %>
    </div>
  </:main>
</.shell>
```

---

## page_layout

Standard page with header, optional sidebar, and main content.

```heex
<.page_layout>
  <:header>
    <.page_header title="Users" description="Manage your team members." />
  </:header>
  <:sidebar>
    <.context_nav>…</.context_nav>
  </:sidebar>
  <:content>
    <.table rows={@users}>…</.table>
  </:content>
</.page_layout>
```

---

## split_layout

Two-column layout — list + detail panel.

```heex
<.split_layout ratio={:two_thirds}>
  <:left>
    <.data_grid id="users" rows={@users}>…</.data_grid>
  </:left>
  <:right>
    <%= if @selected_user do %>
      <.user_detail user={@selected_user} />
    <% else %>
      <.empty_state icon="user" title="Select a user" />
    <% end %>
  </:right>
</.split_layout>
```

**Attrs**: `ratio` (`:half` | `:one_third` | `:two_thirds`, default `:half`)

---

## page_header

Page title + description + optional action slot.

```heex
<.page_header title="Projects" description="All active projects and their status.">
  <:actions>
    <.button phx-click="new_project">
      <:icon><.icon name="plus" /></:icon>
      New project
    </.button>
  </:actions>
</.page_header>
```

---

## fixed_bar

Stuck to the top or bottom of the viewport.

```heex
<.fixed_bar position={:bottom} class="border-t bg-card px-6 py-3 flex justify-end gap-2">
  <.button variant="outline" phx-click="cancel">Cancel</.button>
  <.button type="submit">Save changes</.button>
</.fixed_bar>
```

---

## sticky

Position-sticky wrapper with configurable top offset.

```heex
<.sticky top={64}>
  <div class="bg-card border-b px-6 py-2 flex items-center gap-3">
    <.inline_search phx-change="search" />
    <.button variant="outline" size="sm">Filter</.button>
  </div>
</.sticky>
```

---

## container

Responsive centred container with max-width.

```heex
<.container>
  <.page_header title="Dashboard" />
  <.table rows={@data}>…</.table>
</.container>

<.container max_width="max-w-3xl">
  <.prose><%= @article.body %></.prose>
</.container>
```

---

## flex / stack / wrap / box

Spacing and alignment primitives.

```heex
<%!-- Vertical stack --%>
<.stack gap={4}>
  <.card>Item 1</.card>
  <.card>Item 2</.card>
</.stack>

<%!-- Horizontal flex --%>
<.flex align={:center} justify={:between} class="border-b px-4 py-2">
  <span class="font-semibold">Title</span>
  <.button size="sm">Action</.button>
</.flex>

<%!-- Wrapping flex (tag cloud) --%>
<.wrap gap={2}>
  <%= for tag <- @tags do %>
    <.badge><%= tag %></.badge>
  <% end %>
</.wrap>

<%!-- Padded box --%>
<.box padding={6} class="border rounded-lg">
  <p>Content</p>
</.box>
```

---

## grid / simple_grid

CSS Grid containers.

```heex
<.grid cols={3} gap={6}>
  <%= for item <- @items do %>
    <.card><%= item.title %></.card>
  <% end %>
</.grid>

<.simple_grid cols={2} gap={4}>
  <.stat_card title="MRR" value="$24,500" />
  <.stat_card title="ARR" value="$294,000" />
</.simple_grid>
```

---

## masonry_grid

CSS column-based masonry layout.

```heex
<.masonry_grid cols={3} gap={4}>
  <%= for post <- @posts do %>
    <.article_card {post} />
  <% end %>
</.masonry_grid>
```

---

## center

Centres content both horizontally and vertically.

```heex
<.center class="min-h-screen">
  <.card class="w-96 p-8">
    <h1 class="text-2xl font-bold mb-6">Sign in</h1>
    <.login_form />
  </.card>
</.center>
```

---

## section

Padded page section with optional title.

```heex
<.section title="Recent activity">
  <.activity_feed>…</.activity_feed>
</.section>
```

---

## media_object

Image/icon left, text content right.

```heex
<.media_object>
  <:media>
    <.avatar size="md">
      <.avatar_image src={@user.avatar_url} />
      <.avatar_fallback name={@user.name} />
    </.avatar>
  </:media>
  <:content>
    <p class="font-medium"><%= @user.name %></p>
    <p class="text-sm text-muted-foreground"><%= @comment.body %></p>
  </:content>
</.media_object>
```

---

## description_list

Label + value pairs.

```heex
<.description_list>
  <:item label="Status"><.badge>Active</.badge></:item>
  <:item label="Plan">Pro</:item>
  <:item label="Member since"><.relative_time datetime={@user.inserted_at} /></:item>
  <:item label="API Key"><.copy_input value={@user.api_key} /></:item>
</.description_list>
```

---

## accordion

Expand/collapse panels.

```heex
<.accordion type="single" collapsible={true}>
  <.accordion_item value="faq-1">
    <.accordion_trigger>What is PhiaUI?</.accordion_trigger>
    <.accordion_content>
      PhiaUI is a copy-paste Phoenix LiveView component library with 829 components.
    </.accordion_content>
  </.accordion_item>
  <.accordion_item value="faq-2">
    <.accordion_trigger>How do I install it?</.accordion_trigger>
    <.accordion_content>
      Run <code>mix phia.install</code> after adding the dependency.
    </.accordion_content>
  </.accordion_item>
</.accordion>
```

---

## collapsible

Single collapse toggle.

```heex
<.collapsible id="advanced-opts">
  <:trigger :let={open}>
    <.button variant="ghost" size="sm" class="gap-1">
      Advanced options
      <.icon name={if open, do: "chevron-up", else: "chevron-down"} size="sm" />
    </.button>
  </:trigger>
  <.phia_input field={@form[:webhook_url]} label="Webhook URL" class="mt-3" />
</.collapsible>
```

---

## resizable

Draggable split panels. Hook: `PhiaResizable`.

```heex
<.resizable id="editor-split" direction={:horizontal} default_size={50}>
  <:panel>
    <.code_textarea id="editor" name="code" value={@code} />
  </:panel>
  <:panel>
    <div class="prose p-4"><%= raw(@preview) %></div>
  </:panel>
</.resizable>
```

**Attrs**: `id` (required), `direction` (`:horizontal` | `:vertical`), `default_size` (%, default 50)

---

## scroll_area

Custom-styled scrollbar container.

```heex
<.scroll_area class="h-72 rounded-md border">
  <%= for item <- @long_list do %>
    <div class="px-4 py-2 hover:bg-muted/50"><%= item.name %></div>
  <% end %>
</.scroll_area>
```

---

## aspect_ratio

Maintains a fixed aspect ratio for images and embeds.

```heex
<.aspect_ratio ratio={16 / 9} class="overflow-hidden rounded-lg">
  <img src={@image_url} class="w-full h-full object-cover" />
</.aspect_ratio>

<.aspect_ratio ratio={1} class="w-32">
  <iframe src="https://www.youtube.com/embed/dQw4w9WgXcQ" class="w-full h-full" />
</.aspect_ratio>
```

---

## separator / divider

Horizontal or vertical separator line.

```heex
<.separator />
<.separator orientation={:vertical} class="h-6 mx-2" />

<%!-- With label --%>
<.divider label="or" />
<.divider label="Continue with" />
```