@payfit/unity-components
Version:
187 lines (142 loc) • 7.47 kB
Markdown
---
name: unity-migrate-from-midnight
description: >
Load when replacing @payfit/midnight UI with Unity. Use it to choose Unity
equivalents, avoid obsolete Midnight patterns, and route unclear component
mappings to unity-find-component.
type: lifecycle
library: '@payfit/unity-components'
library_version: '2.x'
sources:
- 'PayFit/hr-apps:libs/shared/unity/components/src/components/tooltip/Tooltip.tsx'
- 'PayFit/hr-apps:libs/shared/unity/components/src/components/button/Button.tsx'
- 'PayFit/hr-apps:AGENTS.md'
---
Convert a legacy `@payfit/midnight` screen to Unity. The table below
covers the highest-traffic mappings; the exhaustive ~85-row map lives
in `references/midnight-component-map.md`. When a Midnight component is
not in either, fall through to `unity-find-component`.
## Equivalency Map (high-traffic)
| Midnight | Unity | Notes |
| ---------------------- | -------------------------------------------- | -------------------------------------------------------------------- |
| `Button` | `Button` | Prop renames apply (see below) |
| `Box` | `Flex` (or `Grid` for 2D) | No 1:1 layout box; use `Flex` with `direction`/`gap`/`align` |
| `Modal` | `Dialog` + `DialogContent` + `DialogActions` | Use `PromoDialog` for marketing dialogs (requires `PromoDialogHero`) |
| `Link` | `RawLink` (base) or `Link` (tanstack-router) | Pick the router-aware one when navigating inside the app |
| `Heading` | `Text` with `variant="heading*"` | No dedicated `Heading`; typography is a `Text` variant |
| `Badge` | `Badge` | `color` → `variant`; Unity Badge is the chip with intent colors |
| `Pill` | `Pill` | Unity Pill is numeric-only with fewer colors; same indicator intent |
| `transition('smooth')` | `uy:transition-*` / `uy:duration-*` classes | No JS helper; use TailwindCSS utilities |
For everything else, see `references/midnight-component-map.md`. When a
target is unclear, switch to `unity-find-component` rather than
guessing. Do not invent Unity component names from Midnight ones.
## Prop renames
Confirmed renames from `Button.tsx` and the React Aria base classes Unity
extends:
| Midnight | Unity | Mechanism |
| ----------------- | ---------------- | ------------------------------------------------------ |
| `visual="danger"` | `color="danger"` | Color/intent prop renamed on `Button`, `Pill`, `Alert` |
| `onClick={fn}` | `onPress={fn}` | React Aria press events (touch/keyboard parity) |
| `disabled` | `isDisabled` | React Aria boolean convention |
Do not auto-extend this table. If a prop is not listed, look at the Unity
component source — many props are unchanged and adding speculative
renames produces silent default behavior.
## Accessibility deltas
Unity enforces several WCAG rules that Midnight allowed:
- **Tooltip on disabled control — rejected at the library level.**
`Tooltip` ignores or removes the trigger when the wrapped control is
disabled, because disabled controls are not focusable and therefore the
tooltip is unreachable by keyboard or screen reader. See the
`## Common Mistakes` entry below for the replacement.
- **Dialog focus management.** Unity `Dialog` traps focus, restores it on
close, and requires an accessible title. Midnight `Modal` was looser
about both.
- **Autocomplete keyboard navigation.** Unity `Autocomplete` follows the
ARIA combobox pattern (Arrow keys, Home/End, Escape to dismiss). If
Midnight code relied on click-only interactions, those still work, but
do not strip the keyboard handlers when porting.
## Forms
Midnight-era screens commonly use React Hook Form. When porting, replace
the form with `useTanstackUnityForm` and the Tanstack-bound `*Field`
components (`TextField`, `SelectField`, `NumberField`, etc.). The legacy
RHF `useUnityForm` + `*Field` wrappers in the same package index are
deprecated and will be removed after the rebrand — do not pause on the
RHF intermediate step. See `unity-tanstack-form`.
## Common Mistakes
### CRITICAL Wrap disabled button in Tooltip (Midnight-era pattern)
Wrong:
```tsx
<Tooltip title="Disabled because …">
<Button isDisabled>Submit</Button>
</Tooltip>
```
Correct:
```tsx
<Button isDisabled>Submit</Button>
<Text variant="bodySmall" color="content.neutral.low">
Disabled because …
</Text>
// Or, if you must explain inline, gate the tooltip on enabled state:
{isDisabled
? <Button isDisabled>Submit</Button>
: <Tooltip title="…"><Button>Submit</Button></Tooltip>}
```
Midnight allowed `Tooltip` on disabled buttons; Unity enforces the WCAG
rule that disabled controls must not have tooltips because they are not
keyboard-focusable, so the tooltip is unreachable — Unity's `Tooltip`
component rejects this pairing at the library level.
Source: libs/shared/unity/components/src/components/tooltip/Tooltip.tsx; maintainer interview (Unity enforces)
### HIGH Assume Midnight props map 1:1 to Unity
Wrong:
```tsx
<Button visual="danger" onClick={handleDelete}>
Delete
</Button>
```
Correct:
```tsx
<Button color="danger" onPress={handleDelete}>
Delete
</Button>
```
`visual` is the Midnight intent prop and `color` is the Unity one; `onClick`
still type-checks on the underlying DOM node but bypasses React Aria's
touch and keyboard press handling, so keyboard activations silently no-op.
Source: libs/shared/unity/components/src/components/button/Button.tsx
### MEDIUM Leave Midnight + Unity side-by-side in the same screen
Wrong:
```tsx
import { Button } from '@payfit/midnight'
import { Dialog } from '@payfit/unity-components'
```
Correct:
```tsx
import { Button, Dialog } from '@payfit/unity-components'
```
Midnight and Unity ship divergent theme tokens — colors, spacing, and
typography differ — so a mixed screen renders with mismatched scales and
the visual regression lands silently because both stylesheets are valid.
Source: AGENTS.md migration awareness; maintainer interview
### MEDIUM Port Midnight transition utility instead of using uy: classes
Wrong:
```tsx
import { transition } from '@payfit/midnight'
<Flex transition={transition('smooth')}>
```
Correct:
```tsx
<Flex className="uy:transition-all uy:duration-200 uy:ease-out">
```
Unity has no JS transition helper; the migration target is TailwindCSS 4
transition utilities under the `uy:` prefix, and porting the Midnight
function leaves a dead import that the new theme tokens never honor.
Source: no Unity equivalent for Midnight transition helpers
## See also
- `references/midnight-component-map.md` — exhaustive Midnight → Unity
map (v1 + v2 surfaces, ~85 rows, grouped by category)
- `unity-find-component` — decision tree for any Midnight component
still ambiguous after consulting the full map
- `unity-overlays` — full a11y constraints for `Dialog`, `Tooltip`,
`Popover`, `Menu`
- `unity-tanstack-form` — replacement path for RHF forms in the migrated
screen