Skip to content

Refactor button (and card) styles to a unified system#18946

Open
jeffmerrick wants to merge 17 commits into
masterfrom
jeffmerrick/issue-18865-refactor-button-styles
Open

Refactor button (and card) styles to a unified system#18946
jeffmerrick wants to merge 17 commits into
masterfrom
jeffmerrick/issue-18865-refactor-button-styles

Conversation

@jeffmerrick
Copy link
Copy Markdown
Contributor

Refactor of button classes to use a set of consistent styles from _button.scss. Styles are updated to match visually with the new console styles, though they may differ in sizing.

Bonus: cards styles also standardized on console card styles.


Generated description of changes:

Replaces the grab-bag of legacy button classes (.btn, .btn-primary, .btn-secondary, .btn-primary-template-variant, .header-btn-primary, .home-hero-btn-*, .consent-btn-*, .docs-primary-link-btn, hand-rolled Tailwind soups, etc.) with a single system in theme/src/scss/_button.scss.

Variants

btn-primary, btn-outline, btn-secondary, btn-ghost, btn-ghost-primary, btn-destructive, btn-link

Sizes

default (h-9), btn-xs, btn-sm, btn-lg, btn-xl, plus btn-icon / btn-icon-{xs,sm,lg}

All variants reference existing palette tokens — no new semantic CSS variables were introduced.

What changed

  • New: theme/src/scss/_button.scss is the single source of truth.
  • Deleted/migrated legacy CSS: _buttons.scss reduced to three Stencil-only classes, plus removed sections of _marketing.scss, _header.scss, _hero.scss, _consent-banner.scss, _templates.scss (.cta/.cta--button), marketing/_pulumi-up.scss, marketing/_self-hosted.scss, marketing/_signup.scss, marketing/_workshop-signup.scss, docs/_docs-home.scss, docs/_continuous-delivery.scss, docs/_docs-main.scss. The @utility btn/btn-lg/btn-orange declarations in main.scss are gone; consumers in _hubspot.scss, _audio.scss, _convert.scss now @extend .btn instead.
  • Markup: ~80 partials, page layouts, and shortcodes migrated to class="btn btn-{variant} btn-{size}". Template hero/card/section-header/feature-split/case-study CTAs all use the new classes; arrow literals in template CTAs replaced with bold phosphor arrow-right icons; btn-link CTAs wrapped in flex divs for proper alignment in flex-col parents.
  • Header/nav: desktop and mobile CTAs, hamburger trigger, sheet close button, and GitHub star button (header variant) all routed through the new system.
  • Cards: bonus consolidation pass — standardized .tile, .template-card, .template-sidebar-nav, .case-study-card, .social-carousel-card, and the docs-home card grids on a shared .card (rounded-lg border-gray-200) plus opt-in .card-hover modifier.

Notes for review

  • Visual continuity: marketing-page CTAs use btn-xl to match the previous text-lg py-3 px-8 weight; docs/header buttons use the default size.
  • Blog/tutorial markdown not touched (per AGENTS.md). .btn-primary and .btn-secondary are canonical names in the new system, so historical markdown using class="btn btn-primary" keeps rendering — with the new styling.
  • Footer GitHub star button (variant: "footer") is intentionally kept bespoke — no dark-theme button variant exists yet.
  • Consent banner: .consent-btn-* class names are hardcoded by @segment/consent-manager JS, so they @extend the new btn classes rather than being rewritten in markup.

Stats

128 files changed, +525 / -647

