[{"data":1,"prerenderedAt":316},["ShallowReactive",2],{"navigation_docs":3,"-docs-architecture-layers":64,"-docs-architecture-layers-surround":311},[4,29,49],{"title":5,"path":6,"stem":7,"children":8,"page":28},"Getting Started","\u002Fdocs\u002Fgetting-started","docs\u002F1.getting-started",[9,14,19,24],{"title":10,"path":11,"stem":12,"icon":13},"Introduction","\u002Fdocs\u002Fgetting-started\u002Fintroduction","docs\u002F1.getting-started\u002F1.introduction","i-lucide-house",{"title":15,"path":16,"stem":17,"icon":18},"Installation","\u002Fdocs\u002Fgetting-started\u002Finstallation","docs\u002F1.getting-started\u002F2.installation","i-lucide-rocket",{"title":20,"path":21,"stem":22,"icon":23},"Configuration","\u002Fdocs\u002Fgetting-started\u002Fconfiguration","docs\u002F1.getting-started\u002F3.configuration","i-lucide-sliders-horizontal",{"title":25,"path":26,"stem":27,"icon":18},"Deployment","\u002Fdocs\u002Fgetting-started\u002Fdeployment","docs\u002F1.getting-started\u002F4.deployment",false,{"title":30,"path":31,"stem":32,"children":33,"page":28},"Architecture","\u002Fdocs\u002Farchitecture","docs\u002F2.architecture",[34,39,44],{"title":35,"path":36,"stem":37,"icon":38},"Layers","\u002Fdocs\u002Farchitecture\u002Flayers","docs\u002F2.architecture\u002F1.layers","i-lucide-layers",{"title":40,"path":41,"stem":42,"icon":43},"Multi-tenancy","\u002Fdocs\u002Farchitecture\u002Fmulti-tenancy","docs\u002F2.architecture\u002F2.multi-tenancy","i-lucide-building-2",{"title":45,"path":46,"stem":47,"icon":48},"Testing","\u002Fdocs\u002Farchitecture\u002Ftesting","docs\u002F2.architecture\u002F3.testing","i-lucide-flask-conical",{"title":50,"path":51,"stem":52,"children":53,"page":28},"Reference","\u002Fdocs\u002Freference","docs\u002F3.reference",[54,59],{"title":55,"path":56,"stem":57,"icon":58},"Commands","\u002Fdocs\u002Freference\u002Fcommands","docs\u002F3.reference\u002F1.commands","i-lucide-terminal",{"title":60,"path":61,"stem":62,"icon":63},"Decisions","\u002Fdocs\u002Freference\u002Fdecisions","docs\u002F3.reference\u002F2.decisions","i-lucide-git-branch",{"id":65,"title":35,"body":66,"description":304,"extension":305,"links":306,"meta":307,"navigation":308,"path":36,"seo":309,"stem":37,"__hash__":310},"docs\u002Fdocs\u002F2.architecture\u002F1.layers.md",{"type":67,"value":68,"toc":298},"minimark",[69,91,101,120,125,294],[70,71,72,73,77,78,82,83,86,87,90],"p",{},"The root project is the ",[74,75,76],"strong",{},"shell",": ",[79,80,81],"code",{},"app.vue",", layouts, auth pages, stores, base\ncomponents, shared utils and the core ",[79,84,85],{},"server\u002F",". Every feature is a ",[74,88,89],{},"Nuxt Layer","\nthe root extends.",[92,93,98],"pre",{"className":94,"code":96,"language":97},[95],"language-text","layers\u002F\n├── ui         design system — theme tokens, AA overrides, fonts, brand chrome\n├── marketing  landing, pricing, privacy, terms\n├── notes      the reference CRUD\n├── admin      user list, invite, set-role, ban, delete, impersonate\n├── account    self-serve account deletion\n├── billing    Polar checkout, portal, webhook\n├── email      templates + \u002Fdev\u002Femails preview\n├── feedback   self-hosted in-app widget\n├── tour       driver.js first-run product tour\n└── analytics  PostHog pageviews + useFeatureFlag()\n","text",[79,99,96],{"__ignoreMap":100},"",[70,102,103,104,107,108,111,112,115,116,119],{},"Adding a feature is a new ",[79,105,106],{},"layers\u002F\u003Cname>\u002F"," with its own ",[79,109,110],{},"nuxt.config.ts",", listed in\nthe root ",[79,113,114],{},"extends"," — or just ",[79,117,118],{},"pnpm gen:layer \u003Cname>",".",[121,122,124],"h2",{"id":123},"the-one-rule-that-bites","The one rule that bites",[126,127,128,142,161,204,208,214,217,221,249,252],"caution",{},[70,129,130,133,134,137,138,141],{},[79,131,132],{},"~"," and ",[79,135,136],{},"@"," always resolve to the ",[74,139,140],{},"root app",", never the current layer.\n::",[70,143,144,145,148,149,152,153,156,157,160],{},"From inside a layer, don't ",[79,146,147],{},"import from '~\u002F…'",". Rely on auto-imports instead —\ncomponents, composables, utils, stores and Nitro ",[79,150,151],{},"server\u002Futils"," all auto-import\nacross layers. Shared types live in ",[79,154,155],{},"shared\u002Ftypes\u002F"," and are imported via ",[79,158,159],{},"#shared",":",[92,162,166],{"className":163,"code":164,"language":165,"meta":100,"style":100},"language-ts shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","import type { Note } from '#shared\u002Ftypes'\n","ts",[79,167,168],{"__ignoreMap":100},[169,170,173,177,180,184,188,191,194,197,201],"span",{"class":171,"line":172},"line",1,[169,174,176],{"class":175},"s7zQu","import",[169,178,179],{"class":175}," type",[169,181,183],{"class":182},"sMK4o"," {",[169,185,187],{"class":186},"sTEyZ"," Note",[169,189,190],{"class":182}," }",[169,192,193],{"class":175}," from",[169,195,196],{"class":182}," '",[169,198,200],{"class":199},"sfazB","#shared\u002Ftypes",[169,202,203],{"class":182},"'\n",[121,205,207],{"id":206},"the-design-system-layer","The design-system layer",[70,209,210,213],{},[79,211,212],{},"layers\u002Fui"," is the single source of truth for theme tokens, AA contrast overrides,\nNuxt UI defaults, fonts and brand chrome. Every app extends it, so a future\nmarketing\u002Fapp\u002Fdocs split can't drift into three slightly different indigos.",[70,215,216],{},"Don't put theme values anywhere else.",[121,218,220],{"id":219},"i18n-stays-central","i18n stays central",[70,222,223,224,227,228,231,232,235,236,239,240,243,244,248],{},"Locales live in the root ",[79,225,226],{},"i18n\u002Flocales\u002F*.json"," even though features are layered —\n",[79,229,230],{},"en"," is the default, ",[79,233,234],{},"sr"," the alternate. Add every key to ",[74,237,238],{},"both"," files and run\n",[79,241,242],{},"pnpm lint:i18n",", which checks key parity ",[245,246,247],"em",{},"and"," that every key is actually used.",[70,250,251],{},"Build dynamic keys with template literals so the usage checker can resolve them:",[92,253,255],{"className":163,"code":254,"language":165,"meta":100,"style":100},"const label = t(`common.role.${role}`)\n",[79,256,257],{"__ignoreMap":100},[169,258,259,263,266,269,273,276,279,282,285,288,291],{"class":171,"line":172},[169,260,262],{"class":261},"spNyl","const",[169,264,265],{"class":186}," label ",[169,267,268],{"class":182},"=",[169,270,272],{"class":271},"s2Zo4"," t",[169,274,275],{"class":186},"(",[169,277,278],{"class":182},"`",[169,280,281],{"class":199},"common.role.",[169,283,284],{"class":182},"${",[169,286,287],{"class":186},"role",[169,289,290],{"class":182},"}`",[169,292,293],{"class":186},")\n",[295,296,297],"style",{},"html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}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);}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}",{"title":100,"searchDepth":299,"depth":299,"links":300},2,[301,302,303],{"id":123,"depth":299,"text":124},{"id":206,"depth":299,"text":207},{"id":219,"depth":299,"text":220},"How features are organised, and why ~ never means what you think.","md",null,{},{"icon":38},{"title":35,"description":304},"Naqi4aftVHRyJ5o1Dgl6vRLh6BfAtWMtdyY35taQgvQ",[312,314],{"title":25,"path":26,"stem":27,"description":313,"icon":18,"children":-1},"Getting it onto a real domain, with a hosted database and the migrations applied.",{"title":40,"path":41,"stem":42,"description":315,"icon":43,"children":-1},"Single database, shared schema, and a policy that does the work.",1785951589234]