Build or revise CAO dashboard UI with Dashboard Language, reusable UI elements, and the reactive.js framework.
Instal·la
npx skillscat add githubnext/gh-aw-cao/reactive-ui Instal·la des del registre de SkillsCat.
SKILL.md
Reactive UI
Build dashboard experiences declaratively first. Use JavaScript only for interaction or presentation that Dashboard Language and the shared presenter cannot express.
Decision order
- Read
docs/dashboard-language-specification.md, the affected dashboard document, and the existing presenter and component patterns. - Express data shaping under
dashboard.queries; keep joins, projections, aggregates, filters, ordering, and computed fields out of view-specific JavaScript. - Prefer an existing Dashboard Language mark, encoding, layout, section, route, control, or named UI element.
- Extend a shared declarative renderer when the missing behavior applies to an existing mark.
- Add a named UI element only when the experience requires reusable interaction or presentation that the declarative vocabulary cannot represent. Record a concise
intentand declare every source it consumes. - Do not infer specialized behavior from page IDs, view IDs, source names, or source contents.
Named UI elements
- Register elements in
dashboard/site/src/components/ui-elements.jsand render only from the suppliedElementRenderContext. - Put element-specific DOM creation, updates, event handling, accessibility, loading, empty, partial, unavailable, and error states inside the UI element or reusable component it delegates to.
- Keep specialized views declarative: they select sources and an element but do not directly query, create, replace, or mutate DOM nodes.
- Reuse
dashboard/site/src/dom.js, shared UI primitives, chart elements, badges, formatters, Octicons, and view chrome instead of duplicating markup or behavior. - Return one owned root element. Keep selectors and events scoped to that root, preserve semantic HTML and keyboard operation, and expose accessible names and state.
- Add the element to
EMPTY_AWARE_ELEMENTSonly when it renders honest empty or unavailable states itself. - Use lazy registration for substantial elements that are not needed on every page.
Reactive state
Use dashboard/site/src/reactive.js for state-driven updates:
stateowns mutable local state; use its functional setter when the next value depends on the current value.derivedcomputes values from reactive dependencies. Dispose it when its owner is removed.effectperforms the smallest DOM update needed from reactive state. Stop the handle when work is complete or the owner is removed.render(root, update, { signal }?)renders reactive output into an isolated shadow tree and morphs it into an owned root with the fewest mutations, preserving focus, scroll position, and control state without hand-written diffing. Prefer it over a raweffectplusreplaceChildrenwhenever a component's entire visible output is a function of its reactive state, as indashboard/site/src/components/animated-number.js,factory-station.js, andfactory-header.js.onCleanupremoves listeners, observers, timers, and other resources created by an effect.batchgroups related writes so dependents observe one consistent final state.- Pass an
AbortSignalto effects whose lifetime follows a page, view, request, or asynchronous operation. UsecreateFactoryScope()fromdashboard/site/src/components/factory-elements.jsto obtain that signal and bind it to detachment of an owned root instead of writing a bespokeMutationObserver/wasConnectedpair; every new element should stop its effects and requests once its root leaves the document. - Use stable keyed rendering from
dashboard/site/src/dom.jsfor changing collections; do not rebuild an entire view when only state or list membership changed. - Do not put source derivation, querying, or business rules in effects. Perform those operations in Dashboard Language or the data worker and react only to their results.
DOM ownership
- Build markup with
hand shared render helpers. AvoidinnerHTML, inline event attributes, document-wide selectors, and ad hocdocument.createElementcalls in specialized views. - A component that creates a node owns its updates and cleanup. Parent views compose component roots rather than reaching into their descendants.
- Keep asynchronous rendering race-safe: ignore or abort stale work and never update a detached or superseded element.
- Preserve focus, scroll position, and control state across reactive updates. Replace only the smallest owned subtree.
- Publish transient user feedback through
dashboard/site/src/notification-service.js. Do not create view-specific toast containers or duplicate their lifecycle, accessibility, or responsive behavior. - Data-worker code may call
publishWorkerNotification; keep worker notifications serializable and let the main-thread notification service own presentation and actions.
Styles and Primer
- Put dashboard component styles in the focused
dashboard/site/src/styles-*.jsmodules composed bystyles-primer.js;styles.jsremains the shared minified stylesheet entry point. Preserve composition order and keep responsive and accessibility overrides last. Do not add inline styles or element-local style injection for ordinary views. - Reuse existing classes and CSS custom properties before adding selectors or tokens.
- Follow the established Primer visual language: system font stack, semantic canvas/foreground/border/accent/status tokens, Octicons, six-pixel control radii, restrained elevation, compact spacing, and visible focus rings.
- Support light, dark, and explicit dashboard themes through existing semantic tokens. Do not hard-code a color where a token exists.
- Use color as a supporting cue, never the only status signal. Maintain readable contrast, reduced-motion behavior, responsive layouts, and touch-sized interactive targets.
- Scope new selectors to the reusable component or named element. Do not encode page or view identity into shared styling.
Validation
- Add focused unit tests for reactive transitions, cleanup, empty and unavailable states, and accessible output.
- Add or extend a Playwright test when behavior depends on real browser layout, navigation, workers, focus, scrolling, or responsive interaction.
- From
dashboard/site/, run the focused tests,npm test,npm run lint, andnpm run typecheck. Run focused end-to-end tests for browser-facing changes. - Validate every changed dashboard document with the repository validator. If a workflow Markdown source changes, run the repository workflow compile command.
Completion contract
- Dashboard Language owns all representable queries and view composition.
- Named UI elements own specialized DOM and interaction.
- Reactive resources have explicit lifetimes and cleanup.
- Styles are centralized, reusable, responsive, accessible, and Primer-aligned.
- Tests cover the declarative boundary and every new reactive behavior.