Skip to content

Map GoodWalletV2 UI components to GoodWidget Tamagui primitives #4

Description

@L03TJ3

Original prompt:

Use GitHub MCP to inspect the repository:

GoodDollar/GoodWalletV2
Context: We already have mapped, based on GoodWalletV2, the high-level design system and css into our local preset (local path: packages/ui/src/presets.ts) it also includes already claim-flow localized design definitions from GoodWalletV2. (local path: packages/claim-widget/src/ClaimWidget) and component like Drawer (local path: pacakges/ui/src/components/Drawer.tsx)

Use these as starting reference before starting to analyse and map GoodWalletV2's remaining components.

Goal: Map existing UI components from GoodWalletV2 to GoodWidget primitives using Tamagui.

Steps:

Explore GoodWalletV2 repository and identify reusable UI components
For each component:
name
purpose
usage patterns
design system: verify both what it uses from high-level design system (GoodWalletV2 path: src/ui/styles.css) and what is in GoodWalletV2 defined in a given .module.css (usually sibling file in same directory of a component).
Map each to:
Tamagui primitive (if exists)
or GoodWidget composition
Use Drawer.tsx as reference for correct pattern
Output:

structured mapping
recommended first-wave primitives
create new GitHub issue for GoodWidget

=== End of original prompt, below is the Agents contribution

Overview

This issue tracks iterative application of GoodWalletV2 design elements onto GoodWidget's existing demo primitives in packages/ui/src/components-test/ and packages/ui/src/components/.

We are not migrating all primitives at once. Instead, we start from GoodWalletV2 in-use components and add UI primitives on a per-need basis, iteratively.

Relationship to issue #3:
Issue #3 covers the first-wave migration of components-test primitives from Stack-based scaffolds to correct Tamagui-native equivalents. That migration is a pre-condition for several items below. Where a component needs issue #3 migration before GoodWalletV2 design can be cleanly applied, this is called out explicitly.

