Skip to content

feat(home): refresh home screen chrome - #2936

Open
CassioMG wants to merge 20 commits into
masterfrom
feature/home-refresh-chrome
Open

feat(home): refresh home screen chrome#2936
CassioMG wants to merge 20 commits into
masterfrom
feature/home-refresh-chrome

Conversation

@CassioMG

@CassioMG CassioMG commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Part of the Home refresh project. This is the Home screen's chrome — header, action row, tab row, and the new floating "+ Add token/collectible" buttons.

What changed

Per Figma frames 9567-17044 (Home) and 9673-19461 (Add Collectible):

  • History moves into the top icon row, between the options button and the network globe. The order is now options → history → network.
  • The action row becomes three equal columns — Add / Send / Swap — filling the width. History left the row to make space.
  • The tab row is just Tokens | Collectibles, active tab purple with an underline. The Sliders01 manage button and its whole dropdown are deleted.
  • New floating "Add token" / "Add collectible" pill replaces that dropdown, labelled and routed by the active tab.
  • Hidden collectibles moved from the deleted dropdown to a purple "Show hidden" button on the Add Collectible screen, with new helper text and an X header icon.
home-chrome-720p.mov

Analytics

Unchanged. history.full_history_opened moved position but kept its event name and source: "account_header" verbatim — still accurate, since both the old and new positions are in the header.

Note "Manage tokens" is not orphaned by deleting the tab dropdown: the menu already routes to ROUTES.manageAssets.

Also removed a genuinely dead isIncludingIcons prop from TabButtons — its JSDoc claimed it was "used in Send flow", but a grep across src and e2e-tests finds no consumer.

Verification

  • yarn test:ci — 1520/1571 tests, identical to baseline
  • yarn build:extension — clean
  • e2e: accountHistory (11 passed, unedited), addCollectible, hideCollectible, loadAccount, addAsset — all green
  • Both add-collectible-page snapshots regenerated and visually inspected

🤖 Generated with Claude Code

…e screen

Hand-added the new "Add collectible", "Add token", "Show hidden",
"View history", and helper-copy translation keys to en/pt directly --
yarn build:extension:translations is broken on master
(I18nextWebpackPlugin is not a constructor).

Also drops autoFocus from the collection-address field: with it
focused on mount, the first click anywhere else blurs it and Formik's
validation error shifts the layout down between mousedown and
mouseup, so a first click on the new Show hidden button (or any
control in that area) can land under the wrong element. Confirmed
by instrumenting the click in a throwaway e2e run: the handler never
fired with a real (non-forced) click until autoFocus was removed.
--update-snapshots=all only regenerated add-collectible-page-chromium-
darwin.png; the project has a `name` field so Playwright never writes
the plain -darwin.png variant. Both are the same chromium/darwin
capture, so the plain file is synced to match byte-for-byte after
visually confirming the regenerated PNG. Both were inspected: X close
icon, Show hidden link, and helper text render correctly with no other
regressions. Also noticed the prior checked-in snapshot was already
stale on master (old "Collectible address" placeholder copy and a
paste-icon affordance neither exists in current source) -- unrelated
pre-existing drift that this regeneration incidentally corrects.
Task 7's full e2e pass caught a real click-interception bug:
accountHistory.test.ts "Orders failed transactions..." clicks the USDC
row after returning to the Tokens tab, and with only two balances the
last row sits where the absolutely-positioned FloatingAddButton is
pinned, so the pill silently ate the click (Playwright reported the
row's own click as intercepted by the pill's subtree). Reserving
bottom padding on the sliding pane so real content never renders
underneath the pill fixes it for both the Tokens and Collectibles
panes without touching accountHistory.test.ts.
@github-actions

github-actions Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

PR Preview build is ready: https://github.com/stellar/freighter/releases/tag/untagged-ee46b743957bfc5b8627 (SDF collaborators only — install instructions in the release description)

CassioMG and others added 10 commits August 6, 2026 16:46
Brings the Home screen chrome to parity with the Figma design (node
9567:17044), plus the empty-state and list frames (9569:38124, 9569:37947).

Styling:

- Top nav: all three icon buttons now render 16px white icons. They were
  inconsistent because the size override keyed off `AccountHeader__dropdown`,
  which the history button isn't wrapped in, leaving it at the SDS NavButton
  default of 20px while its neighbours sat at 12px. Styling now hangs off the
  shared `__icon-btn__left` row.
