Screen Layout Language

A complete reference for the JSX-style markup that defines ticket screens, covering every tag, attribute, the 12-column grid, and validation rules, with rendered examples.

Overview

The screen layout language is how you describe a ticket screen. It reads like JSX: a sequence of elements, some of which nest, each with attributes in name="text" or name={value} form. The same markup powers the create form, detail view, preview pane, hover card, board card, and sidebar sections.

You write it in the screen editor, which validates the markup and renders a live preview beside the code. Throughout this page, examples show the markup and, where it helps, the layout it produces.

Screen
<Field id="Title" width={12} mandatory />
<Row width={12}>
  <Field id="Priority" width={6} />
  <Field id="Assignee" width={6} />
</Row>
<Field id="Description" width={12} height={4} />
Result
Title*
12 × 1
Priority
6 × 1
Assignee
6 × 1
Description
12 × 4

Anatomy

An element is either a container with children:

screen
<Row>
  <Field id="Priority" width={6} />
  <Field id="Assignee" width={6} />
</Row>

or a self-closing leaf:

screen
<Field id="Title" width={12} />

Attribute values come in three forms:

  • Strings in double quotes: id="Title", align="right".
  • Expressions in braces, for numbers and booleans: width={6}, defaultExpanded={true}.
  • Bare booleans, where the name alone means true: mandatory is the same as mandatory={true}.

Only elements are allowed - free text between tags is a parse error. There are no comments.

The 12-Column Grid

Every screen is laid out on a 12-column grid. Widths and positions are expressed in these column units.

  • width sets how many of the 12 columns an element spans. width={12} is full width, width={6} is half.
  • Elements stack vertically by default. Put them in a Row to sit side by side.
  • x and y place an element at an exact column and row for precise control. x is the starting column (0-11) and y is the row (0+).
  • At the top level, x + width must not exceed 12, and two positioned elements may not occupy the same cell.
Screen
<Field id="Status" width={4} />
<Field id="Priority" width={4} />
<Field id="Assignee" width={4} />
Result
Status
4 × 1
Priority
4 × 1
Assignee
4 × 1

Three fields, each four columns wide, fill one row.

Height is edit-only

The height attribute sizes multi-line fields in the editor. In read-only previews the height collapses to fit the content.

Tags

There are twelve tags: seven containers that can hold children, four leaves that must be self-closing, and one text tag that wraps plain text.

  • Containers: Row, Column, Section, Collapsible, Color, Font, Trigger
  • Leaves: Field, Label, Plugin, Spacer
  • Text: Text

Field

Use it to place an editable ticket field on a screen. Field is the workhorse of the create, detail, preview-pane, and section screens.

It requires an id that matches a field on the template. Common attributes are width, height for multi-line fields, mandatory to require it, variant for alternate displays such as "pills", and heightAutoExpand for rich-text fields that should grow with their content.

Screen
<Field id="Title" width={12} mandatory />
<Field id="Description" width={12} height={4} heightAutoExpand />
<Field id="Tags" width={12} variant="pills" />
Result
Title*
12 × 1
Description
12 × 4+
Tags
12 × 1

width={1} is a compact, icon-only field

A field at width={1} (with the default height={1}) renders in a compact, icon-only form and its label is always omitted, whatever the screen's label setting. In read-only views a user field shows just the avatar, and dropdown, tag, or multi-select fields show just their option icon. Use it for dense rows and board cards where space is tight.

Label

Use it to show read-only information. Labels are ideal for preview modals and board cards where you want to display a value without an editable input.

A label is one of two things:

  • A system label via type, which renders built-in metadata such as the status badge or the last-updated time.
  • A field label via type="field" with an id, which renders a template field read-only using the exact same widget the editable field uses (a status badge, a user chip, a date, and so on).

Provide type on its own for a system value, or type="field" together with an id for a field value. A bare id (no type) is a validation error - a field-backed label must use type="field". The valid system label types are:

typeRenders
fieldA template field's value, read-only - requires id
statusThe status badge
authorThe ticket's author
sequence-numberThe ticket key, such as TASK-01
template-nameThe template's name
template-iconThe template's icon
links / link-countLinked-ticket summary
created / created-relativeCreation date, absolute or relative
last-updated / last-updated-relativeLast-updated date, absolute or relative
Screen
<Row width={12}>
  <Label type="sequence-number" />
  <Label type="status" />
  <Label type="author" />
</Row>
<Label type="field" id="Priority" width={12} />
Result
sequence-number
auto × 1
status
auto × 1
author
auto × 1
Priority
read-only12 × 1

Row

Use it to lay elements out horizontally. Children with a width take that fraction of the row; children without one shrink to fit their content. To position the group, add a Spacer - for example a Spacer before the children pushes them to the right, and one on each side centers them.

Screen
<Row width={12}>
  <Label type="status" />
  <Field id="Priority" width={2} />
  <Field id="Assignee" width={3} />
</Row>
Result
status
auto × 1
Priority
2 × 1
Assignee
3 × 1

Column

Use it to group elements into a vertical stack that occupies part of the grid, so you can build multi-column layouts by placing columns inside a row.

Screen
<Row width={12}>
  <Column width={6}>
    <Field id="Assignee" width={12} />
    <Field id="Priority" width={12} />
  </Column>
  <Column width={6}>
    <Field id="Start Date" width={12} />
    <Field id="End Date" width={12} />
  </Column>
</Row>
Result
Assignee
max × 1
Priority
max × 1
Start Date
max × 1
End Date
max × 1

Section

Use it to group related fields inside a bordered block, keeping the layout scannable when a screen has many fields.

Screen
<Section title="Scheduling">
  <Field id="Start Date" width={6} />
  <Field id="End Date" width={6} />
  <Field id="Effort" width={12} />
</Section>
Result
Scheduling
Start Date
6 × 1
End Date
6 × 1
Effort
12 × 1

Collapsible

Use it to tuck away optional or advanced fields behind a header that expands on click. It requires a title, and defaultExpanded controls whether it starts open.

Screen
<Collapsible title="Advanced" defaultExpanded={true}>
  <Field id="Effort" width={6} />
  <Field id="Priority" width={6} />
</Collapsible>
<Collapsible title="Internal notes">
  <Field id="Description" width={12} height={3} />
</Collapsible>
Result
Effort
6 × 1
Priority
6 × 1

Color

Use it to tint the text of everything nested inside it. Color is a transparent wrapper: it injects a text color into its children but adds no box of its own, so it never changes how those children are laid out. Provide exactly one color source:

  • value - a literal CSS color. Anything the browser accepts works: value="#e11d48", value="rgb(225 29 72)", value="tomato".
  • class - a theme color token, so the color follows the active theme and adapts between light and dark automatically. Valid tokens are foreground, background, primary, primary-foreground, secondary, secondary-foreground, muted, muted-foreground, accent, accent-foreground, destructive, destructive-foreground, card, card-foreground, border, and ring.
  • from="template" - uses the color configured on the template itself (the same color shown on its icon). The raw template color is automatically adapted for the current light/dark background so the text stays legible, which is why it can look slightly different from the literal template color.
  • from="field" - uses the current value of another field as the color. This requires an id naming that field, and the field's value must be a CSS color string (for example a text field holding #e11d48). As the field's value changes, so does the tint. Omitting id with from="field" is a validation error.
Screen
<Color class="primary">
  <Label type="sequence-number" />
</Color>
<Color from="template">
  <Label type="template-name" width={12} />
</Color>
<Color from="field" id="Priority">
  <Label type="field" id="Priority" width={12} />
</Color>
<Color value="#e11d48">
  <Field id="Title" width={12} />
</Color>
Result
sequence-number
12 × 1
template-name
12 × 1
Priority
read-only12 × 1
Title
12 × 1

