@getmicdrop/svelte-components 5.18.2 → 5.20.0

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 (122) hide show
  1. package/dist/base.css +18 -0
  2. package/dist/calendar/Calendar/MiniMonthCalendar.svelte +10 -8
  3. package/dist/calendar/Calendar/MiniMonthCalendar.svelte.d.ts.map +1 -1
  4. package/dist/components/Heading.svelte +8 -2
  5. package/dist/components/Heading.svelte.d.ts +1 -0
  6. package/dist/components/Heading.svelte.d.ts.map +1 -1
  7. package/dist/components/Text.svelte +13 -2
  8. package/dist/components/Text.svelte.d.ts +2 -1
  9. package/dist/components/Text.svelte.d.ts.map +1 -1
  10. package/dist/constants/formOptions.d.ts +2 -5
  11. package/dist/constants/formOptions.d.ts.map +1 -1
  12. package/dist/constants/formOptions.js +6 -6
  13. package/dist/constants/formOptions.spec.js +2 -7
  14. package/dist/index.d.ts +6 -2
  15. package/dist/index.d.ts.map +1 -1
  16. package/dist/index.js +29 -2
  17. package/dist/patterns/forms/FormSection.svelte +2 -1
  18. package/dist/patterns/forms/FormSection.svelte.d.ts +2 -0
  19. package/dist/patterns/forms/FormSection.svelte.d.ts.map +1 -1
  20. package/dist/patterns/layout/SidebarTestWrapper.svelte.d.ts +1 -1
  21. package/dist/patterns/navigation/Header.svelte +3 -3
  22. package/dist/primitives/Input/Select.svelte +1 -1
  23. package/dist/primitives/Pagination/DotIndicator.svelte +66 -0
  24. package/dist/primitives/Pagination/DotIndicator.svelte.d.ts +18 -0
  25. package/dist/primitives/Pagination/DotIndicator.svelte.d.ts.map +1 -0
  26. package/dist/primitives/index.d.ts +1 -0
  27. package/dist/primitives/index.js +1 -0
  28. package/dist/recipes/inputs/phoneInput/CountrySelector.svelte +1 -1
  29. package/dist/recipes/modals/FeedbackModal.svelte +205 -0
  30. package/dist/recipes/modals/FeedbackModal.svelte.d.ts +24 -0
  31. package/dist/recipes/modals/FeedbackModal.svelte.d.ts.map +1 -0
  32. package/dist/recipes/modals/index.d.ts +1 -0
  33. package/dist/recipes/modals/index.js +1 -0
  34. package/dist/schemas/event.d.ts +4 -4
  35. package/dist/schemas/order.d.ts +2 -2
  36. package/dist/schemas/promo.d.ts +4 -4
  37. package/dist/services/event.service.d.ts +11 -0
  38. package/dist/services/event.service.d.ts.map +1 -0
  39. package/dist/services/event.service.js +64 -0
  40. package/dist/services/event.service.spec.d.ts +2 -0
  41. package/dist/services/event.service.spec.d.ts.map +1 -0
  42. package/dist/services/event.service.spec.js +168 -0
  43. package/dist/services/{ShowService.d.ts → show.service.d.ts} +1 -1
  44. package/dist/services/show.service.d.ts.map +1 -0
  45. package/dist/services/show.service.js +115 -0
  46. package/dist/services/show.service.spec.d.ts +2 -0
  47. package/dist/services/show.service.spec.d.ts.map +1 -0
  48. package/dist/services/show.service.spec.js +242 -0
  49. package/dist/tailwind/preset.cjs +5 -0
  50. package/dist/tailwind/preset.d.cts +2 -0
  51. package/dist/tailwind/preset.d.cts.map +1 -1
  52. package/dist/tokens/__tests__/spacing.test.js +2 -2
  53. package/dist/tokens/base-resets.css +124 -0
  54. package/dist/tokens/spacing.d.ts +2 -0
  55. package/dist/tokens/spacing.d.ts.map +1 -1
  56. package/dist/tokens/spacing.js +1 -0
  57. package/dist/tokens/tokens.css +1 -1
  58. package/dist/tokens/utilities.css +79 -2
  59. package/dist/utils/apiConfig.js +1 -1
  60. package/dist/utils/apiConfig.spec.js +34 -27
  61. package/dist/utils/assets.d.ts +3 -0
  62. package/dist/utils/assets.d.ts.map +1 -0
  63. package/dist/utils/assets.js +3 -0
  64. package/dist/utils/classNames.d.ts +10 -0
  65. package/dist/utils/classNames.d.ts.map +1 -0
  66. package/dist/utils/classNames.js +15 -0
  67. package/dist/utils/clickOutside.d.ts +4 -0
  68. package/dist/utils/clickOutside.d.ts.map +1 -0
  69. package/dist/utils/clickOutside.js +13 -0
  70. package/dist/utils/cookieHelpers.d.ts +40 -0
  71. package/dist/utils/cookieHelpers.d.ts.map +1 -0
  72. package/dist/utils/cookieHelpers.js +102 -0
  73. package/dist/utils/dateHelpers.d.ts +71 -0
  74. package/dist/utils/dateHelpers.d.ts.map +1 -0
  75. package/dist/utils/dateHelpers.js +253 -0
  76. package/dist/utils/eventFormatters.d.ts +9 -0
  77. package/dist/utils/eventFormatters.d.ts.map +1 -0
  78. package/dist/utils/eventFormatters.js +96 -0
  79. package/dist/utils/feedbackContext.d.ts +24 -0
  80. package/dist/utils/feedbackContext.d.ts.map +1 -0
  81. package/dist/utils/feedbackContext.js +19 -0
  82. package/dist/utils/fetchHelpers.d.ts +17 -0
  83. package/dist/utils/fetchHelpers.d.ts.map +1 -0
  84. package/dist/utils/fetchHelpers.js +45 -0
  85. package/dist/utils/focusTrap.d.ts +20 -0
  86. package/dist/utils/focusTrap.d.ts.map +1 -0
  87. package/dist/utils/focusTrap.js +130 -0
  88. package/dist/utils/formatters.d.ts +56 -0
  89. package/dist/utils/formatters.d.ts.map +1 -1
  90. package/dist/utils/formatters.js +121 -1
  91. package/dist/utils/formatters.spec.js +128 -1
  92. package/dist/utils/logger.d.ts +25 -1
  93. package/dist/utils/logger.d.ts.map +1 -1
  94. package/dist/utils/logger.js +59 -1
  95. package/dist/utils/logger.spec.js +99 -1
  96. package/dist/utils/permissions.d.ts +9 -0
  97. package/dist/utils/permissions.d.ts.map +1 -0
  98. package/dist/utils/permissions.js +93 -0
  99. package/dist/utils/stringHelpers.d.ts +17 -0
  100. package/dist/utils/stringHelpers.d.ts.map +1 -0
  101. package/dist/utils/stringHelpers.js +38 -0
  102. package/dist/utils/transitions.d.ts +99 -1
  103. package/dist/utils/transitions.d.ts.map +1 -1
  104. package/dist/utils/transitions.js +144 -2
  105. package/dist/utils/utils/utils.d.ts +2 -73
  106. package/dist/utils/utils/utils.d.ts.map +1 -1
  107. package/dist/utils/utils/utils.js +2 -2
  108. package/dist/utils/utils.d.ts +41 -98
  109. package/dist/utils/utils.d.ts.map +1 -1
  110. package/dist/utils/utils.js +58 -701
  111. package/package.json +16 -3
  112. package/dist/services/EventService.d.ts +0 -5
  113. package/dist/services/EventService.d.ts.map +0 -1
  114. package/dist/services/EventService.js +0 -79
  115. package/dist/services/EventService.spec.d.ts +0 -2
  116. package/dist/services/EventService.spec.d.ts.map +0 -1
  117. package/dist/services/EventService.spec.js +0 -217
  118. package/dist/services/ShowService.d.ts.map +0 -1
  119. package/dist/services/ShowService.js +0 -144
  120. package/dist/services/ShowService.spec.d.ts +0 -2
  121. package/dist/services/ShowService.spec.d.ts.map +0 -1
  122. package/dist/services/ShowService.spec.js +0 -345
