You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Reduce loader flicker / layout shift while CWA resources load, and let developers register a front-end placeholder/skeleton per component type that renders in place of the bare spinner until the resource resolves — ideally reserving the right space to avoid CLS.
Two phases:
Anti-flicker (immediate): stop the current bare spinners popping in/out and shifting layout during normal loads.
Developer-defined placeholders: opt-in per-component skeleton templates, laid out from the manifest tree so space is reserved before anything resolves.
Front-end only — no API/manifest change (api-components-bundle #198 closed as won't-do; rationale below).
Current state (flicker sources)
ResourceLoader.vue — v-if="isLoading" renders a bare <Spinner> (isLoading = resource IN_PROGRESS with no data, or undefined + buffering). Pops in/out with no reserved space.
ComponentGroup.vue — v-if="showLoader" renders a <Spinner> in a component-group-placeholder.
These appear/disappear as each resource flips state, causing spinner flicker and content jumping in.
Why this is viable with no API help
The nested manifest (resourceTree, NestedJsonStructure[], landed with #250) already gives us — up front, before fetchBatch resolves — the full tree of IRIs about to load plus their nesting + order. And each IRI yields its component type with zero extra calls:
coarse type ← IRI prefix (getResourceTypeFromIri)
specific component type ← /component/{collection}/{uuid} → reverse the {collection} segment via the resourceName → endpoint map getComponentMetadata already builds (e.g. /component/images/… → Image)
So we can pick the right placeholder per node, and reserve layout from the tree, with zero API metadata.
Proposed shape (design in this issue)
Authoring convention: a placeholder alongside a component, e.g. app/cwa/components/<Name>/placeholder.vue (mirrors the existing admin/ + ui/ subdir scanning in module.ts → cwa-options.ts), auto-registered as the loading template for CwaComponent<Name>.
Rendering:ResourceLoader / CwaComponentGroup render the resolved placeholder (keyed by IRI-derived type) until the resource fetch resolves, then swap to the real component — placeholder ideally occupying the same box to avoid CLS.
Opt-in: no placeholder registered ⇒ current behaviour (or a generic default skeleton); developers choose per component.
Tree-driven layout: consider driving pre-content layout straight from resourceTree so ancestors/siblings can reserve space before any child resolves.
Anti-flicker baseline (phase 1): even without per-type placeholders, smooth the existing spinner (debounce/min-display, hold space, avoid flip-flop between undefined/IN_PROGRESS/buffering) so quick loads don't flash a spinner and slow ones don't jump.
Alternatives considered
Minimal per-item UI info from the API (e.g. which UI variant / a skeleton hint per node in the manifest): would let placeholders be more instance-accurate, but couples the manifest cache to component/UI edits — a change to a component would invalidate the manifest, undermining the piecemeal, independently-cacheable design and therefore overall responsiveness and speed (bigger, more-often-invalidated manifest responses). Deferred in favour of front-end-only. Revisit only if the front-end-only approach proves insufficient, and weigh the cache/speed cost then.
Fast resource loads do not flash a spinner; slow loads show a stable placeholder that doesn't jump when the real content swaps in.
A developer can register a placeholder for a component type and see it render (in the correct slot, reserving space) while that component's resource loads, then swap to the real component.
No placeholder registered ⇒ graceful default (generic skeleton or current behaviour).
No API/manifest changes; layout/type derivation uses resourceTree + IRI-derived type only.
Covered by unit tests (placeholder resolution by type, swap-on-resolve, anti-flicker timing).
Summary
Reduce loader flicker / layout shift while CWA resources load, and let developers register a front-end placeholder/skeleton per component type that renders in place of the bare spinner until the resource resolves — ideally reserving the right space to avoid CLS.
Two phases:
Front-end only — no API/manifest change (api-components-bundle #198 closed as won't-do; rationale below).
Current state (flicker sources)
ResourceLoader.vue—v-if="isLoading"renders a bare<Spinner>(isLoading= resourceIN_PROGRESSwith no data, orundefined+ buffering). Pops in/out with no reserved space.ComponentGroup.vue—v-if="showLoader"renders a<Spinner>in acomponent-group-placeholder.These appear/disappear as each resource flips state, causing spinner flicker and content jumping in.
Why this is viable with no API help
The nested manifest (
resourceTree,NestedJsonStructure[], landed with #250) already gives us — up front, beforefetchBatchresolves — the full tree of IRIs about to load plus their nesting + order. And each IRI yields its component type with zero extra calls:getResourceTypeFromIri)/component/{collection}/{uuid}→ reverse the{collection}segment via theresourceName → endpointmapgetComponentMetadataalready builds (e.g./component/images/…→Image)So we can pick the right placeholder per node, and reserve layout from the tree, with zero API metadata.
Proposed shape (design in this issue)
app/cwa/components/<Name>/placeholder.vue(mirrors the existingadmin/+ui/subdir scanning inmodule.ts→cwa-options.ts), auto-registered as the loading template forCwaComponent<Name>.ResourceLoader/CwaComponentGrouprender the resolved placeholder (keyed by IRI-derived type) until the resource fetch resolves, then swap to the real component — placeholder ideally occupying the same box to avoid CLS.resourceTreeso ancestors/siblings can reserve space before any child resolves.undefined/IN_PROGRESS/buffering) so quick loads don't flash a spinner and slow ones don't jump.Alternatives considered
Acceptance criteria
resourceTree+ IRI-derived type only.Related
resourceTree— the enabler)ResourceLoader.vue,ComponentGroup.vue,module.ts(subdir scanning →cwa-options.ts),getComponentMetadata