Skip to content

feat(buttons): add Button and LinkButton with CVA variants [INTORG-705]#266

Draft
Infi-Knight wants to merge 3 commits intostagingfrom
ravi/intorg-705
Draft

feat(buttons): add Button and LinkButton with CVA variants [INTORG-705]#266
Infi-Knight wants to merge 3 commits intostagingfrom
ravi/intorg-705

Conversation

@Infi-Knight
Copy link
Copy Markdown
Contributor

@Infi-Knight Infi-Knight commented May 4, 2026

Summary

Ships Button.astro and LinkButton.astro plus a long-lived SEO-excluded preview route at /preview/ui-components. Establishes the pillar-aware primary-button colour system through three runtime CSS vars (--color-button-primary, --color-button-primary-hover, --color-button-primary-disabled) with orchid as the :root default.

Related Issue

Fixes INTORG-705. Builds on INTORG-643 (PR #263) which landed the design-system token foundation.

Manual Test

  1. pnpm dev and open http://localhost:1103/preview/ui-components
  2. Visual matrix: every section renders (Primary lg/sm, Secondary lg/sm in light + dark, LinkButton, Form, RTL). All buttons should have rounded corners (8px), Poppins font, orchid background on primary.
  3. Hover: primary darkens to #662b81. Secondary-light goes black-on-white. Secondary-dark goes white-on-black.
  4. Tab through the page: every focused button shows a 2px solid orchid focus ring with 2px offset.
  5. LinkButton aria-disabled: not focusable via Tab, clicking does nothing (href is stripped).
  6. Pillar override (Pattern A) via DevTools console:
    document.documentElement.setAttribute('data-pillar', 'tech')
  7. Inline-style override (Pattern B) via DevTools:
    document.querySelector('button').setAttribute(
      'style',
      '--color-button-primary: red; --color-button-primary-hover: darkred;'
    )
    First button turns red.
  8. Class override (Pattern C): adding class="w-full" to a <Button> produces a full-width button (tailwind-merge lets caller classes win).

Checks

  • pnpm run format
  • pnpm run lint

PR Checklist

  • PR title follows Conventional Commits
  • Linked issue included
  • Scope is focused
  • Screenshots for UI changes

Why CVA

Multi-axis variant matrix (variant x mode x size x iconOnly) handled with compoundVariants. Every Tailwind class is a literal string at definition time so the v4 content scanner sees them. Caller class flows through tailwind-merge so per-property dedup picks the right winner.

Pillar-aware primary, how it works

Primary button colour resolves through three runtime CSS vars defined on :root in src/styles/base/variables.css and exposed as Tailwind utilities (bg-button-primary, etc.) via an @theme inline block in src/styles/theme.css. Orchid is the :root default, effectively "the orchid pillar." Per-pillar mappings for tech, mission, vision, and values are out of scope for this PR (blocked on design); each future pillar is a 3-line addition under a [data-pillar='X'] selector.

Caller overrides, three patterns

Pattern A (preferred when re-tinting to a known pillar):

<div data-pillar="tech">
  <Button>Tech-tinted action</Button>
</div>

Pattern B (preferred when forcing a specific palette colour):

<Button
  style="
    --color-button-primary: var(--color-orchid-100);
    --color-button-primary-hover: var(--color-orchid-150);
    --color-button-primary-disabled: var(--color-orchid-50);
  "
>
  Force orchid regardless of pillar
</Button>

Pattern C (preferred for one-off non-colour tweaks):

<Button class="w-full">Full-width primary</Button>

Ships two new components (Button.astro, LinkButton.astro) and a long-lived
SEO-excluded preview route at /preview/ui-components. Establishes the
pillar-aware primary button colour system via three runtime CSS vars
(--color-button-primary{,-hover,-disabled}) with orchid as the :root default.

No consumers migrated in this PR. Migration is a follow-up series.
@netlify
Copy link
Copy Markdown

netlify Bot commented May 4, 2026

Deploy Preview for interledger-org-v5 ready!

Name Link
🔨 Latest commit 0cfbea0
🔍 Latest deploy log https://app.netlify.com/projects/interledger-org-v5/deploys/69f9bdf597287900092331a4
😎 Deploy Preview https://deploy-preview-266--interledger-org-v5.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@Infi-Knight Infi-Knight self-assigned this May 4, 2026
@Infi-Knight Infi-Knight marked this pull request as draft May 4, 2026 05:56
@Infi-Knight Infi-Knight requested a review from Anca2022 May 4, 2026 11:43
Comment thread src/styles/base/variables.css Outdated
],
{
variants: {
variant: {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: for the secondary variant, the disabled button changes on hover

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch, fixed in 0cfbea0. Root cause: :hover still fires on <button disabled> natively (the spec only blocks click events, not pointer states). The compoundVariants for secondary now explicitly reset bg/border/text under both disabled: and aria-disabled:, so the disabled state is fully inert visually.

Comment thread src/pages/preview/ui-components.astro Outdated
Comment on lines +33 to +46
<svg
slot="icon-left"
class="size-4"
viewBox="0 0 16 16"
fill="none"
aria-hidden="true"
>
<path
d="M10 4 6 8l4 4"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"></path>
</svg>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The icons in the preview don't match the current Figma (has the design shifted since this was put together?)
Also, would it make sense to add an <Icon> component, instead of inlining the <svg> every time?

Image

Copy link
Copy Markdown
Contributor Author

@Infi-Knight Infi-Knight May 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Refactored in 0cfbea0: added src/components/shared/Icon.astro keyed by name (chevron-left, chevron-right for now), default size-4 overridable via class, currentColor so it inherits from the parent button text colour. The preview page now uses <Icon name="chevron-right" /> instead of inlined SVGs.

On the Figma mismatch: the chevrons in the preview page are placeholders: icons are slot content the caller passes in. Adding the exact Figma icon set is a separate piece of work (icon extract / sprite pipeline / naming convention), so I would prefer to keep this PR focused. Happy to open a follow-up issue once we agree on the approach. Does that work?

Comment thread src/components/shared/LinkButton.astro Outdated
class={twMerge(
buttonVariants({ variant, mode, size, iconOnly, class: className })
)}
data-umami-event={umami}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For analytics, there's already a PR that auto-instruments data-umami-event on links (See PR #256) - flagging in case it slipped past you, since we're turning away from manually adding umami props.
I'm not saying you should handle it in this PR, but if you don't, could you open a follow-up issue so it doesn't get lost? 😁

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed the umami prop in 0cfbea0. The wrapper was incomplete anyway (only set data-umami-event, not the full data-event-* set that buildUmamiAttrs produces). Consumers can pass data-umami-event (and any data-event-*) directly via the rest spread on <LinkButton>, or use buildUmamiAttrs and spread the result. PR #256 covers MDX inline links via the rehype plugin; component-level instrumentation stays at the call site where it has the right context.

Copy link
Copy Markdown
Contributor

@Anca2022 Anca2022 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Waiting on the final Figma, but in the meantime I left a few comments and flagged one bug.
Other than that, this truly looks great, Ravi! 😁

- Reset bg/border on disabled secondary buttons; hover was still firing on
  disabled <button>, leaving the hover bg/border visible.
- Remove docs/plans/intorg-705.md reference
- Remove redundant umami prop from LinkButton; callers can pass
  data-umami-event via rest props if needed (auto-instrumentation in #256
  covers MDX inline links).
- Add Icon.astro component, replace inlined SVGs in the preview page.
@Infi-Knight Infi-Knight requested a review from Anca2022 May 5, 2026 09:57
@JoblersTune
Copy link
Copy Markdown
Collaborator

The 404 page seems to have broken
https://deploy-preview-266--interledger-org-v5.netlify.app/hello
image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants