@payfit/unity-components
Version:
267 lines (195 loc) • 7.32 kB
Markdown
---
name: unity-layout-and-styling
description: >
Load when composing Unity layouts or styling with uy: Tailwind utilities.
Use it for Flex/Grid/Text choices, class merging/variants, and validating
Unity token names before adding styles.
metadata:
type: core
library: '@payfit/unity-components, @payfit/unity-themes'
library_version: '2.x'
sources:
- 'PayFit/hr-apps:libs/shared/unity/components/src/components/flex/Flex.tsx'
- 'PayFit/hr-apps:libs/shared/unity/components/src/components/grid/Grid.tsx'
- 'PayFit/hr-apps:libs/shared/unity/components/src/components/text/Text.tsx'
- 'PayFit/hr-apps:libs/shared/unity/themes/src/utils/tailwind-merge.ts'
- 'PayFit/hr-apps:libs/shared/unity/themes/src/utils/tailwind-variants.ts'
- 'PayFit/hr-apps:libs/shared/unity/themes/src/utils/cn.ts'
- 'PayFit/hr-apps:libs/shared/unity/themes/src/scripts/build.ts'
---
Layout primitives, the `uy:` utility-class system, and the variant/merge tools
used inside `@payfit/unity-components`.
## Setup
```tsx
import { Card, Flex, Grid, Text } from '@payfit/unity-components'
export function PayslipSummary() {
return (
<Card>
<Flex direction="col" gap="200" className="uy:p-300">
<Text variant="h3" asElement="h2">
Payslip
</Text>
<Grid cols={12} className="uy:gap-200 uy:md:gap-300">
<Flex direction="col" className="uy:col-span-12 uy:md:col-span-6">
<Text variant="overline">Gross</Text>
<Text variant="bodyLargeStrong">€ 4,200.00</Text>
</Flex>
<Flex direction="col" className="uy:col-span-12 uy:md:col-span-6">
<Text variant="overline">Net</Text>
<Text variant="bodyLargeStrong">€ 3,150.00</Text>
</Flex>
</Grid>
</Flex>
</Card>
)
}
```
## Core Patterns
- Use `Flex` for one-dimensional layout and `Grid` for two-dimensional layout.
- Express responsiveness and interaction states with `uy:` variants.
- Use `uyTv` for typed component variants, `uyMerge` when merging external
classes, and `cn` for simple conditional strings.
- Use `Text` variants for typography and prefer exposed `data-*` states over
raw hover selectors when components provide them.
Read [references/patterns.md](references/patterns.md) for implementation
examples.
## Common Mistakes
### CRITICAL Use bare Tailwind classes without uy: prefix
Wrong:
```tsx
<Flex className="flex gap-4 p-3"> … </Flex>
```
Correct:
```tsx
<Flex gap="100" className="uy:p-300">
{' '}
…{' '}
</Flex>
```
Unity CSS is built with `prefix(uy)`; bare classes are not in the compiled stylesheet and produce no styling.
Source: themes/src/scripts/build.ts:298 (prefix(uy) import)
### HIGH Pass responsive prop objects (v0.x style)
Wrong:
```tsx
<Flex gap={{ initial: '100', md: '200' }} />
```
Correct:
```tsx
<Flex gap="100" className="uy:md:gap-200" />
```
v1.x removed the responsive prop-object API. Use className with uy:md: et al.
Source: components/flex/Flex.tsx:33; themes/docs/files/MIGRATION-v1.md
### HIGH Import twMerge from tailwind-merge directly
Wrong:
```tsx
import { twMerge } from 'tailwind-merge'
twMerge('uy:p-100', 'uy:p-200')
```
Correct:
```tsx
import { uyMerge } from '@payfit/unity-themes'
uyMerge('uy:p-100', 'uy:p-200') // → 'uy:p-200'
```
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.
Source: themes/src/utils/tailwind-merge.ts:1-7,75-77
### HIGH Import tv from tailwind-variants directly
Wrong:
```tsx
import { tv } from 'tailwind-variants'
export const button = tv({ base: 'uy:px-200', variants: {...} })
```
Correct:
```tsx
import { uyTv } from '@payfit/unity-themes'
export const button = uyTv({ base: 'uy:px-200', variants: {...} })
```
tv() is unconfigured; uyTv pre-applies the Unity twMergeConfig so variant conflict resolution understands Unity tokens.
Source: themes/src/utils/tailwind-variants.ts:48-51
### MEDIUM Use <div> + typography class instead of <Text>
Wrong:
```tsx
<div className="uy:typography-h1 uy:text-content-primary">Title</div>
```
Correct:
```tsx
<Text variant="h1" color="content.primary">
Title
</Text>
```
`<Text variant="h1">` auto-selects the correct semantic element (h1) and applies the Unity typography variant; `<div>` loses the semantics and the variant API.
Source: components/text/Text.tsx:60-104,137-139
### MEDIUM Use uy:hover: when component exposes data-\* state
Wrong:
```tsx
<ListViewItem className="uy:hover:bg-surface-primary-hover" />
```
Correct:
```tsx
<ListViewItem className="uy:data-[hovered=true]:bg-surface-primary-hover" />
```
Some Unity components manage state via data-hovered, data-selected, etc. `uy:data-[hovered=true]:` targets the component-managed state and avoids drift.
Source: themes/src/scripts/build.ts:303-307 (custom-variant for data attrs)
### HIGH Hallucinate token names that look plausible but do not exist
Wrong:
```tsx
<div className="uy:bg-primary-500 uy:text-gray-900 uy:border-blue-600" />
```
Correct:
```tsx
// Use Unity's semantic token names (verify against the live class index
// in themes docs or the @theme block in dist/css/unity.css):
<div className="uy:bg-surface-primary-default uy:text-content-primary uy:border-surface-primary-active" />
```
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.
Source: themes/dist/css/unity.css (@theme block enumerates valid tokens)
### MEDIUM Reach for cn() to compose variant classes when uyTv fits
Wrong:
```tsx
import { cn } from '@payfit/unity-themes'
function Pill({
size,
color,
}: {
size: 'sm' | 'lg'
color: 'primary' | 'danger'
}) {
return (
<span
className={cn(
'uy:inline-flex uy:items-center',
size === 'sm' && 'uy:px-100 uy:text-xs',
size === 'lg' && 'uy:px-200 uy:text-sm',
color === 'primary' && 'uy:bg-surface-primary-default',
color === 'danger' && 'uy:bg-surface-danger-default',
)}
/>
)
}
```
Correct:
```tsx
import type { VariantProps } from '@payfit/unity-themes'
import { uyTv } from '@payfit/unity-themes'
const pill = uyTv({
base: 'uy:inline-flex uy:items-center',
variants: {
size: { sm: 'uy:px-100 uy:text-xs', lg: 'uy:px-200 uy:text-sm' },
color: {
primary: 'uy:bg-surface-primary-default',
danger: 'uy:bg-surface-danger-default',
},
},
})
type PillProps = VariantProps<typeof pill>
function Pill(props: PillProps) {
return <span className={pill(props)} />
}
```
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.
Source: themes/src/utils/tailwind-variants.ts
## See also
- `unity-find-component` — the decision tree's "React Aria + uy: classes" branch
when no Unity component fits.
- `unity-contribute-component` — `uyTv` is the contributor's variant tool.
- `unity-themes-tokens-and-docs` — token discipline if you need a new token
rather than reusing an existing one.