The seam between layers needs to be marked. Where the Gutenberg layer ends and the Semantic layer begins — where content becomes code, where data becomes structure, where the designer’s territory meets the developer’s — that boundary does real work. It needs to be findable.

The question is how loud the marking should be.

A white line on tarmac marks the boundary without interrupting the road. You drive across it freely, you just know which side is yours. A concrete barrier marks it so hard that nothing gets through — including the traffic that was supposed to. Erasing the line entirely doesn’t remove the edge. It just removes the warning.

Erasing the line is sinkhole UX. The surface looks continuous. The structure underneath has a gap. The user steps through without knowing the edge was there, because nothing told them.

Proper separation and seamless integration are not opposing goals. They are the same goal, named from opposite sides of the seam. The marking that achieves both is findable breadth-first — visible when you need it, out of the way when you don’t.


HTML, CSS, JS: The Marking Is the File Boundary

HTML, CSS, and JavaScript get this right by putting the seam at the most visible level possible: the file system.

Open the folder. You see index.html, style.css, app.js. Content, presentation, behaviour. The boundary is findable without opening a single file. A designer can work in style.css without touching app.js. A developer can change behaviour without touching index.html. The seam is architectural, not syntactic — it does not need to shout because it is already the loudest thing in the folder.

Inside each file, the marking recedes. HTML is readable as content with light markup — a non-technical person can scan it and find the text. CSS is readable as a list of visual intentions. The white line is there at the folder level. Inside the file, the road is just road.

HTTP makes the same choice at the protocol level. A blank line separates headers from body. Two bytes, invisible if you are not looking, immediately findable if you are. Headers are machine-readable structure. Body is content. The seam between them is exact, minimal, and never confused with the material on either side.


Excel: One Painted Kerb

Excel’s seam is a single character: =.

A cell that starts with = is a formula. A cell that does not is a value. One character, at the start of the cell, sufficient to make the boundary exact. The content — the numbers, the labels, the text — dominates the visual field. The marking is minimal but findable.

This is the right calibration for a load-bearing seam. The = is doing real structural work — this cell computes, that one stores — and it announces itself just enough to be found. A non-technical person can read a spreadsheet and see values. A developer can scan for = and find the computation. One painted kerb, exactly where the road changes character.

The file extension does the same work at the folder level. .xlsx tells you before you open it that you are looking at a spreadsheet — the medium is the message, and the message is correct. The problem appears when a spreadsheet is used where a document belongs: send someone a .xlsx when they expected a report to read, and the seam is in the wrong place. They open Excel instead of a PDF. The format declared itself correctly; the choice of format was the mistake. The white line was painted on the right road, just not the road anyone needed to cross.


Markdown: White Lines, Until They’re Load-Bearing

Markdown is mostly right. The markings are quiet — an asterisk for emphasis, a hash for a heading, a backtick for code. The content dominates the visual field. A non-technical person can read a Markdown file without seeing the seams. That is the win.

The problem is invisible block boundaries.

A blank line between paragraphs is a white line on tarmac — it marks the boundary, correct the vast majority of the time, unobtrusive enough that reading flows across it naturally. But when the blank line is load-bearing — when it determines whether a code block closes, whether a list item continues, whether indented text is a nested list or a literal block — the invisibility becomes the bug. The marking is doing structural work while being indistinguishable from empty space.

Missing semicolons in JavaScript are the same failure at the micro level. The parser infers the statement boundary, gets it right 95% of the time, and gets it silently wrong at edges. The white line was erased to make the syntax friendlier. The sinkhole opened quietly.

The principle: white lines are right until the seam is load-bearing, then you need the marking to be findable. A blank line between paragraphs is a white line doing a white line’s job. A blank line that determines whether a code block closes is a white line doing a kerb’s job, wearing no paint.

Markdown gives you paragraphs when you sometimes need pages. A paragraph is a unit of reading — you flow across the blank line without noticing. A page is a unit of navigation, with an explicit boundary you cross deliberately. When the blank line is just separating prose, the white line is sufficient. When it is marking something structural, the driver needs to see the kerb.


XML: Concrete Barriers

XML makes the opposite mistake. The angle brackets are louder than the content. Open a large XML configuration and you see structure — closing tags, attributes, namespace declarations — before you see what it is about. The concrete barriers are taller than the buildings they surround.

<configuration>
  <interface>
    <name>ge-0/0/0</name>
    <description>Uplink to core</description>
    <unit>
      <name>0</name>
      <family>
        <inet>
          <address>
            <name>192.0.2.1/30</name>
          </address>
        </inet>
      </family>
    </unit>
  </interface>
</configuration>

The seam is perfectly explicit. Every boundary is marked with an opening and a closing tag. Nothing is inferred. Nothing is invisible. And the content — “this interface has IP address 192.0.2.1/30” — is buried behind six levels of barrier that all have to be read to reach it.

Concrete barriers are not always wrong. They are right when violation is genuinely dangerous and no softer marking would hold. But most of what XML marks is not dangerous. The closing tags repeat information the opening tags already stated. The barriers are load-bearing only to the machine — to the person reading the file, they are obstacles.


Observable: Two Answers to the Same Road

The Observable team built the same product twice and got a different answer each time.

Observable notebooks put code cells and prose cells in the same document, visually distinguished by background colour and a small icon. The seam is marked but quietly — too quietly for the job it is doing. The execution order of cells is determined by dependency, not position, which means the document’s reading order and its computational order can diverge without warning. The white line is there. The sinkhole is behind it.

Observable Framework puts the seam at the folder level. Markdown files for content. Data loaders as separate files. Fenced code blocks for inline computation, explicitly marked. The computational dependencies are in the file system, visible before you open anything. The designer sees content files. The developer sees data loaders. The marking is at the level where it costs nothing to read.


White Lines Are Should, Not Must

The white line on tarmac is a should, not a must. Parking on the grass is wrong in normal conditions — the marking is there, follow it. But if every car park is full and the ambulance needs to get through, parking on the grass is the right call. The white line marks the default. It does not override judgment when context demands otherwise.

This is the right relationship between a seam marking and the people on either side of it. The HTML/CSS/JS file boundary is a should — you can inline a style attribute when you need to, the system does not stop you. The Markdown blank line is a should — it marks the boundary and trusts you to read the result. The concrete barrier is a must — nothing gets through regardless of context.

Most seams should be white lines. Concrete is for the cases where violation is genuinely dangerous and the marking alone will not hold. The format that makes everything a concrete barrier has decided that machine-readability matters more than the person reading it. Sometimes that is right. It should be a conscious choice, not the default.


Chesterton’s White Line

Before erasing a seam marking because it seems unnecessary — the blank line that “just adds noise,” the file boundary that “could be inlined,” the = that “everyone already knows means formula” — it is worth asking what the marking was doing.

Chesterton’s principle applies: do not remove the fence until you understand why it was built. The marking that looks like decoration is sometimes the thing that shows you where the edge is. Remove it and the road looks cleaner. The sinkhole opens the first time someone steps where the line used to be.

The seam is still there without the marking. The edge does not move because you stopped painting it. You just stopped being able to see it — which is the definition of sinkhole UX.

Don’t erase the white line. Don’t pour concrete either. Mark the seam at the level where it is findable, in proportion to the work it is doing, and trust the people on both sides of it to read the road.


This post is part of the Gutenberg/Semantic series. Related: Fixing the Seams Across the Waterline on the seam as a design tool, Hiding the Waterline Makes You Drown Without Knowing Why on the cost of invisible boundaries, and A Room Is Not a Collection of Bricks on the difference between the unit of structure and the unit of meaning.