@7365admin1/layer-common 3.2.6 → 3.2.8-staging.200

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 (284) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/assets/css/primitives.css +1113 -0
  3. package/assets/css/screens.css +1097 -0
  4. package/assets/css/shell.css +258 -0
  5. package/assets/css/tokens.css +236 -0
  6. package/components/AcceptDialog.vue +17 -18
  7. package/components/AccessCard/AvailableStats.vue +24 -16
  8. package/components/AccessCardAddForm.vue +42 -42
  9. package/components/AccessCardAssignToUnitForm.vue +13 -21
  10. package/components/AccessCardDeleteDialog.vue +41 -39
  11. package/components/AccessCardDetailsDialog.vue +125 -79
  12. package/components/AccessCardPreviewDialog.vue +58 -84
  13. package/components/AccessCardQrTagging.vue +60 -63
  14. package/components/AccessCardReplaceForm.vue +27 -23
  15. package/components/AccessManagement.vue +86 -85
  16. package/components/AddEqupmentForm.vue +18 -22
  17. package/components/AddPassKeyToVisitor.vue +95 -73
  18. package/components/AppButton.vue +62 -0
  19. package/components/AppCard.vue +20 -0
  20. package/components/AppDateField.vue +49 -0
  21. package/components/AppField.vue +76 -0
  22. package/components/AppKpiTile.vue +65 -0
  23. package/components/AppSegmented.vue +38 -0
  24. package/components/AppSelect.vue +247 -0
  25. package/components/AreaChecklistHistoryLogs.vue +51 -44
  26. package/components/AreaChecklistHistoryMain.vue +14 -7
  27. package/components/AreaFormDialog.vue +71 -59
  28. package/components/AreaMain.vue +73 -107
  29. package/components/AttachmentsViewer.vue +84 -22
  30. package/components/AttendanceCheckInOutDialog.vue +85 -88
  31. package/components/AttendanceDetailsDialog.vue +57 -63
  32. package/components/AttendanceMain.vue +4 -3
  33. package/components/AttendanceMapSearchDialog.vue +42 -44
  34. package/components/AttendanceSettingsDialog.vue +79 -63
  35. package/components/Avatar/Main.vue +54 -2
  36. package/components/BillingMain.vue +26 -30
  37. package/components/BtnUploadFile.vue +53 -11
  38. package/components/BuildingForm.vue +34 -27
  39. package/components/BuildingManagement/buildings.vue +39 -39
  40. package/components/BuildingManagement/units.vue +26 -42
  41. package/components/BuildingUnitFormAdd.vue +20 -23
  42. package/components/BuildingUnitFormEdit.vue +8 -4
  43. package/components/BulletinBoardForm.vue +19 -24
  44. package/components/BulletinBoardManagement.vue +49 -57
  45. package/components/BulletinBoardView.vue +9 -6
  46. package/components/BulletinExpirationChip.vue +13 -4
  47. package/components/Button/Close.vue +2 -2
  48. package/components/CameraForm.vue +21 -22
  49. package/components/CameraMain.vue +50 -49
  50. package/components/Card/DeleteConfirmation.vue +24 -22
  51. package/components/Card/MemberInfoSummary.vue +28 -3
  52. package/components/Card/Toggle.vue +15 -3
  53. package/components/CardToolbar.vue +37 -0
  54. package/components/Carousel.vue +56 -36
  55. package/components/Chat/Bubbles.vue +43 -14
  56. package/components/Chat/Information.vue +49 -32
  57. package/components/Chat/ListCard.vue +38 -9
  58. package/components/Chat/Message.vue +41 -15
  59. package/components/Chat/Navigation.vue +27 -15
  60. package/components/Chat/SkeletonLoader.vue +9 -2
  61. package/components/ChecklistItemRow.vue +25 -1
  62. package/components/CleaningScheduleMain.vue +44 -49
  63. package/components/ClientDetailForm.vue +81 -56
  64. package/components/ClientMain.vue +92 -82
  65. package/components/ConfirmDialog.vue +14 -8
  66. package/components/Container/Standard.vue +9 -7
  67. package/components/CreateSubsciptionPlan.vue +148 -89
  68. package/components/DashboardEmptyState.vue +48 -3
  69. package/components/DashboardMain.vue +924 -676
  70. package/components/DashboardPanel.vue +84 -21
  71. package/components/DeliveryCompany.vue +34 -20
  72. package/components/Dialog/DeleteConfirmation.vue +20 -21
  73. package/components/Dialog/ReplaceAutofillPrompt.vue +19 -26
  74. package/components/Dialog/ReusablePrompt.vue +30 -29
  75. package/components/Dialog/UpdateMoreAction.vue +62 -59
  76. package/components/DocumentForm.vue +14 -21
  77. package/components/DocumentManagement.vue +162 -108
  78. package/components/DocumentViewer.vue +29 -12
  79. package/components/DrawImage.vue +29 -21
  80. package/components/EntryPass/QrTemplatePreview.vue +27 -16
  81. package/components/EntryPassInformation.vue +40 -10
  82. package/components/EntryPassMain.vue +49 -27
  83. package/components/EquipmentItemMain.vue +84 -97
  84. package/components/EquipmentManagementMain.vue +81 -103
  85. package/components/Facility/BookingSetup.vue +43 -52
  86. package/components/Feedback/Form.vue +26 -12
  87. package/components/FeedbackMain.vue +35 -49
  88. package/components/FileInput.vue +75 -58
  89. package/components/FileInputWithList.vue +59 -12
  90. package/components/FormDialog.vue +60 -21
  91. package/components/HidAccessLogDashboard.vue +512 -490
  92. package/components/HidCameraCaptureDialog.vue +216 -0
  93. package/components/HidEnabledGate.vue +67 -9
  94. package/components/HidIntercomManagement.vue +1322 -653
  95. package/components/HidQrCodeConfiguration.vue +1030 -258
  96. package/components/HidReaderForm.vue +142 -59
  97. package/components/HidReaderManagement.vue +211 -323
  98. package/components/HidServiceSettingsPanel.vue +44 -13
  99. package/components/HidUserEnrollment.vue +1185 -412
  100. package/components/HygieneUpdateMoreAction.vue +44 -44
  101. package/components/IncidentReport/Authorities.vue +30 -18
  102. package/components/IncidentReport/IncidentInformation.vue +10 -10
  103. package/components/IncidentReport/IncidentInformationDownload.vue +3 -4
  104. package/components/IncidentReport/affectedEntities.vue +41 -20
  105. package/components/Input/Date.vue +3 -0
  106. package/components/Input/DatePicker.vue +1 -2
  107. package/components/Input/DateTimePicker.vue +1 -1
  108. package/components/Input/File.vue +55 -10
  109. package/components/Input/FileV2.vue +39 -6
  110. package/components/Input/InputPhoneNumberV2.vue +2 -1
  111. package/components/Input/ListGroupSelection.vue +56 -8
  112. package/components/Input/NRICNumber.vue +1 -0
  113. package/components/Input/NewDate.vue +1 -0
  114. package/components/Input/Number.vue +1 -0
  115. package/components/Input/Password.vue +1 -0
  116. package/components/Input/PhoneNumber.vue +21 -1
  117. package/components/Input/VehicleNumber.vue +1 -0
  118. package/components/InputLabel.vue +2 -2
  119. package/components/InvitationClientForm.vue +21 -19
  120. package/components/InvitationForm.vue +15 -21
  121. package/components/InvitationMain.vue +46 -61
  122. package/components/Layout/Header.vue +263 -36
  123. package/components/Layout/NavigationDrawer.vue +300 -30
  124. package/components/ListItem.vue +33 -3
  125. package/components/LocalPagination.vue +27 -3
  126. package/components/ManageChecklistMain.vue +136 -86
  127. package/components/MemberInformation.vue +32 -16
  128. package/components/MemberMain.vue +113 -93
  129. package/components/MyAttendanceMain.vue +3 -5
  130. package/components/NavigationItem.vue +83 -3
  131. package/components/Nfc/NFCPatrolReportMain.vue +47 -33
  132. package/components/Nfc/NFCPatrolRouteForm.vue +18 -20
  133. package/components/Nfc/NFCPatrolRouteMain.vue +116 -103
  134. package/components/Nfc/NFCPatrolSettings.vue +14 -4
  135. package/components/Nfc/NFCTagForm.vue +22 -8
  136. package/components/Nfc/NFCTagMain.vue +41 -43
  137. package/components/Nfc/PatrolReport/DailyReportTab.vue +8 -1
  138. package/components/Nfc/PatrolReport/MonthlyReportTab.vue +8 -1
  139. package/components/Nfc/PatrolReport/MonthlyReportTable.vue +5 -3
  140. package/components/Nfc/PatrolReport/PatrolActivityTable.vue +26 -6
  141. package/components/Nfc/PatrolReport/PatrolReportTab.vue +8 -0
  142. package/components/Nfc/PatrolReport/PatrolRouteSummary.vue +41 -9
  143. package/components/Nfc/PatrolReport/RemarksDialog.vue +39 -9
  144. package/components/Nfc/PatrolReport/ReportEmptyState.vue +29 -9
  145. package/components/Nfc/PatrolReport/ReportFilters.vue +19 -10
  146. package/components/Nfc/PatrolReport/ReportLocationHeader.vue +32 -6
  147. package/components/Nfc/PatrolReport/SummaryReportTable.vue +28 -6
  148. package/components/NotificationSettings.vue +373 -0
  149. package/components/NumberSettingField.vue +9 -2
  150. package/components/OnlineFormConfigurationForm.vue +725 -237
  151. package/components/OnlineFormFill.vue +213 -106
  152. package/components/OnlineFormRenderPreview.vue +25 -30
  153. package/components/OnlineFormsConfiguration.vue +113 -109
  154. package/components/OrgViewDialog.vue +34 -16
  155. package/components/OvernightParkingAvailability.vue +49 -26
  156. package/components/OvernightParkingManagement.vue +78 -18
  157. package/components/PageHeader.vue +43 -0
  158. package/components/PassInformation.vue +109 -53
  159. package/components/PasswordConfirmation.vue +44 -26
  160. package/components/PeopleForm.vue +41 -20
  161. package/components/PhotoUpload.vue +110 -48
  162. package/components/PlaceholderComponent.vue +35 -19
  163. package/components/PlanActionsMenu.vue +12 -2
  164. package/components/PlanStatusBadge.vue +7 -8
  165. package/components/PlateNumberDisplay.vue +9 -6
  166. package/components/QrTemplate/PrintDialog.vue +66 -43
  167. package/components/RolePermissionFormCreate.vue +104 -79
  168. package/components/RolePermissionFormPreviewUpdate.vue +111 -109
  169. package/components/RolePermissionMain.vue +183 -81
  170. package/components/ScanVisitorQRCode.vue +99 -35
  171. package/components/ScheduleAreaMain.vue +34 -59
  172. package/components/ScheduleTaskForm.vue +61 -42
  173. package/components/ScheduleTaskMain.vue +56 -69
  174. package/components/SearchVehicleNumberUser.vue +39 -37
  175. package/components/ServiceProviderFormCreate.vue +29 -20
  176. package/components/ServiceProviderInvitationPrompt.vue +155 -0
  177. package/components/ServiceProviderMain.vue +601 -432
  178. package/components/Signature.vue +40 -34
  179. package/components/SignaturePad.vue +5 -29
  180. package/components/SiteSettings.vue +41 -11
  181. package/components/SiteSettingsWorkOrder.vue +61 -30
  182. package/components/SlideCardGroup.vue +35 -11
  183. package/components/Snackbar.vue +36 -3
  184. package/components/SpecificAttr.vue +18 -8
  185. package/components/StatusChip.vue +156 -0
  186. package/components/StockCard.vue +17 -12
  187. package/components/SubscriptionPlanTable.vue +207 -140
  188. package/components/SwitchContext.vue +201 -9
  189. package/components/TableHygiene.vue +36 -46
  190. package/components/TableList.vue +10 -10
  191. package/components/TableListSecondary.vue +105 -54
  192. package/components/TableMain.vue +241 -79
  193. package/components/TableV3.vue +61 -70
  194. package/components/TiptapEditor.vue +33 -40
  195. package/components/TiptapEditorQr.vue +5 -2
  196. package/components/Tooltip/Info.vue +69 -24
  197. package/components/UnitMain.vue +40 -60
  198. package/components/UnitPersonCard.vue +42 -13
  199. package/components/VehicleAddSelection.vue +48 -32
  200. package/components/VehicleForm.vue +112 -129
  201. package/components/VehicleManagement.vue +26 -12
  202. package/components/VehicleUpdateMoreAction.vue +59 -46
  203. package/components/VisitorDataFromScannedQRCodeForm.vue +90 -65
  204. package/components/VisitorForm.vue +774 -210
  205. package/components/VisitorFormSelection.vue +40 -37
  206. package/components/VisitorManagement.vue +239 -82
  207. package/components/VisitorPassKeyQRScanner.vue +65 -18
  208. package/components/VisitorSearchResultSummary.vue +60 -16
  209. package/components/VisitorSocketPopUp.vue +22 -14
  210. package/components/VisitorsReportPreview.vue +44 -29
  211. package/components/WorkOrder/Create.vue +26 -17
  212. package/components/WorkOrder/ListView.vue +38 -7
  213. package/components/WorkOrder/Main.vue +62 -59
  214. package/composables/useFeedback.ts +3 -3
  215. package/composables/useHidAmico.ts +269 -0
  216. package/composables/useHidNavigation.ts +2 -9
  217. package/composables/useMember.ts +3 -3
  218. package/composables/useNFCPatrolDailyReport.ts +13 -0
  219. package/composables/useNFCPatrolMonthlyReport.ts +22 -56
  220. package/composables/useNFCPatrolReport.ts +13 -0
  221. package/composables/useNotificationPreference.ts +87 -0
  222. package/composables/useOnlineFormPages.ts +2 -0
  223. package/composables/useRole.ts +22 -0
  224. package/composables/useServiceProvider.ts +3 -3
  225. package/composables/useServiceProviderInvitation.ts +145 -0
  226. package/composables/useSipWebPhone.ts +336 -0
  227. package/composables/useSubscriptionPlan.ts +62 -0
  228. package/composables/useThemePreference.ts +63 -0
  229. package/composables/useVisitor.ts +6 -1
  230. package/composables/useWebUsb.ts +127 -37
  231. package/composables/useWorkOrder.ts +17 -3
  232. package/error.vue +30 -6
  233. package/nuxt.config.ts +36 -0
  234. package/package.json +25 -4
  235. package/pages/[org]/[site]/access-mgmt/administrator/index.vue +2 -2
  236. package/pages/[org]/[site]/access-mgmt/{identity-mapping → hid-cards}/index.vue +2 -2
  237. package/pages/[org]/[site]/access-mgmt/intercom/index.vue +1 -1
  238. package/pages/notification-settings.vue +19 -0
  239. package/pages/payment-method-linked.vue +34 -10
  240. package/pages/require-customer.vue +48 -10
  241. package/pages/require-organization-membership.vue +32 -8
  242. package/pages/unauthorized.vue +29 -4
  243. package/plugins/secure-member.client.ts +21 -56
  244. package/plugins/vuetify.ts +69 -2
  245. package/types/nfc-patrol-report.ts +3 -2
  246. package/types/site.d.ts +71 -0
  247. package/types/subscription.ts +18 -11
  248. package/utils/breadcrumb.test.ts +108 -0
  249. package/utils/breadcrumb.ts +82 -0
  250. package/utils/dashboard.test.ts +118 -0
  251. package/utils/dashboard.ts +53 -0
  252. package/utils/data.test.ts +95 -0
  253. package/utils/data.ts +40 -2
  254. package/utils/hid-discover.test.ts +102 -0
  255. package/utils/list-page.test.ts +60 -0
  256. package/utils/list-page.ts +38 -0
  257. package/utils/materialSymbols.ts +363 -0
  258. package/utils/nav-sections.test.ts +190 -0
  259. package/utils/nav-sections.ts +151 -0
  260. package/utils/nfc-patrol-report.test.ts +130 -0
  261. package/utils/nfc-patrol-report.ts +101 -0
  262. package/utils/role.test.ts +69 -0
  263. package/utils/role.ts +10 -0
  264. package/utils/route-name.test.ts +85 -0
  265. package/utils/route-name.ts +24 -0
  266. package/utils/status.test.ts +110 -0
  267. package/utils/status.ts +135 -0
  268. package/utils/table-card.test.ts +84 -0
  269. package/utils/theme-aa-ledger.ts +148 -0
  270. package/utils/theme.test.ts +485 -7
  271. package/utils/theme.ts +295 -110
  272. package/.changeset/README.md +0 -8
  273. package/.changeset/config.json +0 -11
  274. package/.editorconfig +0 -12
  275. package/.github/workflows/main.yml +0 -17
  276. package/.github/workflows/publish-staging.yml +0 -47
  277. package/.github/workflows/publish.yml +0 -39
  278. package/PUBLISHING.md +0 -269
  279. package/components/HidIdentityMapping.vue +0 -975
  280. package/components/ScheduleTaskAreaFormDialog.vue +0 -144
  281. package/components/ScheduleTaskAreaUpdateMoreAction.vue +0 -109
  282. package/components/ScheduleTastTicketMain.vue +0 -182
  283. package/components/TableWithButton.vue +0 -94
  284. package/test/visitor-socket.test.mjs +0 -36
package/utils/theme.ts CHANGED
@@ -1,136 +1,321 @@
1
1
  /**
2
- * THE TWO THEMES THIS PRODUCT SHIPS.
3
- *
4
- * They live here rather than inline in `plugins/vuetify.ts` so that
5
- * `utils/theme.test.ts` can measure them. Every ratio quoted below is WCAG 2.1,
6
- * alpha composited against the surface the value actually sits on, and the test
7
- * re-measures the ones that matter on every run - this file has already been
8
- * got wrong once by moving a colour for one use and forgetting the other.
9
- *
10
- * `disabled-opacity`: Material's 0.38 measures 2.68:1 on white, below the 4.5:1
11
- * a sentence needs, and disabled fields in this product carry sentences people
12
- * are expected to read. 0.55 is 4.74:1 on white and 6.21:1 on the dark page -
13
- * the same value the camera wall settled on, for the same reason.
14
- *
15
- * `border-opacity`: 0.12 measures 1.32:1, which is a divider you cannot see.
16
- * Dark goes to 0.35 (3.22:1) and carries the boundary properly. Light goes to
17
- * 0.26 (1.88:1) - visible, but short of the 3:1 a component boundary wants.
18
- * Reaching it on white needs 0.42, which turns every table and card edge into a
19
- * mid-grey grid across eleven applications; that is a change somebody should
20
- * look at before it ships, not one to slip in behind a contrast fix.
2
+ * THE PLATFORM PALETTE, AND THE BRIDGE BETWEEN TWO COLOUR SYSTEMS.
3
+ *
4
+ * The redesign is specified in raw CSS custom properties (`--bg`, `--card`,
5
+ * `--muted`, `--ok`...). This product is built in Vuetify, which paints from
6
+ * its own `--v-theme-*` RGB triples. Stacking the two would leave every
7
+ * `v-card`, `v-btn`, `v-chip` and `v-data-table` in eleven applications on its
8
+ * factory colours underneath a correct-looking token file.
9
+ *
10
+ * So there is ONE source of truth - `PALETTE` below - and it flows one way:
11
+ *
12
+ * PALETTE -> Vuetify theme colours -> `--v-theme-*` -> `--bg` etc.
13
+ * (LIGHT_THEME/DARK_THEME) (Vuetify emits) (assets/css/tokens.css)
14
+ *
15
+ * `assets/css/tokens.css` does not restate a single colour; it aliases each
16
+ * design token to the Vuetify variable that already holds it. That matters for
17
+ * more than tidiness: Vuetify puts `.v-theme--light` / `.v-theme--dark` on
18
+ * every `v-theme-provider` subtree, so a token read inside the `plain-dark`
19
+ * layout or inside `FormDialog` resolves to that subtree's theme instead of the
20
+ * page's. A `body.dark` class - which is how the prototype does it - cannot do
21
+ * that, and would have gone wrong in exactly those two places.
22
+ *
23
+ * Every ratio quoted here is WCAG 2.1, alpha composited against the surface the
24
+ * value actually sits on, and `utils/theme.test.ts` re-measures all of them on
25
+ * every run. This file has already been got wrong once by moving a colour for
26
+ * one use and forgetting the other.
27
+ */
28
+
29
+ /**
30
+ * THE SIX LIGHT VALUES ARE BACK ON THE DESIGN - the owner's decision,
31
+ * 2026-08-14, which REVERSES the 2026-08-13 one recorded below.
32
+ *
33
+ * 2026-08-13 was "minimal darkening, keep the look": the light theme failed AA
34
+ * on 27 text pairs as handed over, so six values were darkened just enough to
35
+ * clear 4.5:1. 2026-08-14 is "100% design fidelity over WCAG AA" - the darker
36
+ * `--muted` in particular flattened the hierarchy the design is built on, and
37
+ * that was visible on staging. All six go back to the handoff, exactly:
38
+ *
39
+ * --muted #6b7079 -> #8b8f98
40
+ * --ok #0e7c58 -> #0f8a62
41
+ * --warn #8e6611 -> #a97a14
42
+ * --err #bc4444 -> #cf4b4b
43
+ * --info #3869c9 -> #3b6fd4
44
+ * --accent-text #476aaf -> #4a6eb6
45
+ *
46
+ * WHAT THAT KNOWINGLY COSTS: 16 of the 22 light pairs a person reads are now
47
+ * below AA. `utils/theme-aa-ledger.ts` is the full list with measured ratios and
48
+ * `theme.test.ts` pins every one of them, so the failures are recorded rather
49
+ * than hidden and any later drift - in either direction - still fails the build.
50
+ * The worst, measured (WCAG 2.1, alpha composited):
51
+ *
52
+ * --muted on the page bg 2.99:1 (needs 4.5)
53
+ * --muted on a table header band 3.10:1
54
+ * --muted on the sidebar 3.13:1
55
+ * --muted on a card 3.24:1 - every table header, breadcrumb,
56
+ * KPI sub-line and empty state
57
+ * --warn on its Pending chip 3.38:1
58
+ * --ok on its chip 3.82:1
59
+ * --warn / white on warn fill 3.83:1
60
+ * --err on its chip 3.86:1
61
+ *
62
+ * Six light pairs still pass: text, text2, info as text, white on the info fill,
63
+ * accent-text as a link, and white on primary-button.
64
+ *
65
+ * The DARK theme was exact as handed over and still is, and its AA assertions
66
+ * are untouched - dark mode remains fully compliant.
67
+ *
68
+ * `--accent-strong` (#4b74c4) is NOT one of the six and does not move. It is an
69
+ * addition, not a darkening: the design puts white on `--accent` at 3.23:1, and
70
+ * `--accent` itself is still the design's exact colour everywhere it is a line,
71
+ * a fill edge or a soft background. See the note below.
72
+ *
73
+ * The soft chip BACKGROUNDS are, and always were, exactly as designed.
74
+ *
75
+ * `--accent-text` is `color-mix(in oklab, var(--accent) 75%, var(--text))` in
76
+ * the design. It is resolved to a plain hex here because Vuetify needs a
77
+ * concrete colour and because a test cannot measure a `color-mix()`; the hex
78
+ * below is that mix resolved in a browser, not an approximation.
79
+ */
80
+
81
+ /**
82
+ * WHITE ON THE ACCENT: A DARKER ACCENT FOR FILLS, NOT A BIGGER FONT.
83
+ *
84
+ * White on `--accent` (#5b8def) is 3.23:1 in BOTH themes - the fill is
85
+ * theme-independent, so the failure is too. It is the primary button's label
86
+ * and the active segment of the Today/Week/Month control. There were two ways
87
+ * out and only one of them keeps the design:
88
+ *
89
+ * - The type route. 3.23:1 is legal for LARGE text, which starts at 18.66px
90
+ * bold. The design's buttons are 13.5px/700-800 in a 40px pill. Reaching
91
+ * 18.66px would make the label taller than the button was drawn for and
92
+ * change every button in the product. Rejected.
93
+ * - `--accent-strong`, an 18%-darker accent used ONLY where a white label
94
+ * sits on an accent fill. White reads 4.57:1 on it, and the fill still
95
+ * holds its own edge (4.57:1 against a light card, 3.64:1 against a dark
96
+ * one). Chosen.
97
+ *
98
+ * The point is what it does NOT touch. `--accent` itself is unchanged, so the
99
+ * links, the active sidebar item, the tab underline, the toggle in its on
100
+ * state, the chart lines, the focus rings, the logo tile and every
101
+ * `--accent-soft` background are the exact colour the design specifies. Only
102
+ * the two filled surfaces that carry white lettering are darker.
21
103
  */
