Skip to content

ROU-12881: Rework button and button group states to match Figma - #1174

Merged
OS-giulianasilva merged 4 commits into
ROU-12714from
ROU-12881-buttons
Jul 17, 2026
Merged

ROU-12881: Rework button and button group states to match Figma#1174
OS-giulianasilva merged 4 commits into
ROU-12714from
ROU-12881-buttons

Conversation

@OS-giulianasilva

Copy link
Copy Markdown
Contributor

This PR is for aligning Button and Button Group interactive states with their Figma specs.

Depends on the theme-layer roles PR (ROU-12881-foundation) for --color-primary-active. Merge that first — this branch is stacked on it.

What was happening

  • The unmodified .btn rendered as a blue outline button, but none of the 5 Figma button types look like that — Figma's "Secondary" is a solid dark-gray (neutral-bold) fill with white text.
  • Hover on Success/Error and the pressed state on every type used a generic brightness() filter with no dedicated color — so no type had a real Figma-matching hover/pressed color.
  • Cancel's hover changed the border instead of the background.
  • The focus ring only appeared under .has-accessible-features.
  • Disabled dimmed the whole button toward the page background via opacity.
  • Button Group's hover tinted the wrong element, and the selected item used the raw $token-semantics-primary-base token instead of the --color-primary role.

What was done

  • Button (_btn.scss): base .btn reskinned to Secondary (solid neutral-bold fill + inverse text); each variant gets its own hover and pressed color via shared --osui-btn-hover-background/--osui-btn-active-background vars (Primary pressed → --color-primary-active); Cancel keeps a fixed border and only steps the background tier; always-on :focus-visible ring; disabled switched to a translucent overlay covering fill + border.
  • Button Group (_button-group.scss): hover matches the button technique (neutral items darken border, selected item darkens background + border); selected background → --color-primary.
  • Regenerated the CSS API reference.

Test Steps

Open the Widgets/Button and Widgets/ButtonGroup stories. The button element is <button class="btn …">.

Button — check each type by class:

  1. class="btn" (Secondary): solid dark-gray fill, white text.
  2. class="btn btn-primary", btn-cancel, btn-success, btn-error: each its own fill.
  3. Hover (force :hover in DevTools, page must have .desktop on <body> — the Storybook decorator sets it): each type darkens to its own hover tier; Cancel darkens only the background, border stays.
  4. Pressed (force :active): each type shows a distinct, darker pressed color. For Primary, confirm it's a solid darker blue (--color-primary-active), not a washed-out translucent tint.
  5. Focus (Tab to the button, or force :focus-visible): a focus ring (gap + colored halo) appears — no longer requires .has-accessible-features.
  6. Disabled (add the disabled attribute): the button keeps its own color under a translucent white wash, covering both fill and border (no saturated border ring).

Button Group — items are <button class="button-group-item"> inside .button-group:

  1. Add class button-group-selected-item to one item → its background is --color-primary.
  2. Hover (force :hover, .desktop on body): a non-selected item darkens its border; the selected item darkens background + border.
  3. Disabled (disabled attribute on an item): translucent wash over it.

Screenshots

(prefer animated gif)

Checklist

  • tested locally
  • documented the code
  • clean all warnings and errors of eslint
  • requires changes in OutSystems (if so, provide a module with changes)
  • requires new sample page in OutSystems (if so, provide a module with changes)

Foundation for the widget CSS-API alignment work. Adds four framework
theme-layer role knobs to _root.scss, each defaulting through the
matching design token:

- --color-border-danger  → border-specific danger tier (form validation borders)
- --color-text-danger    → text-specific danger tier (validation messages)
- --color-border-primary → primary tier for focus-ring borders
- --color-primary-active → solid pressed/active state (distinct from the
  translucent --color-primary-selected wash that apps override)

No visual change on its own — components adopt these roles in the
follow-up PRs.
Depends on the theme-layer roles PR.

Button (_btn.scss):
- Base .btn reskinned to the Figma "Secondary" type: solid
  neutral-bold fill + inverse text (was a blue outline that matched no
  Figma type).
- Each variant (Primary/Cancel/Success/Error) gets a dedicated hover
  and pressed color via shared --osui-btn-hover-background /
  --osui-btn-active-background vars, replacing the generic brightness
  filter. Primary pressed uses --color-primary-active.
- Cancel: hover/pressed change only the background tier; border stays
  fixed.
- Always-on :focus-visible ring (was accessibility-mode only).
- Disabled uses a translucent overlay covering fill + border, instead
  of dimming the whole button via opacity.

Button Group (_button-group.scss):
- Hover matches the button technique (neutral items darken border,
  selected item darkens background + border).
- Selected item background reads --color-primary.

Regenerated the CSS API reference.
@OS-giulianasilva
OS-giulianasilva requested a review from a team as a code owner July 17, 2026 09:49
Base automatically changed from ROU-12881-foundation to ROU-12714 July 17, 2026 13:18
# Conflicts:
#	docs/css-api-reference.md
#	stories/CssApiReference.mdx
@sonarqubecloud

Copy link
Copy Markdown

@OS-giulianasilva
OS-giulianasilva merged commit ec06721 into ROU-12714 Jul 17, 2026
9 checks passed
@OS-giulianasilva
OS-giulianasilva deleted the ROU-12881-buttons branch July 17, 2026 13:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants