Vaktor v0.1

Component catalog

The required field is type. id is only needed on field. Unknown fields are ignored. Below: the minimal JSON for each view + variants.

table and chart are documented separately: table, chart.

section — group

The only container. Heading, footer, nested components.

{
  "type": "section",
  "header": "Overview",
  "footer": "Updated just now",
  "items": [  ]
}
FieldTypeDescription
headerstring?heading
footerstring?footer caption
items[component]contents
columnsint?≥2 → tiles in a grid (e.g. metric)
template + dataobject + string/arraylist section — see Data binding

Tiles in a grid: "columns": 2 + several metric in items.

text — text

{
  "type": "text",
  "body": "Sync complete",
  "style": "caption"
}

style: body (default), headline, caption, footnote.

row — key–value row

Icon + title + value on the right. With an action it’s tappable and leads further.

{
  "type": "row",
  "icon": "person",
  "title": "Jane Doe",
  "subtitle": "admin",
  "value": "online",
  "action": {
    "type": "navigate",
    "endpoint": "/pages/users/42"
  }
}
FieldTypeDescription
titlestringtitle
subtitlestring?second line
valuestring?value on the right
iconstring?SF Symbol
statusenum?badge role instead of value (values — see status)
actionaction?present → row is tappable
wrapbool?true — wrap, otherwise truncate
context_menu[action]?long-press menu
swipe_actions[action]?swipes (iOS); on Mac → context menu

Status row: instead of value, specify status — a semantic glyph appears on the right (success → checkmark, error → cross).

metric — KPI indicator

{
  "type": "metric",
  "title": "Revenue",
  "value": "$340,000",
  "trend": "+12%",
  "trend_direction": "up",
  "sentiment": "positive"
}
FieldTypeValues
titlestringcaption
valuestringformatted by the server
trendstring?”+12%“
trend_directionenum?up, down, flat
sentimentenum?positive, negative, neutral — trend color

Dashboard of tiles: several metric in a section with columns: 2.

status — state badge

{
  "type": "status",
  "label": "Database",
  "role": "success"
}

role: success, warning, error, info, neutral. Each role gets a semantic color that’s correct in dark mode.

button — button

{
  "type": "button",
  "title": "Restart",
  "icon": "arrow.clockwise",
  "role": "destructive",
  "action": {
    "type": "submit",
    "endpoint": "/restart",
    "method": "POST",
    "confirm": {
      "title": "Restart the service?",
      "destructive": true
    }
  }
}

role: primary (accent), secondary (default), destructive (red).

destructive without confirm is invalid — always pair it with a confirm.

field — form field

The only stateful component. id is required — it’s the key in submit.

{
  "type": "field",
  "id": "email",
  "kind": "text",
  "label": "E-mail",
  "format": "email",
  "validation": {
    "required": true,
    "max_length": 120
  }
}
FieldTypeValues
idstringkey in submit
kindenumtext, textarea, number, toggle, picker, date, password
labelstringcaption
valueany?initial value
options[{value, label}]?for picker
validationobject?required, min, max, max_length
formatenum?plain, url, domain, email, username, phone
persistbool?store locally; binding {settings.<id>}

format sets the keyboard and autocapitalization (domain → no capital first letter). Dropdown: kind: picker + options. Password: kind: password.

image — content image

{
  "type": "image",
  "url": "https://cdn.example.com/a.jpg",
  "shape": "avatar"
}

shape: avatar (circle), thumbnail, banner. For content only — interface icons are always SF Symbols.

empty_state — empty state

{
  "type": "empty_state",
  "icon": "tray",
  "title": "No orders",
  "message": "New ones will appear here",
  "action": {
    "type": "navigate",
    "endpoint": "/pages/orders/new"
  }
}

Fields: title, message?, icon? (SF Symbol), action? (button).