About components-test:
Files in packages/ui/src/components-test/ are scaffolded demos. They are not yet correctly mapped to the GoodWidget preset or using proper Tamagui primitives in all cases. Changes here require:

  1. Confirming or fixing the Tamagui primitive base (see issue [BOUNTY] Migrate First-Wave UI Primitives To Tamagui-Native Wrappers #3 mapping)
  2. Then applying GoodWalletV2 design values through the preset

Design System Reference

GoodWalletV2's styles.css defines two brand themes (theme-blue, theme-green) switching --token-primary between blue (#1a85ff) and green (#13c636). All semantic layers are already captured in the GoodWidget preset:

CSS token Preset key
--token-bg tokens.color.background / theme.background
--token-bg-raised tokens.color.surface / theme.backgroundHover
--token-bg-input tokens.color.backgroundInput / theme.backgroundPress
--token-bg-overlay tokens.color.backgroundOverlay / theme.backgroundOverlay
--token-primary tokens.color.primary / light_Button.background
--token-text tokens.color.text / theme.color
--token-text-muted tokens.color.textSecondary / theme.placeholderColor
--token-border tokens.color.border / theme.borderColor
--z-drawer (200) tokens.zIndex.4

Components

1. Icon — ❌ new component (no existing demo)

GoodWalletV2 source: src/ui/icon/Icon.tsx
Local file: none — needs to be created in packages/ui/src/components/
Tamagui base: no direct Tamagui Icon primitive; wrap SVG/icon-library elements using styled(Stack) or a named createComponent

What's needed:

  • Decide on icon library (react-icons as in V2, Lucide, or custom SVG set)
  • Create a named Icon component via createComponent with name: 'Icon'
  • Props: name, size (2xs2xl), color (primary/text/muted/error/success/inherit), spin, round
  • Map sizes to existing tokens.size.icon* tokens (icon2xs=12icon2xl=64)

Blocks: Button icon/list variants, Dialog close button, Toast status icons


2. Button⚠️ extend components-test/Button.tsx

GoodWalletV2 source: src/ui/button/Button.tsx + Button.module.css
Local file: packages/ui/src/components-test/Button.tsx
Current base: ButtonFrame = createComponent(Stack, { name: 'Button', ... }) — uses Stack, not Tamagui Button

Pre-condition (issue #3): Migrate ButtonFrame from Stack to Tamagui Button as its behavioral base before extending variants.

What's needed after migration:

  • Radius: V2 solid buttons use pill radius (9999px). Current default is $2 (8px). Update default to $full or $6 for brand alignment.
  • Missing variants to add:
Variant Description
pill Badge/chip style — height: 35, borderRadius: $full, uppercase 11px, muted bg + colored text
icon Icon-only, transparent bg — sizes sm/md/lg/larger (16–35px)
text Full-width text link — 14px 500w; hover changes opacity (not bg, unlike ghost)
list Icon + label row — full width, 42px min height, left-aligned
  • Text color on solid: V2 uses --black text on --main bg for green theme. Current light_Button hardcodes white. Note this theme-level distinction.
  • Loading state: V2 uses shimmer overlay. GoodWidget uses Spinner. Document this difference.

3. Separator⚠️ extend components-test/Separator.ts

GoodWalletV2 source: src/ui/divider/Divider.tsx + Divider.module.css
Local file: packages/ui/src/components-test/Separator.ts
Current base: createComponent(YStack, { name: 'Separator', height: 1, ... }) — uses YStack

Pre-condition (issue #3): Evaluate replacing YStack base with Tamagui's own Separator primitive for semantic correctness.

What's needed after base decision:

  • size variant: sm = 1px, md = 2px, lg = 4px (currently only 1px)
  • color variant: default ($borderColor), muted ($borderColor + opacity), primary ($primary)

4. Text⚠️ extend components-test/Text.ts

GoodWalletV2 source: src/ui/typography/Typography.tsx
Local file: packages/ui/src/components-test/Text.ts
Current base: createComponent(TamaguiText, { name: 'GWText', ... }) — already wraps Tamagui Text correctly; no issue #3 pre-condition

What's needed:

  • Add truncate prop — single-line ellipsis (numberOfLines={1}, overflow: hidden, textOverflow: ellipsis)
  • Add noWrap prop — whiteSpace: 'nowrap'
  • Missing color levels not in current theme:
    • text-soft (#ccc) — between $color (white) and $placeholderColor (grey500); consider adding colorSoft semantic key to light/dark themes in presets.ts
    • text-tertiary (#4d4d4d) — exists as $grey600 token but not as a theme semantic key; consider adding colorDim

5. Dialog — ❌ new component (no existing demo)

GoodWalletV2 source: src/ui/dialog/Dialog.tsx + dialogStore.ts
Local file: none — needs to be created in packages/ui/src/components/
Tamagui base: Tamagui Dialog primitive

What's needed:

  • Use Tamagui Dialog as the behavioral base
  • Styled subparts: DialogOverlay (name: 'DialogOverlay'), DialogFrame (name: 'Dialog', extends Card)
  • Add light_Dialog / dark_Dialog component theme entries in presets.ts
  • Visual spec: centered modal, $backgroundOverlay overlay, $backgroundHover container bg, borderRadius="$4" (16px), width={345}, padding="$8" (32px)
  • Optional image, title (20px/600w), body text (12px/400w), accept + optional reject CTAs, optional close icon button
  • Enter/exit opacity animation
  • Queue-based store: createDialog, updateStatus, useDialog

6. Toast⚠️ extend components-test/Toast.tsx

GoodWalletV2 source: src/ui/notifications/Notifications.tsx + notificationStore.ts
Local file: packages/ui/src/components-test/Toast.tsx
Current base: ToastFrame = createComponent(Stack, { name: 'Toast', ... }) — uses Stack, not Tamagui Toast

Pre-condition (issue #3 second wave): Migrate ToastFrame from Stack to Tamagui Toast as its behavioral base before extending.

What's needed after migration:

  • ToastContainer — fixed-position wrapper at widget boundary bottom (max-width 768px, z-1000)
  • toastStore — queue with createToast, updateToast, removeToast, useToast
  • status variant on ToastFrame: pending (spinner icon), success (check icon), error (exclamation icon) — depends on Icon component (Add iframe/webview bridge package and runnable Expo demo #1 above)
  • Progress bar — animated bottom bar (background: $primary, 0→100% in 3s) for auto-close toasts
  • Enter/exit animation (opacity + max-height)

Public API: Toast, ToastContainer, createToast, updateToast, removeToast, useToast


Implementation Order (recommended)

  1. Icon — unblocked; needed by Button list/icon variants, Dialog close button, Toast status icons
  2. Button additional variants — after issue [BOUNTY] Migrate First-Wave UI Primitives To Tamagui-Native Wrappers #3 Button migration + Icon
  3. Separator enhancements — after issue [BOUNTY] Migrate First-Wave UI Primitives To Tamagui-Native Wrappers #3 Separator base decision; quick and self-contained
  4. Text enhancementstruncate, noWrap, optional colorSoft/colorDim theme keys; no issue [BOUNTY] Migrate First-Wave UI Primitives To Tamagui-Native Wrappers #3 pre-condition
  5. Dialog — new named primitive + store + theme entries; after Icon. should be based on tamagui's Dialog.
  6. Toast / notification system — after issue [BOUNTY] Migrate First-Wave UI Primitives To Tamagui-Native Wrappers #3 Toast migration + Icon

All finalized components should be moved over to, or newly created in components folder


Related files

  • Token/theme preset: packages/ui/src/presets.ts
  • Stable component examples: packages/ui/src/components/Card.ts, packages/ui/src/components/Drawer.tsx
  • Demo primitives to extend: packages/ui/src/components-test/
  • Claim widget (in-use reference): packages/claim-widget/src/ClaimWidget.tsx
  • Component factory: packages/ui/src/createComponent.ts
  • GoodWalletV2 source: src/ui/ in GoodDollar/GoodWalletV2

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions