Skip to content

fix(styles): replace undefined --font-weight-* vars with SDS tokens - #2946

Draft
CassioMG wants to merge 1 commit into
masterfrom
fix/undefined-font-weight-vars
Draft

CassioMG wants to merge 1 commit into
masterfrom
fix/undefined-font-weight-vars

Conversation

@CassioMG

@CassioMG CassioMG commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

What

The SCSS in extension/src referenced var(--font-weight-light|regular|medium|semi-bold|bold) in 59 places. None of those custom properties are defined anywhere — not in extension/src, not in @stellar/design-system. Every one of those declarations has been silently doing nothing.

This swaps all of them for the real SDS tokens, plus one mistyped var(--sds-font-weight-medium) that was broken the same way. 60 replacements, 35 files, no other changes.

Why they're undefined

Tailwind v4 is loaded globally (popup/styles/vendor/tailwind.csspopup/index.tsx), and its theme.css does define --font-weight-medium: 500. But v4 prunes unused theme variables. Extracting the emitted :root, :host block from a fresh extension/build/index.min.js, the only font-weight variable that reaches the cascade is:

--font-weight-semibold: 600

…which is a different spelling from the --font-weight-semi-bold this repo uses.

The full Tailwind list does appear in the bundle, but only inside an @theme default { … } at-rule — a compile-time construct browsers discard as unknown. A naive grep of the build makes it look live, so this is worth knowing if you go verifying.

Since font-weight is inherited, each declaration was invalid at computed-value time → unset → inherited the parent's weight (usually 400).

Mapping

Replaced With Value Sites
--font-weight-light --sds-fw-light 300 5
--font-weight-regular --sds-fw-regular 400 7
--font-weight-medium --sds-fw-medium 500 44
--font-weight-semi-bold --sds-fw-semi-bold 600 2
--font-weight-bold --sds-fw-bold 700 1
--sds-font-weight-medium (typo) --sds-fw-medium 500 1

⚠️ This is a visual change, not a refactor

Text at the medium / semi-bold / bold sites gets heavier — that's the fix landing, but it touches many screens and reviewers should expect a visible diff.

Two things worth a human eyeball:

  1. The five light sites go the other direction — 400 → 300, i.e. thinner. All are large display numerals, so it's plausibly intended, but it's the least-expected part of this change:

    • components/send/styles.scss__amount-label (2.5rem)
    • components/swap/SwapAmount/styles.scss__amount-label, __amount-label-usd, __input-amount (2.5rem)
    • components/accountHistory/TransactionDetail/styles.scss__header (2rem)
  2. The 7 regular sites are true no-ops. Each was checked for an ancestor setting a heavier weight; none nest under one, so all were already inheriting 400 and now get an explicit 400.

swap/SwapAsset/SwapPickerSections/styles.scss deserves a note: it carries a comment saying SDS renders the Notification title semi-bold and this rule wants it regular. SDS does set that. But the broken override still won the cascade and became unset → 400, so the comment's intent was already being met by accident. No visual change there either.

Deliberately left alone

components/manageAssetsLists/DeleteModal/styles.scss:26 is the one remaining --font-weight- reference, and it's genuinely corrupt:

color: --font-weight-medium --font-weight-medium;

Not a var() usage, so this sweep correctly skipped it. git log -L shows it was color: var(--color-gray-70); until 3aec8c8c ("Release/5.20.0"), where a bad find-and-replace clobbered the value of a color property with two bare font-weight idents. It's invalid CSS, dropped at parse time, so .DeleteModal__body currently inherits its color. Left as-is because picking the right modern SDS gray is a design decision, not a mechanical one.

Related rot, out of scope for this PR: --color-white, --color-red-70, and --color-gray-70 are likewise undefined and still referenced in several files (ModalInfo, NetworkForm, accountMigration/basics). Same silent-failure class — worth a follow-up.

Testing

