Skip to content

Plain HTML

Next release

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.

View as Markdown

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 byAn index.html at the folder’s root, and nothing else midcode knows how to run
Runs withmidcode’s own static server
Elements are marked byThat server, as it sends each page
EditingText, classes, attributes, tag; insert, move, duplicate, delete, wrap in a stack
ComponentsNone: every element is written where it shows
PagesEvery .html file, at the path it’s served from. New pages at the top of the folder
Stylesmid: utilities, with midcode.css at the folder’s root and a <link> in every page
Tried withA 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);

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

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, text and media, the style panel, Insert, Layers.

Text. Double-click and type:

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

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:

FilePage
index.htmlThe 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).

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

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

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

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 explains the syntax and why every rule is !important; Theme and design tokens 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 don’t apply here. the free canvas 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.

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.