Pine UI 🌲

Hex.pmDocsLicense

A Tailwind CSS + Alpine.js component library for Phoenix LiveView. All 42 Pines UI elements as 49 Phoenix function components — modal, slide-over, dropdown menu, command palette, toast, table, date picker, tabs, accordion, select, file upload and more. Accessible, dark-mode ready, and usable by AI coding agents out of the box.

Pines is DevDojo's Alpine.js + Tailwind CSS component library, distributed as HTML you copy and paste. This package turns it into real Phoenix components with declarative attr/slot APIs, compile-time validation, dark mode, and the LiveView plumbing Alpine needs to survive DOM patching.

<.card title="Getting started">
<p>Every Pines element, as a Phoenix component.</p>
<:footer>
<.button phx-click={JS.dispatch("pine:open", to: "#docs")}>Read the docs</.button>
</:footer>
</.card>
<.slide_over id="docs" title="Documentation"></.slide_over>

Installation

def deps do
[{:pine_ui_phoenix, "~> 0.2.0"}]
end

Requires Elixir 1.15+ and phoenix_live_view >= 0.20.4 — which covers 0.20.x, 1.0, 1.1 and 1.2.

Three further steps are required. Skipping them is the difference between working components and components that render unstyled or flash their contents on load:

  1. Point Tailwind at the package, or every class it uses is purged from your production build.

    // Tailwind v3 — tailwind.config.js
    content: [..., "../deps/pine_ui_phoenix/lib/**/*.ex"]
    /* Tailwind v4 — app.css */
    @source "../../deps/pine_ui_phoenix/lib";
  2. Import the stylesheet, which carries the [x-cloak] rule and the marquee keyframes.

    @import "../../deps/pine_ui_phoenix/priv/static/pine_ui.css";
  3. Install Alpine and wire up pineDom(), so Alpine state survives LiveView patches.

    import { pineDom, PineHooks } from "../../deps/pine_ui_phoenix/priv/static/pine_ui.js"
    const liveSocket = new LiveSocket("/live", Socket, {
    params: { _csrf_token: csrfToken },
    hooks: { ...PineHooks },
    dom: pineDom()
    })

Full details, including which Alpine plugins each component needs, are in the installation guide.

Usage

defp html_helpers do
quote do
use Phoenix.Component
use PineUiPhoenix
# ...
end
end

A stock Phoenix app already generates its own <.button>, <.table> and <.modal> in CoreComponents, so resolve the clash however suits you:

use PineUiPhoenix, except: [:button, :table, :modal]
use PineUiPhoenix, only: [:command, :marquee, :rating]
use PineUiPhoenix, prefix: :pine # <.pine_button>

Components

All 42 Pines elements, across both the "Tailwind and Alpine" and "Tailwind Only" groups.

Layoutcard · table · blockquote
Formsbutton · text_input · textarea · checkbox · radio_group · select · select_menu · combobox · tags_input · switch · range_slider · rating · date_picker · copy_to_clipboard
Feedbackalert · badge · banner · progress · progress_circle · progress_steps · toast_group · toast_trigger
Navigationaccordion · tabs · breadcrumbs · pagination · navigation_menu · menubar
Overlaysmodal · full_screen_modal · slide_over · dropdown_menu · context_menu · popover · hover_card · tooltip · command
Mediaimage_gallery · carousel · video · monaco_editor
Animationmarquee · retro_grid · typing_effect · text_animation

Fourteen of them need no JavaScript at all.

What this port changes

It is a port, not a transcription. Where the copy-paste original made sense as HTML but not as a component, it was rebuilt:

Using it with AI coding agents

The package ships usage-rules.md and a Claude Code skill, so agents get the component API and the install gotchas without guessing. If you use usage_rules:

{:usage_rules, "~> 1.0", only: [:dev]}
mix usage_rules.sync AGENTS.md pine_ui_phoenix --link-to-folder deps

That pulls the rules into your AGENTS.md/CLAUDE.md. Without it, agents still read deps/pine_ui_phoenix/usage-rules.md directly, and hexdocs publishes llms.txt for the same purpose.

The component table in those rules is generated from the live attr/slot metadata and verified by a test, so it cannot drift from the real API.

Documentation

Component reference: hexdocs.pm/pine_ui_phoenix.

Upgrading from v0.1.x

Every v0.1.x function name still works and is re-exported as a deprecated delegate, so your templates keep rendering. Three things do change — the LiveView floor, the palette, and the removal of :poison. See the upgrade guide.

Development

mix deps.get
mix test # component + cross-component contract tests
mix lint # format check, warnings-as-errors, credo
mix dev # demo app at http://localhost:4444
mix assets.watch # rebuild the demo's Tailwind CSS

mix dev runs a demo of every component with its source shown alongside each example. It serves the shippedpriv/static assets, so it exercises exactly what consumers get.

Credits

Design and behaviour come from Pines by DevDojo. This is an independent Phoenix port.

License

MIT — see LICENSE.