General-purpose utility display components for PhiaUI.
15 components covering accessibility wrappers, text display helpers, time formatting, print/screen visibility, and client-side focus management.
All components are tier :primitive except focus_trap (:interactive).
Components
| Component | Purpose |
|---|---|
visually_hidden | sr-only wrapper for accessibility text |
line_clamp | Truncate text with a read-more toggle (no server round-trip) |
highlight_text | Highlight query matches in text |
relative_time | Format a DateTime as "X minutes ago" |
number_format | Format numbers with compact, prefix, suffix |
reading_time | Estimate reading time from text |
label_with_tooltip | Label + help icon with tooltip |
print_only | Content visible only when printing |
screen_only | Content hidden when printing |
color_mode_value | Render different slots in light vs dark mode |
diff_display | Word-level diff with added/removed markup |
stat_unit | Value + unit display pair |
word_count | Display word count with label |
focus_trap | Focus trap wrapper (requires PhiaFocusTrap JS hook) |
sticky_wrapper | Sticky-positioned wrapper with configurable top offset |
Summary
Functions
Renders different content in light mode vs dark mode.
Renders a word-level diff between two strings.
Wraps content in a focus trap managed by the PhiaFocusTrap JS hook.
Renders text with query matches highlighted in <mark> elements.
Renders a form label with an attached help icon and tooltip on hover.
Clamps text to a number of lines with a client-side read-more/less toggle.
Renders a formatted number with optional compact notation, prefix, and suffix.
Wraps content that should only be visible when printing.
Estimates and displays reading time for a block of text.
Renders a DateTime as a relative human-readable string.
Wraps content that should be hidden when printing.
Renders a value+unit pair for metric or stat displays.
Renders a sticky-positioned wrapper at a configurable top offset.
Renders content that is visually hidden but accessible to screen readers.
Displays the word count of a string.
Functions
Renders different content in light mode vs dark mode.
Uses CSS dark:hidden / dark:block — no JS needed. The :light slot
is hidden in dark mode; the :dark slot is hidden in light mode.
Example
<.color_mode_value>
<:light><img src="/logo-light.png" alt="Logo" /></:light>
<:dark><img src="/logo-dark.png" alt="Logo" /></:dark>
</.color_mode_value>Attributes
class(:string) - Defaults tonil.
Slots
light(required) - Content rendered in light mode only.dark(required) - Content rendered in dark mode only.
Renders a word-level diff between two strings.
Removed words are rendered in <del> with a red background.
Added words are rendered in <ins> with a green background.
Unchanged words are rendered as plain text.
Example
<.diff_display before="The quick brown fox" after="The slow green fox" />Attributes
before(:string) (required) - Original text (words removed will be highlighted).after(:string) (required) - New text (words added will be highlighted).class(:string) - Defaults tonil.
Wraps content in a focus trap managed by the PhiaFocusTrap JS hook.
When enabled, Tab/Shift+Tab cycle only within focusable elements inside
this wrapper. Pressing Escape fires a "focus-trap-escape" push event
to the parent LiveView.
Requires the PhiaFocusTrap JS hook to be installed.
Example
<.focus_trap id="modal-trap" enabled={@modal_open}>
<form>...</form>
<button phx-click="close">Close</button>
</.focus_trap>LiveView handler
def handle_event("focus-trap-escape", _params, socket) do
{:noreply, assign(socket, modal_open: false)}
endAttributes
id(:string) (required) - Unique element ID (required by phx-hook).enabled(:boolean) - When false, the trap is disabled. Defaults totrue.class(:string) - Defaults tonil.- Global attributes are accepted.
Slots
inner_block(required)
Renders text with query matches highlighted in <mark> elements.
XSS-safe: splits in Elixir and renders via HEEx — no raw/1 used.
Case-insensitive matching. Renders plain text when query is empty.
Example
<.highlight_text text="Hello World" query="world" />Attributes
text(:string) (required) - Full text to render.query(:string) - Search query to highlight. Defaults to"".mark_class(:string) - CSS classes for highlighted<mark>elements. Defaults tonil.class(:string) - Defaults tonil.
Renders a form label with an attached help icon and tooltip on hover.
The tooltip is implemented with CSS :hover/:focus-visible using a
group parent — no JS hook needed.
Example
<.label_with_tooltip
label="API Key"
tooltip="Your secret API key. Never share this."
for="api-key-input"
/>Attributes
label(:string) (required) - Label text.tooltip(:string) (required) - Tooltip text shown on hover/focus.for(:string) - HTMLforattribute for the label. Defaults tonil.required(:boolean) - Show required asterisk. Defaults tofalse.class(:string) - Defaults tonil.
Clamps text to a number of lines with a client-side read-more/less toggle.
Uses JS.toggle_class to flip the clamp — no server round-trip needed.
The clamped state uses CSS line-clamp-{n} utilities.
Example
<.line_clamp lines={3}>
A very long article excerpt that goes on for many lines...
</.line_clamp>Attributes
lines(:integer) - Number of lines to clamp to initially. Defaults to3.read_more_label(:string) - Defaults to"Read more".read_less_label(:string) - Defaults to"Read less".class(:string) - Defaults tonil.- Global attributes are accepted.
Slots
inner_block(required)
Renders a formatted number with optional compact notation, prefix, and suffix.
Compact notation: 1000 → "1K", 1_000_000 → "1M", 1_000_000_000 → "1B".
Example
<.number_format value={1234567} compact={true} prefix="$" />
<%!-- Renders: $1.2M --%>
<.number_format value={3.14159} decimals={2} suffix="%" />
<%!-- Renders: 3.14% --%>Attributes
value(:any) (required) - Numeric value to format.compact(:boolean) - Use compact notation: 1K, 1M, 1B. Defaults tofalse.decimals(:integer) - Decimal places (non-compact mode). Defaults to0.prefix(:string) - Prefix string (e.g. '$'). Defaults tonil.suffix(:string) - Suffix string (e.g. '%'). Defaults tonil.class(:string) - Defaults tonil.- Global attributes are accepted.
Wraps content that should only be visible when printing.
Hidden on screen (hidden), shown when printing (print:block).
Example
<.print_only>
<h1>Invoice #1234</h1>
<p>Printed at: 2026-01-01</p>
</.print_only>Attributes
class(:string) - Defaults tonil.- Global attributes are accepted.
Slots
inner_block(required)
Estimates and displays reading time for a block of text.
Uses ceil(word_count / wpm) rounding up to the nearest minute.
Default WPM is 238, which is the average adult silent reading speed.
Example
<.reading_time text={@article.body} />
<%!-- Renders: "5 min read" --%>
<.reading_time text={@post.content} wpm={200} label="minute read" />Attributes
text(:string) (required) - Plain text content to estimate reading time for.wpm(:integer) - Words per minute reading speed (default: 238). Defaults to238.label(:string) - Label appended after the minute count. Defaults to"min read".class(:string) - Defaults tonil.- Global attributes are accepted.
Renders a DateTime as a relative human-readable string.
Returns values like "just now", "5 minutes ago", "2 hours ago", "3 days ago", "4 months ago", "2 years ago". Negative differences show "in X minutes", etc.
Example
<.relative_time datetime={@post.inserted_at} />Attributes
datetime(:any) (required) - ADateTimeorNaiveDateTimestruct.now(:any) - Override 'now' (for testing). Defaults toDateTime.utc_now(). Defaults tonil.class(:string) - Defaults tonil.- Global attributes are accepted.
Wraps content that should be hidden when printing.
Visible on screen, hidden when printing (print:hidden).
Example
<.screen_only>
<nav>...</nav>
<button phx-click="action">Click me</button>
</.screen_only>Attributes
class(:string) - Defaults tonil.- Global attributes are accepted.
Slots
inner_block(required)
Renders a value+unit pair for metric or stat displays.
The value is bold; the unit is muted and slightly smaller.
Example
<.stat_unit value="42" unit="requests/sec" />
<.stat_unit value="99.9" unit="%" value_class="text-3xl" />Attributes
value(:string) (required) - The primary numeric or text value.unit(:string) (required) - The unit label displayed after the value.value_class(:string) - Additional classes for the value element. Defaults tonil.unit_class(:string) - Additional classes for the unit element. Defaults tonil.class(:string) - Defaults tonil.- Global attributes are accepted.
Renders a sticky-positioned wrapper at a configurable top offset.
Use for headers, toolbars, or sidebars that should remain visible during scrolling within a scrollable container.
Example
<.sticky_wrapper offset_top={64}>
<.data_grid_toolbar>...</.data_grid_toolbar>
</.sticky_wrapper>Attributes
offset_top(:integer) - Top offset in pixels for the sticky position. Defaults to0.z_index(:integer) - z-index for the sticky element. Defaults to10.class(:string) - Defaults tonil.- Global attributes are accepted.
Slots
inner_block(required)
Displays the word count of a string.
Splits on whitespace and counts non-empty tokens.
Example
<.word_count text={@body} />
<%!-- Renders: "247 words" --%>
<.word_count text={@body} label="word count" />Attributes
text(:string) (required) - Text to count words in.label(:string) - Label appended after the word count. Defaults to"words".class(:string) - Defaults tonil.- Global attributes are accepted.