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