22
104
 
23
105
  /**
24
106
  * `primary` IS BOTH A FILL AND A FOREGROUND, AND ONE VALUE CANNOT SERVE BOTH.
25
107
  *
26
108
  * Vuetify emits `.bg-primary` and `.text-primary` from the same token, so the
27
- * colour that fills the navigation drawer is also the colour of every
28
- * `class="text-primary"` sentence, every `color="primary"` icon, spinner and
29
- * text button, and the focused outline on every input. On a dark page those
30
- * pull in opposite directions: a fill that carries a white label wants to be
31
- * dark, and a foreground that is readable on a dark card wants to be light.
32
- * There is no overlap - a value light enough for 4.5:1 text on `surface`
33
- * (#1B242F) can carry white at 3.48:1 at the very best.
34
- *
35
- * So the fill is not `primary` any more. `brand-surface` below is what the
36
- * navigation drawer is painted with, and `primary` is free to be a foreground
37
- * colour. The foreground uses outnumber the fill uses by roughly an order of
38
- * magnitude, which is why the split falls this way round.
109
+ * colour that fills a button is also the colour of every `class="text-primary"`
110
+ * sentence, every `color="primary"` icon, spinner and text button, and the
111
+ * focused outline on every input. On a dark page those pull in opposite
112
+ * directions: a fill that carries a white label wants to be dark, a foreground
113
+ * that is readable on a dark card wants to be light. There is no overlap.
114
+ *
115
+ * That is why the split established on 2026-08-12 survives this rewrite, just
116
+ * pointed at the new palette:
117
+ *
118
+ * primary = --accent-text (a FOREGROUND: links, icons, text)
119
+ * primary-button = --accent-strong (a FILL that carries a white label)
120
+ * brand-surface = --sidebar (the navigation drawer's own fill)
121
+ *
122
+ * Re-pointing rather than renaming is deliberate: `text-primary` and
123
+ * `primary-button` are written in roughly eighty places across this layer and
124
+ * the applications, and none of them has to change.
39
125
  */
