Skip to content

feat: Add Tag component for React Native#1053

Merged
georgewrmarshall merged 24 commits intomainfrom
feat-tag/react-native
Apr 21, 2026
Merged

feat: Add Tag component for React Native#1053
georgewrmarshall merged 24 commits intomainfrom
feat-tag/react-native

Conversation

@amandaye0h
Copy link
Copy Markdown
Contributor

@amandaye0h amandaye0h commented Apr 8, 2026

Description

What is the goal of this PR?

  • Creates a tag component that can be used in ListItems, Sections, and KeyValues

What are the key updates?

  • Adds a React Native Tag component with the variants, start/end icons props and conditional horizontal padding when icons are shown/not shown
  • Adds Code Connect (Tag.figma.tsx), stories, tests, and README/Figma link docs

Related issues

JIRA:

This is a dependency for these migration PRs:

Manual testing steps

  1. Run yarn install && yarn build && yarn storybook:ios to see changes

Screenshots/Recordings

Before

Component did not exist before this.

After

Screen.Recording.2026-04-16.at.7.53.17.PM.mov

Pre-merge author checklist

  • I've followed MetaMask Contributor Docs
  • I've completed the PR template to the best of my ability
  • I’ve included tests if applicable
  • I’ve documented my code using JSDoc format if applicable
  • I’ve applied the right labels on the PR (see labeling guidelines). 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.

Note

Medium Risk
Mostly additive, but it introduces new public exports in both design-system-react-native and design-system-shared, which can affect downstream consumers and bundling if misconfigured.

Overview
Adds a new React Native Tag component with severity-based styling and optional start/end icons or accessories, including conditional horizontal padding when icons are present.

Introduces shared TagSeverity and TagPropsShared in design-system-shared and exports them through package indexes, plus supporting Storybook stories, tests, Figma Code Connect mapping (Tag.figma.tsx), and README documentation.

Reviewed by Cursor Bugbot for commit ca8046c. Bugbot is set up for automated code reviews on this repo. Configure here.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 8, 2026

📖 Storybook Preview

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 8, 2026

📖 Storybook Preview


figma.connect(
Tag,
'https://www.figma.com/design/1D6tnzXqWgnUC3spaAOELN/%F0%9F%A6%8A-MMDS-Components?node-id=12339-6553',
Copy link
Copy Markdown
Contributor Author

@amandaye0h amandaye0h Apr 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added this so there is better connection between code:design. It'll help with maintainability in the long term. I can remove it if this is premature at this stage of our DS.

If/when this PR is approved, I'll need to add the code link in Figma.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 8, 2026

📖 Storybook Preview

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 8, 2026

📖 Storybook Preview

@amandaye0h amandaye0h marked this pull request as ready for review April 8, 2026 12:21
@amandaye0h amandaye0h requested a review from a team as a code owner April 8, 2026 12:21
Comment thread packages/design-system-react-native/src/components/index.ts Outdated
Warning: 'warning',
Info: 'info',
} as const;
export type TagVariant = (typeof TagVariant)[keyof typeof TagVariant];
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.

This should be in the shared package (you can get Cursor to help you align these props with cursor rules

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in commits here and here

flexDirection: BoxFlexDirection.Row,
alignItems: BoxAlignItems.Center,
twClassName: 'rounded-md self-start gap-0.5',
} as const;
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.

these styling should not be stored as consts. It should just be used inline

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/**
* Visual emphasis (background and default text color for label / plain-text children).
*/
variant?: TagVariant;
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.

A lot of these props can be extracted to shared (you can get cursor to do this)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/**
* Visual emphasis (background and default text color for label / plain-text children).
*/
variant?: TagVariant;
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.

Additionally I think this prop should be named either severity or semantic cc @georgewrmarshall

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/**
* Optional prop for a custom element to show at the start of the tag when no start icon is shown.
*/
startAccessory?: React.ReactNode;
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.

label, start Accessory, and endAccessory can be reused with BoxRow

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

const horizontalPaddingTw = [
finalStartIconName ? 'pl-1' : 'pl-1.5',
finalEndIconName ? 'pr-1' : 'pr-1.5',
].join(' ');
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.

don't store these consts. just use inline

Copy link
Copy Markdown
Contributor Author

@amandaye0h amandaye0h Apr 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Copy Markdown
Contributor

@brianacnguyen brianacnguyen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest to run each individual file through cursor to align with cursor rules

@brianacnguyen
Copy link
Copy Markdown
Contributor

@georgewrmarshall @amandaye0h should this component also includes a prop called includesSemanticIcon?

Copy link
Copy Markdown
Contributor

@georgewrmarshall georgewrmarshall left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi Amanda, great first component PR 🎉 this is a well structured component and it's clear you've put real thought into the API design! The render helper pattern, variant color mapping, and accessory/icon separation are all clean. There are a few architectural requirements from our cursor rules and some alignment gaps with the Figma design to address before this is ready to merge. I've walked through each one with full context and included Cursor/Claude prompts where they can help. Nothing here is a fundamental problem — just refinements to get it fully aligned with our standards.

TextColor,
} from '../../types';

