@payfit/unity-components 2.55.23 → 2.56.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +8 -8
- package/skills/unity-find-component/SKILL.md +24 -49
- package/skills/unity-layout/SKILL.md +130 -0
- package/skills/unity-layout/references/patterns.md +59 -0
- package/skills/unity-migrate-from-midnight/SKILL.md +2 -0
- package/skills/unity-migrate-from-midnight/references/midnight-component-map.md +10 -10
- package/skills/unity-overlays/SKILL.md +4 -2
- package/skills/unity-tanstack-form/SKILL.md +2 -1
- package/skills/unity-layout-and-styling/SKILL.md +0 -274
- package/skills/unity-layout-and-styling/references/patterns.md +0 -139
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@payfit/unity-components",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.56.1",
|
|
4
4
|
"module": "./dist/esm/index.js",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -96,7 +96,7 @@
|
|
|
96
96
|
"tailwind-variants": "3.2.2",
|
|
97
97
|
"usehooks-ts": "3.1.1",
|
|
98
98
|
"zod": "4.4.3",
|
|
99
|
-
"@payfit/unity-illustrations": "2.
|
|
99
|
+
"@payfit/unity-illustrations": "2.56.1"
|
|
100
100
|
},
|
|
101
101
|
"peerDependencies": {
|
|
102
102
|
"@hookform/devtools": "^4",
|
|
@@ -108,8 +108,8 @@
|
|
|
108
108
|
"react-hook-form": "^7",
|
|
109
109
|
"react-router-dom": "^5",
|
|
110
110
|
"zod": "^3 || ^4",
|
|
111
|
-
"@payfit/unity-icons": "2.
|
|
112
|
-
"@payfit/unity-themes": "2.
|
|
111
|
+
"@payfit/unity-icons": "2.56.1",
|
|
112
|
+
"@payfit/unity-themes": "2.56.1"
|
|
113
113
|
},
|
|
114
114
|
"devDependencies": {
|
|
115
115
|
"@figma/code-connect": "1.4.9",
|
|
@@ -159,10 +159,10 @@
|
|
|
159
159
|
"@payfit/hr-apps-tsconfigs": "0.0.0-use.local",
|
|
160
160
|
"@payfit/storybook-addon-console-errors": "0.0.0-use.local",
|
|
161
161
|
"@payfit/storybook-config": "0.0.0-use.local",
|
|
162
|
-
"@payfit/unity-
|
|
163
|
-
"@payfit/unity-
|
|
164
|
-
"@payfit/
|
|
165
|
-
"@payfit/
|
|
162
|
+
"@payfit/unity-illustrations": "2.56.1",
|
|
163
|
+
"@payfit/unity-themes": "2.56.1",
|
|
164
|
+
"@payfit/vite-configs": "0.0.0-use.local",
|
|
165
|
+
"@payfit/unity-icons": "2.56.1"
|
|
166
166
|
},
|
|
167
167
|
"peerDependenciesMeta": {
|
|
168
168
|
"@hookform/devtools": {
|
|
@@ -30,13 +30,21 @@ rg -i -C 2 'dialog|confirmation|modal' \
|
|
|
30
30
|
|
|
31
31
|
## Standard operating procedure
|
|
32
32
|
|
|
33
|
-
1.
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
33
|
+
1. Build an inventory of distinct interface needs from all task inputs,
|
|
34
|
+
including the primary workflow, surrounding controls, navigation, status
|
|
35
|
+
information, and secondary actions. Do not silently omit peripheral UI.
|
|
36
|
+
2. Expand every identified need into synonyms, interaction patterns, and
|
|
37
|
+
adjacent component terms.
|
|
38
|
+
3. Search the complete catalog for every inventory item; do not stop after
|
|
39
|
+
finding components for the primary workflow or at the first plausible hit.
|
|
40
|
+
4. Rank candidates by intent fit, description, keywords, tags, section, and related entries.
|
|
41
|
+
5. Return the best match first. Mention an alternative only when the evidence is genuinely ambiguous.
|
|
42
|
+
6. Search `libs/shared/unity/components/src` to confirm implementation details or locate nearby source.
|
|
43
|
+
7. Inspect Storybook stories only for props, API details, or usage examples after a likely component has been identified. When the Storybook MCP is available, use its curated component manifest or matching Storybook entry as a complementary source for rendered examples; the canonical JSON catalog remains the source of truth for discovery.
|
|
44
|
+
8. Walk the decision tree below for every identified need before creating a custom primitive.
|
|
45
|
+
9. Before implementation, perform a coverage check: every inventoried
|
|
46
|
+
interface need must map to a Unity component, an explicit React Aria
|
|
47
|
+
fallback, or a deliberate omission requested by the user.
|
|
40
48
|
|
|
41
49
|
## Decision Tree
|
|
42
50
|
|
|
@@ -131,6 +139,9 @@ import it into a new module.
|
|
|
131
139
|
text. `IconButton` / `CircularIconButton` for icon-only actions
|
|
132
140
|
(requires `aria-label`). `RawLinkButton` renders as an anchor but styled
|
|
133
141
|
like a button — use when the action navigates.
|
|
142
|
+
- `Search` vs `TextField`: use `Search` for query, lookup, and filtering
|
|
143
|
+
controls with search affordances and clearing behavior. Use `TextField` for
|
|
144
|
+
general textual data entry.
|
|
134
145
|
- `Menu` vs `Popover`: `Menu` is a list of actionable items keyed by
|
|
135
146
|
keyboard (Enter/Arrow). `Popover` is a free-form floating panel and
|
|
136
147
|
requires a `title`.
|
|
@@ -144,22 +155,12 @@ import it into a new module.
|
|
|
144
155
|
for "this section failed to load." `Alert` is an inline banner that
|
|
145
156
|
coexists with surrounding content.
|
|
146
157
|
|
|
147
|
-
## Icon
|
|
158
|
+
## Icon selection
|
|
148
159
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
icon silently renders empty.
|
|
154
|
-
|
|
155
|
-
```tsx
|
|
156
|
-
import type { UnityIcon } from '@payfit/unity-icons'
|
|
157
|
-
|
|
158
|
-
import { Icon } from '@payfit/unity-components'
|
|
159
|
-
|
|
160
|
-
const icon: UnityIcon = 'MagnifyingGlassOutlined'
|
|
161
|
-
;<Icon src={icon} size={20} />
|
|
162
|
-
```
|
|
160
|
+
Choose the component here (`Icon`, `IconButton`, a prefix/suffix icon, and so
|
|
161
|
+
on), then use `unity-icons` to select an exact sprite name, type shared icon
|
|
162
|
+
props, or migrate a Midnight icon. Do not duplicate or guess the icon catalog
|
|
163
|
+
from the Components package.
|
|
163
164
|
|
|
164
165
|
## Forms Notice
|
|
165
166
|
|
|
@@ -259,37 +260,11 @@ that the `*Field` components handle.
|
|
|
259
260
|
|
|
260
261
|
Source: libs/shared/unity/components/src/components/form-field/FormField.tsx; index.ts:205-223
|
|
261
262
|
|
|
262
|
-
### HIGH Pass an untyped string to Icon src and guess the name
|
|
263
|
-
|
|
264
|
-
Wrong:
|
|
265
|
-
|
|
266
|
-
```tsx
|
|
267
|
-
<Icon src="search" size={20} />
|
|
268
|
-
<Icon src="trash-filled" />
|
|
269
|
-
const name: string = 'trash'
|
|
270
|
-
<Icon src={name as UnityIcon} />
|
|
271
|
-
```
|
|
272
|
-
|
|
273
|
-
Correct:
|
|
274
|
-
|
|
275
|
-
```tsx
|
|
276
|
-
import { Icon } from '@payfit/unity-components'
|
|
277
|
-
import type { UnityIcon } from '@payfit/unity-icons'
|
|
278
|
-
<Icon src="MagnifyingGlassOutlined" size={20} />
|
|
279
|
-
<Icon src="TrashFilled" />
|
|
280
|
-
type Props = { icon: UnityIcon }
|
|
281
|
-
```
|
|
282
|
-
|
|
283
|
-
The `UnityIcon` literal union encodes the exact sprite ids; lowercase or
|
|
284
|
-
kebab-case strings have no matching `<symbol id>` in the injected sprite,
|
|
285
|
-
so `<use href="#search">` resolves to nothing and the SVG renders empty.
|
|
286
|
-
|
|
287
|
-
Source: libs/shared/unity/icons/src/components/icon/parts/IconSprite.tsx; generated/index.ts (UnityIcon type)
|
|
288
|
-
|
|
289
263
|
## See also
|
|
290
264
|
|
|
291
265
|
- For projects not yet configured for Unity, use the repository's
|
|
292
266
|
`@payfit/nx-tools:setup-unity` generator
|
|
293
267
|
- `unity-migrate-from-midnight` — when Level 3 fallback hits a Midnight
|
|
294
268
|
screen, follow this skill to replace it
|
|
269
|
+
- `unity-icons` — select and type the exact icon after choosing the component
|
|
295
270
|
- `unity-tanstack-form` — the only supported form authoring path
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: unity-layout
|
|
3
|
+
description: >
|
|
4
|
+
Load when composing page or component layouts with Unity Page, PageHeader,
|
|
5
|
+
PageHeading, Flex, FlexItem, Grid, GridItem, or Text. Use it to choose the
|
|
6
|
+
right layout primitive, preserve semantic typography, and replace removed
|
|
7
|
+
responsive prop objects.
|
|
8
|
+
metadata:
|
|
9
|
+
type: core
|
|
10
|
+
library: '@payfit/unity-components'
|
|
11
|
+
library_version: '2.x'
|
|
12
|
+
sources:
|
|
13
|
+
- 'PayFit/hr-apps:libs/shared/unity/components/src/components/page/Page.tsx'
|
|
14
|
+
- 'PayFit/hr-apps:libs/shared/unity/components/src/components/page/parts/PageHeader.tsx'
|
|
15
|
+
- 'PayFit/hr-apps:libs/shared/unity/components/src/components/page/parts/PageHeading.tsx'
|
|
16
|
+
- 'PayFit/hr-apps:libs/shared/unity/components/src/components/flex/Flex.tsx'
|
|
17
|
+
- 'PayFit/hr-apps:libs/shared/unity/components/src/components/grid/Grid.tsx'
|
|
18
|
+
- 'PayFit/hr-apps:libs/shared/unity/components/src/components/text/Text.tsx'
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
Compose structure with the layout and typography primitives exported by
|
|
22
|
+
`@payfit/unity-components`.
|
|
23
|
+
|
|
24
|
+
## Choose the primitive
|
|
25
|
+
|
|
26
|
+
- Use `Page`, `PageHeader`, and `PageHeading` for the main Unity page shell
|
|
27
|
+
when an installed `Page` variant fits the application layout.
|
|
28
|
+
- Use `Flex` and `FlexItem` for one-dimensional rows or columns.
|
|
29
|
+
- Use `Grid` and `GridItem` when positioning content across rows and columns.
|
|
30
|
+
- Use `Text` for semantic Unity typography instead of styling a generic
|
|
31
|
+
element with typography classes.
|
|
32
|
+
- Use component props for the base layout. Use `uy:` responsive utilities in
|
|
33
|
+
`className` for breakpoint-specific changes; responsive prop objects were
|
|
34
|
+
removed in Unity v1.
|
|
35
|
+
|
|
36
|
+
```tsx
|
|
37
|
+
import { Card, Flex, Grid, GridItem, Text } from '@payfit/unity-components'
|
|
38
|
+
|
|
39
|
+
export function PayslipSummary() {
|
|
40
|
+
return (
|
|
41
|
+
<Card>
|
|
42
|
+
<Flex direction="col" gap="200">
|
|
43
|
+
<Text variant="h3" asElement="h2">
|
|
44
|
+
Payslip
|
|
45
|
+
</Text>
|
|
46
|
+
<Grid cols={12} className="uy:gap-200 uy:md:gap-300">
|
|
47
|
+
<GridItem colSpan={6}>Gross</GridItem>
|
|
48
|
+
<GridItem colSpan={6}>Net</GridItem>
|
|
49
|
+
</Grid>
|
|
50
|
+
</Flex>
|
|
51
|
+
</Card>
|
|
52
|
+
)
|
|
53
|
+
}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Page containers
|
|
57
|
+
|
|
58
|
+
Use `Page` as the main Unity page shell when one of its installed variants
|
|
59
|
+
matches the application layout.
|
|
60
|
+
|
|
61
|
+
Before composing the surrounding layout, inspect the installed `Page`
|
|
62
|
+
implementation and its variants to understand the spacing, sizing, background,
|
|
63
|
+
border, and responsive behavior it already provides. These details belong to
|
|
64
|
+
the installed component version; do not infer them from memory or duplicate
|
|
65
|
+
them in an outer wrapper.
|
|
66
|
+
|
|
67
|
+
The parent of `Page` should normally define only the space available within the
|
|
68
|
+
application shell. Do not add redundant padding, margins, backgrounds,
|
|
69
|
+
borders, or arbitrary maximum widths around `Page`.
|
|
70
|
+
|
|
71
|
+
When only part of the page needs a width constraint, apply it to the relevant
|
|
72
|
+
content section, form, card, or grid instead of constraining the entire `Page`.
|
|
73
|
+
|
|
74
|
+
If the available `Page` variants do not fit the intended shell, confirm the
|
|
75
|
+
component API and inspect nearby usage before introducing a wrapper or custom
|
|
76
|
+
layout.
|
|
77
|
+
|
|
78
|
+
Read [references/patterns.md](references/patterns.md) for the supported layout
|
|
79
|
+
props and responsive composition examples.
|
|
80
|
+
|
|
81
|
+
## Common mistakes
|
|
82
|
+
|
|
83
|
+
### Use Flex for a two-dimensional layout
|
|
84
|
+
|
|
85
|
+
Use `Grid` when items need explicit row and column placement. Nested `Flex`
|
|
86
|
+
containers obscure the intended structure and make responsive changes harder.
|
|
87
|
+
|
|
88
|
+
### Pass responsive prop objects
|
|
89
|
+
|
|
90
|
+
Wrong:
|
|
91
|
+
|
|
92
|
+
```tsx
|
|
93
|
+
<Flex gap={{ initial: '100', md: '200' }} />
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Correct:
|
|
97
|
+
|
|
98
|
+
```tsx
|
|
99
|
+
<Flex gap="100" className="uy:md:gap-200" />
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### Use a generic element for semantic typography
|
|
103
|
+
|
|
104
|
+
Wrong:
|
|
105
|
+
|
|
106
|
+
```tsx
|
|
107
|
+
<div className="uy:typography-h1">Title</div>
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Correct:
|
|
111
|
+
|
|
112
|
+
```tsx
|
|
113
|
+
<Text variant="h1">Title</Text>
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
Override `asElement` only when the document hierarchy requires a different
|
|
117
|
+
semantic element from the visual variant.
|
|
118
|
+
|
|
119
|
+
### Duplicate Page layout constraints
|
|
120
|
+
|
|
121
|
+
Do not treat `Page` as an unstyled `main` element. Inspect the installed
|
|
122
|
+
variant before adding an outer wrapper: duplicating its spacing, background,
|
|
123
|
+
border, or width constraints can compress the whole page unexpectedly.
|
|
124
|
+
|
|
125
|
+
## See also
|
|
126
|
+
|
|
127
|
+
- `unity-themes` — token lookup, `uy:` utilities, responsive and state
|
|
128
|
+
modifiers, class merging, and typed variants.
|
|
129
|
+
- `unity-find-component` — choose a higher-level Unity component before
|
|
130
|
+
composing a custom primitive.
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# Unity layout patterns
|
|
2
|
+
|
|
3
|
+
## Flex for one dimension
|
|
4
|
+
|
|
5
|
+
Use `Flex` for a row or column and `FlexItem` for item-level growth or
|
|
6
|
+
alignment.
|
|
7
|
+
|
|
8
|
+
```tsx
|
|
9
|
+
import { Flex, FlexItem } from '@payfit/unity-components'
|
|
10
|
+
;<Flex direction="row" gap="200" justify="between" align="center">
|
|
11
|
+
<FlexItem grow="1">Left</FlexItem>
|
|
12
|
+
<FlexItem>Right</FlexItem>
|
|
13
|
+
</Flex>
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
`Flex` exposes `asElement`, `inline`, `direction`, `isReversed`, `wrap`,
|
|
17
|
+
`gap`, `gapX`, `gapY`, `justify`, `align`, `alignContent`, and `className`.
|
|
18
|
+
|
|
19
|
+
## Grid for two dimensions
|
|
20
|
+
|
|
21
|
+
Use `Grid` when content needs row and column placement. Position a `GridItem`
|
|
22
|
+
with either `area` or the column props; do not combine the two strategies.
|
|
23
|
+
|
|
24
|
+
```tsx
|
|
25
|
+
import { Grid, GridItem } from '@payfit/unity-components'
|
|
26
|
+
;<Grid cols={12} className="uy:gap-200">
|
|
27
|
+
<GridItem colSpan={8}>Main</GridItem>
|
|
28
|
+
<GridItem colSpan={4}>Aside</GridItem>
|
|
29
|
+
</Grid>
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
`Grid` exposes `asElement`, `inline`, `cols`, `rows`, `areas`, `flow`,
|
|
33
|
+
`justifyItems`, `alignItems`, and `className`.
|
|
34
|
+
|
|
35
|
+
## Responsive layout
|
|
36
|
+
|
|
37
|
+
Set the default through props, then apply breakpoint changes with `uy:`
|
|
38
|
+
utilities. Read `unity-themes` before choosing utility or token names.
|
|
39
|
+
|
|
40
|
+
```tsx
|
|
41
|
+
<Flex gap="100" className="uy:md:gap-200 uy:lg:gap-300">
|
|
42
|
+
<span>Item</span>
|
|
43
|
+
</Flex>
|
|
44
|
+
|
|
45
|
+
<Grid cols={12} className="uy:grid-cols-1 uy:md:grid-cols-2 uy:lg:grid-cols-3" />
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Semantic typography
|
|
49
|
+
|
|
50
|
+
`Text` selects a semantic element from its variant and supports an explicit
|
|
51
|
+
`asElement` override.
|
|
52
|
+
|
|
53
|
+
```tsx
|
|
54
|
+
import { Text } from '@payfit/unity-components'
|
|
55
|
+
|
|
56
|
+
<Text variant="h1" color="content.primary">Title</Text>
|
|
57
|
+
<Text variant="body" color="content.neutral">Description</Text>
|
|
58
|
+
<Text variant="h1" asElement="h2">Visual h1, semantic h2</Text>
|
|
59
|
+
```
|
|
@@ -184,3 +184,5 @@ Source: no Unity equivalent for Midnight transition helpers
|
|
|
184
184
|
`Popover`, `Menu`
|
|
185
185
|
- `unity-tanstack-form` — replacement path for RHF forms in the migrated
|
|
186
186
|
screen
|
|
187
|
+
- `unity-icons` — select and type replacements for Midnight icons
|
|
188
|
+
- `unity-illustrations` — select, load, and describe replacement artwork
|
|
@@ -143,14 +143,14 @@ look like a link.
|
|
|
143
143
|
|
|
144
144
|
## Media
|
|
145
145
|
|
|
146
|
-
| Midnight | Unity | Note
|
|
147
|
-
| -------------- | ------------------------------------------------------------------- |
|
|
148
|
-
| `Avatar` | `Avatar` | `type` → `variant` (circle/square)
|
|
149
|
-
| `AvatarGroup` | — | No equivalent (planned)
|
|
150
|
-
| `Icon` | `Icon` (`@payfit/unity-icons`) |
|
|
151
|
-
| `Illustration` | `Illustration` / `LazyIllustration` (`@payfit/unity-illustrations`) | `
|
|
152
|
-
| `Image` | native `<img>` | No Unity equivalent
|
|
153
|
-
| `Svg` | native `<svg>` (or `Icon` if it's an icon) | No Unity equivalent for arbitrary SVGs
|
|
146
|
+
| Midnight | Unity | Note |
|
|
147
|
+
| -------------- | ------------------------------------------------------------------- | ----------------------------------------------------------- |
|
|
148
|
+
| `Avatar` | `Avatar` | `type` → `variant` (circle/square) |
|
|
149
|
+
| `AvatarGroup` | — | No equivalent (planned) |
|
|
150
|
+
| `Icon` | `Icon` (`@payfit/unity-icons`) | Select and type the target with `unity-icons` |
|
|
151
|
+
| `Illustration` | `Illustration` / `LazyIllustration` (`@payfit/unity-illustrations`) | Select loading and accessibility with `unity-illustrations` |
|
|
152
|
+
| `Image` | native `<img>` | No Unity equivalent |
|
|
153
|
+
| `Svg` | native `<svg>` (or `Icon` if it's an icon) | No Unity equivalent for arbitrary SVGs |
|
|
154
154
|
|
|
155
155
|
## Misc
|
|
156
156
|
|
|
@@ -173,8 +173,8 @@ Unity primitive.
|
|
|
173
173
|
| `v2/Box` | `Flex` / `Grid` | Same guidance as v1 `Box` |
|
|
174
174
|
| `v2/Flex` | `Flex` | Drop the `v2/` namespace; same props |
|
|
175
175
|
| `v2/Grid` | `Grid` | Drop the `v2/` namespace |
|
|
176
|
-
| `v2/Icon` | `Icon` (`@payfit/unity-icons`) |
|
|
177
|
-
| `v2/Illustration` | `Illustration` / `LazyIllustration` (`@payfit/unity-illustrations`) |
|
|
176
|
+
| `v2/Icon` | `Icon` (`@payfit/unity-icons`) | Select and type the target with `unity-icons` |
|
|
177
|
+
| `v2/Illustration` | `Illustration` / `LazyIllustration` (`@payfit/unity-illustrations`) | Select loading and accessibility with `unity-illustrations` |
|
|
178
178
|
| `v2/Image` | native `<img>` | — |
|
|
179
179
|
| `v2/PageLayout` | `AppLayout` / `Page` | `AppLayout` for the app shell; `Page` for the content surface |
|
|
180
180
|
| `v2/Stack` | `Flex` (with `direction` + `gap`) | — |
|
|
@@ -223,5 +223,7 @@ Source: components/promo-dialog/PromoDialog.tsx:230-236 (console.error guard)
|
|
|
223
223
|
- `unity-migrate-from-midnight` — the disabled+tooltip trap is the most
|
|
224
224
|
common Midnight-era pattern that Unity blocks; the migration skill covers
|
|
225
225
|
the rewrite.
|
|
226
|
-
- `unity-layout
|
|
227
|
-
|
|
226
|
+
- `unity-layout` — compose overlay content with Flex and Grid.
|
|
227
|
+
- `unity-themes` — apply spacing and component-owned `data-*` state styles.
|
|
228
|
+
- `unity-illustrations` — select and load the artwork required by
|
|
229
|
+
`PromoDialogHero`.
|
|
@@ -252,5 +252,6 @@ Source: TanstackTextField.tsx; TanstackFormField.tsx and its parts
|
|
|
252
252
|
## See also
|
|
253
253
|
|
|
254
254
|
- `unity-migrate-from-midnight` — forms migrated off Midnight typically came with React Hook Form; that skill explains the Tanstack-only replacement path.
|
|
255
|
-
- `unity-layout
|
|
255
|
+
- `unity-layout` — compose form layouts with Flex and Grid.
|
|
256
|
+
- `unity-themes` — use `uy:*` spacing and responsive utilities.
|
|
256
257
|
- `unity-navigation` — when a form posts via a route action or links to a sibling step, use the router-aware `Link` from `@payfit/unity-components/integrations/tanstack-router`.
|
|
@@ -1,274 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: unity-layout-and-styling
|
|
3
|
-
description: >
|
|
4
|
-
Load when composing Unity layouts or styling with uy: Tailwind utilities.
|
|
5
|
-
Use it for Flex/Grid/Text choices, class merging/variants, and validating
|
|
6
|
-
Unity token names before adding styles.
|
|
7
|
-
metadata:
|
|
8
|
-
type: core
|
|
9
|
-
library: '@payfit/unity-components, @payfit/unity-themes'
|
|
10
|
-
library_version: '2.x'
|
|
11
|
-
sources:
|
|
12
|
-
- 'PayFit/hr-apps:libs/shared/unity/components/src/components/flex/Flex.tsx'
|
|
13
|
-
- 'PayFit/hr-apps:libs/shared/unity/components/src/components/grid/Grid.tsx'
|
|
14
|
-
- 'PayFit/hr-apps:libs/shared/unity/components/src/components/text/Text.tsx'
|
|
15
|
-
- 'PayFit/hr-apps:libs/shared/unity/themes/src/utils/tailwind-merge.ts'
|
|
16
|
-
- 'PayFit/hr-apps:libs/shared/unity/themes/src/utils/tailwind-variants.ts'
|
|
17
|
-
- 'PayFit/hr-apps:libs/shared/unity/themes/src/utils/cn.ts'
|
|
18
|
-
- 'PayFit/hr-apps:libs/shared/unity/themes/src/scripts/build.ts'
|
|
19
|
-
---
|
|
20
|
-
|
|
21
|
-
Layout primitives, the `uy:` utility-class system, and the variant/merge tools
|
|
22
|
-
used inside `@payfit/unity-components`.
|
|
23
|
-
|
|
24
|
-
## Token lookup SOP
|
|
25
|
-
|
|
26
|
-
Before adding a token-based utility, search the canonical [Unity token catalog](../../../themes/src/agent-references/tokens-catalog.json).
|
|
27
|
-
Use its exact `name` and `cssVariable`, then consult the generated theme
|
|
28
|
-
blueprints under `../../../themes/src/storybook-mcp/` for common class
|
|
29
|
-
patterns. Do not infer token names from raw DTCG paths or standard Tailwind
|
|
30
|
-
conventions when the catalog can provide the transformed name.
|
|
31
|
-
|
|
32
|
-
## Setup
|
|
33
|
-
|
|
34
|
-
```tsx
|
|
35
|
-
import { Card, Flex, Grid, Text } from '@payfit/unity-components'
|
|
36
|
-
|
|
37
|
-
export function PayslipSummary() {
|
|
38
|
-
return (
|
|
39
|
-
<Card>
|
|
40
|
-
<Flex direction="col" gap="200" className="uy:p-300">
|
|
41
|
-
<Text variant="h3" asElement="h2">
|
|
42
|
-
Payslip
|
|
43
|
-
</Text>
|
|
44
|
-
<Grid cols={12} className="uy:gap-200 uy:md:gap-300">
|
|
45
|
-
<Flex direction="col" className="uy:col-span-12 uy:md:col-span-6">
|
|
46
|
-
<Text variant="overline">Gross</Text>
|
|
47
|
-
<Text variant="bodyLargeStrong">€ 4,200.00</Text>
|
|
48
|
-
</Flex>
|
|
49
|
-
<Flex direction="col" className="uy:col-span-12 uy:md:col-span-6">
|
|
50
|
-
<Text variant="overline">Net</Text>
|
|
51
|
-
<Text variant="bodyLargeStrong">€ 3,150.00</Text>
|
|
52
|
-
</Flex>
|
|
53
|
-
</Grid>
|
|
54
|
-
</Flex>
|
|
55
|
-
</Card>
|
|
56
|
-
)
|
|
57
|
-
}
|
|
58
|
-
```
|
|
59
|
-
|
|
60
|
-
## Core Patterns
|
|
61
|
-
|
|
62
|
-
- Use `Flex` for one-dimensional layout and `Grid` for two-dimensional layout.
|
|
63
|
-
- Express responsiveness and interaction states with `uy:` variants.
|
|
64
|
-
- Use `uyTv` for typed component variants, `uyMerge` when merging external
|
|
65
|
-
classes, and `cn` for simple conditional strings.
|
|
66
|
-
- Use `Text` variants for typography and prefer exposed `data-*` states over
|
|
67
|
-
raw hover selectors when components provide them.
|
|
68
|
-
|
|
69
|
-
Read [references/patterns.md](references/patterns.md) for implementation
|
|
70
|
-
examples.
|
|
71
|
-
|
|
72
|
-
## Common Mistakes
|
|
73
|
-
|
|
74
|
-
### CRITICAL Use bare Tailwind classes without uy: prefix
|
|
75
|
-
|
|
76
|
-
Wrong:
|
|
77
|
-
|
|
78
|
-
```tsx
|
|
79
|
-
<Flex className="flex gap-4 p-3"> … </Flex>
|
|
80
|
-
```
|
|
81
|
-
|
|
82
|
-
Correct:
|
|
83
|
-
|
|
84
|
-
```tsx
|
|
85
|
-
<Flex gap="100" className="uy:p-300">
|
|
86
|
-
{' '}
|
|
87
|
-
…{' '}
|
|
88
|
-
</Flex>
|
|
89
|
-
```
|
|
90
|
-
|
|
91
|
-
Unity CSS is built with `prefix(uy)`; bare classes are not in the compiled stylesheet and produce no styling.
|
|
92
|
-
|
|
93
|
-
Source: themes/src/scripts/build.ts:298 (prefix(uy) import)
|
|
94
|
-
|
|
95
|
-
### HIGH Pass responsive prop objects (v0.x style)
|
|
96
|
-
|
|
97
|
-
Wrong:
|
|
98
|
-
|
|
99
|
-
```tsx
|
|
100
|
-
<Flex gap={{ initial: '100', md: '200' }} />
|
|
101
|
-
```
|
|
102
|
-
|
|
103
|
-
Correct:
|
|
104
|
-
|
|
105
|
-
```tsx
|
|
106
|
-
<Flex gap="100" className="uy:md:gap-200" />
|
|
107
|
-
```
|
|
108
|
-
|
|
109
|
-
v1.x removed the responsive prop-object API. Use className with uy:md: et al.
|
|
110
|
-
|
|
111
|
-
Source: components/flex/Flex.tsx:33; themes/docs/files/MIGRATION-v1.md
|
|
112
|
-
|
|
113
|
-
### HIGH Import twMerge from tailwind-merge directly
|
|
114
|
-
|
|
115
|
-
Wrong:
|
|
116
|
-
|
|
117
|
-
```tsx
|
|
118
|
-
import { twMerge } from 'tailwind-merge'
|
|
119
|
-
|
|
120
|
-
twMerge('uy:p-100', 'uy:p-200')
|
|
121
|
-
```
|
|
122
|
-
|
|
123
|
-
Correct:
|
|
124
|
-
|
|
125
|
-
```tsx
|
|
126
|
-
import { uyMerge } from '@payfit/unity-themes'
|
|
127
|
-
|
|
128
|
-
uyMerge('uy:p-100', 'uy:p-200') // → 'uy:p-200'
|
|
129
|
-
```
|
|
130
|
-
|
|
131
|
-
The unconfigured twMerge has no knowledge of Unity tokens; class conflicts on uy:bg-surface-primary-default vs uy:bg-surface-danger-default are not resolved.
|
|
132
|
-
|
|
133
|
-
Source: themes/src/utils/tailwind-merge.ts:1-7,75-77
|
|
134
|
-
|
|
135
|
-
### HIGH Import tv from tailwind-variants directly
|
|
136
|
-
|
|
137
|
-
Wrong:
|
|
138
|
-
|
|
139
|
-
```tsx
|
|
140
|
-
import { tv } from 'tailwind-variants'
|
|
141
|
-
export const button = tv({ base: 'uy:px-200', variants: {...} })
|
|
142
|
-
```
|
|
143
|
-
|
|
144
|
-
Correct:
|
|
145
|
-
|
|
146
|
-
```tsx
|
|
147
|
-
import { uyTv } from '@payfit/unity-themes'
|
|
148
|
-
export const button = uyTv({ base: 'uy:px-200', variants: {...} })
|
|
149
|
-
```
|
|
150
|
-
|
|
151
|
-
tv() is unconfigured; uyTv pre-applies the Unity twMergeConfig so variant conflict resolution understands Unity tokens.
|
|
152
|
-
|
|
153
|
-
Source: themes/src/utils/tailwind-variants.ts:48-51
|
|
154
|
-
|
|
155
|
-
### MEDIUM Use <div> + typography class instead of <Text>
|
|
156
|
-
|
|
157
|
-
Wrong:
|
|
158
|
-
|
|
159
|
-
```tsx
|
|
160
|
-
<div className="uy:typography-h1 uy:text-content-primary">Title</div>
|
|
161
|
-
```
|
|
162
|
-
|
|
163
|
-
Correct:
|
|
164
|
-
|
|
165
|
-
```tsx
|
|
166
|
-
<Text variant="h1" color="content.primary">
|
|
167
|
-
Title
|
|
168
|
-
</Text>
|
|
169
|
-
```
|
|
170
|
-
|
|
171
|
-
`<Text variant="h1">` auto-selects the correct semantic element (h1) and applies the Unity typography variant; `<div>` loses the semantics and the variant API.
|
|
172
|
-
|
|
173
|
-
Source: components/text/Text.tsx:60-104,137-139
|
|
174
|
-
|
|
175
|
-
### MEDIUM Use uy:hover: when component exposes data-\* state
|
|
176
|
-
|
|
177
|
-
Wrong:
|
|
178
|
-
|
|
179
|
-
```tsx
|
|
180
|
-
<ListViewItem className="uy:hover:bg-surface-primary-hover" />
|
|
181
|
-
```
|
|
182
|
-
|
|
183
|
-
Correct:
|
|
184
|
-
|
|
185
|
-
```tsx
|
|
186
|
-
<ListViewItem className="uy:data-[hovered=true]:bg-surface-primary-hover" />
|
|
187
|
-
```
|
|
188
|
-
|
|
189
|
-
Some Unity components manage state via data-hovered, data-selected, etc. `uy:data-[hovered=true]:` targets the component-managed state and avoids drift.
|
|
190
|
-
|
|
191
|
-
Source: themes/src/scripts/build.ts:303-307 (custom-variant for data attrs)
|
|
192
|
-
|
|
193
|
-
### HIGH Hallucinate token names that look plausible but do not exist
|
|
194
|
-
|
|
195
|
-
Wrong:
|
|
196
|
-
|
|
197
|
-
```tsx
|
|
198
|
-
<div className="uy:bg-primary-500 uy:text-gray-900 uy:border-blue-600" />
|
|
199
|
-
```
|
|
200
|
-
|
|
201
|
-
Correct:
|
|
202
|
-
|
|
203
|
-
```tsx
|
|
204
|
-
// Use Unity's semantic token names (verify against the live class index
|
|
205
|
-
// in themes docs or the @theme block in dist/css/unity.css):
|
|
206
|
-
<div className="uy:bg-surface-primary-default uy:text-content-primary uy:border-surface-primary-active" />
|
|
207
|
-
```
|
|
208
|
-
|
|
209
|
-
Agents generate names that match standard Tailwind conventions but are not in the Unity token set; the class is absent from the compiled stylesheet, the element silently renders with no style.
|
|
210
|
-
|
|
211
|
-
Source: themes/dist/css/unity.css (@theme block enumerates valid tokens)
|
|
212
|
-
|
|
213
|
-
### MEDIUM Reach for cn() to compose variant classes when uyTv fits
|
|
214
|
-
|
|
215
|
-
Wrong:
|
|
216
|
-
|
|
217
|
-
```tsx
|
|
218
|
-
import { cn } from '@payfit/unity-themes'
|
|
219
|
-
|
|
220
|
-
function Pill({
|
|
221
|
-
size,
|
|
222
|
-
color,
|
|
223
|
-
}: {
|
|
224
|
-
size: 'sm' | 'lg'
|
|
225
|
-
color: 'primary' | 'danger'
|
|
226
|
-
}) {
|
|
227
|
-
return (
|
|
228
|
-
<span
|
|
229
|
-
className={cn(
|
|
230
|
-
'uy:inline-flex uy:items-center',
|
|
231
|
-
size === 'sm' && 'uy:px-100 uy:text-xs',
|
|
232
|
-
size === 'lg' && 'uy:px-200 uy:text-sm',
|
|
233
|
-
color === 'primary' && 'uy:bg-surface-primary-default',
|
|
234
|
-
color === 'danger' && 'uy:bg-surface-danger-default',
|
|
235
|
-
)}
|
|
236
|
-
/>
|
|
237
|
-
)
|
|
238
|
-
}
|
|
239
|
-
```
|
|
240
|
-
|
|
241
|
-
Correct:
|
|
242
|
-
|
|
243
|
-
```tsx
|
|
244
|
-
import type { VariantProps } from '@payfit/unity-themes'
|
|
245
|
-
|
|
246
|
-
import { uyTv } from '@payfit/unity-themes'
|
|
247
|
-
|
|
248
|
-
const pill = uyTv({
|
|
249
|
-
base: 'uy:inline-flex uy:items-center',
|
|
250
|
-
variants: {
|
|
251
|
-
size: { sm: 'uy:px-100 uy:text-xs', lg: 'uy:px-200 uy:text-sm' },
|
|
252
|
-
color: {
|
|
253
|
-
primary: 'uy:bg-surface-primary-default',
|
|
254
|
-
danger: 'uy:bg-surface-danger-default',
|
|
255
|
-
},
|
|
256
|
-
},
|
|
257
|
-
})
|
|
258
|
-
type PillProps = VariantProps<typeof pill>
|
|
259
|
-
function Pill(props: PillProps) {
|
|
260
|
-
return <span className={pill(props)} />
|
|
261
|
-
}
|
|
262
|
-
```
|
|
263
|
-
|
|
264
|
-
cn() / classNames / clsx are for ad-hoc conditional class strings; component-scoped variant APIs with multiple axes (size × color × intent) belong in `uyTv`, which gives a typed `VariantProps` signature and pre-applied conflict resolution.
|
|
265
|
-
|
|
266
|
-
Source: themes/src/utils/tailwind-variants.ts
|
|
267
|
-
|
|
268
|
-
## See also
|
|
269
|
-
|
|
270
|
-
- `unity-find-component` — the decision tree's "React Aria + uy: classes" branch
|
|
271
|
-
when no Unity component fits.
|
|
272
|
-
- `unity-contribute-component` — `uyTv` is the contributor's variant tool.
|
|
273
|
-
- `unity-themes-tokens-and-docs` — token discipline if you need a new token
|
|
274
|
-
rather than reusing an existing one.
|
|
@@ -1,139 +0,0 @@
|
|
|
1
|
-
# Unity layout and styling patterns
|
|
2
|
-
|
|
3
|
-
### Flex for 1D, Grid for 2D
|
|
4
|
-
|
|
5
|
-
`Flex` is for one-dimensional rows/columns; `Grid` is for the 12-column (or 6-column)
|
|
6
|
-
two-dimensional layout. Each exposes layout props; everything else goes via
|
|
7
|
-
`className` with `uy:` utilities.
|
|
8
|
-
|
|
9
|
-
```tsx
|
|
10
|
-
import { Flex, FlexItem, Grid, GridItem } from '@payfit/unity-components'
|
|
11
|
-
|
|
12
|
-
// Flex props: asElement, inline, direction, isReversed, wrap,
|
|
13
|
-
// gap, gapX, gapY, justify, align, alignContent, className
|
|
14
|
-
<Flex direction="row" gap="200" justify="between" align="center">
|
|
15
|
-
<FlexItem grow="1">Left</FlexItem>
|
|
16
|
-
<FlexItem>Right</FlexItem>
|
|
17
|
-
</Flex>
|
|
18
|
-
|
|
19
|
-
// Grid props: asElement, inline, cols (6 | 12), rows, areas, flow,
|
|
20
|
-
// justifyItems, alignItems, className
|
|
21
|
-
// GridItem positions via colSpan/colStart/colEnd OR area (mutually exclusive)
|
|
22
|
-
<Grid cols={12} className="uy:gap-200">
|
|
23
|
-
<GridItem colSpan={8}>Main</GridItem>
|
|
24
|
-
<GridItem colSpan={4}>Aside</GridItem>
|
|
25
|
-
</Grid>
|
|
26
|
-
```
|
|
27
|
-
|
|
28
|
-
### Responsive classes via uy:md:
|
|
29
|
-
|
|
30
|
-
There is no responsive prop-object API. Responsive behavior is driven by
|
|
31
|
-
TailwindCSS v4 modifiers on `className`.
|
|
32
|
-
|
|
33
|
-
```tsx
|
|
34
|
-
<Flex gap="100" className="uy:md:gap-200 uy:lg:gap-300">
|
|
35
|
-
<span>Item</span>
|
|
36
|
-
</Flex>
|
|
37
|
-
|
|
38
|
-
<Grid cols={12} className="uy:grid-cols-1 uy:md:grid-cols-2 uy:lg:grid-cols-3" />
|
|
39
|
-
```
|
|
40
|
-
|
|
41
|
-
### Variants with uyTv
|
|
42
|
-
|
|
43
|
-
`uyTv` from `@payfit/unity-themes` is the pre-configured tailwind-variants
|
|
44
|
-
factory. It applies the Unity `twMergeConfig` so variant collisions resolve
|
|
45
|
-
against Unity tokens. Export the variant function and derive its typed props
|
|
46
|
-
with `VariantProps`.
|
|
47
|
-
|
|
48
|
-
```tsx
|
|
49
|
-
import type { VariantProps } from '@payfit/unity-themes'
|
|
50
|
-
|
|
51
|
-
import { uyTv } from '@payfit/unity-themes'
|
|
52
|
-
|
|
53
|
-
export const callout = uyTv({
|
|
54
|
-
base: 'uy:inline-flex uy:items-center uy:gap-100 uy:rounded-100 uy:px-200 uy:py-100',
|
|
55
|
-
variants: {
|
|
56
|
-
intent: {
|
|
57
|
-
info: 'uy:bg-surface-primary-default uy:text-content-inverted-default',
|
|
58
|
-
danger: 'uy:bg-surface-danger-default uy:text-content-inverted-default',
|
|
59
|
-
neutral: 'uy:bg-surface-neutral-default uy:text-content-neutral-default',
|
|
60
|
-
},
|
|
61
|
-
size: {
|
|
62
|
-
sm: 'uy:typography-body-small',
|
|
63
|
-
md: 'uy:typography-body',
|
|
64
|
-
},
|
|
65
|
-
},
|
|
66
|
-
defaultVariants: { intent: 'info', size: 'md' },
|
|
67
|
-
})
|
|
68
|
-
|
|
69
|
-
export type CalloutVariantProps = VariantProps<typeof callout>
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
### Class merging with uyMerge
|
|
73
|
-
|
|
74
|
-
`uyMerge` is `tailwind-merge` configured with Unity's class groups. Use it
|
|
75
|
-
whenever an external `className` may collide with internal classes.
|
|
76
|
-
|
|
77
|
-
```tsx
|
|
78
|
-
import { uyMerge } from '@payfit/unity-themes'
|
|
79
|
-
|
|
80
|
-
uyMerge('uy:p-100', 'uy:p-200') // → 'uy:p-200'
|
|
81
|
-
uyMerge('uy:bg-surface-primary-default', 'uy:bg-surface-danger-default')
|
|
82
|
-
// → 'uy:bg-surface-danger-default'
|
|
83
|
-
uyMerge('uy:p-100', 'uy:px-200', 'uy:py-300') // p, px, py don't collide
|
|
84
|
-
```
|
|
85
|
-
|
|
86
|
-
### Conditional classes with cn / classNames / clsx
|
|
87
|
-
|
|
88
|
-
`cn`, `classNames`, and `clsx` are aliases for the same Unity-configured helper
|
|
89
|
-
in `@payfit/unity-themes`. Use them for ad-hoc conditional strings — not for
|
|
90
|
-
component-scoped variant APIs.
|
|
91
|
-
|
|
92
|
-
```tsx
|
|
93
|
-
import { cn } from '@payfit/unity-themes'
|
|
94
|
-
|
|
95
|
-
function Row({
|
|
96
|
-
isActive,
|
|
97
|
-
className,
|
|
98
|
-
}: {
|
|
99
|
-
isActive: boolean
|
|
100
|
-
className?: string
|
|
101
|
-
}) {
|
|
102
|
-
return (
|
|
103
|
-
<div
|
|
104
|
-
className={cn(
|
|
105
|
-
'uy:flex uy:items-center uy:px-200 uy:py-100',
|
|
106
|
-
isActive && 'uy:bg-surface-primary-default',
|
|
107
|
-
className,
|
|
108
|
-
)}
|
|
109
|
-
/>
|
|
110
|
-
)
|
|
111
|
-
}
|
|
112
|
-
```
|
|
113
|
-
|
|
114
|
-
### Typography with `<Text>`
|
|
115
|
-
|
|
116
|
-
Use `<Text variant=...>` instead of a `<div>` + typography class. `Text`
|
|
117
|
-
picks a semantic element from the variant (e.g. `variant="h1"` → `<h1>`),
|
|
118
|
-
applies the typography variant, and exposes `color`, `isTruncated`,
|
|
119
|
-
`lineClamp`, and `maxWidthCh`.
|
|
120
|
-
|
|
121
|
-
```tsx
|
|
122
|
-
import { Text } from '@payfit/unity-components'
|
|
123
|
-
|
|
124
|
-
<Text variant="h1" color="content.primary">Title</Text>
|
|
125
|
-
<Text variant="body" color="content.neutral">Description</Text>
|
|
126
|
-
// Override the semantic element when needed:
|
|
127
|
-
<Text variant="h1" asElement="h2">Visual h1, semantic h2</Text>
|
|
128
|
-
```
|
|
129
|
-
|
|
130
|
-
### Data-attribute pseudo-states
|
|
131
|
-
|
|
132
|
-
Several Unity components expose internal state via `data-hovered`,
|
|
133
|
-
`data-pressed`, `data-focus-visible`, etc. Target the component-owned state
|
|
134
|
-
attribute rather than the native CSS pseudo-state.
|
|
135
|
-
|
|
136
|
-
```tsx
|
|
137
|
-
import { ListViewItem } from '@payfit/unity-components'
|
|
138
|
-
;<ListViewItem className="uy:data-[hovered=true]:bg-surface-primary-hover" />
|
|
139
|
-
```
|