@necrolab/dashboard 0.5.15 → 0.5.16

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 (121) hide show
  1. package/backend/api.js +2 -3
  2. package/eslint.config.js +46 -0
  3. package/index.html +2 -1
  4. package/package.json +5 -2
  5. package/src/App.vue +140 -170
  6. package/src/assets/css/base/mixins.scss +72 -0
  7. package/src/assets/css/base/reset.scss +0 -2
  8. package/src/assets/css/base/scroll.scss +43 -36
  9. package/src/assets/css/base/typography.scss +9 -10
  10. package/src/assets/css/base/variables.scss +43 -0
  11. package/src/assets/css/components/accessibility.scss +37 -0
  12. package/src/assets/css/components/buttons.scss +58 -15
  13. package/src/assets/css/components/forms.scss +31 -32
  14. package/src/assets/css/components/headers.scss +12 -20
  15. package/src/assets/css/components/modals.scss +2 -2
  16. package/src/assets/css/components/search-groups.scss +28 -22
  17. package/src/assets/css/components/tables.scss +5 -7
  18. package/src/assets/css/components/toasts.scss +7 -7
  19. package/src/assets/css/components/utilities.scss +220 -0
  20. package/src/assets/css/main.scss +66 -77
  21. package/src/components/Auth/LoginForm.vue +5 -84
  22. package/src/components/Editors/Account/Account.vue +8 -10
  23. package/src/components/Editors/Account/AccountCreator.vue +28 -59
  24. package/src/components/Editors/Account/AccountView.vue +38 -86
  25. package/src/components/Editors/Account/CreateAccount.vue +8 -50
  26. package/src/components/Editors/Profile/CreateProfile.vue +74 -131
  27. package/src/components/Editors/Profile/Profile.vue +15 -17
  28. package/src/components/Editors/Profile/ProfileCountryChooser.vue +16 -60
  29. package/src/components/Editors/Profile/ProfileView.vue +46 -96
  30. package/src/components/Editors/TagLabel.vue +16 -55
  31. package/src/components/Editors/TagToggle.vue +20 -8
  32. package/src/components/Filter/Filter.vue +62 -75
  33. package/src/components/Filter/FilterPreview.vue +161 -135
  34. package/src/components/Filter/PriceSortToggle.vue +36 -43
  35. package/src/components/Table/Header.vue +1 -1
  36. package/src/components/Table/Table.vue +45 -51
  37. package/src/components/Tasks/CheckStock.vue +7 -16
  38. package/src/components/Tasks/Controls/DesktopControls.vue +15 -60
  39. package/src/components/Tasks/Controls/MobileControls.vue +5 -20
  40. package/src/components/Tasks/CreateTaskAXS.vue +20 -118
  41. package/src/components/Tasks/CreateTaskTM.vue +33 -189
  42. package/src/components/Tasks/EventDetailRow.vue +21 -0
  43. package/src/components/Tasks/MassEdit.vue +6 -16
  44. package/src/components/Tasks/QuickSettings.vue +140 -216
  45. package/src/components/Tasks/ScrapeVenue.vue +4 -13
  46. package/src/components/Tasks/Stats.vue +19 -38
  47. package/src/components/Tasks/Task.vue +65 -268
  48. package/src/components/Tasks/TaskLabel.vue +9 -3
  49. package/src/components/Tasks/TaskView.vue +43 -63
  50. package/src/components/Tasks/Utilities.vue +10 -44
  51. package/src/components/Tasks/ViewTask.vue +23 -107
  52. package/src/components/icons/Close.vue +2 -8
  53. package/src/components/icons/Gear.vue +8 -8
  54. package/src/components/icons/Hash.vue +5 -0
  55. package/src/components/icons/Key.vue +2 -8
  56. package/src/components/icons/Pencil.vue +2 -8
  57. package/src/components/icons/Profile.vue +2 -8
  58. package/src/components/icons/Sell.vue +2 -8
  59. package/src/components/icons/Spinner.vue +4 -7
  60. package/src/components/icons/SquareCheck.vue +2 -8
  61. package/src/components/icons/SquareUncheck.vue +2 -8
  62. package/src/components/icons/Wildcard.vue +2 -8
  63. package/src/components/icons/index.js +3 -1
  64. package/src/components/ui/ActionButtonGroup.vue +113 -52
  65. package/src/components/ui/BalanceIndicator.vue +60 -0
  66. package/src/components/ui/EmptyState.vue +24 -0
  67. package/src/components/ui/EnableDisableToggle.vue +23 -0
  68. package/src/components/ui/FormField.vue +48 -48
  69. package/src/components/ui/IconLabel.vue +23 -0
  70. package/src/components/ui/InfoRow.vue +21 -54
  71. package/src/components/ui/Modal.vue +89 -56
  72. package/src/components/ui/Navbar.vue +60 -41
  73. package/src/components/ui/ReadonlyFieldsSection.vue +31 -0
  74. package/src/components/ui/ReconnectIndicator.vue +111 -124
  75. package/src/components/ui/SectionCard.vue +6 -14
  76. package/src/components/ui/Splash.vue +2 -10
  77. package/src/components/ui/StatusBadge.vue +26 -28
  78. package/src/components/ui/TaskToggle.vue +54 -0
  79. package/src/components/ui/controls/CountryChooser.vue +27 -64
  80. package/src/components/ui/controls/EyeToggle.vue +1 -1
  81. package/src/components/ui/controls/atomic/Checkbox.vue +40 -121
  82. package/src/components/ui/controls/atomic/Dropdown.vue +103 -139
  83. package/src/components/ui/controls/atomic/MultiDropdown.vue +71 -119
  84. package/src/components/ui/controls/atomic/Switch.vue +21 -84
  85. package/src/composables/useColorMapping.js +15 -0
  86. package/src/composables/useCopyToClipboard.js +1 -1
  87. package/src/composables/useDateFormatting.js +21 -0
  88. package/src/composables/useDeviceDetection.js +14 -0
  89. package/src/composables/useDropdownPosition.js +3 -4
  90. package/src/composables/useDynamicTableHeight.js +31 -0
  91. package/src/composables/useRowSelection.js +0 -3
  92. package/src/composables/useTicketPricing.js +16 -0
  93. package/src/composables/useWindowDimensions.js +21 -0
  94. package/src/libs/Filter.js +14 -20
  95. package/src/libs/panzoom.js +1 -5
  96. package/src/libs/utils/array.js +60 -0
  97. package/src/{stores/utils.js → libs/utils/dataGeneration.js} +2 -250
  98. package/src/libs/utils/eventUrl.js +40 -0
  99. package/src/libs/utils/string.js +28 -0
  100. package/src/libs/utils/time.js +20 -0
  101. package/src/libs/utils/validation.js +88 -0
  102. package/src/main.js +0 -2
  103. package/src/stores/connection.js +1 -4
  104. package/src/stores/logger.js +6 -12
  105. package/src/stores/sampleData.js +1 -2
  106. package/src/stores/ui.js +59 -36
  107. package/src/views/Accounts.vue +13 -24
  108. package/src/views/Console.vue +70 -172
  109. package/src/views/Editor.vue +211 -379
  110. package/src/views/FilterBuilder.vue +188 -371
  111. package/src/views/Login.vue +3 -28
  112. package/src/views/Profiles.vue +8 -15
  113. package/src/views/Tasks.vue +49 -36
  114. package/tailwind.config.js +82 -71
  115. package/workbox-config.cjs +47 -5
  116. package/docs/plans/2026-02-08-tailwind-consolidation.md +0 -2438
  117. package/exit +0 -209
  118. package/run +0 -177
  119. package/src/assets/css/base/color-fallbacks.scss +0 -10
  120. package/switch-branch.sh +0 -41
  121. /package/public/{reconnect-logo.png → img/reconnect-logo.png} +0 -0