Each Color uses a different source: a theme token, the template color, another field's value, and a literal color.

One source only

Color must have exactly one of value, class, or from. Combining them - for example value and class together - is a validation error. Use nested Color tags if you need to switch color partway down a subtree.

Font

Use it to change the text size of its children. Like Color, Font is a transparent wrapper - it sets an inherited text size without adding a box, so it never affects layout. The size attribute takes a preset from xs up through 5xl (xs, sm, base, lg, xl, 2xl, 3xl, 4xl, 5xl).

Screen
<Font size="xl">
  <Field id="Title" width={12} />
</Font>
<Font size="xs">
  <Label type="created-relative" width={12} />
</Font>
Result
Title
12 × 1
created-relative
12 × 1

Trigger

Use it to make its children open the ticket's preview modal on interaction. Like Color and Font, Trigger is transparent to layout - it only adds the open-on-interaction behavior. The only supported target is preview-modal, which is useful on board cards so hovering the card surfaces the quick preview.

Screen
<Trigger target="preview-modal">
  <Label type="sequence-number" />
  <Label type="status" />
</Trigger>
Result
sequence-number
12 × 1
status
12 × 1

Plugin

Use it to embed a plugin-provided widget on the screen. It needs a manifestId identifying the plugin and a widgetId identifying which of its widgets to render. Use height to size the widget area.

Screen
<Plugin manifestId="github" widgetId="pull-requests" width={12} height={4} />
Result
Plugin · pull-requests
12 × 4

Spacer

Use it to insert flexible empty space, pushing neighbouring elements apart. A Spacer grows to fill the free space along its container's axis: inside a Row it expands horizontally, and inside a Column it expands vertically (the column needs room to grow - give it a height, or let it stretch to a taller sibling column in the same row). Give a Spacer a width instead to reserve a fixed gap rather than filling all free space.

Screen
<Row width={12}>
  <Label type="status" />
  <Spacer width={6} />
  <Label type="last-updated-relative" />
</Row>
Result
status
auto × 1
last-updated-relative
auto × 1

Inside a Column, a Spacer pushes elements to the top and bottom. Here the column has a height, so the spacer fills the gap between the two fields:

Screen
<Row width={12}>
  <Column width={6} height={5}>
    <Field id="Title" width={12} />
    <Spacer />
    <Field id="Status" width={12} />
  </Column>
  <Column width={6}>
    <Field id="Assignee" width={12} />
    <Field id="Priority" width={12} />
    <Field id="Effort" width={12} />
  </Column>
</Row>
Result
Title
max × 1
Status
max × 1
Assignee
max × 1
Priority
max × 1
Effort
max × 1

Text

Use it to render a piece of static, plain text on the screen - a heading, a caption, or a note that isn't tied to any field. Unlike every other tag, Text wraps its content between an opening and closing tag rather than being self-closing: whatever plain text sits between <Text> and </Text> is shown as-is. It takes layout attributes like width, x, y, and align, and it honours a surrounding Color or Font wrapper so you can size and tint it. It may only contain plain text - nested elements or {expressions} are a validation error.

Screen
<Font size="lg">
  <Text width={12}>Overview</Text>
  </Font>
  <Text width={12}>Fill in the fields below before submitting.</Text>
Result
Text
Overview12 × 1
Text
Fill in the fields below before submitting.12 × 1

A static heading and a caption, neither backed by a field.

Attribute Reference

This section documents every attribute in depth - what it does, which tags accept it, its accepted values and default, and the behavior that isn't obvious from the name. The table below is a quick lookup; the subsections that follow cover each attribute with an example.