@@ -0,0 +1,115 @@
1
+ import { API_BASE_URL, USE_V2_API, buildApiUrl, buildPerformerInviteUrl, getPerformerInviteMethod } from "../utils/apiConfig";
2
+ import { getPerformerToken } from "../utils/utils";
3
+ /**
4
+ * Accept an event performer invitation
5
+ */
6
+ export async function acceptInvite(inviteId, message = "") {
7
+ try {
8
+ const response = await fetch(buildPerformerInviteUrl(inviteId, "accept"), {
9
+ method: getPerformerInviteMethod("accept"),
10
+ headers: {
11
+ "Content-Type": "application/json",
12
+ },
13
+ body: JSON.stringify({ message }),
14
+ });
15
+ if (response.ok) {
16
+ const result = await response.json();
17
+ return { ok: true, result };
18
+ }
19
+ return { ok: false };
20
+ }
21
+ catch (error) {
22
+ console.error("Error accepting invite:", error);
23
+ return { ok: false };
24
+ }
25
+ }
26
+ /**
27
+ * Decline an event performer invitation
28
+ */
29
+ export async function declineInvite(inviteId, message = "") {
30
+ try {
31
+ const response = await fetch(buildPerformerInviteUrl(inviteId, "decline"), {
32
+ method: getPerformerInviteMethod("decline"),
33
+ headers: {
34
+ "Content-Type": "application/json",
35
+ },
36
+ body: JSON.stringify({ message }),
37
+ });
38
+ if (response.ok) {
39
+ const result = await response.json();
40
+ return { ok: true, result };
41
+ }
42
+ return { ok: false };
43
+ }
44
+ catch (error) {
45
+ console.error("Error declining invite:", error);
46
+ return { ok: false };
47
+ }
48
+ }
49
+ /**
50
+ * Cancel an event performer invitation
51
+ */
52
+ export async function cancelInvite(inviteId, message = "") {
53
+ try {
54
+ const response = await fetch(buildPerformerInviteUrl(inviteId, "cancel"), {
55
+ method: getPerformerInviteMethod("cancel"),
56
+ headers: {
57
+ "Content-Type": "application/json",
58
+ },
59
+ body: JSON.stringify({ message }),
60
+ });
61
+ if (response.ok) {
62
+ const result = await response.json();
63
+ return { ok: true, result };
64
+ }
65
+ return { ok: false };
66
+ }
67
+ catch (error) {
68
+ console.error("Error cancelling invite:", error);
69
+ return { ok: false };
70
+ }
71
+ }
72
+ /**
73
+ * Send a message to a venue
74
+ */
75
+ export async function sendVenueMessage(inviteId, message) {
76
+ try {
77
+ const token = getPerformerToken();
78
+ const url = USE_V2_API
79
+ ? buildApiUrl(`/api/v2/performer/invites/${inviteId}/message`)
80
+ : buildApiUrl(`/api/performer/sendVenueMessage/${inviteId}`);
81
+ const response = await fetch(url, {
82
+ method: "POST",
83
+ headers: {
84
+ "Content-Type": "application/json",
85
+ Authorization: `Bearer ${token}`,
86
+ },
87
+ credentials: "include",
88
+ body: JSON.stringify({ message }),
89
+ });
90
+ if (response.ok) {
91
+ const text = await response.text();
92
+ let result = { message: "Email sent successfully" };
93
+ if (text) {
94
+ try {
95
+ result = JSON.parse(text);
96
+ }
97
+ catch (_e) {
98
+ // Empty response is fine
99
+ }
100
+ }
101
+ return { ok: true, result };
102
+ }
103
+ return { ok: false };
104
+ }
105
+ catch (error) {
106
+ console.error("Error sending venue message:", error);
107
+ return { ok: false };
108
+ }
109
+ }
110
+ /**
111
+ * Get the ticketing event URL
112
+ */
113
+ export function getEventUrl(venueId) {
114
+ return `${API_BASE_URL}/ticketing/events/${venueId}`;
115
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=show.service.spec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"show.service.spec.d.ts","sourceRoot":"","sources":["../../src/lib/services/show.service.spec.ts"],"names":[],"mappings":""}
@@ -0,0 +1,242 @@
1
+ import { describe, it, expect, vi, beforeEach } from 'vitest';
2
+ // Mock the utility modules before imports
3
+ vi.mock('$lib/utils/apiConfig', () => ({
4
+ API_BASE_URL: 'https://get-micdrop.com',
5
+ USE_V2_API: false,
6
+ buildApiUrl: (path) => {
7
+ const cleanPath = path.startsWith('/') ? path : `/${path}`;
8
+ return `https://get-micdrop.com${cleanPath}`;
9
+ },
10
+ buildPerformerInviteUrl: (inviteId, action) => {
11
+ const v2Action = action === 'accept' ? 'confirm' : action;
12
+ return `https://get-micdrop.com/api/v2/event-performers/${inviteId}/${v2Action}`;
13
+ },
14
+ getPerformerInviteMethod: (action) => action === 'cancel' ? 'PUT' : 'POST',
15
+ }));
16
+ vi.mock('$lib/utils/utils', () => ({
17
+ getPerformerToken: () => 'test-token',
18
+ }));
19
+ import { acceptInvite, declineInvite, cancelInvite, sendVenueMessage, getEventUrl, } from './show.service';
20
+ describe('ShowService', () => {
21
+ beforeEach(() => {
22
+ vi.clearAllMocks();
23
+ global.fetch = vi.fn();
24
+ });
25
+ describe('acceptInvite', () => {
26
+ it('makes PUT request to accept invite endpoint', async () => {
27
+ global.fetch.mockResolvedValue({
28
+ ok: true,
29
+ json: () => Promise.resolve({ success: true }),
30
+ });
31
+ await acceptInvite('123');
32
+ expect(global.fetch).toHaveBeenCalledWith('https://get-micdrop.com/api/v2/event-performers/123/confirm', expect.objectContaining({
33
+ method: 'POST',
34
+ headers: { 'Content-Type': 'application/json' },
35
+ }));
36
+ });
37
+ it('returns ok: true on successful response', async () => {
38
+ global.fetch.mockResolvedValue({
39
+ ok: true,
40
+ json: () => Promise.resolve({ id: 123 }),
41
+ });
42
+ const result = await acceptInvite('123');
43
+ expect(result.ok).toBe(true);
44
+ expect(result.result).toEqual({ id: 123 });
45
+ });
46
+ it('returns ok: false on failed response', async () => {
47
+ global.fetch.mockResolvedValue({
48
+ ok: false,
49
+ });
50
+ const result = await acceptInvite('123');
51
+ expect(result.ok).toBe(false);
52
+ });
53
+ it('returns ok: false on network error', async () => {
54
+ const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => { });
55
+ global.fetch.mockRejectedValue(new Error('Network error'));
56
+ const result = await acceptInvite('123');
57
+ expect(result.ok).toBe(false);
58
+ consoleSpy.mockRestore();
59
+ });
60
+ it('includes message in request body', async () => {
61
+ global.fetch.mockResolvedValue({
62
+ ok: true,
63
+ json: () => Promise.resolve({}),
64
+ });
65
+ await acceptInvite('123', 'Thanks for the invite!');
66
+ expect(global.fetch).toHaveBeenCalledWith(expect.any(String), expect.objectContaining({
67
+ body: JSON.stringify({ message: 'Thanks for the invite!' }),
68
+ }));
69
+ });
70
+ it('sends empty message by default', async () => {
71
+ global.fetch.mockResolvedValue({
72
+ ok: true,
73
+ json: () => Promise.resolve({}),
74
+ });
75
+ await acceptInvite('123');
76
+ expect(global.fetch).toHaveBeenCalledWith(expect.any(String), expect.objectContaining({
77
+ body: JSON.stringify({ message: '' }),
78
+ }));
79
+ });
80
+ });
81
+ describe('declineInvite', () => {
82
+ it('makes PUT request to decline invite endpoint', async () => {
83
+ global.fetch.mockResolvedValue({
84
+ ok: true,
85
+ json: () => Promise.resolve({}),
86
+ });
87
+ await declineInvite('456');
88
+ expect(global.fetch).toHaveBeenCalledWith('https://get-micdrop.com/api/v2/event-performers/456/decline', expect.objectContaining({
89
+ method: 'POST',
90
+ }));
91
+ });
92
+ it('returns ok: true on success', async () => {
93
+ global.fetch.mockResolvedValue({
94
+ ok: true,
95
+ json: () => Promise.resolve({ declined: true }),
96
+ });
97
+ const result = await declineInvite('456');
98
+ expect(result.ok).toBe(true);
99
+ expect(result.result).toEqual({ declined: true });
100
+ });
101
+ it('returns ok: false on failure', async () => {
102
+ global.fetch.mockResolvedValue({
103
+ ok: false,
104
+ });
105
+ const result = await declineInvite('456');
106
+ expect(result.ok).toBe(false);
107
+ });
108
+ it('returns ok: false on error', async () => {
109
+ const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => { });
110
+ global.fetch.mockRejectedValue(new Error('Error'));
111
+ const result = await declineInvite('456');
112
+ expect(result.ok).toBe(false);
113
+ consoleSpy.mockRestore();
114
+ });
115
+ it('includes decline message in body', async () => {
116
+ global.fetch.mockResolvedValue({
117
+ ok: true,
118
+ json: () => Promise.resolve({}),
119
+ });
120
+ await declineInvite('456', 'Schedule conflict');
121
+ expect(global.fetch).toHaveBeenCalledWith(expect.any(String), expect.objectContaining({
122
+ body: JSON.stringify({ message: 'Schedule conflict' }),
123
+ }));
124
+ });
125
+ });
126
+ describe('cancelInvite', () => {
127
+ it('makes PUT request to cancel invite endpoint', async () => {
128
+ global.fetch.mockResolvedValue({
129
+ ok: true,
130
+ json: () => Promise.resolve({}),
131
+ });
132
+ await cancelInvite('789');
133
+ expect(global.fetch).toHaveBeenCalledWith('https://get-micdrop.com/api/v2/event-performers/789/cancel', expect.objectContaining({
134
+ method: 'PUT',
135
+ }));
136
+ });
137
+ it('returns ok: true on success', async () => {
138
+ global.fetch.mockResolvedValue({
139
+ ok: true,
140
+ json: () => Promise.resolve({ cancelled: true }),
141
+ });
142
+ const result = await cancelInvite('789');
143
+ expect(result.ok).toBe(true);
144
+ });
145
+ it('returns ok: false on failure', async () => {
146
+ global.fetch.mockResolvedValue({
147
+ ok: false,
148
+ });
149
+ const result = await cancelInvite('789');
150
+ expect(result.ok).toBe(false);
151
+ });
152
+ it('returns ok: false on error', async () => {
153
+ const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => { });
154
+ global.fetch.mockRejectedValue(new Error('Error'));
155
+ const result = await cancelInvite('789');
156
+ expect(result.ok).toBe(false);
157
+ consoleSpy.mockRestore();
158
+ });
159
+ });
160
+ describe('sendVenueMessage', () => {
161
+ it('makes POST request with auth token', async () => {
162
+ global.fetch.mockResolvedValue({
163
+ ok: true,
164
+ text: () => Promise.resolve(''),
165
+ });
166
+ await sendVenueMessage('123', 'Hello!');
167
+ expect(global.fetch).toHaveBeenCalledWith('https://get-micdrop.com/api/performer/sendVenueMessage/123', expect.objectContaining({
168
+ method: 'POST',
169
+ headers: {
170
+ 'Content-Type': 'application/json',
171
+ Authorization: 'Bearer test-token',
172
+ },
173
+ credentials: 'include',
174
+ }));
175
+ });
176
+ it('sends message in request body', async () => {
177
+ global.fetch.mockResolvedValue({
178
+ ok: true,
179
+ text: () => Promise.resolve(''),
180
+ });
181
+ await sendVenueMessage('123', 'Hello venue!');
182
+ expect(global.fetch).toHaveBeenCalledWith(expect.any(String), expect.objectContaining({
183
+ body: JSON.stringify({ message: 'Hello venue!' }),
184
+ }));
185
+ });
186
+ it('returns ok: true with default message on empty response', async () => {
187
+ global.fetch.mockResolvedValue({
188
+ ok: true,
189
+ text: () => Promise.resolve(''),
190
+ });
191
+ const result = await sendVenueMessage('123', 'Hello');
192
+ expect(result.ok).toBe(true);
193
+ expect(result.result).toEqual({ message: 'Email sent successfully' });
194
+ });
195
+ it('parses JSON response when present', async () => {
196
+ global.fetch.mockResolvedValue({
197
+ ok: true,
198
+ text: () => Promise.resolve(JSON.stringify({ sent: true, id: 456 })),
199
+ });
200
+ const result = await sendVenueMessage('123', 'Hello');
201
+ expect(result.ok).toBe(true);
202
+ expect(result.result).toEqual({ sent: true, id: 456 });
203
+ });
204
+ it('handles invalid JSON in response gracefully', async () => {
205
+ global.fetch.mockResolvedValue({
206
+ ok: true,
207
+ text: () => Promise.resolve('not json'),
208
+ });
209
+ const result = await sendVenueMessage('123', 'Hello');
210
+ expect(result.ok).toBe(true);
211
+ expect(result.result).toEqual({ message: 'Email sent successfully' });
212
+ });
213
+ it('returns ok: false on failure', async () => {
214
+ global.fetch.mockResolvedValue({
215
+ ok: false,
216
+ });
217
+ const result = await sendVenueMessage('123', 'Hello');
218
+ expect(result.ok).toBe(false);
219
+ });
220
+ it('returns ok: false on error', async () => {
221
+ const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => { });
222
+ global.fetch.mockRejectedValue(new Error('Network error'));
223
+ const result = await sendVenueMessage('123', 'Hello');
224
+ expect(result.ok).toBe(false);
225
+ consoleSpy.mockRestore();
226
+ });
227
+ });
228
+ describe('getEventUrl', () => {
229
+ it('returns ticketing URL for venue', () => {
230
+ const url = getEventUrl('123');
231
+ expect(url).toBe('https://get-micdrop.com/ticketing/events/123');
232
+ });
233
+ it('handles numeric venue ID', () => {
234
+ const url = getEventUrl(456);
235
+ expect(url).toBe('https://get-micdrop.com/ticketing/events/456');
236
+ });
237
+ it('handles string venue ID', () => {
238
+ const url = getEventUrl('venue-abc');
239
+ expect(url).toBe('https://get-micdrop.com/ticketing/events/venue-abc');
240
+ });
241
+ });
242
+ });
@@ -1,3 +1,5 @@
1
+ const forms = require('@tailwindcss/forms');
2
+
1
3
  const colors = {
2
4
  'bg-primary': 'hsl(var(--bg-primary) / <alpha-value>)',
3
5
  'bg-secondary': 'hsl(var(--bg-secondary) / <alpha-value>)',
@@ -79,4 +81,7 @@ module.exports = {
79
81
  },
80
82
  },
