@7365admin1/layer-common 3.2.1-staging.73 → 3.2.1-staging.74

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.
@@ -0,0 +1,15 @@
1
+ ---
2
+ "@7365admin1/layer-common": patch
3
+ ---
4
+
5
+ Dashboard and filled-primary surfaces now follow the theme in dark mode.
6
+
7
+ - `DashboardMain` painted its panels, list rows and text with fixed hex values,
8
+ so the ACTIVE PATROL / STAFF STATUS / Feedbacks cards rendered white on the
9
+ dark page and the heading and KPI figures were dark grey on near-black
10
+ (1.19:1). They now use the theme's surface and foreground tokens.
11
+ - `DARK_THEME` had no `on-primary`, so Vuetify derived white for every filled
12
+ `primary` button, chip, alert, tab bar and snackbar - 2.91:1. It is now set
13
+ explicitly and measures 6.40:1.
14
+
15
+ The print / PDF / Word export styles are unchanged and stay ink-on-white.
@@ -207,10 +207,10 @@
207
207
  class="d-flex flex-column flex-md-row align-md-start justify-space-between ga-5 mb-6"
208
208
  >
209
209
  <div>
210
- <h1 class="text-h5 font-weight-black text-blue-grey-darken-4 mb-0">
210
+ <h1 class="text-h5 font-weight-black text-high-emphasis mb-0">
211
211
  {{ dashboardTitle }}
212
212
  </h1>
213
- <div class="d-flex align-center text-blue-grey ga-1 mt-2 text-caption">
213
+ <div class="d-flex align-center text-medium-emphasis ga-1 mt-2 text-caption">
214
214
  <v-icon icon="mdi-map-marker" size="16" />
215
215
  <span>{{ currentSiteName || "Selected site" }}</span>
216
216
  </div>
@@ -222,7 +222,6 @@
222
222
  >
223
223
  <v-btn
224
224
  variant="outlined"
225
- color="blue-grey-darken-1"
226
225
  class="text-none font-weight-bold"
227
226
  prepend-icon="mdi-tune-variant"
228
227
  @click="openCustomizeDialog"
@@ -235,7 +234,6 @@
235
234
  <v-btn
236
235
  v-bind="menuProps"
237
236
  variant="outlined"
238
- color="blue-grey-darken-1"
239
237
  class="text-none font-weight-bold"
240
238
  append-icon="mdi-chevron-down"
241
239
  :loading="isExporting"
@@ -257,8 +255,8 @@
257
255
  density="comfortable"
258
256
  divided
259
257
  variant="outlined"
260
- class="bg-white"
261
- style="border-radius: 8px; border-color: #d9e1e8"
258
+ class="bg-surface"
259
+ style="border-radius: 8px"
262
260
  >
263
261
  <v-btn
264
262
  v-for="range in rangeOptions"
@@ -269,11 +267,11 @@
269
267
  :style="
270
268
  selectedRange === range.value
271
269
  ? {
272
- backgroundColor: '#e2e8f0',
273
- color: '#0b2f47',
270
+ backgroundColor: 'rgba(var(--v-theme-on-surface), 0.12)',
271
+ color: 'rgb(var(--v-theme-on-surface))',
274
272
  fontWeight: '600',
275
273
  }
276
- : { backgroundColor: 'transparent', color: '#64748b' }
274
+ : { backgroundColor: 'transparent', color: 'rgba(var(--v-theme-on-surface), var(--v-medium-emphasis-opacity))' }
277
275
  "
278
276
  >
279
277
  {{ range.label }}
@@ -363,7 +361,7 @@
363
361
 
364
362
  <div class="mt-6 w-100">
365
363
  <p
366
- class="text-caption font-weight-black text-uppercase text-blue-grey-darken-2 mb-3"
364
+ class="text-caption font-weight-black text-uppercase text-medium-emphasis mb-3"
367
365
  >
368
366
  {{ card.title }}
369
367
  </p>
@@ -376,21 +374,21 @@
376
374
  <div
377
375
  v-for="item in card.raw.items"
378
376
  :key="item._id || item.name"
379
- class="d-flex justify-space-between text-body-2 text-blue-grey-darken-4 font-weight-medium"
377
+ class="d-flex justify-space-between text-body-2 text-high-emphasis font-weight-medium"
380
378
  >
381
379
  <span>{{ item.name }}</span>
