Packages

Tailwind CSS and Alpine.js UI components for Phoenix LiveView. All 42 Pines elements: modal, dropdown, command palette, toast, table, date picker, tabs. Accessible, dark mode, AI-agent ready.

Current section

Files

Jump to
Raw

README.md

# Pine UI 🌲
[![Hex.pm](https://img.shields.io/hexpm/v/pine_ui_phoenix.svg)](https://hex.pm/packages/pine_ui_phoenix)
[![Docs](https://img.shields.io/badge/hex-docs-blue.svg)](https://hexdocs.pm/pine_ui_phoenix)
[![License](https://img.shields.io/hexpm/l/pine_ui_phoenix.svg)](https://github.com/jamesnjovu/pine_ui_phoenix/blob/main/LICENSE)
The [Pines UI library](https://devdojo.com/pines/docs/introduction) — all 42 elements —
ported to Phoenix LiveView function components.
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.
```heex
<.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
```elixir
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.
```js
// Tailwind v3 — tailwind.config.js
content: [..., "../deps/pine_ui_phoenix/lib/**/*.ex"]
```
```css
/* Tailwind v4 — app.css */
@source "../../deps/pine_ui_phoenix/lib";
```
2. Import the stylesheet, which carries the `[x-cloak]` rule and the marquee keyframes.
```css
@import "../../deps/pine_ui_phoenix/priv/static/pine_ui.css";
```
3. Install Alpine and wire up `pineDom()`, so Alpine state survives LiveView patches.
```js
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](https://github.com/jamesnjovu/pine_ui_phoenix/blob/main/guides/installation.md).
## Usage
```elixir
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:
```elixir
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.
| | |
|---|---|
| **Layout** | `card` · `table` · `blockquote` |
| **Forms** | `button` · `text_input` · `textarea` · `checkbox` · `radio_group` · `select` · `select_menu` · `switch` · `range_slider` · `rating` · `date_picker` · `copy_to_clipboard` |
| **Feedback** | `alert` · `badge` · `banner` · `progress` · `progress_circle` · `progress_steps` · `toast_group` · `toast_trigger` |
| **Navigation** | `accordion` · `tabs` · `breadcrumbs` · `pagination` · `navigation_menu` · `menubar` |
| **Overlays** | `modal` · `full_screen_modal` · `slide_over` · `dropdown_menu` · `context_menu` · `popover` · `hover_card` · `tooltip` · `command` |
| **Media** | `image_gallery` · `carousel` · `video` · `monaco_editor` |
| **Animation** | `marquee` · `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:
- **The calendar grid is computed in Elixir.** Pines builds it in Alpine; here `Date` handles
leap years and month lengths, and the days are real `<button>`s in the DOM.
- **`text_animation` needs no GSAP.** The original loads GSAP from a CDN; this splits the
text server-side and animates with CSS transitions.
- **Alpine state is JSON-encoded, never interpolated.** Building `x-data` by string
interpolation breaks on apostrophes and allows expression injection.
- **Classes merge rather than concatenate,** so `<.button class="bg-red-500">` actually
overrides the default background. See [theming](https://github.com/jamesnjovu/pine_ui_phoenix/blob/main/guides/theming.md).
- **Accessibility is wired properly** — real ARIA bindings, unique ids, keyboard navigation,
and `prefers-reduced-motion`.
## Documentation
- [Installation](https://github.com/jamesnjovu/pine_ui_phoenix/blob/main/guides/installation.md) — Tailwind, Alpine, plugins, hooks
- [LiveView integration](https://github.com/jamesnjovu/pine_ui_phoenix/blob/main/guides/liveview-integration.md) — DOM patching, server-driven state, forms, streams
- [Theming](https://github.com/jamesnjovu/pine_ui_phoenix/blob/main/guides/theming.md) — class merging, dark mode, accent colours
- [Upgrading to v0.2](https://github.com/jamesnjovu/pine_ui_phoenix/blob/main/guides/upgrading.md) — from v0.1.x
Component reference: [hexdocs.pm/pine_ui_phoenix](https://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](https://github.com/jamesnjovu/pine_ui_phoenix/blob/main/guides/upgrading.md).
## Development
```bash
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 *shipped* `priv/static` assets, so it exercises exactly what consumers get.
## Credits
Design and behaviour come from [Pines](https://devdojo.com/pines) by
[DevDojo](https://devdojo.com). This is an independent Phoenix port.
## License
MIT — see [LICENSE](LICENSE).