@wherabouts/react-ui 0.1.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.
@@ -0,0 +1,288 @@
1
+ /* ───────────────────────────────────────────────────────────────
2
+ Wherabouts React UI — Design Tokens & Component Styles
3
+ ─────────────────────────────────────────────────────────────── */
4
+
5
+ /* ── Light theme (default) ──────────────────────────────────── */
6
+ :root {
7
+ /* Colors — WCAG AA compliant (4.5:1+ contrast ratios) */
8
+ --wherabouts-color-border: hsl(214.3 31.8% 91.4%);
9
+ --wherabouts-color-input-bg: hsl(0 0% 100%);
10
+ --wherabouts-color-input-text: hsl(222.2 84% 4.9%);
11
+ --wherabouts-color-placeholder: hsl(215.4 16.3% 46.9%);
12
+ --wherabouts-color-focus-ring: hsl(221.2 83.2% 53.3%);
13
+ --wherabouts-color-dropdown-bg: hsl(0 0% 100%);
14
+ --wherabouts-color-dropdown-border: hsl(214.3 31.8% 91.4%);
15
+ --wherabouts-color-dropdown-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1);
16
+ --wherabouts-color-item-bg: hsl(0 0% 100%);
17
+ --wherabouts-color-item-bg-hover: hsl(210 40% 96.1%);
18
+ --wherabouts-color-item-bg-active: hsl(221.2 83.2% 53.3% / 0.1);
19
+ --wherabouts-color-item-text: hsl(222.2 84% 4.9%);
20
+ --wherabouts-color-error: hsl(0 84.2% 60.2%);
21
+ --wherabouts-color-error-border: hsl(0 84.2% 60.2%);
22
+ --wherabouts-color-text-muted: hsl(215.4 16.3% 46.9%);
23
+
24
+ /* Spacing */
25
+ --wherabouts-spacing-xs: 0.25rem;
26
+ --wherabouts-spacing-sm: 0.5rem;
27
+ --wherabouts-spacing-md: 1rem;
28
+ --wherabouts-spacing-lg: 1.5rem;
29
+
30
+ /* Typography */
31
+ --wherabouts-font-size-xs: 0.75rem;
32
+ --wherabouts-font-size-sm: 0.875rem;
33
+ --wherabouts-font-size-base: 1rem;
34
+ --wherabouts-font-weight-normal: 400;
35
+ --wherabouts-font-weight-medium: 500;
36
+
37
+ /* Borders & Radius */
38
+ --wherabouts-border-radius: 0.5rem;
39
+ --wherabouts-border-radius-sm: 0.375rem;
40
+ --wherabouts-border-width: 1px;
41
+
42
+ /* Z-Index */
43
+ --wherabouts-z-dropdown: 50;
44
+
45
+ /* Transitions */
46
+ --wherabouts-transition-fast: 150ms ease-in-out;
47
+ --wherabouts-transition-base: 200ms ease-in-out;
48
+ }
49
+
50
+ /* ── Dark theme ─────────────────────────────────────────────── */
51
+ .dark {
52
+ --wherabouts-color-border: hsl(217.2 32.6% 17.5%);
53
+ --wherabouts-color-input-bg: hsl(222.2 84% 4.9%);
54
+ --wherabouts-color-input-text: hsl(210 40% 98%);
55
+ --wherabouts-color-placeholder: hsl(215.4 16.3% 56.9%);
56
+ --wherabouts-color-focus-ring: hsl(221.2 83.2% 63.3%);
57
+ --wherabouts-color-dropdown-bg: hsl(222.2 84% 4.9%);
58
+ --wherabouts-color-dropdown-border: hsl(217.2 32.6% 17.5%);
59
+ --wherabouts-color-dropdown-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.3);
60
+ --wherabouts-color-item-bg: hsl(222.2 84% 4.9%);
61
+ --wherabouts-color-item-bg-hover: hsl(222.2 84% 10%);
62
+ --wherabouts-color-item-bg-active: hsl(221.2 83.2% 53.3% / 0.2);
63
+ --wherabouts-color-item-text: hsl(210 40% 98%);
64
+ --wherabouts-color-error: hsl(0 84.2% 70.2%);
65
+ --wherabouts-color-error-border: hsl(0 84.2% 70.2%);
66
+ --wherabouts-color-text-muted: hsl(215.4 16.3% 56.9%);
67
+ }
68
+
69
+ /* ── AddressAutocomplete & Input ────────────────────────────── */
70
+ [data-slot="address-autocomplete"] {
71
+ position: relative;
72
+ width: 100%;
73
+ }
74
+
75
+ [data-slot="address-input"] {
76
+ display: block;
77
+ width: 100%;
78
+ padding: var(--wherabouts-spacing-sm) var(--wherabouts-spacing-md);
79
+ border: var(--wherabouts-border-width) solid
80
+ var(--wherabouts-color-border);
81
+ border-radius: var(--wherabouts-border-radius);
82
+ background-color: var(--wherabouts-color-input-bg);
83
+ color: var(--wherabouts-color-input-text);
84
+ font-size: var(--wherabouts-font-size-base);
85
+ font-weight: var(--wherabouts-font-weight-normal);
86
+ transition: border-color var(--wherabouts-transition-fast),
87
+ box-shadow var(--wherabouts-transition-fast);
88
+ }
89
+
90
+ [data-slot="address-input"]::placeholder {
91
+ color: var(--wherabouts-color-placeholder);
92
+ }
93
+
94
+ [data-slot="address-input"]:focus {
95
+ outline: none;
96
+ border-color: var(--wherabouts-color-focus-ring);
97
+ box-shadow: 0 0 0 3px
98
+ hsl(221.2 83.2% 53.3% / 0.1);
99
+ }
100
+
101
+ [data-slot="address-input"]:disabled {
102
+ opacity: 0.5;
103
+ cursor: not-allowed;
104
+ background-color: hsl(0 0% 95% / 0.5);
105
+ }
106
+
107
+ [data-slot="address-input"][aria-invalid="true"] {
108
+ border-color: var(--wherabouts-color-error);
109
+ }
110
+
111
+ [data-slot="address-input"][aria-invalid="true"]:focus {
112
+ box-shadow: 0 0 0 3px
113
+ hsl(0 84.2% 60.2% / 0.1);
114
+ }
115
+
116
+ /* ── Dropdown Listbox ──────────────────────────────────────── */
117
+ [data-slot="address-listbox"] {
118
+ position: absolute;
119
+ top: 100%;
120
+ left: 0;
121
+ right: 0;
122
+ margin-top: var(--wherabouts-spacing-xs);
123
+ max-height: 300px;
124
+ overflow-y: auto;
125
+ z-index: var(--wherabouts-z-dropdown);
126
+ list-style: none;
127
+ padding: var(--wherabouts-spacing-sm);
128
+ margin: var(--wherabouts-spacing-xs) 0 0 0;
129
+ border: var(--wherabouts-border-width) solid
130
+ var(--wherabouts-color-dropdown-border);
131
+ border-radius: var(--wherabouts-border-radius);
132
+ background-color: var(--wherabouts-color-dropdown-bg);
133
+ box-shadow: var(--wherabouts-color-dropdown-shadow);
134
+ }
135
+
136
+ [data-slot="address-listbox"]::-webkit-scrollbar {
137
+ width: 8px;
138
+ }
139
+
140
+ [data-slot="address-listbox"]::-webkit-scrollbar-track {
141
+ background: transparent;
142
+ }
143
+
144
+ [data-slot="address-listbox"]::-webkit-scrollbar-thumb {
145
+ background: var(--wherabouts-color-border);
146
+ border-radius: 4px;
147
+ }
148
+
149
+ /* ── Suggestion Item ───────────────────────────────────────── */
150
+ [data-slot="address-item"] {
151
+ padding: var(--wherabouts-spacing-sm) var(--wherabouts-spacing-md);
152
+ margin: 2px 0;
153
+ border-radius: var(--wherabouts-border-radius-sm);
154
+ background-color: var(--wherabouts-color-item-bg);
155
+ color: var(--wherabouts-color-item-text);
156
+ cursor: pointer;
157
+ font-size: var(--wherabouts-font-size-base);
158
+ transition: background-color var(--wherabouts-transition-fast);
159
+ min-height: 44px;
160
+ display: flex;
161
+ align-items: center;
162
+ }
163
+
164
+ [data-slot="address-item"]:hover {
165
+ background-color: var(--wherabouts-color-item-bg-hover);
166
+ }
167
+
168
+ [data-slot="address-item"][aria-selected="true"] {
169
+ background-color: var(--wherabouts-color-item-bg-active);
170
+ }
171
+
172
+ /* ── Loading / Error / Empty States ────────────────────────── */
173
+ [data-slot="address-item"][data-status="loading"],
174
+ [data-slot="address-item"][data-status="error"],
175
+ [data-slot="address-item"][data-status="empty"] {
176
+ cursor: default;
177
+ justify-content: center;
178
+ font-size: var(--wherabouts-font-size-sm);
179
+ color: var(--wherabouts-color-text-muted);
180
+ min-height: 40px;
181
+ }
182
+
183
+ [data-slot="address-item"][data-status="error"] {
184
+ color: var(--wherabouts-color-error);
185
+ }
186
+
187
+ /* ── Form Field ────────────────────────────────────────────── */
188
+ [data-slot="address-form-field"] {
189
+ display: flex;
190
+ flex-direction: column;
191
+ gap: var(--wherabouts-spacing-sm);
192
+ }
193
+
194
+ [data-slot="address-form-field"] label {
195
+ display: block;
196
+ font-size: var(--wherabouts-font-size-sm);
197
+ font-weight: var(--wherabouts-font-weight-medium);
198
+ color: var(--wherabouts-color-input-text);
199
+ }
200
+
201
+ [data-slot="address-form-field"] label [aria-hidden="true"] {
202
+ color: var(--wherabouts-color-error);
203
+ margin-left: 2px;
204
+ }
205
+
206
+ [data-slot="address-form-field"] [role="alert"] {
207
+ display: block;
208
+ font-size: var(--wherabouts-font-size-sm);
209
+ color: var(--wherabouts-color-error);
210
+ margin-top: 2px;
211
+ }
212
+
213
+ /* ── Field Group ───────────────────────────────────────────── */
214
+ [data-slot="address-field-group"] {
215
+ display: flex;
216
+ flex-direction: column;
217
+ gap: var(--wherabouts-spacing-md);
218
+ }
219
+
220
+ [data-slot="address-field-group-inputs"] {
221
+ display: grid;
222
+ grid-template-columns: repeat(2, 1fr);
223
+ gap: var(--wherabouts-spacing-md);
224
+ }
225
+
226
+ [data-slot="address-field-group-inputs"] > div:nth-child(4) {
227
+ grid-column: 1 / 2;
228
+ }
229
+
230
+ @media (max-width: 640px) {
231
+ [data-slot="address-field-group-inputs"] {
232
+ grid-template-columns: 1fr;
233
+ }
234
+
235
+ [data-slot="address-listbox"] {
236
+ max-height: 250px;
237
+ }
238
+
239
+ [data-slot="address-item"] {
240
+ min-height: 56px;
241
+ }
242
+ }
243
+
244
+ /* ── Reverse/Forward Geocode Input ─────────────────────────── */
245
+ [data-slot="geocode-input"] {
246
+ display: block;
247
+ width: 100%;
248
+ padding: var(--wherabouts-spacing-sm) var(--wherabouts-spacing-md);
249
+ border: var(--wherabouts-border-width) solid
250
+ var(--wherabouts-color-border);
251
+ border-radius: var(--wherabouts-border-radius);
252
+ background-color: hsl(0 0% 97%);
253
+ color: var(--wherabouts-color-input-text);
254
+ font-size: var(--wherabouts-font-size-base);
255
+ cursor: default;
256
+ }
257
+
258
+ .dark [data-slot="geocode-input"] {
259
+ background-color: hsl(222.2 84% 8%);
260
+ }
261
+
262
+ /* ── Accessibility & Focus Visibility ──────────────────────── */
263
+ @media (prefers-reduced-motion: reduce) {
264
+ [data-slot="address-input"],
265
+ [data-slot="address-item"] {
266
+ transition: none;
267
+ }
268
+ }
269
+
270
+ /* Ensure sufficient touch targets on mobile */
271
+ @media (max-width: 768px) {
272
+ [data-slot="address-input"],
273
+ [data-slot="address-item"] {
274
+ min-height: 44px;
275
+ }
276
+ }
277
+
278
+ /* High contrast mode support */
279
+ @media (prefers-contrast: more) {
280
+ [data-slot="address-input"],
281
+ [data-slot="address-listbox"] {
282
+ border-width: 2px;
283
+ }
284
+
285
+ [data-slot="address-item"][aria-selected="true"] {
286
+ text-decoration: underline;
287
+ }
288
+ }
package/package.json ADDED
@@ -0,0 +1,54 @@
1
+ {
2
+ "name": "@wherabouts/react-ui",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "description": "Production-ready React components for Wherabouts SDK — address autocomplete, geocoding, and form fields with accessibility and customization.",
6
+ "license": "UNLICENSED",
7
+ "publishConfig": {
8
+ "access": "public"
9
+ },
10
+ "exports": {
11
+ ".": {
12
+ "types": "./dist/index.d.ts",
13
+ "import": "./dist/index.js",
14
+ "require": "./dist/index.cjs"
15
+ },
16
+ "./styles.css": "./dist/styles.css"
17
+ },
18
+ "files": [
19
+ "dist",
20
+ "README.md",
21
+ "CHANGELOG.md"
22
+ ],
23
+ "scripts": {
24
+ "build": "tsup && node scripts/build-css.mjs",
25
+ "dev": "tsup --watch",
26
+ "test": "vitest run",
27
+ "test:watch": "vitest",
28
+ "prepublishOnly": "pnpm build"
29
+ },
30
+ "peerDependencies": {
31
+ "react": ">=19.0.0",
32
+ "react-dom": ">=19.0.0",
33
+ "@wherabouts/sdk": ">=0.4.2",
34
+ "@wherabouts/react": ">=0.2.0"
35
+ },
36
+ "devDependencies": {
37
+ "@testing-library/jest-dom": "^6.1.5",
38
+ "@testing-library/react": "^15.0.0",
39
+ "@testing-library/user-event": "^14.5.1",
40
+ "@types/react": "catalog:",
41
+ "@types/react-dom": "catalog:",
42
+ "@wherabouts/react": "workspace:*",
43
+ "@wherabouts/sdk": "workspace:*",
44
+ "class-variance-authority": "^0.7.0",
45
+ "clsx": "^2.1.1",
46
+ "jsdom": "^23.0.1",
47
+ "react": "catalog:",
48
+ "react-dom": "catalog:",
49
+ "tailwind-merge": "^2.3.0",
50
+ "tsup": "^8.0.2",
51
+ "typescript": "^5.4.4",
52
+ "vitest": "^1.2.0"
53
+ }
54
+ }