Skip to content
This repository was archived by the owner on Aug 5, 2026. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 56 additions & 0 deletions docs/figma-make-kit/guidelines/components/_component-template.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
<!-- NOTE: COPY this file to components/{name}.md for each component (e.g. button.md, text-input.md). Fill every section.
Cover: when to use, purpose, props, variants, examples (correct + incorrect), do/don't.
Start with your 10–20 most-used components. Model: a native React DS kit's per-component files. -->

# <ComponentName>

`<ExportName>` — <one-line description>.

## When to use

<!-- NOTE: When to reach for this component, and which components it's commonly used with/within. -->

Use `<ComponentName>` for <use case>. Always use the component from `<@scope/package-name>`, never a raw `<html-element>`.

## Variants

<!-- NOTE: List variants with guidance on frequency/when to use each. Delete if no variants. -->

| Variant | Use for |
| ------------- | ------------- |
| `<variant-a>` | <When to use> |
| `<variant-b>` | <When to use> |

IMPORTANT: Valid variants are `<list>` — nothing else. Do NOT invent variant names.

## Props

| Prop | Type | Default | Description |
| ----------- | --------- | ----------- | ---------------------- |
| `<prop>` | `<type>` | `<default>` | <description> |
| `className` | `string` | — | Additional CSS classes |
| `disabled` | `boolean` | `false` | Disable the component |

## Examples

```tsx
import { <ExportName> } from '<@scope/package-name>'

{/* CORRECT — common usage */}
<<ExportName> variant="<variant-a>">Label</<ExportName>>
```

```tsx
{
/* WRONG — <explain the anti-pattern, e.g. raw HTML or invalid variant> */
}
<button className="...">Label</button>;
```

## Rules

<!-- NOTE: Imperative do/don't specific to this component. -->

- <Rule 1, e.g. "Only one primary button per visible section.">
- <Rule 2, e.g. "Use iconStart/iconEnd for icons — not children.">
- Do not style this component with typography or font utility classes — it manages its own type.
59 changes: 59 additions & 0 deletions docs/figma-make-kit/guidelines/components/accordion.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# BpkAccordion

`BpkAccordion` — vertically stacked expand/collapse content sections. Purely presentational — expand/collapse state must be wired up externally or via one of two provided HOCs.

## When to use

Use `BpkAccordion` for stacked, collapsible sections (e.g. filters, FAQ). It does not manage state on its own — decide up front whether only one section should be open at a time or many.

## Variants

Not a prop — a choice of composition HOC:

| Pattern | Behavior |
| --- | --- |
| `withSingleItemAccordionState(BpkAccordion)` | Only one item open at a time |
| `withAccordionItemState(BpkAccordionItem)` | Each item manages its own independent open/close state |

## Props

**`BpkAccordion`**: `children` (required, `BpkAccordionItem`s), `divider` (boolean, default `true`), `onDark` (boolean, default `false`).

**`BpkAccordionItem`**:

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `id` | `string` | required | |
| `title` | `string` | required | Header text |
| `children` | `ReactNode` | required | Collapsible content |
| `expanded` | `boolean` | `false` | Controlled open state |
| `icon` | `ReactElement` | `null` | Leading icon |
| `onClick` | `() => void` | no-op | Toggle handler |
| `tagName` | `Tag` | `'h3'` | Heading semantic tag |
| `textStyle` | `TextStyle` | `bodyDefault` | |

## Examples

```tsx
import { BpkAccordion, BpkAccordionItem, withSingleItemAccordionState } from '@skyscanner/backpack-web/bpk-component-accordion';

const SingleItemAccordion = withSingleItemAccordionState(BpkAccordion);

{/* CORRECT — state managed by the HOC */}
<SingleItemAccordion>
<BpkAccordionItem id="stops" title="Stops" initiallyExpanded>Filter content</BpkAccordionItem>
<BpkAccordionItem id="airlines" title="Airlines">Filter content</BpkAccordionItem>
</SingleItemAccordion>
```

```tsx
{/* WRONG — no expanded/onClick wired and no HOC; clicking the header does nothing */}
<BpkAccordion>
<BpkAccordionItem id="stops" title="Stops">Filter content</BpkAccordionItem>
</BpkAccordion>
```

## Rules

- `BpkAccordionItem` alone is not interactive — always wire `expanded`/`onClick` yourself or use `withSingleItemAccordionState`/`withAccordionItemState`.
- Use `withSingleItemAccordionState` when only one section should be open at a time; use `withAccordionItemState` when multiple sections can be open independently.
54 changes: 54 additions & 0 deletions docs/figma-make-kit/guidelines/components/badge.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# BpkBadge

