@rashidee/co2 1.2.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (190) hide show
  1. package/dist/index.js +25449 -0
  2. package/drizzle/0000_remarkable_charles_xavier.sql +210 -0
  3. package/drizzle/0001_wise_wolf_cub.sql +13 -0
  4. package/drizzle/0002_secret_spyke.sql +16 -0
  5. package/drizzle/0003_workspace_config_singleton.sql +16 -0
  6. package/drizzle/0004_terminal_claim_v120.sql +11 -0
  7. package/drizzle/0005_brainstorm_v124.sql +10 -0
  8. package/drizzle/0006_project_v140.sql +6 -0
  9. package/drizzle/0007_prd_wizard_v124.sql +14 -0
  10. package/drizzle/0008_workflow_artifact_v126.sql +24 -0
  11. package/drizzle/0009_project_import_v150.sql +7 -0
  12. package/drizzle/0010_quality_v151.sql +3 -0
  13. package/drizzle/meta/0000_snapshot.json +1335 -0
  14. package/drizzle/meta/0001_snapshot.json +1416 -0
  15. package/drizzle/meta/0002_snapshot.json +1510 -0
  16. package/drizzle/meta/_journal.json +83 -0
  17. package/package.json +41 -0
  18. package/plugin/.claude-plugin/marketplace.json +36 -0
  19. package/plugin/.claude-plugin/plugin.json +22 -0
  20. package/plugin/README.md +625 -0
  21. package/plugin/SKILLS.md +350 -0
  22. package/plugin/co2.svg +5 -0
  23. package/plugin/commands/brainstorm-loop.md +129 -0
  24. package/plugin/skills/conductor-defect/SKILL.md +898 -0
  25. package/plugin/skills/conductor-feature-develop/SKILL.md +1383 -0
  26. package/plugin/skills/conductor-feature-develop/references/implementation-domain-template.md +131 -0
  27. package/plugin/skills/conductor-feature-develop/references/implementation-master-template.md +89 -0
  28. package/plugin/skills/conductor-feature-develop/references/playwright-setup.md +224 -0
  29. package/plugin/skills/conductor-feature-prepare/SKILL.md +604 -0
  30. package/plugin/skills/depgen-k8s/SKILL.md +468 -0
  31. package/plugin/skills/depgen-k8s/references/dockerfile-laravel.md +197 -0
  32. package/plugin/skills/depgen-k8s/references/dockerfile-nodejs.md +163 -0
  33. package/plugin/skills/depgen-k8s/references/dockerfile-spring-boot.md +174 -0
  34. package/plugin/skills/depgen-k8s/references/k8s-patterns.md +869 -0
  35. package/plugin/skills/mockgen-shadcn/SKILL.md +1067 -0
  36. package/plugin/skills/mockgen-shadcn/references/admin-layout-template.md +1467 -0
  37. package/plugin/skills/mockgen-shadcn/references/mockup-hub-template.md +498 -0
  38. package/plugin/skills/mockgen-shadcn/references/mockup-index-template.md +216 -0
  39. package/plugin/skills/mockgen-tailwind/SKILL.md +904 -0
  40. package/plugin/skills/mockgen-tailwind/references/admin-layout-template.md +720 -0
  41. package/plugin/skills/mockgen-tailwind/references/mockup-hub-template.md +498 -0
  42. package/plugin/skills/mockgen-tailwind/references/mockup-index-template.md +190 -0
  43. package/plugin/skills/modelgen-nosql/SKILL.md +617 -0
  44. package/plugin/skills/modelgen-nosql/references/model-extraction-methodology.md +497 -0
  45. package/plugin/skills/modelgen-nosql/references/nosql-design-guide.md +258 -0
  46. package/plugin/skills/modelgen-relational/SKILL.md +707 -0
  47. package/plugin/skills/modelgen-relational/references/model-extraction-methodology.md +499 -0
  48. package/plugin/skills/specgen-flutter-riverpod/SKILL.md +898 -0
  49. package/plugin/skills/specgen-flutter-riverpod/references/component-patterns.md +971 -0
  50. package/plugin/skills/specgen-flutter-riverpod/references/notification-patterns.md +457 -0
  51. package/plugin/skills/specgen-flutter-riverpod/references/routing-patterns.md +362 -0
  52. package/plugin/skills/specgen-flutter-riverpod/references/security-patterns.md +709 -0
  53. package/plugin/skills/specgen-flutter-riverpod/references/spec-template.md +1566 -0
  54. package/plugin/skills/specgen-flutter-riverpod/references/state-patterns.md +518 -0
  55. package/plugin/skills/specgen-flutter-riverpod/references/storage-patterns.md +370 -0
  56. package/plugin/skills/specgen-laravel-eloquent-bladehtmx/SKILL.md +984 -0
  57. package/plugin/skills/specgen-laravel-eloquent-bladehtmx/references/batch-patterns.md +359 -0
  58. package/plugin/skills/specgen-laravel-eloquent-bladehtmx/references/messaging-patterns.md +468 -0
  59. package/plugin/skills/specgen-laravel-eloquent-bladehtmx/references/modulith-patterns.md +461 -0
  60. package/plugin/skills/specgen-laravel-eloquent-bladehtmx/references/reporting-patterns.md +803 -0
  61. package/plugin/skills/specgen-laravel-eloquent-bladehtmx/references/security-patterns.md +501 -0
  62. package/plugin/skills/specgen-laravel-eloquent-bladehtmx/references/spec-template.md +1268 -0
  63. package/plugin/skills/specgen-laravel-eloquent-bladehtmx/references/web-patterns.md +301 -0
  64. package/plugin/skills/specgen-node-cli-web/SKILL.md +561 -0
  65. package/plugin/skills/specgen-node-cli-web/references/auth-patterns.md +293 -0
  66. package/plugin/skills/specgen-node-cli-web/references/cli-packaging-patterns.md +250 -0
  67. package/plugin/skills/specgen-node-cli-web/references/server-patterns.md +247 -0
  68. package/plugin/skills/specgen-node-cli-web/references/spec-template.md +401 -0
  69. package/plugin/skills/specgen-node-cli-web/references/testing-patterns.md +174 -0
  70. package/plugin/skills/specgen-react-mui/SKILL.md +789 -0
  71. package/plugin/skills/specgen-react-mui/references/component-patterns.md +1077 -0
  72. package/plugin/skills/specgen-react-mui/references/reporting-patterns.md +1998 -0
  73. package/plugin/skills/specgen-react-mui/references/routing-patterns.md +461 -0
  74. package/plugin/skills/specgen-react-mui/references/security-patterns.md +599 -0
  75. package/plugin/skills/specgen-react-mui/references/spec-template.md +1030 -0
  76. package/plugin/skills/specgen-react-mui/references/state-patterns.md +327 -0
  77. package/plugin/skills/specgen-react-tailwind/SKILL.md +859 -0
  78. package/plugin/skills/specgen-react-tailwind/references/component-patterns.md +1212 -0
  79. package/plugin/skills/specgen-react-tailwind/references/reporting-patterns.md +1529 -0
  80. package/plugin/skills/specgen-react-tailwind/references/routing-patterns.md +669 -0
  81. package/plugin/skills/specgen-react-tailwind/references/security-patterns.md +656 -0
  82. package/plugin/skills/specgen-react-tailwind/references/spec-template.md +1237 -0
  83. package/plugin/skills/specgen-react-tailwind/references/state-patterns.md +327 -0
  84. package/plugin/skills/specgen-sdk-java/SKILL.md +933 -0
  85. package/plugin/skills/specgen-sdk-java/references/api-extraction-patterns.md +247 -0
  86. package/plugin/skills/specgen-sdk-java/references/http-client-patterns.md +495 -0
  87. package/plugin/skills/specgen-sdk-java/references/packaging-patterns.md +366 -0
  88. package/plugin/skills/specgen-sdk-java/references/spec-template.md +956 -0
  89. package/plugin/skills/specgen-spring-jpa-jtehtmx/SKILL.md +1284 -0
  90. package/plugin/skills/specgen-spring-jpa-jtehtmx/references/batch-patterns.md +1224 -0
  91. package/plugin/skills/specgen-spring-jpa-jtehtmx/references/jasper-patterns.md +1638 -0
  92. package/plugin/skills/specgen-spring-jpa-jtehtmx/references/messaging-patterns.md +512 -0
  93. package/plugin/skills/specgen-spring-jpa-jtehtmx/references/modulith-patterns.md +354 -0
  94. package/plugin/skills/specgen-spring-jpa-jtehtmx/references/security-patterns.md +551 -0
  95. package/plugin/skills/specgen-spring-jpa-jtehtmx/references/spec-template.md +2502 -0
  96. package/plugin/skills/specgen-spring-jpa-jtehtmx/references/web-patterns.md +503 -0
  97. package/plugin/skills/specgen-spring-jpa-restapi/SKILL.md +990 -0
  98. package/plugin/skills/specgen-spring-jpa-restapi/references/batch-patterns.md +1224 -0
  99. package/plugin/skills/specgen-spring-jpa-restapi/references/messaging-patterns.md +512 -0
  100. package/plugin/skills/specgen-spring-jpa-restapi/references/modulith-patterns.md +279 -0
  101. package/plugin/skills/specgen-spring-jpa-restapi/references/reporting-patterns.md +1536 -0
  102. package/plugin/skills/specgen-spring-jpa-restapi/references/restapi-patterns.md +722 -0
  103. package/plugin/skills/specgen-spring-jpa-restapi/references/security-patterns.md +351 -0
  104. package/plugin/skills/specgen-spring-jpa-restapi/references/spec-template.md +553 -0
  105. package/plugin/skills/specgen-ts-cli/SKILL.md +861 -0
  106. package/plugin/skills/specgen-ts-cli/references/async-patterns.md +1101 -0
  107. package/plugin/skills/specgen-ts-cli/references/command-patterns.md +383 -0
  108. package/plugin/skills/specgen-ts-cli/references/config-patterns.md +430 -0
  109. package/plugin/skills/specgen-ts-cli/references/database-patterns.md +750 -0
  110. package/plugin/skills/specgen-ts-cli/references/packaging-patterns.md +426 -0
  111. package/plugin/skills/specgen-ts-cli/references/spec-template.md +1391 -0
  112. package/plugin/skills/specgen-ts-cli/references/testing-patterns.md +909 -0
  113. package/plugin/skills/testgen-functional/SKILL.md +1381 -0
  114. package/plugin/skills/tracegen-matrix/SKILL.md +243 -0
  115. package/plugin/skills/util-preparek8senv/SKILL.md +422 -0
  116. package/plugin/skills/util-projectinit/SKILL.md +307 -0
  117. package/plugin/skills/util-projectinit/reference/brainstorm-loop.md +129 -0
  118. package/plugin/skills/util-projectinit/reference/brainstorm-protocol.md +144 -0
  119. package/plugin/skills/util-projectinit/reference/brainstorm-setup-guide.md +153 -0
  120. package/plugin/skills/util-projectinit/reference/brainstorm-template-appdev.md +87 -0
  121. package/plugin/skills/util-projectinit/reference/co2-context-generation-guide.md +761 -0
  122. package/plugin/skills/util-projectsync/SKILL.md +501 -0
  123. package/plugin/skills/util-updprd/SKILL.md +324 -0
  124. package/plugin/skills/util-usanalyzer/SKILL.md +307 -0
  125. package/plugin/skills/util-ustagger/SKILL.md +154 -0
  126. package/static/assets/abnfDiagram-VRR7QNED-DvPuwyCV.js +1 -0
  127. package/static/assets/arc-DRKw7sjs.js +1 -0
  128. package/static/assets/architectureDiagram-ZJ3FMSHR-CaNcxUbC.js +36 -0
  129. package/static/assets/blockDiagram-677ZJIJ3-Demd4AOK.js +132 -0
  130. package/static/assets/c4Diagram-LMCZKHZV-PVKUfc2x.js +10 -0
  131. package/static/assets/channel-C2nVbuwv.js +1 -0
  132. package/static/assets/chunk-2Q5K7J3B-CExf8ww_.js +1 -0
  133. package/static/assets/chunk-32BRIVSS-CFPaoONF.js +1 -0
  134. package/static/assets/chunk-5VM5RSS4-DE2esM67.js +15 -0
  135. package/static/assets/chunk-EX3LRPZG-SON_YdDG.js +231 -0
  136. package/static/assets/chunk-JWPE2WC7-Qxah8LNr.js +1 -0
  137. package/static/assets/chunk-MOJQB5TN-BcWGQV3j.js +88 -0
  138. package/static/assets/chunk-RYQCIY6F-DkDj-OR5.js +1 -0
  139. package/static/assets/chunk-V7JOEXUC-BlI1Mrah.js +206 -0
  140. package/static/assets/chunk-VR4S4FIN-Agf__duj.js +1 -0
  141. package/static/assets/chunk-XXDRQBXY-B5CfAh85.js +1 -0
  142. package/static/assets/classDiagram-OUVF2IWQ-DXJIljsR.js +1 -0
  143. package/static/assets/classDiagram-v2-EOCWNBFH-DXJIljsR.js +1 -0
  144. package/static/assets/cose-bilkent-JH36ORCC-DO0cQkcy.js +1 -0
  145. package/static/assets/cynefin-VYW2F7L2-CfOnsnXc.js +178 -0
  146. package/static/assets/cynefinDiagram-TSTJHNR4-BJ8tULAv.js +62 -0
  147. package/static/assets/cytoscape.esm-DTSO7Bv0.js +331 -0
  148. package/static/assets/dagre-VKFMJZFB-DfjwzHqP.js +4 -0
  149. package/static/assets/defaultLocale-DX6XiGOO.js +1 -0
  150. package/static/assets/diagram-FQU43EPY-CndSOIIM.js +3 -0
  151. package/static/assets/diagram-G47NLZAW-Czf5hN0Z.js +24 -0
  152. package/static/assets/diagram-NH7WQ7WH-COAyojGh.js +24 -0
  153. package/static/assets/diagram-OA4YK3LP-H8PoiN2m.js +30 -0
  154. package/static/assets/diagram-WEI45ONY-DzcaVzjp.js +41 -0
  155. package/static/assets/ebnfDiagram-CCIWWBDH-DPazGYLJ.js +1 -0
  156. package/static/assets/erDiagram-Q63AITRT-Bo-fubgF.js +85 -0
  157. package/static/assets/flowDiagram-23GEKE2U-CSvxUDwh.js +156 -0
  158. package/static/assets/ganttDiagram-NO4QXBWP-DnBveWX6.js +292 -0
  159. package/static/assets/gitGraphDiagram-IHSO6WYX-B3rVL8c-.js +106 -0
  160. package/static/assets/graph-C9eacEi8.js +1 -0
  161. package/static/assets/index-LZSQkLE2.css +41 -0
  162. package/static/assets/index-y7XDSgMG.js +495 -0
  163. package/static/assets/infoDiagram-FWYZ7A6U-CvWV9yy2.js +2 -0
  164. package/static/assets/init-Gi6I4Gst.js +1 -0
  165. package/static/assets/ishikawaDiagram-FXEZZL3T-DF28a1N6.js +70 -0
  166. package/static/assets/journeyDiagram-5HDEW3XC-CB6CugYk.js +139 -0
  167. package/static/assets/kanban-definition-HUTT4EX6-ResSLF6d.js +89 -0
  168. package/static/assets/katex-C5jXJg4s.js +257 -0
  169. package/static/assets/layout-DEXfKzaS.js +1 -0
  170. package/static/assets/linear-CLO--ryk.js +1 -0
  171. package/static/assets/map-Czzmt4hB.js +1 -0
  172. package/static/assets/mindmap-definition-LN4V7U3C-BvW3EbxX.js +96 -0
  173. package/static/assets/ordinal-Cboi1Yqb.js +1 -0
  174. package/static/assets/pegDiagram-2B236MQR-BHCgqrrM.js +1 -0
  175. package/static/assets/pieDiagram-ENE6RG2P-DVmCQ5FA.js +39 -0
  176. package/static/assets/quadrantDiagram-ABIIQ3AL-B3e5KR52.js +7 -0
  177. package/static/assets/railroadDiagram-RFXS5EU6-Csv5ysLc.js +1 -0
  178. package/static/assets/requirementDiagram-TGXJPOKE-DOMVzvnB.js +84 -0
  179. package/static/assets/sankeyDiagram-HTMAVEWB-B1dcTOvy.js +40 -0
  180. package/static/assets/sequenceDiagram-DBY2YBRQ-BY414yeO.js +162 -0
  181. package/static/assets/sizeCapture-X5ZJPWSS-CuJfrfBN.js +1 -0
  182. package/static/assets/stateDiagram-2N3HPSRC-FX4rJkzV.js +1 -0
  183. package/static/assets/stateDiagram-v2-6OUMAXLB-BVufbO_H.js +1 -0
  184. package/static/assets/swimlanes-5IMT3BWC-Sfy58mmv.js +2 -0
  185. package/static/assets/swimlanesDiagram-G3AALYLV-wDCDw08H.js +8 -0
  186. package/static/assets/timeline-definition-FHXFAJF6-CmV7by8p.js +120 -0
  187. package/static/assets/vennDiagram-L72KCM5P-FqIyFZFt.js +34 -0
  188. package/static/assets/wardleyDiagram-EHGQE667-BZ06O45i.js +78 -0
  189. package/static/assets/xychartDiagram-FW5EYKEG-BOkffR06.js +7 -0
  190. package/static/index.html +24 -0
@@ -0,0 +1,720 @@
1
+ # Admin Layout Architecture — Alpine.js + HTMX Assets (served by the Mockup Hub)
2
+
3
+ The application's mockup folder contains **assets only**. The shared **Mockup Hub**
4
+ (`<root>/mockup/server.js` — see [mockup-hub-template.md](mockup-hub-template.md))
5
+ assembles pages from partials server-side at `/{app_slug}/{role}/{page}`. **HTMX**
6
+ performs partial content swapping during navigation, and **Alpine.js** powers
7
+ interactive UI components (dropdowns, dark mode toggle).
8
+
9
+ `{{APP_SLUG}}` below is replaced **at generation time** with the application slug
10
+ (app folder name minus the leading `<number>_` prefix). It is constant for the whole
11
+ mockup folder.
12
+
13
+ ---
14
+
15
+ ## File Structure
16
+
17
+ ```
18
+ mockup/
19
+ mockup-manifest.json # Hub discovery manifest (see mockup-hub-template.md)
20
+ MOCKUP.html # Per-app screen index (served by hub at /{app_slug})
21
+ partials/
22
+ shell.html # Page shell (assembled by server)
23
+ header.html # Top header (Alpine.js powered)
24
+ footer.html # Bottom footer
25
+ sidebar-{role_snake_case}.html # One sidebar per role
26
+ {role_snake_case}/
27
+ content/
28
+ home.html # Content fragment (no layout)
29
+ profile.html
30
+ account.html
31
+ notifications.html
32
+ {module_snake_case}.html
33
+ {module_snake_case}_detail.html
34
+ {module_snake_case}_create.html
35
+ {module_snake_case}_edit.html
36
+ ...
37
+ ```
38
+
39
+ ---
40
+
41
+ ## Placeholders Reference
42
+
43
+ | Placeholder | Description |
44
+ |-------------|-------------|
45
+ | `{{APP_NAME}}` | Application name (e.g., "Hub Middleware") |
46
+ | `{{APP_SLUG}}` | App slug used as URL prefix (e.g., "hub_middleware") — replaced at generation time |
47
+ | `{{PAGE_TITLE}}` | Screen title (e.g., "Location Information") |
48
+ | `{{ROLE}}` | Role in snake_case (e.g., "hub_administrator") — in `header.html` it is re-injected by the hub at request time |
49
+ | `{{SIDEBAR_MENU}}` | Navigation menu items HTML |
50
+ | `{{BREADCRUMB}}` | Breadcrumb trail HTML |
51
+ | `{{MAIN_CONTENT}}` | Screen-specific content |
52
+ | `{{COPYRIGHT_YEAR}}` | Current year |
53
+ | `{{VERSION}}` | Latest version from PRD.md |
54
+ | `{{DESIGN_PRIMARY}}` | Primary color hex from design system |
55
+ | `{{DESIGN_SECONDARY}}` | Secondary color hex from design system |
56
+ | `{{DESIGN_ACCENT}}` | Accent/CTA color hex |
57
+ | `{{DESIGN_BG}}` | Background color hex |
58
+ | `{{DESIGN_TEXT}}` | Text color hex |
59
+ | `{{HEADING_FONT}}` | Heading font |
60
+ | `{{BODY_FONT}}` | Body font |
61
+ | `{{GOOGLE_FONTS_URL}}` | Google Fonts import URL |
62
+
63
+ ---
64
+
65
+ ## Server
66
+
67
+ There is **no per-app server**. The shared Mockup Hub (`<root>/mockup/server.js`, created
68
+ per the Ensure-Hub rules in [mockup-hub-template.md](mockup-hub-template.md)) provides:
69
+
70
+ - `GET /{app_slug}` → this app's `MOCKUP.html`
71
+ - `GET /{app_slug}/{role}` → redirect to `/{app_slug}/{role}/home`
72
+ - `GET /{app_slug}/{role}/{page}` → shell + header + sidebar + content + footer assembly
73
+ - `GET /api/content/{app_slug}/{role}/{page}` → content fragment (HTMX swaps)
74
+ - `GET /{app_slug}/static/*`, `GET /{app_slug}/reports/*` → static assets
75
+ - The hub re-injects `{{ROLE}}` into `header.html` at request time
76
+
77
+ ---
78
+
79
+ ## partials/shell.html Template
80
+
81
+ The shell is the outer HTML document. It is assembled server-side and NOT a partial
82
+ served to the browser — the hub substitutes `{{HEADER}}`, `{{SIDEBAR}}`,
83
+ `{{CONTENT}}`, and `{{FOOTER}}` before responding.
84
+
85
+ ```html
86
+ <!DOCTYPE html>
87
+ <html lang="en" x-data="appShell()" :class="{ 'dark': darkMode }">
88
+ <head>
89
+ <meta charset="UTF-8">
90
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
91
+ <title>{{APP_NAME}}</title>
92
+
93
+ <!-- Tailwind CSS -->
94
+ <script src="https://cdn.tailwindcss.com"></script>
95
+
96
+ <!-- HTMX -->
97
+ <script src="https://unpkg.com/htmx.org@2.0.4" integrity="sha384-HGfztofotfshcF7+8n44JQL2oJmowVChPTg48S+jvZoztPfvwD79OC/LTtG6dMp+" crossorigin="anonymous"></script>
98
+
99
+ <!-- Alpine.js -->
100
+ <script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.14.1/dist/cdn.min.js"></script>
101
+
102
+ <!-- Google Fonts -->
103
+ <link rel="preconnect" href="https://fonts.googleapis.com">
104
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
105
+ <link href="{{GOOGLE_FONTS_URL}}" rel="stylesheet">
106
+
107
+ <script>
108
+ tailwind.config = {
109
+ darkMode: 'class',
110
+ theme: {
111
+ extend: {
112
+ colors: {
113
+ primary: '{{DESIGN_PRIMARY}}',
114
+ secondary: '{{DESIGN_SECONDARY}}',
115
+ accent: '{{DESIGN_ACCENT}}',
116
+ },
117
+ fontFamily: {
118
+ heading: ['{{HEADING_FONT}}', 'sans-serif'],
119
+ body: ['{{BODY_FONT}}', 'sans-serif'],
120
+ }
121
+ }
122
+ }
123
+ }
124
+
125
+ function appShell() {
126
+ return {
127
+ darkMode: localStorage.getItem('darkMode') === 'true',
128
+ toggleDark() {
129
+ this.darkMode = !this.darkMode;
130
+ localStorage.setItem('darkMode', this.darkMode);
131
+ }
132
+ }
133
+ }
134
+ </script>
135
+
136
+ <style>
137
+ body { font-family: '{{BODY_FONT}}', sans-serif; }
138
+ h1, h2, h3, h4, h5, h6 { font-family: '{{HEADING_FONT}}', sans-serif; }
139
+ ::-webkit-scrollbar { width: 6px; }
140
+ ::-webkit-scrollbar-track { background: #f1f5f9; }
141
+ ::-webkit-scrollbar-thumb { background: #94a3b8; border-radius: 3px; }
142
+ ::-webkit-scrollbar-thumb:hover { background: #64748b; }
143
+ /* HTMX loading indicator */
144
+ .htmx-request #content-spinner { display: flex; }
145
+ .htmx-request.htmx-request #content-area { opacity: 0.4; }
146
+ </style>
147
+ </head>
148
+ <body class="bg-slate-50 dark:bg-slate-900 text-slate-800 dark:text-slate-200 min-h-screen flex flex-col">
149
+
150
+ <!-- ── Header (injected by server) ─────────────────────────────────── -->
151
+ {{HEADER}}
152
+
153
+ <!-- ── Body: Sidebar + Main ─────────────────────────────────────────── -->
154
+ <div class="flex pt-16 min-h-[calc(100vh-4rem)]">
155
+
156
+ <!-- ── Sidebar (injected by server) ──────────────────────────────── -->
157
+ {{SIDEBAR}}
158
+
159
+ <!-- ── Main ──────────────────────────────────────────────────────── -->
160
+ <main class="flex-1 ml-64 flex flex-col min-w-0">
161
+
162
+ <!-- Loading spinner (shown while HTMX is fetching) -->
163
+ <div id="content-spinner" class="hidden fixed inset-0 bg-white/50 z-50 items-center justify-center">
164
+ <div class="w-8 h-8 border-4 border-primary border-t-transparent rounded-full animate-spin"></div>
165
+ </div>
166
+
167
+ <!-- ── Content area (HTMX target) ─────────────────────────────── -->
168
+ <div id="content-area" class="flex-1 flex flex-col transition-opacity duration-200">
169
+ {{CONTENT}}
170
+ </div>
171
+
172
+ <!-- ── Footer (injected by server) ──────────────────────────────── -->
173
+ {{FOOTER}}
174
+
175
+ </main>
176
+ </div>
177
+
178
+ </body>
179
+ </html>
180
+ ```
181
+
182
+ ---
183
+
184
+ ## partials/header.html Template
185
+
186
+ Alpine.js `x-data` scopes are per-component, declared on each dropdown wrapper.
187
+ The dark mode toggle dispatches to the parent `appShell()` context via `$dispatch`.
188
+
189
+ ```html
190
+
191
+ <header class="bg-white dark:bg-slate-800 border-b border-slate-200 dark:border-slate-700 h-16 flex items-center justify-between px-6 fixed top-0 left-0 right-0 z-30 shadow-sm">
192
+
193
+ <!-- Left: Logo + App Name -->
194
+ <div class="flex items-center gap-3">
195
+ <div class="w-8 h-8 rounded-lg flex items-center justify-center" style="background-color: {{DESIGN_PRIMARY}}">
196
+ <svg class="w-5 h-5 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
197
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
198
+ d="M4 6a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2H6a2 2 0 01-2-2V6zm10 0a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2h-2a2 2 0 01-2-2V6zM4 16a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2H6a2 2 0 01-2-2v-2zm10 0a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2h-2a2 2 0 01-2-2v-2z"/>
199
+ </svg>
200
+ </div>
201
+ <span class="font-semibold text-lg text-slate-900 dark:text-slate-100">{{APP_NAME}}</span>
202
+ </div>
203
+
204
+ <!-- Right: Controls -->
205
+ <div class="flex items-center gap-2">
206
+
207
+ <!-- Notification Bell -->
208
+ <div class="relative" x-data="{ open: false }">
209
+ <button @click="open = !open" @keydown.escape.window="open = false"
210
+ class="relative p-2 text-slate-500 hover:text-slate-700 hover:bg-slate-100 dark:hover:bg-slate-700 rounded-lg transition-colors duration-200">
211
+ <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
212
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
213
+ d="M15 17h5l-1.405-1.405A2.032 2.032 0 0118 14.158V11a6.002 6.002 0 00-4-5.659V5a2 2 0 10-4 0v.341C7.67 6.165 6 8.388 6 11v3.159c0 .538-.214 1.055-.595 1.436L4 17h5m6 0v1a3 3 0 11-6 0v-1m6 0H9"/>
214
+ </svg>
215
+ <span class="absolute top-1.5 right-1.5 w-2 h-2 bg-red-500 rounded-full"></span>
216
+ </button>
217
+ <!-- Notification Dropdown -->
218
+ <div x-show="open" @click.outside="open = false"
219
+ x-transition:enter="transition ease-out duration-150"
220
+ x-transition:enter-start="opacity-0 translate-y-1"
221
+ x-transition:enter-end="opacity-100 translate-y-0"
222
+ x-transition:leave="transition ease-in duration-100"
223
+ x-transition:leave-start="opacity-100 translate-y-0"
224
+ x-transition:leave-end="opacity-0 translate-y-1"
225
+ class="absolute right-0 top-full mt-2 w-80 bg-white dark:bg-slate-800 border border-slate-200 dark:border-slate-700 rounded-xl shadow-xl z-50"
226
+ style="display: none;">
227
+ <div class="px-4 py-3 border-b border-slate-100 dark:border-slate-700 flex items-center justify-between">
228
+ <span class="font-medium text-sm text-slate-800 dark:text-slate-200">Notifications</span>
229
+ <button class="text-xs text-primary cursor-pointer hover:underline">Mark all read</button>
230
+ </div>
231
+ <div class="max-h-72 overflow-y-auto divide-y divide-slate-50 dark:divide-slate-700">
232
+ <div class="px-4 py-3 hover:bg-slate-50 dark:hover:bg-slate-700 flex gap-3">
233
+ <div class="w-2 h-2 rounded-full mt-2 shrink-0"
234
+ style="background-color: {{DESIGN_PRIMARY}}"></div>
235
+ <div>
236
+ <p class="text-sm text-slate-800 dark:text-slate-200 font-medium">New document uploaded</p>
237
+ <p class="text-xs text-slate-500 mt-0.5">A new employment contract has been uploaded for
238
+ review.</p>
239
+ <p class="text-xs text-slate-400 mt-1">2 minutes ago</p>
240
+ </div>
241
+ </div>
242
+ <div class="px-4 py-3 hover:bg-slate-50 dark:hover:bg-slate-700 flex gap-3">
243
+ <div class="w-2 h-2 rounded-full mt-2 shrink-0"
244
+ style="background-color: {{DESIGN_PRIMARY}}"></div>
245
+ <div>
246
+ <p class="text-sm text-slate-800 dark:text-slate-200 font-medium">Corridor status
247
+ changed</p>
248
+ <p class="text-xs text-slate-500 mt-0.5">MYS-BGD corridor has been activated.</p>
249
+ <p class="text-xs text-slate-400 mt-1">15 minutes ago</p>
250
+ </div>
251
+ </div>
252
+ <div class="px-4 py-3 hover:bg-slate-50 dark:hover:bg-slate-700 flex gap-3">
253
+ <div class="w-2 h-2 bg-transparent rounded-full mt-2 shrink-0 border border-slate-300"></div>
254
+ <div>
255
+ <p class="text-sm text-slate-600 dark:text-slate-400">Employer registration pending</p>
256
+ <p class="text-xs text-slate-500 mt-0.5">ABC Manufacturing Sdn Bhd requires approval.</p>
257
+ <p class="text-xs text-slate-400 mt-1">1 hour ago</p>
258
+ </div>
259
+ </div>
260
+ <div class="px-4 py-3 hover:bg-slate-50 dark:hover:bg-slate-700 flex gap-3">
261
+ <div class="w-2 h-2 bg-transparent rounded-full mt-2 shrink-0 border border-slate-300"></div>
262
+ <div>
263
+ <p class="text-sm text-slate-600 dark:text-slate-400">System maintenance scheduled</p>
264
+ <p class="text-xs text-slate-500 mt-0.5">Scheduled downtime on Sunday 2:00 AM – 4:00 AM.</p>
265
+ <p class="text-xs text-slate-400 mt-1">3 hours ago</p>
266
+ </div>
267
+ </div>
268
+ </div>
269
+ <a href="/{{APP_SLUG}}/{{ROLE}}/notifications"
270
+ hx-get="/api/content/{{APP_SLUG}}/{{ROLE}}/notifications"
271
+ hx-target="#content-area"
272
+ hx-swap="innerHTML"
273
+ hx-push-url="/{{APP_SLUG}}/{{ROLE}}/notifications"
274
+ @click="open = false"
275
+ class="block px-4 py-3 border-t border-slate-100 dark:border-slate-700 text-center text-sm font-medium hover:bg-slate-50 dark:hover:bg-slate-700 rounded-b-xl"
276
+ style="color: {{DESIGN_PRIMARY}}">
277
+ View all notifications
278
+ </a>
279
+ </div>
280
+ </div>
281
+
282
+ <!-- Locale / Globe -->
283
+ <div class="relative" x-data="{ open: false }">
284
+ <button @click="open = !open" @keydown.escape.window="open = false"
285
+ class="p-2 text-slate-500 hover:text-slate-700 hover:bg-slate-100 dark:hover:bg-slate-700 rounded-lg transition-colors duration-200">
286
+ <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
287
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
288
+ d="M21 12a9 9 0 01-9 9m9-9a9 9 0 00-9-9m9 9H3m9 9a9 9 0 01-9-9m9 9c1.657 0 3-4.03 3-9s-1.343-9-3-9m0 18c-1.657 0-3-4.03-3-9s1.343-9 3-9m-9 9a9 9 0 019-9"/>
289
+ </svg>
290
+ </button>
291
+ <div x-show="open" @click.outside="open = false"
292
+ x-transition:enter="transition ease-out duration-150"
293
+ x-transition:enter-start="opacity-0 translate-y-1"
294
+ x-transition:enter-end="opacity-100 translate-y-0"
295
+ class="absolute right-0 top-full mt-2 w-48 bg-white dark:bg-slate-800 border border-slate-200 dark:border-slate-700 rounded-xl shadow-xl py-1 z-50"
296
+ style="display: none;">
297
+ <div class="px-4 py-2 border-b border-slate-100 dark:border-slate-700">
298
+ <span class="text-xs font-medium text-slate-400 uppercase tracking-wider">Language</span>
299
+ </div>
300
+ <a href="javascript:void(0)" class="flex items-center gap-3 px-4 py-2 text-sm font-medium"
301
+ style="color: {{DESIGN_PRIMARY}}">
302
+ <span>🇬🇧</span> English (EN)
303
+ </a>
304
+ <a href="javascript:void(0)"
305
+ class="flex items-center gap-3 px-4 py-2 text-sm text-slate-700 dark:text-slate-300 hover:bg-slate-50 dark:hover:bg-slate-700">
306
+ <span>🇲🇾</span> Bahasa Melayu (BM)
307
+ </a>
308
+ <a href="javascript:void(0)"
309
+ class="flex items-center gap-3 px-4 py-2 text-sm text-slate-700 dark:text-slate-300 hover:bg-slate-50 dark:hover:bg-slate-700">
310
+ <span>🇧🇩</span> বাংলা (BN)
311
+ </a>
312
+ <a href="javascript:void(0)"
313
+ class="flex items-center gap-3 px-4 py-2 text-sm text-slate-700 dark:text-slate-300 hover:bg-slate-50 dark:hover:bg-slate-700">
314
+ <span>🇮🇩</span> Bahasa Indonesia (ID)
315
+ </a>
316
+ </div>
317
+ </div>
318
+
319
+ <!-- Dark Mode Toggle -->
320
+ <button @click="toggleDark()"
321
+ class="p-2 text-slate-500 hover:text-slate-700 hover:bg-slate-100 dark:hover:bg-slate-700 rounded-lg transition-colors duration-200"
322
+ :title="darkMode ? 'Switch to light mode' : 'Switch to dark mode'">
323
+ <!-- Moon icon (shown in light mode) -->
324
+ <svg x-show="!darkMode" class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
325
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
326
+ d="M20.354 15.354A9 9 0 018.646 3.646 9.003 9.003 0 0012 21a9.003 9.003 0 008.354-5.646z"/>
327
+ </svg>
328
+ <!-- Sun icon (shown in dark mode) -->
329
+ <svg x-show="darkMode" class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"
330
+ style="display:none">
331
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
332
+ d="M12 3v1m0 16v1m9-9h-1M4 12H3m15.364 6.364l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707.707M6.343 17.657l-.707.707M16 12a4 4 0 11-8 0 4 4 0 018 0z"/>
333
+ </svg>
334
+ </button>
335
+
336
+ <!-- Divider -->
337
+ <div class="w-px h-8 bg-slate-200 dark:bg-slate-600 mx-1"></div>
338
+
339
+ <!-- User Avatar + Dropdown -->
340
+ <div class="relative" x-data="{ open: false }">
341
+ <button @click="open = !open" @keydown.escape.window="open = false"
342
+ class="flex items-center gap-2 cursor-pointer p-1 rounded-lg hover:bg-slate-100 dark:hover:bg-slate-700 transition-colors">
343
+ <div class="w-8 h-8 rounded-full flex items-center justify-center font-semibold text-sm text-white"
344
+ style="background-color: {{DESIGN_PRIMARY}}">
345
+ U
346
+ </div>
347
+ <svg class="w-4 h-4 text-slate-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
348
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"/>
349
+ </svg>
350
+ </button>
351
+ <div x-show="open" @click.outside="open = false"
352
+ x-transition:enter="transition ease-out duration-150"
353
+ x-transition:enter-start="opacity-0 translate-y-1"
354
+ x-transition:enter-end="opacity-100 translate-y-0"
355
+ class="absolute right-0 top-full mt-2 w-48 bg-white dark:bg-slate-800 border border-slate-200 dark:border-slate-700 rounded-xl shadow-xl py-1 z-50"
356
+ style="display: none;">
357
+ <a href="/{{APP_SLUG}}/{{ROLE}}/profile"
358
+ hx-get="/api/content/{{APP_SLUG}}/{{ROLE}}/profile"
359
+ hx-target="#content-area"
360
+ hx-swap="innerHTML"
361
+ hx-push-url="/{{APP_SLUG}}/{{ROLE}}/profile"
362
+ @click="open = false"
363
+ class="flex items-center gap-2 px-4 py-2 text-sm text-slate-700 dark:text-slate-300 hover:bg-slate-50 dark:hover:bg-slate-700">
364
+ <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
365
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
366
+ d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z"/>
367
+ </svg>
368
+ Profile
369
+ </a>
370
+ <a href="/{{APP_SLUG}}/{{ROLE}}/account"
371
+ hx-get="/api/content/{{APP_SLUG}}/{{ROLE}}/account"
372
+ hx-target="#content-area"
373
+ hx-swap="innerHTML"
374
+ hx-push-url="/{{APP_SLUG}}/{{ROLE}}/account"
375
+ @click="open = false"
376
+ class="flex items-center gap-2 px-4 py-2 text-sm text-slate-700 dark:text-slate-300 hover:bg-slate-50 dark:hover:bg-slate-700">
377
+ <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
378
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
379
+ d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.066 2.573c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.573 1.066c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.066-2.573c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z"/>
380
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
381
+ d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/>
382
+ </svg>
383
+ Account
384
+ </a>
385
+ <div class="border-t border-slate-100 dark:border-slate-700 my-1"></div>
386
+ <a href="/"
387
+ class="flex items-center gap-2 px-4 py-2 text-sm text-red-600 hover:bg-red-50 dark:hover:bg-red-900/20">
388
+ <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
389
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
390
+ d="M17 16l4-4m0 0l-4-4m4 4H7m6 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h4a3 3 0 013 3v1"/>
391
+ </svg>
392
+ Logout
393
+ </a>
394
+ </div>
395
+ </div>
396
+ </div>
397
+ </header>
398
+ ```
399
+
400
+ > **Note:** In `header.html`, replace `{{APP_SLUG}}` at generation time but leave
401
+ > `{{ROLE}}` as-is — the Mockup Hub injects the actual role slug into the header partial
402
+ > at request time (`.replace(/\{\{ROLE\}\}/g, role)`), so one shared header works for
403
+ > every role.
404
+
405
+ ---
406
+
407
+ ## partials/footer.html Template
408
+
409
+ ```html
410
+ <footer class="border-t border-slate-200 dark:border-slate-700 bg-white dark:bg-slate-800 px-6 py-3 flex items-center justify-between text-xs text-slate-400">
411
+ <span>&copy; {{COPYRIGHT_YEAR}} {{APP_NAME}}. All rights reserved.</span>
412
+ <span>{{VERSION}}</span>
413
+ </footer>
414
+ ```
415
+
416
+ ---
417
+
418
+ ## partials/sidebar-{role}.html Template
419
+
420
+ Replace `{{ROLE}}` with the role's snake_case name. The `hx-get` attribute fetches only
421
+ the content fragment; `hx-push-url` updates the browser URL so the page is bookmarkable.
422
+
423
+ ```html
424
+ <aside class="w-64 bg-white dark:bg-slate-800 border-r border-slate-200 dark:border-slate-700 fixed top-16 bottom-0 left-0 overflow-y-auto z-20">
425
+ <nav class="py-4" x-data="{ active: window.location.pathname }">
426
+
427
+ <!-- Home -->
428
+ <a href="/{{APP_SLUG}}/{{ROLE}}/home"
429
+ hx-get="/api/content/{{APP_SLUG}}/{{ROLE}}/home"
430
+ hx-target="#content-area"
431
+ hx-swap="innerHTML"
432
+ hx-push-url="/{{APP_SLUG}}/{{ROLE}}/home"
433
+ @click="active = '/{{APP_SLUG}}/{{ROLE}}/home'"
434
+ :class="active.startsWith('/{{APP_SLUG}}/{{ROLE}}/home') ? 'bg-primary/5 text-primary border-r-2 border-primary font-medium' : 'text-slate-600 dark:text-slate-400 hover:bg-slate-50 dark:hover:bg-slate-700 hover:text-slate-900'"
435
+ class="flex items-center gap-3 px-4 py-2.5 text-sm transition-colors duration-200 cursor-pointer">
436
+ <svg class="w-5 h-5 shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
437
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
438
+ d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6"/>
439
+ </svg>
440
+ <span>Home</span>
441
+ </a>
442
+
443
+ <!-- Module separator -->
444
+ <div class="px-4 pt-4 pb-1">
445
+ <span class="text-xs font-semibold text-slate-400 uppercase tracking-wider">Menu</span>
446
+ </div>
447
+
448
+ <!-- Module menu items: repeat for each module assigned to this role -->
449
+ {{SIDEBAR_MENU}}
450
+
451
+ </nav>
452
+ </aside>
453
+ ```
454
+
455
+ ### Sidebar Active Menu Item Template
456
+
457
+ ```html
458
+ <!-- Active state -->
459
+ <a href="/{{APP_SLUG}}/{{ROLE}}/{{SCREEN_FILE_NO_EXT}}"
460
+ hx-get="/api/content/{{APP_SLUG}}/{{ROLE}}/{{SCREEN_FILE_NO_EXT}}"
461
+ hx-target="#content-area"
462
+ hx-swap="innerHTML"
463
+ hx-push-url="/{{APP_SLUG}}/{{ROLE}}/{{SCREEN_FILE_NO_EXT}}"
464
+ @click="active = '/{{APP_SLUG}}/{{ROLE}}/{{SCREEN_FILE_NO_EXT}}'"
465
+ :class="active.startsWith('/{{APP_SLUG}}/{{ROLE}}/{{SCREEN_FILE_NO_EXT}}') ? 'bg-primary/5 text-primary border-r-2 border-primary font-medium' : 'text-slate-600 dark:text-slate-400 hover:bg-slate-50 dark:hover:bg-slate-700 hover:text-slate-900'"
466
+ class="flex items-center gap-3 px-4 py-2.5 text-sm transition-colors duration-200 cursor-pointer">
467
+ <svg class="w-5 h-5 shrink-0"><!-- module-appropriate icon --></svg>
468
+ <span>{{MODULE_NAME}}</span>
469
+ </a>
470
+ ```
471
+
472
+ ### Suggested SVG Icons by Module Type
473
+
474
+ | Module Pattern | Icon |
475
+ |---------------|------|
476
+ | Document / File | paper with lines (`DocumentTextIcon`) |
477
+ | Location / Geography | map pin (`MapPinIcon`) |
478
+ | Corridor / Route | bidirectional arrows (`ArrowsRightLeftIcon`) |
479
+ | Recruitment / Steps | ordered list (`QueueListIcon`) |
480
+ | Employer / Company | office building (`BuildingOfficeIcon`) |
481
+ | Classification / Category | tag (`TagIcon`) |
482
+ | Quota / Allocation | pie chart (`ChartPieIcon`) |
483
+ | Job / Demand | briefcase (`BriefcaseIcon`) |
484
+ | Employee / Person | group of people (`UserGroupIcon`) |
485
+ | Settings / Config | gear (`CogIcon`) |
486
+
487
+ ---
488
+
489
+ ## Content Fragment Template
490
+
491
+ Content files contain **only the page content area** — no `<html>`, `<head>`, `<body>`,
492
+ layout, or Tailwind config. They are loaded via HTMX and injected into `#content-area`.
493
+
494
+ ```html
495
+ <!-- {{PAGE_TITLE}} — {{ROLE}} -->
496
+ <!-- User story traceability: {{US_TAG}} [{{US_VERSION}}] -->
497
+
498
+ <!-- Breadcrumb -->
499
+ <div class="px-6 py-3 border-b border-slate-100 dark:border-slate-700 bg-white dark:bg-slate-800">
500
+ <nav class="flex items-center gap-2 text-sm text-slate-500">
501
+ <a href="/{{APP_SLUG}}/{{ROLE}}/home"
502
+ hx-get="/api/content/{{APP_SLUG}}/{{ROLE}}/home"
503
+ hx-target="#content-area"
504
+ hx-swap="innerHTML"
505
+ hx-push-url="/{{APP_SLUG}}/{{ROLE}}/home"
506
+ class="hover:text-slate-700 dark:hover:text-slate-300 cursor-pointer">Home</a>
507
+ <svg class="w-4 h-4 shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
508
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"/>
509
+ </svg>
510
+ <span class="text-slate-800 dark:text-slate-200 font-medium">{{PAGE_TITLE}}</span>
511
+ </nav>
512
+ </div>
513
+
514
+ <!-- Page Content -->
515
+ <div class="flex-1 p-6">
516
+ {{MAIN_CONTENT}}
517
+ </div>
518
+ ```
519
+
520
+ ### Breadcrumb for Sub-screens (detail / edit / create)
521
+
522
+ ```html
523
+ <a href="/{{APP_SLUG}}/{{ROLE}}/home" hx-get="/api/content/{{APP_SLUG}}/{{ROLE}}/home" hx-target="#content-area" hx-swap="innerHTML" hx-push-url="/{{APP_SLUG}}/{{ROLE}}/home" class="hover:text-slate-700 cursor-pointer">Home</a>
524
+ <svg ...><path .../></svg>
525
+ <a href="/{{APP_SLUG}}/{{ROLE}}/{{MODULE}}" hx-get="/api/content/{{APP_SLUG}}/{{ROLE}}/{{MODULE}}" hx-target="#content-area" hx-swap="innerHTML" hx-push-url="/{{APP_SLUG}}/{{ROLE}}/{{MODULE}}" class="hover:text-slate-700 cursor-pointer">{{MODULE_NAME}}</a>
526
+ <svg ...><path .../></svg>
527
+ <span class="text-slate-800 dark:text-slate-200 font-medium">{{PAGE_TITLE}}</span>
528
+ ```
529
+
530
+ ---
531
+
532
+ ## Link Rules Inside Content Fragments
533
+
534
+ ### Navigation links (within the app)
535
+
536
+ Use HTMX attributes for in-app navigation — never full page reloads for screen transitions:
537
+
538
+ ```html
539
+ <a href="/{{APP_SLUG}}/{{ROLE}}/{{TARGET_PAGE}}"
540
+ hx-get="/api/content/{{APP_SLUG}}/{{ROLE}}/{{TARGET_PAGE}}"
541
+ hx-target="#content-area"
542
+ hx-swap="innerHTML"
543
+ hx-push-url="/{{APP_SLUG}}/{{ROLE}}/{{TARGET_PAGE}}"
544
+ class="...">
545
+ View Details
546
+ </a>
547
+ ```
548
+
549
+ ### Image links — MUST open in new tab
550
+
551
+ ```html
552
+ <a href="/{{APP_SLUG}}/static/images/sample.jpg" target="_blank" rel="noopener noreferrer" class="...">
553
+ View Image
554
+ </a>
555
+ ```
556
+
557
+ ### PDF / document links — MUST open in new tab
558
+
559
+ ```html
560
+ <a href="/{{APP_SLUG}}/static/docs/sample.pdf" target="_blank" rel="noopener noreferrer" class="...">
561
+ <svg ...><!-- pdf icon --></svg>
562
+ View PDF
563
+ </a>
564
+ ```
565
+
566
+ ### Action buttons in tables
567
+
568
+ ```html
569
+ <!-- View / Details → HTMX navigation -->
570
+ <a href="/{{APP_SLUG}}/{{ROLE}}/{{MODULE}}_detail"
571
+ hx-get="/api/content/{{APP_SLUG}}/{{ROLE}}/{{MODULE}}_detail"
572
+ hx-target="#content-area"
573
+ hx-swap="innerHTML"
574
+ hx-push-url="/{{APP_SLUG}}/{{ROLE}}/{{MODULE}}_detail"
575
+ class="text-xs px-2 py-1 rounded border border-primary text-primary hover:bg-primary/5">
576
+ View
577
+ </a>
578
+
579
+ <!-- Edit → HTMX navigation -->
580
+ <a href="/{{APP_SLUG}}/{{ROLE}}/{{MODULE}}_edit"
581
+ hx-get="/api/content/{{APP_SLUG}}/{{ROLE}}/{{MODULE}}_edit"
582
+ hx-target="#content-area"
583
+ hx-swap="innerHTML"
584
+ hx-push-url="/{{APP_SLUG}}/{{ROLE}}/{{MODULE}}_edit"
585
+ class="text-xs px-2 py-1 rounded border border-slate-300 text-slate-600 hover:bg-slate-50">
586
+ Edit
587
+ </a>
588
+
589
+ <!-- Delete → Alpine.js confirm dialog (no href navigation) -->
590
+ <button x-data="{ confirm: false }"
591
+ @click="confirm = true"
592
+ x-show="!confirm"
593
+ class="text-xs px-2 py-1 rounded border border-red-300 text-red-600 hover:bg-red-50">
594
+ Delete
595
+ </button>
596
+ <div x-data="{ confirm: false }" x-show="confirm" class="flex items-center gap-2" style="display:none">
597
+ <span class="text-xs text-red-600">Sure?</span>
598
+ <button class="text-xs px-2 py-1 bg-red-600 text-white rounded">Yes, Delete</button>
599
+ <button @click="confirm = false" class="text-xs px-2 py-1 border border-slate-300 rounded">Cancel</button>
600
+ </div>
601
+
602
+ <!-- Add New → HTMX navigation -->
603
+ <a href="/{{APP_SLUG}}/{{ROLE}}/{{MODULE}}_create"
604
+ hx-get="/api/content/{{APP_SLUG}}/{{ROLE}}/{{MODULE}}_create"
605
+ hx-target="#content-area"
606
+ hx-swap="innerHTML"
607
+ hx-push-url="/{{APP_SLUG}}/{{ROLE}}/{{MODULE}}_create"
608
+ class="px-4 py-2 rounded-lg text-sm text-white"
609
+ style="background-color: {{DESIGN_PRIMARY}}">
610
+ + Add New
611
+ </a>
612
+ ```
613
+
614
+ ### Tab navigation in detail screens
615
+
616
+ Each tab links to its own content page via HTMX:
617
+
618
+ ```html
619
+ <div class="flex border-b border-slate-200 mb-6">
620
+ <!-- Active tab -->
621
+ <a href="/{{APP_SLUG}}/{{ROLE}}/{{MODULE}}_tab_overview"
622
+ hx-get="/api/content/{{APP_SLUG}}/{{ROLE}}/{{MODULE}}_tab_overview"
623
+ hx-target="#content-area"
624
+ hx-swap="innerHTML"
625
+ hx-push-url="/{{APP_SLUG}}/{{ROLE}}/{{MODULE}}_tab_overview"
626
+ class="px-4 py-2 text-sm font-medium border-b-2 -mb-px"
627
+ style="color: {{DESIGN_PRIMARY}}; border-color: {{DESIGN_PRIMARY}}">
628
+ Overview
629
+ </a>
630
+ <!-- Inactive tab -->
631
+ <a href="/{{APP_SLUG}}/{{ROLE}}/{{MODULE}}_tab_documents"
632
+ hx-get="/api/content/{{APP_SLUG}}/{{ROLE}}/{{MODULE}}_tab_documents"
633
+ hx-target="#content-area"
634
+ hx-swap="innerHTML"
635
+ hx-push-url="/{{APP_SLUG}}/{{ROLE}}/{{MODULE}}_tab_documents"
636
+ class="px-4 py-2 text-sm font-medium border-b-2 border-transparent text-slate-500 hover:text-slate-700 -mb-px">
637
+ Documents
638
+ </a>
639
+ </div>
640
+ ```
641
+
642
+ ### Back / Cancel buttons
643
+
644
+ ```html
645
+ <a href="/{{APP_SLUG}}/{{ROLE}}/{{PARENT_MODULE}}"
646
+ hx-get="/api/content/{{APP_SLUG}}/{{ROLE}}/{{PARENT_MODULE}}"
647
+ hx-target="#content-area"
648
+ hx-swap="innerHTML"
649
+ hx-push-url="/{{APP_SLUG}}/{{ROLE}}/{{PARENT_MODULE}}"
650
+ class="px-4 py-2 border border-slate-300 rounded-lg text-sm text-slate-600 hover:bg-slate-50">
651
+ ← Back to List
652
+ </a>
653
+ ```
654
+
655
+ ### Pagination links
656
+
657
+ ```html
658
+ <a href="/{{APP_SLUG}}/{{ROLE}}/{{MODULE}}"
659
+ hx-get="/api/content/{{APP_SLUG}}/{{ROLE}}/{{MODULE}}"
660
+ hx-target="#content-area"
661
+ hx-swap="innerHTML"
662
+ hx-push-url="/{{APP_SLUG}}/{{ROLE}}/{{MODULE}}"
663
+ class="px-3 py-1.5 text-sm border border-slate-300 rounded hover:bg-slate-50">
664
+ 2
665
+ </a>
666
+ ```
667
+
668
+ ---
669
+
670
+ ## Common Screens Content
671
+
672
+ ### home.html content fragment
673
+
674
+ ```html
675
+ <!-- Dashboard Home -->
676
+ <div class="px-6 py-3 border-b border-slate-100 dark:border-slate-700 bg-white dark:bg-slate-800">
677
+ <nav class="flex items-center gap-2 text-sm text-slate-500">
678
+ <span class="text-slate-800 dark:text-slate-200 font-medium">Home</span>
679
+ </nav>
680
+ </div>
681
+ <div class="flex-1 p-6">
682
+ <h1 class="text-2xl font-bold text-slate-900 dark:text-slate-100 mb-2">Welcome back</h1>
683
+ <p class="text-slate-500 mb-6">{{APP_NAME}} — {{ROLE_DISPLAY}}</p>
684
+ <!-- Summary stat cards -->
685
+ <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4 mb-8">
686
+ <!-- Repeat: one card per module -->
687
+ </div>
688
+ <!-- Recent activity table -->
689
+ </div>
690
+ ```
691
+
692
+ ### profile.html content fragment
693
+
694
+ User profile: avatar, full name, email, role badge, personal info form (read-only), Edit button.
695
+
696
+ ### account.html content fragment
697
+
698
+ Account settings: Change Password form, Notification Preferences toggles (Alpine.js),
699
+ Language/Locale selector, Session Management table.
700
+
701
+ ### notifications.html content fragment
702
+
703
+ Notifications list: Filter tabs (All / Unread / Read with Alpine.js tab state),
704
+ notification cards with icon / title / message / timestamp / unread dot,
705
+ Mark all as read button.
706
+
707
+ ---
708
+
709
+ ## Quick-Start Instructions (append to output summary)
710
+
711
+ ```
712
+ Quick Start
713
+ ===========
714
+ 1. cd <root>/mockup
715
+ 2. npm start (zero dependencies — no npm install required)
716
+ 3. Open http://localhost:3000 in your browser
717
+ - Landing page lists ALL applications and roles
718
+ - This app's screen index: http://localhost:3000/{app_slug}
719
+ 4. Change port: PORT=4000 npm start, or edit <root>/mockup/mockup.config.json
720
+ ```