@signalk/app-dock 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,412 @@
1
+ /* ─── Reset & base ─────────────────────────────────────────────────────────── */
2
+ *,
3
+ *::before,
4
+ *::after {
5
+ box-sizing: border-box;
6
+ margin: 0;
7
+ padding: 0;
8
+ }
9
+
10
+ html,
11
+ body {
12
+ width: 100%;
13
+ height: 100%;
14
+ overflow: hidden;
15
+ background: #000;
16
+ font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Display', 'Helvetica Neue', Arial, sans-serif;
17
+ -webkit-font-smoothing: antialiased;
18
+ touch-action: none;
19
+ }
20
+
21
+ /* ─── iFrame container ──────────────────────────────────────────────────────── */
22
+ #iframe-container {
23
+ position: fixed;
24
+ inset: 0;
25
+ z-index: 1;
26
+ }
27
+
28
+ #iframe-container iframe {
29
+ position: absolute;
30
+ inset: 0;
31
+ width: 100%;
32
+ height: 100%;
33
+ border: none;
34
+ display: none;
35
+ touch-action: auto;
36
+ }
37
+
38
+ #iframe-container iframe.active {
39
+ display: block;
40
+ }
41
+
42
+ /* ─── Welcome screen ───────────────────────────────────────────────────────── */
43
+ #idle-hint {
44
+ position: fixed;
45
+ inset: 0;
46
+ z-index: 2;
47
+ display: flex;
48
+ flex-direction: column;
49
+ align-items: center;
50
+ justify-content: center;
51
+ gap: 12px;
52
+ pointer-events: none;
53
+ opacity: 0;
54
+ animation: idle-fade-in 1.2s ease 0.6s forwards;
55
+ }
56
+
57
+ #idle-hint.hidden {
58
+ display: none;
59
+ }
60
+
61
+ #idle-logo {
62
+ width: 72px;
63
+ height: 72px;
64
+ border-radius: 16px;
65
+ opacity: 0.85;
66
+ margin-bottom: 4px;
67
+ }
68
+
69
+ #idle-title {
70
+ color: rgba(255, 255, 255, 0.7);
71
+ font-size: 22px;
72
+ font-weight: 700;
73
+ letter-spacing: 0.02em;
74
+ }
75
+
76
+ #idle-version {
77
+ color: rgba(255, 255, 255, 0.35);
78
+ font-size: 11px;
79
+ font-weight: 500;
80
+ letter-spacing: 0.05em;
81
+ margin-top: -8px;
82
+ }
83
+
84
+ #idle-hint-text {
85
+ color: rgba(255, 255, 255, 0.55);
86
+ font-size: 15px;
87
+ font-weight: 500;
88
+ letter-spacing: 0.02em;
89
+ text-align: center;
90
+ padding: 0 32px;
91
+ max-width: 480px;
92
+ line-height: 1.6;
93
+ margin-top: 12px;
94
+ }
95
+
96
+ @keyframes idle-fade-in {
97
+ to {
98
+ opacity: 1;
99
+ }
100
+ }
101
+
102
+ /* ─── Backdrop ──────────────────────────────────────────────────────────────── */
103
+ #dock-backdrop {
104
+ position: fixed;
105
+ inset: 0;
106
+ z-index: 10;
107
+ background: rgba(0, 0, 0, 0.45);
108
+ opacity: 0;
109
+ pointer-events: none;
110
+ transition: opacity 0.28s ease;
111
+ backdrop-filter: blur(2px);
112
+ -webkit-backdrop-filter: blur(2px);
113
+ }
114
+
115
+ #dock-backdrop.visible {
116
+ opacity: 1;
117
+ pointer-events: auto;
118
+ }
119
+
120
+ /* ─── Dock shell ────────────────────────────────────────────────────────────── */
121
+ #dock {
122
+ position: fixed;
123
+ z-index: 20;
124
+ opacity: 0;
125
+ pointer-events: none;
126
+ transition:
127
+ transform 0.42s cubic-bezier(0.34, 1.45, 0.64, 1),
128
+ opacity 0.25s ease;
129
+ }
130
+
131
+ #dock.visible {
132
+ opacity: 1;
133
+ pointer-events: auto;
134
+ }
135
+
136
+ /* --- Position: bottom (default) --- */
137
+ #dock.pos-bottom {
138
+ bottom: max(16px, env(safe-area-inset-bottom, 16px));
139
+ left: 50%;
140
+ transform: translateX(-50%) translateY(calc(100% + 32px)) scale(0.92);
141
+ }
142
+ #dock.pos-bottom.visible {
143
+ transform: translateX(-50%) translateY(0) scale(1);
144
+ }
145
+
146
+ /* --- Position: top --- */
147
+ #dock.pos-top {
148
+ top: max(16px, env(safe-area-inset-top, 16px));
149
+ left: 50%;
150
+ transform: translateX(-50%) translateY(calc(-100% - 32px)) scale(0.92);
151
+ }
152
+ #dock.pos-top.visible {
153
+ transform: translateX(-50%) translateY(0) scale(1);
154
+ }
155
+
156
+ /* --- Position: left --- */
157
+ #dock.pos-left {
158
+ left: max(16px, env(safe-area-inset-left, 16px));
159
+ top: 50%;
160
+ transform: translateY(-50%) translateX(calc(-100% - 32px)) scale(0.92);
161
+ }
162
+ #dock.pos-left.visible {
163
+ transform: translateY(-50%) translateX(0) scale(1);
164
+ }
165
+
166
+ /* --- Position: right --- */
167
+ #dock.pos-right {
168
+ right: max(16px, env(safe-area-inset-right, 16px));
169
+ top: 50%;
170
+ transform: translateY(-50%) translateX(calc(100% + 32px)) scale(0.92);
171
+ }
172
+ #dock.pos-right.visible {
173
+ transform: translateY(-50%) translateX(0) scale(1);
174
+ }
175
+
176
+ /* ─── Dock pill ─────────────────────────────────────────────────────────────── */
177
+ #dock-inner {
178
+ display: flex;
179
+ flex-direction: row;
180
+ gap: 10px;
181
+ padding: 12px 18px;
182
+
183
+ /* Frosted glass - macOS Sequoia style */
184
+ background: rgba(30, 30, 32, 0.55);
185
+ backdrop-filter: blur(60px) saturate(180%);
186
+ -webkit-backdrop-filter: blur(60px) saturate(180%);
187
+
188
+ border: 1px solid rgba(255, 255, 255, 0.18);
189
+ border-radius: 26px;
190
+
191
+ box-shadow:
192
+ 0 16px 48px rgba(0, 0, 0, 0.5),
193
+ 0 2px 6px rgba(0, 0, 0, 0.25),
194
+ inset 0 0.5px 0 rgba(255, 255, 255, 0.15);
195
+ }
196
+
197
+ /* Alignment: icons grow away from the dock edge */
198
+ #dock-inner.align-bottom {
199
+ align-items: flex-end;
200
+ }
201
+ #dock-inner.align-top {
202
+ align-items: flex-start;
203
+ }
204
+ #dock-inner.align-left,
205
+ #dock-inner.align-right {
206
+ align-items: center;
207
+ }
208
+
209
+ #dock-inner.layout-vertical {
210
+ flex-direction: column;
211
+ }
212
+
213
+ /* ─── Individual dock item ──────────────────────────────────────────────────── */
214
+ .dock-item {
215
+ position: relative;
216
+ display: flex;
217
+ flex-direction: column;
218
+ align-items: center;
219
+ gap: 5px;
220
+ cursor: pointer;
221
+ -webkit-tap-highlight-color: transparent;
222
+ user-select: none;
223
+ touch-action: manipulation;
224
+ }
225
+
226
+ /* ─── Icon box ──────────────────────────────────────────────────────────────── */
227
+ .dock-icon {
228
+ display: flex;
229
+ align-items: center;
230
+ justify-content: center;
231
+ overflow: hidden;
232
+ position: relative;
233
+ background: rgba(58, 58, 62, 0.9);
234
+ will-change: width, height;
235
+ transition:
236
+ width 0.08s ease,
237
+ height 0.08s ease,
238
+ border-radius 0.08s ease,
239
+ box-shadow 0.18s ease,
240
+ filter 0.18s ease;
241
+ box-shadow:
242
+ 0 2px 10px rgba(0, 0, 0, 0.4),
243
+ inset 0 1px 0 rgba(255, 255, 255, 0.08);
244
+ }
245
+
246
+ /* Settle-back transition when pointer leaves the dock */
247
+ .dock-icon.settling {
248
+ transition:
249
+ width 0.28s cubic-bezier(0.34, 1.2, 0.64, 1),
250
+ height 0.28s cubic-bezier(0.34, 1.2, 0.64, 1),
251
+ border-radius 0.28s ease,
252
+ box-shadow 0.18s ease,
253
+ filter 0.18s ease;
254
+ }
255
+
256
+ /* Icon reflection glow */
257
+ .dock-icon::after {
258
+ content: '';
259
+ position: absolute;
260
+ bottom: -4px;
261
+ left: 15%;
262
+ right: 15%;
263
+ height: 6px;
264
+ background: radial-gradient(ellipse, rgba(255, 255, 255, 0.06) 0%, transparent 70%);
265
+ pointer-events: none;
266
+ opacity: 0;
267
+ transition: opacity 0.2s ease;
268
+ }
269
+
270
+ .dock-item.active .dock-icon::after,
271
+ .dock-item.mag-closest .dock-icon::after {
272
+ opacity: 1;
273
+ }
274
+
275
+ .dock-icon img {
276
+ width: 100%;
277
+ height: 100%;
278
+ object-fit: contain;
279
+ padding: 10px;
280
+ pointer-events: none;
281
+ }
282
+
283
+ /* Press feedback */
284
+ .dock-item.pressing .dock-icon {
285
+ filter: brightness(0.88);
286
+ }
287
+
288
+ /* Active app glow */
289
+ .dock-item.active .dock-icon {
290
+ box-shadow:
291
+ 0 4px 18px rgba(0, 0, 0, 0.5),
292
+ 0 0 0 2px rgba(255, 255, 255, 0.22),
293
+ inset 0 1px 0 rgba(255, 255, 255, 0.12);
294
+ }
295
+
296
+ /* ─── Active dot ────────────────────────────────────────────────────────────── */
297
+ .dock-dot {
298
+ width: 4px;
299
+ height: 4px;
300
+ border-radius: 50%;
301
+ background: rgba(255, 255, 255, 0.85);
302
+ flex-shrink: 0;
303
+ opacity: 0;
304
+ transition: opacity 0.2s ease;
305
+ }
306
+
307
+ .dock-item.active .dock-dot {
308
+ opacity: 1;
309
+ }
310
+
311
+ /* ─── Label tooltip ─────────────────────────────────────────────────────────── */
312
+ .dock-label {
313
+ position: absolute;
314
+ bottom: calc(100% + 10px);
315
+ left: 50%;
316
+ transform: translateX(-50%);
317
+
318
+ background: rgba(22, 22, 24, 0.92);
319
+ color: rgba(255, 255, 255, 0.95);
320
+ font-size: 11px;
321
+ font-weight: 600;
322
+ letter-spacing: 0.02em;
323
+ padding: 5px 10px;
324
+ border-radius: 7px;
325
+ white-space: nowrap;
326
+ pointer-events: none;
327
+ border: 1px solid rgba(255, 255, 255, 0.1);
328
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.4);
329
+
330
+ opacity: 0;
331
+ transition: opacity 0.12s ease;
332
+ z-index: 1;
333
+ }
334
+
335
+ /* Show label on press or when closest during magnification scrub */
336
+ .dock-item.pressing .dock-label,
337
+ .dock-item.mag-closest .dock-label {
338
+ opacity: 1;
339
+ }
340
+
341
+ /* Label positions for other dock positions */
342
+ .dock-label-below {
343
+ bottom: auto;
344
+ top: calc(100% + 10px);
345
+ }
346
+
347
+ .dock-label-right {
348
+ bottom: auto;
349
+ top: 50%;
350
+ left: calc(100% + 10px);
351
+ transform: translateY(-50%);
352
+ }
353
+
354
+ .dock-label-left {
355
+ bottom: auto;
356
+ top: 50%;
357
+ left: auto;
358
+ right: calc(100% + 10px);
359
+ transform: translateY(-50%);
360
+ }
361
+
362
+ /* ─── Invisible corner trigger zone ────────────────────────────────────────── */
363
+ #trigger-corner {
364
+ position: fixed;
365
+ z-index: 9;
366
+ width: 120px;
367
+ height: 120px;
368
+ pointer-events: auto;
369
+ touch-action: none;
370
+ -webkit-touch-callout: none;
371
+ -webkit-user-select: none;
372
+ user-select: none;
373
+ }
374
+
375
+ /* ─── Loading overlay ───────────────────────────────────────────────────────── */
376
+ #loading-overlay {
377
+ position: fixed;
378
+ inset: 0;
379
+ z-index: 8;
380
+ display: flex;
381
+ flex-direction: column;
382
+ align-items: center;
383
+ justify-content: center;
384
+ gap: 16px;
385
+ background: rgba(0, 0, 0, 0.6);
386
+ color: rgba(255, 255, 255, 0.75);
387
+ font-size: 14px;
388
+ font-weight: 500;
389
+ opacity: 0;
390
+ pointer-events: none;
391
+ transition: opacity 0.2s ease;
392
+ }
393
+
394
+ #loading-overlay.visible {
395
+ opacity: 1;
396
+ pointer-events: auto;
397
+ }
398
+
399
+ .spinner {
400
+ width: 32px;
401
+ height: 32px;
402
+ border: 3px solid rgba(255, 255, 255, 0.15);
403
+ border-top-color: rgba(255, 255, 255, 0.75);
404
+ border-radius: 50%;
405
+ animation: spin 0.75s linear infinite;
406
+ }
407
+
408
+ @keyframes spin {
409
+ to {
410
+ transform: rotate(360deg);
411
+ }
412
+ }