Skyscanner Backpack

repository·main·Indexed 19 days ago

https://github.com/skyscanner/backpack

Skyscanner's design system providing reusable UI components, design resources, and guidelines. Includes the @skyscanner/backpack-web library with components like AnimateHeight and BpkAccordion, as well as the Backpack Adoption Guard GitHub Action and Nx plugin to monitor and enforce design system adoption thresholds across projects.

Tokens
160.7K
Snippets
518
Records
783
Agent score
68%

What's inside Backpack

  1. Overview of token-sync

    main

    token-sync is a CLI tool that synchronizes design tokens from the Backpack Foundations & Components Figma file into the Backpack codebase. It operates in a two-stage pipeline:

    1. Figma → DTCG: Fetches Figma variables via REST API and transforms them into Design Tokens Community Group (DTCG) JSON files.
    2. DTCG → CSS: Uses Style Dictionary v5 to transform those JSON files into CSS custom properties.

    Note: This tool requires a Figma Enterprise organization. Non-Enterprise accounts will receive a 403 error even with a valid token.

  2. Overview of Backpack Design System

    main
    Backpack is a collection of design resources, reusable components, and guidelines used to create Skyscanner's products. It provides a suite of web components and foundational design elements to ensure consistency across applications.
  3. What is the Future API and how to use it?

    main

    The Future API is a mechanism used by the Backpack library to introduce breaking changes in a backwards-compatible way. It allows developers to opt-in to new behaviors, APIs, or component versions before they become the default in a subsequent major release.

    Key characteristics:

    • Opt-in basis: You must explicitly choose to use these features.
    • Breaking changes: Once the library moves to the next major version, the Future API features typically become the default, and the old Stable API is removed.
    • Coexistence: Future API features and Stable API features coexist in the same version to allow for a migration period (typically a minimum of 3 months).
  4. Handle React 19 defaultProps and prop-types migration

    main

    Because the standard React 19 migration recipe is destructive to Backpack's license headers and type definitions, a custom migration strategy is used for propTypes and defaultProps.

    Functional Components

    • propTypes: Removed from .tsx files via custom jscodeshift transforms. For .js files using Flow, propTypes removal is deferred until the full TS migration to avoid linting errors.
    • defaultProps: For functional components, static defaultProps or Component.defaultProps are migrated to ES6 destructuring defaults (e.g., const MyComponent = ({ prop = defaultValue }) => ...).

    Class Components

    • Class components continue to support static defaultProps in React 19.
    • For consistency, these should eventually be converted to functional components or use destructuring within the render() method.

    Edge Cases

    • Some .js story or HOC files may still contain Component.defaultProps = .... React 19 ignores these on functional components, so they will silently stop applying. These require manual cleanup.
  5. Understand BpkSlider event behavior

    main

    The BpkSlider behaves similarly to a native <input type="range"> but with specific differences in event firing:

    • Mouse/Touch Events: It fires change events from a hidden <input type="number"> for each thumb. These events are triggered when the user drags a thumb and on mouseup or click.
    • Keyboard Events: Unlike a native type="range" which fires on every keystroke, BpkSlider fires the change event on keyup.
  6. Understanding Deprecated APIs in Backpack

    main

    A Deprecated API is a feature (function, component, variable, type, or property) that is marked for removal from the Public API in a future major version.

    When you encounter a deprecated API in Backpack, it serves as a warning that you should migrate to the suggested alternative as soon as possible to avoid breaking changes when the feature is eventually removed. Deprecating an API is designed to be non-breaking; therefore, any deprecated props or arguments will always be made optional to ensure existing code continues to function.

  7. Use the polymorphic `as` prop in BpkLink

    main

    The as prop allows you to change the underlying HTML element while keeping the link's visual style. This is useful for semantic correctness when the link's behavior isn't standard navigation.

    Element Mapping:

    • "a" (default): Navigation to other pages or external URLs.
    • "button": Actions that don't navigate (e.g., opening modals, triggering functions).
    • "span": Non-interactive inline text with link styling (e.g., SEO, disabled states).
    • "div": Non-interactive block-level content with link styling.

    Note: When using as="button", as="span", or as="div", the component accepts props appropriate to that element type. For example, href is not valid for a button and will cause a TypeScript error.

    import BpkLink from '@skyscanner/backpack-web/bpk-component-link';
    
    // Rendering as a button (for actions without navigation)
    <BpkLink as="button" onClick={() => {}}>
      Trigger action
    </BpkLink>
    
    // Rendering as a span (non-interactive, for SEO or disabled states)
    <BpkLink as="span">
      Link styling without interaction
    </BpkLink>
    
    // Rendering as a div (block-level non-interactive element)
    <BpkLink as="div">
      Block-level link-styled text
    </BpkLink>
  8. Load Google Maps script with withGoogleMapsScript

    main

    The withGoogleMapsScript Higher-Order Component (HOC) handles loading the Google Maps JavaScript library before rendering the map.

    Usage Note: If you have multiple maps on a single page, wrap BpkMap with this HOC once. This ensures the Google Maps script is not re-downloaded for every map instance.

    Required Prop:

    • googleMapsApiKey: Your valid Google Maps API key.
    import BpkMap, {
      withGoogleMapsScript,
    } from '@skyscanner/backpack-web/bpk-component-map';
    
    const BpkMapWithScript = withGoogleMapsScript(BpkMap);
    
    export default () => (
      <BpkMapWithScript
        googleMapsApiKey="YOUR_API_KEY"
        zoom={15}
        center={{ latitude: 27.9881, longitude: 86.925 }}
      />
    );
  9. Manage BpkComparisonTray state and constraints

    main

    When implementing BpkComparisonTray, follow these consumer responsibilities:

    • Manage the items array: The tray only renders what you pass to the items prop.
    • Cap the array at 3 items: The tray only shows the first 3 items. Any additional items are silently ignored. It is recommended to disable "Add to compare" buttons in your UI once the limit is reached.
    • Control positioning: The component does not have built-in positioning; it is intended for use at the bottom of the viewport.
    • Accessibility (a11y): When an item is removed, the DOM node for the remove button is destroyed, which can cause focus to drop to the <body>. You must manually manage focus in your onRemove handler by moving focus to the next or previous remove button.