- Account row: 16px avatar on gray-03 with no border (was 24px bordered), 12px
  gray-09 chevron, 6px gap.
- Action tiles: fill the row, no border, 78px tall, 24px lilac-11 icons. Three
  separate rules were keeping them from stretching -- the NavLink grid item, the
  tile inside it, and `__account-info__details`, which is content-sized under
  `justify-content: space-around`. Swap glyph switched to RefreshCw02 to match
  the design.
- Dropped the divider under the tab row.
- Action labels are 12px per Figma's Text/XS/500 (were 14px).
- Added a hover highlight on the action tiles (gray-03 -> gray-04), matching the
  convention already used by `AccountHeader__options__item`.

Spacing, all measured in a 360x600 render against the Figma node geometry:

- Nav buttons -> account row: 24px -> 48px.
- Tab strip -> panes: `AccountTabs` had 12px of bottom padding that
  double-counted against each pane's own top offset, pushing all four panes 12px
  low. Removing it corrects the token list, collectibles list and both empty
  states at once. Both empty states also needed their own trims and now land at
  the same offset, so switching tabs no longer shifts them.
- Floating pill: 16px -> 24px from the bottom.

The floating pill also needed a positioning fix. It was `position: absolute`
inside the scrolling inset, so it rode along with the list. Home has no inner
scroll container at all -- `.View--scrollable` resolves to `height: auto` against
an unsized body, so `.View` grows and the document scrolls -- which means
re-parenting alone wasn't enough. It is now rendered outside `View.Content` and
pinned with `position: fixed`. Verified: window scrolled 500px, pill unmoved.

Weights use `--sds-fw-*`; the `--font-weight-*` custom properties these files
previously referenced are undefined and silently resolved to inherit. The
repo-wide cleanup is #2946.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…tible

- Tiles -> tab strip is 28px where Figma (9567:17044) has 24px: the Balance
  frame closes with 24px and the tab strip adds nothing, but we were stacking
  `__actions` padding-bottom (16px) on `AccountTabs` padding-top (12px).
  `AccountTabs` is now flush and the 24px is owned solely by the tiles, which
  also matches how the 24px below the strip is owned by the panes. Measured with
  the MobileAppBanner dismissed, since Figma has no banner.

- The floating pill's "+" rendered at 1.33px, not the 2px Figma draws. SDS icons
  carry a 24x24 viewBox, so a 16px box scales strokes by 2/3 and the authored
  `stroke-width: 2` shrinks. Authoring 3 renders as 2px. (Computed style reports
  the authored value, so this is invisible unless you account for the viewBox.)

- Added a hover highlight to the floating pill, matching the action tiles.

- Add Collectible: autofocus the Collection address input on open, and give the
  content inset the existing `hasTopInput` variant instead of `hasNoTopPadding`
  so the input's 4px focus ring isn't clipped by the zero-padding inset.

- "Show hidden" now renders at its intended 500 weight. It asked for
  `--font-weight-medium`, which is undefined and silently resolved to inherit;
  the same applied to the helper text below it. See #2946 for the repo-wide
  sweep -- these two sites aren't on that branch.

Verified in a 360x600 render: tiles -> tabs 24px, tabs -> first row box 12px,
pill 24px off the bottom and its glyph stroke rendering at exactly 2px, pill
hover transitioning, address input holding focus on mount, its focus ring
clearing the inset by 4px, and "Show hidden" computing to 500 / lilac-11.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The previous commit set `stroke-width: 3` on the floating pill's svg to get
Figma's 2px stroke out of a 24x24 viewBox rendered at 16px. It had no effect.

SDS ships icons through SVGR, so the rendered DOM is the raw file and the
`<path>` keeps its own `stroke-width="2"` presentation attribute. Presentation
attributes lose to any CSS rule that matches the element, but they beat a value
inherited from an ancestor -- so a rule on the `<svg>` never reaches the path.
Targeting `svg path` fixes it: computed stroke-width on the painted element goes
from 2 to 3, which at the 2/3 viewBox scale renders as 2px.

Worth noting the earlier verification missed this because it read
`getComputedStyle(svg)`, the ancestor, which reported the value that was being
set and ignored. Reading the path shows the value actually painted.

Also converts the action tiles' `stroke-width: 2` to target the path. That one
was dead for the same reason, but harmlessly so -- the path's own attribute is
already 2, and at a 24px box that is the 2px Figma draws -- so this is a no-op
that stops the declaration from looking load-bearing.

