With HEEX released we decided to start a components library using Tailwind CSS - check it out here: Petal Components.
We also have a boilerplate project if you want to get up and running quickly - it’s just a fresh install of Phoenix with Tailwind/Alpine and petal_components included.
Petal Components allows you to get up and running with a suite of styled components eg:
<Container.container>
<Typography.h1>Petal Components</Typography.h1>
<Button.a href="/" label="a" />
<Button.button label="Button" />
<Button.patch href="/" label="Live Patch" />
<Button.redirect href="/" label="Live Redirect" />
<!-- Includes every single heroicon -->
<Heroicons.Solid.home class="w-5 h-5" />
<!-- Includes all the basic form elements. eg: -->
<Form.text_input form={:user} field={:name} placeholder="eg. John" />
<Form.textarea form={:user} field={:description} />
<!--Plus much more -->
</Container.container>
We plan to add more components over time and eventually add a “pro” (paid) version that adds things like styled authentication pages / Stripe integration / email templates etc… Does this sound like something you guys would be interested in?
We welcome your thoughts or any ideas for components. Cheers
I for one would be terribly interested in this sort of thing, as prophesied by Chris McChord.
Would be interesting to hear your thoughts on the terms of the license of Tailwind UI (haven’t reviewed these yet, but I have secured the full license in any event).
Is there any way to achieve your goals without Alpine?
We’re pretty much building our components from scratch (taking influence from a number of sources). As far as we’re aware none of our components contravene any licensing terms.
And yes we’re going to try remove the Alpine dependency. eg. with the dropdown component I tried to use the new Phoenix.LiveView.JS lib but the click.away event was giving me headaches. Seems like Chris has fixed that now so I’ll try again.
This is super great! I have a bunch of similar components in my own apps. My one big suggestion would be to consolidate the modules. You could likely have a single module with the current surface area. One of the goals of function components was to push folks away from the one-function-per-module for component pattern. By grouping the functions, they are more discoverable, doc’d in one place, etc. For example, folks could import PetalComponent (or single alias) and have most core UI components available as <.link, <.button. I’m not saying everything must be define under the same namespace, but the current surface area would be really well suited and would be nice in user land. Awesome work!
Thanks Joe - haha good find - yeah eventually I’ll put a demo there and also have a pro version. It’s still a WIP, but people can sign up now and I’ll send out an email to users once it’s complete.
Very interested in using something like this. Coming from a React background it makes the dev feel more familiar. Wondering if you plan to remove Node/NPM totally (not sure how is that is with tailwind), having that removed and Alpine removed would make for a great setup, less is better if the same functionality can be achieved.
Side note: I did not realise that you setup github as a template repo till I saw you github link, something I have an immediate use for, so glad I took a look.
Unfortunately Tailwind needs Node for the tailwindcss CLI to work. You could try just loading the entire Tailwind CSS lib in your <head> but it’d be massive. Using their CLI means your css file only has css definitions for the classes you actually use.
Yep I haven’t really used Github templates but for anyone else you can click the “Use template” button on the boilerplate project to get up and running quickly. There’s also a script in there to rename your project to whatever.
I forced the version to 0.3.2 and it passed. But the assets failed:
19:31:23.673 [info] Migrations already up
npm WARN read-shrinkwrap This version of npm is compatible with lockfileVersion@1, but package-lock.json was generated for lockfileVersion@2. I'll try to do my best with it!
npm ERR! code EACCES
npm ERR! syscall open
npm ERR! path /home/xxx/.npm/_cacache/index-v5/0c/ba/3efdcf9368b37769f14ae73c1feb025960f50c09d844da4be8d3e295b485
npm ERR! errno -13
npm ERR!
npm ERR! Your cache folder contains root-owned files, due to a bug in
npm ERR! previous versions of npm which has since been addressed.
npm ERR!
npm ERR! To permanently fix this problem, please run:
npm ERR! sudo chown -R 1001:1001 "/home/xxx/.npm"
npm ERR! A complete log of this run can be found in:
npm ERR! /home/xxx/.npm/_logs/2021-11-15T11_31_38_208Z-debug.log
** (exit) 243
(mix 1.12.3) lib/mix/tasks/cmd.ex:64: Mix.Tasks.Cmd.run/1
(mix 1.12.3) lib/mix/task.ex:394: anonymous fn/3 in Mix.Task.run_task/3
(mix 1.12.3) lib/mix/task.ex:452: Mix.Task.run_alias/5
(mix 1.12.3) lib/mix/cli.ex:84: Mix.CLI.run_task/2
(elixir 1.12.3) lib/code.ex:1261: Code.require_file/2
I updated mix.exs to point to the latest version of petal_components to fix that first issue (will keep that up to date in future).
But I’m not sure about the npm issue - it works fine for me. Maybe try updating node/npm or running that command there in the error message sudo chown -R 1001:1001 "/home/xxx/.npm".
The dropdown now can utilise Phoenix.Liveview.JS if you don’t want to use Alpine. However, this only works in live views or live components (dead views will need Alpine).
Function components are imported so you don’t need the module anymore - eg <.button> instead of <Button.button>
# Live view
@impl true
def mount(_params, _session, socket) do
{:ok, assign(socket, :show_modal, false)}
end
@impl true
def handle_params(_params, _uri, socket) do
case socket.assigns.live_action do
:index ->
{:noreply, assign(socket, show_modal: false)}
:show ->
{:noreply, assign(socket, show_modal: true)}
end
end
# This event is emitted by the component and must be catched
@impl true
def handle_event("close_modal", _, socket) do
{:noreply, push_patch(socket, to: "/index")}
end
@impl true
def render(assigns) do
~H"""
<.button label="Show modal" link_type="live_patch" to="/show" />
<%= if @modal do %>
<.modal max_width="sm | md | lg | xl | 2xl | full" title="Modal">
Modal contents goes here
<div class="gap-5 text-sm">
<div class="flex justify-end">
<.button label="close" phx-click={PetalComponents.Modal.hide_modal()} />
</div>
</div>
</.modal>
<% end %>
"""
end
There’s live demo’s of all the components and code snippets you can directly copy and paste into your projects - soon you’ll be able to download the boilerplate (hopefully over the coming week or two). We’d really welcome your feedback on both the new site and boilerplate (when the latter becomes available).