The three goal cards, the stat row and their bars were the regions
dashboard's own markup. They are library components now, and the plugin
renders them instead of holding a copy:
GoalCard title + figure + a goal line pinned to the card's bottom,
with a slot for whatever sits between
StatCard one figure and its caption
ProgressBar a single-value track, or a segmented one with a legend
useCardSurface the card surface both cards share, injected once per
root under a stable label instead of copied into each
The bar and its legend now read from ONE list of segments, so a width and
the number printed beside it come from the same figure and cannot drift
apart; before, each was computed separately. A segment's colour is a prop
carrying the same token its .rd-seg--full/--partial/--none class used to
set, so a shared bar needs no vocabulary about prayer and those three
rules are deleted rather than left unreachable.
ProgressBar carries its own reduced-motion guard. The plugin's reset is
scoped to .rd-root and would not have followed the component anywhere
else, which would have reintroduced the one-second sliding bar their own
site suppresses.
The rendered DOM is unchanged for all four instances, measured by
server-rendering the real components and comparing against the markup
that was deleted, including that the bar widths equal the percentOfSubject()
they replace.
check:regions-uses-library grows to cover all four, asserting both halves
again: the plugin imports and renders them, and no longer ships their
markup or CSS. The .rd-root strong rule STAYS in the plugin -- UupgsList
renders a <strong> in the same shadow root and depends on it, which a
file-scoped read would have called dead.
44 lines
2.7 KiB
JavaScript
44 lines
2.7 KiB
JavaScript
/**
|
|
* useCardSurface.js — the DX-life card surface, as ONE rule set shared by every card component.
|
|
*
|
|
* WHY THIS IS A MODULE AND NOT A COPY IN EACH CARD. Vue only injects the ROOT component's <style>
|
|
* into a shadow root, so every library component that renders inside one carries its own CSS and
|
|
* injects it with useShadowStyles (see that file). StatCard and GoalCard are two components that
|
|
* need the SAME surface -- radius, elevation, padding, and the two variants -- and a copy in each is
|
|
* a rule set that will drift the first time one of them is tuned.
|
|
*
|
|
* THE STABLE LABEL IS THE WHOLE TRICK. useShadowStyles dedups on its label, so both cards (and any
|
|
* consumer that renders .rd-card markup of its own) can call useCardSurface() and exactly ONE
|
|
* <style data-shadow-styles="dxlife-card-surface"> lands per shadow root, no matter how many card
|
|
* components mount inside it. Calling it from N components is not N stylesheets.
|
|
*
|
|
* ★ THE CLASS NAMES KEEP THEIR `rd-` PREFIX, DELIBERATELY. It came from the regions-dashboard port
|
|
* and means nothing outside it. Renaming would change no pixel and would cost the traceability from
|
|
* each declaration back to the DX-life rule and the measurement that fixed it, which is the only
|
|
* reason these values can be defended. The names are internal to the components; a consumer writes
|
|
* <StatCard> and never types them. A rename is a safe later cleanup, not a prerequisite for reuse.
|
|
*
|
|
* CONTRACT: the shadow root must already carry the DX-life token layer (--dx-radius, --dx-elevated,
|
|
* --dx-text, --dx-dark, --dx-on-dark, --spacing-*). The regions plugin injects it as DXLIFE_TOKENS.
|
|
* Without it these rules resolve to nothing -- they are a skin over that scale, not a replacement.
|
|
*/
|
|
import { useShadowStyles } from './useShadowStyles.js'
|
|
|
|
export const CARD_SURFACE_CSS =
|
|
// ── .card: blocks/_cards.scss:9-21. radius --border-radius-xlg (35px);
|
|
// padding clamp(--spacing-lg, 6vw, --spacing-2xl) clamp(--spacing-lg, 6vw, --spacing-3xl).
|
|
'.rd-card{ position:relative; border-radius:var(--dx-radius);' +
|
|
' background:var(--dx-elevated); color:var(--dx-text);' +
|
|
' padding:clamp(var(--spacing-lg), 6cqw, var(--spacing-2xl)) clamp(var(--spacing-lg), 6cqw, var(--spacing-3xl)); }' +
|
|
// .card[padding-small] -> --spacing-lg flat
|
|
'.rd-card--small{ padding:var(--spacing-lg); }' +
|
|
// .card[data-variant="secondary"] -> surface-brand-dark on text-on-brand-light
|
|
'.rd-card--secondary{ background:var(--dx-dark); color:var(--dx-on-dark); }'
|
|
|
|
/**
|
|
* Injects the card surface into the containing shadow root, once, under a stable label.
|
|
* Call it at setup() time from any component that renders .rd-card markup.
|
|
*/
|
|
export function useCardSurface() {
|
|
useShadowStyles(CARD_SURFACE_CSS, 'dxlife-card-surface')
|
|
}
|