AttributeApplies toValuesDefault
idField, Label, Colorfield name- (required on Field)
widthany1-12full width / content width
heightField, Plugin, Column1+1
xany (top level)0-11auto
yany (top level)0+auto
alignField, Labelleft, middle, rightleft
mandatoryFieldbooleanfalse
heightAutoExpandField, Pluginbooleanfalse
showIfEmptyField, Labelbooleantrue
variantFieldpillsnormal
truncateLinesField, Labelnumbernone
typeLabelsystem label type-
titleCollapsible, Sectiontext- (required on Collapsible)
defaultExpandedCollapsiblebooleanfalse
value / from / classColorsee Color- (one required)
sizeFontxs-5xl- (required)
targetTriggerpreview-modal- (required)
manifestId / widgetIdPluginplugin / widget id- (both required)

id

On Field (required), on Label with type="field" (required), and on Color with from="field", id names the template field involved. It must match a field that exists on the template, or the screen fails validation.

An id is a field's name as defined on the template - for example id="Assignee". On a Label, id must be paired with type="field" to render that field's value read-only; a bare id (no type) is a validation error.

Screen
<Field id="Assignee" width={6} />
<Label type="field" id="Priority" width={6} />
Result
Assignee
6 × 1
Priority
read-only6 × 1

An editable Assignee field beside a read-only Priority value, both referenced by id.

width

Applies to any element. width is how many of the 12 grid columns the element spans: width={12} is full width, width={6} is half, width={4} is a third. It behaves differently depending on the container:

  • In the grid (top level, Column, Section, Collapsible) an element with no width spans the full 12 columns.
  • In a Row an element with no width shrinks to fit its content instead of filling the row; give it a width to claim a fixed fraction. This is why the schematic labels a widthless row child as "auto".
  • width={1} is a special compact form: the field renders icon-only and its label is omitted regardless of the screen's label setting (see the callout under Field).
Screen
<Row width={12}>
  <Field id="Status" width={4} />
  <Field id="Priority" width={4} />
  <Field id="Assignee" width={4} />
</Row>
<Row width={12}>
  <Field id="Assignee" width={1} />
  <Field id="Title" />
</Row>
Result
Status
4 × 1
Priority
4 × 1
Assignee
4 × 1
Assignee
1 × 1
Title
auto × 1

Top row: three equal columns. Bottom row: an icon-only Assignee followed by a content-width Title.

height

Applies to Field, Plugin, and Column. height sizes multi-line content in the editor, measured in grid rows (each row is roughly 60px). It defaults to 1.

  • For rich-text and textarea fields it sets the editable area's height - a taller height shows more lines at once.
  • For a Plugin it sets the widget's height.
  • For a Column it sets a minimum height, giving a vertical Spacer room to push the column's elements apart top-to-bottom.
  • In read-only previews the forced height is dropped and the content collapses to its natural height, so a tall description doesn't leave a large gap when shown as read-only text.
  • Pair it with heightAutoExpand for fields that should grow with their content rather than scroll inside a fixed box.
Screen
<Field id="Description" width={12} height={5} />
<Field id="Notes" width={12} height={3} />
Result
Description
12 × 5
Notes
12 × 3

A tall rich-text editor and a shorter multi-line notes field.

x and y

Top-level only. x and y pin an element to an exact grid cell instead of letting it flow: x is the starting column (0-11) and y is the row (0+). Use them for precise placement, such as anchoring a label to the right edge on the same row as other elements.

At the top level, x + width must not exceed 12, and two explicitly positioned elements may not occupy the same cell. Inside containers, x and y have no effect - elements there flow in order.

Screen
<Label type="status" x={0} y={0} width={6} />
<Label type="last-updated-relative" x={6} y={0} width={6} align="right" />
Result
status
6 × 1
last-updated-relative
6 × 1

Two labels pinned to the same row, one at each edge.

align

Applies to Field and Label. align positions the element's content within its own cell: left (the default), middle, or right. It is most useful for pushing a value to the right edge - for example a timestamp label. It has no effect on a Row; to position a row's children, use a Spacer to push them left, right, or to center them.

Screen
<Label type="field" id="Priority" width={12} align="right" />
<Label type="last-updated-relative" width={12} align="right" />
Result
Priority
read-only12 × 1
last-updated-relative
12 × 1