Test plan

  • Homepage hero, pricing page, enterprise page, product/* pages — primary/secondary CTAs render at btn-xl weight
  • Header nav (desktop + mobile) — CTAs, hamburger, GitHub star button visually match
  • Mobile sheet open/close, signup/workshop-signup pages
  • Template hero / section-header / feature-split / card-grid / case-study render correctly with arrow icons
  • Docs home page primary/secondary buttons; CI/CD platforms widget; blog post in-content CTAs
  • Consent banner accept/decline buttons
  • Hubspot newsletter form submit button

jeffmerrick and others added 15 commits May 13, 2026 10:07
Add theme/src/scss/_button.scss as the single source of truth for button
styles. Variants: primary, outline, secondary, ghost, ghost-primary,
destructive, link. Sizes: default (baked into .btn), xs, sm, lg, icon,
icon-xs, icon-sm, icon-lg. All colors reference existing palette tokens
in _theme.scss — no new semantic tokens added.

Remove the @Utility btn / btn-lg / btn-orange declarations from main.scss.
They lived in Tailwind's utilities layer and would have beaten the new
.btn class in @layer components. Migrate the four SCSS files that were
@apply'ing those utilities (_hubspot, _audio, _convert, _continuous-delivery)
to @extend the shared .btn class instead, so they continue to share one
definition.

Add legacy aliases for .btn-primary-template-variant and
.btn-secondary-template-variant — to be removed in a later commit once
the template partials are migrated.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Replace .btn-primary-template-variant and .btn-secondary-template-variant
in all template partials (template-hero, template-card, template-case-study-hero,
template-three-column, template-location) with btn btn-primary btn-xl /
btn btn-outline btn-xl. Section-header variants and template-feature-split
toggle between btn btn-link and btn btn-primary btn-xl based on cta_as_button.
template-card-grid and template-case-study-cards .cta links migrate to btn btn-link.

Update cta-get-started.html default class to btn btn-primary btn-xl and update
its callers (gads-template, gads-header, product/neo, product/superintelligence)
to drop the now-redundant explicit class arg. Update footer/cta.html ranges
to emit btn-primary/btn-outline btn-xl based on .style.

Add .btn-xl size (h-12 px-8 text-base) and switch .btn base to font-normal.

Delete the conflicting body-scoped button block from _marketing.scss
(global a[class^="btn-"] selector + .btn-primary/.btn-secondary overrides
that fought the new system on specificity). Delete the [class*="template-"] .cta
and .cta--button rules from _templates.scss now that all template callers
have moved to the new classes.

Strip _buttons.scss down to the three classes still consumed by Stencil web
components and one Hugo partial (.btn-download, .btn-scroll-top,
.card-cta-contained-btn). The conflicting .btn:hover/:active/.btn-secondary/
.btn-sm/.btn-lg/.btn-orange:hover rules with !important were leaking into
the @extend .btn consumers (audio, hubspot, etc.) — removing them lets the
new system render cleanly.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Shortcodes:
  - blog/cta-button.html: btn-secondary -> btn-outline
  - get-started-next-step.html: btn-secondary -> btn-outline

Supporting partials:
  - help-links.html, hand-raise-section.html: btn-primary -> btn btn-primary btn-xl
    (4 cards each with prominent CTA)
  - learnmore-contactus.html: btn btn-secondary -> btn btn-outline
  - docs/feedback.html, tutorials/feedback.html: bare .btn submit -> btn btn-primary
  - header/nav-mobile-sheet.html: replace hand-rolled tailwind utility blobs on
    contact/signIn/dashboard/signup buttons with btn btn-outline / btn btn-primary
  - how-pulumi-works.html, pulumi-up/speakers.html: btn-primary/btn-secondary
    bare classes -> btn btn-primary btn-xl / btn btn-outline btn-xl
  - docs/special-pages/docs-home.html: btn btn-secondary -> btn btn-outline

Tighten btn-outline border to gray-300 for better visual weight.

github-star-button.html and the consent-banner classes are intentionally
left in their bespoke form - the github star is a custom split-button
component, and consent-btn-* are JS-driven and namespaced separately
from the .btn system.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Replace hand-rolled utility blobs on the desktop and mobile CTAs (contact,
signIn, dashboard, signup) in nav.html with btn btn-outline / btn btn-primary.
Replace the hamburger trigger and mobile sheet close button with btn btn-outline
btn-icon and btn btn-ghost btn-icon-sm. Bump the list and x icons to bold weight
for better visual weight at small sizes.

Refactor github-star-button.html so the header variant routes through the
new system: simple branch uses btn btn-outline px-2.5 gap-1.5; split branch
uses btn btn-outline p-0 items-stretch overflow-hidden so the inner spans
control segment widths and the divider can stretch full height. The footer
variant stays bespoke — buttons on the dark footer background don't yet
have a matching variant in the system.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…llsites

Across 65 page and partial layouts, transform every bare class="btn-primary"
or class="btn-secondary" into the explicit composed form:

  btn-primary  -> btn btn-primary btn-xl
  btn-secondary -> btn btn-outline  btn-xl

These callsites previously relied on _marketing.scss's global
body a[class*=" btn-"] selector (deleted in an earlier commit) to supply
their h-9 / px-8 / rounded-lg spacing. Adding the explicit "btn" base
class restores those properties through the new system. btn-xl preserves
the original visual weight on marketing pages where these CTAs were big
text-lg py-3 px-8 buttons.

Callsites that already had the "btn" base (class="btn btn-secondary",
class="btn btn-primary") only get the btn-secondary -> btn-outline rename;
their existing size choice is preserved.

Files touched: pricing, solutions, product/*, partner/*, partners,
migrate/*, page/* event/marketing pages, topics, proserv, security,
docs/main-home, community, challenge, awsx, gads, shortcodes/stepper.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- btn-outline: add hover:border-gray-500 so the border darkens on hover
  along with the background fill, matching the visual feedback of
  btn-primary's color shift.
- btn-secondary: drop bg-gray-100 to bg-gray-100/80 so it reads as a
  softer surface against gray-100 page backgrounds.
- btn-xl: tighten horizontal padding from px-8 to px-7 to better balance
  the h-12 height.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…8865-refactor-button-styles

# Conflicts:
#	layouts/partials/template-partials/template-card-grid.html
#	layouts/partials/template-partials/template-hero.html
#	layouts/partials/template-partials/template-section-header.html
#	layouts/partner/aws.html
#	layouts/partner/azure.html
#	layouts/partner/gcp.html
#	layouts/product/superintelligence-infrastructure.html
#	layouts/topics/kubernetes.html
- .btn-link: switch to font-semibold and underline-offset-5, force h-auto
  and p-0 so the link sits flush like a text link, and explicitly strip the
  border inherited from .btn.
- template-card-grid: large-card CTAs render as btn btn-link btn-lg to give
  them a touch more height than the default small-card link CTAs.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
CSS cleanup:
- Delete .header-btn-primary (_header.scss), .home-hero-btn-primary/secondary
  (_hero.scss), .docs-primary-link-btn/.docs-secondary-link-btn (_docs-home.scss).
  All their callsites now use the new btn / btn-primary / btn-outline classes.
- Migrate _consent-banner.scss .consent-btn/.consent-btn-accept/.consent-btn-secondary
  to @extend the new .btn / .btn-primary / .btn-outline (segment-consent-manager
  applies these class names at runtime, so the JS contract is preserved).
- Delete the .btn-primary:hover override in marketing/_pulumi-up.scss.
- Update _self-hosted.scss to anchor on a:not(.btn) (was a:not(.btn-secondary, .btn-primary)).
- Update docs/_docs-main.scss to anchor on a:not(.btn).
- Rewrite marketing/_signup.scss and marketing/_workshop-signup.scss to hide the
  header signup CTA on the dedicated signup pages via [data-role=cta-get-started]
  (the legacy .header-btn-primary::after pseudo-element hack is gone).
- Revert docs/_continuous-delivery.scss to scoped manual styles on
  .supported-cicd-platforms > a (no @extend / @apply btn).
- Drop the .btn-primary-template-variant / .btn-secondary-template-variant
  back-compat aliases from _button.scss — all callsites are migrated.
- .btn-link: tighten styles, scope hover underline off SVG via [&_svg]:no-underline,
  add violet-300 decoration color.

Template CTAs:
- Replace every <span>→</span> in template partials with a bold phosphor
  arrow-right icon (sized size-4).
- Bump template btn-link CTAs from default size to btn-xl.
- Wrap each btn-link in a flex div so inline-flex buttons no longer stretch
  full-width inside flex-col parents.
- Migrate the hand-rolled link in template-social-carousel.html (was
  text-violet-700 font-semibold no-underline hover:underline) to btn btn-link btn-xl.
- Migrate the remaining legacy .cta / .cta--button anchors in template-video-embed,
  template-case-study-grid, template-feature-callout to the new btn system.

Markup migrations:
- home-b.html: drop .home-hero-btn-primary/.home-hero-btn-secondary; use btn classes.
- docs/main-home.html, partials/docs/special-pages/docs-home.html: use
  btn btn-primary / btn btn-outline instead of .docs-primary-link-btn /
  .docs-secondary-link-btn.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Six callsites were styling links as ad-hoc text-violet-primary font-semibold
inline-flex with a literal &rarr; — switch them to the new btn btn-link btn-xl
pattern with a bold phosphor arrow-right icon and a flex wrapper for left
alignment in flex-col parents.

- case-studies/list.html: "Read the story" (both redirect_to and RelPermalink branches)
- whitepapers/list.html: "Read the whitepaper"
- page/about.html: "View all achievements", "View all press releases", "View all recent news"
- partials/learnmore-ai.html: "Explore the docs", "Try Pulumi AI"
  (note: this partial is currently unreferenced — left in tree for now)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Consolidate bespoke border/radius styling on cards into a single .card
(rounded-lg, border-gray-200) plus an opt-in .card-hover modifier
(gray-300 border on hover). Aligns .tile, .template-card,
.template-sidebar-nav, .case-study-card, .social-carousel-card, and
docs-home .cards-logo-label-link / .full-width-card to the same
baseline, and migrates ~15 inline rounded-* / border-gray-* callsites
to the new class.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The flex wrappers I added around btn-link CTAs default to justify-start,
which left-aligned them inside text-center parents. Add justify-center to
the two affected callsites:

- about.html: "View all achievements" sits in a text-center awards section.
- template-social-carousel.html: "Join us on Slack" sits inside
  .template-social-carousel-header (which is text-center).

Other btn-link wrappers I added live in left-aligned card or column
contexts, where justify-start is correct — no changes needed there.

Also pick up the user's _docs-home.scss cleanup (drops the old
.docs-primary-link-btn / .docs-secondary-link-btn rules from the link-buttons
container along with the legacy h2 dup).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Pricing tier CTAs (5 hand-rolled buttons in pricing.html, all using a
text-white bg-violet-primary border-2 ... hover:bg-violet-500 pattern):
swap to btn btn-primary / btn btn-outline. Replace the inline mr-1 mb-0
lg:mb-1 xl:mb-0 spacing on the side-by-side row with gap-2 on the parent.

Blog sidebar mobile toggles (3 instances of px-3 py-2 border rounded
text-violet-primary border-violet-primary): swap to btn btn-outline btn-sm.
Keeps the .blog-sidebar-toggle hook for the JS that wires up the panel.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Migrate remaining `btn-{{ $button.type }}` callsites to `btn btn-{variant} btn-xl`
  (mapping `secondary` → `outline`) so buttons inherit the new base styles
- Convert skip-to-content link to `btn btn-outline`
- Refine `.btn` base: text-ellipsis + overflow-hidden so long labels truncate
- Drop redundant `<span>` wrappers around button text
- Remove `flex` from single-CTA wrappers so inline-flex buttons can shrink
  inside their block parents; switch `justify-*` to `text-*` for alignment
@claude
Copy link
Copy Markdown
Contributor

claude Bot commented May 13, 2026

Docs review — PR #18946

Scope: 100 files changed, all template/SCSS — no docs content (content/docs/, content/blog/, content/tutorials/) touched, so style-guide / frontmatter / link-update rules don't apply. Review focused on template hygiene and small defects in or adjacent to the changed lines.

Issues

1. theme/src/scss/_button.scss — missing trailing newline

The new file ends at line 34 without a trailing newline (the diff shows the standard "No newline at end of file" marker). Other SCSS files in theme/src/scss/ end with a newline. Please add a final newline.

2. layouts/partials/learnmore-ai.html:10, :25 — heading tag mismatches

These two <h4>...</h3> mismatches are pre-existing, but the PR touches the surrounding block so it is a good moment to fix:

                <h4>Learn more</h4>
                <h4>Pulumi AI</h4>

3. layouts/page/pulumi-up-2023.html:93 — orphaned closing tag

The PR's last edit to this block (line 92) leaves the pre-existing stray </a> on line 93 in place:

                <a class="m-4 sm:m-2 btn btn-primary btn-xl" href="/events/from-infrastructure-engineering-to-platform-engineering/">Watch the Panel</a>
            </div>

Notes (not blocking)

  • Dead markup in HTML comments: layouts/page/aws-summit-2022.html:62, layouts/page/devops-days-2022.html:62, and layouts/page/reinvent.html:163-166 and :172-175 still contain commented-out button elements that were mechanically updated to the new classes. Worth deleting outright since the surrounding comments mark them as removed/disabled — but harmless to leave.
  • Lost :active state: The old _buttons.scss had explicit .btn:active { background-color: var(--color-violet-900) !important; }. The new _button.scss only defines hover: states. Minor visual regression on click — intentional? If so, consider documenting in the file header.
  • Signup-page CTA behavior change: marketing/_signup.scss and marketing/_workshop-signup.scss previously replaced the header CTA text with "Try Pulumi" via CSS content:; now they hide [data-role="cta-get-started"] entirely. Quietly different UX (CTA disappears instead of relabels). Worth confirming this matches the design intent — the PR description does not call it out.
  • btn-disabled modifier referenced but not defined: aws-summit.html:65 and microsoft-build.html:30 use class="btn btn-outline btn-xl btn-disabled". The new _button.scss does not define .btn-disabled, and the legacy .disabled rule in _marketing.scss was removed. Pre-existing usage that the PR does not fix or break, but worth knowing — the class is now a no-op.

Otherwise, the migration looks clean and consistent. The cond (eq $button.type "secondary") "outline" $button.type shim in product pages preserves data-file compatibility nicely.


Mention @claude if you would like additional review or fixes.

@pulumi-bot
Copy link
Copy Markdown
Collaborator

pulumi-bot commented May 13, 2026

@pulumi-bot
Copy link
Copy Markdown
Collaborator

pulumi-bot commented May 13, 2026

Lighthouse Performance Report

Commit: 1f2509f | Metric definitions

Page Device Score FCP LCP TBT CLS SI
Homepage Mobile 🟡 60 2.8s 2.9s 1173ms 0.051 7.4s
Homepage Desktop 🟡 84 0.8s 1.1s 288ms 0.037 1.6s
Install Pulumi Mobile 🔴 35 5.3s 7.8s 229ms 0.436 8.0s
Install Pulumi Desktop 🟡 83 1.2s 1.7s 22ms 0.018 3.0s
AWS Get Started Mobile 🟡 57 5.1s 7.6s 220ms 0.085 5.1s
AWS Get Started Desktop 🟡 83 1.2s 1.7s 22ms 0.033 3.0s

- Add focus-visible border, active translate-y (skipped on dropdown
  triggers via aria-haspopup), aria-invalid/aria-disabled styling, and
  default SVG sizing/pointer rules to .btn base
- Add aria-expanded styling to .btn-outline/secondary/ghost/ghost-primary
- Migrate nav-desktop trigger and link to btn btn-ghost; add
  aria-haspopup="menu" on the dropdown trigger
- Soften .btn-ghost/.btn-ghost-primary hover/aria-expanded to /70 opacity
- Fix heading-tag mismatches in learnmore-ai.html
- Remove orphaned </a> in pulumi-up-2023.html
- Swap legacy btn-disabled to aria-disabled="true" on aws-summit and
  microsoft-build pages
- Add trailing newline to _button.scss

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
</div>
<div>
<h4>Learn more</h3>
<h4>Learn more</h4>
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.

Nice catch 😄

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.

Hah I can't take credit for that one, all Claude

Replace duplicated rounded-lg + border-gray-200 (+ hover:border-gray-300
+ transition) declarations with @extend .card / @extend .card-hover in:

- .template-card
- .template-sidebar-nav
- .social-carousel-card
- .case-study-card (extends both, picks up the transition)
- .tile (extends both)
- .neo-card (extends .card only; keeps bespoke shadow-sm hover)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
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