Skip to content
← State Machine
Edit State Machine
Name
Also known as (comma separated)
Brief definition
Formal model of states and transitions.
Notes
Tier
Core
Useful
Reference
Deleted
Controls schema (JSON array)
[ { "id": "title", "type": "text", "label": "Title", "default": "Document lifecycle" }, { "id": "current", "type": "select", "label": "Current state", "default": "in review", "options": [ "draft", "in review", "approved", "published" ] }, { "id": "accent", "type": "color", "label": "Accent colour", "default": "#241430", "presets": [ "#241430", "#A8E6C1", "#F5C2E0", "#BFD9F2", "#D9C8F0", "#E8A020" ] }, { "id": "compact", "type": "toggle", "label": "Hide transition labels", "default": false }, { "id": "nodeGap", "type": "number", "label": "Node gap", "default": 28, "min": 16, "max": 48, "step": 4 } ]
Templates (JSON object keyed by format)
{ "html": "<div class=\"state-machine\" data-current=\"{{current}}\" style=\"--accent:{{accent}};--gap:{{nodeGap}}px;\">\n <h3 class=\"state-machine__title\">{{title}}</h3>\n <div class=\"state-machine__diagram\">\n <div class=\"state-machine__node state-machine__node--draft\">\n <span class=\"state-machine__current\" aria-current=\"step\">Current</span>\n <span class=\"state-machine__name\">Draft</span>\n </div>\n <div class=\"state-machine__edge\">\n <svg class=\"state-machine__arrow\" viewBox=\"0 0 40 10\" aria-hidden=\"true\"><path d=\"M0 5h32M26 1l6 4-6 4\" fill=\"none\" stroke=\"#241430\" stroke-width=\"1.6\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/></svg>\n {{#unless compact}}<span class=\"state-machine__label\">submit</span>{{/unless}}\n </div>\n <div class=\"state-machine__node state-machine__node--in-review\">\n <span class=\"state-machine__current\" aria-current=\"step\">Current</span>\n <span class=\"state-machine__name\">In review</span>\n </div>\n <div class=\"state-machine__edge\">\n <svg class=\"state-machine__arrow\" viewBox=\"0 0 40 10\" aria-hidden=\"true\"><path d=\"M0 5h32M26 1l6 4-6 4\" fill=\"none\" stroke=\"#241430\" stroke-width=\"1.6\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/></svg>\n {{#unless compact}}<span class=\"state-machine__label\">approve</span>{{/unless}}\n </div>\n <div class=\"state-machine__node state-machine__node--approved\">\n <span class=\"state-machine__current\" aria-current=\"step\">Current</span>\n <span class=\"state-machine__name\">Approved</span>\n </div>\n <div class=\"state-machine__edge\">\n <svg class=\"state-machine__arrow\" viewBox=\"0 0 40 10\" aria-hidden=\"true\"><path d=\"M0 5h32M26 1l6 4-6 4\" fill=\"none\" stroke=\"#241430\" stroke-width=\"1.6\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/></svg>\n {{#unless compact}}<span class=\"state-machine__label\">publish</span>{{/unless}}\n </div>\n <div class=\"state-machine__node state-machine__node--published\">\n <span class=\"state-machine__current\" aria-current=\"step\">Current</span>\n <span class=\"state-machine__name\">Published</span>\n </div>\n </div>\n <p class=\"state-machine__caption\">Currently: <strong>{{current}}</strong></p>\n</div>\n<style>\n.state-machine { width: 640px; max-width: 100%; padding: 20px; background: #FAF7F2; color: #241430; font: 14px/1.4 system-ui, sans-serif; border-radius: 14px; box-shadow: 0 8px 24px rgba(36,20,48,.12); box-sizing: border-box; }\n.state-machine__title { margin: 0 0 16px; font-size: 16px; }\n.state-machine__diagram { display: flex; align-items: center; gap: var(--gap); flex-wrap: wrap; }\n.state-machine__node { position: relative; padding: 10px 14px; border-radius: 10px; background: #24143008; border: 2px solid transparent; text-align: center; min-width: 88px; }\n.state-machine__name { display: block; font-weight: 700; font-size: 13px; }\n.state-machine__current { display: none; position: absolute; top: -10px; left: 50%; transform: translateX(-50%); background: var(--accent); color: #FAF7F2; font-size: 10px; font-weight: 700; padding: 1px 8px; border-radius: 999px; text-transform: uppercase; letter-spacing: .04em; }\n.state-machine__edge { display: flex; flex-direction: column; align-items: center; gap: 2px; flex-shrink: 0; }\n.state-machine__arrow { width: 34px; height: 9px; }\n.state-machine__label { font-size: 11px; color: #241430b3; font-style: italic; }\n.state-machine[data-current=\"draft\"] .state-machine__node--draft,\n.state-machine[data-current=\"in review\"] .state-machine__node--in-review,\n.state-machine[data-current=\"approved\"] .state-machine__node--approved,\n.state-machine[data-current=\"published\"] .state-machine__node--published {\n border-color: var(--accent); background: color-mix(in srgb, var(--accent) 14%, #FAF7F2);\n}\n.state-machine[data-current=\"draft\"] .state-machine__node--draft .state-machine__current,\n.state-machine[data-current=\"in review\"] .state-machine__node--in-review .state-machine__current,\n.state-machine[data-current=\"approved\"] .state-machine__node--approved .state-machine__current,\n.state-machine[data-current=\"published\"] .state-machine__node--published .state-machine__current {\n display: inline-block;\n}\n</style>", "tailwind": "<div class=\"state-machine-tw w-[640px] max-w-full p-5 bg-[#FAF7F2] text-[#241430] font-sans text-sm rounded-2xl shadow-lg box-border\" data-current=\"{{current}}\" style=\"--accent:{{accent}};--gap:{{nodeGap}}px;\">\n <h3 class=\"m-0 mb-4 text-base\">{{title}}</h3>\n <div class=\"flex items-center flex-wrap\" style=\"gap:var(--gap);\">\n <div class=\"state-machine-tw__node state-machine-tw__node--draft relative text-center min-w-[88px] px-3.5 py-2.5 rounded-[10px] bg-[#24143008] border-2 border-transparent\">\n <span class=\"state-machine-tw__current absolute -top-2.5 left-1/2 -translate-x-1/2 text-[10px] font-bold px-2 py-px rounded-full uppercase tracking-wide\" style=\"background:{{accent}};color:#FAF7F2;\" aria-current=\"step\">Current</span>\n <span class=\"block font-bold text-[13px]\">Draft</span>\n </div>\n <div class=\"flex flex-col items-center gap-0.5 flex-shrink-0\">\n <svg class=\"w-[34px] h-[9px]\" viewBox=\"0 0 40 10\" aria-hidden=\"true\"><path d=\"M0 5h32M26 1l6 4-6 4\" fill=\"none\" stroke=\"#241430\" stroke-width=\"1.6\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/></svg>\n {{#unless compact}}<span class=\"text-[11px] italic text-[#241430]/70\">submit</span>{{/unless}}\n </div>\n <div class=\"state-machine-tw__node state-machine-tw__node--in-review relative text-center min-w-[88px] px-3.5 py-2.5 rounded-[10px] bg-[#24143008] border-2 border-transparent\">\n <span class=\"state-machine-tw__current absolute -top-2.5 left-1/2 -translate-x-1/2 text-[10px] font-bold px-2 py-px rounded-full uppercase tracking-wide\" style=\"background:{{accent}};color:#FAF7F2;\" aria-current=\"step\">Current</span>\n <span class=\"block font-bold text-[13px]\">In review</span>\n </div>\n <div class=\"flex flex-col items-center gap-0.5 flex-shrink-0\">\n <svg class=\"w-[34px] h-[9px]\" viewBox=\"0 0 40 10\" aria-hidden=\"true\"><path d=\"M0 5h32M26 1l6 4-6 4\" fill=\"none\" stroke=\"#241430\" stroke-width=\"1.6\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/></svg>\n {{#unless compact}}<span class=\"text-[11px] italic text-[#241430]/70\">approve</span>{{/unless}}\n </div>\n <div class=\"state-machine-tw__node state-machine-tw__node--approved relative text-center min-w-[88px] px-3.5 py-2.5 rounded-[10px] bg-[#24143008] border-2 border-transparent\">\n <span class=\"state-machine-tw__current absolute -top-2.5 left-1/2 -translate-x-1/2 text-[10px] font-bold px-2 py-px rounded-full uppercase tracking-wide\" style=\"background:{{accent}};color:#FAF7F2;\" aria-current=\"step\">Current</span>\n <span class=\"block font-bold text-[13px]\">Approved</span>\n </div>\n <div class=\"flex flex-col items-center gap-0.5 flex-shrink-0\">\n <svg class=\"w-[34px] h-[9px]\" viewBox=\"0 0 40 10\" aria-hidden=\"true\"><path d=\"M0 5h32M26 1l6 4-6 4\" fill=\"none\" stroke=\"#241430\" stroke-width=\"1.6\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/></svg>\n {{#unless compact}}<span class=\"text-[11px] italic text-[#241430]/70\">publish</span>{{/unless}}\n </div>\n <div class=\"state-machine-tw__node state-machine-tw__node--published relative text-center min-w-[88px] px-3.5 py-2.5 rounded-[10px] bg-[#24143008] border-2 border-transparent\">\n <span class=\"state-machine-tw__current absolute -top-2.5 left-1/2 -translate-x-1/2 text-[10px] font-bold px-2 py-px rounded-full uppercase tracking-wide\" style=\"background:{{accent}};color:#FAF7F2;\" aria-current=\"step\">Current</span>\n <span class=\"block font-bold text-[13px]\">Published</span>\n </div>\n </div>\n <p class=\"mt-4 mb-0 text-[13px]\">Currently: <strong>{{current}}</strong></p>\n</div>\n<style>\n.state-machine-tw__current { display: none; }\n.state-machine-tw[data-current=\"draft\"] .state-machine-tw__node--draft,\n.state-machine-tw[data-current=\"in review\"] .state-machine-tw__node--in-review,\n.state-machine-tw[data-current=\"approved\"] .state-machine-tw__node--approved,\n.state-machine-tw[data-current=\"published\"] .state-machine-tw__node--published {\n border-color: var(--accent); background: color-mix(in srgb, var(--accent) 14%, #FAF7F2);\n}\n.state-machine-tw[data-current=\"draft\"] .state-machine-tw__node--draft .state-machine-tw__current,\n.state-machine-tw[data-current=\"in review\"] .state-machine-tw__node--in-review .state-machine-tw__current,\n.state-machine-tw[data-current=\"approved\"] .state-machine-tw__node--approved .state-machine-tw__current,\n.state-machine-tw[data-current=\"published\"] .state-machine-tw__node--published .state-machine-tw__current {\n display: inline-block;\n}\n</style>", "react": "export function StateMachine({\n title = \"{{title}}\",\n current = \"{{current}}\",\n accent = \"{{accent}}\",\n compact = {{compact}},\n nodeGap = {{nodeGap}},\n}) {\n const nodes = [\n { key: \"draft\", name: \"Draft\" },\n { key: \"in review\", name: \"In review\" },\n { key: \"approved\", name: \"Approved\" },\n { key: \"published\", name: \"Published\" },\n ];\n const edges = [\"submit\", \"approve\", \"publish\"];\n return (\n <div style={{ width: 640, maxWidth: \"100%\", padding: 20, background: \"#FAF7F2\", color: \"#241430\",\n fontFamily: \"system-ui, sans-serif\", fontSize: 14, borderRadius: 14, boxShadow: \"0 8px 24px rgba(36,20,48,.12)\",\n boxSizing: \"border-box\" }}>\n <h3 style={{ margin: \"0 0 16px\", fontSize: 16 }}>{title}</h3>\n <div style={{ display: \"flex\", alignItems: \"center\", gap: nodeGap, flexWrap: \"wrap\" }}>\n {nodes.map((node, i) => (\n <span key={node.key} style={{ display: \"inline-flex\", alignItems: \"center\", gap: nodeGap }}>\n <span style={{ position: \"relative\", textAlign: \"center\", minWidth: 88, padding: \"10px 14px\",\n borderRadius: 10, display: \"inline-block\",\n background: current === node.key ? `color-mix(in srgb, ${accent} 14%, #FAF7F2)` : \"#24143008\",\n border: current === node.key ? `2px solid ${accent}` : \"2px solid transparent\" }}>\n {current === node.key && (\n <span aria-current=\"step\" style={{ position: \"absolute\", top: -10, left: \"50%\", transform: \"translateX(-50%)\",\n background: accent, color: \"#FAF7F2\", fontSize: 10, fontWeight: 700, padding: \"1px 8px\",\n borderRadius: 999, textTransform: \"uppercase\", letterSpacing: \"0.04em\" }}>Current</span>\n )}\n <span style={{ display: \"block\", fontWeight: 700, fontSize: 13 }}>{node.name}</span>\n </span>\n {i < edges.length && (\n <span style={{ display: \"inline-flex\", flexDirection: \"column\", alignItems: \"center\", gap: 2 }}>\n <svg width=\"34\" height=\"9\" viewBox=\"0 0 40 10\" aria-hidden=\"true\"><path d=\"M0 5h32M26 1l6 4-6 4\" fill=\"none\" stroke=\"#241430\" strokeWidth=\"1.6\" strokeLinecap=\"round\" strokeLinejoin=\"round\" /></svg>\n {!compact && <span style={{ fontSize: 11, fontStyle: \"italic\", color: \"#241430b3\" }}>{edges[i]}</span>}\n </span>\n )}\n </span>\n ))}\n </div>\n <p style={{ margin: \"16px 0 0\", fontSize: 13 }}>Currently: <strong>{current}</strong></p>\n </div>\n );\n}" }
Save
History