81
83
  },
84
+ // @tailwindcss/forms is included here so consuming apps get it via the preset.
85
+ // Consuming apps should NOT also @plugin "@tailwindcss/forms" to avoid double-loading.
86
+ plugins: [forms],
82
87
  };
@@ -62,6 +62,7 @@ declare const zIndex: {
62
62
  70: string;
63
63
  100: string;
64
64
  };
65
+ import forms = require("@tailwindcss/forms");
65
66
  export namespace theme {
66
67
  namespace extend {
67
68
  export { colors };
@@ -74,5 +75,6 @@ export namespace theme {
74
75
  }
75
76
  }
76
77
  }
78
+ export let plugins: (typeof forms)[];
77
79
  export {};
78
80
  //# sourceMappingURL=preset.d.cts.map
@@ -1 +1 @@
1
- {"version":3,"file":"preset.d.cts","sourceRoot":"","sources":["../../../../src/lib/tailwind/preset.cjs"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA4BE;AAEF;;;;;;;;;;EAUE;AAEF;;;;;;;;;;EAUE;AAEF;;;;;;;;;;;;EAYE"}
1
+ {"version":3,"file":"preset.d.cts","sourceRoot":"","sources":["../../../../src/lib/tailwind/preset.cjs"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA4BE;AAEF;;;;;;;;;;EAUE;AAEF;;;;;;;;;;EAUE;AAEF;;;;;;;;;;;;EAYE"}
@@ -62,8 +62,8 @@ describe('spacing tokens', () => {
62
62
  expect(gapScale.semantic.layout).toBe('gap-8');
63
63
  expect(gapScale.semantic.page).toBe('gap-12');
64
64
  });
65
- it('has 11 semantic gap values', () => {
66
- expect(Object.keys(gapScale.semantic)).toHaveLength(11);
65
+ it('has 12 semantic gap values', () => {
66
+ expect(Object.keys(gapScale.semantic)).toHaveLength(12);
67
67
  });
68
68
  });
69
69
  describe('numeric', () => {
@@ -0,0 +1,124 @@
1
+ /* ==========================================================================
2
+ BASE RESETS
3
+ Shared form resets, accessibility utilities, and platform fixes.
4
+ Imported via base.css -- every Micdrop app gets these automatically.
5
+
6
+ IMPORTANT: Do NOT add `appearance: none` to inputs here.
7
+ @tailwindcss/forms handles form element appearance. Setting it globally
8
+ breaks native checkboxes and radios (see MIC-859).
9
+ ========================================================================== */
10
+
11
+ /* --------------------------------------------------------------------------
12
+ iOS ZOOM PREVENTION
13
+ iOS Safari auto-zooms when focusing inputs with font-size < 16px.
14
+ Set minimum 16px on text inputs only. Exclude checkbox/radio which
15
+ don't trigger zoom and need their native appearance preserved.
16
+ -------------------------------------------------------------------------- */
17
+ input:not([type="checkbox"]):not([type="radio"]),
18
+ textarea,
19
+ select {
20
+ font-size: 16px;
21
+ }
22
+
23
+ /* --------------------------------------------------------------------------
24
+ FORM FONT INHERITANCE
25
+ Form elements don't inherit font by default in most browsers.
26
+ -------------------------------------------------------------------------- */
27
+ input,
28
+ button {
29
+ font-size: inherit;
30
+ font-family: inherit;
31
+ }
32
+
33
+ /* --------------------------------------------------------------------------
34
+ FOCUS MANAGEMENT
35
+ Hide focus ring on mouse/touch interaction, show on keyboard navigation.
36
+ This provides clean UI for mouse users while maintaining accessibility
37
+ for keyboard users.
38
+ -------------------------------------------------------------------------- */
39
+ button:focus:not(:focus-visible) {
40
+ outline: none;
41
+ }
42
+
43
+ /* --------------------------------------------------------------------------
44
+ SCREEN READER ONLY
45
+ Visually hides content while keeping it accessible to screen readers.
46
+ Standard pattern matching Tailwind's built-in sr-only utility.
47
+ This is a fallback for non-Tailwind contexts -- Tailwind's version
48
+ will override it due to CSS specificity/source order.
49
+ -------------------------------------------------------------------------- */
50
+ .sr-only {
51
+ position: absolute;
52
+ width: 1px;
53
+ height: 1px;
54
+ padding: 0;
55
+ margin: -1px;
56
+ overflow: hidden;
57
+ clip: rect(0, 0, 0, 0);
58
+ white-space: nowrap;
59
+ border-width: 0;
60
+ }
61
+
62
+ /* --------------------------------------------------------------------------
63
+ SCREEN READER ONLY - FOCUSABLE
64
+ Makes sr-only content visible when focused. Enables skip-navigation
65
+ links and other focus-activated accessibility patterns.
66
+ -------------------------------------------------------------------------- */
67
+ .sr-only-focusable:focus,
68
+ .sr-only-focusable:active {
69
+ position: static;
70
+ width: auto;
71
+ height: auto;
72
+ padding: 0;
73
+ margin: 0;
74
+ overflow: visible;
75
+ clip: auto;
76
+ white-space: normal;
77
+ }
78
+
79
+ /* --------------------------------------------------------------------------
80
+ REDUCED MOTION
81
+ Respects user's motion preference. Disables all animations and
82
+ transitions when prefers-reduced-motion is set.
83
+ -------------------------------------------------------------------------- */
84
+ @media (prefers-reduced-motion: reduce) {
85
+ *,
86
+ *::before,
87
+ *::after {
88
+ animation-duration: 0.01ms !important;
89
+ animation-iteration-count: 1 !important;
90
+ transition-duration: 0.01ms !important;
91
+ scroll-behavior: auto !important;
92
+ }
93
+ }
94
+
95
+ /* --------------------------------------------------------------------------
96
+ CHROME AUTOFILL DARK MODE FIX
97
+ Chrome's autofill sets a hardcoded light background on inputs.
98
+ In dark mode, this creates a jarring white flash. Override with
99
+ the app's dark background color.
100
+ -------------------------------------------------------------------------- */
101
+ .dark input:-webkit-autofill,
102
+ .dark input:-webkit-autofill:hover,
103
+ .dark input:-webkit-autofill:focus,
104
+ .dark textarea:-webkit-autofill,
105
+ .dark textarea:-webkit-autofill:hover,
106
+ .dark textarea:-webkit-autofill:focus {
107
+ -webkit-box-shadow: 0 0 0 1000px hsl(var(--bg-primary)) inset !important;
108
+ -webkit-text-fill-color: hsl(var(--text-primary)) !important;
109
+ background-color: hsl(var(--bg-primary)) !important;
110
+ }
111
+
112
+ /* --------------------------------------------------------------------------
113
+ DESKTOP FONT SCALING
114
+ 110% base font on desktop makes all rem-based sizing 10% larger:
115
+ - Buttons: 44px -> 48px (exceeds WCAG AAA 44px target)
116
+ - Text: More readable without zooming
117
+ - Touch targets: More accessible
118
+ Mobile stays at 100% (already optimized for touch).
119
+ -------------------------------------------------------------------------- */
120
+ @media (min-width: 768px) {
121
+ html {
122
+ font-size: 110%;
123
+ }
124
+ }
@@ -71,6 +71,7 @@ export declare const gapScale: {
71
71
  readonly group: "gap-3";
72
72
  readonly tight: "gap-4";
73
73
  readonly content: "gap-4";
74
+ readonly card: "gap-5";
74
75
  readonly standard: "gap-6";
75
76
  readonly section: "gap-6";
76
77
  readonly spacious: "gap-8";
@@ -114,6 +115,7 @@ export declare const gapMap: {
114
115
  readonly group: "gap-3";
115
116
  readonly tight: "gap-4";
116
117
  readonly content: "gap-4";
118
+ readonly card: "gap-5";
117
119
  readonly standard: "gap-6";
118
120
  readonly section: "gap-6";
119
121
  readonly spacious: "gap-8";
@@ -1 +1 @@
1
- {"version":3,"file":"spacing.d.ts","sourceRoot":"","sources":["../../src/lib/tokens/spacing.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,eAAO,MAAM,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAoCV,CAAC;AAEX,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;CAiBjB,CAAC;AAEX,MAAM,MAAM,UAAU,GAAG,MAAM,OAAO,OAAO,CAAC;AAC9C,MAAM,MAAM,YAAY,GAAG,MAAM,OAAO,cAAc,CAAC;AAEvD;;;GAGG;AACH,eAAO,MAAM,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA2CX,CAAC;AAEX,yCAAyC;AACzC,eAAO,MAAM,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAKT,CAAC;AAEX,MAAM,MAAM,cAAc,GAAG,MAAM,OAAO,QAAQ,CAAC,QAAQ,CAAC;AAC5D,MAAM,MAAM,aAAa,GAAG,MAAM,OAAO,QAAQ,CAAC,OAAO,CAAC;AAC1D,MAAM,MAAM,YAAY,GAAG,MAAM,OAAO,QAAQ,CAAC,MAAM,CAAC;AACxD,MAAM,MAAM,UAAU,GAAG,MAAM,OAAO,QAAQ,CAAC,IAAI,CAAC;AACpD,MAAM,MAAM,MAAM,GAAG,MAAM,OAAO,MAAM,CAAC;AAEzC;;GAEG;AACH,eAAO,MAAM,UAAU;;;;;;CAMb,CAAC;AAEX,MAAM,MAAM,QAAQ,GAAG,MAAM,OAAO,UAAU,CAAC;AAE/C;;GAEG;AACH,eAAO,MAAM,YAAY;;;;;;;CAOf,CAAC;AAEX,MAAM,MAAM,UAAU,GAAG,MAAM,OAAO,YAAY,CAAC"}
1
+ {"version":3,"file":"spacing.d.ts","sourceRoot":"","sources":["../../src/lib/tokens/spacing.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,eAAO,MAAM,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAoCV,CAAC;AAEX,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;CAiBjB,CAAC;AAEX,MAAM,MAAM,UAAU,GAAG,MAAM,OAAO,OAAO,CAAC;AAC9C,MAAM,MAAM,YAAY,GAAG,MAAM,OAAO,cAAc,CAAC;AAEvD;;;GAGG;AACH,eAAO,MAAM,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA4CX,CAAC;AAEX,yCAAyC;AACzC,eAAO,MAAM,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAKT,CAAC;AAEX,MAAM,MAAM,cAAc,GAAG,MAAM,OAAO,QAAQ,CAAC,QAAQ,CAAC;AAC5D,MAAM,MAAM,aAAa,GAAG,MAAM,OAAO,QAAQ,CAAC,OAAO,CAAC;AAC1D,MAAM,MAAM,YAAY,GAAG,MAAM,OAAO,QAAQ,CAAC,MAAM,CAAC;AACxD,MAAM,MAAM,UAAU,GAAG,MAAM,OAAO,QAAQ,CAAC,IAAI,CAAC;AACpD,MAAM,MAAM,MAAM,GAAG,MAAM,OAAO,MAAM,CAAC;AAEzC;;GAEG;AACH,eAAO,MAAM,UAAU;;;;;;CAMb,CAAC;AAEX,MAAM,MAAM,QAAQ,GAAG,MAAM,OAAO,UAAU,CAAC;AAE/C;;GAEG;AACH,eAAO,MAAM,YAAY;;;;;;;CAOf,CAAC;AAEX,MAAM,MAAM,UAAU,GAAG,MAAM,OAAO,YAAY,CAAC"}
@@ -70,6 +70,7 @@ export const gapScale = {
70
70
  group: 'gap-3', // 12px - Between related elements
71
71
  tight: 'gap-4', // 16px - Tight grid/content spacing
72
72
  content: 'gap-4', // 16px - Default content spacing
73
+ card: 'gap-5', // 20px - Card internal spacing
73
74
  standard: 'gap-6', // 24px - Standard grid spacing
74
75
  section: 'gap-6', // 24px - Between sections
75
76
  spacious: 'gap-8', // 32px - Spacious grid items
@@ -40,6 +40,7 @@
40
40
  --Text-Primary: var(--text-primary);
41
41
  --Text-Secondary: var(--text-secondary);
42
42
  --Text-Tartiary: var(--text-tertiary);
43
+ --Text-Tertiary: var(--text-tertiary);
43
44
  --Text-Head: var(--text-head);
44
45
  --Brand-Primary: var(--brand-primary);
45
46
  --Accent-Warning: var(--accent-warning);
@@ -49,7 +50,6 @@
49
50
  --Stroke-Secondary: var(--stroke-secondary);
50
51
  }
51
52
 
52
- [data-theme="dark"],
53
53
  .dark {
54
54
  --bg-primary: 220 20% 18%;
55
55
  --bg-secondary: 221 50% 10%;
@@ -26,9 +26,9 @@
26
26
  * Common flex patterns for consistent layouts
27
27
  * ========================================================================== */
28
28
 
29
- /* Horizontal flex with centered items */
29
+ /* Flex row with vertically centered items (base for flex-center-* variants) */
30
30
  .flex-center {
31
- @apply flex items-center justify-center;
31
+ @apply flex items-center;
32
32
  }
33
33
 
34
34
  /* Inline flex centered */
@@ -350,4 +350,81 @@
350
350
  .desktop-flex {
351
351
  @apply hidden md:flex;
352
352
  }
353
+
354
+ /* ==========================================================================
355
+ * ANIMATION UTILITIES
356
+ * Shared animations used across multiple Micdrop apps
357
+ * ========================================================================== */
358
+
359
+ /* Shake animation - used in verify-phone and login flows */
360
+ .shake {
361
+ animation: shake 0.625s linear;
362
+ }
363
+
364
+ /* Skeleton loading - shimmer effect for loading placeholders */
365
+ .skeleton-loading {
366
+ animation: skeleton-shimmer 2.5s ease-in-out infinite;
367
+ background: linear-gradient(
368
+ 90deg,
369
+ #e5e7eb 0%,
370
+ #e5e7eb 40%,
371
+ #f3f4f6 50%,
372
+ #e5e7eb 60%,
373
+ #e5e7eb 100%
374
+ );
375
+ background-size: 200% 100%;
376
+ }
377
+
378
+ .dark .skeleton-loading {
379
+ background: linear-gradient(
380
+ 90deg,
381
+ #374151 0%,
382
+ #374151 40%,
383
+ #4b5563 50%,
384
+ #374151 60%,
385
+ #374151 100%
386
+ );
387
+ background-size: 200% 100%;
388
+ }
389
+ }
390
+
391
+ /* Animation keyframes - placed outside @layer for browser compatibility */
392
+ @keyframes shake {
393
+ 8%,
394
+ 41% {
395
+ transform: translateX(-10px);
396
+ }
397
+ 25%,
398
+ 58% {
399
+ transform: translateX(10px);
400
+ }
401
+ 75% {
402
+ transform: translateX(-5px);
403
+ }
404
+ 92% {
405
+ transform: translateX(5px);
406
+ }
407
+ 0%,
408
+ 100% {
409
+ transform: translateX(0);
410
+ }
411
+ }
412
+
413
+ @keyframes skeleton-shimmer {
414
+ 0% {
415
+ background-position: -200% 0;
416
+ }
417
+ 100% {
418
+ background-position: 200% 0;
419
+ }
420
+ }
421
+
422
+ @keyframes skeleton-pulse {
423
+ 0%,
424
+ 100% {
425
+ opacity: 1;
426
+ }
427
+ 50% {
428
+ opacity: 0.5;
429
+ }
353
430
  }