A compact element for input, attribute, or action (pp_chip/1), in the
spirit of MUI's Chip.
<.pp_chip>Basic</.pp_chip>
<.pp_chip variant="outlined" color="primary">Outlined</.pp_chip>
<.pp_chip deletable on_delete={JS.push("remove_tag", value: %{tag: "react"})}>
React
<:icon><.pp_icon name="hero-check" /></:icon>
</.pp_chip>
<.pp_chip clickable phx-click="select_filter" phx-value-id="unread">
Unread
</.pp_chip>clickable (default false) picks the root element: a real <button>
(gets native keyboard/focus/disabled handling and a Button-style
ripple for free — see PhoenixPaper.Ripple) when true, a plain <div>
otherwise, the same conditional-root-tag approach PhoenixPaper.ListItem
uses for link-vs-static (see AGENTS.md) — HEEx can't parameterize a tag
name, so this is two :if/:if={!...} branches sharing one private
chip_content/1 for the icon/label/delete markup. Pass phx-click
through rest (like PhoenixPaper.ToggleButton) for the click itself;
clickable={false} (the default) with deletable={true} is exactly
MUI's "chip with a delete affordance but no other interaction" case — a
static tag the caller can still remove.
The delete "button" (only rendered when deletable is true) is
deliberately a <span role="button" tabindex="0">, not a real <button>
— a real <button> nested inside clickable's own <button> root would
be invalid HTML (browsers auto-close the outer one, breaking the whole
chip's layout). A small onkeydown snippet (Enter/Space triggers a
synthetic click, same "small vanilla snippet, no hook" precedent as
PhoenixPaper.Ripple) keeps it keyboard-operable despite not being a real
button.
Does not call event.stopPropagation() — an earlier version did, to
stop clicking delete on a clickable chip from also firing the chip's own
click, but that broke on_delete entirely: LiveView's phx-click binding
is one delegated window-level listener (bound during the bubble phase),
so stopPropagation() on the delete span prevented the click from ever
reaching it, and the delete control silently did nothing. Confirmed with a
real click in a real browser, not just a static render — the rendered
markup and phx-click attribute both looked correct in isolation.
Unnecessary anyway: LiveView resolves a click to the nearest
phx-click-bearing ancestor-or-self via closestPhxBinding (starting
from the actual event target and walking up), so a click on the delete
span already resolves to the delete span's own phx-click, never the
outer button's — no manual propagation-stopping needed for that.
disabled dims and disables both the root (when clickable, a real
disabled attribute; when not, pointer-events-none — a plain <div>
has no native disabled) and the delete control (pointer-events-none
plus tabindex="-1", removing it from the tab order) — it isn't gated
behind clickable since deletable-only chips (no other interaction)
can still need to be disabled.
color="default" (gray, using --color-pp-surface-variant/
-on-surface/-outline — the same neutral tokens Input/Select/
NumberField already use for their filled backgrounds) is the default
here, unlike every other colored component in this library — a plain tag
chip (MUI's most common real-world case) is neutral, not brand-colored.
Every other color value (primary/secondary/tertiary/error, plus
success/warning/info for status, see PhoenixPaper.Alert) is also
available.
clickable's hover/active feedback is one filter: brightness() step
(hover:brightness-95 active:brightness-90) applied uniformly across
every color/variant combination, rather than a hand-picked
color-matched tint per branch the way PhoenixPaper.Button's outlined/
text variants do (hover:bg-pp-primary/10, etc.) — simpler, and the
one place a chip's hover feedback is a little more subtle for an
outlined/transparent-background chip than for a filled one, since a
brightness filter has less visible effect over a transparent background.
Summary
Functions
Renders a chip. See the module doc.
Functions
Renders a chip. See the module doc.
Attributes
paperize(:boolean) - Defaults totrue.variant(:string) - Defaults to"filled". Must be one of"filled", or"outlined".color(:string) - Defaults to"default". Must be one of"default","primary","secondary","tertiary","error","success","warning", or"info".size(:string) - Defaults to"medium". Must be one of"small", or"medium".clickable(:boolean) - renders as a real <button> with hover/focus/ripple, for filter/action chips. Defaults tofalse.ripple(:boolean) - the Material ripple effect on click/tap when clickable — off whenever paperize is false, see PhoenixPaper.Ripple. Defaults totrue.disabled(:boolean) - Defaults tofalse.type(:string) - Defaults to"button". Must be one of"button","submit", or"reset".deletable(:boolean) - renders a trailing delete (x) control wired to on_delete. Defaults tofalse.on_delete(Phoenix.LiveView.JS) - JS command run when the delete control is clicked, e.g. JS.push("remove_chip"). Defaults to%Phoenix.LiveView.JS{ops: []}.class(:any) - Defaults tonil.- Global attributes are accepted. Supports all globals plus:
["form", "name", "value", "phx-click"].
Slots
icon- a leading icon or avatar.inner_block(required) - the chip's label.