A field-backed label and a system label, both aligned to the right edge of their cells.

mandatory

Applies to Field. Marks the field as required on this screen, adding a red asterisk after its label. It is a bare boolean, so mandatory alone means mandatory={true}. This governs whether the field is required on this screen - it decorates the label and flags the requirement for the create form.

Screen
<Field id="Title" width={12} mandatory />
<Field id="Assignee" width={6} />
Result
Title*
12 × 1
Assignee
6 × 1

Title is required (note the asterisk); Assignee is optional.

heightAutoExpand

Applies to Field (rich-text) and Plugin. When set, a rich-text field grows with its content instead of scrolling inside a fixed box - height becomes the minimum height rather than a hard cap. On a Plugin it lets the widget size itself to its content. It is a bare boolean. In the layout view the element's size reads as {min}+ (e.g. 3+), mirroring how a width-less element in a row shows auto.

Screen
<Field id="Description" width={12} height={3} heightAutoExpand />
Result
Description
12 × 3+

A rich-text field that starts three rows tall and grows as you type.

showIfEmpty

Applies to Field and Label. Defaults to true. Set showIfEmpty={false} to hide the element whenever its value is empty - useful on preview modals and cards where you only want populated fields to appear, keeping the layout tight. When the value is present the element renders normally.

Screen
<Label type="field" id="Priority" width={12} showIfEmpty={false} />
<Label type="field" id="Description" width={12} showIfEmpty={false} />
Result
Priority
read-only12 × 1
Description
read-only12 × 1

These labels disappear entirely when the ticket has no priority or description.

variant

Applies to Field. Selects an alternate display for tag, dropdown, and multi-select fields. The supported value is pills, which renders the selected options as colored pill badges instead of plain text. Other field types ignore it.

Screen
<Field id="Tags" width={12} variant="pills" />
<Field id="Components" width={12} variant="pills" />
Result
Tags
12 × 1
Components
12 × 1

Tag and multi-select values shown as pill badges.

truncateLines

Applies to Field and Label. Clamps the rendered text to at most N lines, adding an ellipsis when it overflows. Ideal for board cards and dense lists where a long description or title shouldn't push everything else down.

Screen
<Label type="field" id="Description" width={12} truncateLines={2} />
Result
Description
read-only12 × 1

A description label clamped to two lines.

type

Applies to Label. Chooses which built-in, read-only value the label renders. Use type on its own for system metadata, or type="field" together with an id to render a template field's value read-only. The valid types are:

typeRenders
fieldA template field's value, read-only - requires id
statusThe status badge
authorThe ticket's author
sequence-numberThe ticket key, such as TASK-01
template-nameThe template's name
template-iconThe template's icon
links / link-countLinked-ticket summary
created / created-relativeCreation date, absolute or relative
last-updated / last-updated-relativeLast-updated date, absolute or relative

When the underlying value is empty a system label shows a dash (-) unless showIfEmpty={false} hides it.

Screen
<Row width={12}>
  <Label type="sequence-number" />
  <Label type="status" />
  <Label type="author" />
</Row>
<Label type="field" id="Priority" width={12} />
Result
sequence-number
auto × 1
status
auto × 1
author
auto × 1
Priority
read-only12 × 1

System labels - the ticket key, status badge, and author - plus a read-only Priority field value.

title

Applies to Collapsible (required) and Section. Sets the header text. On a Collapsible it is the always-visible clickable header; on a Section it is the bordered group's legend. A Section with no title renders an untitled bordered block.

Screen
<Section title="Scheduling">
  <Field id="Start Date" width={6} />
  <Field id="End Date" width={6} />
</Section>
Result
Scheduling
Start Date
6 × 1
End Date
6 × 1

A titled section groups related fields.

defaultExpanded

Applies to Collapsible. Controls whether the collapsible starts open. Defaults to false (collapsed). It is a bare boolean, so defaultExpanded alone means defaultExpanded={true}.

