# The canvas and breakpoints

> The canvas shows your running site at every breakpoint, side by side. How to move around it, how a breakpoint becomes a Tailwind screen, and what an override writes.

- Page: https://midcode.app/docs/editor/canvas
- From the midcode docs. Every page as Markdown: https://midcode.app/llms.txt

The canvas is the middle of the window: your site, running, once per breakpoint. Each frame is a real page of your dev server at that width, so what you see is what your code does. You pan and zoom over all of them like a design file.

A change made in one frame is written for that breakpoint. That's the whole model: one breakpoint is the rule, the others take overrides, and both end up as ordinary responsive classes in your code.

## What a frame is

Each frame loads the page that's open (see [Pages and navigation](https://midcode.app/docs/editor/pages.md)) from your dev server, at its breakpoint's width. On the canvas the page is in design mode:

- A click selects an element. It doesn't follow links, press buttons or submit forms. To use the site, open the [Preview](#preview).
- The frame is as tall as the whole page, so every section is on the canvas at once and the page itself never scrolls. Scrolling moves the canvas.
- Viewport units (`vh`, `svh`, `dvh`, `lvh`) mean the breakpoint's device height, not the frame's: a `min-h-screen` hero stays one screen tall. The device height is 900 px for a width of 1024 or more, 1024 px from 600, and 844 px below that.
- Motion rests while you design. Looping animations and videos are paused, and one-shot animations are shown finished, so a title that fades in is visible instead of stuck at zero opacity.
- Embeds (a YouTube player, a map, an audio player) can be hovered and selected like any other element.

