@aircall/blocks
Version:
Aircall Blocks — higher-level UI compositions built on @aircall/ds
247 lines (184 loc) • 8.97 kB
Markdown
---
name: aircall-blocks/setup
description: >
Set up @aircall/blocks in an app already (or simultaneously) adopting @aircall/ds.
Load when wiring blocks compositions (DashboardPage, DashboardPageHeader,
DashboardSidebar, empty states, form fields) into a project: installing the
package, importing its precompiled globals.css after the DS bundle, and the DS
providers it relies on. Covers both standalone apps and module-federation
consumers where the host already loads DS globals.
type: core
library: aircall-blocks
requires:
- aircall-ds/setup
sources:
- "aircall/hydra:docs/migration-guides/tractor-to-ds/00-setup.md"
- "aircall/hydra:packages/blocks/package.json"
---
# Setting up @aircall/blocks
This skill builds on aircall-ds/setup. Read it first: blocks compose `@aircall/ds`
primitives and depend on DS being installed and its `globals.css` imported.
## Setup
Install blocks alongside DS:
```bash
pnpm add @aircall/blocks @aircall/ds @aircall/react-icons
```
How you import the CSS depends on whether your app is standalone or a
**module-federation consumer** whose host already loads DS globals.
### Standalone app (owns its own Preflight)
`@aircall/blocks/globals.css` is a **delta build** — it contains only blocks-specific tokens
and utility classes; it does NOT bundle DS. Import DS globals first (Preflight + DS tokens +
DS utilities), then blocks globals (blocks-specific delta). Always import in this order.
```css
/* style.css */
@layer theme, base, components, utilities;
@import 'tailwindcss/theme.css' layer(theme);
@import 'tailwindcss/utilities.css' layer(utilities);
@import '@aircall/ds/globals.css';
@import '@aircall/blocks/globals.css';
@source './src/**/*.{ts,tsx}';
```
Or from a JS/TS entry (no custom Tailwind classes of your own):
```tsx
// main.tsx
import '@aircall/ds/globals.css';
import '@aircall/blocks/globals.css';
```
#### Single-compilation via `theme.css` (recommended for DS + blocks)
The two precompiled bundles above each re-emit the same Tailwind utilities into `@layer
utilities`; at equal specificity the last-loaded bundle can clobber the other's responsive
variants (e.g. blocks' plain `.text-base` beating DS's `md:text-sm` on `Textarea`), and **no
import order fixes it**. To avoid this, import the directive-preserving `theme.css` artifacts
(tokens + dark variant + reset + keyframes only — **no** Preflight, **no** utilities) and let a
single Tailwind pass generate the utilities by `@source`-scanning the library dist:
```css
/* style.css */
@import 'tailwindcss'; /* consumer owns the single Preflight + engine */
@import '@aircall/ds/theme.css';
@import '@aircall/blocks/theme.css';
@source '../node_modules/@aircall/ds/dist/index.js';
@source '../node_modules/@aircall/blocks/dist/index.js';
```
Here the full `@import 'tailwindcss'` is **correct** (unlike the `globals.css` case) because
`theme.css` ships no Preflight/utilities. `globals.css` still ships unchanged for precompiled-
bundle consumers. Full rationale: `packages/ds/docs/single-compilation-theme-css.md`.
### Module-federation consumer (host already owns the Preflight)
When your app is a remote loaded inside a host that already imports DS globals
(e.g. `dashboard-v4`), **do not re-import DS or blocks globals** in your own
CSS. The host's single Preflight applies to the whole document — re-importing
it duplicates the base reset and causes cascade conflicts (see Common Mistakes
below).
Import only the Tailwind layers you need for your own authored utility classes:
```css
/* style.css */
@layer theme, base, components, utilities;
@import 'tailwindcss/theme.css' layer(theme);
@import 'tailwindcss/utilities.css' layer(utilities);
@source './**/*.{ts,tsx}';
```
> **Note on the `.css` extension**: `@import "tailwindcss/theme"` (no `.css`)
> silently fails in webpack/Rsbuild PostCSS pipelines — the extension is required.
Blocks render under the same DS providers — there is no blocks-specific provider; mount the
DS root providers from `aircall-ds/setup` as needed (`ThemeProvider` / `TooltipProvider` /
`Toaster`). Two DS providers matter specifically once you use blocks:
- **`DsI18nProvider`** (from `@aircall/ds`) — importing `@aircall/blocks` runs
`import './i18n/register'` at module load (`packages/blocks/src/index.ts`), which registers
a `blocks` namespace on DS's shared i18next instance. Block strings render localized off the
self-initialized DS singleton even without a provider; to make DS + blocks strings follow the
**user's** language (and track switches), mount `DsI18nProvider` **as a descendant of your
react-i18next `I18nextProvider`** and pass the active language (`language={i18n.language}`).
Use **either** `DsI18nProvider` **or** `syncDsLanguage(i18n)`, never both. See
`aircall-ds/setup` for the full provider tree and nesting order.
- **`NotificationQueueProvider` + `NotificationSlot`** (from `@aircall/ds`) — needed only if
your block compositions surface notifications/banners: wrap `NotificationQueueProvider`
(props `{ children }`) above any notification caller, and render a `NotificationSlot
slot="page"` (props `{ slot: string; className? }`) inside it.
Source: `packages/blocks/src/index.ts`; `@aircall/ds` `DsI18nProvider` / `NotificationQueueProvider`.
## Core Patterns
### Use a block composition
```tsx
import {
DashboardPageHeader,
DashboardPageHeaderTitle,
} from '@aircall/blocks';
function CampaignsHeader() {
return (
<DashboardPageHeader>
<DashboardPageHeaderTitle size="lg">Campaigns</DashboardPageHeaderTitle>
</DashboardPageHeader>
);
}
```
## Common Mistakes
### HIGH — Re-importing DS/blocks globals in a module-federation consumer
Wrong — in a consumer whose host already loads DS globals:
```css
@import 'tailwindcss';
@import '@aircall/ds/globals.css';
@import '@aircall/blocks/globals.css';
```
Correct — consumer owns only its own utilities, no Preflight:
```css
@layer theme, base, components, utilities;
@import 'tailwindcss/theme.css' layer(theme);
@import 'tailwindcss/utilities.css' layer(utilities);
@source './**/*.{ts,tsx}';
```
In a module-federation setup, CSS from each remote lands in the **same document**
as the host. DS globals ships a Preflight (`@layer base { * { border-color: var(--color-border); } }`)
that sets the token-based border color globally. If the consumer also imports DS globals
(or the full `@import 'tailwindcss'`), it emits a second `@layer base` reset. Because
webpack/Rsbuild emits deep dependencies first and the consumer's own CSS last, the
consumer's Preflight (`border-color: currentColor`) ends up later in the output — and
later wins. DS component borders (Card, etc.) lose their token color and become invisible.
### HIGH — Importing blocks globals before DS globals (or omitting DS globals)
Wrong — wrong order, or omitting DS globals:
```css
@import '@aircall/blocks/globals.css';
@import '@aircall/ds/globals.css';
```
```css
/* missing DS globals entirely */
@import '@aircall/blocks/globals.css';
```
Correct — DS globals first, then blocks globals:
```css
@import '@aircall/ds/globals.css';
@import '@aircall/blocks/globals.css';
```
`@aircall/blocks/globals.css` is a delta build — it contains only blocks-specific tokens
and utility classes. It depends on DS globals being loaded first to provide Preflight and
DS token definitions. Loading blocks before DS means DS's base reset (`border-color:
var(--color-border)`) lands after blocks and within the same `@layer base`, and component
tokens like `--background` and `--border` may not be defined when blocks tries to use them.
Source: aircall/hydra:docs/migration-guides/tractor-to-ds/00-setup.md (§3)
### MEDIUM — Using bare specifiers without `.css` in Rsbuild/webpack
Wrong:
```css
@import 'tailwindcss/theme';
@import 'tailwindcss/utilities';
```
Correct:
```css
@import 'tailwindcss/theme.css';
@import 'tailwindcss/utilities.css';
```
The bare form (no `.css`) silently produces no output in webpack/Rsbuild PostCSS
pipelines — classes like `flex`, `gap-4`, `text-sm` are never generated. Always
use the `.css` extension when splitting Tailwind imports.
### MEDIUM — Installing @aircall/blocks without @aircall/ds
Wrong:
```bash
pnpm add @aircall/blocks
```
Correct:
```bash
pnpm add @aircall/blocks @aircall/ds
```
Blocks import from `@aircall/ds` at runtime; without it the blocks barrel fails to resolve its peer and the build breaks.
Source: aircall/hydra:packages/blocks/package.json (peerDependencies)
### MEDIUM — Expecting a separate BlocksProvider
Wrong: searching for a `BlocksProvider` to mount.
Correct: blocks render under the DS providers from aircall-ds/setup; mount those, not a blocks-specific provider.
There is no blocks-specific provider; assuming one wastes time and produces dead wiring.
Source: aircall/hydra:docs/migration-guides/tractor-to-ds/00-setup.md (§4)