Skip to content

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.

View as Markdown

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) 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.

  • 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), “Inspect” (V), “Comment” (C, see Comments) and “Preview” (P). Next.js App Router projects also have “Frame: draw one on the canvas” (F), for the free canvas.

Move around

ToDo this
PanScroll with two fingers or the wheel, anywhere, also over a frame.
Pan by draggingHold Space and drag. Or drag the empty canvas with the middle mouse button.
ZoomPinch, 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 to bring that frame into view.

Breakpoints

A project starts with three, until you change them:

BreakpointWidthTailwind screen
Desktop1440lg
Tablet768md
Phone390none (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. 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.

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 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:

BreakpointWidthScreen beforeScreen after
Desktop1440lgxl
Laptop1200lg
Tablet768mdmd
Phone390nonenone

In a project without Tailwind 4 the same prefixes go after midcode’s own: mid:md:text-4xl. See Without Tailwind.

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 doThe heading’s classes after
Set 5xl in Desktopfont-semibold text-5xl
Set 4xl in Tabletfont-semibold text-4xl lg:text-5xl
Set 3xl in Phonefont-semibold text-3xl lg:text-5xl md:text-4xl
“Remove override” in Tabletfont-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:

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:

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 to a resize handle. Tailwind CSS 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:

.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 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.

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 and Publish.

  • 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.

  • Right: the dev server’s log (Open a project), the site’s languages (Languages), “CMS” (CMS), “Site settings” in the projects that have them (Site settings 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 middleNext 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, Database and Store 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.