PhiaUi.Components.Utilities (phia_ui v0.1.17)

Copy Markdown View Source

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

ComponentPurpose
visually_hiddensr-only wrapper for accessibility text
line_clampTruncate text with a read-more toggle (no server round-trip)
highlight_textHighlight query matches in text
relative_timeFormat a DateTime as "X minutes ago"
number_formatFormat numbers with compact, prefix, suffix
reading_timeEstimate reading time from text
label_with_tooltipLabel + help icon with tooltip
print_onlyContent visible only when printing
screen_onlyContent hidden when printing
color_mode_valueRender different slots in light vs dark mode
diff_displayWord-level diff with added/removed markup
stat_unitValue + unit display pair
word_countDisplay word count with label
focus_trapFocus trap wrapper (requires PhiaFocusTrap JS hook)
sticky_wrapperSticky-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

color_mode_value(assigns)

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 to nil.

Slots

  • light (required) - Content rendered in light mode only.
  • dark (required) - Content rendered in dark mode only.

diff_display(assigns)

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 to nil.

focus_trap(assigns)

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)}
end

Attributes

  • id (:string) (required) - Unique element ID (required by phx-hook).
  • enabled (:boolean) - When false, the trap is disabled. Defaults to true.
  • class (:string) - Defaults to nil.
  • Global attributes are accepted.

Slots

  • inner_block (required)

highlight_text(assigns)

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 to nil.
  • class (:string) - Defaults to nil.

label_with_tooltip(assigns)

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) - HTML for attribute for the label. Defaults to nil.
  • required (:boolean) - Show required asterisk. Defaults to false.
  • class (:string) - Defaults to nil.

line_clamp(assigns)

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 to 3.
  • read_more_label (:string) - Defaults to "Read more".
  • read_less_label (:string) - Defaults to "Read less".
  • class (:string) - Defaults to nil.
  • Global attributes are accepted.

Slots

  • inner_block (required)

number_format(assigns)

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 to false.
  • decimals (:integer) - Decimal places (non-compact mode). Defaults to 0.
  • prefix (:string) - Prefix string (e.g. '$'). Defaults to nil.
  • suffix (:string) - Suffix string (e.g. '%'). Defaults to nil.
  • class (:string) - Defaults to nil.
  • Global attributes are accepted.

reading_time(assigns)

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 to 238.
  • label (:string) - Label appended after the minute count. Defaults to "min read".
  • class (:string) - Defaults to nil.
  • Global attributes are accepted.

relative_time(assigns)

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) - A DateTime or NaiveDateTime struct.
  • now (:any) - Override 'now' (for testing). Defaults to DateTime.utc_now(). Defaults to nil.
  • class (:string) - Defaults to nil.
  • Global attributes are accepted.

screen_only(assigns)

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 to nil.
  • Global attributes are accepted.

Slots

  • inner_block (required)

stat_unit(assigns)

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 to nil.
  • unit_class (:string) - Additional classes for the unit element. Defaults to nil.
  • class (:string) - Defaults to nil.
  • Global attributes are accepted.

sticky_wrapper(assigns)

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 to 0.
  • z_index (:integer) - z-index for the sticky element. Defaults to 10.
  • class (:string) - Defaults to nil.
  • Global attributes are accepted.

Slots

  • inner_block (required)

visually_hidden(assigns)

Renders content that is visually hidden but accessible to screen readers.

Uses the sr-only utility class. Use this for labels, descriptions, and context text that sighted users can infer visually but screen reader users need.

Example

<.visually_hidden>Current step: 3 of 5</.visually_hidden>

Attributes

  • as (:string) - HTML element to render: 'span' or 'div'. Defaults to "span".
  • class (:string) - Defaults to nil.
  • Global attributes are accepted.

Slots

  • inner_block (required)

word_count(assigns)

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 to nil.
  • Global attributes are accepted.