yarn test:ci
  Test Suites: 6 skipped, 197 passed, 197 of 203 total
  Tests:       51 skipped, 1525 passed, 1576 total

yarn build:extension
  webpack compiled in 23116 ms

Prettier clean across all 35 changed files. ESLint is wired through lint-staged for src/**/*.ts?(x) only, so it doesn't apply to a .scss-only diff.

Post-build check confirming the fix reaches the emitted stylesheets:

=== var(--sds-fw-*) in emitted CSS ===
  70 index.min.css:var(--sds-fw-medium)      16 638.min.css:var(--sds-fw-medium)
  14 index.min.css:var(--sds-fw-regular)      8 638.min.css:var(--sds-fw-regular)
   8 index.min.css:var(--sds-fw-semi-bold)    7 638.min.css:var(--sds-fw-semi-bold)
   5 index.min.css:var(--sds-fw-light)        2 638.min.css:var(--sds-fw-bold)
   1 index.min.css:var(--sds-fw-bold)

The only remaining --font-weight-* in the emitted CSS is the DeleteModal color: line above, as expected.

Not run: the Playwright E2E suite. Given this shifts type weight app-wide, any screenshot baselines should be checked before merge.

Draft because

Filed as a draft so the weight changes can be eyeballed on-device first — particularly the five light sites going thinner.

🤖 Generated with Claude Code

The SCSS in extension/src referenced `var(--font-weight-light|regular|
medium|semi-bold|bold)`, but none of those custom properties are defined
anywhere: not in extension/src, not in @stellar/design-system, and not in
Tailwind's emitted output. Tailwind v4 prunes unused theme variables, and
the only font-weight variable that reaches the `:root, :host` block in the
built bundle is `--font-weight-semibold: 600` -- a different spelling from
the `--font-weight-semi-bold` this repo used.

Because `font-weight` is an inherited property, each of these declarations
was invalid at computed-value time, resolved to `unset`, and fell back to
the parent's weight (usually 400). The intended weights were silently never
applied.

Swap them for the Stellar Design System tokens, which are defined in
styles.min.css and were already used elsewhere in the popup:

  --font-weight-light      -> --sds-fw-light      (300)   5 sites
  --font-weight-regular    -> --sds-fw-regular    (400)   7 sites
  --font-weight-medium     -> --sds-fw-medium     (500)  44 sites
  --font-weight-semi-bold  -> --sds-fw-semi-bold  (600)   2 sites
  --font-weight-bold       -> --sds-fw-bold       (700)   1 site

Also fixes a mistyped `var(--sds-font-weight-medium)` in
accountHistory/TransactionDetail, which was broken for the same reason.

This is a real visual change, not a refactor: text at the medium/semi-bold/
bold sites gets heavier, and the five large display-numeral sites that asked
for `light` get thinner.

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

github-actions Bot commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

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

CassioMG added a commit that referenced this pull request Aug 8, 2026
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>
CassioMG added a commit that referenced this pull request Aug 8, 2026
…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>
CassioMG added a commit that referenced this pull request Aug 13, 2026
* feat(home): move history to the top icon row and fill the action row

* feat(home): replace the tab manage dropdown with a floating add button

* feat(collectibles): reach hidden collectibles from the add collectible 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.

* test(collectibles): update add collectible flow for the new add button

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

* fix(home): keep the floating add pill from covering the last list row

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.

* feat(home): match Figma chrome styling and spacing

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>

* feat(home): tighten tab spacing, thicken pill icon, polish add collectible

- 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>

* fix(home): apply icon stroke-width to the path, not the svg

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>

* fix(home): align mobile banner to the content column, soften empty-state 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>

* style(home): set the Add XLM button back to semi-bold

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>

* fix(collectibles): drop autofocus from the collection address input

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>

* fix(collectibles): make Show hidden a real button, cover the Tokens add 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>

* fix(collectibles): announce the hidden-collectibles sheet on its trigger

"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>

---------

Co-authored-by: Claude Opus 5 (1M context) <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.

1 participant