export const TagVariant = {
Copy link
Copy Markdown
Contributor

@georgewrmarshall georgewrmarshall Apr 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Per ADR-0004, this const assertion should live in @metamask/design-system-shared/src/types/Tag/ rather than the React Native package. This matters because when a React (web) Tag component is built later, both platforms need to share the same const object — if it's defined in the platform package there's a risk they drift out of sync.

The mapping constants below (MAP_TAG_VARIANT_BACKGROUND, etc.) are fine to keep here since they reference React Native-specific tokens.

You can prompt Cursor/Claude to handle the migration:

Following .cursor/rules/component-architecture.md ADR-0004 pattern, move TagVariant
from Tag.constants.ts into packages/design-system-shared/src/types/Tag/Tag.types.ts,
create a TagPropsShared type there, update Tag.types.ts to import and extend it,
and update index.ts to export TagVariant directly from @metamask/design-system-shared.
Reference BadgeStatus as the golden path example.

Copy link
Copy Markdown
Contributor Author

@amandaye0h amandaye0h Apr 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

};

/** Vertical padding (Tailwind `py-*`) */
export const TAG_PADDING_VERTICAL_TW = 'py-0';
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.

TAG_PADDING_VERTICAL_TW = 'py-0' has no effect — py-0 is the Tailwind default, so this is a no-op. Remove it.

TAG_LAYOUT below is used in exactly one place. Single-use constants like this add indirection without benefit — a future engineer has to jump between files to understand the layout. Move these values inline into Tag.tsx directly.

Copy link
Copy Markdown
Contributor Author

@amandaye0h amandaye0h Apr 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/**
* Visual emphasis (background and default text color for label / plain-text children).
*/
variant?: TagVariant;
Copy link
Copy Markdown
Contributor

@georgewrmarshall georgewrmarshall Apr 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Curious on your thought here, I'm leaning towards two things here:

  1. Renaminh variant to severity. BannerAlert and IconAlert use severity for similar semantic set of states (neutral/info/success/error/warning). Keeping the prop name consistent across components makes the design system more predictable for consumers.

  2. The Figma component currently has primary where the code has Info — there is no info in Figma and no primary in the code. I'd recommend keeping Info (to stay consistent with BannerAlert) and updating the Figma component, but we could discuss before making this change

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Copy Markdown
Contributor Author

@amandaye0h amandaye0h Apr 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm onboard with updating Figma.

Replaced primary with info

Screenshot 2026-04-16 at 8 12 07 PM

/**
* Label string; when set, shown instead of string/number `children`. Matches Figma `Label` in Code Connect.
*/
label?: string;
Copy link
Copy Markdown
Contributor

@georgewrmarshall georgewrmarshall Apr 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: I realize label was in the original insight report but I think we can temove the label prop and textProps prop. BoxRow (which Tag will be used alongside) uses a TextOrChildren pattern — children is rendered as a Text component automatically when passed a string/number, and rendered as-is when passed a node. Tag should follow the same pattern. Having both label and children serve the same purpose creates a confusing API where two props compete.

In Tag.tsx and Tag.types.ts, remove the label and textProps props.
Update the component to render string/number children using the TextOrChildren
pattern from BoxRow — wrap string/number children in a Text component with
the severity's textColor, TextVariant.BodyXs, and FontWeight.Medium.
Node children should render as-is.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

const meta: Meta<typeof Tag> = {
title: 'Components/Tag',
component: Tag,
};
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.

Per our component documentation standards, the first story must always be Default with minimal args and all controls wired up via argTypes. This powers the Storybook controls panel.

const meta: Meta<typeof Tag> = {
  title: 'Components/Tag',
  component: Tag,
  argTypes: {
    severity: {
      control: 'select',
      options: Object.values(TagSeverity),
    },
    startIconName: { control: 'select', options: Object.values(IconName) },
    endIconName: { control: 'select', options: Object.values(IconName) },
  },
};

export const Default: Story = {
  args: {
    children: 'Tag',
    severity: TagSeverity.Neutral,
  },
};

non-blocking: TagVariant is imported from './Tag.constants' — once moved to shared, import from '.' (the index) to match the pattern used across the codebase.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

