Card
The Card node in VaultPDF. Renders a structured key-value block with configurable variants, border styles, label layouts, and field definitions. Ideal for contact details, order summaries, address blocks, and grouped metadata.
Overview
A Card (card) is a self-contained block that displays a set of labelled fields as a structured table. Unlike a section (which lays out child nodes vertically), a card owns its field definitions directly through a fields array, making it more portable and easier to compose inside layout groups.
Four variants give the card different visual treatments:
| Variant | Best used for |
|---|---|
default | General key-value information (customer details, order metadata) |
summary | Totals and KPI callouts - each field becomes a mini labeled tile |
address | Postal or delivery addresses - values stacked as a single text block, no labels |
grouped | Nested payload objects - renders child cards recursively |
Title & Header
| Property | Type | Default | Description |
|---|---|---|---|
title | string | - | Card heading. Supports {{FIELDNAME}} tokens. |
subtitle | string | - | Optional secondary line rendered beneath the title in a smaller muted font. |
titleIcon | string | - | Decorative icon key placed next to the title (e.g. "star", "package", "list"). |
accentColor | color | theme.primary | Color applied to the title text, icon accent pipe, and -- when showHeader is off -- the inline title. |
showHeader | boolean | true | When true, renders a filled header bar (same style as a section header). When false, the title is rendered inline as an accent-colored heading inside the card body. |
headerFillColor | color | theme.primary | Background color of the header bar. Only relevant when showHeader is true. |
headerBorderBottom | number | 0 | Width (pt) of the border line drawn below the header bar. |
Title-less cards
Set showHeader: false and omit title entirely to produce a title-less card - useful for compact summary tiles inside a layout group where the section already provides a heading.
Data Binding
| Property | Type | Default | Description |
|---|---|---|---|
dataPath | string | - | Dot-path into the JSON payload used to scope field-name suggestions in the Designer. Example: "customer" makes the Designer suggest fields from payload.customer. |
All field value expressions resolve against the full JSON payload root - dataPath does not restrict which payload fields can be referenced. Setting dataPath tells the Designer which object in your payload to use when suggesting field names, but you can reference any payload path in a value string regardless.
Variant
| Property | Type | Default | Description |
|---|---|---|---|
variant | "default" | "summary" | "address" | "grouped" | "default" | Controls the visual treatment of the card body. See Variant Details below. |
Field Layout
| Property | Type | Default | Description |
|---|---|---|---|
labelPosition | "left" | "top" | "left" | "left" - label and value sit side-by-side in a two-column row. "top" - label renders above the value in a single column. |
labelWidth | number | 110 | Width (pt) of the label column when labelPosition is "left". Ignored for "top" layout. |
valueAlign | "left" | "right" | "center" | "left" | Horizontal alignment of value text within its cell. |
columns | 1 | 2 | 3 | 1 | Splits the fields array into N side-by-side columns inside the card body. |
fieldGap | number | 4 | Gap (pt) between internal field columns. Also used as the row spacing between mini-tiles in summary variant. |
labelBold | boolean | false | Apply bold to all label cells. |
valueBold | boolean | false | Apply bold to all value cells. |
valueAllCaps | boolean | false | Uppercase all value text. Static values are transformed at build time; {{token}} values are flagged for runtime transformation. |
emphasizeLastRow | boolean | false | Renders the last field row with bold text and a slightly larger font size -- useful for "Total" rows in a summary card. |
showDivider | boolean | same as emphasizeLastRow | Draws a horizontal separator line immediately above the last field row when there are two or more fields. Set explicitly to override the default. |
Border & Background
| Property | Type | Default | Description |
|---|---|---|---|
borderStyle | "full" | "minimal" | "none" | "full" ("minimal" for address variant) | Outer card border. "full" -- full rectangle on all 4 sides. "minimal" -- bottom line only. "none" -- no outer border. |
borderColor | color | theme.borderColor | Color of the outer card border. |
innerBorderStyle | "full" | "minimal" | "none" | "minimal" | Divider lines between field rows. "full" -- full-width row separators. "minimal" -- bottom-only line on each row. "none" -- no row dividers. |
innerBorderColor | color | theme.borderColor | Color of the inner row dividers. |
backgroundColor | color | (transparent) | Card body fill color. Useful for highlighted summary blocks. |
radius | number | 0 | Corner radius (pt) applied to the outer card border. Range: 0--16. |
Spacing
| Property | Type | Default | Description |
|---|---|---|---|
padding | number | 12 | Inner padding (pt) applied to the card content area. Range: 0--30. |
marginTop | number | 0 | Space (pt) above the card block. Range: 0--60. |
marginBottom | number | 0 | Space (pt) below the card block. Range: 0--60. |
width | number | "star" | "star" | Explicit card width in pt, or "star" to fill available space. Useful when placing a narrow summary card on the right side of a layout group. |
Variant Details
Default
The standard label-value block. Each field renders as a two-column row (label left, value right) or a single stacked column when labelPosition: "top". Rows are separated by innerBorderStyle lines.
{
"type": "card",
"props": {
"title": "Customer",
"variant": "default",
"showHeader": true,
"labelPosition": "left",
"labelWidth": 110,
"valueAlign": "left",
"borderStyle": "full",
"innerBorderStyle": "minimal",
"fields": [
{ "key": "name", "label": "Name", "value": "{{customer.name}}" },
{ "key": "email", "label": "Email", "value": "{{customer.email}}" },
{ "key": "phone", "label": "Phone", "value": "{{customer.phone}}" }
]
}
}Summary
Each field becomes a mini tile - a small table cell with the label displayed in a filled header bar (accentColor background, contrast text) and the value centered below. Tiles are arranged in rows of up to columns tiles each (defaults to 1 if columns is unset, or fills up to 4 tiles per row automatically when columns: 0).
Use emphasizeLastRow: true and showDivider: true to visually separate a total row from sub-totals.
{
"type": "card",
"props": {
"title": "Order Totals",
"titleIcon": "star",
"variant": "summary",
"showHeader": false,
"columns": 1,
"valueAlign": "right",
"emphasizeLastRow": true,
"showDivider": true,
"fields": [
{ "key": "subtotal", "label": "Subtotal", "value": "{{subtotal}}", "format": "currency" },
{ "key": "tax", "label": "GST (10%)", "value": "{{tax}}", "format": "currency" },
{ "key": "total", "label": "Total", "value": "{{total}}", "format": "currency" }
]
}
}Address
Values are rendered as a single multiline text block - one line per field, labels omitted. Ideal for postal addresses, delivery notes, or any block where the formatting itself conveys structure.
The outer borderStyle defaults to "minimal" for this variant (bottom-line only). Set borderStyle: "none" for a borderless address block.
{
"type": "card",
"props": {
"title": "Deliver To",
"variant": "address",
"showHeader": false,
"borderStyle": "none",
"fields": [
{ "key": "line_0", "label": "Name", "value": "{{deliveryName}}" },
{ "key": "line_1", "label": "Street", "value": "{{deliveryStreet}}" },
{ "key": "line_2", "label": "City/State/Zip", "value": "{{deliveryCity}}, {{deliveryState}} {{deliveryPostcode}}" }
]
}
}Grouped
Renders a nested payload object as a stack of sub-cards. Instead of a flat fields array, a grouped card holds a children array - each child is typically a default or address card. Use this variant when your payload contains nested objects (for example, a shipTo object that itself contains a contact sub-object) and you want each level to appear as a distinct, titled card block.
Fields
The fields array defines the rendered rows (or tiles, for summary). Fields are rendered in declaration order.
Field Properties
| Property | Type | Required | Description |
|---|---|---|---|
key | string | Yes | Unique identifier for the field within this card. |
label | string | Yes | Display label. Shown as-is in default variant; used as tile heading in summary. Omitted in address. |
value | string | Yes | Value text. Supports {{fieldName}} and {{fieldName|format}} tokens. |
format | string | No | Format hint applied to {{token}} values. See Field Formats. |
hidden | boolean | No | When true, the field is omitted from the rendered output. Useful for toggling fields without removing them from the schema. |
style | object | No | Per-field style overrides: { fontSize, color }. |
span | "full" | No | Force the field to render at full card width, below the multi-column field layout. Useful for long text or signature-style lines. |
Field Formats
| Value | Effect |
|---|---|
text | Plain string -- no transformation (default). |
number | Numeric with locale-aware thousands separator. |
currency | Locale-formatted currency value (e.g. $1,234.56). |
date | Short date string (e.g. 31/03/2026). |
datetime | Date and time (e.g. 31/03/2026 14:22). |
boolean | Renders true/false as Yes/No. |
percentage | Appends % and formats as a number. |
Formats can also be embedded inline in the value string using the pipe syntax: "{{amount|currency}}".
Connecting tokens to your payload
{{FIELDNAME}} tokens are resolved against the full JSON payload at render time. Use dot-notation for nested fields: {{order.customer.name}} reads payload.order.customer.name. Field names containing dots or spaces are also supported using the greedy longest-match resolver - for example {{Header.No.}} resolves the literal key "No." on Header.
Visibility
| Property | Type | Default | Description |
|---|---|---|---|
hidden | boolean | false | Hides the entire card. Equivalent to visible: false on section nodes. |
Examples
Info card -- two columns, top label
{
"type": "card",
"props": {
"title": "Order Details",
"titleIcon": "package",
"variant": "default",
"showHeader": true,
"labelPosition": "top",
"columns": 2,
"fieldGap": 4,
"borderStyle": "full",
"innerBorderStyle": "none",
"padding": 10,
"fields": [
{ "key": "orderId", "label": "Order Number", "value": "{{orderId}}" },
{ "key": "orderDate", "label": "Date", "value": "{{orderDate}}", "format": "date" },
{ "key": "reference", "label": "Reference", "value": "{{reference}}" },
{ "key": "dueDate", "label": "Due Date", "value": "{{dueDate}}", "format": "date" }
]
}
}Summary card -- totals block on the right
Pair this card with a table on the left inside a layout_group:
{
"type": "card",
"props": {
"title": "Invoice Totals",
"variant": "summary",
"showHeader": false,
"accentColor": "theme.primary",
"columns": 1,
"valueAlign": "right",
"labelBold": true,
"emphasizeLastRow": true,
"showDivider": true,
"borderStyle": "full",
"innerBorderStyle": "full",
"padding": 10,
"fields": [
{ "key": "subtotal", "label": "Subtotal", "value": "{{subtotal}}", "format": "currency" },
{ "key": "discount", "label": "Discount", "value": "{{discount}}", "format": "currency" },
{ "key": "tax", "label": "GST (10%)", "value": "{{tax}}", "format": "currency" },
{ "key": "total", "label": "Total Due", "value": "{{totalDue}}", "format": "currency" }
]
}
}Address card -- borderless
{
"type": "card",
"props": {
"title": "Ship To",
"variant": "address",
"showHeader": false,
"borderStyle": "none",
"accentColor": "theme.primary",
"padding": 0,
"fields": [
{ "key": "company", "label": "Company", "value": "{{shipTo.company}}" },
{ "key": "street", "label": "Street", "value": "{{shipTo.street}}" },
{ "key": "suburb", "label": "City", "value": "{{shipTo.city}}, {{shipTo.state}} {{shipTo.postcode}}" },
{ "key": "country", "label": "Country", "value": "{{shipTo.country}}" }
]
}
}Placing Cards in Layout Groups
Cards placed inside a layout_group respect the column width computed by the group. Use width to pin a card to a fixed pt size (e.g. a narrow totals card on the right):
{
"type": "layout_group",
"props": { "columns": [{ "width": "star" }, { "width": 30 }], "columnGap": 12 },
"children": [
{ "type": "table", "props": { "...": "..." } },
{
"type": "card",
"props": {
"variant": "summary",
"width": 160
}
}
]
}Equal-height borders in layout groups
When a card is placed inside a layout_group, its borderStyle is promoted to the cell level so all cells in the same row share equal-height borders. Cards and sections placed side-by-side will automatically align their border lines.
Related Pages
- Section - child-node-based layout block; use cards when you want self-contained field ownership
- Layout Group - multi-column container for placing cards side-by-side
- Table - for array-backed tabular data
- Template Settings - theme tokens referenced by
accentColor,borderColor, andheaderFillColor
Section
The primary content container in VaultPDF. Sections group related fields and controls, provide titled regions with optional accent pipes and icons, and support a full box model for borders, fills, and header rows.
Bento Card
A visually-rich feature showcase card. The Bento Card renders a tiered information block with an icon, badge, title, hero metric, feature list, and call-to-action. Ideal for plan comparisons, service tiers, product offerings, and KPI summaries.