From b931b41df450a61d701114e6f86312152f2c4e90 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 5 Nov 2026 20:44:54 +0000 Subject: [PATCH 1/5] docs(ui): expand the shared ui-development base and make MMDS guidance MCP-free Co-authored-by: George Marshall --- CHANGELOG.md | 1 + .../agentic-design-system-strategy.md | 18 +- .../references/reading-mmds-manifests.md | 70 ++++++ domains/ui/skills/ui-development/skill.md | 199 ++++++++++++++++++ 4 files changed, 280 insertions(+), 8 deletions(-) create mode 100644 domains/ui/skills/ui-development/references/reading-mmds-manifests.md diff --git a/CHANGELOG.md b/CHANGELOG.md index 6915cacb..da248184 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +- Expand the shared `ui/ui-development` base so it works with no MCP server: a source order (installed package types win, then the hosted Storybook components manifests, the component README at the installed version's tag, repository usage, and Figma MCP with Code Connect when connected), component choice and deprecation rules, foundations, durable typography rules, the `Legacy*` alias rule for incremental migrations, and the Active accessibility, trust, and sheet rules from the Design Brain. Add `references/reading-mmds-manifests.md`, and update the agentic design system strategy so Storybook MCP is optional and skills carry no release-pinned inventories. - Update the `testing/visual-testing` skill for `@metamask/client-mcp-core` 0.13.1: remove the knowledge store commands and the mandatory knowledge-reuse step, and document the global `--observe` and `--pretty` flags, one-line JSON output, observations without app state, `run-steps` `observe` (replaces `includeObservations`), timed-out calls being cancelled, and one element per ref. Replace the removed `yarn build:test:webpack` script with `yarn build:test`. - Update the MetaMask Mobile performance overlay and animation references for React Native 0.86.x, Expo SDK 57, and Reanimated 4.x with `react-native-worklets`. From a worklet, call `scheduleOnRN` (`runOnJS` is a deprecated alias). Static-ESM tree-shaking stays off. ([#203](https://github.com/MetaMask/skills/pull/203)) - Require every revert yes/no to be one line that names the pull request or commit, the files, and exactly one yes/no question, whether or not security or privacy is involved. A shorter message is not that step. diff --git a/domains/ui/knowledge/agentic-design-system-strategy.md b/domains/ui/knowledge/agentic-design-system-strategy.md index 50d6dd6f..7113569b 100644 --- a/domains/ui/knowledge/agentic-design-system-strategy.md +++ b/domains/ui/knowledge/agentic-design-system-strategy.md @@ -29,7 +29,7 @@ One knowledge layer, three deliveries. MMDS holds tokens, components, and patter | Moment | What happens | | :---- | :---- | -| **Create** | Agents read current MMDS guidance before building — through skills, Storybook MCP, Figma MCP, and Code Connect. | +| **Create** | Agents read current MMDS guidance before building — through skills, installed package types, the hosted Storybook components manifests, and Figma MCP with Code Connect when connected. | | **Review** | Deterministic checks (ESLint, fitness functions) plus agentic review for judgment calls. | | **Audit** | Recurring queries across existing screens to find drift, custom UI, and documentation gaps. | @@ -37,13 +37,14 @@ One knowledge layer, three deliveries. MMDS holds tokens, components, and patter - **Consumer skills** (`ui-development` repo overlays) route agents to current MMDS guidance for product UI create and review workflows. - **Provider skills** (future `design-system` domain) support MMDS authoring, release, and documentation maintenance. -- **Storybook MCP** (`storybook-broker-mcp`) is the preferred delivery mechanism for current MMDS knowledge in coding agents. Package exports and consumer Tailwind configuration are the ordered fallback when the broker is unavailable. +- **Baseline delivery** needs no MCP server: the installed package types in the consumer repo, the hosted Storybook components manifests ([React](https://metamask.github.io/metamask-design-system/manifests/components.json), [React Native](https://metamask.github.io/metamask-design-system/react-native/manifests/components.json)), and the consumer Tailwind configuration. Installed types win when sources disagree, because the manifests track MMDS `main`. +- **Storybook MCP** (`storybook-broker-mcp`, via Context Forge) is optional. Agents may use it when it is already configured and responds, but skills must never depend on it. ## Foundation workstreams 1. **Thin consumer gateway skills** — Refine `ui-development` overlays into stable routers (Create + Review) instead of static component inventories. -2. **Storybook MCP integration** — Treat Context Forge's preconfigured `storybook-broker-mcp` as the primary source; document explicit fallback behavior without embedding setup instructions in skills. -3. **MMDS source and broker readiness** — Keep Storybook documentation, exports, and broker surfaces current for agent consumption. +2. **No-MCP baseline** — Keep the installed types, hosted components manifests, and consumer Tailwind configuration sufficient on their own. Storybook MCP is an optional extra, never a prerequisite, and skills carry no setup instructions for it. +3. **MMDS source readiness** — Keep type JSDoc (including `@deprecated`), READMEs, stories, and the published components manifests current for agent consumption. 4. **Automated alignment checks** — Extend CI and review workflows to validate tokens, components, and patterns where feasible. 5. **Provider-side skills** — Add a `design-system` domain and `metamask-design-system` maintainer overlay in the skills repo. @@ -51,7 +52,7 @@ One knowledge layer, three deliveries. MMDS holds tokens, components, and patter | Horizon | Focus | | :---- | :---- | -| **Now** | Build the shared knowledge path and establish quality baselines — point MetaMask Skills to Storybook MCP, begin Figma/React/React Native alignment, expand Code Connect, audit MMDS and product usage. | +| **Now** | Build the shared knowledge path and establish quality baselines — point MetaMask Skills at installed types and the hosted components manifests, begin Figma/React/React Native alignment, expand Code Connect, audit MMDS and product usage. | | **Next** | Operationalize Create, Review, and Audit in normal delivery — enable the gateway by default in Extension and Mobile, run MMDS-focused review, load relevant skills per UI PR, run recurring audits. | | **Later** | Build a trusted feedback and remediation loop — agents create issues or PRs for alignment gaps, automate low-risk fixes, retain human approval for judgment calls. | @@ -59,6 +60,7 @@ One knowledge layer, three deliveries. MMDS holds tokens, components, and patter - MMDS is the default; intentional documented custom UI is allowed. - Skills are gateways and durable constraints, not second copies of MMDS documentation. -- Do not maintain static component inventories, deprecated-name lists, or MCP setup instructions in consumer skills. -- Query current Storybook guidance before choosing components, APIs, tokens, or patterns. -- When Storybook MCP is unavailable, follow the ordered fallback defined in the relevant repo overlay. +- Skills must work with no MCP server connected. +- Do not put release-pinned inventories, version pins, prop lists, or MCP setup instructions in skills. Point at the installed package and the hosted manifests instead. +- Read the installed types before choosing components, APIs, tokens, or patterns. When anything disagrees with them, the installed types win. +- Inline only durable rules that agents need and cannot read elsewhere (for example Active Design Brain rules). Link to Proposed rules; do not enforce them. diff --git a/domains/ui/skills/ui-development/references/reading-mmds-manifests.md b/domains/ui/skills/ui-development/references/reading-mmds-manifests.md new file mode 100644 index 00000000..1f8742e0 --- /dev/null +++ b/domains/ui/skills/ui-development/references/reading-mmds-manifests.md @@ -0,0 +1,70 @@ +# Reading the MMDS components manifests + +MMDS publishes a Storybook components manifest for each platform: + +| Platform | Manifest | +| --- | --- | +| React (Extension) | | +| React Native (Mobile) | | + +This file explains the format. It deliberately lists no components, versions, +or props: read those from the manifest and the installed package. + +## What the manifest is + +- It is generated from the Storybook build of MMDS `main`. It can be ahead of + the version a consumer repo installs, so a component or prop in the manifest + may not exist in `node_modules` yet. The installed types decide. +- It is large (around 1 MB). Fetch it once and search it, for example with + `curl -s | jq` or a script, rather than reading it whole. +- It is a discovery and deprecation index, not the API contract. + +## Shape + +```text +{ + "v": , + "components": { + "": { + "id": "", + "name": "", + "path": "", + "import": "", + "jsDocTags": { "deprecated": [""] }, + "reactDocgen": { "description": "...", "props": { "": { "required": ..., "tsType": ..., "description": "..." } } }, + "stories": [ { "id": "...", "name": "...", "snippet": "" } ], + "description": "", + "error": "" + } + }, + "meta": { ... } +} +``` + +Entries are keyed by Storybook story id, not by component name. Some entries +are design-token or foundation pages rather than components. Match on `name`. + +## Common queries + +- **Is there a component for X?** Search `name` and `reactDocgen.description`, then confirm + the name is exported by the installed package index. +- **Is it deprecated?** A non-empty `jsDocTags.deprecated` means deprecated; + the message names the replacement. Also check for `@deprecated` in the + installed `.types` file, because the installed version may differ. +- **Which props does it take?** `reactDocgen.props` gives names, types, + `required`, and descriptions for `main`. Confirm each prop in the installed + types before using it. +- **How is it used?** `stories[].snippet` shows example JSX. + +## Caveats + +- `import` lines come from MMDS story files. They may import enums from + `@metamask/design-system-shared` or use relative paths. In product code, + import components and enums from the platform package + (`@metamask/design-system-react` or `@metamask/design-system-react-native`). +- `jsDocTags` is `{}` when a component has no tags. An entry without a + `deprecated` key is not deprecated in `main`; the installed version can + still differ. +- An entry with `error` and no `reactDocgen` has no prop data. Read the + installed types instead. +- Do not copy manifest content into a skill. It changes with every MMDS merge. diff --git a/domains/ui/skills/ui-development/skill.md b/domains/ui/skills/ui-development/skill.md index 6b89a949..faa73595 100644 --- a/domains/ui/skills/ui-development/skill.md +++ b/domains/ui/skills/ui-development/skill.md @@ -10,4 +10,203 @@ description: >- base: true --- +# MetaMask UI Development — MMDS Guidance + Strategy context: [Agentic Design System Strategy](../../knowledge/agentic-design-system-strategy.md) + +The MetaMask Design System (MMDS) is the default for MetaMask product UI. This +base holds the rules shared by every client. The repository overlay below it +adds platform paths, the styling surface, and platform-only rules. + +## When to use + +Use this skill when you create, change, restyle, or review UI in a MetaMask +client: components, screens, sheets, modals, stories, styles, tokens, themes, +and UI behavior. Use it to decide between an MMDS component, a legacy +component-library component, a feature component, and custom UI. + +Copy and wording rules live in `content-guidelines`. File layout and test +setup live in `component-scaffold` where the repository has it. + +## Sources, in order + +None of these needs an MCP server. Work from them in this order: + +1. **Installed package types (`node_modules`).** The installed version is the + contract for the code you are changing. Read the export index and the + component's `.types` file (with its JSDoc) under + `node_modules/@metamask//dist/`. Shared props and enums + come from `node_modules/@metamask/design-system-shared/dist/types/`. When the + types disagree with anything else on this list, the types win. +2. **Hosted Storybook components manifest**, for discovery, deprecation tags, + and story snippets: + - React (Extension): + - React Native (Mobile): + + The manifests are built from MMDS `main`, so they can list components or + props that are newer than the version the consumer repo installs. Confirm + anything you find there against the installed types before you use it. + Story `import` snippets sometimes pull enums from + `@metamask/design-system-shared`; in product code, import components and + enums from the platform package (`@metamask/design-system-react` or + `@metamask/design-system-react-native`). How to read a manifest: + [references/reading-mmds-manifests.md](references/reading-mmds-manifests.md). +3. **Component README at the installed version's tag.** READMEs are not in the + npm tarball. Read the installed version from + `node_modules/@metamask//package.json` (or the lockfile), then open + the README on GitHub at the per-package tag + `@metamask/@`. The package `CHANGELOG.md` compare links + use the same tag names. Never hard-code a release in code comments or + skills. +4. **Existing repository usage and stories.** Search the consumer repo for the + component or token. Treat examples as usage evidence, not as the contract. +5. **Figma MCP and Code Connect, when connected.** Use them to read the design + and its component mapping. If they are not connected, carry on without them. + +Storybook MCP or Context Forge may add pattern docs when one is already +configured and responds. It is optional: never block on it, do not install, +configure, or authenticate it, and do not mention its absence unless a +question can only be answered there. + +## Workflow + +**Create:** read the installed types (and README when behavior is unclear) +for each building block, choose the component as below, style with preset +classes and tokens, then run the review checklist on your own diff. + +**Review:** list the changed UI, check each MMDS usage against the installed +types, then report component choice, deprecated usage, tokens, typography, +and accessibility findings with file, line, evidence, and a recommended fix. +Custom UI without a recorded reason is a finding, not an automatic failure. + +## Choosing a component + +- Before building anything, check the manifest and the installed exports for + an existing component. Never invent a component, a prop, or a variant, and + never port an API from the other platform or another UI library. +- Choose in this order, and record the reason whenever you go past step 1: + 1. An MMDS component exported by the installed platform package. + 2. A non-deprecated legacy component-library component, only when MMDS does + not cover the need. + 3. An existing feature component that already solves it. + 4. Custom UI composed from MMDS primitives and tokens, with the reason + recorded in the PR or the repository's exception format. +- A component, prop, or enum value is deprecated when its installed type + carries `@deprecated` or its manifest entry has `jsDocTags.deprecated`. Do + not add new usage. Use the replacement the JSDoc names; if none is named, + keep existing usage and record the gap. +- Do not invent button or control chrome that MMDS does not have. Keep + single-select (checkmark) and multi-select (checkbox) patterns distinct. +- If an MMDS layout makes a primary data point read as secondary, compose a + stronger treatment instead of forcing the wrong primitive. + +## Foundations + +- Color, typography, shadows, and motion come from MMDS design tokens. Use + semantic tokens (`background-*`, `text-*`, `border-*`, `icon-*`, status + tokens), never hex values or raw palette names. +- Tailwind comes from the MMDS presets configured in the consumer repo. Use + only class names those presets generate. Do not use default Tailwind colors + or arbitrary values (`bg-[#...]`, `p-[15px]`, `rounded-[16px]`, + `text-[14px]`). +- Spacing and radius use the Tailwind scale through the presets: the 4px + spacing scale (`p-4`, `gap-2`) and the default radii (`rounded-lg`, + `rounded-xl`, `rounded-2xl`, `rounded-full`). MMDS has no separate spacing + or radius tokens. Never hard-code numbers such as `borderRadius: 16`. +- Meet WCAG AA contrast with semantic tokens (normal text 4.5:1, large text and + UI graphics 3:1) and check dark mode. +- Layouts must survive longer translated strings and future RTL without + clipping or overlap. Avoid fixed-width text boxes. + +## Typography + +- `FontFamily.Default` is the default for all UI text. +- Set type with `Text` and a `TextVariant`. Never hand-set font sizes, line + heights, or arbitrary text classes. +- Use `FontFamily.Hero` only for approved hero cases: the single primary + display amount of a view and marketing-moment headlines through the + platform's `MarketingMomentTitle`. Never pair Hero with a font weight, and + never use it for body text, labels, buttons, titles, or small numbers. +- Do not use `FontFamily.Accent`; it has been removed from MMDS. +- Amount inputs are not settled. See the Proposed Typography rule in the + [Design Brain](https://www.notion.so/b71f86d67d68827395a001f7ee7a8a86) and the + [MMDS reference](https://www.notion.so/40ef86d67d68811c9b31cf9992a076cd) + before choosing a face for one. + +## Incremental migration: `Legacy*` aliases + +When a file still imports a legacy component-library export whose name is also +exported by the MMDS platform package, alias the legacy import with a `Legacy` +prefix and keep the MMDS import on the plain name. Do the same for clashing +enums. Do not alias legacy names that MMDS does not export, and never put the +prefix on the MMDS import. Finishing the migration is deleting the `Legacy*` +import and its usages. The overlay gives the platform's import paths and an +example. + +## Accessibility, trust, and sheets + +These are Active rules from the Design Brain, inlined because agents in code +repositories cannot read Notion. Proposed rules are guidance only: link to +them, do not enforce them. + +- Every text field has a persistent accessible label tied to the input. A + placeholder alone is not a label. +- Meaningful icons have an accessible name. Decorative icons next to visible + text are hidden from assistive tech, so the control is not read twice. +- Icon-only buttons need an accessible name and a real action. Do not ship an + info button whose press does nothing. +- Keep the visible text in a control's accessible name (WCAG 2.5.3). Put extra + context in a hint instead of replacing visible words. +- When visible text is abbreviated, put the full term in the accessible name. +- Do not show selected, active, or unread state through color alone. Add a + non-color cue and expose the state (selected, checked, expanded) to + assistive tech. Tabs expose the tab role and the selected state. +- Do not nest interactive controls inside another link or button. Make one + primary target, or split the actions. +- Announce async errors and status changes (save, load, retry, failures) to + screen readers. The overlay gives the platform mechanism. +- Autocomplete and suggestion lists use combobox/listbox semantics and stay + reachable by keyboard. +- Full-screen overlays on top of another flow use the MMDS modal or dialog + pattern: an accessible name, dialog semantics, and focus moved into the + overlay until it is confirmed or cancelled. +- Sheets and modals must not trap keyboard or assistive-tech focus, keep + close or back available, and follow visual reading order. +- Respect reduced motion. Never rely on animation alone to show state. +- Protect trust in wallet UI: use semantic color only for real meaning; + reconcile displayed totals and units exactly; keep sensitive details off + overviews; give irreversible send, approve, and connect actions a clear + preview (asset, amount, network, recipient or permission) with proportionate + confirmation; keep back, cancel, and recovery available; never imply + certainty the system does not have. Keep available, pending, claimed, paid, + estimated, and lifetime values distinct. Never expose the Secret Recovery + Phrase or other secrets in labels. + +Full rule set, including Proposed rules: +[Design Brain — Lessons & Rules](https://www.notion.so/b71f86d67d68827395a001f7ee7a8a86). + +## MMDS agent docs + +Read these in the MMDS repository instead of copying them into skills: + +- [`AGENTS.md`](https://github.com/MetaMask/metamask-design-system/blob/main/AGENTS.md) +- [`docs/ai-agents.md`](https://github.com/MetaMask/metamask-design-system/blob/main/docs/ai-agents.md) +- [`.cursor/rules/styling.md`](https://github.com/MetaMask/metamask-design-system/blob/main/.cursor/rules/styling.md) +- [`.cursor/rules/figma-integration.md`](https://github.com/MetaMask/metamask-design-system/blob/main/.cursor/rules/figma-integration.md) +- [`.cursor/rules/component-migration.md`](https://github.com/MetaMask/metamask-design-system/blob/main/.cursor/rules/component-migration.md) + +These track MMDS `main`. Where they disagree with the installed types, the +installed types win. + +## Review checklist + +- [ ] Installed types were read for every MMDS building block; the manifest + and README were used only as supporting sources. +- [ ] MMDS was used wherever it covers the need; any step past MMDS has a + recorded reason. +- [ ] No invented components, props, or variants, and no cross-platform ports. +- [ ] No new usage of anything tagged `@deprecated`. +- [ ] Only preset-generated classes and semantic tokens; no arbitrary values. +- [ ] Typography uses `Text` + `TextVariant`; Hero only for approved cases. +- [ ] Clashing legacy imports are aliased `Legacy*`; MMDS keeps plain names. +- [ ] The accessibility and trust rules above hold for the changed UI. From 2d1ba46f77c9aa1a2a3e2d72ba12aaf98e335142 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 5 Nov 2026 20:46:10 +0000 Subject: [PATCH 2/5] docs(ui): reduce the Mobile ui-development overlay to durable platform rules Co-authored-by: George Marshall --- CHANGELOG.md | 1 + .../repos/metamask-mobile.md | 55 +- .../repos/metamask-mobile.md | 2 +- domains/ui/skills/content-guidelines/skill.md | 7 +- .../ui-development/repos/metamask-mobile.md | 523 ++++-------------- 5 files changed, 150 insertions(+), 438 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index da248184..c48d4cb1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Skip a planning brief for an unkept spike or POC and for routine CI-only work, including a workflow edit that changes how jobs or E2E launch. A CI change that is a new design, a contract or API change, or multi-surface work still stops for a brief and a spec. For a revert of a merged pull request or commit, name the target and files, then ask one yes/no, and name the risk when security or privacy is involved. Recommend a spec for a new design, a contract or API change, or multi-surface work even when the ask is fully specified or single-file; any other fully specified ask gets one line of scope and one yes/no instead of a brief, unless it touches auth, secrets, logs or exports of state, permissions, keyring or vault, migrations, or persisted state, which still gets a brief. Always read the installed skill catalog, including for reverts and fully specified asks. - Add component-view rules to `testing/mobile-testing` from the Predict flake investigation: clear call history before call-count assertions, re-query after a remount instead of holding a `findBy*` result, keep time-dependent behaviour in unit tests or target-state fixtures, require a named async boundary for any explicit `{ timeout }`, and a Fixing a Flake section (name the cause, reproduce it, prove the fix; a longer timeout alone is not a fix). - Document confirmed Appium interaction-stability patterns in `testing/mobile-testing` (`references/appium-e2e.md`): child readiness, fresh interactive queries, shared open retries, virtualized-list proof, native control state, action postconditions, and single assertions for short-lived alerts. +- Reduce the MetaMask Mobile `ui/ui-development` overlay to durable platform rules: package paths and the React Native components manifest for inventory, the twrnc styling surface, `ButtonBase twClassName={(pressed) => ...}` for pressed state, sheet rules (`ScrollView` from `react-native-gesture-handler`, nested sheets, one `Toaster`), React Native accessibility mechanics, and the `Legacy*` alias paths. Drop the component lists, prop inventories, and version pins. In the Mobile `ui/component-scaffold` and `ui/content-guidelines` overlays, read READMEs at the installed version's tag with no MCP, replace the removed `useElevatedSurface()` step with the MMDS `BottomSheet` surface, fix pressed-state templates, and point new strings at `locales/languages/en.json`. ### Fixed diff --git a/domains/ui/skills/component-scaffold/repos/metamask-mobile.md b/domains/ui/skills/component-scaffold/repos/metamask-mobile.md index 1ed4cc87..54a3bc72 100644 --- a/domains/ui/skills/component-scaffold/repos/metamask-mobile.md +++ b/domains/ui/skills/component-scaffold/repos/metamask-mobile.md @@ -50,7 +50,7 @@ Two systems coexist in the repo: ### Layer 1 — `@metamask/design-system-react-native` (MMDS, npm package) ✅ Always use first -The only correct choice for new component primitives. Exports `Box`, `Text`, `BottomSheet`, `BottomSheetHeader`, `BottomSheetFooter`, `ButtonBase`, `ButtonIcon`, `HeaderStandard`, `Icon`, `Skeleton`, `Tag`, and more. +The only correct choice for new component primitives (`Box`, `Text`, `Button`, `ButtonBase`, `Icon`, `ListItem`, `BottomSheet`, and more). For the current inventory and deprecations, use the [React Native components manifest](https://metamask.github.io/metamask-design-system/react-native/manifests/components.json) as described in `ui-development`. Before writing any UI, verify what the installed version actually exports — the package changes frequently: @@ -64,7 +64,15 @@ node_modules/@metamask/design-system-react-native/dist/components/index.d.ts node_modules/@metamask/design-system-react-native/src/components/index.ts ``` -The installed package is the source of truth, not this skill's examples. +The installed package is the source of truth, not this skill's examples. Read props from `dist/components//.types.d.cts`. For behavior and examples, read the component README on GitHub at the release tag (READMEs are not in the npm package): + +``` +https://github.com/MetaMask/metamask-design-system/blob/@metamask/design-system-react-native@/packages/design-system-react-native/src/components//README.md +``` + +Take `` from `node_modules/@metamask/design-system-react-native/package.json`. When a README and the types disagree, the types win. No MCP server is needed; if Storybook MCP happens to be configured it is optional, so never block scaffolding on it. + +Do not add new usage of anything marked `@deprecated` in the installed types or in the manifest's `jsDocTags.deprecated`. For local visual testing of a component in isolation, see `docs/readme/storybook.md`. @@ -128,12 +136,25 @@ import { ### Interactive / pressed state +`ButtonBase` takes pressed styling through a `twClassName` function. Its `style` prop is a plain style, so a `style={({ pressed }) => ...}` function does not type-check: + +```tsx + `w-full ${pressed ? 'bg-pressed' : 'bg-default'}`} +> + {strings('foo.action')} + +``` + +For a custom pressable container, use `Pressable` with `tw.style()`: + ```tsx import { useTailwind } from '@metamask/design-system-twrnc-preset'; const tw = useTailwind(); - tw.style('w-full flex-row items-center', pressed && 'bg-pressed') } @@ -144,14 +165,13 @@ const tw = useTailwind(); ## 5. Bottom sheet specifics -1. Apply `useElevatedSurface()` to the `BottomSheet`'s `twClassName` — required for pure-black theme support: +1. Use the MMDS `BottomSheet`, `BottomSheetHeader`, and `BottomSheetFooter`. The sheet surface already uses the `bg-elevated1` token, so do not add a surface class unless the design puts the sheet on a different surface (then pass a token class such as `twClassName="bg-default"`): ```tsx -import { useElevatedSurface } from '../../../../util/theme/themeUtils'; // adjust depth - -const surfaceClass = useElevatedSurface(); - - + + {strings('foo.title')} + {/* content */} + ``` 2. `ScrollView` inside a `BottomSheet` must come from `react-native-gesture-handler` — the standard React Native `ScrollView` will not scroll on Android inside a gesture-managed bottom sheet: @@ -176,7 +196,7 @@ import { strings } from '../../../../locales/i18n'; // adjust depth to file loca {strings('namespace.key')} ``` -Add new keys to `app/locales/languages/en.json` only. Crowdin picks up new English strings automatically after the PR merges — do not edit other language files manually. +Add new keys to `locales/languages/en.json` (repo root) only. Crowdin picks up new English strings automatically after the PR merges — do not edit other language files manually. --- @@ -187,7 +207,7 @@ Add new keys to `app/locales/languages/en.json` only. Crowdin picks up new Engli Example from `app/components/UI/Compliance/AccessRestrictedModal/AccessRestrictedModal.tsx`: ```tsx import { strings } from '../../../../../locales/i18n'; -import { useElevatedSurface } from '../../../../util/theme/themeUtils'; +import { AccessRestrictedModalProps } from './AccessRestrictedModal.types'; ``` --- @@ -255,13 +275,11 @@ testID={`${FooSelectorsIDs.ITEM}-${item.id}`} ```tsx import React from 'react'; import { - Box, ButtonBase, Text, TextColor, TextVariant, } from '@metamask/design-system-react-native'; -import { useTailwind } from '@metamask/design-system-twrnc-preset'; import { strings } from '../../../../locales/i18n'; // adjust depth import { FooProps } from './Foo.types'; import { FooSelectorsIDs } from './Foo.testIds'; @@ -271,17 +289,13 @@ const Foo: React.FC = ({ onClose, testID = FooSelectorsIDs.CONTAINER, }) => { - const tw = useTailwind(); - if (!isVisible) return null; return ( - tw.style('px-4 pb-6', pressed && 'bg-pressed') - } + twClassName={(pressed) => `px-4 pb-6 ${pressed ? 'bg-pressed' : ''}`} > = ({ export default Foo; ``` -> For a bottom sheet, add `useElevatedSurface()` — see Section 5. +> For a bottom sheet, use the MMDS `BottomSheet` components — see Section 5. ### `index.ts` @@ -385,6 +399,7 @@ Re-exports point at the component's own `index.ts` (`./Foo`), not directly at th - [ ] `Foo.test.tsx` — colocated (not in `__tests__/`); testIds via constant (never raw strings); `toBeOnTheScreen()` for presence, `.toBeNull()` for absence; `beforeEach(jest.clearAllMocks)` - [ ] ESLint and TypeScript pass: no `any`, no `eslint-disable`, no import fence violations - [ ] If consumed outside the folder: parent feature `index.ts` updated (Section 10) -- [ ] If bottom sheet: `useElevatedSurface()` applied; `ScrollView` from `react-native-gesture-handler` +- [ ] If bottom sheet: MMDS `BottomSheet` components, no extra surface class unless the design needs one; `ScrollView` from `react-native-gesture-handler` +- [ ] No new usage of `@deprecated` MMDS APIs; pressed styles via `ButtonBase twClassName={(pressed) => ...}` or `Pressable` + `tw.style()` - [ ] All user-visible strings use `strings()` — no hardcoded copy diff --git a/domains/ui/skills/content-guidelines/repos/metamask-mobile.md b/domains/ui/skills/content-guidelines/repos/metamask-mobile.md index 6e356b6c..21b7cbff 100644 --- a/domains/ui/skills/content-guidelines/repos/metamask-mobile.md +++ b/domains/ui/skills/content-guidelines/repos/metamask-mobile.md @@ -5,7 +5,7 @@ parent: content-guidelines # MetaMask Content Design Guidelines — Mobile -> Source of truth: MetaMask Content Design Style Guide (internal). This skill encodes the decisions from that guide for AI-assisted development. For component-specific copy rules, read the relevant component README in MetaMask/metamask-design-system via Storybook MCP. +> Source of truth: MetaMask Content Design Style Guide (internal). This skill encodes the decisions from that guide for AI-assisted development. For component-specific copy rules, read the "Content guidelines" section of the component README in MetaMask/metamask-design-system on GitHub, at the tag `@metamask/design-system-react-native@` (`packages/design-system-react-native/src/components//README.md`). No MCP server is needed. --- diff --git a/domains/ui/skills/content-guidelines/skill.md b/domains/ui/skills/content-guidelines/skill.md index 68cd75b1..a9402b07 100644 --- a/domains/ui/skills/content-guidelines/skill.md +++ b/domains/ui/skills/content-guidelines/skill.md @@ -12,8 +12,11 @@ maturity: stable Encodes the MetaMask Content Design Style Guide for AI-assisted development. Covers capitalization, punctuation, voice and tone, and the approved -terminology list. For component-specific copy rules, refer to the relevant -component README in MetaMask/metamask-design-system (exposed via Storybook MCP). +terminology list. For component-specific copy rules, read the "Content +guidelines" section of the relevant component README in +[MetaMask/metamask-design-system](https://github.com/MetaMask/metamask-design-system) +on GitHub, at the tag that matches the installed package version. No MCP +server is needed. ## When to use diff --git a/domains/ui/skills/ui-development/repos/metamask-mobile.md b/domains/ui/skills/ui-development/repos/metamask-mobile.md index 1c9e4fb9..ff5f0247 100644 --- a/domains/ui/skills/ui-development/repos/metamask-mobile.md +++ b/domains/ui/skills/ui-development/repos/metamask-mobile.md @@ -3,433 +3,126 @@ repo: metamask-mobile parent: ui-development --- - -# MetaMask Mobile React Native UI Development Guidelines - -## Core Principle - -Always prioritize @metamask/design-system-react-native components and Tailwind CSS patterns over custom implementations. - -## Component Hierarchy (STRICT ORDER) - -### The Rule: Check Design System First -**Before writing any new component or choosing what to use, ask: "Does @metamask/design-system-react-native have this?"** - -1. **FIRST**: Use `@metamask/design-system-react-native` components - - **Availability is dynamic**: read the installed package export index before deciding a component is unavailable. - - **Rule**: If the installed package exports the component, you MUST use it. - - **Common examples**: Box/BoxRow/BoxColumn, Text, Button/ButtonBase/ButtonIcon/TextButton, Icon, Checkbox, Avatar variants, Badge variants, BottomSheet components, HeaderBase/HeaderRoot/HeaderSearch/HeaderStandard, Input/TextField/TextFieldSearch/Label, ListItem, Skeleton, Tag, Toast. - -2. **SECOND**: Use `app/component-library` ONLY if design system lacks it - - **Use for**: MetaMask-specific or not-yet-migrated components such as Tabs and feature-owned modal wrappers - - **Rule**: These are MetaMask-specific implementations not (yet) in the design system - - **Important**: component-library components should themselves use design system primitives internally - -3. **THIRD**: Feature-specific components - - **Use for**: Complex, domain-specific UI that combines multiple design system/component-library components - - **Examples**: `BridgeInputSelector`, `StakeInputView`, `NFTDetailsModal` - - **Rule**: Must be built using Box, Text, and other design system primitives - NO raw View/Text or StyleSheet - - **Reuse**: Search for existing feature components before building new ones to avoid duplication - -4. **LAST RESORT**: Custom components with StyleSheet - - **Only when**: Highly specialized one-off needs with no design system equivalent AND no component-library equivalent - - **Requires**: Strong justification why design system primitives can't be composed - -### Decision Tree -``` -Need a component? - ├─ Does the installed @metamask/design-system-react-native package export it? - │ └─ YES → Use @metamask/design-system-react-native [STOP] - │ - ├─ Is it Tabs or a MetaMask-specific component that is not exported by @metamask/design-system-react-native? - │ └─ YES → Use app/component-library [STOP] - │ - ├─ Is it feature-specific UI (e.g., BridgeInputSelector, StakeInputView)? - │ ├─ Does it already exist? (search codebase for similar components) - │ │ ├─ YES → Reuse existing component [STOP] - │ │ └─ NO → Build new component using design system primitives [STOP] - │ └─ - │ - └─ Can I compose it from Box + Text + other primitives? - ├─ YES → Compose from design system [STOP] - └─ NO → Consider if custom implementation is truly necessary -``` - -### Why This Hierarchy Matters -- **Consistency**: Design system ensures consistent look, feel, and behavior -- **Maintenance**: Centralized updates benefit all consumers -- **Accessibility**: Design system components include a11y best practices -- **Performance**: Optimized implementations tested at scale -- **Type Safety**: Full TypeScript support with JSDoc documentation - -## Required Availability Check - -Before choosing `app/component-library` or building custom UI, inspect the consumer repo's installed package. Do not rely on this skill's examples as a complete component list. - -1. Check `node_modules/@metamask/design-system-react-native/dist/components/index.d.cts` for exported components and enums. -2. If the package uses a different build layout, inspect `node_modules/@metamask/design-system-react-native/dist/components/index.d.ts` or `node_modules/@metamask/design-system-react-native/src/components/index.ts`. -3. Read the matching component type file before writing usage, for example `dist/components//.types.d.cts`. -4. If `node_modules` is unavailable, check `package.json`/lockfile for the installed package version and inspect the package source only as a fallback. - -This lookup costs a few file reads, but it prevents stale skill guidance from overriding the version actually installed in Mobile. The package version is the source of truth. - -## Required Imports for React Native - -```tsx -// ALWAYS prefer these imports -import { useTailwind } from '@metamask/design-system-twrnc-preset'; -import { - Box, - Text, - Button, - ButtonBase, - ButtonFilter, - ButtonHero, - ButtonIcon, - ButtonSemantic, - BottomSheet, - BottomSheetDialog, - BottomSheetHeader, - BottomSheetFooter, - BottomSheetOverlay, - ButtonsAlignment, - HeaderBase, - HeaderRoot, - HeaderSearch, - HeaderStandard, - Icon, - IconAlert, - MainActionButton, - TabEmptyState, - TextVariant, - FontWeight, - ButtonBaseSize, - TitleHub, - TitleStandard, - TitleSubpage, - BoxFlexDirection, - BoxAlignItems, - BoxJustifyContent, - // ... other design system components -} from '@metamask/design-system-react-native'; -``` - -## Component Documentation Access - -### Type Definitions & JSDoc Comments -All @metamask/design-system-react-native components have comprehensive TypeScript definitions with JSDoc comments: - -- **Box**: `/node_modules/@metamask/design-system-react-native/dist/components/Box/Box.types.d.cts` -- **Text**: `/node_modules/@metamask/design-system-react-native/dist/components/Text/Text.types.d.cts` -- **Button**: `/node_modules/@metamask/design-system-react-native/dist/components/Button/Button.types.d.cts` -- **BottomSheet**: `/node_modules/@metamask/design-system-react-native/dist/components/BottomSheet/BottomSheet.types.d.cts` -- **BottomSheetDialog**: `/node_modules/@metamask/design-system-react-native/dist/components/BottomSheetDialog/BottomSheetDialog.types.d.cts` -- **BottomSheetHeader**: `/node_modules/@metamask/design-system-react-native/dist/components/BottomSheetHeader/BottomSheetHeader.types.d.cts` -- **BottomSheetFooter**: `/node_modules/@metamask/design-system-react-native/dist/components/BottomSheetFooter/BottomSheetFooter.types.d.cts` -- **HeaderBase**: `/node_modules/@metamask/design-system-react-native/dist/components/HeaderBase/HeaderBase.types.d.cts` -- **HeaderSearch**: `/node_modules/@metamask/design-system-react-native/dist/components/HeaderSearch/HeaderSearch.types.d.cts` -- **HeaderStandard**: `/node_modules/@metamask/design-system-react-native/dist/components/HeaderStandard/HeaderStandard.types.d.cts` - -When unsure about component APIs: -1. Read the `.types.d.cts` files for complete prop documentation -2. Reference `app/component-library/components/design-system.stories.tsx` for usage examples -3. Check GitHub source: https://github.com/MetaMask/metamask-design-system/tree/main/packages/design-system-react-native/src/components - -### Box Component Quick Reference -- **Spacing**: Use `gap`, `padding*`, `margin*` props with values 0-12 (maps to 0px-48px) -- **Flexbox**: Use `flexDirection`, `alignItems`, `justifyContent` enum props -- **Colors**: Use `backgroundColor` and `borderColor` with semantic tokens -- **Tailwind**: Use `twClassName` for utilities not covered by props - -## Styling Rules (ENFORCE STRICTLY) - -### ✅ ALWAYS DO: - -- Use `const tw = useTailwind();` hook instead of importing twrnc directly -- Use `Box` component instead of `View` -- Use `Text` component with variants instead of raw Text with styles -- Use `twClassName` prop for static styles -- Use `tw.style()` function for interactive/dynamic styles -- Use design system color tokens: `bg-default`, `text-primary`, `border-muted` -- Use component props first: `variant`, `color`, `size`, etc. - -### ❌ NEVER SUGGEST: - -- `import tw from 'twrnc'` (use useTailwind hook instead) -- `StyleSheet.create()` (use Tailwind classes) -- Raw `View` or `Text` components (use Box/Text from design system) -- Arbitrary color values like `bg-[#3B82F6]` or `text-[#000000]` -- Inline style objects unless for dynamic values -- Mixing multiple styling approaches unnecessarily - -## Code Pattern Templates - -### Basic Container: - -```tsx -const MyComponent = () => { - const tw = useTailwind(); - - return ( - - Title - - ); -}; -``` - -### Flex Layout: - -```tsx - -``` - -### Interactive Element: +# MetaMask Mobile overlay + +Platform rules for React Native UI in `metamask-mobile`. The base above covers +sources, component choice, foundations, typography, migration, and the shared +accessibility rules. + +## Packages and paths + +- Components and enums: `@metamask/design-system-react-native`. Do not import + enums from `@metamask/design-system-shared`, and never import MMDS through a + repository-relative path. +- Tailwind: `@metamask/design-system-twrnc-preset`, through `useTailwind()`. +- Inventory and deprecations: the + [React Native components manifest](https://metamask.github.io/metamask-design-system/react-native/manifests/components.json), + confirmed against `node_modules/@metamask/design-system-react-native/dist/components/index.d.cts`. +- README: `packages/design-system-react-native/src/components//README.md` + at the tag `@metamask/design-system-react-native@`. +- Legacy component library: `app/component-library/` (being migrated to MMDS). + `components-temp/` holds MetaMask-specific components such as `Tabs`. +- Product UI: `app/components/`. Real MMDS usage: + `app/component-library/components/design-system.stories.tsx`. + +Do not apply React web guidance (`className`, HTML tags, `aria-*` props) to +Mobile. + +## Styling surface + +- Use `const tw = useTailwind();`. Never `import tw from 'twrnc'`. +- Use MMDS `Box` instead of `View` and MMDS `Text` instead of React Native + `Text`. +- Prefer typed props (`flexDirection`, `alignItems`, `gap`, `padding*`, + `margin*`, `backgroundColor`) over `twClassName` for layout. Use + `twClassName` for everything else: size, position, radius, opacity, + `flex-1`. `Box` has no `borderRadius` prop; use `twClassName="rounded-xl"`. +- No `StyleSheet.create()` and no new `.styles.ts` files. Inline style objects + only for truly dynamic values. +- Pressed state: `ButtonBase` takes a function in `twClassName`; its `style` + prop is a plain style, so a `style={({ pressed }) => ...}` function does not + type-check. For a custom `Pressable`, use `tw.style()` in its style function. ```tsx - tw.style( - 'w-full flex-row items-center justify-center', - pressed && 'bg-pressed', - ) - } + onPress={onPress} + twClassName={(pressed) => `w-full ${pressed ? 'bg-pressed' : 'bg-default'}`} > - Button Text + {label} -``` - -### Pressable with Tailwind: -```tsx - tw.style( - 'w-full flex-row items-center justify-between px-4 py-2', - pressed && 'bg-pressed', - ) - } -> + style={({ pressed }) => tw.style('w-full flex-row', pressed && 'bg-pressed')} +/> ``` -### Bottom Sheet: +- Interactive list rows use MMDS `ListItem` with its interactive mode and the + pressed background token. Keep the row's own horizontal padding; do not add + an outer pad that double-indents it. + +## Sheets + +- Use the MMDS `BottomSheet`, `BottomSheetHeader`, and `BottomSheetFooter`. The + sheet already sets its surface token; override it only when the design puts + the sheet on another surface. +- A `ScrollView` inside a `BottomSheet` must come from + `react-native-gesture-handler`. The React Native `ScrollView` does not scroll + inside the sheet on Android. +- Use the full sheet composition (overlay, container, header, footer, safe + areas, drag, keyboard, accessibility). In nested sheet flows, keep the + container stable, move content directionally, use back for the child and + close for the whole flow, and avoid two systems fighting over height or drag. +- Mobile mounts one `` in `app/components/Nav/App/ToasterOverlay`. + Call `toast()`; do not render another `Toaster`. + +## Mobile accessibility mechanics + +- Custom pressables set `accessibilityRole`, `accessibilityState` (`disabled`, + `selected`, `checked`, `busy`, `expanded`), and an `accessibilityLabel` or + `accessibilityHint` when visible text is not enough. Prefer MMDS controls over + a `View` with `onPress`. +- Headings (`HeadingLg/Md/Sm`, `PageHeading`, `SectionHeading`, `Display*`) pass + `accessibilityRole="header"`; MMDS `Text` defaults to `"text"`. A tappable + heading is a button. +- Tabs use `accessibilityRole="tab"` with `accessibilityState={{ selected }}`. +- Announce async errors and status with + `AccessibilityInfo.announceForAccessibility` (iOS VoiceOver ignores + `accessibilityLiveRegion`), and give inline error text + `accessibilityRole="alert"`. +- Touch targets are about 44×44 (minimum 24×24). Use `hitSlop` or padding when + the visible control is smaller. +- Support Dynamic Type: no fixed-height text boxes or absolute layouts that + clip or overlap as text grows. +- Wire skeletons and other motion to `useReducedMotion()` so they stop when + Reduce Motion is on. + +## Incremental migration paths + +Mobile's legacy library has no single barrel. Each component is a default +export from its own folder under `app/component-library/components/` (for +example `Texts/Text`, `Buttons/Button`, `Icons/Icon`), and there is no legacy +`Box`. Apply the base `Legacy*` rule to the default export and to clashing +named enums. `ButtonVariants` (legacy) and `ButtonVariant` (MMDS) do not clash, +so do not alias them. Adjust the `../` depth to the file. ```tsx -import { - BottomSheet, - BottomSheetFooter, - BottomSheetHeader, - ButtonsAlignment, -} from '@metamask/design-system-react-native'; - - - Title - - Content - - - +import { Text, TextVariant } from '@metamask/design-system-react-native'; +import LegacyText, { + TextVariant as LegacyTextVariant, +} from '../../../component-library/components/Texts/Text'; ``` -## Box Component Best Practices - -### Prefer Props Over twClassName for Layout -✅ **DO** - Use typed props for type safety and consistency: -```tsx - -``` - -❌ **DON'T** - Use twClassName for properties that have dedicated props: -```tsx - -``` - -### When to Use twClassName -Use `twClassName` for: -- Width and height: `w-full`, `h-20`, `w-[337px]` -- Complex positioning: `absolute`, `relative`, `top-0`, `left-0` -- Borders (partial): `rounded-lg`, `border-t` -- Shadows and opacity: `shadow-lg`, `opacity-50` -- Utilities not covered by props: `overflow-hidden`, `z-10` - -### Spacing System -- Use numeric props (0-12) for spacing: `padding={4}` = 16px -- Each unit = 4px (so 12 = 48px max) -- For custom spacing beyond 48px, use twClassName: `twClassName="p-20"` - -### Color Tokens -Always use semantic color tokens: -```tsx -// ✅ Semantic tokens - - - - -// ❌ Arbitrary colors - - -``` - -## Component Conversion Guide - -| DON'T Use | USE Instead | -| ------------------------------------ | -------------------------------------- | -| `` | `` | -| `` | `` | -| `app/component-library` BottomSheets | `@metamask/design-system-react-native` BottomSheet components | -| `StyleSheet.create()` | `twClassName="..."` | -| `style={{ backgroundColor: 'red' }}` | `twClassName="bg-error-default"` | -| `flexDirection: 'row'` | `flexDirection={BoxFlexDirection.Row}` | -| Manual padding/margin | `twClassName="p-4 m-2"` | - -## Platform-Specific Gotchas - -### ScrollView Inside BottomSheet -When using a `ScrollView` inside a `BottomSheet`, you **MUST** import `ScrollView` from `react-native-gesture-handler`, not from `react-native`. The standard React Native `ScrollView` will not scroll on Android within a gesture-handler-managed `BottomSheet`. - -```tsx -// ✅ CORRECT - works on both iOS and Android -import { ScrollView } from 'react-native-gesture-handler'; - -// ❌ WRONG - will not scroll on Android inside BottomSheet -import { ScrollView } from 'react-native'; -``` - -## Legacy Code Migration Guidelines - -### Identifying Legacy Patterns -🚫 **Anti-patterns to refactor when encountered:** -- Files using `StyleSheet.create()` -- Separate `.styles.ts` or `.styles.tsx` files -- Raw `View` components instead of `Box` -- Raw `Text` components with custom styles instead of design system `Text` with variants -- Inline style objects for static styles - -### Migration Priority -1. **High Priority**: Components being actively modified -2. **Medium Priority**: Frequently used shared components in `app/component-library` -3. **Low Priority**: Stable legacy components with no active development - -### Migration Steps -1. Replace `View` → `Box` from design system -2. Replace `Text` → `Text` with appropriate `TextVariant` -3. Replace migrated component-library imports with design system exports when available -4. Convert `StyleSheet.create()` styles → `twClassName` props or `tw.style()` -5. Convert arbitrary colors → design system color tokens -6. Delete `.styles.ts` files after migration -7. Test thoroughly - layout can shift during migration - -### Example Migration - -**Before:** -```tsx -import { View, Text, StyleSheet } from 'react-native'; - -const styles = StyleSheet.create({ - container: { - flexDirection: 'row', - alignItems: 'center', - padding: 16, - backgroundColor: '#ffffff', - }, - title: { - fontSize: 16, - fontWeight: '500', - color: '#000000', - }, -}); - - - Title - -``` - -**After:** -```tsx -import { useTailwind } from '@metamask/design-system-twrnc-preset'; -import { Box, Text, TextVariant, BoxFlexDirection, BoxAlignItems, FontWeight } from '@metamask/design-system-react-native'; - -const tw = useTailwind(); - - - - Title - - -``` - -## Error Prevention & Code Review Checklist - -### Before Committing Code, Verify: -- [ ] No `import tw from 'twrnc'` (must use `useTailwind()` hook) -- [ ] No raw `View` components (use `Box`) -- [ ] No raw `Text` without variants (use `Text` with `TextVariant`) -- [ ] No component-library BottomSheet imports when design system exports cover the use case -- [ ] No `StyleSheet.create()` (use `twClassName` or `tw.style()`) -- [ ] No arbitrary color values (use design system tokens) -- [ ] No separate `.styles.ts` files for new components -- [ ] Component props used before `twClassName` for layout -- [ ] Interactive styles use `tw.style()` with state functions -- [ ] `ScrollView` inside `BottomSheet` imported from `react-native-gesture-handler` (not `react-native`) - -### When You See These Patterns, IMMEDIATELY Suggest Alternatives: -- Any `import tw from 'twrnc'` → `import { useTailwind } from '@metamask/design-system-twrnc-preset'` -- Any `View` component → `Box` from design system -- Any `app/component-library/components/BottomSheets/*` import → equivalent `@metamask/design-system-react-native` BottomSheet export -- Any `StyleSheet` usage → Tailwind classes -- Any arbitrary color values → Design system tokens -- Any manual flex properties → Box component props + twClassName - -### AI Agent Guidelines -When suggesting code changes: -1. ALWAYS read component type definitions first for accurate API usage -2. ALWAYS check `design-system.stories.tsx` for real-world patterns -3. ALWAYS search for existing feature-specific components before building new ones (use Glob/Grep to find similar components in feature directories) -4. REJECT any suggestions that violate the hierarchy -5. SUGGEST migrations when encountering legacy patterns -6. EXPLAIN why design system approach is preferred - -## Design System Priority - -Before suggesting any UI solution: - -1. Check if `@metamask/design-system-react-native` has the component -2. Use component's built-in props (variant, color, size) -3. Add layout/spacing with `twClassName` -4. Add interactions with `tw.style()` -5. Only suggest component-library or custom components if design system lacks it - -## Reference Examples - -Always reference the patterns from `app/component-library/components/design-system.stories.tsx` for proper usage examples. - -## Enforcement +Prop mapping from legacy to MMDS components: +[React Native `MIGRATION.md`](https://github.com/MetaMask/metamask-design-system/blob/main/packages/design-system-react-native/MIGRATION.md). -- REJECT any code suggestions that use StyleSheet.create() -- REJECT raw View/Text usage when Box/Text components exist -- REQUIRE useTailwind hook for all Tailwind usage -- REQUIRE design system components as first choice -- ENFORCE design token usage over arbitrary values +## Mobile checklist -@app/component-library/components/design-system.stories.tsx +- [ ] `useTailwind()`; no `twrnc` import, `StyleSheet.create()`, or new + `.styles.ts`. +- [ ] MMDS `Box` and `Text` instead of `View` and React Native `Text`. +- [ ] Pressed styles via `ButtonBase twClassName={(pressed) => ...}` or + `Pressable` + `tw.style()`. +- [ ] `ScrollView` inside a `BottomSheet` comes from + `react-native-gesture-handler`. +- [ ] Controls, headings, and tabs expose roles and state; errors are + announced; touch targets are large enough. From 6868dbc744c1ec442e90a5ea97f9b0e171a1840c Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 5 Nov 2026 22:46:06 +0000 Subject: [PATCH 3/5] docs(ui): apply eval fixes to the shared ui-development base Deprecation replacements must be exported on the installed platform, Legacy* aliases trigger only on same-file clashes, cross-skill pointers are qualified, the Strategy link survives install, and the manifest reference explains non-component entries. Co-authored-by: George Marshall --- .../references/reading-mmds-manifests.md | 15 ++++++++++++-- domains/ui/skills/ui-development/skill.md | 20 ++++++++++--------- 2 files changed, 24 insertions(+), 11 deletions(-) diff --git a/domains/ui/skills/ui-development/references/reading-mmds-manifests.md b/domains/ui/skills/ui-development/references/reading-mmds-manifests.md index 1f8742e0..29a7eb00 100644 --- a/domains/ui/skills/ui-development/references/reading-mmds-manifests.md +++ b/domains/ui/skills/ui-development/references/reading-mmds-manifests.md @@ -41,8 +41,19 @@ or props: read those from the manifest and the installed package. } ``` -Entries are keyed by Storybook story id, not by component name. Some entries -are design-token or foundation pages rather than components. Match on `name`. +Entries are keyed by Storybook story id, not by component name, and not every +entry is a component: + +- Design-token and foundation pages (ids starting `design-tokens-` or `docs-`, + with story paths in `packages/design-tokens`). +- Pattern stories (ids starting `patterns-`). These are composition examples. + Their `name` can look like a component (for example `MarketingMoment`) or + be a code fragment (`() => null`), and nothing with that name is exported. +- Story wrappers whose `name` is a cast expression rather than an identifier. + +Treat an entry as a component only when its `path` is a story under the +platform package's `src/components/` (including `temp-components/`) and the +component name appears in the installed export index. ## Common queries diff --git a/domains/ui/skills/ui-development/skill.md b/domains/ui/skills/ui-development/skill.md index faa73595..bc9986f5 100644 --- a/domains/ui/skills/ui-development/skill.md +++ b/domains/ui/skills/ui-development/skill.md @@ -12,7 +12,7 @@ base: true # MetaMask UI Development — MMDS Guidance -Strategy context: [Agentic Design System Strategy](../../knowledge/agentic-design-system-strategy.md) +Strategy context: [Agentic Design System Strategy](https://github.com/MetaMask/skills/blob/main/domains/ui/knowledge/agentic-design-system-strategy.md) The MetaMask Design System (MMDS) is the default for MetaMask product UI. This base holds the rules shared by every client. The repository overlay below it @@ -25,8 +25,8 @@ client: components, screens, sheets, modals, stories, styles, tokens, themes, and UI behavior. Use it to decide between an MMDS component, a legacy component-library component, a feature component, and custom UI. -Copy and wording rules live in `content-guidelines`. File layout and test -setup live in `component-scaffold` where the repository has it. +Copy and wording rules live in `content-guidelines`, and file layout and test +setup live in `component-scaffold`, where the repository has them. ## Sources, in order @@ -93,8 +93,9 @@ Custom UI without a recorded reason is a finding, not an automatic failure. recorded in the PR or the repository's exception format. - A component, prop, or enum value is deprecated when its installed type carries `@deprecated` or its manifest entry has `jsDocTags.deprecated`. Do - not add new usage. Use the replacement the JSDoc names; if none is named, - keep existing usage and record the gap. + not add new usage. Use the replacement the JSDoc names if the installed + platform package exports it. If no replacement is named, or the named one is + not exported on this platform, keep existing usage and record the gap. - Do not invent button or control chrome that MMDS does not have. Keep single-select (checkmark) and multi-select (checkbox) patterns distinct. - If an MMDS layout makes a primary data point read as secondary, compose a @@ -135,10 +136,11 @@ Custom UI without a recorded reason is a finding, not an automatic failure. ## Incremental migration: `Legacy*` aliases -When a file still imports a legacy component-library export whose name is also -exported by the MMDS platform package, alias the legacy import with a `Legacy` -prefix and keep the MMDS import on the plain name. Do the same for clashing -enums. Do not alias legacy names that MMDS does not export, and never put the +When a file imports the same name from a legacy source and from the MMDS +platform package, alias the legacy import with a `Legacy` prefix and keep the +MMDS import on the plain name. Do the same for clashing enums. Leave legacy +imports that have no MMDS counterpart in the same file on their plain names +until that step migrates them. Do not alias legacy names that MMDS does not export, and never put the prefix on the MMDS import. Finishing the migration is deleting the `Legacy*` import and its usages. The overlay gives the platform's import paths and an example. From 02bb1c2b8e3c2aec09d3faf4277906c4b3b3d404 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 5 Nov 2026 22:46:16 +0000 Subject: [PATCH 4/5] docs(ui): apply eval fixes to the Mobile ui-development overlay Explain retiring useStyles + .styles.ts pairs, note that interactive ListItem applies its own pressed background, and point at MarketingMomentTitle. Co-authored-by: George Marshall --- .../ui/skills/ui-development/repos/metamask-mobile.md | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/domains/ui/skills/ui-development/repos/metamask-mobile.md b/domains/ui/skills/ui-development/repos/metamask-mobile.md index ff5f0247..4a0a0452 100644 --- a/domains/ui/skills/ui-development/repos/metamask-mobile.md +++ b/domains/ui/skills/ui-development/repos/metamask-mobile.md @@ -24,6 +24,7 @@ accessibility rules. `components-temp/` holds MetaMask-specific components such as `Tabs`. - Product UI: `app/components/`. Real MMDS usage: `app/component-library/components/design-system.stories.tsx`. +- Marketing-moment headlines: `app/components/UI/MarketingMomentTitle`. Do not apply React web guidance (`className`, HTML tags, `aria-*` props) to Mobile. @@ -39,6 +40,9 @@ Mobile. `flex-1`. `Box` has no `borderRadius` prop; use `twClassName="rounded-xl"`. - No `StyleSheet.create()` and no new `.styles.ts` files. Inline style objects only for truly dynamic values. +- When you change a component that uses `useStyles` with a `*.styles.ts` + file, move its styles to typed `Box` props or `twClassName`, then delete the + styles file and the `useStyles` call. - Pressed state: `ButtonBase` takes a function in `twClassName`; its `style` prop is a plain style, so a `style={({ pressed }) => ...}` function does not type-check. For a custom `Pressable`, use `tw.style()` in its style function. @@ -56,9 +60,10 @@ Mobile. /> ``` -- Interactive list rows use MMDS `ListItem` with its interactive mode and the - pressed background token. Keep the row's own horizontal padding; do not add - an outer pad that double-indents it. +- Interactive list rows use MMDS `ListItem` with `isInteractive`, which + applies the pressed background itself; do not add `bg-pressed` again. Keep + the row's own horizontal padding; do not add an outer pad that + double-indents it. ## Sheets From 82f57ebcde9d8ab1be21360d9f8b855428db7150 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Fri, 6 Nov 2026 22:58:17 +0000 Subject: [PATCH 5/5] docs(changelog): remove ui-development entries from the Mobile PR Skill reshape changes are not release-note material for this PR. Co-authored-by: George Marshall --- CHANGELOG.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c48d4cb1..6915cacb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,7 +19,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed -- Expand the shared `ui/ui-development` base so it works with no MCP server: a source order (installed package types win, then the hosted Storybook components manifests, the component README at the installed version's tag, repository usage, and Figma MCP with Code Connect when connected), component choice and deprecation rules, foundations, durable typography rules, the `Legacy*` alias rule for incremental migrations, and the Active accessibility, trust, and sheet rules from the Design Brain. Add `references/reading-mmds-manifests.md`, and update the agentic design system strategy so Storybook MCP is optional and skills carry no release-pinned inventories. - Update the `testing/visual-testing` skill for `@metamask/client-mcp-core` 0.13.1: remove the knowledge store commands and the mandatory knowledge-reuse step, and document the global `--observe` and `--pretty` flags, one-line JSON output, observations without app state, `run-steps` `observe` (replaces `includeObservations`), timed-out calls being cancelled, and one element per ref. Replace the removed `yarn build:test:webpack` script with `yarn build:test`. - Update the MetaMask Mobile performance overlay and animation references for React Native 0.86.x, Expo SDK 57, and Reanimated 4.x with `react-native-worklets`. From a worklet, call `scheduleOnRN` (`runOnJS` is a deprecated alias). Static-ESM tree-shaking stays off. ([#203](https://github.com/MetaMask/skills/pull/203)) - Require every revert yes/no to be one line that names the pull request or commit, the files, and exactly one yes/no question, whether or not security or privacy is involved. A shorter message is not that step. @@ -27,7 +26,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Skip a planning brief for an unkept spike or POC and for routine CI-only work, including a workflow edit that changes how jobs or E2E launch. A CI change that is a new design, a contract or API change, or multi-surface work still stops for a brief and a spec. For a revert of a merged pull request or commit, name the target and files, then ask one yes/no, and name the risk when security or privacy is involved. Recommend a spec for a new design, a contract or API change, or multi-surface work even when the ask is fully specified or single-file; any other fully specified ask gets one line of scope and one yes/no instead of a brief, unless it touches auth, secrets, logs or exports of state, permissions, keyring or vault, migrations, or persisted state, which still gets a brief. Always read the installed skill catalog, including for reverts and fully specified asks. - Add component-view rules to `testing/mobile-testing` from the Predict flake investigation: clear call history before call-count assertions, re-query after a remount instead of holding a `findBy*` result, keep time-dependent behaviour in unit tests or target-state fixtures, require a named async boundary for any explicit `{ timeout }`, and a Fixing a Flake section (name the cause, reproduce it, prove the fix; a longer timeout alone is not a fix). - Document confirmed Appium interaction-stability patterns in `testing/mobile-testing` (`references/appium-e2e.md`): child readiness, fresh interactive queries, shared open retries, virtualized-list proof, native control state, action postconditions, and single assertions for short-lived alerts. -- Reduce the MetaMask Mobile `ui/ui-development` overlay to durable platform rules: package paths and the React Native components manifest for inventory, the twrnc styling surface, `ButtonBase twClassName={(pressed) => ...}` for pressed state, sheet rules (`ScrollView` from `react-native-gesture-handler`, nested sheets, one `Toaster`), React Native accessibility mechanics, and the `Legacy*` alias paths. Drop the component lists, prop inventories, and version pins. In the Mobile `ui/component-scaffold` and `ui/content-guidelines` overlays, read READMEs at the installed version's tag with no MCP, replace the removed `useElevatedSurface()` step with the MMDS `BottomSheet` surface, fix pressed-state templates, and point new strings at `locales/languages/en.json`. ### Fixed