`BpkBadge` — a small inline status/label indicator, typically paired with text, rendered as a `<span>`.

## When to use

Use `BpkBadge` for status labels, counters, and tags next to other content (e.g. "Cheapest", "Sold out", "New"). Always use `BpkBadge` from `@skyscanner/backpack-web/bpk-component-badge`, never a raw styled `<span>`.

## Variants

| Variant (`BADGE_TYPES`) | Use for |
| --- | --- |
| `normal` (default) | Neutral label |
| `success` | Positive status |
| `warning` | Cautionary status |
| `critical` | Negative/urgent status |
| `strong` | High-emphasis neutral label |
| `brand` | Brand-colored label |
| `subtle` | Low-emphasis label |
| `inverse` | For use on dark backgrounds |
| `outline` | For use on dark or image backgrounds |

IMPORTANT: Valid `type` values are exactly the `BADGE_TYPES` above — do NOT invent a variant name.

## Props

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `type` | `BadgeType` | `normal` | See variants table |
| `docked` | `'right'\|'left' \| null` | `null` | Docks the badge to an edge (e.g. overlaying imagery) |
| `centered` | `boolean` | `false` | Vertically aligns the badge to the center of surrounding text |
| `children` | `string \| ReactNode` | required | Text and/or icon content |
| `className` | `string \| null` | `null` | Additional CSS class |

## Examples

```tsx
import BpkBadge, { BADGE_TYPES } from '@skyscanner/backpack-web/bpk-component-badge';
import BpkSmallTickIcon from '@skyscanner/backpack-web/bpk-component-icon/sm/tick';

{/* CORRECT — icon placed as a child, badge has no dedicated icon slot */}
<BpkBadge type={BADGE_TYPES.success}><BpkSmallTickIcon /> Confirmed</BpkBadge>
```

```tsx
{/* WRONG — leadingIcon is not a BpkBadge prop */}
<BpkBadge type={BADGE_TYPES.success} leadingIcon={<BpkSmallTickIcon />}>Confirmed</BpkBadge>
```

## Rules

- There is no `size` prop — badges have one fixed size.
- Icons go directly in `children`, alongside the text — `BpkBadge` has no `leadingIcon`/`trailingIcon` slot (unlike `BpkButton`).
- Choose `type` by the status it represents (`success`/`warning`/`critical`), never by picking a color that "looks right."
46 changes: 46 additions & 0 deletions docs/figma-make-kit/guidelines/components/breadcrumb.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# BpkBreadcrumb

`BpkBreadcrumb` — shows the page hierarchy/path as a `nav` + list of links, with optional schema.org structured-data output.

## When to use

Use `BpkBreadcrumb` for hierarchical wayfinding on deep pages (e.g. Home > Hotels > Amsterdam).

## Variants

No type/variant enum — purely structural.

## Props

**`BpkBreadcrumb`**: `label` (string, required — `aria-label` on the `<nav>`), `children` (required, `BpkBreadcrumbItem`s), `schemaMetaData` (optional `{url, label}[]` — renders a JSON-LD BreadcrumbList).

**`BpkBreadcrumbItem`**:

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `children` | `ReactNode` | required | Item label |
| `active` | `boolean` | `false` | Marks the current page — renders as plain text with `aria-current="page"`, ignores `href` |
| `href` | `string` | `'#'` | Link target (non-active items only) |

## Examples

```tsx
import BpkBreadcrumb, { BpkBreadcrumbItem } from '@skyscanner/backpack-web/bpk-component-breadcrumb';

{/* CORRECT — exactly one active item, at the end */}
<BpkBreadcrumb label="Breadcrumb">
<BpkBreadcrumbItem href="/">Home</BpkBreadcrumbItem>
<BpkBreadcrumbItem href="/hotels">Hotels</BpkBreadcrumbItem>
<BpkBreadcrumbItem active>Amsterdam</BpkBreadcrumbItem>
</BpkBreadcrumb>
```

```tsx
{/* WRONG — giving href to the active item; it's ignored, active items always render as text */}
<BpkBreadcrumbItem href="/amsterdam" active>Amsterdam</BpkBreadcrumbItem>
```

## Rules

