@net-advantage/jo-ui-shadow
v2.1.0
Published
Publishable skeleton ("shadow") loader. Renders grey placeholder blocks shaped like the content that is loading, so layouts stay stable while data is fetched in the background.
Readme
@net-advantage/jo-ui-shadow
Publishable skeleton ("shadow") loader. Renders grey placeholder blocks shaped like the content that is loading, so layouts stay stable while data is fetched in the background.
{loading ? <Shadow variant="cards" count={6} /> : <CardList items={items} ... />}
{loading ? <JourneyDetailsShadow count={4} /> : <JourneyDetails ... />}- Variants:
cards(matchesjo-ui-card-list),grid(header plus rows),form(label/input fields plus actions). New variants are added in the model without changing the view. - Journey details:
JourneyDetailsShadowholds a plain Journey Details shape: a row of moment cards above a thin year bar, then empty panel rectangles. Below 720px the panels stack. From 720px note and story sit together and the side column spans below. From 1024px there are three columns (note, story, calendar). From 1500px the route map is a fourth column. It does not draw the shell header. - Animation:
shimmer(default) uses one fixed gradient so a single light sweep crosses every block together;pulsefades blocks with a staggered wave;noneis static. Groups fade in with a staggered entrance. All motion stops underprefers-reduced-motion. - Accessibility: the root is a polite
statusregion witharia-busyand a visually hidden label (LoadingonShadow,Loading journeyonJourneyDetailsShadow). The placeholder layout isaria-hidden. - Theming: colors are CSS variables with fallbacks, and
.theme-darkoverrides. The built stylesheet stays scoped tojo-ui-shadowclasses. It does not emit global utilities such as.flexor.block. - Dependencies: journey details reads year-bar, moment-card, color, and radius tokens from
@net-advantage/jo-ui-theme. The dependency isworkspace:*, so the published package depends on the workspace theme (1.4.5), which is the versioncheck:distchecks. The peer stays^1.4.4. Moment-card widths repeat150pxand196pxas fallbacks, so a 1.4.4 stylesheet still lays the rail out. The gutter is the local--jo-ui-shadow-journey-gutter(0.75rem); the theme has no spacing token for it. The16pxside inset is local for the same reason. This package does not import other components' JavaScript.
MVVM
flowchart LR
Props["ShadowProps"] --> ViewModel["useShadowViewModel"]
Model["jo-ui-shadow.model.ts"] --> ViewModel
ViewModel --> View["Shadow.tsx"]
JourneyProps["JourneyDetailsShadowProps"] --> JourneyViewModel["useJourneyDetailsShadowViewModel"]
JourneyModel["journey-details-shadow.model.ts"] --> JourneyViewModel
JourneyViewModel --> JourneyView["JourneyDetailsShadow.tsx"]
View --> Host["Host application"]
JourneyView --> Host| Layer | File | Responsibility | | --- | --- | --- | | Contract | jo-ui-shadow.types.ts | Props, variants, animations, node tree, view-model | | Contract | journey-details-shadow.types.ts | Journey details props and view-model | | Model | jo-ui-shadow.model.ts | Count defaults, class names, CSS variables, deterministic widths, per-variant node trees | | Model | journey-details-shadow.model.ts | Moment count, sticky year bar, rail label, and plain panels | | View-model | jo-ui-shadow.view-model.ts | Apply defaults and memoize the node tree | | View-model | journey-details-shadow.view-model.ts | Apply journey defaults and memoize that tree | | View | Shadow.tsx | Render the status region and the node tree | | View | JourneyDetailsShadow.tsx | Render the journey details status region |
Models
flowchart TD
Props["ShadowProps"] --> Variant["ShadowVariant"]
Props --> Animation["ShadowAnimation"]
Variant --> Nodes["ShadowNode[]"]
Nodes --> ViewModel["ShadowViewModel"]
Animation --> ViewModel
JourneyProps["JourneyDetailsShadowProps"] --> MomentCount["moment count"]
JourneyProps --> Sticky["sticky year bar"]
JourneyProps --> AddLabel["addMomentLabel"]
MomentCount --> JourneyNodes["rail, year bar, body"]
Sticky --> JourneyNodes
AddLabel --> JourneyNodes
JourneyNodes --> JourneyViewModel["JourneyDetailsShadowViewModel"]countmeans cards, grid rows, or form fields, depending on the variant. Invalid values fall back to the variant default (4, 5, 4).columnsapplies togrid(default 4).minCardWidthapplies tocards(default16rem).showActions={false}drops theformbuttons row, for example inside a dialog that already has a footer.- Widths vary deterministically by position, so the skeleton looks organic but never jumps between renders.
JourneyDetailsShadowcountis the number of moment cards.0is an empty rail. Any other invalid count falls back to 4. The rail is a row of empty card blocks and always ends with the Add New Moment card.addMomentLabelis that card's accessible name and defaults toAdd New Moment. It is not drawn inside the block.- There is no shell header, title, breadcrumb, or action row.
- The rail sits above the year bar. The year bar is one thin bar, the same height as
YearBar, with nothing inside it. The rail, the year bar, and the panels share one gutter,0.75rem(--jo-ui-shadow-journey-gutter), and a16pxside inset. stickydefaults totrue, matchingYearBar.sticky={false}leaves the year bar in normal flow. The pinned bar usestop: var(--jo-year-bar-sticky-top, 0px)andz-index: var(--jo-year-bar-z-index, 2).- Moment cards use
--jo-moment-card-min-width(150px) and--jo-moment-card-max-width(196px). Below 720px the panels stack: note, story, then the side column. From 720px note and story are side by side and the side column spans underneath. From 1024px the columns are note240px, story, and calendar272px. From 1500px they are note280px, story, route map, and calendar300px. Each panel is an empty rectangle.
Structure
- src: runtime component, model, view-model, types, and companion tests.
- stories: Storybook stories for documentation and visual testing.
Publish footprint
This package publishes only:
- dist
- README.md
Stories and tests are excluded from the published artifact by design.
