UNPKG

@aircall/blocks

Version:

Aircall Blocks — higher-level UI compositions built on @aircall/ds

328 lines (267 loc) 9.67 kB
--- name: aircall-blocks/migrate-dashboard/layout description: > Migrate @dashboard/library GridLayout, GridItem, and Gap to native <div> elements with Tailwind CSS grid, flex, and gap utilities — no component import needed. Load when a file imports GridLayout, GridItem, or Gap from @dashboard/library. type: sub-skill library: aircall-blocks requires: - aircall-blocks/setup - aircall-blocks/migrate-dashboard sources: - "aircall/hydra:packages/ds/src/index.ts" --- This skill builds on aircall-blocks/migrate-dashboard. ## 1. Target mapping `GridLayout`, `GridItem`, and `Gap` are layout-only primitives backed by tractor's `Grid` and `Flex` (xstyled). Their sole purpose is to apply CSS grid/flex/gap rules. The direct replacement is native `<div>` elements with Tailwind utility classes — **no component import is needed**. Do not import anything from `@aircall/blocks` or `@aircall/ds` for these three components unless your replacement also composes a real DS component (e.g. a `Card` inside a grid cell). | `@dashboard/library` | Replacement | Package | | --- | --- | --- | | `GridLayout` | `<div className="grid grid-cols-12 gap-x-4 gap-y-4 md:gap-x-6">` | native HTML | | `GridItem` | `<div className="col-span-{xs} sm:col-span-{sm} …">` | native HTML | | `Gap` (row, default) | `<div className="flex flex-col gap-4">` | native HTML | | `Gap` (row, explicit direction) | `<div className="flex flex-row gap-{n}">` | native HTML | | `GapContainer` (internal) | (never used directly — see `Gap`) | — | ### Space token → Tailwind gap class `Gap` and `GridLayout` express spacing using tractor `SpaceVariants` (string keys) or numeric xstyled scale values. Map them to Tailwind `gap-*` classes: | Tractor token | px | Tailwind class | | --- | --- | --- | | `0` / `0` | 0 | `gap-0` | | `xxxs` / `1` | 4 | `gap-1` | | `xxs` / `2` | 8 | `gap-2` | | `xs` / `3` | 12 | `gap-3` | | `s` / `4` (default) | 16 | `gap-4` | | `m` / `5` | 24 | `gap-6` | | `l` / `6` | 32 | `gap-8` | | `xl` / `7` | 40 | `gap-10` | | `xxl` / `8` | 64 | `gap-16` | | `xxxl` / `9` | 80 | `gap-20` | For column-only or row-only overrides use `gap-x-*` / `gap-y-*`. ### GridLayout defaults `GridLayout` hardcoded `columnGap: { _: 16, lg: 24 }` and `rowGap: "s"` (16 px). The canonical Tailwind equivalent is `gap-x-4 lg:gap-x-6 gap-y-4`. ### GridItem column-span mapping `GridItem` accepted `xs | sm | md | lg | xl` as a column count (1–12) and mapped each to a `span N` value. The Tailwind replacement uses `col-span-{n}` with responsive prefixes: | Breakpoint | `@dashboard/library` token | Tailwind prefix | | --- | --- | --- | | all sizes | `xs` | `col-span-{n}` | | ≥ 576 px | `sm` | `sm:col-span-{n}` | | ≥ 768 px | `md` | `md:col-span-{n}` | | ≥ 992 px | `lg` | `lg:col-span-{n}` | | ≥ 1200 px | `xl` | `xl:col-span-{n}` | --- ## 2. Before / After examples ### 2a. GridLayout with GridItem — responsive page grid **Before (`@dashboard/library`):** ```tsx import { GridItem, GridLayout } from '@dashboard/library'; function AnalyticsDashboard() { return ( <GridLayout> <GridItem xs={12} lg={8}> <MainChart /> </GridItem> <GridItem xs={12} lg={4}> <SideStats /> </GridItem> <GridItem xs={12} sm={6} lg={4}> <MetricCard title="Calls" /> </GridItem> <GridItem xs={12} sm={6} lg={4}> <MetricCard title="Missed" /> </GridItem> <GridItem xs={12} sm={6} lg={4}> <MetricCard title="Duration" /> </GridItem> </GridLayout> ); } ``` **After (native `<div>` + Tailwind):** ```tsx function AnalyticsDashboard() { return ( <div className="grid grid-cols-12 gap-x-4 lg:gap-x-6 gap-y-4"> <div className="col-span-12 lg:col-span-8"> <MainChart /> </div> <div className="col-span-12 lg:col-span-4"> <SideStats /> </div> <div className="col-span-12 sm:col-span-6 lg:col-span-4"> <MetricCard title="Calls" /> </div> <div className="col-span-12 sm:col-span-6 lg:col-span-4"> <MetricCard title="Missed" /> </div> <div className="col-span-12 sm:col-span-6 lg:col-span-4"> <MetricCard title="Duration" /> </div> </div> ); } ``` Key changes: - `GridLayout``<div className="grid grid-cols-12 gap-x-4 lg:gap-x-6 gap-y-4">`. - `GridItem xs={N}``col-span-N`; each named breakpoint prop becomes its Tailwind prefix. - No import needed — remove the `@dashboard/library` import entirely. ### 2b. Gap — vertical stack (default direction) **Before (`@dashboard/library`):** ```tsx import { Gap } from '@dashboard/library'; function SectionStack() { return ( <Gap flexDirection="column" gap="m"> <FilterBar /> <DataPanel /> <PaginationRow /> </Gap> ); } ``` **After (native `<div>` + Tailwind):** ```tsx function SectionStack() { return ( <div className="flex flex-col gap-6"> <FilterBar /> <DataPanel /> <PaginationRow /> </div> ); } ``` Key changes: - `Gap flexDirection="column"``flex flex-col`. - `gap="m"` (24 px) → `gap-6` (see token table above). - Drop the import. ### 2c. Gap — horizontal row **Before (`@dashboard/library`):** ```tsx import { Gap } from '@dashboard/library'; function ActionRow() { return ( <Gap gap="xs" alignItems="center"> <Badge /> <Label /> <Button /> </Gap> ); } ``` **After (native `<div>` + Tailwind):** ```tsx function ActionRow() { return ( <div className="flex flex-row items-center gap-3"> <Badge /> <Label /> <Button /> </div> ); } ``` Key changes: - `Gap` with no explicit `flexDirection` defaulted to `row` (inherits `Flex` default). - `alignItems="center"``items-center`. - `gap="xs"` (12 px) → `gap-3`. ### 2d. Gap — independent column-gap / row-gap **Before (`@dashboard/library`):** ```tsx import { Gap } from '@dashboard/library'; function TagCloud() { return ( <Gap flexWrap="wrap" columnGap="s" rowGap="xxs"> {tags.map(tag => <Chip key={tag}>{tag}</Chip>)} </Gap> ); } ``` **After (native `<div>` + Tailwind):** ```tsx function TagCloud() { return ( <div className="flex flex-wrap gap-x-4 gap-y-2"> {tags.map(tag => <Chip key={tag}>{tag}</Chip>)} </div> ); } ``` Key changes: - `columnGap="s"` (16 px) → `gap-x-4`. - `rowGap="xxs"` (8 px) → `gap-y-2`. - `flexWrap="wrap"``flex-wrap`. --- ## 3. Common mistakes ### Mistake 1 — Keeping a component import for layout primitives ```tsx // ❌ Wrong — GridLayout and Gap are layout-only; importing from blocks/ds adds bundle weight import { GridLayout, GridItem } from '@dashboard/library'; <GridLayout> <GridItem xs={12}>content</GridItem> </GridLayout> // ✅ Correct — delete the import; write native HTML with Tailwind classes <div className="grid grid-cols-12 gap-x-4 lg:gap-x-6 gap-y-4"> <div className="col-span-12">content</div> </div> ``` `GridLayout` and `Gap` had no semantic meaning — they rendered a plain `<div>` (via `Grid` / `Flex` from `@aircall/tractor`). Tailwind utility classes applied directly to `<div>` produce identical CSS with zero runtime overhead. Source: `packages/library/src/components/GridLayout/GridLayout.tsx` ### Mistake 2 — Omitting `grid-cols-12` on the GridLayout replacement ```tsx // ❌ Wrong — missing column definition; col-span-* has nothing to span against <div className="grid gap-x-4 gap-y-4"> <div className="col-span-6">left</div> <div className="col-span-6">right</div> </div> // ✅ Correct — declare the 12-column template that GridLayout hardcoded <div className="grid grid-cols-12 gap-x-4 lg:gap-x-6 gap-y-4"> <div className="col-span-6">left</div> <div className="col-span-6">right</div> </div> ``` `GridLayout` hardcoded `gridTemplateColumns="repeat(12, 1fr)"`. Without `grid-cols-12` the browser defaults to `auto` columns and `col-span-*` values are ignored. Source: `packages/library/src/components/GridLayout/GridLayout.tsx` ### Mistake 3 — Forgetting the `lg:gap-x-6` breakpoint variant ```tsx // ❌ Wrong — loses the responsive gutter that GridLayout applied at lg <div className="grid grid-cols-12 gap-x-4 gap-y-4"> … </div> // ✅ Correct — GridLayout used DEFAULT_GUTTER = { _: 16, lg: 24 }; match it <div className="grid grid-cols-12 gap-x-4 lg:gap-x-6 gap-y-4"> … </div> ``` `GridLayout` sourced its column gutter from `DEFAULT_GUTTER = { _: 16, lg: 24 }` (the xstyled responsive object syntax). At viewports ≥ lg the gutter widens from 16 px (`gap-x-4`) to 24 px (`gap-x-6`). Omitting `lg:gap-x-6` silently produces narrower gutters on large screens. Source: `packages/library/src/components/GridLayout/GridLayout.tsx` ### Mistake 4 — Passing the `space` alias instead of `gap` to Gap ```tsx // ❌ Wrong — `space` was a Gap-specific alias; Tailwind has no "space" concept here import { Gap } from '@dashboard/library'; // Old code used `space` prop: <Gap space="m">…</Gap> // Migrated incorrectly as if `space` maps directly: <div className="space-y-6">…</div> // space-y-* distributes margin, not gap // ✅ Correct — map to gap-* utility which uses CSS gap (same as what Gap rendered) <div className="flex flex-col gap-6">…</div> ``` `Gap` treated `space` as an alias for `gap` (both resolved the same tractor spacing token into a CSS `gap` value). Tailwind's `space-y-*` / `space-x-*` utilities work via child selectors and `margin`, not CSS `gap` — they produce different stacking behavior when children have their own margins. Source: `packages/library/src/components/Gap/Gap.tsx`