This document is the authoritative reference for the current GoodWidget codebase.
For detailed Tamagui/theming rules, see
docs/architecture/theming-contract.md.
GoodWidget is a framework for building cross-platform mini apps that can render as:
- a React web application
- a React Native / Expo application
- a Web Component embeddable in a host page
The framework provides:
- an EIP-1193 wallet integration layer
- a Tamagui-based UI system
- a preset-driven design system with constrained token/theme overrides
- widget-level and host-level theming boundaries
GoodWidget/
ARCHITECTURE.md # this file — system overview
AGENTS.md # agent operating guide
package.json
pnpm-workspace.yaml
turbo.json
tsconfig.base.json
playwright.config.ts # Playwright config targeting localhost:3000
packages/
core/ # GoodWidgetProvider, hooks, host detection, wallet context
ui/ # Tamagui tokens, preset, themes, config assembly, manifest, primitives
embed/ # Web Component wrapper + CSS custom property bridge
claim-widget/ # Example widget package using core + ui + embed
examples/
react-web/ # React web override and theming demo
html/ # web component demo
expo/ # Expo demo app
docs/
PACKAGING.md # packaging and distribution guide
demo-environment.md # Storybook, Playwright, demo routes, fixtures
architecture/
theming-contract.md # detailed Tamagui/theming rules
agent-next-steps/
native-primitives-design-integration-task.md
@goodwidget/ui
^
|
@goodwidget/core
^
|
@goodwidget/embed
@goodwidget/claim-widget -> depends on core + ui + embed
@goodwidget/ui is the leaf design-system package and must not depend on @goodwidget/core.
- plain token seed values in
packages/ui/src/theme.ts - the GoodWalletV2 preset in
packages/ui/src/presets.ts - theme derivation from effective token values
createTamagui()assembly inpackages/ui/src/config.tscreateComponent()and the runtime theme manifest- exported primitives and composite UI building blocks
GoodWidgetProvider(packages/core/src/provider.tsx)- host/provider detection
- wallet and host React context
- author
config+ hostthemeOverridesplumbing intoTamaguiProvider
- the Web Component shell
- Shadow DOM style setup
- CSS custom property reading and observation
- merging JS
themeOverrideswith CSS-derived overrides before rendering the provider
This separation is important: packages/core does not author tokens or themes, and
packages/ui does not own runtime host detection.
GoodWidget does not treat the provider as the authoring layer for Tamagui config objects. The current flow is:
- Start from plain token seed values in
defaultTokenValues. - Layer preset token overrides on top.
- Layer author token overrides on top.
- Layer host token overrides on top.
- Derive the full semantic theme map from the effective token set.
- Apply preset, author, and host theme overrides by theme name.
- Build one Tamagui config from that single effective source.
The implementation lives in:
packages/ui/src/theme.tspackages/ui/src/presets.tspackages/ui/src/config.ts
For the detailed rules governing this pipeline — including token vs theme distinctions,
component naming conventions, override surface constraints, and correction targets — see
docs/architecture/theming-contract.md.
The system is not doing a deep merge of already-created Tamagui token/theme objects.
Instead:
- token seeds remain plain objects until config creation
createThemeValues()derives semantic themes from the effective token setcreateGoodWidgetConfig()callscreateTamagui()once for that effective config
This is the core architectural guardrail for keeping token overrides and theme derivation aligned.
There are three main override inputs in the runtime pipeline:
- preset defaults from
packages/ui - author configuration via
config - host overrides via
themeOverrides
For the Web Component path there is an additional web-only host layer:
- CSS custom properties read by
packages/embed
And finally there are local component-instance props:
- one-off inline style props on JSX usage
1. GoodWidget preset defaults
2. Author config (`config`)
3. Host theme overrides (`themeOverrides`)
4. Host CSS custom properties (`--gw-*`, web component path only)
5. Inline instance props
packages/embed merges JS host overrides with CSS-derived overrides before rendering
GoodWidgetProvider, so CSS wins over the host JS prop in that path.
| File | Responsibility |
|---|---|
configTypes.ts |
Public config, token, theme, preset, typography, animation types |
theme.ts |
Plain token seeds + createGoodWidgetTokens() + createThemeValues() |
presets.ts |
GoodWalletV2 preset tokens and partial theme overrides |
config.ts |
Config resolution, token override merge, theme derivation, createGoodWidgetConfig() |
createComponent.ts |
Named styled-component wrapper + manifest registration |
manifest.ts |
Runtime manifest for named override targets |
index.ts |
Public exports |
- tokens are static primitives and scales
- themes are semantic/contextual values
- named components opt into
light_Component/dark_Componentsub-themes throughname $fooresolves theme-first, token-second
See docs/architecture/theming-contract.md for the
full rules and decision checklist.
createComponent() wraps Tamagui styled() and does two things:
- requires a stable
name - registers the component in the runtime manifest
That name is the contract Tamagui uses for component sub-themes and the contract
GoodWidget uses for host-facing manifest discovery.
The public UI surface is exported from packages/ui/src/index.ts.
At the moment there are two implementation areas:
packages/ui/src/components/- current production-aligned components such as
Card,GlowCard,Drawer,TokenAmount
- current production-aligned components such as
packages/ui/src/components-test/- many still-exported primitives and composites such as
Button,Input,Checkbox,Switch,Select,Alert,Badge,Text,MiniAppShell
- many still-exported primitives and composites such as
This split is real and intentional for the current branch state. The next-step documents cover how to reduce that transitional surface.
| File | Responsibility |
|---|---|
eip1193.ts |
EIP-1193 types |
detect.ts |
Host/provider detection |
types.ts |
Provider props and host/wallet state types |
provider.tsx |
GoodWidgetProvider |
hooks.ts |
useWallet(), useHost(), useGoodWidget() |
wagmi.ts |
wagmi integration surface |
GoodWidgetProvider:
- resolves the wallet provider
- subscribes to account and chain changes
- merges author
configwith hostthemeOverrides - rebuilds the effective Tamagui config through
createGoodWidgetConfig() - renders
TamaguiProvider
This means per-instance widget theming is a first-class runtime behavior at the provider boundary.
| File | Responsibility |
|---|---|
createMiniAppElement.tsx |
Web Component factory |
cssPropertyBridge.ts |
Reads --gw-* overrides from the host |
shadowStyles.ts |
Shadow DOM reset and runtime style syncing |
bridge.ts |
attribute/prop/event bridging |
The custom element:
- creates a shadow root
- injects reset/runtime styles
- reads CSS custom property overrides from the host element
- merges those CSS overrides with any JS
themeOverrides - renders
GoodWidgetProvider
- token overrides:
--gw-{category}-{tokenName} - component theme overrides:
--gw-{ComponentName}-{themeKey}
Examples:
--gw-color-primary--gw-Card-borderColor
The manifest exposed on the custom element constructor is generated from the runtime
component registry in packages/ui.
packages/claim-widget is the example publishable widget package and the best current
reference for how a widget author is expected to use the system.
Key patterns in packages/claim-widget/src/ClaimWidget.tsx:
- widget-local named components such as
ClaimCardandClaimActionButton extendsrelationships for manifest lineage- author-level
configsupport - host-level
themeOverridessupport - direct use of shared primitives from
@goodwidget/ui
<<<<<<< HEAD
examples/react-web is a route-based Vite + React + RN-web SPA that serves as
the canonical review and Playwright test environment.
Routes:
| Route | Content |
|---|---|
/ |
Link grid to all demo routes |
/components/:name |
Per-primitive demo page for each UI component |
/widget/claim |
ClaimWidget full-flow demo with token, component, and host override examples |
/theme-overrides |
The original 5-tab OverrideShowcase (Default / Tokens / Component / Host / Inline) |
Wallet-aware pages (WalletInfo, AddressDisplay, ChainBadge, ClaimWidget) use a
lightweight mock EIP-1193 provider (src/mock/mockEip1193.ts) that provides a stable
address and chain ID without requiring a real browser wallet.
Start with: pnpm --filter @goodwidget/example-react-web dev → http://localhost:3000
See docs/demo-environment.md for full documentation.
Storybook is the canonical demo and review environment for UI components and widget flows.
- stories live alongside their components
- Storybook runs at
localhost:6006during development - Playwright tests run against Storybook at
localhost:6006
See docs/demo-environment.md for full details on Storybook
setup, story conventions, Playwright fixtures, screenshot evidence requirements, and demo
routes.
The examples/ directory contains additional integration demos:
examples/react-web/— React web app demonstrating preset baseline, token overrides, component theme overrides, hostthemeOverrides, and local inline overridesexamples/html/— plain HTML page using the claim widget as a web componentexamples/expo/— Expo / React Native app validating the current compatibility set
These examples are useful integration references but Storybook is the primary review tool.
Use createComponent() when the component owns a theme contract or should be discoverable
as a named override target.
Use plain React composition when the component is mostly orchestration or state.
- prefer semantic theme keys for reusable visual styling
- use token scale references for spacing, sizing, and radius
- avoid hardcoded colors in shared primitives unless they are deliberate exceptions
- treat component names and sub-theme names as API surface once exposed to hosts
packages/ui/src/components/should hold stable production-aligned primitivespackages/ui/src/components-test/currently still contains many exported primitives in transition
That second point is not ideal, but it is the current truth and the docs should reflect it honestly.
Host / wallet / app
|
|-- provider
|-- config
|-- themeOverrides
|-- CSS custom properties (web component path)
|
v
Web Component shell (optional)
|
|-- read CSS overrides
|-- merge CSS overrides with JS host overrides
|
v
GoodWidgetProvider
|
|-- detect host/provider
|-- merge author config with host overrides
|-- createGoodWidgetConfig()
|
v
TamaguiProvider
|
v
Named GoodWidget components
|
v
Wallet / chain interactions
- Many exported primitives still live under
packages/ui/src/components-test/. Checkbox,Switch,Select,Input,Button,Alert, and several composites still use customStack-based behavior rather than Tamagui-native primitives.- The runtime manifest is generated in memory rather than from a build-time extracted artifact.
createComponent()still returnsanybecause Tamagui variant generics do not survive the current wrapper cleanly.- Multi-widget theming is supported through provider boundaries, but broad token overrides are still broad design-system inputs and should not be described as narrowly targeted styling.
AGENTS.md— agent operating guide (always-read before coding)docs/demo-environment.md— Storybook, Playwright, demo routes, story conventions, fixture setup, screenshot evidencedocs/architecture/theming-contract.md— detailed Tamagui/theming rules, decision checklist, correction targetsdocs/PACKAGING.md— packaging and distribution guideagent-next-steps/native-primitives-design-integration-task.md— next-step task context