Loading and placeholder card components for the card stack viewport. Empty-state rendering is provided by cjm-fasthtml-app-core’s render_empty_state (V8 anatomy composition helper) — card-stack consumers wire that helper into their viewport’s empty callback rather than card-stack shipping a local empty-state default.
render_placeholder_card
Rendered in viewport slots that fall outside the items list (e.g., before the first item or after the last). The placeholder card fills the geometric slot so the focus position stays centered and the visible-card-count indicator remains accurate.
The label text ("Beginning" / "End") is hidden by default via visibility.invisible — this preserves the placeholder’s height contract (text dimensions still drive layout) while removing the visual text noise that competes with content cards. Set show_label=True to restore the visible label for debugging or alternate consumer contexts. Padding matches non-placeholder content cards (p(3)) so the placeholder’s vertical footprint is consistent with single-line content cards — this keeps the slot-count indicator faithful.
render_placeholder_card
def render_placeholder_card( placeholder_type:Literal, # Which edge of the list show_label:bool=False, # Render the "Beginning"/"End" label visibly. Default hides via visibility.invisible (preserves height contract).)->Any: # Placeholder card component
Render a placeholder card for viewport edges (fills geometric slot so focus position stays centered).
# Test render_placeholder_cardfrom fasthtml.common import to_xml# Default (show_label=False): label text is in the DOM but hidden via# visibility.invisible — preserves height contract while removing visible# "Beginning"/"End" text noise from the card stack viewport.start_card = render_placeholder_card("start")html = to_xml(start_card)assert'data-placeholder-type="start"'in htmlassert'placeholder-card'in html# Label text still in DOM (height contract preserved)assert'Beginning'in html# Visibility class applied (text invisible)assert'invisible'in html# End variant — same shape, different text content.end_card = render_placeholder_card("end")end_html = to_xml(end_card)assert'data-placeholder-type="end"'in end_htmlassert'End'in end_htmlassert'invisible'in end_html# show_label=True restores visible label (no `invisible` class).labeled = render_placeholder_card("start", show_label=True)labeled_html = to_xml(labeled)assert'Beginning'in labeled_htmlassert'invisible'notin labeled_html, ("show_label=True must produce a P tag without visibility.invisible. "f"Got: {labeled_html!r}")# Padding standardized to p(3) — matches non-placeholder content cards# so the placeholder's vertical footprint is consistent.assert'p-3'in html# Regression guard: the historical p(4) padding must not return.assert'p-4'notin html, ("Placeholder card body padding must be p(3) (matching non-placeholder cards), ""not p(4). A regression to p(4) would distort the slot-count indicator at ""every card-stack consumer site.")print("render_placeholder_card tests passed!")
render_placeholder_card tests passed!
render_loading_state
Displayed while the card stack is initializing (e.g., fetching items from a service).
render_loading_state
def render_loading_state( ids:CardStackHtmlIds, # HTML IDs for this card stack instance message:str='Loading...', # Loading message text)->Any: # Loading component