[{"data":1,"prerenderedAt":407},["ShallowReactive",2],{"docs-sections":3,"docs-page-\u002Fdocs\u002Ftheming":123},[4,9,13,17,22,26,30,34,38,42,46,50,55,59,63,67,72,76,80,85,89,93,97,102,106,110,114,119],{"path":5,"title":6,"description":7,"group":8},"\u002Fdocs","Introduction","What tunables is, the problem it solves, and the principles it holds to.","Get started",{"path":10,"title":11,"description":12,"group":8},"\u002Fdocs\u002Fquickstart","Quickstart","Take one group of settings from constants to a screen with a history, in a Nuxt app on Postgres.",{"path":14,"title":15,"description":16,"group":8},"\u002Fdocs\u002Fpatterns","What it is for","The kinds of value tunables keeps — live limits, plans, per-workspace and per-person values, model choices, values that bound each other, costly switches — and the feature that does each.",{"path":18,"title":19,"description":20,"group":21},"\u002Fdocs\u002Fsettings-and-groups","Settings and groups","Declare each setting once, in a group edited beside what it affects, and read it with types.","Concepts",{"path":23,"title":24,"description":25,"group":21},"\u002Fdocs\u002Fkinds-and-units","Kinds and units","The eight kinds of setting, the built-in units, your own units, and how values are checked and read.",{"path":27,"title":28,"description":29,"group":21},"\u002Fdocs\u002Fchecks-and-guards","Checks and guards","Rules across settings, guards for what your app keeps, within for presets, confirmations for costly changes, and notes.",{"path":31,"title":32,"description":33,"group":21},"\u002Fdocs\u002Fhistory-and-restore","History and restore","Every change is a revision with who, when, why and each value before and after; restoring one runs the same checks as any save.",{"path":35,"title":36,"description":37,"group":21},"\u002Fdocs\u002Fpresets","Presets","Named, reusable configurations with one default per slot, assigned by reference and bounded by the settings.",{"path":39,"title":40,"description":41,"group":21},"\u002Fdocs\u002Fscopes","Scopes","Values per workspace and per person, through layers — the system, a plan, the workspace, the person — each bounded by what it inherits.",{"path":43,"title":44,"description":45,"group":21},"\u002Fdocs\u002Fcatalogs","Catalogs","Lists of allowed choices, such as permitted models, whose entries are retired rather than deleted and cannot be retired while in use.",{"path":47,"title":48,"description":49,"group":21},"\u002Fdocs\u002Fdeployment-values","Deployment values","Values a deployment sets in its environment and nobody tunes while the app runs, declared once and listed in .env.example.",{"path":51,"title":52,"description":53,"group":54},"\u002Fdocs\u002Fstorage","Storage","Postgres through your own client, the four tables and how to create them, joining your transactions, and the storage contract behind it.","Integrate",{"path":56,"title":57,"description":58,"group":54},"\u002Fdocs\u002Fserver","Server and access","Mount tunables' API as a fetch handler in Nitro, Hono, Bun or Deno, as Express middleware or as an oRPC router, with access decided by your app.",{"path":60,"title":61,"description":62,"group":54},"\u002Fdocs\u002Faccess","Who can do what","End users bound by values they never see, staff who tune groups, owners and members who change only their own scope, and the terminal.",{"path":64,"title":65,"description":66,"group":54},"\u002Fdocs\u002Ftesting","Testing","Your app's tunables in memory for its own tests, with values set where a test needs them, and how to test rules, guards and confirmations.",{"path":68,"title":69,"description":70,"group":71},"\u002Fdocs\u002Fnuxt-layer","The Nuxt layer","Ready screens for a Nuxt UI app — a group's form, All settings, history, presets, assignments and catalogs — placed beside what they affect.","Screens",{"path":73,"title":74,"description":75,"group":71},"\u002Fdocs\u002Fvue-composables","Vue composables","Headless composables for building tunables' screens in any Vue app, on any design system.",{"path":77,"title":78,"description":79,"group":71},"\u002Fdocs\u002Ftheming","Theming","The screens bring no colors, fonts or CSS of their own; they take your Nuxt UI theme.",{"path":81,"title":82,"description":83,"group":84},"\u002Fdocs\u002Fcli","The CLI","The tunables command, tunables.config.ts, and which commands need a database.","CLI",{"path":86,"title":87,"description":88,"group":84},"\u002Fdocs\u002Fcli-check","Check and generate","The commands that need no database — checking definitions, writing .env.example, and keeping explanations and defaults in step with a pdd-kit Product Model.",{"path":90,"title":91,"description":92,"group":84},"\u002Fdocs\u002Fcli-values","Read and change values","The commands that read and change stored values, through the same checks and history as the screens.",{"path":94,"title":95,"description":96,"group":84},"\u002Fdocs\u002Fcli-skills","Install skills","Install tunables' agent skills for Claude Code, Codex CLI, Cursor, Gemini CLI and GitHub Copilot, and keep them current.",{"path":98,"title":99,"description":100,"group":101},"\u002Fdocs\u002Fskills","Agent skills","Three skills that let a coding agent adopt tunables, define settings the right way and verify that every value has one home.","Skills",{"path":103,"title":104,"description":105,"group":101},"\u002Fdocs\u002Fskill-adopt","tunables-adopt","Bring tunables into an app that has none yet, from an inventory of every value to a wired, synced app with the old homes removed.",{"path":107,"title":108,"description":109,"group":101},"\u002Fdocs\u002Fskill-define","tunables-define","Add, change, rename or remove one value in an app that uses tunables, with its home, its checks, its reads and what happens to stored values.",{"path":111,"title":112,"description":113,"group":101},"\u002Fdocs\u002Fskill-verify","tunables-verify","Audit an app's tunables against their rules, report every finding with where it is and the rule it breaks, and fix only the findings you choose.",{"path":115,"title":116,"description":117,"group":118},"\u002Fdocs\u002Fapi","API reference","Every export of the tunables package, by the subpath it is imported from.","Reference",{"path":120,"title":121,"description":122,"group":118},"\u002Fdocs\u002Ftasklane-example","Tasklane, the example app","A todo app with workspaces and people as tunables — two groups, plans as a layer, a catalog of models, scopes, a rule, a confirmation, and the playground that runs it.",{"id":124,"title":78,"body":125,"description":79,"extension":401,"group":71,"meta":402,"navigation":332,"order":403,"path":77,"seo":404,"stem":405,"__hash__":406},"docs\u002Fdocs\u002Ftheming.md",{"type":126,"value":127,"toc":395},"minimark",[128,132,152,157,160,259,274,278,285,346,349,353,360,378,382,391],[129,130,78],"h1",{"id":131},"theming",[133,134,135,136,140,141,144,145,140,148,151],"p",{},"The screens are part of your app, so they look like it. The layer brings no\ncolors, fonts or stylesheet: every component is built from Nuxt UI components\nand Nuxt UI's semantic utilities (",[137,138,139],"code",{},"text-highlighted",", ",[137,142,143],{},"border-default",",\n",[137,146,147],{},"bg-elevated",[137,149,150],{},"text-primary","), so it takes whatever your theme makes of them,\nin light and dark.",[153,154,156],"h2",{"id":155},"your-colors","Your colors",[133,158,159],{},"Set them where you set them for the rest of your app:",[161,162,168],"pre",{"className":163,"code":164,"filename":165,"language":166,"meta":167,"style":167},"language-ts shiki shiki-themes tunables-panel-light tunables-panel-light tunables-panel-dark","export default defineAppConfig({\n  ui: {\n    colors: { primary: 'teal', neutral: 'slate' }\n  }\n})\n","app\u002Fapp.config.ts","ts","",[137,169,170,190,203,247,253],{"__ignoreMap":167},[171,172,175,179,182,186],"span",{"class":173,"line":174},"line",1,[171,176,178],{"class":177},"sZ4mS","export",[171,180,181],{"class":177}," default",[171,183,185],{"class":184},"sOThN"," defineAppConfig",[171,187,189],{"class":188},"sp9ci","({\n",[171,191,193,197,200],{"class":173,"line":192},2,[171,194,196],{"class":195},"sVz71","  ui",[171,198,199],{"class":188},":",[171,201,202],{"class":188}," {\n",[171,204,206,209,211,214,217,219,222,226,229,232,235,237,239,242,244],{"class":173,"line":205},3,[171,207,208],{"class":195},"    colors",[171,210,199],{"class":188},[171,212,213],{"class":188}," {",[171,215,216],{"class":195}," primary",[171,218,199],{"class":188},[171,220,221],{"class":188}," '",[171,223,225],{"class":224},"sjCzm","teal",[171,227,228],{"class":188},"'",[171,230,231],{"class":188},",",[171,233,234],{"class":195}," neutral",[171,236,199],{"class":188},[171,238,221],{"class":188},[171,240,241],{"class":224},"slate",[171,243,228],{"class":188},[171,245,246],{"class":188}," }\n",[171,248,250],{"class":173,"line":249},4,[171,251,252],{"class":188},"  }\n",[171,254,256],{"class":173,"line":255},5,[171,257,258],{"class":188},"})\n",[133,260,261,262,265,266,269,270,273],{},"Primary marks unsaved changes and the Save button; warning marks a change\nthat costs something and the button that confirms it; error marks problems.\nYour component overrides under ",[137,263,264],{},"ui"," apply too: a rounder ",[137,267,268],{},"button"," or a denser\n",[137,271,272],{},"input"," changes the screens with the rest of the app.",[153,275,277],{"id":276},"let-tailwind-see-the-components","Let Tailwind see the components",[133,279,280,281,284],{},"Tailwind generates only the classes it finds in the files it scans. The\nlayer's components live in ",[137,282,283],{},"node_modules",", so name them as a source in your\nmain stylesheet:",[161,286,291],{"className":287,"code":288,"filename":289,"language":290,"meta":167,"style":167},"language-css shiki shiki-themes tunables-panel-light tunables-panel-light tunables-panel-dark","@import \"tailwindcss\";\n@import \"@nuxt\u002Fui\";\n\n@source \"..\u002F..\u002F..\u002Fnode_modules\u002Ftunables\u002Flayers\u002Fnuxt\u002Ftunables\u002Fapp\";\n","app\u002Fassets\u002Fcss\u002Fmain.css","css",[137,292,293,313,328,334],{"__ignoreMap":167},[171,294,295,298,301,304,307,310],{"class":173,"line":174},[171,296,297],{"class":188},"@",[171,299,300],{"class":177},"import",[171,302,303],{"class":188}," \"",[171,305,306],{"class":224},"tailwindcss",[171,308,309],{"class":188},"\"",[171,311,312],{"class":188},";\n",[171,314,315,317,319,321,324,326],{"class":173,"line":192},[171,316,297],{"class":188},[171,318,300],{"class":177},[171,320,303],{"class":188},[171,322,323],{"class":224},"@nuxt\u002Fui",[171,325,309],{"class":188},[171,327,312],{"class":188},[171,329,330],{"class":173,"line":205},[171,331,333],{"emptyLinePlaceholder":332},true,"\n",[171,335,336,338,341,344],{"class":173,"line":249},[171,337,297],{"class":188},[171,339,340],{"class":177},"source",[171,342,343],{"class":195}," \"..\u002F..\u002F..\u002Fnode_modules\u002Ftunables\u002Flayers\u002Fnuxt\u002Ftunables\u002Fapp\"",[171,345,312],{"class":188},[133,347,348],{},"The path is relative to the stylesheet. Without it, the screens render, but\nspacing and layout classes your own app never uses are missing.",[153,350,352],{"id":351},"icons","Icons",[133,354,355,356,359],{},"Effects take an Iconify name, such as ",[137,357,358],{},"i-lucide-coins",", and the screens use a\nfew Lucide icons of their own. Install the collection, so Nuxt Icon serves\nthem from your app:",[161,361,365],{"className":362,"code":363,"language":364,"meta":167,"style":167},"language-bash shiki shiki-themes tunables-panel-light tunables-panel-light tunables-panel-dark","npm install @iconify-json\u002Flucide\n","bash",[137,366,367],{"__ignoreMap":167},[171,368,369,372,375],{"class":173,"line":174},[171,370,371],{"class":184},"npm",[171,373,374],{"class":224}," install",[171,376,377],{"class":224}," @iconify-json\u002Flucide\n",[153,379,381],{"id":380},"words","Words",[133,383,384,385,390],{},"The screens use your words: group, field and section names, explanations,\neffects and problems all come from your definitions and your checks. Write\nthem in the product's language, the way the person changing a setting thinks\nof it. With a ",[386,387,389],"a",{"href":388},".\u002Fcli-check#explain","Product Model",", the explanations are the\nmodel's own.",[392,393,394],"style",{},"html pre.shiki code .sZ4mS, html code.shiki .sZ4mS{--shiki-light:#115E59;--shiki-default:#115E59;--shiki-dark:#5EEAD4}html pre.shiki code .sOThN, html code.shiki .sOThN{--shiki-light:#2A3439;--shiki-light-font-weight:bold;--shiki-default:#2A3439;--shiki-default-font-weight:bold;--shiki-dark:#F6F8F8;--shiki-dark-font-weight:bold}html pre.shiki code .sp9ci, html code.shiki .sp9ci{--shiki-light:#4F5D63;--shiki-default:#4F5D63;--shiki-dark:#A3AFB4}html pre.shiki code .sVz71, html code.shiki .sVz71{--shiki-light:#182025;--shiki-default:#182025;--shiki-dark:#E6EBEC}html pre.shiki code .sjCzm, html code.shiki .sjCzm{--shiki-light:#92400E;--shiki-default:#92400E;--shiki-dark:#FCD34D}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":167,"searchDepth":192,"depth":192,"links":396},[397,398,399,400],{"id":155,"depth":192,"text":156},{"id":276,"depth":192,"text":277},{"id":351,"depth":192,"text":352},{"id":380,"depth":192,"text":381},"md",{},18,{"title":78,"description":79},"docs\u002Ftheming","GzQz6I8xe9FVeu8D3t-hgHVQkwM-FbzrSewKaiQ-mCg",1792845119891]