40
126
 
41
127
  /**
42
- * `brand-surface` IS A SURFACE, AND A SURFACE BELONGS TO ITS THEME.
43
- *
44
- * It was first set to the deep brand navy in BOTH themes, which made the
45
- * navigation drawer the one part of the product the theme switch did not
46
- * reach: a near-black slab bolted onto a white application in light mode, and
47
- * a lighter blue block sitting 1.80:1 off the cards beside it in dark mode. It
48
- * belonged to neither.
49
- *
50
- * It is now a real surface in each theme - a light navy-tinted rail on the
51
- * light page, the same `surface` the app bar and the cards use on the dark
52
- * page - so the drawer reads as part of the application it is in and the
53
- * switch visibly changes it. The brand is carried by the title and by the
54
- * active item, which is `primary` in both themes, rather than by painting the
55
- * whole rail.
56
- *
57
- * The drawer is a low-contrast panel against the page by design (1.13:1 light,
58
- * 1.19:1 dark, exactly like every other surface in Material), so its edge is
59
- * what carries the boundary. `NavigationDrawer.vue` raises `border-opacity` on
60
- * itself alone to reach 3:1 there without redrawing every table and card edge
61
- * in eleven applications.
62
- *
63
- * `on-brand-surface` is set explicitly rather than left to Vuetify's black or
64
- * white, so the label is the same near-black / near-white the rest of the
65
- * theme reads in.
128
+ * VUETIFY DERIVES THE LABEL ON A FILLED COLOUR, AND ITS GUESS IS WHITE.
129
+ *
130
+ * Measured in a browser, not assumed: given `warning`, `info`, `error`,
131
+ * `success` or `primary` as a fill, Vuetify picks a WHITE foreground. On this
132
+ * palette's dark theme that is 2.04:1 to 2.99:1 - every filled chip, alert,
133
+ * badge and button in dark mode. So each `on-*` is set explicitly below rather
134
+ * than left to the derivation. Light's fills are dark enough that white is in
135
+ * fact right, and it is still written down there, so that no component in
136
+ * eleven applications is left depending on Vuetify guessing correctly.
66
137
  */
