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.
<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} />Anatomy
An element is either a container with children:
<Row>
<Field id="Priority" width={6} />
<Field id="Assignee" width={6} />
</Row>or a self-closing leaf:
<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:mandatoryis the same asmandatory={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.
widthsets 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
Rowto sit side by side. xandyplace an element at an exact column and row for precise control.xis the starting column (0-11) andyis the row (0+).- At the top level,
x + widthmust not exceed 12, and two positioned elements may not occupy the same cell.
<Field id="Status" width={4} />
<Field id="Priority" width={4} />
<Field id="Assignee" width={4} />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.
<Field id="Title" width={12} mandatory />
<Field id="Description" width={12} height={4} heightAutoExpand />
<Field id="Tags" width={12} variant="pills" />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 anid, 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:
type | Renders |
|---|---|
field | A template field's value, read-only - requires id |
status | The status badge |
author | The ticket's author |
sequence-number | The ticket key, such as TASK-01 |
template-name | The template's name |
template-icon | The template's icon |
links / link-count | Linked-ticket summary |
created / created-relative | Creation date, absolute or relative |
last-updated / last-updated-relative | Last-updated date, absolute or relative |
<Row width={12}>
<Label type="sequence-number" />
<Label type="status" />
<Label type="author" />
</Row>
<Label type="field" id="Priority" width={12} />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.
<Row width={12}>
<Label type="status" />
<Field id="Priority" width={2} />
<Field id="Assignee" width={3} />
</Row>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.
<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>Section
Use it to group related fields inside a bordered block, keeping the layout scannable when a screen has many fields.
<Section title="Scheduling">
<Field id="Start Date" width={6} />
<Field id="End Date" width={6} />
<Field id="Effort" width={12} />
</Section>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.
<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>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 areforeground,background,primary,primary-foreground,secondary,secondary-foreground,muted,muted-foreground,accent,accent-foreground,destructive,destructive-foreground,card,card-foreground,border, andring.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 anidnaming 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. Omittingidwithfrom="field"is a validation error.
<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>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).
<Font size="xl">
<Field id="Title" width={12} />
</Font>
<Font size="xs">
<Label type="created-relative" width={12} />
</Font>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.
<Trigger target="preview-modal">
<Label type="sequence-number" />
<Label type="status" />
</Trigger>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.
<Plugin manifestId="github" widgetId="pull-requests" width={12} height={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.
<Row width={12}>
<Label type="status" />
<Spacer width={6} />
<Label type="last-updated-relative" />
</Row>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:
<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>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.
<Font size="lg">
<Text width={12}>Overview</Text>
</Font>
<Text width={12}>Fill in the fields below before submitting.</Text>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.
| Attribute | Applies to | Values | Default |
|---|---|---|---|
id | Field, Label, Color | field name | - (required on Field) |
width | any | 1-12 | full width / content width |
height | Field, Plugin, Column | 1+ | 1 |
x | any (top level) | 0-11 | auto |
y | any (top level) | 0+ | auto |
align | Field, Label | left, middle, right | left |
mandatory | Field | boolean | false |
heightAutoExpand | Field, Plugin | boolean | false |
showIfEmpty | Field, Label | boolean | true |
variant | Field | pills | normal |
truncateLines | Field, Label | number | none |
type | Label | system label type | - |
title | Collapsible, Section | text | - (required on Collapsible) |
defaultExpanded | Collapsible | boolean | false |
value / from / class | Color | see Color | - (one required) |
size | Font | xs-5xl | - (required) |
target | Trigger | preview-modal | - (required) |
manifestId / widgetId | Plugin | plugin / 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.
<Field id="Assignee" width={6} />
<Label type="field" id="Priority" width={6} />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 nowidthspans the full 12 columns. - In a
Rowan element with nowidthshrinks to fit its content instead of filling the row; give it awidthto 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).
<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>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
heightshows more lines at once. - For a
Pluginit sets the widget's height. - For a
Columnit sets a minimum height, giving a verticalSpacerroom 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
heightAutoExpandfor fields that should grow with their content rather than scroll inside a fixed box.
<Field id="Description" width={12} height={5} />
<Field id="Notes" width={12} height={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.
<Label type="status" x={0} y={0} width={6} />
<Label type="last-updated-relative" x={6} y={0} width={6} align="right" />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.
<Label type="field" id="Priority" width={12} align="right" />
<Label type="last-updated-relative" width={12} align="right" />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.
<Field id="Title" width={12} mandatory />
<Field id="Assignee" width={6} />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.
<Field id="Description" width={12} height={3} heightAutoExpand />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.
<Label type="field" id="Priority" width={12} showIfEmpty={false} />
<Label type="field" id="Description" width={12} showIfEmpty={false} />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.
<Field id="Tags" width={12} variant="pills" />
<Field id="Components" width={12} variant="pills" />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.
<Label type="field" id="Description" width={12} truncateLines={2} />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:
type | Renders |
|---|---|
field | A template field's value, read-only - requires id |
status | The status badge |
author | The ticket's author |
sequence-number | The ticket key, such as TASK-01 |
template-name | The template's name |
template-icon | The template's icon |
links / link-count | Linked-ticket summary |
created / created-relative | Creation date, absolute or relative |
last-updated / last-updated-relative | Last-updated date, absolute or relative |
When the underlying value is empty a system label shows a dash (-) unless showIfEmpty={false} hides it.
<Row width={12}>
<Label type="sequence-number" />
<Label type="status" />
<Label type="author" />
</Row>
<Label type="field" id="Priority" width={12} />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.
<Section title="Scheduling">
<Field id="Start Date" width={6} />
<Field id="End Date" width={6} />
</Section>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}.
<Collapsible title="Advanced" defaultExpanded={true}>
<Field id="Effort" width={6} />
<Field id="Story Points" width={6} />
</Collapsible>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 asvalue="#e11d48".class- a theme color token (for exampleprimaryormuted-foreground) that follows the active theme.from- a dynamic source:from="template"uses the template's color (adapted for light/dark), andfrom="field"uses the current value of the field named byidas the color, so it also requires anid.
<Color from="template">
<Label type="template-name" width={12} />
</Color>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.
<Font size="2xl">
<Field id="Title" width={12} />
</Font>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.
<Trigger target="preview-modal">
<Label type="sequence-number" />
<Label type="status" />
</Trigger>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.
<Plugin manifestId="github" widgetId="pull-requests" width={12} height={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 isText, which in turn may not hold nested elements or expressions. - Field: must have an
idthat exists on the template. - Label: must have a
type(system value) ortype="field"with anid. A bareidwithouttype="field"is invalid,type="field"requires anidnaming a field that exists on the template, and anytypemust be one of the valid label types. - Collapsible: must have a
title. - Color: must have exactly one of
value,class, orfrom.frommust befieldortemplate, andfrom="field"also requires anid. - Font: must have a
size. - Trigger: must have a
target. - Ranges:
widthmust be between 1 and 12, andxbetween 0 and 11. (height,y, andtruncateLinesare not range-checked.) - Grid: at the top level
x + widthmay 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.
<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 />A preview modal with a status row on the left and a right-aligned timestamp beside it, then the description.
<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} />