Skip to content

@payfit/unity-components

unity-migrate-from-midnight

193 linesSource

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.

This skill targets the current workspace Unity v3 API. Legacy examples are migration context.

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)

MidnightUnityNotes
ButtonButtonProp renames apply (see below)
BoxFlex (or Grid for 2D)No 1:1 layout box; use Flex with direction/gap/align
ModalDialog + DialogContent + DialogActionsUse PromoDialog for marketing dialogs (requires PromoDialogHero)
LinkRawLink (base) or Link (tanstack-router)Pick the router-aware one when navigating inside the app
HeadingText with variant="heading*"No dedicated Heading; typography is a Text variant
BadgeBadgecolor → variant; Unity Badge is the chip with intent colors
PillPillUnity Pill is numeric-only with fewer colors; same indicator intent
transition('smooth')uy:transition-* / uy:duration-* classesNo 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:

MidnightUnityMechanism
visual="danger"color="danger"Color/intent prop renamed on Button, Pill, Alert
onClick={fn}onPress={fn}React Aria press events (touch/keyboard parity)
disabledisDisabledReact 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 + RHF *Field wrappers were removed in Unity v3. Import useTanstackUnityForm from @payfit/unity-components/forms. 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/core/components/tooltip/Tooltip.tsx

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/core/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

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
  • unity-icons — select and type replacements for Midnight icons
  • unity-illustrations — select, load, and describe replacement artwork