`stroke` (colour) is unaffected throughout: the paths carry no `stroke`
attribute of their own, so they inherit it from the svg as intended.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ate CTA

The Freighter Mobile banner carried `margin-left: -16px`, `margin-right: 32px`
and `width: calc(100% + 32px)`, which pushed it 16px past the content column on
both sides -- visibly wider than the action tiles, tab strip and token rows above
and below it.

Those offsets predate `AccountHeader__account-info__details` gaining
`width: 100%`. Back when that box was content-sized under
`justify-content: space-around`, the negative margin was compensating for its
inset; now it just overhangs. Dropping all three lets the banner stretch to its
parent. Measured at 360x600, the banner now spans left 24 / right 336, matching
the token rows exactly and the tiles to within the sub-pixel rounding of their
`1fr` grid.

Also on the unfunded Tokens empty state:

- "Add XLM" goes from the SDS Button default of semi-bold (600) to medium (500).
- "Add XLM" goes from size md to lg.

Verified against a real unfunded render: the empty-state badge sits 24px below
the tab strip per Figma -- the one spacing target from the previous commits that
had not been confirmed on an actual unfunded account, only derived -- and the
button computes to font-weight 500.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Reverts the medium (500) weight from the previous commit; the empty-state CTA
reads better at semi-bold (600). Kept as an explicit declaration rather than
deleting the rule so the intended weight stays stated at the call site.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@CassioMG CassioMG self-assigned this Aug 10, 2026
@CassioMG
CassioMG marked this pull request as ready for review August 10, 2026 23:58
Copilot AI balanced review requested due to automatic review settings August 10, 2026 23:58
Autofocusing the first field meant that tapping "Show hidden" blurred it, which
marked it touched and surfaced "Collection address is required" -- so reaching
hidden collectibles always went via a validation error the user had not caused.

The `hasTopInput` padding on View.Content stays: the focus ring still needs the
clearance once the field is focused by hand.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

Copilot AI 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.

Pull request overview

Refreshes the Home screen chrome and relocates hidden-collectible management.

Changes:

  • Reworks header actions, history placement, and tabs.
  • Adds contextual floating token/collectible actions.
  • Moves hidden collectibles to the Add Collectible screen.

Reviewed changes

Copilot reviewed 18 out of 20 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
extension/src/popup/views/AddCollectibles/styles.scss Styles helper text and hidden-items action.
extension/src/popup/views/AddCollectibles/index.tsx Adds hidden-collectibles access and revised header.
extension/src/popup/views/Account/styles.scss Reserves space for the floating action.
extension/src/popup/views/Account/index.tsx Renders the contextual add button.
extension/src/popup/locales/pt/translation.json Adds Portuguese translations.
extension/src/popup/locales/en/translation.json Adds English translation keys.
extension/src/popup/components/account/NotFundedMessage/styles.scss Updates empty-state spacing and typography.
extension/src/popup/components/account/NotFundedMessage/index.tsx Enlarges the funding action.
extension/src/popup/components/account/MobileAppBanner/styles.scss Aligns the banner with the refreshed layout.
extension/src/popup/components/account/FloatingAddButton/styles.scss Styles the floating add pill.
extension/src/popup/components/account/FloatingAddButton/index.tsx Routes contextual token or collectible actions.
extension/src/popup/components/account/AccountTabs/styles.scss Implements the simplified tab appearance.
extension/src/popup/components/account/AccountTabs/index.tsx Removes the asset-management dropdown.
extension/src/popup/components/account/AccountHeader/styles.scss Refreshes header, balance, and action-row styling.
extension/src/popup/components/account/AccountHeader/index.tsx Moves history and reduces actions to three columns.
extension/src/popup/components/account/AccountCollectibles/styles.scss Adjusts collectible empty-state spacing.
extension/e2e-tests/hideCollectible.test.ts Updates hidden-collectible navigation tests.
extension/e2e-tests/addCollectible.test.ts Updates collectible-add navigation coverage.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread extension/src/popup/components/account/FloatingAddButton/index.tsx
Comment thread extension/src/popup/views/AddCollectibles/index.tsx Outdated
…dd pill

Addresses both Copilot review comments on #2936.