Screen
<Collapsible title="Advanced" defaultExpanded={true}>
  <Field id="Effort" width={6} />
  <Field id="Story Points" width={6} />
</Collapsible>
Result
Effort
6 × 1
Story Points
6 × 1

A collapsible that renders expanded on load.

value, from, and class

Applies to Color. These are the three mutually exclusive color sources - provide exactly one. See the Color tag for full detail and examples.

  • value - a literal CSS color, such as value="#e11d48".
  • class - a theme color token (for example primary or muted-foreground) that follows the active theme.
  • from - a dynamic source: from="template" uses the template's color (adapted for light/dark), and from="field" uses the current value of the field named by id as the color, so it also requires an id.
Screen
<Color from="template">
  <Label type="template-name" width={12} />
</Color>
Result
template-name
12 × 1

Text tinted with the template's own color.

size

Applies to Font (required). Sets the text size of the font wrapper's children to one of the presets xs, sm, base, lg, xl, 2xl, 3xl, 4xl, or 5xl.

Screen
<Font size="2xl">
  <Field id="Title" width={12} />
</Font>
Result
Title
12 × 1

A larger title via the Font wrapper.

target

Applies to Trigger (required). Names the interaction the wrapper performs. The only supported value is preview-modal, which opens the ticket's quick preview when its children are interacted with - most useful on board cards.

Screen
<Trigger target="preview-modal">
  <Label type="sequence-number" />
  <Label type="status" />
</Trigger>
Result
sequence-number
12 × 1
status
12 × 1

Interacting with these labels opens the preview modal.

manifestId and widgetId

Applies to Plugin (both required). manifestId identifies the installed plugin and widgetId selects which of that plugin's widgets to render. Pair them with height to size the widget's area and heightAutoExpand to let it grow to fit.

Screen
<Plugin manifestId="github" widgetId="pull-requests" width={12} height={4} />
Result
Plugin · pull-requests
12 × 4

Embed a plugin widget by naming its plugin and widget.

Validation Rules

The editor rejects markup that breaks these rules:

  • Tags: only the twelve tags above are valid. Leaf tags (Field, Label, Plugin, Spacer) must be self-closing, and container tags may not enclose free text - only elements. The only tag that may hold plain text is Text, which in turn may not hold nested elements or expressions.
  • Field: must have an id that exists on the template.
  • Label: must have a type (system value) or type="field" with an id. A bare id without type="field" is invalid, type="field" requires an id naming a field that exists on the template, and any type must be one of the valid label types.
  • Collapsible: must have a title.
  • Color: must have exactly one of value, class, or from. from must be field or template, and from="field" also requires an id.
  • Font: must have a size.
  • Trigger: must have a target.
  • Ranges: width must be between 1 and 12, and x between 0 and 11. (height, y, and truncateLines are not range-checked.)
  • Grid: at the top level x + width may not exceed 12, and two explicitly positioned elements may not overlap the same cell.

Complete Examples

A tidy create form: a required title, two fields side by side, and a tall description.

Screen
<Field id="Title" width={12} mandatory />
<Row width={12}>
  <Field id="Priority" width={6} />
  <Field id="Assignee" width={6} />
</Row>
<Field id="Description" width={12} height={5} heightAutoExpand />
Result
Title*
12 × 1
Priority
6 × 1
Assignee
6 × 1
Description
12 × 5+

A preview modal with a status row on the left and a right-aligned timestamp beside it, then the description.

Screen
<Row width={6}>
  <Label type="status" />
  <Field id="Priority" width={2} />
  <Field id="Assignee" width={2} />
</Row>
<Label type="last-updated-relative" x={6} y={0} width={6} align="right" />
<Field id="Description" width={12} height={3} />
Result
status
auto × 1
Priority
2 × 1
Assignee
2 × 1
last-updated-relative
6 × 1
Description
12 × 3

See also