Skeleton
Use CSkeleton to hold a known layout while its data loads. Compose explicit primitives instead of encoding a page shape in a preset string.
Skeleton at a glance
Show code
import citry_ui
from citry import Component, citry
citry.register_library(citry_ui)
class SkeletonGlance(Component):
template = """
<section class="skeleton-glance" aria-label="Loading field note" aria-busy="true">
<c-CSkeleton height="8rem" animation="wave" />
<c-CGroup c-gap="'sm'">
<c-CSkeleton kind="circle" width="2.75rem" />
<c-CSkeleton kind="text" c-lines="3" />
</c-CGroup>
</section>
"""
css = """
:where(.skeleton-glance) {
display: grid;
max-inline-size: 24rem;
gap: 1rem;
padding: 1rem;
border: 1px solid light-dark(#b8cbb9, #425947);
border-radius: 0.9rem;
}
"""
preview = SkeletonGlance()
preview # noqa: B018
Choose a primitive
Rectangles hold media and panels, circles hold avatars and icons, and text lines track typography.
Show code
import citry_ui
from citry import Component, citry
citry.register_library(citry_ui)
class SkeletonPrimitives(Component):
template = """
<div class="skeleton-primitives" aria-label="Loading archive specimens" aria-busy="true">
<c-CSkeleton width="10rem" height="5rem" />
<c-CSkeleton kind="circle" width="3rem" />
<c-CSkeleton kind="text" width="12rem" />
</div>
"""
css = """
:where(.skeleton-primitives) {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 1rem;
}
"""
preview = SkeletonPrimitives()
preview # noqa: B018
Shape text
lines produces compact paragraph geometry. Set the final line width to make the placeholder resemble real prose.
Show code
import citry_ui
from citry import Component, citry
citry.register_library(citry_ui)
class SkeletonText(Component):
template = """
<div class="skeleton-text" aria-label="Loading fern description" aria-busy="true">
<c-CSkeleton kind="text" height="1.15rem" width="55%" />
<c-CSkeleton kind="text" c-lines="4" last_line_width="38%" />
</div>
"""
css = """
:where(.skeleton-text) {
display: grid;
max-inline-size: 30rem;
gap: 1rem;
}
"""
preview = SkeletonText()
preview # noqa: B018
Compose real layouts
Build familiar patterns with CStack, CGroup, and ordinary CSS. The visible structure stays inspectable and responsive.
Show code
import citry_ui
from citry import Component, citry
citry.register_library(citry_ui)
class SkeletonCard(Component):
template = """
<c-CCard c-attrs="{'aria-label': 'Loading moonfern field note', 'aria-busy': 'true'}">
<c-fill name="media"><c-CSkeleton height="9rem" /></c-fill>
<c-fill name="default">
<c-CStack c-gap="'sm'">
<c-CSkeleton kind="text" height="1.2rem" width="48%" />
<c-CSkeleton kind="text" c-lines="3" />
</c-CStack>
</c-fill>
</c-CCard>
"""
preview = SkeletonCard()
preview # noqa: B018
Show code
import citry_ui
from citry import Component, citry
citry.register_library(citry_ui)
class SkeletonList(Component):
class Kwargs:
pass
class Slots:
pass
template = """
<div class="skeleton-list" aria-label="Loading specimen index" aria-busy="true">
<c-for each="item in items">
<c-CGroup #c-key="item" c-gap="'sm'" c-align="'center'">
<c-CSkeleton kind="circle" width="2.5rem" />
<c-CSkeleton kind="text" c-lines="2" c-last_line_width="f'{45 + item * 8}%'" />
<c-CSkeleton width="3.5rem" height="1.5rem" />
</c-CGroup>
</c-for>
</div>
"""
def template_data(
self,
kwargs: Kwargs, # noqa: ARG002
slots: Slots, # noqa: ARG002
) -> dict[str, object]:
return {"items": (0, 1, 2)}
css = """
:where(.skeleton-list) {
display: grid;
max-inline-size: 30rem;
gap: 1rem;
}
:where(.skeleton-list [data-citry-ui-part="group"] > :nth-child(2)) {
flex: 1 1 auto;
}
"""
preview = SkeletonList()
preview # noqa: B018
Choose motion
Pulse is the default. Wave provides stronger progress motion, while none makes a static wireframe. Reduced-motion preferences disable both animations.
Show code
import citry_ui
from citry import Component, citry
citry.register_library(citry_ui)
class SkeletonMotion(Component):
class Kwargs:
pass
class Slots:
pass
template = """
<div class="skeleton-motion" aria-label="Loading archive shelves" aria-busy="true">
<c-for each="motion in motions">
<div><span>{{ motion }}</span><c-CSkeleton c-animation="motion" height="2.5rem" /></div>
</c-for>
</div>
"""
def template_data(
self,
kwargs: Kwargs, # noqa: ARG002
slots: Slots, # noqa: ARG002
) -> dict[str, object]:
return {"motions": ("pulse", "wave", "none")}
css = """
:where(.skeleton-motion) {
display: grid;
gap: 0.75rem;
}
:where(.skeleton-motion > div) {
display: grid;
grid-template-columns: 4rem 1fr;
align-items: center;
gap: 0.75rem;
font: 0.75rem ui-sans-serif, system-ui, sans-serif;
}
"""
preview = SkeletonMotion()
preview # noqa: B018
Customize Skeleton
Public variables control dimensions, color, radius, spacing, and timing.
Show code
import citry_ui
from citry import Component, citry
citry.register_library(citry_ui)
class SkeletonCustomization(Component):
template = """
<div class="skeleton-lichen" aria-label="Loading lichen plates" aria-busy="true">
<c-CSkeleton height="5rem" animation="wave" />
<c-CSkeleton kind="text" c-lines="3" />
</div>
"""
css = """
:where(.skeleton-lichen) {
--cui-skeleton-background: light-dark(#c9dfc8, #36513c);
--cui-skeleton-highlight: light-dark(rgb(255 255 255 / 70%), rgb(190 239 200 / 28%));
--cui-skeleton-radius: 1rem;
display: grid;
max-inline-size: 24rem;
gap: 1rem;
}
"""
preview = SkeletonCustomization()
preview # noqa: B018
Accessibility and loading ownership
Skeletons are decorative and hidden from assistive technology. Put aria-busy="true" and a useful accessible name on the region whose content is loading. That region, not Skeleton, owns async state and announcements.
API reference
Inputs
CSkeleton server inputs
Server inputs are passed in a template through <c-CSkeleton ... /> or in Python through CSkeleton(...).
| Input | Type | Default | Effect |
|---|---|---|---|
kind | "rect" | "text" | "circle" (CSkeletonKind) | "rect" | Selects primitive geometry. |
lines | int (1..100) | 1 | Renders one or more text lines; values above one require text kind. |
animation | "pulse" | "wave" | "none" (CSkeletonAnimation) | "pulse" | Selects CSS-only motion. Reduced-motion always disables it. |
width | str | None | None | Sets the root width to one CSS length or percentage. |
height | str | None | None | Sets the primitive or line height to one CSS length or percentage. |
last_line_width | str | "70%" | Sets the final line width when multiple text lines render. |
class_ | str | Mapping[str, bool] | Sequence[CClassValue] | None (CClassValue) | None | Adds root classes and merges them with attrs. |
style | str | Mapping[str, str | int | float | bool | None] | Sequence[CStyleValue] | None (CStyleValue) | None | Adds root inline styles before direct dimension inputs. |
attrs | Mapping[str, object] | None | None | Adds copied trusted root attributes without replacing decorative semantics, children, reflections, focus, or Citry runtime fields. |
Slots
-
Events
-
Methods
-
CSS
CSS variables to theme the components. Set them on an ancestor or the component itself.
CSkeleton CSS variables
Apply these variables to CSkeleton or one of its ancestors.
| Variable | Type | Purpose | Default |
|---|---|---|---|
--cui-skeleton-width | length-or-percentage | Root or line-group width. | Kind-derived 100% or 3rem. |
--cui-skeleton-height | length-or-percentage | Root or line height. | Kind-derived 6rem, 0.75em, or 3rem. |
--cui-skeleton-radius | length | Primitive corners. | Kind-derived 0.5rem, 999px, or 50%. |
--cui-skeleton-background | color | Resting placeholder surface. | Scheme-derived neutral. |
--cui-skeleton-highlight | color | Wave highlight. | Translucent white. |
--cui-skeleton-gap | length | Text line gap. | 0.5em |
--cui-skeleton-duration | time | Pulse or wave cycle. | 1.5s |
--cui-skeleton-last-line-width | length-or-percentage | Final text-line width. | Input-derived 70%. |
Attributes
HTML attributes defined on the components that you can refer to for CSS, inspection, and testing. Read-only.
CSkeleton attributes
| Attribute | Element | Type | Meaning |
|---|---|---|---|
data-kind | Root | "rect" | "text" | "circle" | Reflects primitive geometry. |
data-animation | Root | "pulse" | "wave" | "none" | Reflects requested motion. |
aria-hidden | Root | "true" | Keeps decorative placeholder geometry out of the accessibility tree. |
Selectors
Selectors for the DOM nodes in the components that you can use for CSS, inspection, and testing.
CSkeleton selectors
| Selector | Element | Purpose |
|---|---|---|
[data-citry-ui-part="skeleton"] | Root span | Stable primitive and attrs destination. |
[data-citry-ui-part="line"] | Text line span | Stable direct child in text mode. |
Interfaces
Aliases and data shapes referenced above.
Input type aliases
| Interface | Definition |
|---|---|
CClassValue | str | Mapping[str, bool] | Sequence[CClassValue] |
CStyleValue | str | Mapping[str, str | int | float | bool | None] | Sequence[CStyleValue] |
CSkeletonKind | Literal["rect", "text", "circle"] |
CSkeletonAnimation | Literal["pulse", "wave", "none"] |
Translation keys
-