coding.sgit.ai / the house style / HTML

HTML

37 files. Most of it is the markup half of a component, so it follows from the component system — and it carries one rule that is worth its own line on any site.

1. The component's markup half

components/sg-compute/sg-compute-left-nav/v0/v0.1/v0.1.0/sg-compute-left-nav.htmlexcerpt · 03__html-and-css.md
<nav class="left-nav" role="navigation" aria-label="Main navigation">

  <button class="nav-item" data-view="compute" aria-label="Compute">
    <span class="nav-icon">⬡</span>
    <span class="nav-label">Compute</span>
  </button>

  <button class="nav-item" data-view="nodes" aria-label="Active Nodes">
    <span class="nav-icon">▣</span>
    <span class="nav-label">Nodes</span>
  </button>

Apache-2.0 — quoted from the estate's own source, not covered by this site's CC BY 4.0. Counted 2026-08-24.

Six conventions are visible in it:

  1. A fragment, not a document. No <html>, no <head>, no wrapper <template>. The base class fetches it and puts it in the shadow root.
  2. 2-space indent — deliberately different from the 4 used in JS and CSS. The narrower indent keeps nested markup readable at the same line width.
  3. Semantic elements. <nav>, <button> — not <div onclick>. Every interactive control is a real control, so keyboard handling and focus work without a line of code.
  4. ARIA on every interactive element. role, aria-label, and aria-current set dynamically from JS.
  5. data-* is the behaviour hook. data-view="compute" — JS reads btn.dataset.view.
  6. Unicode glyphs instead of icon fonts or SVG sprites. No asset pipeline, no icon dependency, consistent with the no-build-step position.
Classes are for styling, data- is for behaviour, and the two never mix.

It is a one-line rule and it removes a whole category of accident. A stylesheet refactor cannot break an event handler; a behaviour change cannot silently depend on a visual class that somebody renames next week. The two systems address the same elements through two different namespaces, on purpose.

The pair of rules that would actually catch defects, if anyone wrote them down, are every interactive element has an accessible name and no onclick attributes. The rest is formatting. The checks that would encode them →

2. The page-level conventions

Beyond components, the estate's HTML carries two site-wide patterns worth documenting.

Programmatically injected chrome

Navigation, footers and version badges are injected at build time rather than repeated per page — so it cannot drift. Hand-written static HTML plus generated furniture: a human can open any file and edit it, and nobody can forget to update the nav on page thirty-one.

This site is built that way, which is the only honest way to publish the pattern. admin/build/chrome.py holds the single definition of this site's nav and footer and rewrites both in place across every page, generated pages included. How it works here →

The markdown twin

Every page is available as markdown at the same path with the extension swapped, and links inside the markdown point at markdown, so a traversing agent never has to parse HTML.

The mechanism is non-obvious and should be published with the convention: these are static files, so the browser will not run JavaScript on them, and the rendering happens in a Lambda@Edge function instead.

This is an HTML convention that exists entirely for machine readers, which makes it the clearest single piece of evidence for the argument on the for-agents page. This site implements the same promise more modestly: the front page has a twin at /index.md, every source document is fetchable as raw markdown at a constructed URL, and /llms-full.txt is the whole site in one request.

3. What is not settled

Nothing about HTML is documented anywhere in the rule set, so everything above is a convention rather than a rule. The two gaps worth naming: