Version 1 of 3 · · razie via designer-26designer · Current version

tags
#help #apps #pages #style
order
25
description
making topics, app pages and HTML pages, and styling them (where CSS can go and where it can't)

Help: apps and pages

How to make the things people see in a project: plain topics, pages that show data, live HTML, whole-page apps, and the look of them all. Each part says which tool to reach for and its limits.

Topics

  • A topic is a page of markdown. Its id is Category:name, or just name for an ordinary page. Names use letters, digits, - and _.
  • Write it: PUT /api/v2/topics/<name> with the markdown as the body (Content-Type: text/markdown). A PUT replaces the whole topic, so read it, change it, write it back.
  • New topics must be published. Writing a new topic makes a draft nobody else can see: POST /api/v2/drafts/<name>/publish makes it live. Edits to existing topics follow the project's draft rules (a draft needs If-Match: "none" the first time, then "d<n>").
  • Check the answer. errors, domain and objects in the reply must be empty; if not, fix and save again.
  • Frontmatter sits at the top between --- lines: tags: (flat lower-case words, comma-separated), description:, and for special topics their own keys.
  • Links: [[Topic]], [[Topic#section]], [[Category:name]], [[target|label]].

Pages that show data

Most app pages need no HTML at all.

  • ```table shows a class's objects (class:, columns:, totals:, sort:, filter:, color:). Each row opens its edit form; + Add makes a new one. Drawn with the page: reload for new data.
  • ```cards makes a row of tiles, one line each: icon | title | link | text (soon for one that isn't live). The first cards block under a home page's title is its app list.
  • An app is a Spec: topic (the model), its objects (often seeded from a Story: topic), a page with its table, and a card on Home.

Built-in blocks

Some fences are drawn by d2 itself, because they show per-viewer data, change accounts, or carry a secret: ```connect-ai, ```next-step, ```my-projects and the like. A topic places them; it can't rebuild them in HTML (an embed can't use the viewer's session). To change how one looks, style it (below).

Live HTML in a page: embedhtml

A ```embedhtml fence holds HTML, CSS and a <script>, run in a sandboxed frame inside the page.

  • Its CSS stays inside the frame: it styles the widget, never the page around it.
  • It gets the page's colours and fonts as variables (var(--ink), var(--card), var(--line), var(--soft)…): use them so it matches.
  • It can fetch sources that allow any origin; it can't read the page, the person's session or the project's API as them. For the project's own data use a table.
  • Show a clear state when a source doesn't answer, never a blank.

Whole HTML pages: App topics

An App:<name> topic is a whole HTML page at /app/<name>, under the project's top bar: for a dashboard or a custom editor.

  • It runs sandboxed and reaches the project through await d2.api(method, path, body), as the person looking at it (the project's /api/v2 routes only).
  • Keep the data in objects and the logic in rules; the page shows and triggers.
  • layout: full in its frontmatter gives it the whole window (its own navigation, a small button home).

Styling: where CSS can go

Learned restyling the Onboard your AI steps, 2026-10-04.

  • <style> in a topic does nothing. d2 strips it from the markdown when it draws the page, silently. (A home page's old "polish" block had never applied.)
  • A project's own Style: topic is a theme, not a layer. It sets the colour and font tokens in a ```style block (known tokens only: --paper --card --ink --soft --line --indigo --indigo-bg --slate --slate-bg --sun --sun-bg --leaf --rose --rose-bg --god --god-bg --admin --admin-bg --attn --display --body --mono), and a person picks it at /styles. Rules put in one don't reach other pages.
  • Rules that every page gets live in Style:Base on base d2, in its ```css block. Publishing it rebuilds /styles/shared.css (its ?h= changes), which every page of every project loads: no coder, no deploy.
    • Because it reaches everything, scope every rule to the thing you're styling, e.g. [data-block="connect-ai"] .btn{…}, never a bare h2 or .btn.
    • Shared rules can override a block's own behaviour: a base rule showed a form the block had marked hidden. Add [data-block="…"] [hidden]{display:none!important} when you restyle a block with hidden parts.
  • Style what's really there. Before writing CSS for a block, get its real markup in every state it has: a token or a visitor often sees a placeholder, not the block. Log in as a person, click through each state (for connect-ai: before Done, waiting, connected) and read the HTML. Then style its own classes (.blk-step, .nextstep, .done…).
  • Restyle, don't rebuild. CSS goes a long way on markup you can't change:
    • order (with display:flex on the block) puts steps back in order when the markup moves them;
    • :has() picks a part by what it contains (.blk-step:has(details), h3:has([data-reload-while]));
    • :last-of-type finds the last step;
    • ::before / ::after add icons: an SVG as a data: URL background, animated with @keyframes;
    • always add @media (prefers-reduced-motion:reduce){…animation:none} for anything that moves.
    • What CSS can't do: change text baked into the markup (a heading's "1." stays text).
  • Check every state after a change, on the person's real screen: a squeezed button (white-space:nowrap; flex:none) or an un-hidden form only shows in one of them.
  • Colours: prefer the page's tokens (var(--line), var(--soft)); a fixed hex is fine for an accent that must stay the same in every theme.