Card
v0.0.10
mix mishka.ui.gen.headless card
Overview of the Phoenix and LiveView Headless Card Component
The Mishka Chelekom Headless Card is a block of content with an optional figure, a title, a body and a row of actions. It has no JavaScript and no styling. What it adds is markup that is easy to get wrong by hand: the :title slot renders a real heading, an h3 unless you pick another level, and the root points at that heading with aria-labelledby.
Add href, navigate or patch and the whole card becomes a link. That is the correct markup for a card that is one big click target, and it has a single focus stop. In a Phoenix app with LiveView, navigate and patch behave like the same attributes on .link. Each part takes its own class attribute: class for the root, then figure_class, body_class, title_class, content_class and actions_class.
Base UI Card example - titled card with an action (Phoenix headless)
Base UI Card example - titled card with an action (Phoenix headless)
Weekly digest
<.card
id="baseui-card-hero"
class="w-full max-w-80 rounded-lg border border-neutral-200 bg-white text-neutral-950 dark:border-neutral-800 dark:bg-neutral-950 dark:text-white"
body_class="flex flex-col gap-2 p-4"
title_class="text-base font-medium"
actions_class="flex justify-end pt-2"
>
<:title>Weekly digest</:title>
<span class="text-sm text-neutral-600 dark:text-neutral-400">
A summary of everything that changed since Monday.
</span>
<:actions>
<button type="button" class="inline-flex items-center justify-center gap-2 rounded-md border px-3 py-1.5 text-sm font-medium border-neutral-200 text-neutral-950 dark:border-neutral-800 dark:text-white hover:bg-neutral-100 dark:hover:bg-neutral-800 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-neutral-950 dark:focus-visible:outline-white">Read</button>
</:actions>
</.card>Base UI Card example - the whole card as a link (Phoenix headless)
Base UI Card example - the whole card as a link (Phoenix headless)
<.card
id="baseui-card-link"
href="#"
class="block w-full max-w-80 rounded-lg border border-neutral-200 bg-white text-neutral-950 hover:bg-neutral-100 dark:border-neutral-800 dark:bg-neutral-950 dark:text-white dark:hover:bg-neutral-800 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-neutral-950 dark:focus-visible:outline-white"
body_class="flex flex-col gap-1 p-4"
title_class="text-base font-medium"
>
<:title>Changelog</:title>
<span class="text-sm text-neutral-600 dark:text-neutral-400">
The whole card is one link and one focus stop.
</span>
</.card>Figure, title level and link attributes
The :figure slot renders inside a figure element. figure_position is start (before the body, the default) or end (after it). It moves the element in the markup instead of changing a class, so corner rounding that targets the first or last child follows the figure to the other side. title_level sets the heading element: h1 to h6, or div to keep the title out of the page outline. On a card that is a link, download, hreflang, referrerpolicy, rel and target pass through to the anchor.
<.card
id="release-notes"
figure_position="end"
title_level="h2"
class="w-full max-w-80 rounded-lg border border-neutral-200"
body_class="flex flex-col gap-1 p-4"
>
<:title>Release notes</:title>
Everything that changed in this version.
<:figure>
<img src="/images/release.png" alt="" class="w-full" />
</:figure>
</.card>Parts & data attributes
Each part carries a stable data-part and a chelekom-card__* class you can target. The root also has data-figure-position with the value start or end.
| Part | data-part | State / ARIA | Purpose |
|---|---|---|---|
| Root | root | aria-labelledby, data-figure-position | A div, or an a when href, navigate or patch is set. |
| Figure | figure | - | A figure element, before or after the body per figure_position. Rendered only when the slot is given. |
| Body | body | - | Wraps the title, content and actions. |
| Title | title | id (the target of aria-labelledby) | A heading at title_level. Rendered only when the slot is given. |
| Content | content | - | The inner block. Rendered only when there is content. |
| Actions | actions | - | Buttons or links at the end of the body. Rendered only when the slot is given. |
Accessibility
A card has no WAI-ARIA pattern of its own: the heading carries the meaning. With an id and a :title slot, the root gets aria-labelledby pointing at the heading, so a card read on its own says what it is. A card that is a link takes that title as its accessible name, is one tab stop, and Enter follows it. An anchor cannot hold buttons or other links, so leave the :actions slot out of a card that is a link.
Attributes
| Name | Type | Default | Values | Required | Description |
|---|---|---|---|---|---|
id
|
string
| — | — | No | Unique id (anchors the title for aria-labelledby) |
title_level
|
string
|
h3
|
h1h2h3h4h5h6div
| No | Heading element for the `:title` slot; `div` opts out of the document outline |
figure_position
|
string
|
start
|
startend
| No | Render the figure before or after the body |
navigate
|
string
| — | — | No | Make the whole card a `<.link navigate>` |
patch
|
string
| — | — | No | Make the whole card a `<.link patch>` |
href
|
string
| — | — | No | Make the whole card a `<.link href>` |
class
|
any
| — | — | No | Extra classes for the root |
figure_class
|
any
| — | — | No | — |
body_class
|
any
| — | — | No | — |
title_class
|
any
| — | — | No | — |
actions_class
|
any
| — | — | No | — |
content_class
|
any
| — | — | No | — |
rest
|
global
| — | — | No | — |
Slots
| Name | Required | Description |
|---|---|---|
figure
| No | The image (or any media); rendered inside a `<figure>` |
title
| No | Card heading |
actions
| No | Buttons or links at the end of the body |
inner_block
| No | The card content |
Options
Each option takes one or more of its values, separated by commas.
| Option | Values |
|---|---|
--type
|
card
|
--module
| A module name of your own |
What it generates
mix mishka.ui.gen.headless card