Packages
petal_components
0.5.1
4.6.2
4.6.1
4.6.0
4.5.0
4.4.0
4.3.0
4.2.1
4.2.0
4.1.2
4.1.1
4.1.0
4.0.12
4.0.11
4.0.10
4.0.6
4.0.5
4.0.4
4.0.3
4.0.1
3.2.2
3.2.1
3.2.0
3.1.0
3.0.2
3.0.1
3.0.0
2.9.3
2.9.2
retired
2.9.1
retired
2.9.0
retired
2.8.4
2.8.3
2.8.2
2.8.1
2.8.0
2.7.4
2.7.3
2.7.2
2.7.1
2.7.0
2.6.1
2.6.0
2.5.2
2.5.1
2.5.0
2.4.3
2.4.2
2.4.1
2.4.0
2.3.0
2.2.1
2.2.0
2.1.2
2.1.1
2.1.0
2.0.6
2.0.5
2.0.4
2.0.3
2.0.2
2.0.1
2.0.0
1.9.3
1.9.2
1.9.1
1.9.0
1.8.0
1.7.1
1.7.0
1.6.2
1.6.1
1.6.0
1.5.5
1.5.4
1.5.3
1.5.2
1.5.1
1.5.0
1.4.9
1.4.8
1.4.7
1.4.6
1.4.5
1.4.4
1.4.3
1.4.2
1.4.1
1.4.0
1.3.0
1.2.14
1.2.13
1.2.12
1.2.11
1.2.10
1.2.9
1.2.8
1.2.7
1.2.6
1.2.5
1.2.4
1.2.3
1.2.2
1.2.1
1.2.0
1.1.6
1.1.5
1.1.4
1.1.3
1.1.2
1.1.1
1.1.0
1.0.8
1.0.7
1.0.6
1.0.5
1.0.4
1.0.3
1.0.2
1.0.1
1.0.0
0.19.10
0.19.9
0.19.8
0.19.7
0.19.6
0.19.5
0.19.4
0.19.3
0.19.2
0.19.1
0.19.0
0.18.5
0.18.4
0.18.3
0.18.2
0.18.1
0.18.0
0.17.7
0.17.6
0.17.5
0.17.4
0.17.3
0.17.2
0.17.1
0.17.0
0.16.0
0.15.0
0.14.1
0.14.0
0.13.7
0.13.6
0.13.5
0.13.4
0.13.3
0.13.2
0.13.1
0.13.0
0.12.0
0.11.4
0.11.3
0.11.2
0.11.1
0.11.0
0.10.8
0.10.7
0.10.6
0.10.5
0.10.4
0.10.3
0.10.2
0.10.1
0.10.0
0.9.3
0.9.2
0.9.1
0.9.0
0.8.0
0.7.0
0.6.1
0.6.0
0.5.1
0.5.0
0.4.0
0.3.2
0.3.1
0.3.0
0.2.2
0.2.1
0.2.0
0.1.0
Shadcn-style Phoenix LiveView components that AI assistants can actually use. Pair with the MCP server so AI coding tools can inspect the real component API.
Current section
Files
Jump to
Current section
Files
petal_components
README.md
README.md
<p align="center">
<img src="https://res.cloudinary.com/wickedsites/image/upload/v1635752721/petal/logo_rh2ras.png" height="128">
<h1 align="center">Petal Components</h1>
</p>
## About 🌺
Petal stands for:
* [Phoenix](https://www.phoenixframework.org/)
* [Elixir](https://elixir-lang.org/)
* [Tailwind CSS](https://tailwindcss.com/)
* [Alpine JS](https://alpinejs.dev/) (optional)
* [Live View (HEEX)](https://hexdocs.pm/phoenix_live_view/Phoenix.LiveView.html)
Petal is a set of HEEX components that makes it easy for Phoenix developers to start building beautiful web apps.
Some components like [Dropdowns](#dropdowns) require Javascript to work. We default to Alpine JS (17kb) but you can choose to use `Phoenix.LiveView.JS` as an alternative (though this will only work in live environments like live views or live components).
<p align="center">
<img src="https://res.cloudinary.com/wickedsites/image/upload/v1635752726/petal/screenshot_tti5my.png" height="1200">
</p>
## Docs 📄
- [Install](#install)
- [Existing projects](#existing-projects)
- [New projects](#new-projects)
- [Roadmap](#roadmap)
- [Layout](#layout)
- [Form components](#form-components)
- [Buttons](#buttons)
- [Misc](#misc)
- [Examples](#examples)
- [Containers](#containers)
- [Links](#links)
- [Link types](#link-types)
- [Buttons](#buttons)
- [Button types](#button-types)
- [Button colors](#button-colors)
- [Button colors (outline)](#button-colors-(outline))
- [Button sizes](#button-sizes)
- [Button states](#button-states)
- [Disabled](#disabled)
- [Loading](#loading)
- [Button with icon](#button-with-icon)
- [Typography](#typography)
- [Heroicons](#heroicons)
- [Heroicons solid](#heroicons-solid)
- [Heroicons outline](#heroicons-outline)
- [Badges](#badges)
- [Alerts](#alerts)
- [Info alert](#info-alert)
- [Success alert](#success-alert)
- [Warning alert](#warning-alert)
- [Danger alert](#danger-alert)
- [Forms](#forms)
- [Text input](#text-input)
- [Number input](#number-input)
- [Email input](#email-input)
- [Password input](#password-input)
- [Search input](#search-input)
- [Telephone input](#telephone-input)
- [URL input](#url-input)
- [Time input](#time-input)
- [Time select](#time-input)
- [Datetime local input](#datetime-input)
- [Datetime select](#datetime-select)
- [Color input](#color-input)
- [File input](#file-input)
- [Range input](#range-input)
- [Text area](#text-area)
- [Select](#select)
- [Radios](#radios)
- [Dropdowns](#dropdowns)
- [Loading indicators](#loading-indicators)
- [Breadcrumbs](#breadcrumbs)
- [Slash](#slash)
- [Chevron](#chevron)
- [Avatar](#avatar)
- [Basic Avatars](#basic-avatars)
- [Avatars with placeholder icon](#avatars-with-placeholder-icon)
- [Avatar groups stacked](#avatar-groups-stacked)
- [Avatars with placeholder initials](#avatars-with-placeholder-initials)
- [Random color generated avatars with placeholder initials](#random-color-generated-avatars-with-placeholder-initials)
- [Progress](#progress)
- [Progress bar](#progress-bar)
- [Sizes](#sizes)
## Install
For Petal to work you simply need Tailwind CSS and Alpine JS installed along with with some Tailwind configuration.
### Existing projects
1 - Follow [this guide](https://sergiotapia.com/phoenix-160-liveview-esbuild-tailwind-jit-alpinejs-a-brief-tutorial) to install Tailwind and Alpine.
2 - Add Petal to your deps:
`mix.exs`
```elixir
defp deps do
[
{:petal_components, "~> 0.5.1"},
]
end
```
3 - Modify your `tailwind.config.js` file to include these settings:
```js
const colors = require("tailwindcss/colors");
module.exports = {
mode: "jit",
purge: [
"../lib/*_web/**/*.*ex",
"./js/**/*.js",
// We need to include the Petal dependency so the classes get picked up by JIT.
"../deps/petal_components/**/*.*ex"
],
darkMode: false,
theme: {
extend: {
// Set these to your brand colors
colors: {
primary: colors.blue,
secondary: colors.pink,
},
},
},
plugins: [require("@tailwindcss/forms")],
};
```
4 - Alias the components in your `<your_project>_web.ex` file
```elixir
defmodule YourProjectWeb
...
defp view_helpers do
quote do
...
use PetalComponents
end
end
```
This will import the functions so you can go `<.button />` in your templates / live views.
If the function names clash with yours (eg. you already have a `.button` function) you can opt to alias the modules instead:
```elixir
defmodule YourProjectWeb
...
defp view_helpers do
quote do
...
alias PetalComponents.{
Heroicons,
Alert,
Badge,
Button,
Container,
Dropdown,
Form,
Loading,
Typography,
Breadcrumb,
Avatar,
Progress
}
end
end
```
### New projects
We recommend using [Petal boilerplate](https://github.com/petalframework/petal_boilerplate), which is a fresh Phoenix install with Tailwind + Alpine installed. It comes with a project renaming script so you can still rename your project to whatever you like.
## Roadmap
### Layout
- [x] container
### Form components
- [x] text input
- [x] select dropdown
- [x] textarea
- [x] checkbox
- [x] radios
- [x] errors
- [x] labels
- [x] file upload
- [x] text variants (email, password, tel)
- [x] color input
- [x] range input
- [x] time & datetime input
- [ ] multiple select
- [ ] switch
### Buttons
- [x] basic button
- [x] change size
- [x] change color
- [x] loading state (with spinner)
- [x] filled vs outline
- [ ] button group
### Misc
- [x] menu dropdown
- [ ] tooltips
- [x] avatar
- [x] alerts
- [ ] tables
- [ ] cards
- [x] breadcrumbs
- [ ] modal
- [ ] slide over
- [x] spinners
- [ ] accordian
- [x] pagination
- [x] badges
- [x] progress
- [x] links
## Examples
### Containers
```elixir
<Container.container max_width="full | lg | md | sm">
```
### Links
#### Link types
```elixir
<.link link_type="a | live_patch | live_redirect" to="/" class="" label="" />
```
### Buttons
#### Button types
```elixir
<.button label="Button" />
<.button link_type="a | live_patch | live_redirect" to="#" label="a" />
```
#### Button colors
```elixir
<.button color="primary | secondary | white | success | danger" label="Primary" />
```
#### Button colors (outline)
```elixir
<.button color="primary | secondary | white | success | danger" label="Primary" variant="outline" />
```
#### Button sizes
```elixir
<.button size="sm | md | lg | xl">
```
#### Button states
##### Disabled
```elixir
<.button disabled link_type="a | live_patch | live_redirect" to="/" label="Disabled" />
```
##### Loading
```elixir
<.button loading link_type="a | live_patch | live_redirect" to="/" label="Loading" />
```
#### Button with icon
```elixir
<.button icon>
<Heroicons.Solid.home class="w-5 h-5" />
With label
</.button>
<.button icon link_type="a | live_patch | live_redirect" icon to="/">
<Heroicons.Solid.home class="w-5 h-5" />
With label
</.button>
```
### Typography
```elixir
<.h1>Heading 1</.h1>
<.h2>Heading 2</.h2>
<.h3>Heading 3</.h3>
<.h4>Heading 4</.h4>
<.h5>Heading 5</.h5>
```
### Heroicons
#### Heroicons solid
```elixir
<Heroicons.Solid.home class="w-6 h-6 text-blue-500" />
<Heroicons.Solid.render icon={:home} />
```
#### Heroicons outline
```elixir
<Heroicons.Outline.home class="w-6 h-6 text-blue-500" />
<Heroicons.Outline.render icon={:home} />
```
### Badges
```elixir
<.badge color="primary | secondary | Info | Success | Warning | Danger | Gray" label="Primary" />
```
### Alerts
#### Info alert
```elixir
<.alert state="info">
This is an info state
</.alert>
```
#### Success alert
```elixir
<.alert state="success" label="This is a success state" />
```
#### Warning alert
```elixir
<.alert state="warning" label="This is a warning state" />
```
#### Danger alert
```elixir
<.alert state="danger" label="This is a danger state" />
```
#### Optional heading alert
```elixir
<.alert heading="Optional heading">
This is quite a long paragraph that takes up more than one line.
</.alert>
```
### Forms
Inputs can take the same arguments as the equivalents in [Phoenix.HTML.Form](https://hexdocs.pm/phoenix_html/Phoenix.HTML.Form.html#content). Eg. `<.time_input precision={:second} />`.
#### Text input
```elixir
<.text_input form={:user} field={:name} placeholder="eg. John" />
<!-- With a label, errors and bottom margin -->
<div class="mb-6">
<.form_label form={f} field={:name} />
<.text_input form={f} field={:name} placeholder="eg. John" />
<.form_field_error form={f} field={:name} class="mt-1" />
</div>
<!-- Includes label, errors and bottom margin -->
<.form_field
type="text_input"
form={f}
field={:first_name}
placeholder="eg. John"
/>
```
#### Number input
Same as [Text input](#text-input) but use `<.number_input>`.
#### Email input
Same as [Text input](#text-input) but use `<.email_input>`.
#### Password input
Same as [Text input](#text-input) but use `<.password_input>`.
#### Telephone input
Same as [Text input](#text-input) but use `<.telephone_input>`.
#### URL input
Same as [Text input](#text-input) but use `<.url_input>`.
#### Time input
Same as [Text input](#text-input) but use `<.time_input>`.
#### Time select
Same as [Text input](#text-input) but use `<.time_select>`.
#### Datetime input
Same as [Text input](#text-input) but use `<.datetime_local_input>`.
#### Datetime select
Same as [Text input](#text-input) but use `<.datetime_select>`.
#### Color input
Same as [Text input](#text-input) but use `<.color_input>`.
#### File input
Same as [Text input](#text-input) but use `<.file_input>`.
#### Range input
Same as [Text input](#text-input) but use `<.range_input>`.
#### Text area
Same as [Text input](#text-input) but use `<.textarea>`.
#### Select
```elixir
<.select
options={["Admin": "admin", "User": "user"]}
form={f}
field={:role}
/>
<!-- With a label, errors and bottom margin -->
<div class="mb-6">
<.form_label form={f} field={:role} />
<.select
options={["Admin": "admin", "User": "user"]}
form={f}
field={:role}
/>
<.form_field_error form={f} field={:name} class="mt-1" />
</div>
<!-- Includes label, errors and bottom margin -->
<.form_field
type="select"
options={["Admin": "admin", "User": "user"]}
form={f}
field={:role}
/>
```
#### Checkbox
```elixir
<.checkbox form={f} field={:read_terms} />
<!-- With a label, errors and bottom margin -->
<label class="inline-flex items-center block gap-3 mb-6 text-sm text-gray-900 dark:text-gray-200">
<.checkbox form={f} field={:read_terms} />
<div>I accept</div>
</label>
<.form_field_error form={f} field={:read_terms} class="mt-1" />
<!-- Includes label, errors and bottom margin -->
<.form_field
type="checkbox"
form={f}
field={:read_terms}
label="I accept"
/>
```
#### Radios
```elixir
<.checkbox form={f} field={:read_terms} />
<!-- With a label, errors and bottom margin -->
<.form_label form={f} field={:eye_color} />
<div class="flex flex-col gap-1">
<label class="inline-flex items-center block gap-3 text-sm text-gray-900 dark:text-gray-200">
<.radio form={f} field={:eye_color} value="green" />
<div>Green</div>
</label>
<label class="inline-flex items-center block gap-3 text-sm text-gray-900 dark:text-gray-200">
<.radio form={f} field={:eye_color} value="blue" />
<div>Blue</div>
</label>
<label class="inline-flex items-center block gap-3 text-sm text-gray-900 dark:text-gray-200">
<.radio form={f} field={:eye_color} value="gray" />
<div>Gray</div>
</label>
</div>
<.form_field_error form={f} field={:read_terms} class="mt-1" />
<!-- Includes label, errors and bottom margin -->
<.form_field
type="radio_group"
form={f}
field={:eye_color}
options={["Green": "green", "Blue": "blue", "Gray": "gray"]}
label="Eye color"
/>
```
### Dropdowns
Dropdowns require Javascript. You can choose whether to use Alpine JS or the `Phoenix.LiveView.JS` module.
Note that the `Phoenix.LiveView.JS` option only works in live components. For dead components you must use Alpine JS.
```elixir
<.dropdown label="Dropdown" js_lib="alpine_js|live_view_js" placement="left|right">
<.dropdown_menu_item>
<Heroicons.Outline.home class="w-5 h-5 text-gray-500" />
Button item with icon
</.dropdown_menu_item>
<.dropdown_menu_item link_type="button" label="a item" />
<.dropdown_menu_item link_type="a" to="/" label="a item" />
<.dropdown_menu_item link_type="live_patch" to="/" label="Live Patch item" />
<.dropdown_menu_item link_type="live_redirect" to="/" label="Live Redirect item" />
</.dropdown>
```
### Loading indicators
```elixir
<.spinner show={false} />
<.spinner show={true} size="sm" />
<.spinner show={true} size="md" class="text-green-500" />
<.spinner show={true} size="lg" class="text-red-500" />
```
### Breadcrumbs
#### Slash
```elixir
<.breadcrumbs links={[
%{ label: "Link 1", to: "#" },
%{ label: "Link 2", to: "#" },
%{ label: "Link 3", to: "#" }
]}/>
```
#### Chevron
```elixir
<.breadcrumbs separator="chevron" links={[
%{ label: "Link 1", to: "#" },
%{ label: "Link 2", to: "#", link_type: "live_patch" },
%{ label: "Link 3", to: "#", link_type: "live_redirect" },
]}/>
```
### Breadcrumbs
#### Basic Avatars
```elixir
<.avatar size="xs | sm | md | lg | xl " src="https://res.cloudinary.com/wickedsites/image/upload/v1604268092/unnamed_sagz0l.jpg" />
```
#### Avatars with placeholder icon
```elixir
<.avatar size="xs | sm | md | lg | xl"/>
```
#### Avatar groups stacked
```elixir
<.avatar_group avatars={[
"https://res.cloudinary.com/wickedsites/image/upload/v1604268092/unnamed_sagz0l.jpg",
"https://res.cloudinary.com/wickedsites/image/upload/v1636595188/dummy_data/avatar_1_lc8plf.png",
"https://res.cloudinary.com/wickedsites/image/upload/v1636595188/dummy_data/avatar_2_jhs6ww.png",
"https://res.cloudinary.com/wickedsites/image/upload/v1636595189/dummy_data/avatar_14_rkiyfa.png",
]} size="xs | sm | md | lg | xl" class="inline-block"/>
```
#### Avatars with placeholder initials
```elixir
<.avatar name="Petal Components" size="xs | sm | md | lg | xl" />
```
#### Random color generated avatars with placeholder initials
```elixir
<.avatar name="Matt Platts" size="xs | sm | md | lg | xl" random_color />
```
### Progress
#### Progress bar
```elixir
<.progress color="primary | secondary | info | success | warning | danger" value={30} max={100} class="max-w-xl" />
```
##### Sizes
```elixir
<.progress size="xs | sm | md | lg | xl" value={15} max={100} class="max-w-xl" label="15%" />
```
### Pagination
#### Basic pagination
```elixir
<.pagination link_type="a | live_patch | live_redirect" class="mb-5" path="/:page" current_page={1} total_pages={10} />
```