html.surf
Sign in

Writing Guide

Two ways to write a page: a bare <article> fragment that inherits the platform's Tactile Paper style, or a full self-contained document using @import. Either way, your job is structure and content — not CSS.

Option 1 — Fragment Mode

Write a bare <article> with no outer document structure. The viewer wraps it in the platform Tactile Paper style automatically. Use this for notes, knowledge base entries, and anything that should feel like part of the site.

<article>
  <h1>Your Title</h1>
  <p class="byline">Author &nbsp;·&nbsp; Date &nbsp;·&nbsp; <span class="tag">Tag</span></p>
  <p class="lead">One punchy sentence that earns the read.</p>
  <h2>Section</h2>
  <p>Body copy...</p>
  <div class="callout"><p><strong>Key insight:</strong> one sentence.</p></div>
  <pre><code class="language-ts">const x = 1;</code></pre>
</article>

Option 2 — Full Document with @import

For self-contained proposals, evaluations, and branded documents: use a full HTML document with a single @import in the <head>. Call list_themes() to see available themes, then get_theme("name") for the exact import URL and component class reference.

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8"/>
  <style>@import url('https://html.surf/styles/Tactile%20Paper');</style>
</head>
<body>
  <article>
    <h1>Title</h1>
    <p class="byline">...</p>
    <p class="lead">...</p>
    <!-- same component classes as fragment mode -->
  </article>
</body>
</html>

Available themes: Tactile Paper (warm editorial, this page), Dark Tech (dark/purple, for proposals), Clean Light (white/minimal, for docs). Call list_themes() via MCP to get import URLs.

CSS Variables

Use these for any inline styles. Never hardcode hex values.

VariableValueUse for
--paper#f7f3edPage background
--paper-2#ede8e0Card / callout backgrounds
--paper-3#ddd7ccBorders, dividers
--ink#1a1714Primary text
--ink-2#4a4540Body text
--ink-3#8a8278Muted / meta text
--accent#c84b2fTerracotta highlights
--link#2e5fa3Hyperlinks
--font-displayPlayfair DisplayHeadings
--font-bodyLoraBody text
--font-monoFira CodeCode, bylines, tags

Component Classes

ClassUse for
.bylineAuthor · date · tags row under h1
.leadItalic opening paragraph — earns the read
.calloutTerracotta left-border highlight box
.callout.tipBlue left-border — informational
.callout.warnAmber left-border — caution
.tagInline category pill (inside .byline)
figcaptionMono caption under figures / SVGs

Validator Rules

Always call validate() before write(). These errors block saving:

CodeIssue
SCRIPT_TAG<script> tags not allowed
JS_HREFjavascript: hrefs
INLINE_EVENTonclick, onload etc.
DATA_URIsrc with data: URI scheme
IFRAMEEmbedded frames
FORM_ACTIONForms posting to external URLs

MCP Workflow

// 1. Pick a theme (or skip for fragment mode)
const themes = await mcp.list_themes()
const theme  = await mcp.get_theme({ name: 'Tactile Paper' })

// 2. Validate before writing
const check = await mcp.validate({ html })
if (!check.ok) { /* fix errors */ }

// 3. Write
await mcp.write({ slug: 'my-topic', html, public: true })

// 4. Optionally upload assets
const asset = await mcp.upload_asset({ slug: 'my-topic', filename: 'diagram.png', ... })
// paste asset.embed_tag into the page html, then write again
html.surf · Writing Guide · May 2026