# Plain HTML

> How midcode serves a folder of .html files itself, marks each element as it sends the page, edits the files in place and links midcode.css from every page.

- Page: https://midcode.app/docs/frameworks/html
- From the midcode docs. Every page as Markdown: https://midcode.app/llms.txt
- Status: This ships with the next release of midcode. The version you can download today (1.1.2) doesn't have it yet.

midcode edits a folder of `.html` files with no framework and no build step. It serves the folder itself, reloads the page when a file changes, and writes each edit (text, classes, attributes, new elements, their order) into the `.html` file, with the rest of the file left byte for byte as it was. You don't need Node or a `package.json`. midcode 1.1.2 and earlier open a folder of HTML to check every breakpoint and leave comments, without editing.

## At a glance

| | Plain HTML |
| --- | --- |
| Detected by | An `index.html` at the folder's root, and nothing else midcode knows how to run |
| Runs with | midcode's own static server |
| Elements are marked by | That server, as it sends each page |
| Editing | Text, classes, attributes, tag; insert, move, duplicate, delete, wrap in a stack |
| Components | None: every element is written where it shows |
| Pages | Every `.html` file, at the path it's served from. New pages at the top of the folder |
| Styles | `mid:` utilities, with `midcode.css` at the folder's root and a `<link>` in every page |
| Tried with | A plain HTML site, and the one New project creates |

## How midcode runs it

A folder is plain HTML when it has an `index.html` at its root and midcode finds nothing else to run:

- none of the files that give away another stack (`artisan`, `manage.py`, `_config.yml`, `index.php` and the rest of the list in [Any other stack](https://midcode.app/docs/frameworks/custom-server.md));
- and either no `package.json`, or one with none of the frameworks midcode knows and no `dev` script.

midcode then serves the folder on `http://localhost:4310` (the first free port from there) and writes one line in the log: `midcode serves <folder> on http://localhost:4310`. The server is small on purpose:

- A folder's address answers with its `index.html`, and `/about` answers with `about.html`.
- Nothing is cached, so what you see is what's on disk.
- It reloads every frame when a `.html`, `.css`, `.js`, `.json` or image file in the folder changes, whoever changed it: midcode, your editor or an agent.

### How elements get their marks

The marks are added to each page as the server sends it. Your file:

```html title="index.html"
<!doctype html>
<html lang="en">
  <head>
    <title>Oak and Iron</title>
    <link rel="stylesheet" href="styles.css">
  </head>
  <body>
    <main>
      <h1>Welcome</h1>
      <p class="lead">Handmade furniture.</p>
    </main>
  </body>
</html>
```

What the canvas receives:

```html
  <body data-mc="index.html:7:3">
    <main data-mc="index.html:8:5">
      <h1 data-mc="index.html:9:7">Welcome</h1>
      <p data-mc="index.html:10:7" class="lead">Handmade furniture.</p>
    </main>
  <script>new EventSource('/__midcode/reload').onmessage = () => location.reload()</script></body>
```

`data-mc` is the file, line and column where the element is written. The script at the end is what reloads the page. Both exist only in what's served: the file on disk is never given a mark or a script. `<html>`, `<head>` and everything in it, `<script>`, `<style>` and `<noscript>` aren't marked.

## What you can edit

Click an element and the right panel shows where it's written, as `file:line`. The editor's pages describe each tool: [select, move and resize](https://midcode.app/docs/editor/select-move-resize.md), [text and media](https://midcode.app/docs/editor/text-and-media.md), [the style panel](https://midcode.app/docs/editor/styles.md), [Insert](https://midcode.app/docs/editor/insert.md), [Layers](https://midcode.app/docs/editor/layers.md).

**Text.** Double-click and type:

```diff title="index.html"
-      <h1>Welcome</h1>
+      <h1>Welcome back</h1>
```

A text is edited when everything inside its element is written text. A `<` you type is written as `&lt;`.

**Styles.** The style panel writes classes on the element (see [Styles](#styles) below):

```diff title="index.html"
-      <p class="lead">Handmade furniture.</p>
+      <p class="lead mid:text-[20px] mid:md:text-[18px]">Handmade furniture.</p>
```

**Attributes.** A link's `href`, an image's `alt`, an input's type and placeholder, a `<select>` and its options are read from the tag and written back as plain attributes: a text as `href="/about.html"`, a switch that's on as `disabled`, a number as `maxlength="40"`.

**Structure.** Insert adds plain markup at the selection or at the end of the page. Drag an element before, after or inside another one in the same file, and the comment right above it travels with it. Several elements can be deleted, moved or wrapped in a stack (`⇧A`) at once, and `⌥`-drag leaves the element where it is and puts a copy where you drop it, in the same file.

midcode's reader is tolerant on purpose. An `<li>` or a `<p>` that's never closed, an attribute without quotes or a stray closing tag are read as they are and stay as they are: only the range you edit changes, and nothing is tidied or reformatted. A tag with a dash in its name (a custom element) is an element like any other.

## Pages

Every `.html` file in the folder is a page, at the path the server answers it from:

| File | Page |
| --- | --- |
| `index.html` | The home page |
| `about.html` | `/about.html` |
| `blog/index.html` | `/blog` |
| `blog/first-post.html` | `/blog/first-post.html` |

`node_modules` and folders whose name starts with a dot or `_` aren't listed. Pick a page from the page menu in the top bar ([Pages and navigation](https://midcode.app/docs/editor/pages.md)).

"New page" in that menu writes a new `.html` file at the top of the folder. Type its path without the extension: `/about` makes `about.html`. The file gets a copy of the home page's `<head>`, so it links the same stylesheets, with its own `<title>`:

```html title="about.html"
<!doctype html>
<html lang="en">
  <head>
    <title>About</title>
    <link rel="stylesheet" href="styles.css">
  </head>
  <body>
    <main>
      <h1>About</h1>
      <p>A new page, made in midcode.</p>
    </main>
  </body>
</html>
```

A path with a folder in it is refused ("In a folder of HTML, a new page goes at the top of the site: /about"), because the copied `<head>` links its files by paths that only hold at the top. Make a page in a subfolder by hand, or with your agent, and it appears in the list. "Remove" moves a page's file to the Trash.

## Site settings

The globe in the top bar opens [Site settings](https://midcode.app/docs/editor/site-settings.md). In a folder of HTML every page has its own `<head>`, so there are two kinds of form: one for the site, written in `index.html`, and one for each page, written in that page's file (right-click a page in the page menu, then "Page settings…"). The title, the description, the language, search engines, the favicon and the social image are tags of that `<head>`:

```diff title="index.html"
   <head>
-    <title>Oak and Iron</title>
+    <title>Oak and Iron, furniture</title>
     <link rel="stylesheet" href="styles.css">
+    <meta name="description" content="Handmade furniture.">
   </head>
```

A tag that's there has its value changed. A new one goes last in the `<head>`, indented and closed like its neighbours. Images are copied to the top of the folder as `favicon` and `og-image`, with the extension of the file you picked, and linked by that name.

## Styles

A folder of HTML has no Tailwind build, so midcode uses its own utilities: Tailwind 4's, written with the prefix `mid:` and turned into plain CSS by midcode itself. Your own stylesheets and classes aren't touched.

The first style edit sets this up, as one undoable step:

- `midcode.css` is created at the folder's root, with the CSS of exactly the `mid:` classes found in your files.
- Every `.html` page gets one `<link>` to it at the end of its `<head>`, indented like the line above. A page in a subfolder gets the relative path (`../midcode.css`).

```diff title="index.html"
     <link rel="stylesheet" href="styles.css">
+    <link rel="stylesheet" href="./midcode.css">
   </head>
```

A page you add later gets its `<link>` with the next style edit. `midcode.css` is rewritten whenever the classes in your files change, and it's part of your site: publish it with the pages. Colors, fonts and text styles you make in the Styles list of the Assets tab go in `.midcode/theme.css`. [Without Tailwind](https://midcode.app/docs/styling/without-tailwind.md) explains the syntax and why every rule is `!important`; [Theme and design tokens](https://midcode.app/docs/styling/theme.md) covers the theme file.

## Limits

- Markup that a script builds in the browser has no marks: it's edited in that script.
- Nothing is shared between pages. A header repeated in ten files is ten elements, and an edit changes the one you clicked.
- A page has to be an `.html` file. The server doesn't send an `.htm` file as a page.
- midcode reads `{{ … }}`, `{% … %}` and `{# … #}` as something a template language computes, because many generators call their templates `.html` too. A text with one of those in it isn't edited on the canvas, and a `{{` you type is written as an entity.
- A new image is copied to `public/images/` and referenced as `/images/<file>`, the way frameworks with a `public/` folder serve it. midcode's static server serves the folder itself, so after swapping an image check that its `src` points at where the file is.
- Insert's interactive components (carousel, tabs and the like) are React and answer "Needs a React page". Icons go in as inline SVG.
- [Components](https://midcode.app/docs/editor/components.md) don't apply here. [the free canvas](https://midcode.app/docs/editor/free-canvas.md) comes with the next release, kept in `midcode-scratch.html` beside your pages.
- A page's form in Site settings has a title, a description, the search engines switch and a social image. The language and the favicon are in the site's form, which writes `index.html` only: another page keeps the `lang` and the icon its own file says.

## Troubleshooting

**The canvas asks "How does this site run?"** There's no `index.html` at the root of the folder you opened, so midcode didn't take it for a plain site. Open the folder that holds `index.html`, or tell midcode how to serve it: see [Any other stack](https://midcode.app/docs/frameworks/custom-server.md).

**It opens as something else.** A `package.json` with a `dev` script, or a file like `index.php` or `_config.yml`, makes midcode run that instead of serving the folder. The **Dev server log** in the top bar shows what it ran.

**A style shows on one page and not on another.** The other page has no `<link>` to `midcode.css`. Make any style edit and midcode adds the missing ones.