382
- <span class="text-blue-grey">Balance: {{ item.qty }}</span>
380
+ <span class="text-medium-emphasis">Balance: {{ item.qty }}</span>
383
381
  </div>
384
382
  </div>
385
383
  </template>
386
384
  <template v-else>
387
385
  <div class="d-flex align-center mb-3" style="min-height: 31px">
388
- <span class="text-h5 font-weight-black text-blue-grey-darken-4">
386
+ <span class="text-h5 font-weight-black text-high-emphasis">
389
387
  {{ card.value }}
390
388
  </span>
391
389
  </div>
392
390
  <div class="d-flex align-center flex-wrap ga-2">
393
- <span v-if="card.meta" class="text-caption text-blue-grey">{{
391
+ <span v-if="card.meta" class="text-caption text-medium-emphasis">{{
394
392
  card.meta
395
393
  }}</span>
396
394
  <v-chip
@@ -426,7 +424,7 @@
426
424
  class="px-5 pt-4 pb-3 mt-3"
427
425
  >
428
426
  <div class="d-flex align-center justify-space-between">
429
- <h2 class="text-body-1 font-weight-black text-blue-grey-darken-4 mb-0">
427
+ <h2 class="text-body-1 font-weight-black text-high-emphasis mb-0">
430
428
  {{ activityTitle }}
431
429
  </h2>
432
430
  </div>
@@ -444,7 +442,7 @@
444
442
  :y1="tick"
445
443
  :x2="chartWidth - 20"
446
444
  :y2="tick"
447
- style="stroke: #e9eef2; stroke-width: 1"
445
+ style="stroke: rgba(var(--v-theme-on-surface), 0.12); stroke-width: 1"
448
446
  />
449
447
  <text
450
448
  v-for="label in chartYLabels"
@@ -453,7 +451,7 @@
453
451
  :y="label.y"
454
452
  text-anchor="end"
455
453
  alignment-baseline="middle"
456
- style="fill: #7e8b96; font-size: 11px; font-weight: 500"
454
+ style="fill: rgba(var(--v-theme-on-surface), var(--v-medium-emphasis-opacity)); font-size: 11px; font-weight: 500"
457
455
  >
458
456
  {{ label.text }}
459
457
  </text>
@@ -483,7 +481,7 @@
483
481
  :cx="point.x"
484
482
  :cy="point.y"
485
483
  r="4"
486
- style="fill: #ff9b9b; stroke: #fff; stroke-width: 2"
484
+ style="fill: #ff9b9b; stroke: rgb(var(--v-theme-surface)); stroke-width: 2"
487
485
  />
488
486
  <circle
489
487
  v-for="point in secondaryDots"
@@ -491,7 +489,7 @@
491
489
  :cx="point.x"
492
490
  :cy="point.y"
493
491
  r="4"
494
- style="fill: #e53935; stroke: #fff; stroke-width: 2"
492
+ style="fill: #e53935; stroke: rgb(var(--v-theme-surface)); stroke-width: 2"
495
493
  />
496
494
  <text
497
495
  v-for="label in chartAxisLabels"
@@ -499,7 +497,7 @@
499
497
  :x="label.x"
500
498
  y="246"
501
499
  text-anchor="middle"
502
- style="fill: #7e8b96; font-size: 12px; font-weight: 600"
500
+ style="fill: rgba(var(--v-theme-on-surface), var(--v-medium-emphasis-opacity)); font-size: 12px; font-weight: 600"
503
501
  >
504
502
  {{ label.text }}
505
503
  </text>
@@ -507,7 +505,7 @@
507
505
 
508
506
  <div class="d-flex align-center justify-center ga-5 mt-2">
509
507
  <span
510
- class="d-inline-flex align-center ga-2 text-caption font-weight-bold text-blue-grey"
508
+ class="d-inline-flex align-center ga-2 text-caption font-weight-bold text-medium-emphasis"
511
509
  >
512
510
  <span
513
511
  class="rounded-circle"
@@ -521,7 +519,7 @@
521
519
  {{ primaryLegendLabel }}
522
520
  </span>
523
521
  <span
524
- class="d-inline-flex align-center ga-2 text-caption font-weight-bold text-blue-grey"
522
+ class="d-inline-flex align-center ga-2 text-caption font-weight-bold text-medium-emphasis"
525
523
  >
526
524
  <span
527
525
  class="rounded-circle"
@@ -565,11 +563,11 @@
565
563
  <div class="d-flex align-center flex-wrap ga-3">
566
564
  <div class="flex-grow-1" style="min-width: 0">
567
565
  <div
568
- class="text-body-2 font-weight-bold text-blue-grey-darken-4 text-truncate"
566
+ class="text-body-2 font-weight-bold text-high-emphasis text-truncate"
569
567
  >
570
568
  {{ item.title }}
571
569
  </div>
572
- <div class="text-caption text-blue-grey mt-1 text-truncate">
570
+ <div class="text-caption text-medium-emphasis mt-1 text-truncate">
573
571
  {{ item.subtitle
574
572
  }}<template v-if="item.subtitle && item.time">
575
573
  &bull; </template
@@ -577,7 +575,7 @@
577
575
  </div>
578
576
  </div>
579
577
  <div
580
- class="d-flex align-center ga-2 text-caption text-blue-grey"
578
+ class="d-flex align-center ga-2 text-caption text-medium-emphasis"
581
579
  >
582
580
  <AvatarMain :name="item.person" :size="30" />
583
581
  <span class="text-truncate" style="max-width: 100px">{{
@@ -632,17 +630,16 @@
632
630
  <AvatarMain :name="item.name" :size="32" />
633
631
  <div class="flex-grow-1" style="min-width: 0">
634
632
  <div
635
- class="text-body-2 font-weight-bold text-blue-grey-darken-4 text-truncate"
633
+ class="text-body-2 font-weight-bold text-high-emphasis text-truncate"
636
634
  >
637
635
  {{ item.name }}
638
636
  </div>
639
- <div class="text-caption text-blue-grey mt-1 text-truncate">
637
+ <div class="text-caption text-medium-emphasis mt-1 text-truncate">
640
638
  {{ item.role }}
641
639
  </div>
642
640
  </div>
643
641
  <v-chip
644
642
  size="small"
645
- color="blue-grey-darken-1"
646
643
  variant="tonal"
647
644
  label
648
645
  class="ml-auto flex-shrink-0 font-weight-bold"
@@ -669,10 +666,10 @@
669
666
  class="d-flex align-center justify-space-between"
670
667
  style="
671
668
  padding: 16px 20px;
672
- border: 1px solid #e2e8f0;
669
+ border: 1px solid rgba(var(--v-border-color), var(--v-border-opacity));
673
670
  border-radius: 12px;
674
671
  margin-bottom: 12px;
675
- background-color: #ffffff;
672
+ background-color: rgb(var(--v-theme-surface-bright));
676
673
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.02);
677
674
  "
678
675
  >
@@ -682,12 +679,12 @@
682
679
  display: block;
683
680
  font-size: 15px;
684
681
  font-weight: 700;
685
- color: #0b2f47;
682
+ color: rgb(var(--v-theme-on-surface));
686
683
  margin-bottom: 4px;
687
684
  "
688
685
  >{{ item.title }}</strong
689
686
  >
690
- <span style="font-size: 13px; color: #94a3b8">{{
687
+ <span style="font-size: 13px; color: rgba(var(--v-theme-on-surface), var(--v-medium-emphasis-opacity))">{{
691
688
  item.subtitle
692
689
  }}</span>
693
690
  </div>
@@ -696,7 +693,7 @@
696
693
  style="flex: 1; display: flex; align-items: center; gap: 12px"
697
694
  >
698
695
  <AvatarMain :name="item.person" :size="36" />
699
- <span style="font-size: 14px; color: #64748b">{{
696
+ <span style="font-size: 14px; color: rgba(var(--v-theme-on-surface), var(--v-medium-emphasis-opacity))">{{
700
697
  item.person
701
698
  }}</span>
702
699
  </div>
@@ -721,9 +718,9 @@
721
718
  variant="outlined"
722
719
  class="text-none font-weight-medium"
723
720
  size="small"
724
- color="#3b82f6"
721
+ color="primary"
725
722
  style="
726
- border-color: #e2e8f0;
723
+ border-color: rgba(var(--v-border-color), var(--v-border-opacity));
727
724
  border-radius: 8px;
728
725
  padding: 0 16px;
729
726
  height: 32px;
@@ -1026,9 +1023,9 @@
1026
1023
  variant="outlined"
1027
1024
  class="text-none font-weight-medium"
1028
1025
  size="small"
1029
- color="#3b82f6"
1026
+ color="primary"
1030
1027
  style="
1031
- border-color: #e2e8f0;
1028
+ border-color: rgba(var(--v-border-color), var(--v-border-opacity));
1032
1029
  border-radius: 8px;
1033
1030
  padding: 0 16px;
1034
1031
  height: 32px;
@@ -1064,10 +1061,10 @@
1064
1061
  class="d-flex align-center justify-space-between"
1065
1062
  style="
1066
1063
  padding: 16px 20px;
1067
- border: 1px solid #e2e8f0;
1064
+ border: 1px solid rgba(var(--v-border-color), var(--v-border-opacity));
1068
1065
  border-radius: 12px;
1069
1066
  margin-bottom: 12px;
1070
- background-color: #ffffff;
1067
+ background-color: rgb(var(--v-theme-surface-bright));
1071
1068
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.02);
1072
1069
  "
1073
1070
  >
@@ -1087,14 +1084,14 @@
1087
1084
  display: block;
1088
1085
  font-size: 15px;
1089
1086
  font-weight: 700;
1090
- color: #0b2f47;
1087
+ color: rgb(var(--v-theme-on-surface));
1091
1088
  margin-bottom: 4px;
1092
1089
  "
1093
1090
  >{{
1094
1091
  item.userName || item.personName || "Unassigned"
1095
1092
  }}</strong
1096
1093
  >
1097
- <span style="font-size: 13px; color: #94a3b8">{{
1094
+ <span style="font-size: 13px; color: rgba(var(--v-theme-on-surface), var(--v-medium-emphasis-opacity))">{{
1098
1095
  item.role || item.designation || "Officer"
1099
1096
  }}</span>
1100
1097
  </div>
@@ -1134,10 +1131,10 @@
1134
1131
  class="d-flex align-center justify-space-between"
1135
1132
  style="
1136
1133
  padding: 16px 20px;
1137
- border: 1px solid #e2e8f0;
1134
+ border: 1px solid rgba(var(--v-border-color), var(--v-border-opacity));
1138
1135
  border-radius: 12px;
1139
1136
  margin-bottom: 12px;
1140
- background-color: #ffffff;
1137
+ background-color: rgb(var(--v-theme-surface-bright));
1141
1138
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.02);
1142
1139
  "
1143
1140
  >
@@ -1147,12 +1144,12 @@
1147
1144
  display: block;
1148
1145
  font-size: 15px;
1149
1146
  font-weight: 700;
1150
- color: #0b2f47;
1147
+ color: rgb(var(--v-theme-on-surface));
1151
1148
  margin-bottom: 4px;
1152
1149
  "
1153
1150
  >{{ item.title }}</strong
1154
1151
  >
1155
- <span style="font-size: 13px; color: #94a3b8">{{
1152
+ <span style="font-size: 13px; color: rgba(var(--v-theme-on-surface), var(--v-medium-emphasis-opacity))">{{
1156
1153
  item.subtitle
1157
1154
  }}</span>
1158
1155
  </div>
@@ -1161,7 +1158,7 @@
1161
1158
  style="flex: 1; display: flex; align-items: center; gap: 12px"
1162
1159
  >
1163
1160
  <AvatarMain :name="item.person" :size="36" />
1164
- <span style="font-size: 14px; color: #64748b">{{
1161
+ <span style="font-size: 14px; color: rgba(var(--v-theme-on-surface), var(--v-medium-emphasis-opacity))">{{
1165
1162
  item.person
1166
1163
  }}</span>
1167
1164
  </div>
@@ -1186,9 +1183,9 @@
1186
1183
  variant="outlined"
1187
1184
  class="text-none font-weight-medium"
1188
1185
  size="small"
1189
- color="#3b82f6"
1186
+ color="primary"
1190
1187
  style="
1191
- border-color: #e2e8f0;
1188
+ border-color: rgba(var(--v-border-color), var(--v-border-opacity));
1192
1189
  border-radius: 8px;
1193
1190
  padding: 0 16px;
1194
1191
  height: 32px;
@@ -1211,10 +1208,10 @@
1211
1208
  class="d-flex align-start justify-space-between pa-6 pb-4 border-b"
1212
1209
  >
1213
1210
  <div>
1214
- <div class="text-h6 font-weight-black text-blue-grey-darken-4">
1211
+ <div class="text-h6 font-weight-black text-high-emphasis">
1215
1212
  Customize Dashboard
1216
1213
  </div>
1217
- <div class="text-caption text-blue-grey mt-1">
1214
+ <div class="text-caption text-medium-emphasis mt-1">
1218
1215
  Select which widgets to display on your dashboard
1219
1216
  </div>
1220
1217
  </div>
@@ -1240,7 +1237,6 @@
1240
1237
  <div class="d-flex justify-end mt-3 mb-1">
1241
1238
  <v-btn
1242
1239
  variant="text"
1243
- color="blue-grey-darken-4"
1244
1240
  size="small"
1245
1241
  class="text-none font-weight-bold"
1246
1242
  prepend-icon="mdi-refresh"
@@ -1262,11 +1258,11 @@
1262
1258
  <div class="d-flex align-center justify-space-between ga-4">
1263
1259
  <div>
1264
1260
  <div
1265
- class="text-body-2 font-weight-black text-blue-grey-darken-4"
1261
+ class="text-body-2 font-weight-black text-high-emphasis"
1266
1262
  >
1267
1263
  {{ widget.title }}
1268
1264
  </div>
1269
- <div class="text-caption text-blue-grey mt-1">
1265
+ <div class="text-caption text-medium-emphasis mt-1">
1270
1266
  {{ widget.description }}
1271
1267
  </div>
1272
1268
  </div>
@@ -1292,7 +1288,7 @@
1292
1288
  <v-card-actions class="pa-6 pt-4 border-t justify-end ga-3">
1293
1289
  <v-btn
1294
1290
  variant="text"
1295
- class="text-none font-weight-bold text-blue-grey"
1291
+ class="text-none font-weight-bold text-medium-emphasis"
1296
1292
  @click="cancelCustomize"
1297
1293
  >
1298
1294
  Cancel
@@ -1320,7 +1316,7 @@
1320
1316
  <v-card-title
1321
1317
  class="d-flex align-center justify-space-between pa-6 pb-3"
1322
1318
  >
1323
- <div class="text-body-1 font-weight-black text-blue-grey-darken-4">
1319
+ <div class="text-body-1 font-weight-black text-high-emphasis">
1324
1320
  Facility Booking
1325
1321
  </div>
1326
1322
  <v-btn
@@ -4185,8 +4181,8 @@ function formatDashboardTime(value?: string) {
4185
4181
  }
4186
4182
 
4187
4183
  .dashboard-card {
4188
- background: #fff;
4189
- border: 1px solid #d9e0e7;
4184
+ background: rgb(var(--v-theme-surface));
4185
+ border: 1px solid rgba(var(--v-border-color), var(--v-border-opacity));
4190
4186
  border-radius: 8px;
4191
4187
  box-shadow: 0 1px 2px rgba(15, 35, 54, 0.04);
4192
4188
  }
@@ -4209,7 +4205,7 @@ function formatDashboardTime(value?: string) {
4209
4205
  }
4210
4206
 
4211
4207
  .figma-panel {
4212
- border-color: #dde4ea;
4208
+ border-color: rgba(var(--v-border-color), var(--v-border-opacity));
4213
4209
  box-shadow: none;
4214
4210
  min-height: 276px;
4215
4211
  padding: 26px 28px 24px;
@@ -4224,7 +4220,7 @@ function formatDashboardTime(value?: string) {
4224
4220
  }
4225
4221
 
4226
4222
  .figma-panel-header h2 {
4227
- color: #18324a;
4223
+ color: rgb(var(--v-theme-on-surface));
4228
4224
  font-size: 12px;
4229
4225
  font-weight: 900;
4230
4226
  letter-spacing: 0;
@@ -4238,7 +4234,7 @@ function formatDashboardTime(value?: string) {
4238
4234
  appearance: none;
4239
4235
  background: transparent;
4240
4236
  border: 0;
4241
- color: #1976d2;
4237
+ color: rgb(var(--v-theme-primary));
4242
4238
  cursor: pointer;
4243
4239
  font-size: 12px;
4244
4240
  font-weight: 800;
@@ -4248,7 +4244,7 @@ function formatDashboardTime(value?: string) {
4248
4244
  }
4249
4245
 
4250
4246
  .facility-booking-modal {
4251
- border: 1px solid #d9e0e7;
4247
+ border: 1px solid rgba(var(--v-border-color), var(--v-border-opacity));
4252
4248
  box-shadow: 0 18px 48px rgba(15, 35, 54, 0.18);
4253
4249
  }
4254
4250
 
@@ -4269,7 +4265,7 @@ function formatDashboardTime(value?: string) {
4269
4265
  }
4270
4266
 
4271
4267
  .facility-booking-summary-label {
4272
- color: #1565c0;
4268
+ color: rgb(var(--v-theme-primary));
4273
4269
  font-size: 16px;
4274
4270
  font-weight: 500;
4275
4271
  line-height: 1.25;
@@ -4289,7 +4285,7 @@ function formatDashboardTime(value?: string) {
4289
4285
  }
4290
4286
 
4291
4287
  .facility-booking-summary-dots {
4292
- border-bottom: 2px dotted #9aa3ad;
4288
+ border-bottom: 2px dotted rgba(var(--v-theme-on-surface), var(--v-medium-emphasis-opacity));
4293
4289
  display: block;
4294
4290
  height: 1px;
4295
4291
  min-width: 32px;
@@ -4297,7 +4293,7 @@ function formatDashboardTime(value?: string) {
4297
4293
 
4298
4294
  .facility-booking-summary-value,
4299
4295
  .facility-booking-total-label {
4300
- color: #0b2f47;
4296
+ color: rgb(var(--v-theme-on-surface));
4301
4297
  font-size: 16px;
4302
4298
  font-weight: 800;
4303
4299
  line-height: 1.25;
@@ -4320,8 +4316,8 @@ function formatDashboardTime(value?: string) {
4320
4316
 
4321
4317
  .figma-list-row {
4322
4318
  align-items: center;
4323
- background: #fff;
4324
- border: 1px solid #e1e7ed;
4319
+ background: rgb(var(--v-theme-surface-bright));
4320
+ border: 1px solid rgba(var(--v-border-color), var(--v-border-opacity));
4325
4321
  border-radius: 6px;
4326
4322
  display: grid;
4327
4323
  gap: 10px;
@@ -4332,7 +4328,7 @@ function formatDashboardTime(value?: string) {
4332
4328
  }
4333
4329
 
4334
4330
  .figma-list-row:hover {
4335
- border-color: #d5e0ea;
4331
+ border-color: rgba(var(--v-border-color), var(--v-border-opacity));
4336
4332
  box-shadow: 0 6px 14px rgba(15, 44, 69, 0.05);
4337
4333
  }
4338
4334
 
@@ -4341,7 +4337,7 @@ function formatDashboardTime(value?: string) {
4341
4337
  }
4342
4338
 
4343
4339
  .figma-list-main strong {
4344
- color: #152f47;
4340
+ color: rgb(var(--v-theme-on-surface));
4345
4341
  display: block;
4346
4342
  font-size: 12px;
4347
4343
  font-weight: 800;
@@ -4352,7 +4348,7 @@ function formatDashboardTime(value?: string) {
4352
4348
  }
4353
4349
 
4354
4350
  .figma-list-main p {
4355
- color: #7b8895;
4351
+ color: rgba(var(--v-theme-on-surface), var(--v-medium-emphasis-opacity));
4356
4352
  font-size: 11px;
4357
4353
  font-weight: 700;
4358
4354
  line-height: 1.3;
@@ -4398,8 +4394,8 @@ function formatDashboardTime(value?: string) {
4398
4394
 
4399
4395
  .active-patrol-row {
4400
4396
  align-items: center;
4401
- background-color: #ffffff;
4402
- border: 1px solid #e2e8f0;
4397
+ background-color: rgb(var(--v-theme-surface-bright));
4398
+ border: 1px solid rgba(var(--v-border-color), var(--v-border-opacity));
4403
4399
  border-radius: 8px;
4404
4400
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.02);
4405
4401
  display: grid;
@@ -4416,7 +4412,7 @@ function formatDashboardTime(value?: string) {
4416
4412
  }
4417
4413
 
4418
4414
  .active-patrol-main strong {
4419
- color: #0b2f47;
4415
+ color: rgb(var(--v-theme-on-surface));
4420
4416
  display: block;
4421
4417
  font-size: 13px;
4422
4418
  font-weight: 700;
@@ -4426,7 +4422,7 @@ function formatDashboardTime(value?: string) {
4426
4422
  }
4427
4423
 
4428
4424
  .active-patrol-main span {
4429
- color: #94a3b8;
4425
+ color: rgba(var(--v-theme-on-surface), var(--v-medium-emphasis-opacity));
4430
4426
  display: block;
4431
4427
  font-size: 11px;
4432
4428
  line-height: 1.25;
@@ -4442,8 +4438,8 @@ function formatDashboardTime(value?: string) {
4442
4438
 
4443
4439
  .active-patrol-assignee {
4444
4440
  align-items: center;
4445
- background: #f8fafc;
4446
- border: 1px solid #e7edf3;
4441
+ background: rgba(var(--v-theme-on-surface), 0.05);
4442
+ border: 1px solid rgba(var(--v-border-color), var(--v-border-opacity));
4447
4443
  border-radius: 999px;
4448
4444
  display: inline-flex;
4449
4445
  flex: 1 1 auto;
@@ -4454,7 +4450,7 @@ function formatDashboardTime(value?: string) {
4454
4450
  }
4455
4451
 
4456
4452
  .active-patrol-assignee span {
4457
- color: #475569;
4453
+ color: rgba(var(--v-theme-on-surface), var(--v-medium-emphasis-opacity));
4458
4454
  font-size: 11px;
4459
4455
  font-weight: 600;
4460
4456
  line-height: 1.2;
@@ -4484,10 +4480,10 @@ function formatDashboardTime(value?: string) {
4484
4480
 
4485
4481
  .active-patrol-more-pill {
4486
4482
  align-items: center;
4487
- background: #eef3f8;
4488
- border: 1px solid #dbe4ed;
4483
+ background: rgba(var(--v-theme-on-surface), 0.05);
4484
+ border: 1px solid rgba(var(--v-border-color), var(--v-border-opacity));
4489
4485
  border-radius: 999px;
4490
- color: #607386;
4486
+ color: rgba(var(--v-theme-on-surface), var(--v-medium-emphasis-opacity));
4491
4487
  display: inline-flex;
4492
4488
  flex: 0 0 auto;
4493
4489
  font-size: 11px;
@@ -4587,7 +4583,7 @@ function formatDashboardTime(value?: string) {
4587
4583
 
4588
4584
  .figma-status-label {
4589
4585
  align-items: center;
4590
- color: #263d53;
4586
+ color: rgb(var(--v-theme-on-surface));
4591
4587
  display: flex;
4592
4588
  font-size: 12px;
4593
4589
  font-weight: 800;
@@ -4597,13 +4593,13 @@ function formatDashboardTime(value?: string) {
4597
4593
  }
4598
4594
 
4599
4595
  .figma-status-label strong {
4600
- color: #18324a;
4596
+ color: rgb(var(--v-theme-on-surface));
4601
4597
  font-size: 12px;
4602
4598
  font-weight: 900;
4603
4599
  }
4604
4600
 
4605
4601
  .figma-progress-track {
4606
- background: #edf2f6;
4602
+ background: rgba(var(--v-theme-on-surface), 0.08);
4607
4603
  border-radius: 999px;
4608
4604
  height: 6px;
4609
4605
  overflow: hidden;
@@ -4624,8 +4620,8 @@ function formatDashboardTime(value?: string) {
4624
4620
 
4625
4621
  .feedback-row {
4626
4622
  align-items: center;
4627
- background: #fff;
4628
- border: 1px solid #e0e5ea;
4623
+ background: rgb(var(--v-theme-surface-bright));
4624
+ border: 1px solid rgba(var(--v-border-color), var(--v-border-opacity));
4629
4625
  border-radius: 8px;
4630
4626
  display: grid;
4631
4627
  gap: 16px;
@@ -4636,7 +4632,7 @@ function formatDashboardTime(value?: string) {
4636
4632
  }
4637
4633
 
4638
4634
  .feedback-row:hover {
4639
- border-color: #d8e3ec;
4635
+ border-color: rgba(var(--v-border-color), var(--v-border-opacity));
4640
4636
  box-shadow: 0 6px 14px rgba(15, 44, 69, 0.04);
4641
4637
  }
4642
4638
 
@@ -4645,7 +4641,7 @@ function formatDashboardTime(value?: string) {
4645
4641
  }
4646
4642
 
4647
4643
  .feedback-summary strong {
4648
- color: #132b43;
4644
+ color: rgb(var(--v-theme-on-surface));
4649
4645
  display: block;
4650
4646
  font-size: 12px;
4651
4647
  font-weight: 800;
@@ -4656,7 +4652,7 @@ function formatDashboardTime(value?: string) {
4656
4652
  }
4657
4653
 
4658
4654
  .feedback-summary p {
4659
- color: #888;
4655
+ color: rgba(var(--v-theme-on-surface), var(--v-medium-emphasis-opacity));
4660
4656
  font-size: 12px;
4661
4657
  font-weight: 500;
4662
4658
  line-height: 1.35;
@@ -4726,7 +4722,7 @@ function formatDashboardTime(value?: string) {
4726
4722
  appearance: none;
4727
4723
  background: transparent;
4728
4724
  border: 0;
4729
- color: #2196f3;
4725
+ color: rgb(var(--v-theme-primary));
4730
4726
  cursor: pointer;
4731
4727
  font-size: 12px;
4732
4728
  font-weight: 800;
@@ -7,7 +7,7 @@
7
7
  style="min-height: 200px"
8
8
  >
9
9
  <div class="d-flex align-center justify-space-between pa-5 pb-3">
10
- <h2 class="text-body-1 font-weight-black text-blue-grey-darken-4 mb-0">
10
+ <h2 class="text-body-1 font-weight-black text-high-emphasis mb-0">
11
11
  {{ title }}
12
12
  </h2>
13
13
  <v-btn
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@7365admin1/layer-common",
3
3
  "license": "MIT",
4
4
  "type": "module",
5
- "version": "3.2.1-staging.73",
5
+ "version": "3.2.1-staging.74",
6
6
  "author": "7365admin1",
7
7
  "main": "./nuxt.config.ts",
8
8
  "publishConfig": {
@@ -180,6 +180,21 @@ test("primary-button carries a white label in both themes", () => {
180
180
  assert.ok(contrast(WHITE, light["primary-button"]) >= 4.5);
181
181
  });
182
182
 
183
+ /**
184
+ * `primary` is still a fill in a v-btn, a v-chip, a v-alert and a tab bar. The
185
+ * comment above `primary` used to claim Vuetify derives black for `on-primary`;
186
+ * it derives WHITE, and white on this blue is 2.68:1. Nothing measured it, so
187
+ * it shipped. This is that measurement.
188
+ */
189
+ test("a filled primary surface carries a readable label in both themes", () => {
190
+ assert.equal(DARK_THEME.colors["on-primary"], dark.background);
191
+ assert.ok(contrast(dark["on-primary"], dark.primary) >= 4.5);
192
+ assert.ok(contrast(WHITE, dark.primary) < 4.5, "white here is the defect");
193
+
194
+ // Light derives its own and it passes; measure it rather than assume.
195
+ assert.ok(contrast(WHITE, light.primary) >= 4.5);
196
+ });
197
+
183
198
  /**
184
199
  * Both themes correct the same two Vuetify defaults, and a value that drifts
185
200
  * apart between them is a bug nobody sees until they flip the switch.
package/utils/theme.ts CHANGED
@@ -108,12 +108,20 @@ export const DARK_THEME = {
108
108
  * camera wall already draws its accents in (`--vms-accent`), so the two
109
109
  * dark surfaces in this product now agree on one blue.
110
110
  *
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.
111
+ * Where it is still used as a fill it needs a foreground, and the claim
112
+ * that used to sit here - that Vuetify derives black - is wrong. Measured
113
+ * in a browser, Vuetify picks WHITE, which reads at 2.68:1 on this fill:
114
+ * every filled primary button, chip, alert and tab bar in dark mode. So
115
+ * `on-primary` is set explicitly below rather than derived.
115
116
  */
116
117
  primary: "#5B9BE0",
118
+ /**
119
+ * The label on a filled `primary` surface. Measured 6.40:1 against
120
+ * `primary`, against the 2.68:1 Vuetify's own derivation gives. Not a new
121
+ * colour - it is the dark page `background` above, so a filled button
122
+ * reads as a hole punched in the page rather than a fifth shade of navy.
123
+ */
124
+ "on-primary": "#0E1319",
117
125
  /**
118
126
  * The navigation drawer's own fill. Deliberately the same value as
119
127
  * `surface`: on a dark page the drawer, the app bar and the cards are the