#15 Add components-react and components-web packages with HTML/React toggle - #40
Draft
trackleft wants to merge 22 commits into
Draft
#15 Add components-react and components-web packages with HTML/React toggle#40trackleft wants to merge 22 commits into
trackleft wants to merge 22 commits into
Conversation
…toggle Adds @az-digital/components-react (React) and @az-digital/components-web (framework-agnostic HTML/CSS) as parallel implementations of the design system's components, starting with Button. Both packages share the same prop shape and are consumed live from source in Storybook via a Vite alias, no build step required during development. The Button story wires up a toolbar "Implementation" switcher (HTML | React) plus a synced docs code panel, so a single set of controls can demo both implementations side by side. Button styling loads Arizona Bootstrap's real CSS from the CDN rather than a local stub, so classes like .btn-red and .btn-outline-blue match production exactly.
…ision @az-digital/components-web read as "Web Components," but this package is plain framework-agnostic HTML/CSS markup, not the Lit-based custom elements from az-web-components (a separate, actual Web Components project under consideration for issue #16). Renamed to components-html to keep the two concepts unambiguous.
Not every component needs both a React and HTML implementation. Extract the render/source branching into a shared implementations.tsx helper: stories now declare a per-story `implementations` map keyed by whichever kinds they actually have, and the toolbar switcher falls back to a "not implemented yet" placeholder (canvas and docs code panel both) for any kind a story doesn't provide, instead of assuming every component ships both.
Chosen over reactstrap after checking maintenance signal: react-bootstrap
has commits within the last few months vs. reactstrap's ~2-year-stale
repo, ~3x the weekly npm downloads, and 2x the GitHub stars. It also
doesn't ship its own CSS (we already load Arizona Bootstrap's), and
builds class names as `${prefix}-${variant}` — the same pattern our
hand-rolled components already use — so custom Arizona Bootstrap
variants like `red`/`blue`/`sky` work via its `variant` prop without any
type augmentation, verified live with a throwaway Accordion probe
(collapse/expand, focus ring, ARIA all worked correctly, then removed).
Simple presentational components (Button) stay hand-rolled since there's
little to gain from wrapping them; react-bootstrap is for components
with real interactive behavior (Modal, Dropdown, Accordion, Offcanvas,
Tooltip/Popover, Nav/Tabs) where hand-rolling focus-trap/keyboard/ARIA
behavior correctly is expensive and easy to get subtly wrong.
Documents the React/HTML dual-implementation pattern, the Storybook toggle and its docs-panel-sync gotcha, partial implementation coverage with placeholders, and when to hand-roll vs. wrap react-bootstrap. Symlinked into .github/instructions/ (with an added `applyTo` glob in the frontmatter) so VS Code's GitHub Copilot picks up the same guidance when editing matching paths, mirroring the CLAUDE.md -> AGENTS.md symlink from issue #14 — one canonical file instead of two to keep in sync.
|
Review site: https://review.digital.arizona.edu/design/pr-40/ |
Design tokens had no component layer yet — Button's color prop reached
straight down to the brand primitives (az.color.brand.red/blue) with no
named indirection point specific to the component. Add
az.component.button.color.{red,blue}, aliased to those primitives, so
Button's actual token dependency is explicit and can be repointed later
(e.g. once a semantic layer exists) without touching the component.
Add a Tokens story rendering swatchbook's <TokenTable filter="az.component.button.**" />
so the component's real, resolved token values are visible and
interactive right next to its other stories, instead of only living in
the global token catalog.
Component-tier tokens (az.component.*) were only visible mixed into the unfiltered "All tokens" table, with nothing calling out that they're a distinct tier from the base/brand tokens. Give them their own filtered section, same pattern as the existing Colors section, and link out to Button's own filtered Tokens story as an example of the per-component view.
Extends az.component.button.* beyond color to cover the rest of
Button's real CSS surface: padding, font size/weight, border
width/radius, disabled opacity, and sm/lg size overrides — 15 tokens
total, values pulled directly from Arizona Bootstrap's compiled
.btn/.btn-sm/.btn-lg CSS (verified against the CDN build), not
estimated. Colors keep aliasing to az.color.brand.*; everything else is
hard-coded since there's no base/semantic tier for spacing or
typography yet to alias to.
Also downgrades core/valid-dimension to warn and switches dimension
values to the DTCG { value, unit } object format. This isn't cosmetic:
the plain legacy-string format (e.g. "1.25rem") passes lint but
@terrazzo/plugin-css's dimension serializer can't read it, silently
compiling every dimension token to `undefinedundefined` in tokens.css.
Verified directly by building both ways. Colors don't have this
problem — legacy hex strings compile correctly — so this is
dimension-specific.
Covers what the Button work just established: every component needs
its own az.component.<name>.* tokens covering its real structural
properties, not just color; colors alias to existing base tokens while
everything else without a backing base/semantic tier gets hard-coded to
the real production value (sourced from Arizona Bootstrap's actual CSS,
not guessed); and dimension tokens specifically need the DTCG
{ value, unit } object format since the legacy string format compiles
to broken CSS even though lint only warns on it.
Adds `success` to components-html's ButtonColor (Bootstrap's stock semantic color, not an Arizona brand color — components-react doesn't support it). The Success story demonstrates the partial-implementation placeholder at the variant level rather than the whole-component level. Along the way, found and fixed a real bug in the mechanism itself: overriding parameters.implementations at the story level doesn't work because Storybook deep-merges parameters objects, so a story's narrower map gets merged into the meta-level one instead of replacing it — the key being omitted quietly survives. Fixed by reading a separate parameters.implementationsOverride key instead, which nothing at the meta level defines, so there's nothing to merge against. Verified live: the canvas and docs code panel both now correctly show the "no react implementation" placeholder instead of silently falling back to a default-colored React render.
Storybook deep-merges parameters objects, so a story can't override parameters.implementations by setting a narrower map at the story level — it merges into the meta-level default instead of replacing it, silently keeping keys the story meant to omit. Document the working fix (a separate implementationsOverride parameter, established while adding Button's HTML-only Success variant) as its own subsection under Partial implementation coverage, with the broken version shown first since it looks correct until the toggle is actually tested.
Three comments still said things that were true a few commits ago but aren't now: the Tokens story and tokens.mdx both said component tokens are "aliased to base tokens" (only 2 of 15 are — the rest are deliberately hard-coded), and components-react's Button.tsx still claimed 1:1 prop parity with components-html despite components-html's `success` color having no React counterpart. Caught by re-reading the docs against the actual code rather than assuming they were still accurate.
Combines the Storybook MCP addon (main) with real React/HTML Button components (this branch). Conflict resolutions: - tokens.json: kept both branches' new brand colors plus the az.component.button.* tree; the colliding `white` token keeps main's definition (#FFFFFF, no $extensions) per user decision. - tokens/dist/* and .swatchbook/tokens.d.ts: regenerated rather than hand-merged (npm run build:tokens). - package-lock.json: regenerated via npm install. - packages/storybook/.storybook/main.ts: kept both addons (addon-designs, addon-mcp) and this branch's source-alias viteFinal. - debug-storybook.log: deleted (also removed from tracking on this branch; see .gitignore change).
| const label = escapeHtml(text); | ||
|
|
||
| if (htmlTag === 'button') { | ||
| return `<button type="button" class="${classes}"${disabled ? ' disabled' : ''}>${label}</button>`; |
| return `<button type="button" class="${classes}"${disabled ? ' disabled' : ''}>${label}</button>`; | ||
| } | ||
|
|
||
| return `<a href="${escapeHtml(href)}" role="button" class="${classes}"${disabled ? ' aria-disabled="true" tabindex="-1"' : ''}>${label}</a>`; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds @az-digital/components-react (React) and @az-digital/components-html
(framework-agnostic HTML/CSS) as parallel implementations of the design
system's components, starting with Button. Both packages share the same
prop shape and are consumed live from source in Storybook via a Vite
alias, no build step required during development.
The Button story wires up a toolbar "Implementation" switcher (HTML |
React) plus a synced docs code panel, so a single set of controls can
demo both implementations side by side. Button styling loads Arizona
Bootstrap's real CSS from the CDN rather than a local stub, so classes
like .btn-red and .btn-outline-blue match production exactly.
How to test
Go to : https://review.digital.arizona.edu/design/pr-40/?path=/story/components-button--outline&globals=implementation:react