headless

Headless UI components for Phoenix


License
MIT

Documentation

Headless UI Components for Phoenix

Unstyled, accessible UI components for Phoenix and Phoenix LiveView. To be styled with the CSS framework of your choice.

Warning

This project is in a very early stage - see Components list below.

Preview

Demo

See demo website

Goals & Rules

  • Provide unstyled Phoenix components as building blocks for your own UI components
  • If something can be achieved with HTML and CSS only, it should be done with HTML and CSS only (no-JS)
  • Where JS is required, use Alpine.js
  • Use Alpine.data() instead of inline markup
  • Components must work with standard Phoenix controllers (dead views)
  • Components must work with Phoenix LiveView
  • Components must work with standard Phoenix forms
  • Components must be accessible (aria attributes, keyboard navigation, focus, etc.)

Components

Component Functions Status
Avatar use_avatar/1 βœ… Done
Checkbox input/1 βœ… Done
Clipboard use_clipboard/1 βœ… Done
Combobox use_combobox/1 πŸ—οΈ In progress
Command πŸ—οΈ In progress
Dialog πŸ—ΊοΈ Planned
File Preview πŸ—ΊοΈ Planned
Input OTP πŸ—ΊοΈ Planned
Popover use_popover/1 βœ… Done
Radio button πŸ—ΊοΈ Planned
Tabs πŸ—ΊοΈ Planned
Text input input/1 βœ… Done
Textarea πŸ—ΊοΈ Planned
Toggle use_toggle/1 βœ… Done

Installation

The package can be installed by adding headless to your list of dependencies in mix.exs:

def deps do
  [
    {:headless, "~> 0.1"}
  ]
end

Include JavaScript package in your app.js:

// assets/js/app.js

// import and start headless
import headless from "headless"
headless.start()

// ...

// configure LiveSocket
let liveSocket = new LiveSocket('/live', Socket, {
  // ...

  // configure dom hook
  dom: headless.dom
})

Usage

Headless components are meant to be used as building blocks for your own components. Most components are built using use_* functions that expose the necessary HTML attributes to provide the functionality leaving all tag rendering to the user. This way every element can be 100% customized.

See example app components.

defmodule MyAppWeb.Components do
  use Phoenix.Component
  import Headless

  attr :src, :any
  attr :alt, :any
  attr :initials, :string

  def avatar(assigns) do
    ~H"""
    <.use_avatar :let={a} src={@src}>
      <div {a.root}>
        <img {a.image} alt={@alt} />
        <div {a.fallback}><%= @initials %></div>
      </div>
    </.use_avatar>
    """
  end
end

Adding your own Alpine components

If you want to add your own Alpine components you can import the bundled Alpine like this:

// assets/js/app.js

import headless, { Alpine } from "headless"

Alpine.data("my_custom_component", () => ...)

headless.start()

Development

# Start development server with examples
mix phx.server

# Update bundled Alpine
curl -L https://unpkg.com/@alpinejs/csp/dist/module.cjs.js > ./apps/headless/assets/vendor/alpine.js

Inspirations