Show hidden was a bare `div` with only an `onClick`: not focusable, no button
semantics, and inert to Enter/Space, so hidden collectibles were unreachable
without a pointer. It is now a `<button type="button">` with the native chrome
stripped, following the reset already used by `AccountCollectibles__header`.
`font: inherit` is ordered before the type-specific declarations so those still
win, and `display: block` preserves the div's layout -- the button default of
inline-block would add baseline leading beneath it. Verified by keyboard: the
control is reachable with Tab and Enter opens the panel.

Worth noting eslint could not have caught this. `eslint-plugin-jsx-a11y` is
registered in eslint.config.js but no preset is spread and no rule is enabled,
so none of its checks actually run.

The Tokens tab's floating add pill also had no e2e coverage -- `add-token-btn`
appeared nowhere under e2e-tests, while the collectibles branch of the same
component was covered. `addAsset.test.ts` reaches asset search through the
options menu, so a broken pill would not have failed anything. Adds two tests:
the pill renders on a funded Tokens tab and routes to asset search, and it is
absent for an unfunded account, where the empty state carries its own "Add XLM"
action instead.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@CassioMG

Copy link
Copy Markdown
Contributor Author

Code review

No issues found. Checked for bugs and CLAUDE.md compliance.

🤖 Generated with Claude Code

@CassioMG
CassioMG requested a review from piyalbasu August 11, 2026 03:02
@piyalbasu

Copy link
Copy Markdown
Contributor

Floating add pill can cover the row a keyboard user just focused

Accessibility — WCAG 2.2 AA (2.4.11 Focus Not Obscured), low-to-moderate severity — not a merge blocker.

TL;DR: The new floating "Add token / Add collectible" pill sits pinned to the bottom of the window while the list scrolls behind it. The extra space added at the bottom of the list only guarantees the last row clears the pill — rows in the middle of the list still pass underneath it as you scroll. So a keyboard user tabbing down the token list can end up with the highlighted row hidden behind the pill, and a mouse user can have a click land on the pill instead of the row beneath it.

Steps to reproduce:

  1. Open Home on a funded account with enough tokens to scroll (roughly 8+).
  2. Click the first token row, then press Tab repeatedly to walk down the list.
  3. Watch the focus ring as it approaches the bottom of the window — on the rows where the browser scrolls just enough to bring the next row into view, the focus ring lands behind the pill instead of above it.
  4. Alternatively, scroll so a mid-list row sits under the pill and try to click that row.

Detailed explanation (for agents)

Root cause: the pill is taken out of flow and pinned to the viewport:

