chore: align React Native IconSize enum to t-shirt sizing#1049
Conversation
📖 Storybook Preview |
| Xl = 'xl', | ||
| } | ||
|
|
||
| // ///////////////////////////////////////////////////// |
There was a problem hiding this comment.
Why t-shirt tokens instead of pixel strings?
React's IconSize already uses t-shirt tokens ('xs', 'sm', 'md', 'lg', 'xl'). Aligning React Native to the same values is the prerequisite for a future shared-type migration (ADR-0004). Using semantic tokens also decouples the public API from any specific pixel measurement — actual rendered dimensions are determined by the Tailwind class map, not the string value itself.
| [IconSize.Md]: 'w-5 h-5', // 20px | ||
| [IconSize.Lg]: 'w-6 h-6', // 24px | ||
| [IconSize.Xl]: 'w-8 h-8', // 32px | ||
| }; |
There was a problem hiding this comment.
Why a static class map instead of arbitrary value interpolation?
The previous implementation used w-[${size}px] template strings, which Tailwind cannot statically analyze — those classes would only work if twrnc evaluated arbitrary values at runtime. A static Record<IconSize, string> map is the idiomatic pattern: classes are known at build time, lintable, and consistent with how the React web package already handles IconSize in packages/design-system-react/src/components/Icon/Icon.constants.ts.
| TWCLASSMAP_ICON_SIZE_DIMENSION[size], | ||
| twClassName, | ||
| ); | ||
|
|
There was a problem hiding this comment.
Single class string vs. two separate width/height classes
Previously the component called tw.style(color, w-[${size}px], h-[${size}px]) — two separate arbitrary-value classes. The class map stores a single combined string (e.g. 'w-5 h-5') so the lookup is one operation. twrnc parses both utilities from the single string, keeping the call site clean and matching the pattern used in the React web Icon component.
| expect(icon.props.style[0].height).toStrictEqual(expectedStyle.height); | ||
| }, | ||
| ); | ||
| }); |
There was a problem hiding this comment.
Why does AvatarIcon's test need updating?
The old assertion cast MAP_AVATARICON_SIZE_ICONSIZE[size] (an IconSize value) to a string and compared it against icon.props.style[0].width — this only worked because the old IconSize values were numeric pixel strings ('12', '16', etc.) that twrnc happened to resolve to matching width numbers. With t-shirt tokens the values are now 'xs', 'sm', etc., so comparing them against pixel widths makes no sense. The fix resolves the expected dimensions through useTailwind() + TWCLASSMAP_ICON_SIZE_DIMENSION, which is the same path the component itself uses — keeping the assertion behaviorally equivalent.
📖 Storybook Preview |
## Release 30.0.0 This release adds the `NoPhotography` icon and continues ADR-0003/ADR-0004 type migrations for `AvatarToken` and `AvatarAccount` across all platforms, plus an internal `IconSize` token alignment in React Native. ### 📦 Package Versions - `@metamask/design-system-shared`: **0.8.0** - `@metamask/design-system-react`: **0.15.0** - `@metamask/design-system-react-native`: **0.15.0** ### 🔄 Shared Type Updates (0.8.0) #### AvatarToken and AvatarAccount Type Additions ([#1009](#1009), [#1015](#1015)) **What Changed:** - Added `AvatarToken` shared types (`AvatarTokenSize`, `AvatarTokenPropsShared`) - Added `AvatarAccount` shared types (`AvatarAccountPropsShared`) **Impact:** - Enables consistent `AvatarToken` and `AvatarAccount` implementations across React and React Native - Continues ADR-0003/ADR-0004 const-object + string-union pattern adoption ### 🌐 React Web Updates (0.15.0) #### Added - Added `NoPhotography` icon ([#1056](#1056)) #### Changed - **BREAKING:** Updated `AvatarToken` and `AvatarAccount` exports to use shared const-object + string-union types (ADR-0003/ADR-0004); normal use is unaffected ([#1009](#1009), [#1015](#1015)) ### 📱 React Native Updates (0.15.0) #### Added - Added `NoPhotography` icon ([#1056](#1056)) #### Changed - **BREAKING:** Updated `IconSize` underlying string values to semantic t-shirt size tokens; normal use is unaffected ([#1049](#1049)) - **BREAKING:** Updated `AvatarToken` and `AvatarAccount` exports to use shared const-object + string-union types (ADR-0003/ADR-0004); normal use is unaffected ([#1009](#1009), [#1015](#1015)) ###⚠️ Breaking Changes All breaking changes in this release are internal type/value alignment changes. Normal consumer usage is unaffected. ### ✅ Checklist - [x] Changelogs updated with human-readable descriptions - [x] Changelog validation passed (`yarn changelog:validate`) - [x] Version bumps follow semantic versioning - [x] design-system-shared: minor (0.7.0 → 0.8.0) - new shared types added - [x] design-system-react: minor (0.14.0 → 0.15.0) - new icon + type migrations - [x] design-system-react-native: minor (0.14.0 → 0.15.0) - new icon + type migrations - [x] Breaking changes documented in changelogs - [x] PR references included in changelog entries ## **Pre-merge author checklist** - [x] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) - [x] I've reviewed the [Release Workflow](./.cursor/rules/release-workflow.md) cursor rule - [ ] All tests pass (`yarn build && yarn test && yarn lint`) - [x] Changelog validation passes (`yarn changelog:validate`) ## **Pre-merge reviewer checklist** - [ ] I've reviewed the [Reviewing Release PRs](./docs/reviewing-release-prs.md) guide - [ ] Package versions follow semantic versioning - [ ] Changelog entries are consumer-facing (not commit message regurgitation) - [ ] Breaking changes are documented with examples - [ ] All unreleased changes are accounted for in changelogs <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Low Risk** > Only version bumps and changelog updates; no runtime code changes, so risk is low aside from potential downstream impact of publishing new package versions. > > **Overview** > Bumps the monorepo release to `30.0.0` and increments package versions for `@metamask/design-system-react` and `@metamask/design-system-react-native` to `0.15.0`, and `@metamask/design-system-shared` to `0.8.0`. > > Updates the package changelogs to document the new release entries (including `NoPhotography` icon and ADR-0003/ADR-0004 type export migrations, plus React Native `IconSize` token alignment) and advances the `[Unreleased]` compare links accordingly. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit a1d8f0e. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY -->
## **Description**
Aligns the React Native `IconSize` enum values to match React's t-shirt
sizing tokens (`xs`/`sm`/`md`/`lg`/`xl`) instead of pixel strings
(`12`/`16`/`20`/`24`/`32`).
**Why:** React and React Native had divergent `IconSize` values. React
used semantic t-shirt tokens; React Native used raw pixel strings. This
inconsistency made cross-platform usage confusing and prevented a future
shared type migration.
**What changed:**
- Updated `IconSize` enum values in `src/types/index.ts` from pixel
strings to t-shirt tokens (matching React)
- Added `Icon.constants.ts` with `TWCLASSMAP_ICON_SIZE_DIMENSION` — a
Tailwind class map from size token to `w-*/h-*` classes (mirrors the
existing React pattern)
- Refactored `Icon.tsx` to look up size classes via the map instead of
interpolating `w-[${size}px]` arbitrary values
- Updated `Icon.test.tsx` and `AvatarIcon.test.tsx` size assertions to
use the class map
This is an incremental step toward a shared `IconSize` type across
platforms (ADR-0003 + ADR-0004).
## **Related issues**
Fixes:
## **Manual testing steps**
1. Run `yarn workspace @metamask/design-system-react-native test` — all
811 tests should pass
2. In Storybook (`yarn storybook:ios`), verify Icon renders correctly at
all 5 sizes
## **Screenshots/Recordings**
### **After**
no visual changes to icon size. Works as expected
https://github.com/user-attachments/assets/925902b5-5330-435c-9ec7-41ed26cd465e
## **Pre-merge author checklist**
- [x] I've followed [MetaMask Contributor
Docs](https://github.com/MetaMask/contributor-docs)
- [x] I've completed the PR template to the best of my ability
- [x] I've included tests if applicable
- [x] I've documented my code using [JSDoc](https://jsdoc.app/) format
if applicable
- [x] I've applied the right labels on the PR (see [labeling
guidelines](https://github.com/MetaMask/metamask-extension/blob/develop/.github/guidelines/LABELING_GUIDELINES.md)).
Not required for external contributors.
## **Pre-merge reviewer checklist**
- [ ] I've manually tested the PR (e.g. pull and build branch, run the
app, test code being changed).
- [ ] I confirm that this PR addresses all acceptance criteria described
in the ticket it closes and includes the necessary testing evidence such
as recordings and or screenshots.
<!-- CURSOR_SUMMARY -->
---
> [!NOTE]
> **Medium Risk**
> Changes the public `IconSize` enum values from pixel strings to
semantic tokens, which can break downstream consumers that relied on
numeric/string pixel values. Runtime icon sizing behavior also shifts to
a fixed Tailwind class map, so any missing/incorrect mapping would
affect visual output across components using `Icon`.
>
> **Overview**
> Aligns React Native `IconSize` to semantic t-shirt tokens
(`xs`/`sm`/`md`/`lg`/`xl`) instead of pixel-string values, reducing
cross-platform type divergence.
>
> Updates `Icon` to derive width/height from a new
`TWCLASSMAP_ICON_SIZE_DIMENSION` Tailwind class map rather than
interpolating arbitrary `w-[${size}px]`/`h-[${size}px]` classes, and
adjusts `Icon`/`AvatarIcon` tests to assert against the mapped styles.
>
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
61a37bc. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
## Release 30.0.0 This release adds the `NoPhotography` icon and continues ADR-0003/ADR-0004 type migrations for `AvatarToken` and `AvatarAccount` across all platforms, plus an internal `IconSize` token alignment in React Native. ### 📦 Package Versions - `@metamask/design-system-shared`: **0.8.0** - `@metamask/design-system-react`: **0.15.0** - `@metamask/design-system-react-native`: **0.15.0** ### 🔄 Shared Type Updates (0.8.0) #### AvatarToken and AvatarAccount Type Additions ([#1009](#1009), [#1015](#1015)) **What Changed:** - Added `AvatarToken` shared types (`AvatarTokenSize`, `AvatarTokenPropsShared`) - Added `AvatarAccount` shared types (`AvatarAccountPropsShared`) **Impact:** - Enables consistent `AvatarToken` and `AvatarAccount` implementations across React and React Native - Continues ADR-0003/ADR-0004 const-object + string-union pattern adoption ### 🌐 React Web Updates (0.15.0) #### Added - Added `NoPhotography` icon ([#1056](#1056)) #### Changed - **BREAKING:** Updated `AvatarToken` and `AvatarAccount` exports to use shared const-object + string-union types (ADR-0003/ADR-0004); normal use is unaffected ([#1009](#1009), [#1015](#1015)) ### 📱 React Native Updates (0.15.0) #### Added - Added `NoPhotography` icon ([#1056](#1056)) #### Changed - **BREAKING:** Updated `IconSize` underlying string values to semantic t-shirt size tokens; normal use is unaffected ([#1049](#1049)) - **BREAKING:** Updated `AvatarToken` and `AvatarAccount` exports to use shared const-object + string-union types (ADR-0003/ADR-0004); normal use is unaffected ([#1009](#1009), [#1015](#1015)) ###⚠️ Breaking Changes All breaking changes in this release are internal type/value alignment changes. Normal consumer usage is unaffected. ### ✅ Checklist - [x] Changelogs updated with human-readable descriptions - [x] Changelog validation passed (`yarn changelog:validate`) - [x] Version bumps follow semantic versioning - [x] design-system-shared: minor (0.7.0 → 0.8.0) - new shared types added - [x] design-system-react: minor (0.14.0 → 0.15.0) - new icon + type migrations - [x] design-system-react-native: minor (0.14.0 → 0.15.0) - new icon + type migrations - [x] Breaking changes documented in changelogs - [x] PR references included in changelog entries ## **Pre-merge author checklist** - [x] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) - [x] I've reviewed the [Release Workflow](./.cursor/rules/release-workflow.md) cursor rule - [ ] All tests pass (`yarn build && yarn test && yarn lint`) - [x] Changelog validation passes (`yarn changelog:validate`) ## **Pre-merge reviewer checklist** - [ ] I've reviewed the [Reviewing Release PRs](./docs/reviewing-release-prs.md) guide - [ ] Package versions follow semantic versioning - [ ] Changelog entries are consumer-facing (not commit message regurgitation) - [ ] Breaking changes are documented with examples - [ ] All unreleased changes are accounted for in changelogs <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Low Risk** > Only version bumps and changelog updates; no runtime code changes, so risk is low aside from potential downstream impact of publishing new package versions. > > **Overview** > Bumps the monorepo release to `30.0.0` and increments package versions for `@metamask/design-system-react` and `@metamask/design-system-react-native` to `0.15.0`, and `@metamask/design-system-shared` to `0.8.0`. > > Updates the package changelogs to document the new release entries (including `NoPhotography` icon and ADR-0003/ADR-0004 type export migrations, plus React Native `IconSize` token alignment) and advances the `[Unreleased]` compare links accordingly. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit a1d8f0e. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY -->
## **Description**
Aligns the React Native `IconSize` enum values to match React's t-shirt
sizing tokens (`xs`/`sm`/`md`/`lg`/`xl`) instead of pixel strings
(`12`/`16`/`20`/`24`/`32`).
**Why:** React and React Native had divergent `IconSize` values. React
used semantic t-shirt tokens; React Native used raw pixel strings. This
inconsistency made cross-platform usage confusing and prevented a future
shared type migration.
**What changed:**
- Updated `IconSize` enum values in `src/types/index.ts` from pixel
strings to t-shirt tokens (matching React)
- Added `Icon.constants.ts` with `TWCLASSMAP_ICON_SIZE_DIMENSION` — a
Tailwind class map from size token to `w-*/h-*` classes (mirrors the
existing React pattern)
- Refactored `Icon.tsx` to look up size classes via the map instead of
interpolating `w-[${size}px]` arbitrary values
- Updated `Icon.test.tsx` and `AvatarIcon.test.tsx` size assertions to
use the class map
This is an incremental step toward a shared `IconSize` type across
platforms (ADR-0003 + ADR-0004).
## **Related issues**
Fixes:
## **Manual testing steps**
1. Run `yarn workspace @metamask/design-system-react-native test` — all
811 tests should pass
2. In Storybook (`yarn storybook:ios`), verify Icon renders correctly at
all 5 sizes
## **Screenshots/Recordings**
### **After**
no visual changes to icon size. Works as expected
https://github.com/user-attachments/assets/925902b5-5330-435c-9ec7-41ed26cd465e
## **Pre-merge author checklist**
- [x] I've followed [MetaMask Contributor
Docs](https://github.com/MetaMask/contributor-docs)
- [x] I've completed the PR template to the best of my ability
- [x] I've included tests if applicable
- [x] I've documented my code using [JSDoc](https://jsdoc.app/) format
if applicable
- [x] I've applied the right labels on the PR (see [labeling
guidelines](https://github.com/MetaMask/metamask-extension/blob/develop/.github/guidelines/LABELING_GUIDELINES.md)).
Not required for external contributors.
## **Pre-merge reviewer checklist**
- [ ] I've manually tested the PR (e.g. pull and build branch, run the
app, test code being changed).
- [ ] I confirm that this PR addresses all acceptance criteria described
in the ticket it closes and includes the necessary testing evidence such
as recordings and or screenshots.
<!-- CURSOR_SUMMARY -->
---
> [!NOTE]
> **Medium Risk**
> Changes the public `IconSize` enum values from pixel strings to
semantic tokens, which can break downstream consumers that relied on
numeric/string pixel values. Runtime icon sizing behavior also shifts to
a fixed Tailwind class map, so any missing/incorrect mapping would
affect visual output across components using `Icon`.
>
> **Overview**
> Aligns React Native `IconSize` to semantic t-shirt tokens
(`xs`/`sm`/`md`/`lg`/`xl`) instead of pixel-string values, reducing
cross-platform type divergence.
>
> Updates `Icon` to derive width/height from a new
`TWCLASSMAP_ICON_SIZE_DIMENSION` Tailwind class map rather than
interpolating arbitrary `w-[${size}px]`/`h-[${size}px]` classes, and
adjusts `Icon`/`AvatarIcon` tests to assert against the mapped styles.
>
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
61a37bc. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
## Release 30.0.0 This release adds the `NoPhotography` icon and continues ADR-0003/ADR-0004 type migrations for `AvatarToken` and `AvatarAccount` across all platforms, plus an internal `IconSize` token alignment in React Native. ### 📦 Package Versions - `@metamask/design-system-shared`: **0.8.0** - `@metamask/design-system-react`: **0.15.0** - `@metamask/design-system-react-native`: **0.15.0** ### 🔄 Shared Type Updates (0.8.0) #### AvatarToken and AvatarAccount Type Additions ([#1009](#1009), [#1015](#1015)) **What Changed:** - Added `AvatarToken` shared types (`AvatarTokenSize`, `AvatarTokenPropsShared`) - Added `AvatarAccount` shared types (`AvatarAccountPropsShared`) **Impact:** - Enables consistent `AvatarToken` and `AvatarAccount` implementations across React and React Native - Continues ADR-0003/ADR-0004 const-object + string-union pattern adoption ### 🌐 React Web Updates (0.15.0) #### Added - Added `NoPhotography` icon ([#1056](#1056)) #### Changed - **BREAKING:** Updated `AvatarToken` and `AvatarAccount` exports to use shared const-object + string-union types (ADR-0003/ADR-0004); normal use is unaffected ([#1009](#1009), [#1015](#1015)) ### 📱 React Native Updates (0.15.0) #### Added - Added `NoPhotography` icon ([#1056](#1056)) #### Changed - **BREAKING:** Updated `IconSize` underlying string values to semantic t-shirt size tokens; normal use is unaffected ([#1049](#1049)) - **BREAKING:** Updated `AvatarToken` and `AvatarAccount` exports to use shared const-object + string-union types (ADR-0003/ADR-0004); normal use is unaffected ([#1009](#1009), [#1015](#1015)) ###⚠️ Breaking Changes All breaking changes in this release are internal type/value alignment changes. Normal consumer usage is unaffected. ### ✅ Checklist - [x] Changelogs updated with human-readable descriptions - [x] Changelog validation passed (`yarn changelog:validate`) - [x] Version bumps follow semantic versioning - [x] design-system-shared: minor (0.7.0 → 0.8.0) - new shared types added - [x] design-system-react: minor (0.14.0 → 0.15.0) - new icon + type migrations - [x] design-system-react-native: minor (0.14.0 → 0.15.0) - new icon + type migrations - [x] Breaking changes documented in changelogs - [x] PR references included in changelog entries ## **Pre-merge author checklist** - [x] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) - [x] I've reviewed the [Release Workflow](./.cursor/rules/release-workflow.md) cursor rule - [ ] All tests pass (`yarn build && yarn test && yarn lint`) - [x] Changelog validation passes (`yarn changelog:validate`) ## **Pre-merge reviewer checklist** - [ ] I've reviewed the [Reviewing Release PRs](./docs/reviewing-release-prs.md) guide - [ ] Package versions follow semantic versioning - [ ] Changelog entries are consumer-facing (not commit message regurgitation) - [ ] Breaking changes are documented with examples - [ ] All unreleased changes are accounted for in changelogs <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Low Risk** > Only version bumps and changelog updates; no runtime code changes, so risk is low aside from potential downstream impact of publishing new package versions. > > **Overview** > Bumps the monorepo release to `30.0.0` and increments package versions for `@metamask/design-system-react` and `@metamask/design-system-react-native` to `0.15.0`, and `@metamask/design-system-shared` to `0.8.0`. > > Updates the package changelogs to document the new release entries (including `NoPhotography` icon and ADR-0003/ADR-0004 type export migrations, plus React Native `IconSize` token alignment) and advances the `[Unreleased]` compare links accordingly. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit a1d8f0e. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY -->
Description
Aligns the React Native
IconSizeenum values to match React's t-shirt sizing tokens (xs/sm/md/lg/xl) instead of pixel strings (12/16/20/24/32).Why: React and React Native had divergent
IconSizevalues. React used semantic t-shirt tokens; React Native used raw pixel strings. This inconsistency made cross-platform usage confusing and prevented a future shared type migration.What changed:
IconSizeenum values insrc/types/index.tsfrom pixel strings to t-shirt tokens (matching React)Icon.constants.tswithTWCLASSMAP_ICON_SIZE_DIMENSION— a Tailwind class map from size token tow-*/h-*classes (mirrors the existing React pattern)Icon.tsxto look up size classes via the map instead of interpolatingw-[${size}px]arbitrary valuesIcon.test.tsxandAvatarIcon.test.tsxsize assertions to use the class mapThis is an incremental step toward a shared
IconSizetype across platforms (ADR-0003 + ADR-0004).Related issues
Fixes:
Manual testing steps
yarn workspace @metamask/design-system-react-native test— all 811 tests should passyarn storybook:ios), verify Icon renders correctly at all 5 sizesScreenshots/Recordings
After
no visual changes to icon size. Works as expected
icon.size.after.mov
Pre-merge author checklist
Pre-merge reviewer checklist
Note
Medium Risk
Changes the public
IconSizeenum from pixel-string values to semantic tokens, which can break downstream consumers that persist/compare old values or expect numeric sizing. Icon rendering now relies on a centralized Tailwind class map, so mistakes in the mapping would affect sizing across all icons.Overview
Aligns React Native
IconSizevalues from pixel strings ("12","16", …) to semantic t-shirt tokens (xs/sm/md/lg/xl).Introduces
TWCLASSMAP_ICON_SIZE_DIMENSIONand refactorsIcon(and related tests, includingAvatarIcon) to derive width/height from this Tailwind class map instead of interpolating arbitraryw-[${size}px]/h-[${size}px]classes.Reviewed by Cursor Bugbot for commit ced4de7. Bugbot is set up for automated code reviews on this repo. Configure here.