- Exactly one item should be `active`, typically the last.
- Don't add your own separator between items — non-active items render a trailing arrow separator automatically (RTL-aware).
70 changes: 70 additions & 0 deletions docs/figma-make-kit/guidelines/components/button.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# BpkButton

`BpkButton` — the primary interactive action control; also covers link-styled actions and pure navigation links via `href`.

## When to use

Use `BpkButton` for any clickable action — form submission, triggering a modal, navigating (via `href`). Always use `BpkButton` from `@skyscanner/backpack-web/bpk-component-button`, never a raw `<button>` or `<a>` styled to look like one.

## Variants

| Variant (`BUTTON_TYPES`) | Use for |
| --- | --- |
| `primary` | Main/default call to action |
| `primaryOnDark` | Primary CTA on a dark background |
| `primaryOnLight` | Primary CTA variant tuned for light backgrounds |
| `secondary` | Secondary action alongside a primary |
| `secondaryOnDark` | Secondary action on a dark background |
| `destructive` | Destructive/dangerous action (delete, remove, cancel booking) |
| `featured` | Highlighted/promotional action |
| `link` | Button styled like a text link (underlined) but keeps button semantics |
| `linkOnDark` | Link-style button for dark backgrounds |

IMPORTANT: Valid `type` values are exactly the `BUTTON_TYPES` above — do NOT invent a variant name.

`SIZE_TYPES`: `small` (default) | `large`.

## Props

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `type` | `ButtonType` | `primary` | Visual variant, see table above |
| `size` | `SizeType` | `small` | `small` \| `large` |
| `disabled` | `boolean` | `false` | Disables interaction |
| `loading` | `boolean` | `false` | Shows a spinner in place of the label, disables interaction, sets `aria-busy` |
| `fullWidth` | `boolean` | `false` | Stretches the button to its container's width |
| `iconOnly` | `boolean` | `false` | Icon-only button; **requires an `aria-label`** |
| `leadingIcon` / `trailingIcon` | `ReactNode` | `null` | Icon slots — auto-aligned and spaced; do not pass icons as children instead |
| `href` | `string \| null` | `null` | Renders as `<a>` instead of `<button>` when set and not disabled |
| `blank` | `boolean` | `false` | Sets `target="_blank"` with `rel="noopener noreferrer"` by default |
| `submit` | `boolean` | `false` | Native button `type="submit"` instead of `"button"` |
| `onClick` | `(e) => void` | — | Click handler |
| `children` | `string \| ReactNode` | required | Button label |
| `className` | `string` | — | Additional CSS class |

## Examples

```tsx
import BpkButton, { BUTTON_TYPES } from '@skyscanner/backpack-web/bpk-component-button';
import BpkSmallLongArrowRightIcon from '@skyscanner/backpack-web/bpk-component-icon/sm/long-arrow-right';

{/* CORRECT — icon passed via trailingIcon, not as a child */}
<BpkButton type={BUTTON_TYPES.link} trailingIcon={<BpkSmallLongArrowRightIcon />}>
Learn more
</BpkButton>
```

```tsx
{/* WRONG — deprecated icon-as-sibling pattern; use trailingIcon instead */}
<BpkButton type={BUTTON_TYPES.link}>
Learn more <BpkSmallLongArrowRightIcon />
</BpkButton>
```

## Rules

- Icons inside `BpkButton` must use the `leadingIcon`/`trailingIcon` props — never place a raw icon as a child, and never wrap it in `withButtonAlignment` (the button already handles alignment/spacing).
- `iconOnly` requires an explicit `aria-label` — an icon-only button with no label is an accessibility failure.
- `disabled` and `loading` are functionally equivalent for interaction purposes — don't gate on one when you mean both.
- Only one `primary`/`featured` button per visible section — use `secondary` or `link` for accompanying actions.
- Do not style this component with typography or font utility classes — it manages its own type.
54 changes: 54 additions & 0 deletions docs/figma-make-kit/guidelines/components/card.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# BpkCardV2

`BpkCardV2` — a composable container for grouping related content, with explicit `Header`/`Body`/`Footer`/`Section`/`Divider` slots and responsive multi-column layout.

## When to use

Use `BpkCardV2` for any card/tile surface. It's the current, preferred card API — prefer it over the older `BpkCard` for new composition work. `BpkCard` (v1) remains a simple button/link/div wrapper with no Header/Body/Footer composition; only reach for it if you specifically need its plain clickable-container behavior and not the composed layout.

## Variants

