Kit
The kit holds what a file's designs share. Send it with any build, or alone; it merges into the file's kit by name and is saved with the file.
{
"kit": {
"font": "Inter 15/22",
"colors": {
"ink": "#1A1423",
"paper": "#F6F1EA",
"saffron": "#F29F05",
"rose": "#D9485F",
"mute": "#6F6879",
"board": "#ECE6DD"
},
"type": {
"display": "Fraunces 40/44 600 -1",
"title": "Fraunces 22/26 600",
"deva": "Tiro Devanagari Hindi 14/20",
"deva-xl": "Tiro Devanagari Hindi 40/44",
"eyebrow": "Inter 12/16 700 +1.6 caps",
"small": "Inter 13/18 500"
},
"parts": {
"swara": {
"params": {"note": "", "tone": "@saffron"},
"node": {
"row": [{"text": "{note}", "style": "deva"}],
"padding": [2, 8],
"cornerRadius": 8,
"fill": "{tone}/20%"
}
},
"sticker": {
"params": {"label": "", "tone": "#FBE3B5", "turn": 0},
"node": {
"row": [{"text": "{label}", "font": "Inter 13/18 700"}],
"padding": [6, 12],
"cornerRadius": 14,
"fill": "{tone}",
"rotation": "{turn}"
}
},
"status": {
"params": {"ink": "@ink"},
"node": {
"row": [
{"text": "9:41", "font": "Inter 15/20 600", "color": "{ink}"},
{"space": "fill"},
{"icon": "lucide:signal", "size": 16, "color": "{ink}"},
{"icon": "lucide:battery-full", "size": 16, "color": "{ink}"}
],
"gap": 6,
"align": "center",
"width": "fill",
"padding": [16, 26, 6, 26]
}
},
"phone": {
"params": {"name": "Phone", "bg": "@paper", "children": []},
"node": {
"name": "{name}",
"column": [
{"status": {}},
{
"column": "{children}",
"padding": [10, 22, 28, 22],
"gap": 18,
"width": "fill",
"height": "fill"
}
],
"width": 390,
"height": 844,
"cornerRadius": 44,
"fill": "{bg}"
}
}
}
}
}
The kit behind the Raag examples: a default font, six colors, six type presets and four parts.
| Section | Holds | Used as |
|---|---|---|
font | the default font of every text | nothing to write: texts start from it |
colors | named colors, one value or one per mode | @ink, @ink/40% |
modes | mode names for the colors (["Light", "Dark"]) | "mode": "Dark" on a layer |
type | named text presets (a font or text keys) | {"title": "Today"}, "style": "title" |
parts | nodes with parameters | {"chip": "May 2027"} |
Colors become variables
Kit colors are variables in the collection Kit, so a layer painted @ink stays bound: change ink in the kit and every layer follows, and a layer shown in another mode takes that mode's value. A tint such as @rose/20% is a variable of its own (Kit/rose-20, made the first time it is used), so tints follow modes too. See Color.
Presets become text styles
Each preset is a text style of the same name. {"title": "Today"} makes a text that follows the style; send the preset again with new values and every text using it changes at once, with no rebuild.
Parts
A part is a node with parameters. Inside it, {name} stands for a parameter: a string that is only {name} takes the argument as it is (a number, a color, a list), and {name} inside longer text is replaced by its text.
{
"kit": {
"parts": {
"chip": {
"params": {"label": "", "tone": "@saffron"},
"node": {
"row": [{"text": "{label}"}],
"padding": [2, 8],
"cornerRadius": 8,
"fill": "{tone}/20%"
}
}
}
}
}
{"chip": "May 2027"}passes one value to the first parameter.{"chip": {"label": "May 2027", "tone": "@rose"}}names them.- Keys beside the name apply to the part's root:
{"chip": "Late", "rotation": -4}. - A parameter called
childrentakes the children written at the use: put"{children}"where they go. - A list parameter whose default is
[], written alone among children ("{status}"), adds nothing when the use leaves it out and its items when it is given. - An argument may be a node or a list of nodes, not only text:
{"card": {"title": "Today", "children": [{"chip": "Late"}]}}. - The part's root is named after the part unless its node names it:
"name": "{title}"names each use by its argument. - Parts use presets, other parts and components. A part that uses itself is stopped at 24 levels.
A part is not a component: it expands where it is used and leaves no link. When the same thing must stay in sync across a file, make it a component.
Seeing and changing the kit
$ facet kit
{"font":"Inter 15/22","colors":{"ink":"#1A1423","paper":"#F6F1EA","saffron":"#F29F05","rose":"#D9485F","mute":"#6F6879","board":"#ECE6DD"},"type":{"display":{"fontFamily":"Fraunces","fontSize":40.0,"lineHeight":44.0,"fontWeight":600,"letterSpacing":-1.0},"title":{"fontFamily":"Fraunces","fontSize":22.0,"lineHeight":26.0,"fontWeight":600},"deva":{"fontFamily":"Tiro Devanagari Hindi","fontSize":14.0,"lineHeight":20.0},"deva-xl":{"fontFamily":"Tiro Devanagari Hindi","fontSize":40.0,"lineHeight":44.0},"eyebrow":{"fontFamily":"Inter","fontSize":12.0,"lineHeight":16.0,"fontWeight":700,"letterSpacing":1.6,"textCase":"upper"},"small":{"fontFamily":"Inter","fontSize":13.0,"lineHeight":18.0,"fontWeight":500}},"parts":{"swara":{"params":{"note":"","tone":"@saffron"},"node":{"row":[{"text":"{note}","style":"deva"}],"padding":[2,8],"cornerRadius":8,"fill":"{tone}/20%"}},"sticker":{"params":{"label":"","tone":"#FBE3B5","turn":0},"node":{"row":[{"text":"{label}","font":"Inter 13/18 700"}],"padding":[6,12],"cornerRadius":14,"fill":"{tone}","rotation":"{turn}"}},"status":{"params":{"ink":"@ink"},"node":{"row":[{"text":"9:41","font":"Inter 15/20 600","color":"{ink}"},{"space":"fill"},{"icon":"lucide:signal","size":16,"color":"{ink}"},{"icon":"lucide:battery-full","size":16,"color":"{ink}"}],"gap":6,"align":"center","width":"fill","padding":[16,26,6,26]}},"phone":{"params":{"name":"Phone","bg":"@paper","children":[]},"node":{"name":"{name}","column":[{"status":{}},{"column":"{children}","padding":[10,22,28,22],"gap":18,"width":"fill","height":"fill"}],"width":390,"height":844,"cornerRadius":44,"fill":"{bg}"}}}}
Send a kit section again to change it; names you send replace those entries and the rest stay. null removes an entry: {"kit": {"parts": {"badge": null}}}. A removed color keeps its variable, because layers may still use it; delete the variable with facet call variables '{"delete": "Kit/rose"}' once nothing does.
A name lives in one section: a preset and a part cannot share it, because {"badge": ...} could mean either. The kit refuses the second one and says which to rename.