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
Current section
Files
pine_ui_phoenix
mix.exs
mix.exs
defmodule PineUiPhoenix.MixProject do
use Mix.Project
@version "0.2.0"
@source_url "https://github.com/jamesnjovu/pine_ui_phoenix"
# Allows CI to test against a specific LiveView release without editing this file:
#
# LIVE_VIEW_VERSION="~> 1.1.0" mix deps.update phoenix_live_view
#
# `||` is not enough here: an unset matrix value arrives as "" rather than nil, and "" is
# truthy in Elixir — which Mix then rejects as a malformed version requirement.
@live_view_requirement (case System.get_env("LIVE_VIEW_VERSION") do
blank when blank in [nil, ""] -> ">= 0.20.4"
requirement -> requirement
end)
def project do
[
app: :pine_ui_phoenix,
version: @version,
elixir: "~> 1.15",
start_permanent: Mix.env() == :prod,
elixirc_paths: elixirc_paths(Mix.env()),
package: package(),
name: "Pine UI",
source_url: @source_url,
homepage_url: @source_url,
description:
"Phoenix LiveView components for Pine UI - an AlpineJS and TailwindCSS component library",
deps: deps(),
docs: docs(),
aliases: aliases(),
test_coverage: [summary: [threshold: 0]],
dialyzer: [
plt_add_apps: [:mix, :ex_unit],
# Per-env PLT: the dev and test dependency sets differ, and a single shared file
# goes stale when the environment changes.
plt_file: {:no_warn, "priv/plts/dialyzer-#{Mix.env()}.plt"}
]
]
end
def application do
# A component library needs no supervision tree. Deliberately no `mod:` key.
[extra_applications: [:logger]]
end
# `dev/` holds the demo application; `test/support` holds the test case templates.
# Neither is compiled for consumers, who always build in :prod.
defp elixirc_paths(:test), do: ["lib", "test/support"]
defp elixirc_paths(:dev), do: ["lib", "dev"]
defp elixirc_paths(_), do: ["lib"]
defp deps do
[
# Required: the library cannot compile without Phoenix.Component.
{:phoenix_live_view, @live_view_requirement},
{:phoenix_html, "~> 3.3 or ~> 4.0"},
{:jason, "~> 1.2"},
# Demo application (`mix dev`) — never shipped in the Hex package.
{:bandit, "~> 1.5", only: :dev},
{:phoenix_live_reload, "~> 1.5", only: :dev},
{:tailwind, "~> 0.2", only: :dev, runtime: false},
# Tooling
{:floki, ">= 0.30.0", only: :test},
{:credo, "~> 1.7", only: [:dev, :test], runtime: false},
# :test as well as :dev — the CI lint job runs with MIX_ENV=test, so a dev-only
# dialyxir makes `mix dialyzer` an unknown task there.
{:dialyxir, "~> 1.4", only: [:dev, :test], runtime: false},
{:ex_doc, "~> 0.30", only: :dev, runtime: false}
]
end
defp aliases do
[
dev: "run --no-halt dev.exs",
"assets.build": ["tailwind default --minify"],
"assets.watch": ["tailwind default --watch"],
lint: [
"format --check-formatted",
"compile --warnings-as-errors",
"credo --strict"
]
]
end
defp package do
[
name: "pine_ui_phoenix",
# Allowlist. `dev/`, `test/`, `assets/` and `dev.exs` are excluded by omission.
# priv/static, not priv: the Dialyzer PLT cache lives in priv/plts and must not
# be shipped.
files: ~w(lib priv/static guides .formatter.exs mix.exs README.md CHANGELOG.md LICENSE),
licenses: ["MIT"],
links: %{
"GitHub" => @source_url,
"Changelog" => "#{@source_url}/blob/main/CHANGELOG.md",
"Pines (upstream)" => "https://devdojo.com/pines/docs/introduction"
},
maintainers: ["James Njovu"]
]
end
defp docs do
[
main: "readme",
# `mix docs` runs in :dev, where dev/ compiles — without this the demo application's
# modules are published to hexdocs and listed in llms.txt, which is noise for readers
# and for coding agents.
filter_modules: ~r"^Elixir\.PineUiPhoenix",
source_ref: "v#{@version}",
source_url: @source_url,
extras: [
"README.md": [title: "Overview"],
"CHANGELOG.md": [title: "Changelog"],
LICENSE: [title: "License"]
],
groups_for_modules: [
Core: [PineUiPhoenix, PineUiPhoenix.Component],
Components: ~r"PineUiPhoenix.Components\\.",
Internals: ~r"PineUiPhoenix.Internal\\.",
"Deprecated (v0.1.x)": [PineUiPhoenix.Legacy]
],
# Driven by `@doc group: "..."` metadata on each component.
groups_for_docs: [
Disclosure: &(&1[:group] == "Disclosure"),
Overlays: &(&1[:group] == "Overlays"),
Navigation: &(&1[:group] == "Navigation"),
Forms: &(&1[:group] == "Forms"),
Feedback: &(&1[:group] == "Feedback"),
Layout: &(&1[:group] == "Layout"),
Media: &(&1[:group] == "Media"),
Animation: &(&1[:group] == "Animation"),
"Deprecated (v0.1.x)": &(&1[:group] == "Deprecated (v0.1.x)")
]
]
end
end