| Variant (`CARD_V2_VARIANTS`) | Use for |
| --- | --- |
| `default` | Standard interactive card — drop shadow, lifts on hover |
| `outlined` | Border instead of shadow, no hover elevation |
| `noElevation` | Flat, no shadow/border |
| `carsPrompt` | Bespoke fixed-background variant for cars prompts — `bgColor` cannot be set with this variant |

`CARD_V2_SURFACE_COLORS` (the `bgColor` prop, default `surfaceDefault`): `surfaceDefault`, `surfaceElevated`, `surfaceTint`, `surfaceSubtle`, `surfaceHero`, `surfaceContrast`, `surfaceLowContrast`, `surfaceHighlight`.

## Props

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `variant` | `BpkCardV2Variant` | `default` | See table above |
| `bgColor` | `BpkCardV2SurfaceColor` | `surfaceDefault` | Invalid when `variant="carsPrompt"` |
| `children` | `ReactNode` | required | Use `.Header`/`.Body`/`.Footer` subcomponents |

`BpkCardV2.Body` accepts `templateColumns` (a `BpkGrid` prop) for multi-column layout; pair each `Section` with a `Divider`.

## Examples

```tsx
import BpkCardV2, { CARD_V2_VARIANTS, CARD_V2_SURFACE_COLORS } from '@skyscanner/backpack-web/bpk-component-card';

{/* CORRECT */}
<BpkCardV2.Root variant={CARD_V2_VARIANTS.outlined} bgColor={CARD_V2_SURFACE_COLORS.surfaceElevated}>
<BpkCardV2.Header>Amsterdam Hostel</BpkCardV2.Header>
<BpkCardV2.Body>Content</BpkCardV2.Body>
<BpkCardV2.Footer>From $42/night</BpkCardV2.Footer>
</BpkCardV2.Root>
```

```tsx
{/* WRONG — bgColor cannot be combined with the carsPrompt variant */}
<BpkCardV2.Root variant={CARD_V2_VARIANTS.carsPrompt} bgColor={CARD_V2_SURFACE_COLORS.surfaceTint}>
...
</BpkCardV2.Root>
```

## Rules

- Always compose via `BpkCardV2.Root` / `.Header` / `.Body` / `.Footer` — don't nest raw `<div>`s inside a card instead of the provided slots.
- `bgColor` is not settable when `variant="carsPrompt"`.
- Prefer surface color and the `variant` prop over any custom shadow/border styling.
45 changes: 45 additions & 0 deletions docs/figma-make-kit/guidelines/components/checkbox.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# BpkCheckbox

`BpkCheckbox` — a self-labeling checkbox; renders as a `<label>` wrapping a checkbox input plus a visual box.

## When to use

Use `BpkCheckbox` for an independent boolean choice inside a form (e.g. "I accept the terms"). Unlike `BpkInput`/`BpkSelect`, it is self-labeling — do not wrap it in a separate `BpkLabel`.

## Variants

No semantic type enum. States: `valid` (boolean, `false` → invalid style), `white` (for dark/contrast backgrounds), `indeterminate` (visual-only, doesn't affect `checked`), `smallLabel`, `required` (shows an asterisk, hidden when disabled).

## Props

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `name` | `string` | required | |
| `label` | `ReactNode` | required | Visible label text (and `aria-label` if a string) |
| `checked` | `boolean` | `false` | |
| `required` | `boolean` | `false` | Shows `*`, hidden when disabled |
| `disabled` | `boolean` | `false` | |
| `white` | `boolean` | `false` | For dark backgrounds |
| `indeterminate` | `boolean` | `false` | Visual minus-sign state only |
| `valid` | `boolean \| null` | `null` | |
| `onChange` | `(e) => void` | — | |

## Examples

```tsx
import BpkCheckbox from '@skyscanner/backpack-web/bpk-component-checkbox';

{/* CORRECT — label is a prop, not a separate BpkLabel */}
<BpkCheckbox name="terms" label="I accept the terms" checked={checked} onChange={onChange} />
```

```tsx
{/* WRONG — unnecessary; BpkCheckbox is already the <label> */}
<BpkLabel htmlFor="terms">I accept the terms</BpkLabel>
<BpkCheckbox name="terms" label="I accept the terms" checked={checked} onChange={onChange} />
```

## Rules

- Never wrap `BpkCheckbox` in a separate `BpkLabel` — it self-labels via its own `label` prop.
- `indeterminate` is visual only — it does not change the underlying `checked` value; manage that separately if needed.
Loading
Loading