@@ -1,8 +1,8 @@
1
1
  <template>
2
- <div class="login-container flex min-h-screen flex-col items-center px-4 mt-[3vh] landscape:mt-[1vh] landscape:justify-start landscape:min-h-dvh" v-once>
3
- <div class="login-card w-full max-w-[420px] rounded-lg border border-dark-650 bg-dark-400 p-7 backdrop-blur-[10px] shadow-[0_25px_50px_-12px_rgba(0,0,0,0.5)]">
2
+ <div class="flex min-h-screen flex-col items-center px-4 mt-[3vh] landscape:mt-[1vh] landscape:justify-start landscape:min-h-dvh max-md:mt-[2vh]" v-once>
3
+ <div class="w-full max-w-105 rounded-lg border-2 bg-dark-400 p-7 backdrop-blur-md shadow-login max-md:max-w-95 max-md:p-6 landscape:p-5 landscape:my-1" style="border-color: var(--color-border);">
4
4
  <div class="mb-10 flex justify-center landscape:mb-2">
5
- <img src="@/assets/img/logo_trans.png" class="mb-3 h-16 object-cover landscape:h-10" alt="Logo: NecroLab" />
5
+ <img src="@/assets/img/logo_trans.png" class="mb-3 h-16 object-cover landscape:h-10 max-md:h-12" alt="Logo: NecroLab" />
6
6
  </div>
7
7
 
8
8
  <LoginForm />
@@ -12,28 +12,3 @@
12
12
  <script setup>
13
13
  import LoginForm from "@/components/Auth/LoginForm.vue";
14
14
  </script>