Over the bottom of the canvas floats the toolbar: "Insert" (the +, see [Insert](https://midcode.app/docs/editor/insert.md)), "Inspect" (`V`), "Comment" (`C`, see [Comments](https://midcode.app/docs/editor/comments.md)) and "Preview" (`P`). Next.js App Router projects also have "Frame: draw one on the canvas" (`F`), for [the free canvas](https://midcode.app/docs/editor/free-canvas.md).

## Move around

| To | Do this |
| --- | --- |
| Pan | Scroll with two fingers or the wheel, anywhere, also over a frame. |
| Pan by dragging | Hold `Space` and drag. Or drag the empty canvas with the middle mouse button. |
| Zoom | Pinch, or hold `⌘` and scroll. It zooms on the pointer. |
| Zoom in, zoom out | `⌘=` and `⌘-` |
| Zoom to fit every frame | `⌘1`, `⇧1`, or click the percentage in the top bar. |
| Actual size (100%) | `⌘0` |
| Fit the first frame | `⇧2` |

The zoom goes from 5% to 400%. Right-click the empty canvas for the same commands as a menu: "Preview", "Zoom to fit", "Actual size", "Paste", "Show rulers" / "Hide rulers", "Reload breakpoints" and "Deselect".

Double-click a breakpoint's row in [Layers](https://midcode.app/docs/editor/layers.md) to bring that frame into view.

## Breakpoints

A project starts with three, until you change them:

| Breakpoint | Width | Tailwind screen |
| --- | --- | --- |
| Desktop | 1440 | `lg` |
| Tablet | 768 | `md` |
| Phone | 390 | none (no prefix) |

They sit on the canvas from the widest to the narrowest. Above each frame is its bar:

- The play button opens that breakpoint in the [Preview](#preview). So does a double-click on the bar.
- The name and width open "Edit breakpoint".
- "Primary" marks the primary breakpoint.
- A third number, like `× 5230`, is the page's height when it's taller than the device.
- The + on the first bar is "New breakpoint".

Right-click a bar for all of it: "Preview Desktop", "Edit breakpoint…", "Make primary", "New breakpoint…" and "Remove breakpoint".

### Add, edit and remove

1. Click the + on the first bar, or right-click any bar and choose "New breakpoint…".
2. Type a name and a width in px (240 to 3840). The name is optional: without one, the breakpoint is called by its width.
3. Click "Add".

A new frame appears at that width, in its place by size. Two breakpoints can't have the same width.

"Edit breakpoint…" changes the name or the width. A new width also gives the frame the device height that goes with it, and a new screen if the old one no longer fits. "Remove breakpoint" takes the frame away; the last breakpoint can't be removed. Removing the primary makes the widest one primary.

> [!NOTE]
> Adding, editing or removing a breakpoint changes `.midcode/breakpoints.json` and nothing else. Classes already in your code are not rewritten.

### The primary breakpoint and overrides

One breakpoint is the primary: Desktop, until you choose another with "Make primary". The rule is the one Framer uses:

- A change made in the primary frame applies to every breakpoint that doesn't say otherwise.
- A change made in any other frame is an override for that breakpoint. It also carries on to the breakpoints further from the primary that were showing the same value: override a size on Tablet and Phone follows, until Phone gets a value of its own.

Which breakpoint you're editing is the frame you selected the element in. The right panel says so in its "Breakpoint" row, where you can also switch, and picking a layer under a breakpoint in [Layers](https://midcode.app/docs/editor/layers.md) selects it there.

In a breakpoint that isn't the primary, the panel reads "Changes here override Desktop. Overridden properties show in blue." A property with an override has a blue label. Hover it to see what it overrides, and right-click it for "Remove override": the breakpoint follows its neighbour toward the primary again.

### How a breakpoint becomes a Tailwind screen

Overrides are written as Tailwind's responsive prefixes, mobile first. So each breakpoint needs a screen, and midcode picks it:

- The narrowest breakpoint writes with no prefix.
- Every other breakpoint gets a screen that starts above the next narrower breakpoint's width and no later than its own. That keeps each frame in its own range, so an override never leaks into its neighbour.
- A named screen is used when one fits (`sm` 640, `md` 768, `lg` 1024, `xl` 1280, `2xl` 1536). If none fits, midcode writes an exact one in rem: a breakpoint at 1000 px gets `min-[62.5rem]`.

Screens are worked out again whenever the list changes. Add a "Laptop" at 1200 to the defaults and it takes `lg`, which moves Desktop to `xl`:

| Breakpoint | Width | Screen before | Screen after |
| --- | --- | --- | --- |
| Desktop | 1440 | `lg` | `xl` |
| Laptop | 1200 | | `lg` |
| Tablet | 768 | `md` | `md` |
| Phone | 390 | none | none |

In a project without Tailwind 4 the same prefixes go after midcode's own: `mid:md:text-4xl`. See [Without Tailwind](https://midcode.app/docs/styling/without-tailwind.md).

## What midcode writes

The value at each breakpoint becomes one class at that breakpoint's screen, from the narrowest up. Neighbours with the same value share a class. Here is one heading through four edits of its font size, with the default breakpoints and Desktop as primary:

| You do | The heading's classes after |
| --- | --- |
| Set 5xl in Desktop | `font-semibold text-5xl` |
| Set 4xl in Tablet | `font-semibold text-4xl lg:text-5xl` |
| Set 3xl in Phone | `font-semibold text-3xl lg:text-5xl md:text-4xl` |
| "Remove override" in Tablet | `font-semibold text-3xl md:text-5xl` |

The second row shows the carry-on: the Tablet override reached Phone too, so the unprefixed class is the Tablet value and Desktop keeps its own behind `lg:`. As a diff:

```diff title="src/app/page.tsx"
-<h1 className="font-semibold text-5xl">Design in code</h1>
+<h1 className="font-semibold text-4xl lg:text-5xl">Design in code</h1>
```

A class that changes is swapped where it stands, and new ones go at the end of the list. The order of classes in the attribute doesn't change what the browser shows.

When a narrower breakpoint has a value and a wider one has none, the wider one gets the value that means "nothing". Padding given to a stack on Phone only:

```diff title="src/components/Card.tsx"
-<div className="flex">
+<div className="flex p-4 md:p-0">
```

Every style edit goes through this, from the [style panel](https://midcode.app/docs/editor/styles.md) to a resize handle. [Tailwind CSS](https://midcode.app/docs/styling/tailwind.md) has the rest: arbitrary values, your theme's tokens, and classes built with `cn()`.

### .midcode/breakpoints.json

Your breakpoints are saved in the project the first time you change them. Until then the file doesn't exist and the defaults apply. This is the file after adding the Laptop breakpoint above:

```json title=".midcode/breakpoints.json"
{
  "version": 1,
  "list": [
    {
      "id": "desktop",
      "label": "Desktop",
      "width": 1440,
      "height": 900,
      "screen": "xl"
    },
    {
      "id": "laptop-1200",
      "label": "Laptop",
      "width": 1200,
      "height": 900,
      "screen": "lg"
    },
    {
      "id": "tablet",
      "label": "Tablet",
      "width": 768,
      "height": 1024,
      "screen": "md"
    },
    {
      "id": "phone",
      "label": "Phone",
      "width": 390,
      "height": 844,
      "screen": ""
    }
  ],
  "primary": "desktop"
}
```

- `height` is the device height that viewport units mean in that frame.
- `screen` is the prefix its overrides are written with. Write your own (`"screen": "xl"`) and midcode keeps it as long as it fits the rule above. One that doesn't fit is ignored, and replaced the next time midcode saves the file.
- `primary` is the `id` of the primary breakpoint.

Commit the file so everyone who opens the project gets the same frames. Delete it to go back to the defaults. [What midcode adds to your project](https://midcode.app/docs/start/project-files.md) lists every file of this kind.

## Preview

The canvas never runs the site. The Preview does: one breakpoint, full size, scrolling and taking clicks like a browser, with its animations, menus and videos.

1. Press `P`, or click "Preview" in the toolbar. That opens the primary breakpoint. The play button on a breakpoint's bar opens that one.
2. Use the site.
3. Press `Esc`, or click "Back", to return to the canvas. `P` closes it too until you click into the site: from then on the site has the keyboard and `P` is its key.

The bar over the preview has "Reload", "Hide UI (full screen)", a menu to switch breakpoint, and the width and height, which you can type. Drag either side of the frame to try any width: the breakpoint then reads "Custom". With the interface hidden, "Show UI" in the corner (or `Esc`) brings it back. A frame too big for the window is scaled down, and the scale is shown under it.

Nothing is edited in the Preview. Opened from a component, it shows that component alone: see [Components](https://midcode.app/docs/editor/components.md).

## Reload

Your dev server's hot reload brings each edit into every frame without loading the page again. When a frame gets stuck anyway, click "Reload breakpoints" in the top bar (the circular arrow), or right-click the empty canvas and choose it. Every frame loads the page again.

## The top bar

- Left: the project's name, its branch, and the page that's open. See [Pages and navigation](https://midcode.app/docs/editor/pages.md) and [Publish](https://midcode.app/docs/publish/publish.md).
- Middle: "Design" and "Code", two ways into the same project. "Code" swaps the canvas for your files, an editor and a terminal: see [Code view](https://midcode.app/docs/editor/code.md).
- Right: the dev server's log ([Open a project](https://midcode.app/docs/start/open-a-project.md)), the site's languages ([Languages](https://midcode.app/docs/data/languages.md)), "CMS" ([CMS](https://midcode.app/docs/data/cms.md)), "Site settings" in the projects that have them ([Site settings](https://midcode.app/docs/editor/site-settings.md) says which), "Reload breakpoints", the zoom, and "Publish".

"Reload breakpoints" and the zoom belong to the canvas. They're hidden in the Code view.

### CMS, Database and Store in the middle (next release)

In the next release the middle of the bar lists every place of a project: "Design", "Code", "CMS", "Database" and, in a Shopify theme, "Store". [CMS](https://midcode.app/docs/data/cms.md), [Database](https://midcode.app/docs/data/database.md) and [Store](https://midcode.app/docs/shopify/store.md) open over the canvas, which stays loaded underneath, and "Reload breakpoints" and the zoom show only while the canvas itself is on screen. In version 1.1.2, CMS is a button on the right of the bar and the other two don't exist.

## Limits

- A breakpoint is a width. There are no height breakpoints, and midcode doesn't write screens that end at a width (`max-md:`). A class in your code with a prefix it doesn't know is left where it is: the panel doesn't read it and never rewrites it.
- A frame grows to 40,000 px. A page taller than that is cut off on the canvas.
- The canvas shows one page at a time, the same in every frame.
- Text styles and link styles from your theme can't be overridden per breakpoint: they're always written for every breakpoint.
