Skip to content
This repository was archived by the owner on Aug 7, 2026. It is now read-only.

style(buttons): make primary buttons read as raised, tappable controls - #301

Merged
rubenhensen merged 9 commits into
mainfrom
feat/primary-button-affordance
Jul 13, 2026
Merged

style(buttons): make primary buttons read as raised, tappable controls#301
rubenhensen merged 9 commits into
mainfrom
feat/primary-button-affordance

Conversation

@rubenhensen

@rubenhensen rubenhensen commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Problem

From the user-test session (#288): the primary action buttons ("Choose files", "Sign & send", modal actions) weren't obviously buttons. The near-black, near-flat .primary-btn reads as a label/graphic on the white page rather than a tappable control.

Change

1. Restyle .primary-btn globally to look like a raised, clearly-interactive button:

  • Colour: brand blue via var(--pg-primary-contrast), routed through a local --_btn-bg var so hover/active derive from the same base — matches the existing blue "Inbox" nav button.
  • Shape: 6px corners (--pg-border-radius-md).
  • Elevation: layered drop shadow + an inset bottom edge; hover deepens the blue (color-mix(... 82%, #000)) + shadow and lifts 1px, active presses it down.
  • Dark mode: the shared --pg-primary-contrast (#3095de) is too light for a white label to clear WCAG AA, so .dark .primary-btn pins --_btn-bg to a deeper blue #2277b3. The label stays white in both themes (--pg-on-primary = #ffffff) rather than inverting to dark-on-blue.
  • The dead .primary-btn:disabled img fade rule (the button is never natively disabled) was removed.

2. Yivi attribution → "Signing powered by" caption below the send button. Instead of keeping the multi-colour Yivi wordmark on the (now-blue) button, the wordmark moves into a Signing powered by caption (.powered-by) beneath the button (SendButton.svelte). It uses theme-swapped light/dark wordmark variants (yivi-logo--light / yivi-logo--dark, mirroring Header.svelte's logo swap) so it stays legible on either theme — no outline / drop-shadow trickery needed.

Accessibility

--pg-primary-contrast is paired with a white label (--pg-on-primary) per theme, keeping text contrast above WCAG AA (4.5:1) in both themes at rest and on hover — verified with an exact sRGB-linearized contrast calc:

  • Light: #1673b6 bg + white text → 5.05:1 (hover #125e956.86:1)
  • Dark: pinned #2277b3 bg + white text → 4.82:1 (hover #1c62936.53:1)

Darkening the background on hover increases contrast against the white label in both themes, so no theme-aware hover branch is needed.

Scope

.primary-btn change touches every primary button site-wide. Verified the file-sharing compose page in light and dark mode (Choose files, Sign & send with the Yivi attribution caption adapting per theme).

Pairs with #300 (send button's aria-disabled inactive state). Merge note: #300 has a .send-btn[aria-disabled='true'] img { opacity } rule that should stay targeting img (the Yivi wordmark remains an <img>, so no conflict). svelte-check and stylelint pass clean.

In user testing the near-black flat .primary-btn read as a label rather
than a button. Switch the fill to the brand blue via --pg-primary-contrast
(which keeps the paired text token at >=5:1 AA contrast in both light and
dark themes), round the corners to 6px, and add a layered drop shadow plus
an inset bottom edge so the button visibly lifts off the page. Hover
deepens the blue and shadow; active presses it down. This matches the
existing blue Inbox button.

Affects every primary button site-wide (Choose files, Sign & send, modal
actions, etc.).

@dobby-coder dobby-coder Bot left a comment

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 affordance upgrade — the raised, brand-blue treatment reads far more clearly as a tappable control, and it aligns with the existing Inbox nav button. Tokens (--pg-primary-contrast, --pg-general-background, --pg-border-radius-md) are defined in both themes, color-mix is already used in-repo, and the resting-state contrast checks out exactly (light 5.05:1, dark 5.39:1).

One blocker: the dark-mode hover state drops below WCAG AA. Details inline. Light-mode hover is fine (6.86:1). svelte-check/stylelint/build all pass.

Comment thread src/lib/global.scss Outdated
The Yivi wordmark is a fixed multi-colour asset that clashed with the new
blue send button. Replace the <img> with a <span> that uses the wordmark
as a CSS mask painted in the button's own text colour (currentColor), so
the logo always matches the label and adapts to the button colour and to
light/dark mode with nothing to keep in sync. Removes the now-dead
'fade logo when disabled' rule.
Replaces the earlier currentColor mask (which dropped the brand colours).
Keep the full-colour (dark) Yivi logo and give it a thin 1px outline (a
stack of hard drop-shadows around the glyph alpha) painted in the button's
text colour via currentColor, so the outline is white in light mode and
dark in dark mode, always matching the label.
Take the Yivi logo out of the Sign & send button (where its fixed
multi-colour wordmark never separated cleanly from the coloured fill)
and place it in a "Signing powered by Yivi" caption directly below the
button, on the page background. The wordmark swaps between the light and
dark variants per theme — same mechanism as Header.svelte's logo — so it
stays legible on either ground.
The dark-mode button used the page background (#061b2d) as its label
colour, giving dark text on the #3095de fill — technically AA (5.39:1)
but it reads as low-contrast dark-on-blue. White on #3095de fails AA
(3.24:1), so instead pin the dark-mode button to a deeper blue
(#2277b3) and keep the label white: 4.82:1 in dark, 5.05:1 in light,
and a conventional white-on-blue CTA in both themes. Routed through a
local --_btn-bg var so hover/active stay in sync.
Brings in #300 (aria-disabled send button + relocated required-fields
legend) and #302 (footer alignment). Reconciled SendButton.svelte so
the aria-disabled inactive state coexists with the new 'Signing powered
by Yivi' caption; dropped the now-orphaned .send-btn[aria-disabled] img
rule since the wordmark moved out of the button.
…note

Move the "* required" legend out of `.button-container` to be a direct
child of the compose column so it can pin to the column's bottom
(margin-top:auto on desktop) instead of trailing the "What is Yivi?"
block. Style it as a footnote — smaller, muted, with a hairline top
rule. The id is unchanged, so RecipientSelectionFields' aria-describedby
still resolves.
Per feedback: no hairline separator above the required-fields legend; just the small muted text pinned to the bottom of the column.
The legend lost its left indent when it moved out of .button-container; add padding-left: 1.25rem on desktop to line it up with the other column content.
@rubenhensen

Copy link
Copy Markdown
Contributor Author

/dobby review

@dobby-coder dobby-coder Bot left a comment

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.

Dobby reviewed this PR — the button-affordance restyle looks solid. 🧦

Code: internally consistent and verified. Contrast clears WCAG AA in both themes (light #1673b6 + white → 5.05:1; dark #2277b3 + white → 4.82:1), the --_btn-bg local-var routing keeps hover/active derived from one base, and the required-fields legend keeps its id so aria-describedby still resolves. svelte-check, stylelint and all 11 Playwright tests pass.

Rule checks: ran per-rule compliance across the memory rulebook (WCAG-AA, design-slop tells, conventional-commit title, docs-drift, tests-on-fixes, SvelteKit route/asset rules). All pass.

One nit (non-blocking): the PR description no longer matches the shipped code — see the inline comment. Worth a quick edit before merge so reviewers aren't misled. Not approving/blocking; leaving this as a comment.

Comment thread src/lib/global.scss
@rubenhensen

Copy link
Copy Markdown
Contributor Author

/dobby fix the comments

@dobby-coder dobby-coder Bot left a comment

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.

Dobby ran the post-review rule sweep on PR #301 and it comes back clean. 🧦

Rule checks (Haiku per-rule sweep): WCAG-AA, design-slop tells, conventional-commit PR title (style(buttons): … — valid), docs-drift / translation sync (en + nl both carry the new poweredBy key), tests-required, and SvelteKit route/export rules — all pass.

Contrast, re-verified in every interactive state: enabled primary button light #1673b6 on white = 5.05:1, dark #2277b3 on white = 4.82:1 (resting). Hover darkens the base toward black (color-mix(in srgb, var(--_btn-bg) 82%, #000)), so it only raises contrast — dark-mode hover computes to 6.53:1, light hover 6.86:1. The old dark-hover blocker from the 2026-07-10 review was on a pre-fix commit; the --_btn-bg local-var routing resolved it.

Accessibility: Yivi wordmark now carries alt="Yivi", and the required-fields legend keeps its id, so RecipientSelectionFields' aria-describedby still resolves.

Review Dobby 2 reported 0 findings; svelte-check, stylelint, build and all 11 Playwright tests pass. Nothing to fix — approving.

@rubenhensen
rubenhensen merged commit dbbcb7a into main Jul 13, 2026
9 checks passed
@rubenhensen
rubenhensen deleted the feat/primary-button-affordance branch July 13, 2026 09:43
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant