Empty block

What a screen says when there is nothing to show yet: a heading, one sentence, and the two doors out. It is the only block in the product whose whole job is to be replaced by content, and the reason it is a component rather than a heading and a paragraph is the tone. An empty state here is never an error, so nothing about it is clay and nothing about it apologises.

Organism2 grey pagesno axisno statesone colour, and it is muted

Anatomy

Three slots and nothing around them. Everything below is drawn by design/system/components/empty-block.css and by the door's own file, through the same entry point a product screen loads, so this page cannot drift from the screens: there is no second copy of the CSS here to drift from.

Two ways to start

See everything you pay for in one calm place. Pick the one that feels right; you can change it later.

Behaviour at width

It carries the reading measure, --container-text. It was already 52ch, which is what made 52 the mode of the four measures the product had, so the token took this value and this component did not move a pixel at either width. See Width.

Variants and sizes

One form, so there is no matrix. Both occurrences are the same three slots at the same three values, and the only thing that differs between them is the words. There is no tone axis, no size axis and no host axis: the block reads one colour, --text-muted, on one of its three parts, and everything else it needs is inherited or belongs to the grid inside it.

The two in the product, side by side:

PageHeadingSentenceWhat is missing
home-empty, colouredTwo ways to startSee everything you pay for in one calm place. Pick the one that feels right; you can change it later.subscriptions, because none have been found yet
connections-empty, greyNo sources yetAdd one and your subscriptions appear on your list. You can change this later, and you can have both.sources, which is what would find them

The stand shows this one at the full column, and that is a measurement. The default stage measure is 440px and .wide is 620, and both would be wrong here: on design/home-empty.html at a 1440 viewport the block renders 908px wide, because it is a direct child of .screen and past a 900 container the shell's blanket 780px measure comes off. At 360 it renders 313px, which is the whole screen inside its own padding. So the block genuinely spans its column at both ends of the range, the stage is unbounded to match, and the sentence is held back not by the stage but by its own 52ch.

When to use it

An empty block stands where content will stand, on a screen that has already introduced itself. Home before a bank is connected or a subscription is typed, and Connections before a source exists. Both are the very first thing a person sees after arriving, which is why the tone matters more here than anywhere: this is the audience that closes finance apps, and the first screen has to read as a beginning rather than as a fault.

It says what to do, not what is absent, and the wording was corrected once for exactly that. voice/docs/microcopy.md records the change: home-empty's second heading was "Nothing here yet" and became "Two ways to start", because two headings a hundred pixels apart were doing one job and the first already said what was true. The heading says the move; the sentence says the reward and takes the risk out of it ("you can change it later"); the doors are the move itself. On Connections the same shape runs the other way round, because "No sources yet" IS the move's subject there.

The two doors are equals and the block never picks one. D2 makes manual entry a real second path rather than a fallback, so neither door is filled, neither is first by emphasis, and the sentence says "Pick the one that feels right" instead of recommending. That is the one rule an empty state in this product could most easily break, and it would break it by making one door louder.

The rule, and the anti-rule

A beginning, on the page ground

Two ways to start

See everything you pay for in one calm place. Pick the one that feels right; you can change it later.

No box, no icon, no illustration and no colour but the muted second tier. The only edges on screen belong to the two doors, which are the thing to press. An empty state that draws its own frame turns an absence into an object, and this is the screen whose job is to make an absence feel like a start.

Use a text block instead

Still gathering your history

Trends need a few months to be worth looking at, and we have less than three so far. Come back in a few weeks and the shape of your spending will be here.

When a screen has nothing to show because it is waiting rather than unstarted, the component is the Text block. It opens the screen and carries its h1; an empty block sits inside a screen that already has one, and carries an h2. The two are one line apart in the markup and a whole level apart in the heading order, which is how a screen reader tells them apart and how a person does too. History and trends with under three months is the clearest case: nothing is missing that a person could add, so there are no doors and there is no empty block.

Nothing went wrong here

No sources yet

Add one and your subscriptions appear on your list. You can change this later, and you can have both.

The sentence is muted and the heading is plain. There is no tone token in this component at all: no amber, no clay, no wash and no badge. DESIGN.md forbids it by name, and the rule is one line: do not use clay on an empty state, because nothing went wrong in an empty state.

Use a wash block instead

We could not refresh just now. Showing your last update from today, 9:14 AM.

Try again

When something actually failed, the component is the Wash block in its error tone, and that is where the desaturated clay lives. It is a tinted paragraph inside the flow with its own inline link, not a heading and two doors. Reaching for an empty block to carry a failure would make a fault look like a beginning, and dressing an empty block in clay would make a beginning look like a fault. The two mistakes are the same mistake from opposite ends.

States

No states, and the reason is one line: an empty block is not interactive. It is a section on both occurrences, it holds no anchor, no button, no summary, no tabindex and no role of its own, and kit.css gave it no pointer response either. What a person presses inside it is a Door, and the door has hover and focus-visible on its own page, which is where somebody looking for them will go. Faking a state here with a class the product does not carry would document something the product cannot do.

It has no loading form either, and the absence is a decision rather than a gap. A screen that does not yet know whether it is empty shows the loading form of the thing it is waiting for: home-loading renders skeleton rows, not a skeleton empty state. An empty block is what the product says when it knows there is nothing, so a version of it that is still finding out would be a contradiction.

The technical half

ReadsWhich tokenGrowing from
the sentence--text-muted--muted, the single secondary tier, and the only colour in the file
the heading's size--type-subprimitive, straight. 17px folded to 16 at the founder's review of the foundations
the sentence's size--type-bodyprimitive, straight. 13.5px folded to 14
its three margins--space-8, --space-16, --space-24primitive, straight. 6 folded up to 8 and 18 folded down to 16
no fill, no edge, no radiusnoneby decision. The doors draw the only edges on the block
the reading measure52cha character width, which is on neither scale, so it stays the literal it is

The stand had to clear this component's name before it could be built. _page.css used .empty for the cell that marks a forbidden combination in a matrix, and it was renamed to .kit-empty on 2026-08-11, ahead of this round rather than during it. The collision would have been harmless on the day (no stand cell sits inside an .app scope), and "the code and the stand do not mix" is a rule about NAMES and not only about the cascade: the moment this organism landed, a product rule would have started matching reviewer chrome and nothing would have errored.

Lives in design/system/components/empty-block.css. Stands on 2 grey pages, 1 of them coloured: Home, nothing tracked yet. The second, wireframes/connections-empty.html, is still grey.

<section class="empty"> <h2>Two ways to start</h2> <p>See everything you pay for in one calm place. Pick the one that feels right; you can change it later.</p> <div class="grid roomy"> <a class="door" href="connect-bank.html"> <h3>Connect your bank</h3> <p>Read-only, through Plaid, and about a minute. Tendd cannot move your money.</p> </a> <a class="door" href="add-subscription.html"> <h3>Add a subscription</h3> <p>Start with one and add more later. No bank is involved, and nothing leaves your control.</p> </a> </div> </section>