'https://www.figma.com/design/1D6tnzXqWgnUC3spaAOELN/%F0%9F%A6%8A-MMDS-Components?node-id=12339-6553',
{
props: {
variant: figma.enum('variant', {
Copy link
Copy Markdown
Contributor

@georgewrmarshall georgewrmarshall Apr 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Figma property is mistmatched type not variant. This mapping will silently produce no output in Dev Mode. Based on what the outcome is for variant/severity update to:

severity: figma.enum('severity', {
  neutral: TagSeverity.Neutral,
  info: TagSeverity.Info,
  success: TagSeverity.Success,
  error: TagSeverity.Error,
  warning: TagSeverity.Warning,
})

Hold off on publishing (yarn figma:connect:publish) until the severity/variant prop rename and info/primary Figma alignment are resolved. Once those are confirmed, run yarn figma:connect:publish:dry-run first to validate.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Screen.Recording.2026-04-16.at.8.07.16.PM.mov

export { BannerAlert, BannerAlertSeverity } from './BannerAlert';
export type { BannerAlertProps } from './BannerAlert';

export { Tag, TagVariant, type TagProps } from './Tag';
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.

non-blocking: Every other component in this file uses separate export and export type statements. The inline type pattern here is the only instance of this style in the file:

// current
export { Tag, TagVariant, type TagProps } from './Tag';

// consistent with rest of file
export { Tag, TagSeverity } from './Tag';
export type { TagProps } from './Tag';

Note: once TagSeverity is moved to shared, this export will change anyway — tackle after the shared types migration.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment thread packages/design-system-react-native/src/components/Tag/Tag.constants.ts Outdated
Copy link
Copy Markdown
Contributor

@georgewrmarshall georgewrmarshall left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This has come a long way through the review rounds and the component is looking strong overall. The API is much clearer now, the shared type split looks good, and I think this is in a good place to merge.

I have a couple of small non-blocking follow-ups, but they feel better suited to a separate PR than holding this up.

const iconColor = MAP_TAG_SEVERITY_ICON_COLOR[severity];

return (
<Box
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.

non-blocking: This works, but I think the container styling would be a bit easier to read and maintain if we used tw.style(...) here instead of building the class list with a template literal. We do this in other components in design-system-react-native, and I think it would make this component a bit clearer as a reference example.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

{...endIconProps}
/>
);
})()}
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.

non-blocking: I don’t think we need to support numeric children here. In practice our client usage is text content, and this branch adds a little extra behavior for a case we don’t really model elsewhere. I’d lean toward just passing children through directly unless we want number support to be an intentional part of the component contract.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@github-actions
Copy link
Copy Markdown
Contributor

📖 Storybook Preview

@github-actions
Copy link
Copy Markdown
Contributor

📖 Storybook Preview

Comment thread packages/design-system-react-native/src/components/Tag/Tag.tsx Outdated
@github-actions
Copy link
Copy Markdown
Contributor

📖 Storybook Preview

@github-actions
Copy link
Copy Markdown
Contributor

📖 Storybook Preview

@github-actions
Copy link
Copy Markdown
Contributor

📖 Storybook Preview

Copy link
Copy Markdown
Contributor

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit c464af6. Configure here.

Comment thread packages/design-system-react-native/src/components/Tag/Tag.tsx
@github-actions
Copy link
Copy Markdown
Contributor

📖 Storybook Preview

Copy link
Copy Markdown
Contributor

@georgewrmarshall georgewrmarshall left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great job on this. This has come a long way through the review rounds, the API is much clearer now, and the follow-up cleanup on Tag.tsx landed well. I think this is in a good place to merge.

@georgewrmarshall georgewrmarshall enabled auto-merge (squash) April 21, 2026 17:58
@github-actions
Copy link
Copy Markdown
Contributor

📖 Storybook Preview

@georgewrmarshall georgewrmarshall merged commit 68ddf99 into main Apr 21, 2026
79 of 81 checks passed
@georgewrmarshall georgewrmarshall deleted the feat-tag/react-native branch April 21, 2026 18:07
This was referenced Apr 23, 2026
georgewrmarshall added a commit that referenced this pull request Apr 23, 2026
## Release 35.0.0

This release adds new React Native title primitives, continues the
enum-to-const-object/string-union migration for public type exports, and
updates the release documentation so breaking changes are
consumer-focused and migration-oriented.

### Package Versions

- `@metamask/design-system-shared`: **0.13.0**
- `@metamask/design-system-react`: **0.18.0**
- `@metamask/design-system-react-native`: **0.20.0**
- `@metamask/design-system-tailwind-preset`: **0.6.2**

### Shared Type Updates (0.13.0)

#### Added (#1051, #1053, #1059)

**What Changed:**

- Added `TitleStandardPropsShared` and `TitleSubpagePropsShared`
- Added `TagSeverity` and `TagPropsShared`

**Impact:**

- Supports the new React Native `TitleStandard`, `TitleSubpage`, and
`Tag` APIs

#### Changed (#1026, #1042)

**What Changed:**

- **BREAKING:** Updated shared `Box` and `Icon` exports from enums to
const objects with derived string-union types
- Removed stale Box `WarningAlternative`, `SuccessAlternative`, and
`InfoAlternative` color entries that no longer map to design tokens

**Impact:**

- Affects consumers of `@metamask/design-system-shared` directly
- Platform package consumers should continue importing from
`@metamask/design-system-react` or
`@metamask/design-system-react-native`

### React Web Updates (0.18.0)

#### Changed

- **BREAKING:** Updated `IconName`, `IconColor`, and `IconSize` exports
to use const-object + string-union types instead of enums (#1042, #1101)
- **BREAKING:** Updated `Box` type exports to use const-object +
string-union types and removed stale Box color entries (#1026)
- Updated `ButtonTertiary` to use the default text color for more
consistent contrast across states (#1099)

### React Native Updates (0.20.0)

#### Added

- Added `TitleStandard` for mobile title layouts with optional top and
bottom accessory rows (#1051)
- Added `TitleSubpage` for subpage headers with avatar, title, subtitle,
amount, and bottom-label layouts (#1059)
- Added `Tag` for compact severity-based metadata labels with optional
icons or custom accessories (#1053)

#### Changed

- **BREAKING:** Updated `IconName`, `IconColor`, and `IconSize` exports
to use const-object + string-union types instead of enums (#1042)
- **BREAKING:** Updated `Box` type exports to use const-object +
string-union types and removed stale Box color entries (#1026)
- `Box` now forwards refs to the underlying `View` (#1102)
- Updated `ButtonTertiary` to use the default text color for more
consistent contrast across states (#1099)

### Tailwind Preset Updates (0.6.2)

#### Changed

- No consumer-facing API or behavior changes in this release; this patch
republishes the existing preset without requiring changes in consuming
apps

### Breaking Changes

#### Icon and Box enum exports migrated to const objects plus string
unions (Both Platforms)

**What Changed:**

- `IconName`, `IconColor`, and `IconSize` now use const objects with
derived string-union types instead of enums
- `BoxFlexDirection`, `BoxFlexWrap`, `BoxAlignItems`,
`BoxJustifyContent`, `BoxBackgroundColor`, `BoxBorderColor`,
`BoxSpacing`, and `BoxBorderWidth` now use const objects with derived
string-union types instead of enums
- Removed stale Box color entries with no backing design token:
  - `BoxBackgroundColor.WarningAlternative`
  - `BoxBackgroundColor.SuccessAlternative`
  - `BoxBorderColor.WarningAlternative`
  - `BoxBorderColor.SuccessAlternative`
  - `BoxBorderColor.InfoAlternative`

**Migration:**

```tsx
// Before
import {
  BoxBackgroundColor,
  IconColor,
  IconName,
} from '@metamask/design-system-react-native';

<Box backgroundColor={BoxBackgroundColor.WarningAlternative} />
<Icon name={IconName.Add} color={IconColor.IconDefault} />

// After
import {
  BoxBackgroundColor,
  IconColor,
  IconName,
} from '@metamask/design-system-react-native';

<Box backgroundColor={BoxBackgroundColor.WarningDefault} />
<Icon name={IconName.Add} color={IconColor.IconDefault} />
```

**Impact:**

- Affects consumers relying on enum-specific TypeScript behavior for
`Icon*` and `Box*` exports
- Import paths stay the same for platform-package consumers
- Any use of the removed Box `*Alternative` color entries will need to
switch to the corresponding `*Default` or `*Muted` token

See migration guides for complete instructions:

- [React Migration
Guide](./packages/design-system-react/MIGRATION.md#from-version-0170-to-0180)
- [React Native Migration
Guide](./packages/design-system-react-native/MIGRATION.md#from-version-0190-to-0200)

### Validation

- `yarn changelog:validate`

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Medium risk because it publishes new versions with **breaking
TypeScript surface changes** (Box/Icon enums → const-object/string-union
and removal of stale Box color members), which can break downstream
builds despite minimal runtime behavior changes.
> 
> **Overview**
> Bumps the monorepo release to `35.0.0` and publishes new package
versions for `@metamask/design-system-react` (`0.18.0`),
`@metamask/design-system-react-native` (`0.20.0`), and
`@metamask/design-system-shared` (`0.13.0`).
> 
> Updates changelogs/migration guides to reflect the release: adds React
Native primitives (`TitleStandard`, `TitleSubpage`, `Tag`) and shared
prop contracts, and documents **breaking** shifts of `Box*` and `Icon*`
exports from enums to const-object + string-union types (plus removal of
stale `*Alternative` Box color entries) with consumer-facing migration
steps.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
addbae5. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
georgewrmarshall pushed a commit that referenced this pull request Apr 27, 2026
<!--
Please submit this PR as a draft initially.
Do not mark it as "Ready for review" until the template has been
completely filled out, and PR status checks have passed at least once.
-->

## **Description**

### What is the goal of this PR?
- Creates a tag component that can be used in `ListItems`, `Sections`,
and `KeyValues`

### What are the key updates?
- Adds a React Native Tag component with the variants, start/end icons
props and conditional horizontal padding when icons are shown/not shown
- Adds Code Connect (`Tag.figma.tsx`), stories, tests, and README/Figma
link docs

## **Related issues**

JIRA:
- https://consensyssoftware.atlassian.net/browse/DSYS-568

This is a dependency for these migration PRs:

- [chore: Add deprecation messages for Tag and StockBadge
components](MetaMask/metamask-mobile#28918)
- [chore: Modify Cursor guidelines to include Tag
specs](MetaMask/metamask-mobile#28923)

## **Manual testing steps**

1. Run `yarn install && yarn build && yarn storybook:ios` to see changes

## **Screenshots/Recordings**

<!-- If applicable, add screenshots and/or recordings to visualize the
before and after of your change. -->

### **Before**

Component did not exist before this.

### **After**




https://github.com/user-attachments/assets/6aa3f7c3-7e2f-4a13-a4a1-880f24553c74








## **Pre-merge author checklist**

- [ ] I've followed [MetaMask Contributor
Docs](https://github.com/MetaMask/contributor-docs)
- [ ] I've completed the PR template to the best of my ability
- [ ] I’ve included tests if applicable
- [ ] I’ve documented my code using [JSDoc](https://jsdoc.app/) format
if applicable
- [ ] 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]
> **Low Risk**
> Primarily additive design-system UI work (new component + shared
types) with minimal impact outside new exports; risk is limited to
potential styling/prop API integration issues for consumers.
> 
> **Overview**
> Adds a new React Native `Tag` component with shared `TagSeverity`
semantics, optional start/end icons or custom accessories, and automatic
padding adjustments based on icon presence.
> 
> Introduces shared `TagSeverity`/`TagPropsShared` in
`design-system-shared`, wires exports in both shared and RN component
indexes, and includes Storybook stories, Code Connect (`Tag.figma.tsx`),
unit tests, and README documentation.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
7d5d89b. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

---------

Co-authored-by: Amanda <amanda@Amandas-MacBook-Pro.local>
georgewrmarshall added a commit that referenced this pull request Apr 27, 2026
## Release 35.0.0

This release adds new React Native title primitives, continues the
enum-to-const-object/string-union migration for public type exports, and
updates the release documentation so breaking changes are
consumer-focused and migration-oriented.

### Package Versions

- `@metamask/design-system-shared`: **0.13.0**
- `@metamask/design-system-react`: **0.18.0**
- `@metamask/design-system-react-native`: **0.20.0**
- `@metamask/design-system-tailwind-preset`: **0.6.2**

### Shared Type Updates (0.13.0)

#### Added (#1051, #1053, #1059)

**What Changed:**

- Added `TitleStandardPropsShared` and `TitleSubpagePropsShared`
- Added `TagSeverity` and `TagPropsShared`

**Impact:**

- Supports the new React Native `TitleStandard`, `TitleSubpage`, and
`Tag` APIs

#### Changed (#1026, #1042)

**What Changed:**

- **BREAKING:** Updated shared `Box` and `Icon` exports from enums to
const objects with derived string-union types
- Removed stale Box `WarningAlternative`, `SuccessAlternative`, and
`InfoAlternative` color entries that no longer map to design tokens

**Impact:**

- Affects consumers of `@metamask/design-system-shared` directly
- Platform package consumers should continue importing from
`@metamask/design-system-react` or
`@metamask/design-system-react-native`

### React Web Updates (0.18.0)

#### Changed

- **BREAKING:** Updated `IconName`, `IconColor`, and `IconSize` exports
to use const-object + string-union types instead of enums (#1042, #1101)
- **BREAKING:** Updated `Box` type exports to use const-object +
string-union types and removed stale Box color entries (#1026)
- Updated `ButtonTertiary` to use the default text color for more
consistent contrast across states (#1099)

### React Native Updates (0.20.0)

#### Added

- Added `TitleStandard` for mobile title layouts with optional top and
bottom accessory rows (#1051)
- Added `TitleSubpage` for subpage headers with avatar, title, subtitle,
amount, and bottom-label layouts (#1059)
- Added `Tag` for compact severity-based metadata labels with optional
icons or custom accessories (#1053)

#### Changed

- **BREAKING:** Updated `IconName`, `IconColor`, and `IconSize` exports
to use const-object + string-union types instead of enums (#1042)
- **BREAKING:** Updated `Box` type exports to use const-object +
string-union types and removed stale Box color entries (#1026)
- `Box` now forwards refs to the underlying `View` (#1102)
- Updated `ButtonTertiary` to use the default text color for more
consistent contrast across states (#1099)

### Tailwind Preset Updates (0.6.2)

#### Changed

- No consumer-facing API or behavior changes in this release; this patch
republishes the existing preset without requiring changes in consuming
apps

### Breaking Changes

#### Icon and Box enum exports migrated to const objects plus string
unions (Both Platforms)

**What Changed:**

- `IconName`, `IconColor`, and `IconSize` now use const objects with
derived string-union types instead of enums
- `BoxFlexDirection`, `BoxFlexWrap`, `BoxAlignItems`,
`BoxJustifyContent`, `BoxBackgroundColor`, `BoxBorderColor`,
`BoxSpacing`, and `BoxBorderWidth` now use const objects with derived
string-union types instead of enums
- Removed stale Box color entries with no backing design token:
  - `BoxBackgroundColor.WarningAlternative`
  - `BoxBackgroundColor.SuccessAlternative`
  - `BoxBorderColor.WarningAlternative`
  - `BoxBorderColor.SuccessAlternative`
  - `BoxBorderColor.InfoAlternative`

**Migration:**

```tsx
// Before
import {
  BoxBackgroundColor,
  IconColor,
  IconName,
} from '@metamask/design-system-react-native';

<Box backgroundColor={BoxBackgroundColor.WarningAlternative} />
<Icon name={IconName.Add} color={IconColor.IconDefault} />

// After
import {
  BoxBackgroundColor,
  IconColor,
  IconName,
} from '@metamask/design-system-react-native';

<Box backgroundColor={BoxBackgroundColor.WarningDefault} />
<Icon name={IconName.Add} color={IconColor.IconDefault} />
```

**Impact:**

- Affects consumers relying on enum-specific TypeScript behavior for
`Icon*` and `Box*` exports
- Import paths stay the same for platform-package consumers
- Any use of the removed Box `*Alternative` color entries will need to
switch to the corresponding `*Default` or `*Muted` token

See migration guides for complete instructions:

- [React Migration
Guide](./packages/design-system-react/MIGRATION.md#from-version-0170-to-0180)
- [React Native Migration
Guide](./packages/design-system-react-native/MIGRATION.md#from-version-0190-to-0200)

### Validation

- `yarn changelog:validate`

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Medium risk because it publishes new versions with **breaking
TypeScript surface changes** (Box/Icon enums → const-object/string-union
and removal of stale Box color members), which can break downstream
builds despite minimal runtime behavior changes.
> 
> **Overview**
> Bumps the monorepo release to `35.0.0` and publishes new package
versions for `@metamask/design-system-react` (`0.18.0`),
`@metamask/design-system-react-native` (`0.20.0`), and
`@metamask/design-system-shared` (`0.13.0`).
> 
> Updates changelogs/migration guides to reflect the release: adds React
Native primitives (`TitleStandard`, `TitleSubpage`, `Tag`) and shared
prop contracts, and documents **breaking** shifts of `Box*` and `Icon*`
exports from enums to const-object + string-union types (plus removal of
stale `*Alternative` Box color entries) with consumer-facing migration
steps.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
addbae5. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
georgewrmarshall added a commit that referenced this pull request Apr 28, 2026
## Release 35.0.0

This release adds new React Native title primitives, continues the
enum-to-const-object/string-union migration for public type exports, and
updates the release documentation so breaking changes are
consumer-focused and migration-oriented.

### Package Versions

- `@metamask/design-system-shared`: **0.13.0**
- `@metamask/design-system-react`: **0.18.0**
- `@metamask/design-system-react-native`: **0.20.0**
- `@metamask/design-system-tailwind-preset`: **0.6.2**

### Shared Type Updates (0.13.0)

#### Added (#1051, #1053, #1059)

**What Changed:**

- Added `TitleStandardPropsShared` and `TitleSubpagePropsShared`
- Added `TagSeverity` and `TagPropsShared`

**Impact:**

- Supports the new React Native `TitleStandard`, `TitleSubpage`, and
`Tag` APIs

#### Changed (#1026, #1042)

**What Changed:**

- **BREAKING:** Updated shared `Box` and `Icon` exports from enums to
const objects with derived string-union types
- Removed stale Box `WarningAlternative`, `SuccessAlternative`, and
`InfoAlternative` color entries that no longer map to design tokens

**Impact:**

- Affects consumers of `@metamask/design-system-shared` directly
- Platform package consumers should continue importing from
`@metamask/design-system-react` or
`@metamask/design-system-react-native`

### React Web Updates (0.18.0)

#### Changed

- **BREAKING:** Updated `IconName`, `IconColor`, and `IconSize` exports
to use const-object + string-union types instead of enums (#1042, #1101)
- **BREAKING:** Updated `Box` type exports to use const-object +
string-union types and removed stale Box color entries (#1026)
- Updated `ButtonTertiary` to use the default text color for more
consistent contrast across states (#1099)

### React Native Updates (0.20.0)

#### Added

- Added `TitleStandard` for mobile title layouts with optional top and
bottom accessory rows (#1051)
- Added `TitleSubpage` for subpage headers with avatar, title, subtitle,
amount, and bottom-label layouts (#1059)
- Added `Tag` for compact severity-based metadata labels with optional
icons or custom accessories (#1053)

#### Changed

- **BREAKING:** Updated `IconName`, `IconColor`, and `IconSize` exports
to use const-object + string-union types instead of enums (#1042)
- **BREAKING:** Updated `Box` type exports to use const-object +
string-union types and removed stale Box color entries (#1026)
- `Box` now forwards refs to the underlying `View` (#1102)
- Updated `ButtonTertiary` to use the default text color for more
consistent contrast across states (#1099)

### Tailwind Preset Updates (0.6.2)

#### Changed

- No consumer-facing API or behavior changes in this release; this patch
republishes the existing preset without requiring changes in consuming
apps

### Breaking Changes

#### Icon and Box enum exports migrated to const objects plus string
unions (Both Platforms)

**What Changed:**

- `IconName`, `IconColor`, and `IconSize` now use const objects with
derived string-union types instead of enums
- `BoxFlexDirection`, `BoxFlexWrap`, `BoxAlignItems`,
`BoxJustifyContent`, `BoxBackgroundColor`, `BoxBorderColor`,
`BoxSpacing`, and `BoxBorderWidth` now use const objects with derived
string-union types instead of enums
- Removed stale Box color entries with no backing design token:
  - `BoxBackgroundColor.WarningAlternative`
  - `BoxBackgroundColor.SuccessAlternative`
  - `BoxBorderColor.WarningAlternative`
  - `BoxBorderColor.SuccessAlternative`
  - `BoxBorderColor.InfoAlternative`

**Migration:**

```tsx
// Before
import {
  BoxBackgroundColor,
  IconColor,
  IconName,
} from '@metamask/design-system-react-native';

<Box backgroundColor={BoxBackgroundColor.WarningAlternative} />
<Icon name={IconName.Add} color={IconColor.IconDefault} />

// After
import {
  BoxBackgroundColor,
  IconColor,
  IconName,
} from '@metamask/design-system-react-native';

<Box backgroundColor={BoxBackgroundColor.WarningDefault} />
<Icon name={IconName.Add} color={IconColor.IconDefault} />
```

**Impact:**

- Affects consumers relying on enum-specific TypeScript behavior for
`Icon*` and `Box*` exports
- Import paths stay the same for platform-package consumers
- Any use of the removed Box `*Alternative` color entries will need to
switch to the corresponding `*Default` or `*Muted` token

See migration guides for complete instructions:

- [React Migration
Guide](./packages/design-system-react/MIGRATION.md#from-version-0170-to-0180)
- [React Native Migration
Guide](./packages/design-system-react-native/MIGRATION.md#from-version-0190-to-0200)

### Validation

- `yarn changelog:validate`

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Medium risk because it publishes new versions with **breaking
TypeScript surface changes** (Box/Icon enums → const-object/string-union
and removal of stale Box color members), which can break downstream
builds despite minimal runtime behavior changes.
> 
> **Overview**
> Bumps the monorepo release to `35.0.0` and publishes new package
versions for `@metamask/design-system-react` (`0.18.0`),
`@metamask/design-system-react-native` (`0.20.0`), and
`@metamask/design-system-shared` (`0.13.0`).
> 
> Updates changelogs/migration guides to reflect the release: adds React
Native primitives (`TitleStandard`, `TitleSubpage`, `Tag`) and shared
prop contracts, and documents **breaking** shifts of `Box*` and `Icon*`
exports from enums to const-object + string-union types (plus removal of
stale `*Alternative` Box color entries) with consumer-facing migration
steps.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
addbae5. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
cursor Bot pushed a commit that referenced this pull request Apr 28, 2026
<!--
Please submit this PR as a draft initially.
Do not mark it as "Ready for review" until the template has been
completely filled out, and PR status checks have passed at least once.
-->

## **Description**

### What is the goal of this PR?
- Creates a tag component that can be used in `ListItems`, `Sections`,
and `KeyValues`

### What are the key updates?
- Adds a React Native Tag component with the variants, start/end icons
props and conditional horizontal padding when icons are shown/not shown
- Adds Code Connect (`Tag.figma.tsx`), stories, tests, and README/Figma
link docs

## **Related issues**

JIRA:
- https://consensyssoftware.atlassian.net/browse/DSYS-568

This is a dependency for these migration PRs:

- [chore: Add deprecation messages for Tag and StockBadge
components](MetaMask/metamask-mobile#28918)
- [chore: Modify Cursor guidelines to include Tag
specs](MetaMask/metamask-mobile#28923)

## **Manual testing steps**

1. Run `yarn install && yarn build && yarn storybook:ios` to see changes

## **Screenshots/Recordings**

<!-- If applicable, add screenshots and/or recordings to visualize the
before and after of your change. -->

### **Before**

Component did not exist before this.

### **After**




https://github.com/user-attachments/assets/6aa3f7c3-7e2f-4a13-a4a1-880f24553c74








## **Pre-merge author checklist**

- [ ] I've followed [MetaMask Contributor
Docs](https://github.com/MetaMask/contributor-docs)
- [ ] I've completed the PR template to the best of my ability
- [ ] I’ve included tests if applicable
- [ ] I’ve documented my code using [JSDoc](https://jsdoc.app/) format
if applicable
- [ ] 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]
> **Low Risk**
> Primarily additive design-system UI work (new component + shared
types) with minimal impact outside new exports; risk is limited to
potential styling/prop API integration issues for consumers.
> 
> **Overview**
> Adds a new React Native `Tag` component with shared `TagSeverity`
semantics, optional start/end icons or custom accessories, and automatic
padding adjustments based on icon presence.
> 
> Introduces shared `TagSeverity`/`TagPropsShared` in
`design-system-shared`, wires exports in both shared and RN component
indexes, and includes Storybook stories, Code Connect (`Tag.figma.tsx`),
unit tests, and README documentation.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
7d5d89b. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

---------

Co-authored-by: Amanda <amanda@Amandas-MacBook-Pro.local>
cursor Bot pushed a commit that referenced this pull request Apr 28, 2026
## Release 35.0.0

This release adds new React Native title primitives, continues the
enum-to-const-object/string-union migration for public type exports, and
updates the release documentation so breaking changes are
consumer-focused and migration-oriented.

### Package Versions

- `@metamask/design-system-shared`: **0.13.0**
- `@metamask/design-system-react`: **0.18.0**
- `@metamask/design-system-react-native`: **0.20.0**
- `@metamask/design-system-tailwind-preset`: **0.6.2**

### Shared Type Updates (0.13.0)

#### Added (#1051, #1053, #1059)

**What Changed:**

- Added `TitleStandardPropsShared` and `TitleSubpagePropsShared`
- Added `TagSeverity` and `TagPropsShared`

**Impact:**

- Supports the new React Native `TitleStandard`, `TitleSubpage`, and
`Tag` APIs

#### Changed (#1026, #1042)

**What Changed:**

- **BREAKING:** Updated shared `Box` and `Icon` exports from enums to
const objects with derived string-union types
- Removed stale Box `WarningAlternative`, `SuccessAlternative`, and
`InfoAlternative` color entries that no longer map to design tokens

**Impact:**

- Affects consumers of `@metamask/design-system-shared` directly
- Platform package consumers should continue importing from
`@metamask/design-system-react` or
`@metamask/design-system-react-native`

### React Web Updates (0.18.0)

#### Changed

- **BREAKING:** Updated `IconName`, `IconColor`, and `IconSize` exports
to use const-object + string-union types instead of enums (#1042, #1101)
- **BREAKING:** Updated `Box` type exports to use const-object +
string-union types and removed stale Box color entries (#1026)
- Updated `ButtonTertiary` to use the default text color for more
consistent contrast across states (#1099)

### React Native Updates (0.20.0)

#### Added

- Added `TitleStandard` for mobile title layouts with optional top and
bottom accessory rows (#1051)
- Added `TitleSubpage` for subpage headers with avatar, title, subtitle,
amount, and bottom-label layouts (#1059)
- Added `Tag` for compact severity-based metadata labels with optional
icons or custom accessories (#1053)

#### Changed

- **BREAKING:** Updated `IconName`, `IconColor`, and `IconSize` exports
to use const-object + string-union types instead of enums (#1042)
- **BREAKING:** Updated `Box` type exports to use const-object +
string-union types and removed stale Box color entries (#1026)
- `Box` now forwards refs to the underlying `View` (#1102)
- Updated `ButtonTertiary` to use the default text color for more
consistent contrast across states (#1099)

### Tailwind Preset Updates (0.6.2)

#### Changed

- No consumer-facing API or behavior changes in this release; this patch
republishes the existing preset without requiring changes in consuming
apps

### Breaking Changes

#### Icon and Box enum exports migrated to const objects plus string
unions (Both Platforms)

**What Changed:**

- `IconName`, `IconColor`, and `IconSize` now use const objects with
derived string-union types instead of enums
- `BoxFlexDirection`, `BoxFlexWrap`, `BoxAlignItems`,
`BoxJustifyContent`, `BoxBackgroundColor`, `BoxBorderColor`,
`BoxSpacing`, and `BoxBorderWidth` now use const objects with derived
string-union types instead of enums
- Removed stale Box color entries with no backing design token:
  - `BoxBackgroundColor.WarningAlternative`
  - `BoxBackgroundColor.SuccessAlternative`
  - `BoxBorderColor.WarningAlternative`
  - `BoxBorderColor.SuccessAlternative`
  - `BoxBorderColor.InfoAlternative`

**Migration:**

```tsx
// Before
import {
  BoxBackgroundColor,
  IconColor,
  IconName,
} from '@metamask/design-system-react-native';

<Box backgroundColor={BoxBackgroundColor.WarningAlternative} />
<Icon name={IconName.Add} color={IconColor.IconDefault} />

// After
import {
  BoxBackgroundColor,
  IconColor,
  IconName,
} from '@metamask/design-system-react-native';

<Box backgroundColor={BoxBackgroundColor.WarningDefault} />
<Icon name={IconName.Add} color={IconColor.IconDefault} />
```

**Impact:**

- Affects consumers relying on enum-specific TypeScript behavior for
`Icon*` and `Box*` exports
- Import paths stay the same for platform-package consumers
- Any use of the removed Box `*Alternative` color entries will need to
switch to the corresponding `*Default` or `*Muted` token

See migration guides for complete instructions:

- [React Migration
Guide](./packages/design-system-react/MIGRATION.md#from-version-0170-to-0180)
- [React Native Migration
Guide](./packages/design-system-react-native/MIGRATION.md#from-version-0190-to-0200)

### Validation

- `yarn changelog:validate`

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Medium risk because it publishes new versions with **breaking
TypeScript surface changes** (Box/Icon enums → const-object/string-union
and removal of stale Box color members), which can break downstream
builds despite minimal runtime behavior changes.
> 
> **Overview**
> Bumps the monorepo release to `35.0.0` and publishes new package
versions for `@metamask/design-system-react` (`0.18.0`),
`@metamask/design-system-react-native` (`0.20.0`), and
`@metamask/design-system-shared` (`0.13.0`).
> 
> Updates changelogs/migration guides to reflect the release: adds React
Native primitives (`TitleStandard`, `TitleSubpage`, `Tag`) and shared
prop contracts, and documents **breaking** shifts of `Box*` and `Icon*`
exports from enums to const-object + string-union types (plus removal of
stale `*Alternative` Box color entries) with consumer-facing migration
steps.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
addbae5. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
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