15
- <style lang="scss" scoped>
16
- /* Mobile-specific overrides (max-width: 480px) */
17
- @media (max-width: 480px) {
18
- .login-container {
19
- margin-top: 2vh;
20
- }
21
-
22
- .login-card {
23
- max-width: 380px;
24
- padding: 1.5rem;
25
- }
26
-
27
- .login-card img {
28
- @apply h-12;
29
- }
30
- }
31
-
32
- /* Landscape overrides */
33
- @media (orientation: landscape) {
34
- .login-card {
35
- padding: 1.25rem;
36
- margin: 0.25rem 0;
37
- }
38
- }
39
- </style>
@@ -14,6 +14,7 @@
14
14
  :disabled="ui.disabledButtons['add-profiles']"
15
15
  @click="ui.toggleModal('create-profile', true)"
16
16
  class="smooth-hover"
17
+ aria-label="Add profile"
17
18
  >
18
19
  <PlusIcon class="w-4 h-4" />
19
20
  </button>
@@ -23,6 +24,7 @@
23
24
  :disabled="ui.disabledButtons['add-profiles']"
24
25
  @click="disable"
25
26
  class="smooth-hover text-red-400"
27
+ aria-label="Disable selected profiles"
26
28
  >
27
29
  <CloseXIcon />
28
30
  </button>
@@ -32,6 +34,7 @@
32
34
  :disabled="ui.disabledButtons['add-profiles']"
33
35
  @click="enable"
34
36
  class="smooth-hover text-green-400"
37
+ aria-label="Enable selected profiles"
35
38
  >
36
39
  <CheckIcon />
37
40
  </button>
@@ -57,6 +60,7 @@
57
60
  <input
58
61
  class="min-w-32 lg:min-w-38 text-white text-sm"
59
62
  :placeholder="`Search ${ui.search.profiles.field}s`"
63
+ :aria-label="`Search ${ui.search.profiles.field}s`"
60
64
  v-model="ui.search.profiles.query"
61
65
  />
62
66
 
@@ -113,23 +117,9 @@
113
117
  </transition-group>
114
118
  </div>
115
119
  </template>
116
- <style lang="scss" scoped>
117
- /* Page buttons styling - match Tasks page */
118
- button.bg-dark-400,
119
- button.bg-green-400,
120
- button.bg-red-400 {
121
- &:active,
122
- &:focus {
123
- outline: 1px solid oklch(0.72 0.15 145);
124
- outline-offset: 0;
125
- border-color: oklch(0.72 0.15 145) !important;
126
- }
127
- }
128
- </style>
129
120
  <script setup>
130
- import { computed, ref, watch } from "vue";
121
+ import { computed, defineAsyncComponent, ref, watch } from "vue";
131
122
  import ProfileView from "@/components/Editors/Profile/ProfileView.vue";
132
- import CreateProfile from "@/components/Editors/Profile/CreateProfile.vue";
133
123
  import { useUIStore } from "@/stores/ui";
134
124
  import { PlusIcon, GroupIcon, CloseXIcon, CheckIcon } from "@/components/icons";
135
125
  import Dropdown from "@/components/ui/controls/atomic/Dropdown.vue";
@@ -137,6 +127,9 @@ import EyeToggle from "@/components/ui/controls/EyeToggle.vue";
137
127
  import TagToggle from "@/components/Editors/TagToggle.vue";
138
128
  import Switch from "@/components/ui/controls/atomic/Switch.vue";
139
129
 
130
+ // Lazy-loaded modal component
131
+ const CreateProfile = defineAsyncComponent(() => import("@/components/Editors/Profile/CreateProfile.vue"));
132
+
140
133
  const ui = useUIStore();
141
134
  ui.search.profiles.results = ui.profiles;
142
135
 
@@ -1,36 +1,35 @@
1
1
  <template>
2
2
  <div class="tasks-page">
3
- <div class="page-header tasks-header">
3
+ <div class="page-header">
4
4
  <div class="page-header-card">
5
- <div class="icon-button" @click="ui.toggleModal('quick-settings')">
6
- <GearIcon />
7
- </div>
5
+ <GearIcon class="smooth-hover cursor-pointer" @click="ui.toggleModal('quick-settings')" aria-label="Open settings" role="button" tabindex="0" />
8
6
  <h4>Tasks</h4>
9
- <span class="page-header-count">{{ taskCount }}</span>
7
+ <span class="pl-1.5 text-sm font-medium text-light-400">{{ taskCount }}</span>
10
8
  </div>
11
9
  <ul class="mobile-icons mobile-header-controls">
12
10
  <li>
13
- <button @click="ui.startTasks()"><PlayIcon class="h-4 w-4" /></button>
11
+ <button @click="ui.startTasks()" aria-label="Start all tasks"><PlayIcon class="h-4 w-4" /></button>
14
12
  </li>
15
13
  <li>
