style(buttons): make primary buttons read as raised, tappable controls - #301
Conversation
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.).
There was a problem hiding this comment.
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.
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.
|
/dobby review |
There was a problem hiding this comment.
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.
|
/dobby fix the comments |
There was a problem hiding this comment.
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.
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-btnreads as a label/graphic on the white page rather than a tappable control.Change
1. Restyle
.primary-btnglobally to look like a raised, clearly-interactive button:var(--pg-primary-contrast), routed through a local--_btn-bgvar so hover/active derive from the same base — matches the existing blue "Inbox" nav button.--pg-border-radius-md).color-mix(... 82%, #000)) + shadow and lifts 1px, active presses it down.--pg-primary-contrast(#3095de) is too light for a white label to clear WCAG AA, so.dark .primary-btnpins--_btn-bgto a deeper blue#2277b3. The label stays white in both themes (--pg-on-primary=#ffffff) rather than inverting to dark-on-blue..primary-btn:disabled imgfade 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 bycaption (.powered-by) beneath the button (SendButton.svelte). It uses theme-swapped light/dark wordmark variants (yivi-logo--light/yivi-logo--dark, mirroringHeader.svelte's logo swap) so it stays legible on either theme — no outline / drop-shadow trickery needed.Accessibility
--pg-primary-contrastis 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:#1673b6bg + white text → 5.05:1 (hover#125e95→ 6.86:1)#2277b3bg + white text → 4.82:1 (hover#1c6293→ 6.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-btnchange 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-disabledinactive state). Merge note: #300 has a.send-btn[aria-disabled='true'] img { opacity }rule that should stay targetingimg(the Yivi wordmark remains an<img>, so no conflict).svelte-checkandstylelintpass clean.