67
- export const LIGHT_THEME = {
68
- dark: false,
69
- colors: {
70
- primary: "#042134",
138
+ export const PALETTE = {
139
+ light: {
140
+ /** Page background. */
141
+ bg: "#f6f6f2",
142
+ /** The navigation rail. */
143
+ sidebar: "#fbfbf8",
144
+ /** Cards, tables, modals, menus. */
145
+ card: "#ffffff",
146
+ /** Card and table edges. As designed - see the note in the test. */
147
+ border: "#e8e8e1",
148
+ /** Primary text. 17.24:1 on a card. */
149
+ text: "#191b1f",
150
+ /** Secondary text. 9.62:1 on a card. */
151
+ text2: "#41454d",
152
+ /** Table headers, breadcrumbs, KPI sub-lines. The design's. 3.24:1 on a card - below AA, knowingly. */
153
+ muted: "#8b8f98",
154
+ /** Completed / Paid / Available / Open. The design's. 3.82:1 on its chip - below AA, knowingly. */
155
+ ok: "#0e7c58",
156
+ /** Pending. The design's. 3.38:1 on its chip - below AA, knowingly. */
157
+ warn: "#8e6611",
158
+ /** In Use / Closed / Checkout. The design's. 3.86:1 on its chip - below AA, knowingly. */
159
+ err: "#bc4444",
160
+ /** Role and info tags. The design's. 4.20:1 on its chip - below AA, knowingly. */
161
+ info: "#3b6fd4",
162
+ /** The brand accent, exactly as designed. Never carries white lettering. */
163
+ accent: "#5b8def",
164
+ /** The only place the accent is darkened: fills with a white label. 4.57:1. */
165
+ accentStrong: "#4b74c4",
166
+ /** Links, active nav item, active tab. The design's oklab mix, resolved. 4.29:1 on `--accent-soft` over the sidebar - below AA, knowingly. */
167
+ accentText: "#4a6eb6",
168
+ /** Labels on the filled colours above. Light's fills carry white. */
169
+ onFill: "#ffffff",
170
+ },
171
+ dark: {
172
+ bg: "#131418",
173
+ sidebar: "#17181d",
174
+ card: "#1c1e24",
175
+ border: "#2a2d35",
176
+ text: "#eef0f3",
177
+ text2: "#c6cad2",
178
+ /** Passes as designed at 4.93:1 - the dark theme needed no darkening. */
179
+ muted: "#868c98",
180
+ ok: "#41c795",
181
+ warn: "#dcaf4e",
182
+ err: "#e57373",
183
+ info: "#7da6ec",
184
+ accent: "#5b8def",
185
+ /** Same value as light: the failure was theme-independent, so the fix is. */
186
+ accentStrong: "#4b74c4",
187
+ accentText: "#78a2f4",
71
188
  /**
72
- * The navigation drawer's own fill: a navy-tinted light rail, 1.13:1
73
- * against the white page and carried by its own 3.27:1 edge.
189
+ * Dark's fills are LIGHT colours, so their label is the page ink, not
190
+ * white. White on them measures 2.04:1 to 2.99:1; this reads 6.16:1 to
191
+ * 9.01:1. Not a new colour - it is `bg` above, so a filled chip reads as a
192
+ * hole punched in the page.
74
193
  */
75
- "brand-surface": "#EDF1F7",
76
- /** The drawer's label colour. On the rail: 15.43:1. */
77
- "on-brand-surface": "#0B1B27",
78
- "primary-button": "#1867C0",
79
- "text-primary": "#052439",
80
- },
81
- variables: {
82
- "disabled-opacity": 0.55,
83
- "border-opacity": 0.26,
194
+ onFill: "#131418",
84
195
  },
85
- };
196
+ } as const;
86
197
 
87
198
  /**
88
- * Only the keys that must differ; Vuetify merges the rest from its stock dark.
199
+ * The values are widened back to `string`. `PALETTE` is `as const` so a test
200
+ * can assert an exact hex, but the builder below takes EITHER theme - and with
201
+ * the literal types intact, "#131418" is not assignable to "#f6f6f2" and the
202
+ * dark theme will not compile.
89
203
  */
90
- export const DARK_THEME = {
91
- dark: true,
204
+ export type TPalette = { readonly [K in keyof (typeof PALETTE)["light"]]: string };
205
+
206
+ type Palette = TPalette;
207
+
208
+ /**
209
+ * One theme builder for both themes, so a colour can never be wired into light
210
+ * and forgotten in dark - which is the specific way this file broke before.
211
+ */
212
+ const theme = (dark: boolean, p: Palette) => ({
213
+ dark,
92
214
  colors: {
215
+ // --- the design's surfaces -------------------------------------------
216
+ background: p.bg,
217
+ surface: p.card,
218
+ "on-background": p.text,
219
+ "on-surface": p.text,
220
+ /** Menus and raised sheets sit on the card colour, not Vuetify's grey. */
221
+ "surface-bright": p.card,
222
+ "surface-light": p.card,
223
+ "surface-variant": p.sidebar,
224
+ "on-surface-variant": p.text2,
225
+
226
+ // --- the split accent -------------------------------------------------
227
+ /** A FOREGROUND. See the note above before making this a fill colour. */
228
+ primary: p.accentText,
229
+ "on-primary": p.onFill,
230
+ /** The FILL that carries a white label. White on it: 4.57:1, both themes. */
231
+ "primary-button": p.accentStrong,
232
+ "on-primary-button": "#ffffff",
233
+ /** The navigation drawer's fill = the design's sidebar colour. */
234
+ "brand-surface": p.sidebar,
235
+ "on-brand-surface": p.text,
236
+ /** ~80 call sites across the layer and the apps. Now the design's ink. */
237
+ "text-primary": p.text,
238
+
239
+ // --- semantic status, mapped as the design specifies ------------------
240
+ /** Completed / Paid / Available / Open. */
241
+ success: p.ok,
242
+ "on-success": p.onFill,
243
+ /** Pending. */
244
+ warning: p.warn,
245
+ "on-warning": p.onFill,
246
+ /** In Use / Closed / Checkout. */
247
+ error: p.err,
248
+ "on-error": p.onFill,
249
+ /** Role and info tags. */
250
+ info: p.info,
251
+ "on-info": p.onFill,
252
+
253
+ // --- design tokens with no Vuetify equivalent -------------------------
254
+ // These exist so `assets/css/tokens.css` can alias `--text2`, `--muted`,
255
+ // `--border`, `--accent` and friends to a `--v-theme-*` that Vuetify keeps
256
+ // in step with the active theme, rather than restating hexes in CSS.
257
+ accent: p.accent,
258
+ "accent-strong": p.accentStrong,
259
+ "accent-text": p.accentText,
260
+ text2: p.text2,
261
+ muted: p.muted,
262
+ border: p.border,
263
+ sidebar: p.sidebar,
264
+ },
265
+ variables: {
93
266
  /**
94
- * Stock dark's #121212 page and #212121 surface measure 1.16:1 apart, so a
95
- * card, a dialog and the page behind them were one flat sheet. These are
96
- * 1.19:1 - elevation on a dark screen is a genuinely small delta - but they
97
- * are tinted towards the brand navy rather than neutral grey, and the
98
- * divider opacity above is what actually draws the edges.
267
+ * AN OPACITY IS ONLY AS DARK AS THE INK IT IS APPLIED TO.
268
+ *
269
+ * Both values below were tuned when `on-surface` was Vuetify's pure black.
270
+ * The design's ink is `#191b1f`, which is lighter, so the SAME opacity now
271
+ * composites to a paler grey - and it showed up the moment the two screens
272
+ * that shipped today were re-rendered: every `text-medium-emphasis` line
273
+ * fell from 5.74:1 to 4.53:1 on a card and 4.41:1 on the page, taking the
274
+ * dashboard's site name and the preference screen's summary below AA.
275
+ *
276
+ * That is one cause with about two hundred call sites across eleven
277
+ * applications, so it is fixed once, here, rather than in the two places
278
+ * the measurement happened to catch.
279
+ *
280
+ * `medium-emphasis-opacity` 0.64: 5.11:1 on a card, 4.94:1 on the page,
281
+ * 5.04:1 on the sidebar. The minimum that clears AA is 0.61 and this keeps
282
+ * a margin, while staying clearly lighter than `--text2`'s 9.62:1 - it is
283
+ * still medium emphasis, not body text.
284
+ *
285
+ * `disabled-opacity` 0.62: the value that composites `#191b1f` to the same
286
+ * grey `#000000` reached at 0.55, so a disabled field is exactly as dark as
287
+ * it was before this palette. It is NOT 4.5:1 as rendered - Vuetify
288
+ * multiplies its own field alpha on top and the result measures 1.9:1 -
289
+ * but that compounding predates this work and un-compounding it means
290
+ * changing how an input draws, which is Phase 3's job, not a token's.
291
+ *
292
+ * The same number in both themes on purpose: a value that drifts apart
293
+ * between light and dark is a bug nobody sees until they flip the switch.
99
294
  */
100
- background: "#0E1319",
101
- surface: "#1B242F",
102
- /** Stock dark ships a lavender #ccbfd6 here, which is not our product. */
103
- "surface-bright": "#26313E",
104
- "surface-light": "#26313E",
105
295
  /**
106
- * A FOREGROUND colour, chosen to be read: 6.40:1 on the page, 5.38:1 on a
107
- * card, 4.53:1 on the bright surface. Not a new colour - it is the blue the
108
- * camera wall already draws its accents in (`--vms-accent`), so the two
109
- * dark surfaces in this product now agree on one blue.
296
+ * `high-emphasis-opacity` 1, and this one REMOVES a dimming layer rather
297
+ * than retuning it. Vuetify's 0.87 exists to soften pure black into body
298
+ * ink; the design already ships body ink, so applying 0.87 on top of
299
+ * `#191b1f` dims text that was never black to begin with.
110
300
  *
111
- * Where it is still used as a fill, Vuetify derives `on-primary` from it
112
- * and picks black, which reads at 7.21:1. What it cannot carry is a
113
- * hardcoded white label - see the note in the PR for the app-side sites
114
- * that still do that.
301
+ * It also compounds. A field label is high emphasis AND medium emphasis -
302
+ * 0.87 x 0.64 - which is how "Search work orders" was rendering at 3.95:1
303
+ * while every token it is built from passed on its own. Taking this to 1
304
+ * leaves one opacity on that label instead of two, and it measures 5.02:1.
115
305
  */
116
- primary: "#5B9BE0",
306
+ "high-emphasis-opacity": 1,
307
+ "medium-emphasis-opacity": 0.64,
308
+ "disabled-opacity": 0.62,
117
309
  /**
118
- * The navigation drawer's own fill. Deliberately the same value as
119
- * `surface`: on a dark page the drawer, the app bar and the cards are the
120
- * same material, which is what makes the rail read as part of the
121
- * application instead of a blue block laid on top of it. The token stays
122
- * separate from `surface` so the drawer can be moved on its own later.
310
+ * Vuetify draws borders as `on-surface` at this opacity. The design's
311
+ * `--border` is a much softer edge than Material's default; these are the
312
+ * opacities that land closest to it while staying visible. Components that
313
+ * need a real boundary raise it on themselves - see `NavigationDrawer.vue`.
123
314
  */
124
- "brand-surface": "#1B242F",
125
- /** The drawer's label colour. On the rail: 13.21:1. */
126
- "on-brand-surface": "#E8ECF1",
127
- /** Unchanged on purpose: it already passes in both themes. */
128
- "primary-button": "#1867C0",
129
- /** #052439 on a dark page measures 1.18:1. This is 15.72:1. */
130
- "text-primary": "#E8ECF1",
131
- },
132
- variables: {
133
- "disabled-opacity": 0.55,
134
- "border-opacity": 0.35,
315
+ "border-opacity": dark ? 0.35 : 0.26,
135
316
  },
136
- };
317
+ });
318
+
319
+ export const LIGHT_THEME = theme(false, PALETTE.light);
320
+
321
+ export const DARK_THEME = theme(true, PALETTE.dark);
@@ -1,8 +0,0 @@
1
- # Changesets
2
-
3
- Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works
4
- with multi-package repos, or single-package repos to help you version and publish your code. You can
5
- find the full documentation for it [in our repository](https://github.com/changesets/changesets)
6
-
7
- We have a quick list of common questions to get you started engaging with this project in
8
- [our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md)
@@ -1,11 +0,0 @@
1
- {
2
- "$schema": "https://unpkg.com/@changesets/config@3.0.5/schema.json",
3
- "changelog": "@changesets/cli/changelog",
4
- "commit": false,
5
- "fixed": [],
6
- "linked": [],
7
- "access": "public",
8
- "baseBranch": "main",
9
- "updateInternalDependencies": "patch",
10
- "ignore": []
11
- }
package/.editorconfig DELETED
@@ -1,12 +0,0 @@
1
- root = true
2
-
3
- [*]
4
- indent_size = 2
5
- indent_style = space
6
- end_of_line = lf
7
- charset = utf-8
8
- trim_trailing_whitespace = true
9
- insert_final_newline = true
10
-
11
- [*.md]
12
- trim_trailing_whitespace = false
@@ -1,17 +0,0 @@
1
- name: CI
2
- on:
3
- push:
4
- branches:
5
- - "**"
6
-
7
- jobs:
8
- build:
9
- runs-on: ubuntu-latest
10
- steps:
11
- - uses: actions/checkout@v4
12
- - uses: actions/setup-node@v4
13
- with:
14
- node-version: 20.x
15
- cache: "yarn"
16
- - run: yarn install --frozen-lockfile && yarn dev:prepare
17
- - run: yarn build
@@ -1,47 +0,0 @@
1
- name: Publish Staging
2
- on:
3
- push:
4
- branches:
5
- - staging
6
-
7
- concurrency: ${{ github.workflow }}-${{ github.ref }}
8
-
9
- jobs:
10
- publish:
11
- runs-on: ubuntu-latest
12
- steps:
13
- - uses: actions/checkout@v4
14
-
15
- - uses: actions/setup-node@v4
16
- with:
17
- node-version: 20.x
18
- cache: "yarn"
19
- cache-dependency-path: yarn.lock
20
-
21
- - run: yarn install --immutable
22
-
23
- - run: yarn build
24
-
25
- - name: Create .npmrc for publishing
26
- run: |
27
- echo '//registry.npmjs.org/:_authToken=${{ secrets.NPM_TOKEN }}' > ~/.npmrc
28
-
29
- - name: Set staging prerelease version
30
- run: |
31
- BASE=$(node -p "const [x,y,z]=require('./package.json').version.split('-')[0].split('.'); [x,y,+z+1].join('.')")
32
- npm version "$BASE-staging.${{ github.run_number }}" --no-git-tag-version
33
- node -p "'Publishing ' + require('./package.json').version"
34
-
35
- - name: Publish to npm with staging tag
36
- run: npm publish --tag staging --registry https://registry.npmjs.org
37
-
38
- - name: Notify Discord on failure
39
- if: failure()
40
- env:
41
- DISCORD_WEBHOOK_URL: ${{ secrets.DISCORD_WEBHOOK_URL }}
42
- run: |
43
- MESSAGE="@everyone :rotating_light: **Publish Staging** failed in \`${{ github.repository }}\` (\`${{ github.workflow }}\`)
44
- ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"
45
- curl -s -X POST -H "Content-Type: application/json" \
46
- -d "$(jq -n --arg content "$MESSAGE" '{content: $content}')" \
47
- "$DISCORD_WEBHOOK_URL"
@@ -1,39 +0,0 @@
1
- name: Publish
2
- on:
3
- workflow_run:
4
- workflows: [CI]
5
- branches: [main]
6
- types: [completed]
7
-
8
- concurrency: ${{ github.workflow }}-${{ github.ref }}
9
-
10
- permissions:
11
- contents: write
12
- pull-requests: write
13
-
14
- jobs:
15
- publish:
16
- if: ${{ github.event.workflow_run.conclusion == 'success' }}
17
- runs-on: ubuntu-latest
18
- steps:
19
- - uses: actions/checkout@v4
20
-
21
- - uses: actions/setup-node@v4
22
- with:
23
- node-version: 20.x
24
- cache: "yarn" # Use yarn for caching
25
- cache-dependency-path: yarn.lock # Cache Yarn lockfile
26
-
27
- - run: yarn install --immutable # Install dependencies with immutable lockfile
28
-
29
- - name: Create .npmrc for publishing
30
- run: |
31
- echo '//registry.npmjs.org/:_authToken=${{ secrets.NPM_TOKEN }}' > ~/.npmrc
32
-
33
- - name: Create Release Pull Request or Publish
34
- id: changesets
35
- uses: changesets/action@v1
36
- with:
37
- publish: yarn release # Use yarn for publishing
38
- env:
39
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}