16
- <button @click="ui.stopTasks()"><PauseIcon class="h-4 w-4" /></button>
14
+ <button @click="ui.stopTasks()" aria-label="Stop all tasks"><PauseIcon class="h-4 w-4" /></button>
17
15
  </li>
18
16
  <li>
19
17
  <button
20
18
  class="text-sm"
21
19
  :disabled="ui.disabledButtons['add-tasks']"
22
- @click="ui.toggleModal('create-task')">
20
+ @click="ui.toggleModal('create-task')"
21
+ aria-label="Create new task">
23
22
  <PlusIcon class="h-4 w-4" />
24
23
  </button>
25
24
  </li>
26
25
  <li>
27
- <button @click="ui.deleteTasks()"><TrashIcon class="h-3.5 w-3.5" /></button>
26
+ <button @click="ui.deleteTasks()" aria-label="Delete all tasks"><TrashIcon class="h-3.5 w-3.5" /></button>
28
27
  </li>
29
28
  </ul>
30
29
  </div>
31
30
 
32
31
  <div class="controls-wrapper">
33
- <div class="controls-wrapper lg:mb-3">
32
+ <div class="mb-2 lg:mb-3">
34
33
  <DesktopControls
35
34
  class="desktop-controls-hide"
36
35
  @stopAll="ui.stopTasks()"
@@ -38,9 +37,14 @@
38
37
  @deleteAll="ui.deleteTasks()" />
39
38
  </div>
40
39
 
41
- <div class="mb-1 flex items-center justify-between gap-2 md:hidden">
40
+ <div class="mb-2 flex items-center justify-between gap-2 md:hidden">
42
41
  <Stats class="stats-component flex-1" />
43
42
  <div class="flex items-center gap-2">
43
+ <div
44
+ class="flex h-8 items-center justify-between rounded-md border border-dark-650 bg-dark-400 px-2 text-xs font-medium text-white">
45
+ <p class="text-2xs">Name</p>
46
+ <Switch class="scale-75" v-model="preferEventName" />
47
+ </div>
44
48
  <PriceSortToggle
45
49
  class="h-8 min-w-20 max-w-28 flex-shrink-0"
46
50
  :options="['All', 'Checkout']"
@@ -67,7 +71,8 @@
67
71
  v-model="taskSearchQuery"
68
72
  type="text"
69
73
  placeholder="Search tasks..."
70
- class="h-full w-full bg-transparent text-sm text-white outline-none" />
74
+ aria-label="Search tasks"
75
+ class="transparent-input" />
71
76
  <span v-if="taskSearchQuery" class="ml-2 text-xs text-light-500">{{ filteredTaskCount }}</span>
72
77
  </div>
73
78
  </div>
@@ -102,7 +107,8 @@
102
107
  v-model="taskSearchQuery"
103
108
  type="text"
104
109
  placeholder="Search tasks..."
105
- class="h-full w-full bg-transparent text-sm text-white outline-none" />
110
+ aria-label="Search tasks"
111
+ class="transparent-input" />
106
112
  <span v-if="taskSearchQuery" class="ml-2 text-xs text-light-500">{{ filteredTaskCount }}</span>
107
113
  </div>
108
114
  </div>
@@ -130,16 +136,17 @@
130
136
  </div>
131
137
  </template>
132
138
  <style lang="scss" scoped>
133
- // Add padding in PWA mode to prevent content hiding under navbar
134
139
  .tasks-header {
135
140
  @media (display-mode: standalone) {
136
- padding-top: 3.5rem;
141
+ padding-top: 3.5rem !important;
137
142
  }
138
143
  }
139
144
 
140
- /* ==========================================================================
141
- TASKS PAGE RESPONSIVE LAYOUT - MOBILE FIRST
142
- ========================================================================== */
145
+ .custom-dropdown-content {
146
+ top: 2.6rem !important;
147
+ left: -13px;
148
+ @apply border border-dark-650;
149
+ }
143
150
 
144
151
  /* Default mobile layout */
145
152
  .desktop-controls-hide {
@@ -150,11 +157,10 @@
150
157
  display: flex;
151
158
  }
152
159
 
153
- /* Event dropdown base styling */
154
160
  .event-dropdown {
155
161
  min-width: 0;
156
162
  /* Match PriceSortToggle height instead of input-default */
157
- height: 40px;
163
+ height: 40px !important;
158
164
  }
159
165
 
160
166
  .event-dropdown .dropdown-value {
@@ -164,8 +170,11 @@
164
170
  white-space: nowrap;
165
171
  }
166
172
 
167
- /* Small mobile screens (portrait) */
168
173
  @media (max-width: 480px) and (orientation: portrait) {
174
+ .event-dropdown {
175
+ height: 40px !important; /* Match PriceSortToggle height exactly */
176
+ }
177
+
169
178
  .event-dropdown .dropdown-value {
170
179
  max-width: calc(100vw - 140px);
171
180
  font-size: 0.875rem;
@@ -176,15 +185,17 @@
176
185
  }
177
186
  }
