Skullet (Skullet v0.1.0)

Copy Markdown View Source

Draws a skeleton of what is about to load.

While data is missing, your own component is rendered from a sample with its text transparent, the browser lays it out for real, and rounded bars are drawn over it — one per wrapped line, one block per image or control. Nothing is guessed, so nothing moves when the content arrives.

The most common shape, one assign_async/3 and a sample:

<Skullet.skeleton :let={user} id="profile" async={@user} sample={@sample_user}>
  <Profile.card user={user} />
</Skullet.skeleton>

Wiring, in the app's assets/js/app.js and assets/css/app.css:

import { SkulletHook } from "../../deps/skullet/priv/static/skullet.js"
new LiveSocket("/live", Socket, { hooks: { Skullet: SkulletHook } })

@import "../../deps/skullet/priv/static/skullet.css";

The CSS is not optional: it keeps the sample invisible before the engine runs.

Under a Content Security Policy the boot script and the theme's <style> block need the page's nonce: nonce={@csp_nonce} on skeleton/1, provider/1 and box/1; see the CSP section.

The detail lives in the guides:

  • Usage — async assigns and plain data, attributes and slots, empty and error states, memory, images, plain pages without LiveView.
  • Theming — every custom property, the theme map, the provider.
  • Reveals — how smart decides, the built-in modes, specs of your own.
  • Internals — how measuring works, first paint, the engine's build.

Skullet.Boot.boot/1 covers the gap before the bundle loads, and Skullet.Memory backs remember.

Summary

Functions

A placeholder block for what cannot be measured — a logo, a chart, a canvas.

Sets the skeleton's look for everything inside — one place instead of every skeleton.

Functions

box(assigns)

@spec box(map()) :: Phoenix.LiveView.Rendered.t()

A placeholder block for what cannot be measured — a logo, a chart, a canvas.

Put it inside the skeleton's sample in place of the thing, sized like the thing:

<Skullet.skeleton :let={report} id="report" async={@report} sample={@sample}>
  <Skullet.box width={240} height={120} />
  <p>{report.title}</p>
</Skullet.skeleton>

Attributes

  • width (:any) - any CSS length, or a number of pixels. Defaults to "60%".
  • height (:any) - any CSS length, or a number of pixels. Defaults to "1em".
  • radius (:any) - defaults to --skullet-radius. Defaults to nil.
  • nonce (:string) - the CSP nonce for the <style> block this emits. Defaults to nil.

provider(assigns)

Sets the skeleton's look for everything inside — one place instead of every skeleton.

<Skullet.provider mode="dark" animation="group-shimmer" speed="fast">
  …
</Skullet.provider>

mode sets color-scheme on the wrapper, which is what the bars' colours follow, so "dark" gives dark-page bars even where the page itself is light. theme, animation and speed become the custom properties the skeletons inside inherit.

Attributes

  • id (:string) - DOM id for the wrapper. Defaults to nil.
  • nonce (:string) - the CSP nonce for the <style> block this emits. Defaults to nil.
  • mode (:string) - Defaults to nil.Must be one of nil, "light", "dark", or "system".
  • animation (:string) - Defaults to nil.Must be one of nil, "shimmer", "group-shimmer", "pulse", or "none".
  • speed (:string) - Defaults to nil.Must be one of nil, "slow", or "fast".
  • theme (:map) - custom properties for everything inside. Defaults to %{}.
  • Global attributes are accepted.

Slots

  • inner_block (required)

skeleton(assigns)

@spec skeleton(map()) :: Phoenix.LiveView.Rendered.t()

Attributes

  • id (:string) (required) - DOM id for the hook; also the future shape key.
  • async (:any) - a %Phoenix.LiveView.AsyncResult{}. Defaults to nil.
  • data (:any) - plain data; nil means loading, anything else is loaded. Defaults to nil.
  • sample (:any) (required) - stands in for the data to draw the skeleton.
  • loading (:boolean) - forces the skeleton, even over data. Defaults to false.
  • empty (:boolean) - shows the empty_message slot in place of the content. Defaults to false.
  • reveal (:string) - smart, together, sequence, sweep, instant, or a name registered with registerReveal/2 in JS. Defaults to "smart".
  • theme (:map) - custom properties to override on this skeleton. Defaults to %{}.
  • nonce (:string) - the CSP nonce for the <style> block this emits when theme is set. Defaults to nil.
  • label (:string) - read to screen readers while loading. Defaults to "Loading".
  • remember (:any) - opts in to remembering the data this skeleton last showed, under this scope. Defaults to nil.

Slots

  • error - rendered in place of the content when the async assign failed. It is your markup — the library styles none of it — so an alert, an empty state, or your own card frame all work here.

  • skeleton - your own markup to measure instead of the sample — for a logo, a chart, a canvas. It is rendered while loading in place of the inner block.

  • empty_message - rendered in place of the content while empty is true — the content itself when left out. It is your markup: the library styles none of it, so a dashed box, an illustration or a card frame all work here.

  • inner_block (required) - receives the sample, then the real value.