.FloatingAddButton {
// Fixed, not absolute: on Home the layout has no inner scroll container —
// `.View` grows with the token list and the document itself scrolls. Any
// absolutely-positioned anchor therefore rides along with the content.
// Fixed pins the pill to the viewport, which is what "floating" means here.
// Safe because no ancestor sets transform/filter/backdrop-filter, which
// would otherwise turn into its containing block.
position: fixed;
bottom: pxToRem(24px);
left: 50%;
transform: translateX(-50%);
z-index: var(--z-index--banner);

The mitigation is bottom padding on the pane:

&__pane {
padding: 0 var(--account-view-padding-side);
// Reserve room below the last row so the floating add pill (an
// absolutely-positioned overlay pinned near the bottom of the view)
// never sits on top of real list content and swallows its clicks.
padding-bottom: pxToRem(88px);
height: auto;
overflow: auto;

padding-bottom extends the scrollable content past the end of the list, which correctly stops the pill from resting on the final row at max scroll. It does nothing for intermediate scroll positions: the pill is fixed to the viewport, so at any scroll offset other than the very bottom there is real list content occupying the 24px–60px band the pill covers.

The keyboard case is the one that trips a WCAG check. When focus moves to an element outside the visible area, the browser performs a minimal scroll to bring it into view — which typically aligns the newly focused element flush with the bottom scrollport edge, i.e. exactly under the pill. 2.4.11 requires that the focused component not be entirely hidden by author-created content; a 36px-tall pill over a ~64px row generally leaves a sliver visible, so this is closer to 2.4.12 (AAA, "not obscured at all") in the strictest reading — but it is a real degradation either way, and it's cheap to fix properly.

Suggested fixes (in increasing order of depth):

  1. Cheap and correct for the keyboard case: add scroll-padding-bottom to the scroll container so the browser never scrolls a focused element into the band the pill occupies. Note this must go on whatever element actually scrolls — .AccountView overrides .View__content to overflow: visible, and the FloatingAddButton comment states the document itself scrolls on Home, so .multi-pane-slider__pane having overflow: auto may not be the live scrollport. Worth confirming with document.scrollingElement before picking the selector.

    // alongside the existing padding-bottom
    scroll-padding-bottom: pxToRem(88px);
  2. Also fixes the pointer case: give the pill pointer-events handling so it never eats a click meant for a row — e.g. wrap it in a pointer-events: none positioning container with pointer-events: auto restored on the pill itself. This narrows the dead zone to the pill's actual bounds rather than any padding/hit-slop around it, but does not eliminate it; the pill is genuinely on top of the row it covers.

  3. Root cause: make the pill hide on scroll-down and reappear on scroll-idle / scroll-up (the common pattern for bottom-pinned FABs), or anchor it to the bottom of the pane rather than the viewport so it participates in layout. Bigger change and a design call, so only worth it if the pill turns out to be visually in the way in practice.

Note on the code comment: views/Account/styles.scss describes the pill as "an absolutely-positioned overlay" — it is position: fixed, and the distinction is exactly why the padding doesn't fully solve the problem. Worth correcting so the next reader doesn't infer the padding is sufficient.

@piyalbasu

Copy link
Copy Markdown
Contributor

History becomes an icon-only control with no visible label

Accessibility — moderate, discoverability + assistive-tech quality. Not a merge blocker.

TL;DR: History used to be a labelled tile — an icon with the word "History" underneath it. In the new header it's a bare clock icon, and the only thing that names it is a browser tooltip on hover. Tooltips don't appear for touch users or keyboard users, and some screen reader setups don't read them, so the control is harder to identify than it was. This matches how the neighbouring options and network buttons already work, so it's a consistency question for the team rather than a new defect introduced here — but it's worth a conscious call, since History moved from the labelled group into the unlabelled one.


Detailed explanation (for agents)

Root cause: the control moved from a NavLink with a visible <Text> label into an SDS NavButton, which exposes no aria-label prop:

<div data-testid="nav-link-account-history">
<NavButton
showBorder
title={t("View history")}
id="nav-btn-history"
icon={<Icon.ClockRewind />}
onClick={() => {
emitMetric(METRIC_NAMES.historyFullHistoryOpened, {
source: "account_header",
});
navigateTo(ROUTES.accountHistory, navigate);
}}
/>
</div>

NavButton in @stellar/design-system@3.2.8 renders exactly this:

<button id={id} className={`NavButton ${showBorder ? "NavButton--border" : ""}`}
        title={title} onClick={onClick} disabled={disabled}>
  {icon}
</button>

and the icon assets carry no <title> element:

<!-- build/assets/icons/clock-rewind.svg — same shape as plus.svg / x.svg -->
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor">
  <path d="" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

So the accessible name resolves entirely through the title attribute. That is a valid name source — the accname spec uses title as the last-resort fallback, and both NVDA and VoiceOver will announce it — so this is not a 4.1.2 Name, Role, Value failure, and no automated checker will flag it. The cost is real but softer:

  • title tooltips never render on touch, and never render on keyboard focus (only hover), so keyboard and touch users get no label at all.
  • title is suppressed in some AT verbosity configurations.
  • 2.5.3 Label in Name doesn't apply (there's no visible text label to mismatch), but the cognitive-accessibility argument for a visible label stands: three adjacent unlabelled circles (options , history, network globe) is a lot to disambiguate by glyph alone.

Target size is fine — the header override sets these to 32×32, comfortably over the 24×24 required by 2.5.8.

Suggested fixes (in increasing order of depth):

  1. Do nothing, deliberately. It's consistent with the existing options and network buttons, and the design calls for an icon row. If this is the decision, no code change — just worth recording so the next a11y sweep doesn't re-litigate it.
  2. Strengthen the name without changing the visual: wrap with an explicit label, since NavButton won't forward one:
    <div data-testid="nav-link-account-history" role="none">
      <span id="nav-btn-history-label" className="sr-only">{t("View history")}</span>
      <NavButton  />
    </div>
    or add aria-label support to NavButton upstream in SDS, which fixes all three header buttons at once and is probably the right home for it.
  3. Root cause: keep a visible label under the icon, as the old tile had. This conflicts with the Figma header design, so it's a design conversation, not a code fix.

@piyalbasu

Copy link
Copy Markdown
Contributor

Add Collectible: "Show hidden" doesn't announce that it opens a panel, and the helper text reads out of order

Accessibility — minor, two small polish items on the same screen. Not a merge blocker.

TL;DR: Two things on the new Add Collectible layout. First, "Show hidden" opens a slide-up panel, but nothing tells a screen reader that's what will happen — it's announced as an ordinary button, so the panel appearing is a surprise. Second, the sentence explaining what the screen is for ("If a collectible is missing from your wallet, you can manually add it here") is placed after the "Show hidden" button, so a screen reader user hears the unrelated action before they hear the explanation of the field they just landed on.

Worth noting the positive here too: making "Show hidden" a real <button> instead of the clickable div it replaced is a genuine keyboard-accessibility improvement — it's now reachable by Tab and activates on Enter/Space, which the old dropdown item never did.


Detailed explanation (for agents)

Root cause: both items live in the same block:

<button
type="button"
className="AddCollectibles__show-hidden"
data-testid="hidden-collectibles-btn"
onClick={() => setIsHiddenCollectiblesOpen(true)}
>
{t("Show hidden")}
</button>
<div className="AddCollectibles__helper-text">
{t(
"If a collectible is missing from your wallet, you can manually add it here.",
)}
</div>

1. Missing dialog affordance. The button toggles isHiddenCollectiblesOpen, which mounts <HiddenCollectibles isOpen={…} /> — a sheet. Nothing on the trigger advertises that. Per the ARIA authoring practices a control that opens a dialog should carry aria-haspopup="dialog", and if the disclosure state is meaningful, aria-expanded. Neither is present, so AT announces "Show hidden, button" and the user has no warning that focus context is about to change.

2. Reading order. DOM order is FormRows (the contract-address input) → <button>Show hidden</button> → helper text. The helper text is descriptive copy about the input, but it is neither adjacent to the input nor programmatically associated with it, so a screen reader user tabbing from the input hears the "Show hidden" action first and only reaches the explanation afterwards — by which point it reads as commentary on the button rather than the field. Sighted users resolve this instantly from layout; AT users don't get that cue.

Suggested fixes (in increasing order of depth):

  1. Cheap: add the dialog affordance to the trigger.
    <button
      type="button"
      className="AddCollectibles__show-hidden"
      data-testid="hidden-collectibles-btn"
      aria-haspopup="dialog"
      aria-expanded={isHiddenCollectiblesOpen}
      onClick={() => setIsHiddenCollectiblesOpen(true)}
    >
      {t("Show hidden")}
    </button>
  2. Also cheap, fixes the ordering: move the helper-text div above the button so DOM order matches its meaning. If the Figma layout requires the button to sit visually above the text, keep the DOM order semantic and reorder visually with flex order rather than reordering the DOM — never the other way round.
  3. Most correct: associate the copy with the field it describes instead of leaving it as free-floating text, so it's announced when focus enters the input:
    <Input  aria-describedby="add-collectible-helper" />
    
    <div id="add-collectible-helper" className="AddCollectibles__helper-text"></div>
    aria-describedby works across DOM distance, so this fixes the ordering problem regardless of where the text physically sits.

Verified separately, no action needed: type="button" is set, so the control does not submit the enclosing Formik <Form>; and the SCSS strips border/background/appearance but leaves outline alone, so the default focus-visible ring survives.

@piyalbasu piyalbasu 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.

Approved with a couple of small accessibility improvements that would be nice to add!

"Show hidden" opens a Radix dialog but announced as a plain button, so the
sheet appearing was unsignalled. Adds aria-haspopup="dialog" and
aria-expanded, mirroring what Radix's own Dialog.Trigger emits. The Trigger
itself isn't usable here because the Dialog Root lives inside
HiddenCollectibles; aria-controls is omitted since Radix generates that id
inside the portal.

Also corrects two comments that described the floating add pill as
"absolutely-positioned" when it is position: fixed, and that named
View.Content's inset as the scroll container. Probed the running popup:
document.scrollingElement is html and it is the only scrollport on Home —
AccountView overrides View.Content and its inset to overflow: visible, and
.multi-pane-slider__pane does not scroll (scrollTop stays 0 when set). The
pane's padding-bottom comment now states what it actually guarantees: the
last row clears the pill at max scroll, not every row at every offset.

Co-Authored-By: Claude Opus 5 <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