178
187
 
179
- /* Extra small screens */
180
188
  @media (max-width: 375px) and (orientation: portrait) {
189
+ .event-dropdown {
190
+ height: 40px !important; /* Maintain 40px height to match PriceSortToggle */
191
+ }
192
+
181
193
  .event-dropdown .dropdown-value {
182
194
  max-width: calc(100vw - 120px);
183
195
  font-size: 0.8rem;
184
196
  }
185
197
  }
186
198
 
187
- /* Mobile landscape - hide non-essential elements */
188
199
  @media (max-height: 500px) and (orientation: landscape) {
189
200
  .stats-component,
190
201
  .utilities-section,
@@ -193,13 +204,12 @@
193
204
  }
194
205
 
195
206
  .pb-2 {
196
- padding-top: 1rem;
207
+ padding-top: 1rem !important;
197
208
  padding-bottom: 0.25rem;
198
209
  margin-bottom: 0.25rem;
199
210
  }
200
211
  }
201
212
 
202
- /* Tablet and small desktop - show desktop controls */
203
213
  @media (min-width: 650px) {
204
214
  .desktop-controls-hide {
205
215
  display: flex;
@@ -211,34 +221,34 @@
211
221
  }
212
222
  </style>
213
223
  <script setup>
214
- import { computed, onMounted, ref, watch } from "vue";
224
+ import { computed, defineAsyncComponent, onMounted, ref, watch } from "vue";
215
225
  import { DesktopControls } from "@/components/Tasks/Controls";
216
226
  import TaskView from "@/components/Tasks/TaskView.vue";
217
- import ViewTask from "@/components/Tasks/ViewTask.vue";
218
227
  import Utilities from "@/components/Tasks/Utilities.vue";
219
- import CreateTaskTM from "@/components/Tasks/CreateTaskTM.vue";
220
- import CreateTaskAXS from "@/components/Tasks/CreateTaskAXS.vue";
221
- import CheckStock from "@/components/Tasks/CheckStock.vue";
222
- import ScrapeVenue from "@/components/Tasks/ScrapeVenue.vue";
223
- import MassEditPresaleCode from "@/components/Tasks/MassEdit.vue";
224
228
  import Dropdown from "@/components/ui/controls/atomic/Dropdown.vue";
225
229
  import Stats from "@/components/Tasks/Stats.vue";
226
230
  import Switch from "@/components/ui/controls/atomic/Switch.vue";
227
231
  import { useUIStore } from "@/stores/ui";
228
232
  import { TrashIcon, GearIcon, PlusIcon, PlayIcon, PauseIcon } from "@/components/icons";
229
- import QuickSettings from "@/components/Tasks/QuickSettings.vue";
230
233
  import PriceSortToggle from "@/components/Filter/PriceSortToggle.vue";
231
234
 
235
+ // Lazy-loaded modal components
236
+ const ViewTask = defineAsyncComponent(() => import("@/components/Tasks/ViewTask.vue"));
237
+ const CreateTaskTM = defineAsyncComponent(() => import("@/components/Tasks/CreateTaskTM.vue"));
238
+ const CreateTaskAXS = defineAsyncComponent(() => import("@/components/Tasks/CreateTaskAXS.vue"));
239
+ const CheckStock = defineAsyncComponent(() => import("@/components/Tasks/CheckStock.vue"));
240
+ const ScrapeVenue = defineAsyncComponent(() => import("@/components/Tasks/ScrapeVenue.vue"));
241
+ const MassEditPresaleCode = defineAsyncComponent(() => import("@/components/Tasks/MassEdit.vue"));
242
+ const QuickSettings = defineAsyncComponent(() => import("@/components/Tasks/QuickSettings.vue"));
243
+
232
244
  const ui = useUIStore();
233
245
  const activeModal = computed(() => ui.activeModal);
234
246
  const taskCount = computed(() => Object.keys(ui.getSelectedTasks()).length);
235
247
  ui.refreshQueueStats();
236
248
 
237
249
  const taskSearchQuery = ref("");
238
- // Load preference from localStorage, default to true
239
250
  const preferEventName = ref(localStorage.getItem("preferEventName") !== "false");
240
251
 
241
- // Persist preference to localStorage when changed
242
252
  watch(preferEventName, (newValue) => {
243
253
  localStorage.setItem("preferEventName", String(newValue));
244
254
  });
@@ -252,6 +262,10 @@ const filteredTaskCount = computed(() => {
252
262
  Object.values(ui.tasks).forEach((task) => {
253
263
  if (task.hidden) return;
254
264
 
265
+ // Filter by current module (same logic as TaskView)
266
+ if (task.siteId !== ui.currentCountry.siteId) return;
267
+ if (ui.currentEvent && task.eventId !== ui.currentEvent) return;
268
+
255
269
  const searchableText = [
256
270
  task.eventId,
257
271
  task.eventName,
@@ -273,7 +287,6 @@ const filteredTaskCount = computed(() => {
273
287
  return count;
274
288
  });
275
289
 
276
- // Ensure "All events" is always selected on page load
277
290
  onMounted(() => {
278
291
  ui.setCurrentEvent("");
279
292
  });
@@ -5,7 +5,6 @@ export default {
5
5
  colors: {
6
6
  transparent: "transparent",
7
7
  white: "oklch(1 0 0)",
8
- pure: "oklch(1 0 0)",
9
8
  lightgray: "oklch(0.93 0 0)",
10
9
  green: {
11
10
  300: "oklch(0.75 0.15 145 / <alpha-value>)",
@@ -36,22 +35,19 @@ export default {
36
35
  50: "oklch(0.98 0 0 / <alpha-value>)",
37
36
  100: "oklch(0.96 0 0 / <alpha-value>)",
38
37
  200: "oklch(0.93 0 0 / <alpha-value>)",
39
- 300: "oklch(0.1822 0 0 / <alpha-value>)", // Base background (Supabase dark)
40
- 350: "oklch(0.1900 0 0 / <alpha-value>)", // rgba(26, 27, 30) replacement
38
+ 300: "oklch(0.1822 0 0 / <alpha-value>)", // Base background
39
+ 350: "oklch(0.1900 0 0 / <alpha-value>)", // Darker backgrounds
41
40
  400: "oklch(0.2046 0 0 / <alpha-value>)", // Card backgrounds
42
- 450: "oklch(0.2200 0 0 / <alpha-value>)", // rgba(35, 36, 41) replacement
43
- 475: "oklch(0.2350 0 0 / <alpha-value>)", // rgba(46, 47, 52) replacement
41
+ 450: "oklch(0.2200 0 0 / <alpha-value>)", // Hover backgrounds
42
+ 475: "oklch(0.2350 0 0 / <alpha-value>)", // Focus backgrounds
44
43
  500: "oklch(0.2603 0 0 / <alpha-value>)", // Input backgrounds
45
- 550: "oklch(0.2809 0 0 / <alpha-value>)", // Subtle elevation
46
- 600: "oklch(0.2809 0 0 / <alpha-value>)", // Borders
47
- 625: "oklch(0.2950 0 0 / <alpha-value>)", // rgba(61, 62, 68) replacement
44
+ 550: "oklch(0.2809 0 0 / <alpha-value>)", // Borders and elevation
45
+ 625: "oklch(0.2950 0 0 / <alpha-value>)", // Border variations
48
46
  650: "oklch(0.3132 0 0 / <alpha-value>)", // Hover states
49
- 675: "oklch(0.3200 0 0 / <alpha-value>)", // rgba(68, 69, 75) replacement
47
+ 675: "oklch(0.3200 0 0 / <alpha-value>)", // Background overlays
50
48
  700: "oklch(0.31 0 0 / <alpha-value>)", // Active states
51
49
  750: "oklch(0.33 0 0 / <alpha-value>)", // Selected states
52
- 800: "oklch(0.08 0 0 / <alpha-value>)", // Deep background
53
- 850: "oklch(0.06 0 0 / <alpha-value>)", // Deepest background
54
- 900: "oklch(0.03 0 0 / <alpha-value>)" // Ultra deep
50
+ 800: "oklch(0.08 0 0 / <alpha-value>)" // Deep background
55
51
  },
56
52
  light: {
57
53
  100: "oklch(0.98 0 0 / <alpha-value>)",
@@ -59,62 +55,41 @@ export default {
59
55
  300: "oklch(0.90 0 0 / <alpha-value>)", // Primary text (text-primary)
60
56
  400: "oklch(0.82 0 0 / <alpha-value>)", // Secondary text (text-secondary)
61
57
  500: "oklch(0.65 0 0 / <alpha-value>)", // Muted text (text-muted)
62
- 600: "oklch(0.50 0 0 / <alpha-value>)", // Disabled text
63
- 700: "oklch(0.38 0 0 / <alpha-value>)", // Very muted
64
- 800: "oklch(0.28 0 0 / <alpha-value>)", // Dark muted
65
- 900: "oklch(0.18 0 0 / <alpha-value>)" // Darkest muted
66
- },
67
- gray: {
68
- 50: "oklch(0.99 0 0 / <alpha-value>)",
69
- 100: "oklch(0.97 0 0 / <alpha-value>)",
70
- 200: "oklch(0.93 0 0 / <alpha-value>)",
71
- 300: "oklch(0.85 0 0 / <alpha-value>)",
72
- 400: "oklch(0.68 0 0 / <alpha-value>)",
73
- 500: "oklch(0.52 0 0 / <alpha-value>)",
74
- 600: "oklch(0.40 0 0 / <alpha-value>)",
75
- 700: "oklch(0.32 0 0 / <alpha-value>)",
76
- 800: "oklch(0.22 0 0 / <alpha-value>)",
77
- 900: "oklch(0.14 0 0 / <alpha-value>)"
58
+ 600: "oklch(0.50 0 0 / <alpha-value>)" // Disabled text
78
59
  },
79
60
  accent: {
80
- blue: "oklch(0.56 0.08 264 / <alpha-value>)",
81
- green: "oklch(0.72 0.15 145 / <alpha-value>)", // Primary accent (#88c999 mint green)
82
- purple: "oklch(0.65 0.12 305 / <alpha-value>)",
83
- pink: "oklch(0.62 0.15 350 / <alpha-value>)",
84
- orange: "oklch(0.70 0.10 55 / <alpha-value>)",
85
- yellow: "oklch(0.85 0.10 95 / <alpha-value>)"
61
+ green: "oklch(0.72 0.15 145 / <alpha-value>)" // Primary accent (#88c999 mint green)
86
62
  },
87
63
  overlay: {
88
- dark: "oklch(0.08 0 0 / 0.85)", // Modal backdrop
89
- darker: "oklch(0.06 0 0 / 0.90)", // Darker overlay
64
+ dark: "oklch(0.08 0 0 / 0.85)" // Modal backdrop
90
65
  },
91
66
  border: "oklch(0.26 0 0 / <alpha-value>)",
92
67
  'text-primary': 'oklch(0.90 0 0 / <alpha-value>)', // light-300
93
68
  'text-secondary': 'oklch(0.82 0 0 / <alpha-value>)', // light-400
94
69
  'text-muted': 'oklch(0.65 0 0 / <alpha-value>)', // light-500
95
- 'text-disabled': 'oklch(0.50 0 0 / <alpha-value>)', // light-600
96
70
 
97
71
  'bg-base': 'oklch(0.1822 0 0 / <alpha-value>)', // dark-300
98
72
  'bg-elevated': 'oklch(0.2046 0 0 / <alpha-value>)', // dark-400
99
73
  'bg-input': 'oklch(0.2603 0 0 / <alpha-value>)', // dark-500
100
74
 
101
- 'border-focus': 'oklch(0.72 0.15 145 / <alpha-value>)', // accent.green
102
- 'primary': 'oklch(0.72 0.15 145 / <alpha-value>)', // accent.green
75
+ 'primary': 'oklch(0.72 0.15 145 / <alpha-value>)' // accent.green
103
76
  },
104
77
  screens: {
105
78
  // Supported devices: iPad Pro, Desktop, iPhone Portrait
106
79
  // iPhone landscape forces rotation to portrait via App.vue
107
80
 
108
81
  // Mobile first approach
109
- xs: "480px", // Small mobile landscape / large mobile portrait
82
+ '2xs': '375px', // Extra small mobile
83
+ xs: '430px', // QuickSettings responsive breakpoint
110
84
  sm: "640px", // Tablet portrait
111
85
  md: "768px", // Tablet landscape / small desktop
112
86
  lg: "1024px", // Desktop
113
87
  xl: "1280px", // Large desktop
114
88
 
115
89
  // Device-specific breakpoints
116
- tablet: "768px", // Tablet-specific styling
90
+ 'tablet-lg': '720px', // CreateTaskTM breakpoint
117
91
  desktop: "1024px", // Desktop-specific styling
92
+ modal: '810px', // Switch component breakpoint
118
93
 
119
94
  // Orientation-based breakpoints (useful for iPad/desktop)
120
95
  landscape: {
@@ -128,24 +103,10 @@ export default {
128
103
  "h-sm": {
129
104
  raw: "(max-height: 500px)"
130
105
  },
131
- "h-md": {
132
- raw: "(min-height: 600px)"
133
- },
134
- "h-lg": {
135
- raw: "(min-height: 900px)"
136
- },
137
106
 
138
107
  // Combined orientation and size breakpoints
139
108
  "mobile-portrait": {
140
109
  raw: "(max-width: 480px) and (orientation: portrait)"
141
- },
142
-
143
- // Touch device detection
144
- touch: {
145
- raw: "(hover: none) and (pointer: coarse)"
146
- },
147
- "no-touch": {
148
- raw: "(hover: hover) and (pointer: fine)"
149
110
  }
150
111
  },
151
112
  extend: {
@@ -153,30 +114,80 @@ export default {
153
114
  'dropdown': '100',
154
115
  'dropdown-high': '200',
155
116
  'modal': '1000',
117
+ 'modal-mask': '25000',
156
118
  'tooltip': '2000',
157
- 'overlay': '3000',
119
+ 'splash': '1000',
158
120
  'max': '9999',
159
121
  },
160
122
  boxShadow: {
161
- 'card': '0 4px 12px rgba(0, 0, 0, 0.2)',
162
- 'button': '0 1px 2px rgba(0, 0, 0, 0.2)',
163
- 'sm': '0 1px 2px rgba(0, 0, 0, 0.1)',
164
- 'md': '0 2px 4px rgba(0, 0, 0, 0.1)',
165
- 'lg': '0 4px 12px rgba(0, 0, 0, 0.3)',
166
- 'xl': '0 6px 16px rgba(0, 0, 0, 0.3)',
167
- 'dropdown': '0 20px 25px -5px rgba(0, 0, 0, 0.4), 0 10px 10px -5px rgba(0, 0, 0, 0.2)',
168
- 'input-inset': 'inset 0 2px 4px rgba(0, 0, 0, 0.4)',
169
- },
170
- borderRadius: {
171
- 'xs': '3px',
123
+ 'card': '0 4px 12px oklch(0 0 0 / 0.2)',
124
+ 'button': '0 1px 2px oklch(0 0 0 / 0.2)',
125
+ 'sm': '0 1px 2px oklch(0 0 0 / 0.1)',
126
+ 'md': '0 2px 4px oklch(0 0 0 / 0.1)',
127
+ 'lg': '0 4px 12px oklch(0 0 0 / 0.3)',
128
+ 'xl': '0 6px 16px oklch(0 0 0 / 0.3)',
129
+ 'dropdown': '0 20px 25px -5px oklch(0 0 0 / 0.4), 0 10px 10px -5px oklch(0 0 0 / 0.2)',
130
+ 'input-inset': 'inset 0 2px 4px oklch(0 0 0 / 0.4)',
131
+ 'focus-ring': '0 0 0 1px oklch(1 0 0 / 0.2)',
132
+ // Custom shadows from cleanup
133
+ 'switch': '0 1px 3px oklch(0 0 0 / 0.3)',
134
+ 'login': '0 25px 50px -12px oklch(0 0 0 / 0.5)'
172
135
  },
173
136
  backdropBlur: {
174
- 'xs': '4px',
175
- 'sm': '8px',
137
+ 'md': '10px'
176
138
  },
177
139
  fontSize: {
178
- 'xxs': '0.625rem', // 10px
179
- 'xxxs': '0.5625rem', // 9px
140
+ '3xs': '0.5rem', // 8px
141
+ '2xs': '0.625rem', // 10px
142
+ 'xs+': '0.6875rem', // 11px
143
+ 'base-': '0.9375rem', // 15px (login button)
144
+ },
145
+ lineHeight: {
146
+ 'tight-sm': '1.2', // Used in metadata/compact displays
147
+ 'tight-md': '1.3', // Slightly more spacious compact text
148
+ },
149
+ spacing: {},
150
+ width: {
151
+ '4.5': '1.125rem', // 18px - checkbox size
152
+ '6.5': '1.625rem', // 26px - status badge
153
+ '20': '5rem', // 80px
154
+ '25': '6.25rem', // 100px
155
+ '26': '6.5rem', // 104px - modal buttons
156
+ '30': '7.5rem', // 120px
157
+ '45': '11.25rem', // 180px
158
+ '75': '18.75rem', // 300px
159
+ '160': '40rem', // 640px
160
+ },
161
+ height: {
162
+ '3.5': '0.875rem', // 14px
163
+ '4.25': '1.0625rem', // 17px
164
+ '4.5': '1.125rem', // 18px - checkbox size
165
+ '6.5': '1.625rem', // 26px
166
+ '13': '3.25rem', // 52px - login button
167
+ '75': '18.75rem', // 300px
168
+ '87.5': '21.875rem', // 350px
169
+ '125': '31.25rem', // 500px
170
+ },
171
+ maxWidth: {
172
+ '30': '7.5rem', // 120px
173
+ '45': '11.25rem', // 180px
174
+ '95': '23.75rem', // 380px - Login form mobile
175
+ '105': '26.25rem', // 420px - Login form desktop
176
+ },
177
+ minWidth: {
178
+ '4.5': '1.125rem', // 18px
179
+ '20': '5rem', // 80px
180
+ '25': '6.25rem', // 100px
181
+ '30': '7.5rem', // 120px
182
+ },
183
+ minHeight: {
184
+ '2.75': '0.6875rem', // 11px
185
+ '14.5': '3.625rem', // 58px
186
+ '17.25': '4.3125rem', // 69px
187
+ '18.75': '4.6875rem', // 75px
188
+ '75': '18.75rem', // 300px
189
+ '87.5': '21.875rem', // 350px
190
+ '125': '31.25rem', // 500px
180
191
  }
181
192
  }
182
193
  },