# Card Layouts Editor

> Map regions onto a card face, bind them to card fields or live counters, and control exactly what an opponent is allowed to see.

- Source: https://diceytable.com/docs/editor/item-editors/card-layouts
- Last verified: 2026-08-25

Open the [Card Data editor](https://diceytable.com/docs/editor/item-editors/card-data), scroll to **Card layouts**, and
press **Open the layout editor**. It opens as its own tab over the same `data/cardSchema.json`.

This is where a card stops being a picture and starts carrying values: a power box that shows
`4/4` and can be changed to `6/6` during a fight, a damage counter, a stun marker.

See [the tab strip conventions](https://diceytable.com/docs/editor/item-editors/model#the-tab-strip) — dirty dots,
closing, cycling and mounted-but-hidden tabs apply here too.

## Vocabulary

| Term | Means |
| --- | --- |
| **Region** | A named rectangle on a card face |
| **Overlay** | A value drawn into a region |
| **Counter** | A small badge shown on a card |

None of these are engine **Components** in the sense the
[scene model](https://diceytable.com/docs/editor/concepts/scene-model) uses that word. A region is not an entity and a
counter is not a table object.

## It edits the same file as the Card Data tab

Both tabs write `data/cardSchema.json`. Having both open is safe and expected — this tab rewrites
only the `layouts` and `counters` sections, so it can never overwrite a field, filter or format
the other tab just saved, and the other tab keeps whatever you save here.

The one thing to know: **this tab reads the document from disk when it opens.** If the Card Data
tab has unsaved changes, save them before opening this one, or the fields you just added will not
be in the field pickers. The Card Data tab says so in place when it applies.

## Layout templates

A mod declares any number of templates. Each has a **match** predicate over the fields you mapped
in the Card Data editor — the same fields, not a second mapping system.

Templates are evaluated **top to bottom, and the first match wins**:

```
   card ──▶ [ Planeswalker? ] ─no─▶ [ Creature? ] ─no─▶ [ Default ]
                  │ yes                  │ yes              │
             4 regions              6 regions          3 regions
```

Drag a template by its `⋮⋮` handle, or use the ↑ / ↓ buttons, to change the order. Order is
meaning here, not presentation.

> **An unconditional template must be last.** A template with no clauses matches every card, so
> everything below it can never be reached. The list strikes those rows through and the panel says
> which template is shadowing them. This is not a style preference — the document **cannot be
> saved** in that state, so the editor warns before you get there rather than after.

### The match readout

Under the clause list:

```
✓ matches 412 / 1104 sample cards
```

This is the most useful control on the screen. It is counted **first-match-wins**, exactly as the
table will evaluate it, so the per-template numbers plus the unmatched count add up to your whole
catalogue.

- **0 matched** — either your predicate is too narrow, or a template above is swallowing
  everything. The readout turns amber so you cannot miss it.
- **Everything matched** — your predicate is probably too broad and later templates will starve.

Clauses are a flat AND-list: every clause must match. There are two operators, `equals` and
`contains`, and that is deliberate — a predicate you cannot write a loop or a side effect in is a
predicate the editor can count instantly across a 20,000-card catalogue.

### Card types

Above the clauses is a checklist of every card type in your game, each with the number of catalogue
cards carrying it. Tick the types this template renders.

Types are an **OR** — a card matching any ticked type qualifies — which is the one thing the clause
list cannot express, and the reason authors used to write one near-identical template per type.
They combine with the clauses by AND, so *"Instant or Sorcery, whose name contains bolt"* is one
template. Tick nothing for "any type".

The checklist needs a field bound to the **Card type** role in the
[Card Data editor](https://diceytable.com/docs/editor/item-editors/card-data#roles). Without one the section says so
instead of showing an empty picker. The vocabulary comes from that field's declared options when it
is a `select`, and is otherwise scanned out of the catalogue — so a type spelled differently in
your data shows up as its own entry with a count of `0`, which is exactly the mistake you want to
see before you ship.

A template with ticked types is **conditional**, so it does not shadow the templates below it.

## The canvas

The preview shows one real card from the catalogue. **Only cards this template actually claims**
are offered, and the shuffle button cycles through them — the fastest way to notice a region that
fits one printing and overflows every other.

- Drag a region's body to move it; drag a handle to resize.
- Arrow keys nudge the selected region by one snap step. Hold **Shift** for ten.
- The snap step is a fraction of the card, not pixels. Set it to *snap off* for fine work.

> **Coordinates are normalised** — 0 to 1 of the card face, origin top-left. That is why one layout
> fits a poker card, a tarot card and a mini-European card without editing. Nothing here is
> measured in pixels or in inches.

### Landscape cards

The preview takes its shape from the card in front of it, measured off the art. A set that mixes
orientations — the Star Wars TCG prints Battle and Location cards landscape and Character cards
portrait, from one catalogue — shows each card at its own proportions as you shuffle through the
matching cards. Because region coordinates are normalised to the face, the same template still
works on both; only the frame changes.

A card with no art falls back to the standard 5:7 portrait.

### Corner rounding

**Corner rounding** under the card is a property of the whole SET, not of this template: it is how
your cards are printed. It is a percentage of the card's **short edge**, so a landscape and a
portrait card in the same set round by the same visible amount, and 0 is square-cut.

It applies everywhere the game's cards are drawn — the table, the deck builder's card grid, a deck's
page, and this preview — so there is one number to change rather than one per surface. Leave it at
8% for standard playing-card stock.

Two notes on precedence:

- A **custom deck**'s own authored rounding still wins for that deck, because it is the number the
  deck editor previews.
- The set's value **outranks** the per-card corner-rounding control in the table inspector. That
  control's value is seeded on every card the moment it spawns, so it cannot tell "the author chose
  8%" from "nobody touched it" — whereas a set that declares its rounding has said so deliberately.
  Declare nothing and nothing changes; the inspector behaves exactly as before.

## Binding a region

| Bind to | Draws | Replicates? |
| --- | --- | --- |
| **Static text** | Fixed text from this document | Nothing |
| **Card field** | A mapped field's value, resolved locally | Nothing, unless it is live |
| **Live counter** | The current count of a declared counter, or a whole area of tokens | The counts only |

Tick **Players may change this** on a card field to make it *live*. A live value can be changed at
the table, and it is the only part of the whole editor that travels over the network:

- **Printed value ± change** — the change is a delta on top of the catalogue's printed value.
  Base power 4, `+2` at the table, the card reads 6.
- **Replace the printed value** — the live value stands in for the printed one.

Everything else here — rectangles, fonts, colours, counter art, anchors — resolves locally on every
player's machine from your mod. It is never sent anywhere.

## Visible when — the control that matters

**Visible when** is not a rendering setting. It is the rule the host uses to decide what to *send*,
and it is the difference between a hidden hand and a spoiled game.

| Setting | What an opponent receives while the card is face down |
| --- | --- |
| **Face up only** | Nothing. The value is stripped before it leaves the host. **Default.** |
| **Count only** | An anonymous tally — how many, never which |
| **Always** | The exact value, even though they cannot see the card |

**Always** is a real, useful setting — a face-down cost marker everyone is meant to see — and it is
also how you leak an opponent's hand. The panel warns in place whenever you choose it.

Two values worth internalising:

- **The default is the concealing one.** Every new region and counter starts at *face up only*.
- **The strictest declaration wins.** If two templates both bind a live `power` and disagree, the
  host takes the more concealing answer. You cannot accidentally publish a value by adding one
  careless template.

**Draw on face** is the separate question of *which side* the region is painted on (front, back or
both). It changes nothing about redaction — a region bound to the back face with *Always*
visibility still sends its value to everyone.

## Who may change it

Per region and per counter: **host only** (the default), **the seat that owns the card**, **any
seated player**, or **scripts only**.

This is enforced by the host when it receives the change, not by the buttons in front of a player.
A modified client can always ask; what stops it is the host.

## Counters

A counter is a badge a player can add to a card: `+1/+1`, damage, stun. Only the **count**
replicates.

- **Art** — a built-in chip, or an image from your own mod repo (a repo-relative path, never a URL).
- **Anchor and offset** — where on the card face it sits.
- **Stacking** — `Single badge with a NUMBER` is recommended and is the default. It is the only
  mode whose cost does not grow with the count; a row or column of twenty chips draws twenty
  sprites on every card that has them.
- **The count is always readable.** From two up, the number rides in a small orange bubble on the
  badge's top-right corner — one bubble for the whole counter, on the right-most chip of a row or
  column rather than one per chip. It is deliberately *not* written across the middle of your art:
  a numeral stamped over a token defaces the thing you drew, and on a light token it disappears
  into it. At exactly one there is no bubble; a built-in chip shows its **label** instead, since a
  coloured dot has nothing else to identify it, and a token drawn from your own art is left alone.
- **Max** — the ceiling a player can raise it to.
- **When the card flips** — *keep the count*, *clear the count*, or *move the count to another
  counter*. Transfer is how a back-face resource carries onto the front face as something else; its
  target may not itself transfer, so a flip can never cascade.

### Mapping the area where tokens go

A counter region has a **Draw as** toggle. *The count, as a number* draws text, exactly like any
other region. *Tokens arranged in this area* turns the rectangle into the counters' **arrangement
area**: tokens wrap into it left-to-right, top-to-bottom, centred as a block, and a counter drawn
this way is not also drawn at its own anchor.

**Stacking still decides how many tokens a count is worth.** A counter set to `Single badge with a
NUMBER` — the default — takes exactly ONE token in the area and draws its count on it, so two
damage is one token reading `2` rather than two identical tokens. Set the counter to a row or a
column when you want the points laid out individually; that is what fills the area. A token drawn
from your own art is left unstamped at exactly one — the art already says what it is — and carries
the number from two up, in the same orange corner bubble the anchored badges use, sized to the
token; a built-in chip shows its **label** at one instead, written across it, since a coloured dot
has nothing else to identify it. Set the region's **text colour** to override what the bubble and
the label are written in.

**Several counters can share one area.** Tick each one in the region's list; damage and shields
sharing one track is a single region, not two rectangles you have to keep from colliding.

- **Order is the draw order.** The ↑ / ↓ buttons beside each ticked counter set it, and the number
  beside them is the position the table honours. All the shields, then all the damage — the same
  sequence on every player's machine, because it comes from the document rather than from the
  order a player happened to add them in.
- **Min / max token size** bound how large a token may be, as a percentage of the card's height.
  `0` means unset. The table picks the largest size between the bounds whose grid fits everything,
  so two tokens draw large and a dozen shrink rather than overflowing at the third. Only past the
  minimum does the area give up and show a `+N` badge — the count stays exact and visible either
  way.
- **The example area in the preview** draws stand-in tokens of every counter you ticked, in order,
  at the size the bounds produce. Counts are per-card runtime state that this document does not
  hold, so it fills the area rather than inventing a number — except for a numbered counter, which
  gets one stand-in, because one token is all it will ever draw.

A region that draws a **number** must name exactly one counter — a number is one number — and it
cannot carry the size bounds. Switching it to tokens is what unlocks both.

### The three-way preview

Three cells: your card face up, your card face down, and **what other players see**.

The third one is the point. It is the same face-down card after host-side redaction, so a counter
you thought was private shows up there before your players find it. Hovering a cell explains every
counter's outcome in the list beneath it — including *why* one is missing, which is otherwise
indistinguishable from a bug.

## Attachments

**No editor yet**, and the tab says so rather than showing one that cannot save. The *contract*
exists: `deckSchema` now accepts a top-level `attachments` array, and the runtime honours it — so a
rule written by hand into `data/cardSchema.json` works today.

One rule declares which card may attach (`match`, a predicate over your mapped fields), which card
may host it (`hostMatch`), how many one host may carry (`maxPerHost`, default 4), what happens past
that (`overflow`: `compress` — the default, the fan tightens; `hide`; or `refuse`), the per-slot
`offset` as a fraction of the host card's face, and what a flip does (`onHostFlip`: `keep` or
`detach`). Rules are evaluated top-down, first match wins, exactly as layout templates are, and an
unconditional rule must be last.

Attachment is object **parenting** — transform and organisation only, with the attached card held
kinematic so it cannot fight its host — and never a physics joint. What replicates is the parent
link plus a slot ordinal; the matching, the arrangement and the overflow policy all resolve locally
from this document. Dropping a card onto a card it may attach to attaches it instead of merging the
two into a pile; a host picked up into a hand releases its attachments where they stand.

## Saving

**Save card layouts** writes `data/cardSchema.json`. The tab refuses to write a document that would
fail to load, and Save Draft / Publish flush it along with every other open editor.

If the rest of the document changed underneath you in a way these layouts contradict — a field they
reference was renamed or removed in the Card Data tab — the save is refused with that explanation
rather than writing a broken file. Reopen the tab to pick up the current card data.
