Skip to content
buttery

v1.0.0

Source on GitHub

Version 1.0.0

Make an existing app buttery

Bringing buttery into an app that already has its own styles and components, a screen at a time.

This guide moves an existing React app onto buttery without a rewrite: wire up the provider, check the layout, then convert a screen at a time from the top down. Follow it when an app already works and you want it to look, move and lay out like buttery.

Move the app across

  1. Wire it up

    bash

    npx buttery init

    buttery init does this step for you, with buttery's lint configs and what coding agents read, and npx buttery doctor checks the result. By hand, import buttery's two stylesheets at the app's entry and wrap the app in ButteryProvider:

    src/main.tsx

    import "@butteryui/core/styles.css";
    import "@butteryui/react/styles.css";
    import { StrictMode } from "react";
    import { createRoot } from "react-dom/client";
    import { ButteryProvider } from "@butteryui/react";
    import { App } from "./App.tsx";
    
    const container = document.getElementById("root");
    if (!container) throw new Error("index.html needs a #root element.");
    
    createRoot(container).render(
      <StrictMode>
        <ButteryProvider colorScheme="light">
          <App />
        </ButteryProvider>
      </StrictMode>,
    );

    buttery's styles sit in CSS cascade layers, so a style the app sets itself still wins. Whatever the app leaves to the browser changes, through buttery's base styles and a small reset:

    Left to the browserWith buttery
    The page's fontInter, with body text at 14px on a 20px line
    The page's background and textbuttery's colors
    Box sizingbox-sizing: border-box on every element
    Headings, paragraphs and listsNo default margins
    Images and SVGsBlocks no wider than their container
    Form controlsTheir font from the page
    Selected textbuttery's selection color
    Keyboard focusbuttery's focus ring
    ScrollbarsThin, in the border color

    Light and dark. ButteryProvider follows the system's light or dark setting, which switches those colors. Pass colorScheme="light", as above, until the app's own styles are ready for dark.

    The app also gains buttery's tokens, the layers that menus, dialogs and toasts open into, and LayoutGuard in development.

  2. See where it stands

    Run the app in development and narrow the window from wide to a phone's width. LayoutGuard outlines what overlaps, overflows or sits off the spacing scale.

    Then add a Playwright test for each main page, and keep its report as the list of what to fix:

    e2e/layout.spec.ts

    import { test } from "@playwright/test";
    import { expectButtery, quickMatrix } from "@butteryui/test";
    
    test("the home page is buttery", async ({ page }) => {
      await page.goto("/");
      await expectButtery(page, { ...quickMatrix, name: "home" });
    });
  3. Convert a screen, top down

    Take one screen at a time, from the whole screen down to its values:

    LevelWhat to do
    The screenMatch it to a pattern if one fits: AppShell with AppPage, a settings page, a detail page
    Its regionsGive each a Surface level, and lay out siblings with Stack, Inline and Grid, dropping margins for gaps
    Its controlsSwap each for its buttery component: Button, TextField, Select, Switch
    Anything without a matchWrap it in buttery(Component, { surface: "card" }), so it takes the surface and motion recipes
    Its valuesReplace raw colors, sizes, radii and durations with tokens; buttery's stylelint config lists the ones left
  4. Keep it there

    Add @butteryui/lint's configs and the expectButtery tests to the app's checks, so a screen stays buttery once it's converted. Lint configs has both config files.

  • Make anything buttery

    The four ways to bring buttery into an app, from wiring up the provider to whole-page patterns.

  • Check your layout

    Catching overlaps, overflow and layout shift while you work and in your tests.