Skip to content

Application

Builder and app chrome — the components a page-builder, dashboard, or editor UI needs. Zero JavaScript; states ride ARIA.

App shell

.k-app is the frame: a grid of __head, __rail, __work, __side, and __foot that fills the viewport (min-block-size: 100dvh). The demo below is scaled down inside a box so it can live on this page — in real use the shell is the page. Honestly: the rail and side panels only show at ≥1080px; below that they're hidden and the shell is header, work, footer. A builder on a phone needs its own pattern — put the panels in a drawer.

A page

On the canvas.

Layout
100% Saved
Markup
<body class="k-app">
  <header class="k-app__head">
    <div class="k-toolbar">…</div>
  </header>
  <nav class="k-app__rail" aria-label="Layers">
    <ul class="k-tree">…</ul>
  </nav>
  <main class="k-app__work">
    <div class="k-canvas">
      <div class="k-artboard">…</div>
    </div>
  </main>
  <aside class="k-app__side" aria-label="Inspector">
    <div class="k-props">…</div>
  </aside>
  <footer class="k-app__foot">
    <div class="k-statusbar">…</div>
  </footer>
</body>

Toolbar

.k-toolbar is the strip along the top: __group clusters related tools, __sep draws a hairline between clusters, and __spring pushes whatever follows to the far end. Tools are toggles — press state lives in aria-pressed, and keel styles it. The segmented switch is a plain .k-btn-group; inside a group, aria-pressed="true" gets the same soft-accent treatment. Moving the attribute between buttons is your JS — the one-line handlers below are page-demo wiring, not part of keel.

Markup
<div class="k-toolbar">
  <div class="k-toolbar__group">
    <button class="k-tool" type="button" aria-pressed="false" aria-label="Menu">
      <span class="k-icon k-icon--menu"></span>
    </button>
    <button class="k-tool" type="button" aria-pressed="true" aria-label="Search">
      <span class="k-icon k-icon--search"></span>
    </button>
  </div>
  <span class="k-toolbar__sep"></span>
  <div class="k-btn-group">
    <button class="k-btn k-btn--small k-btn--ghost" aria-pressed="false">Desktop</button>
    <button class="k-btn k-btn--small k-btn--ghost" aria-pressed="true">Tablet</button>
    <button class="k-btn k-btn--small k-btn--ghost" aria-pressed="false">Mobile</button>
  </div>
  <span class="k-toolbar__spring"></span>
  <button class="k-btn k-btn--small" type="button">Publish</button>
</div>

Tool

.k-tool is the icon toggle on its own — a square quiet button whose pressed state is aria-pressed="true": soft accent fill, accent text. Because it's icon-only, an aria-label is required, not polite. Flipping the attribute on click is your JS; keel only draws the two states.

Markup
<!-- pressed -->
<button class="k-tool" type="button" aria-pressed="true" aria-label="Search">
  <span class="k-icon k-icon--search"></span>
</button>

<!-- unpressed -->
<button class="k-tool" type="button" aria-pressed="false" aria-label="Insert">
  <span class="k-icon k-icon--plus"></span>
</button>

Tree

.k-tree is the layers panel: a nested list where branches are native details — open and close work with no script — and every clickable line is a __row. The chevron is a __twist span that rotates when its branch opens. The selected row carries aria-selected="true" and gets the soft-accent fill with an inset accent edge. Honestly: expanding is native, but selection, drag-to-reorder, and rename are your application's JS — keel draws the rows.

  • Page
    • Hero
    • Section
Markup
<ul class="k-tree">
  <li>
    <details open>
      <summary class="k-tree__row">
        <span class="k-tree__twist"><span class="k-icon k-icon--chevron-right"></span></span>
        Page
      </summary>
      <ul>
        <li>
          <details open>
            <summary class="k-tree__row" aria-selected="true">
              <span class="k-tree__twist"><span class="k-icon k-icon--chevron-right"></span></span>
              Hero
            </summary>
            <ul>
              <li><button class="k-tree__row" type="button">Heading</button></li>
              <li><button class="k-tree__row" type="button">Buttons</button></li>
            </ul>
          </details>
        </li>
      </ul>
    </details>
  </li>
</ul>

Property rows

.k-props is the inspector: a stack of __rows, each an aligned label-and-control pair, with __group headings ruling off sections. Controls are the ordinary form elements — select, range, switch, swatch — just drawn compact inside a row. It's built to live in .k-app__side, but it works anywhere a settings column does.

Layout
Style
var(--k-accent)
Markup
<div class="k-props">
  <div class="k-props__group">Layout</div>
  <div class="k-props__row">
    <label for="width">Width</label>
    <select id="width"><option>Fill container</option></select>
  </div>
  <div class="k-props__group">Style</div>
  <div class="k-props__row">
    <label for="radius">Radius</label>
    <input type="range" id="radius" min="0" max="24" value="8">
  </div>
  <div class="k-props__row">
    <label for="shadow">Shadow</label>
    <span class="k-switch"><input type="checkbox" id="shadow" checked></span>
  </div>
</div>

Canvas & artboard

.k-canvas is the workspace surface — the dotted grid your page floats on. .k-artboard is the page itself: an ordinary surface with a page shadow, and whatever's inside it is real keel markup rendered with real tokens. .k-selected is the builder outline — the accent ring that says "this block is selected." The canvas is just the surface; scrolling and zooming it is layout, not behavior.

Ship the page

A hero, on an artboard, on the canvas.

Get started
Markup
<div class="k-canvas">
  <div class="k-artboard">
    <!-- the page being built — real keel markup -->
    <div class="k-selected">
      <h3>Ship the page</h3>
      <p>A hero, on an artboard, on the canvas.</p>
      <a class="k-btn k-btn--small" href="#">Get started</a>
    </div>
  </div>
</div>

Status bar

.k-statusbar is the thin strip along the bottom — zoom level, where you are, whether the work is saved. __spring pushes the two ends apart, same as in the toolbar. Everything in it is plain text and icons; it states, it doesn't act.

100% Page / Hero / Heading Saved
Markup
<div class="k-statusbar">
  <span>100%</span>
  <span class="k-statusbar__spring"></span>
  <span>Page / Hero / Heading</span>
  <span><span class="k-icon k-icon--check"></span> Saved</span>
</div>

That's the whole tier: keel draws the chrome — shell, strips, rows, surfaces, states. Selecting a block, dragging a layer, undoing an edit — that's your application's JavaScript, and it always was going to be. The split is the point: see Zero JavaScript in concepts.