@lo-ink/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.
package/src/styles.css ADDED
@@ -0,0 +1,560 @@
1
+ @import "@lo-ink/design-tokens/tokens.css";
2
+
3
+ :where(.lo-ui-root) {
4
+ font-family: var(--lo-font-family);
5
+ color: var(--lo-color-text);
6
+ font-size: var(--lo-font-size-body);
7
+ line-height: var(--lo-line-height-body);
8
+ -webkit-text-size-adjust: 100%;
9
+ }
10
+
11
+ :where(.lo-ui-root),
12
+ :where(.lo-ui-root *, .lo-ui-root *::before, .lo-ui-root *::after) {
13
+ box-sizing: border-box;
14
+ }
15
+
16
+ :where(.lo-ui-root button, .lo-ui-root input) {
17
+ font: inherit;
18
+ -webkit-tap-highlight-color: transparent;
19
+ }
20
+
21
+ :where(.lo-ui-root button, .lo-ui-root input):focus-visible {
22
+ outline: 3px solid var(--lo-color-focus);
23
+ outline-offset: 2px;
24
+ }
25
+
26
+ .lo-ui-button {
27
+ min-height: var(--lo-size-control);
28
+ padding: 0 var(--lo-space-5);
29
+ border: 1px solid transparent;
30
+ border-radius: var(--lo-radius-md);
31
+ display: inline-flex;
32
+ align-items: center;
33
+ justify-content: center;
34
+ gap: var(--lo-space-2);
35
+ font-weight: var(--lo-font-weight-medium);
36
+ line-height: var(--lo-line-height-tight);
37
+ cursor: pointer;
38
+ transition:
39
+ background-color var(--lo-motion-fast) var(--lo-ease-out),
40
+ border-color var(--lo-motion-fast) var(--lo-ease-out),
41
+ transform var(--lo-motion-fast) var(--lo-ease-out);
42
+ }
43
+
44
+ .lo-ui-button:active:not(:disabled) {
45
+ transform: scale(0.98);
46
+ }
47
+
48
+ .lo-ui-button:disabled {
49
+ cursor: default;
50
+ opacity: 0.5;
51
+ }
52
+
53
+ .lo-ui-button--small {
54
+ min-height: var(--lo-size-control-sm);
55
+ padding-inline: var(--lo-space-4);
56
+ font-size: var(--lo-font-size-label);
57
+ }
58
+
59
+ .lo-ui-button--full {
60
+ width: 100%;
61
+ }
62
+
63
+ .lo-ui-button--primary {
64
+ color: var(--lo-color-accent-contrast);
65
+ background: var(--lo-color-accent);
66
+ }
67
+
68
+ .lo-ui-button--primary:hover:not(:disabled) {
69
+ background: var(--lo-color-accent-hover);
70
+ }
71
+
72
+ .lo-ui-button--secondary {
73
+ color: var(--lo-color-text);
74
+ background: var(--lo-color-surface);
75
+ border-color: var(--lo-color-border);
76
+ }
77
+
78
+ .lo-ui-button--secondary:hover:not(:disabled),
79
+ .lo-ui-button--quiet:hover:not(:disabled) {
80
+ background: var(--lo-color-accent-soft);
81
+ }
82
+
83
+ .lo-ui-button--danger {
84
+ color: var(--lo-color-danger);
85
+ background: var(--lo-color-danger-soft);
86
+ }
87
+
88
+ .lo-ui-button--danger:hover:not(:disabled) {
89
+ color: var(--lo-color-accent-contrast);
90
+ background: var(--lo-color-danger-hover);
91
+ }
92
+
93
+ .lo-ui-button--quiet {
94
+ color: var(--lo-color-accent);
95
+ background: transparent;
96
+ }
97
+
98
+ .lo-ui-button__icon {
99
+ width: 20px;
100
+ height: 20px;
101
+ display: inline-grid;
102
+ place-items: center;
103
+ }
104
+
105
+ .lo-ui-button__icon > svg {
106
+ width: 100%;
107
+ height: 100%;
108
+ }
109
+
110
+ .lo-ui-spinner {
111
+ width: 16px;
112
+ height: 16px;
113
+ border: 2px solid currentColor;
114
+ border-right-color: transparent;
115
+ border-radius: var(--lo-radius-full);
116
+ animation: lo-ui-spin 700ms linear infinite;
117
+ }
118
+
119
+ .lo-ui-field {
120
+ display: flex;
121
+ flex-direction: column;
122
+ gap: var(--lo-space-2);
123
+ }
124
+
125
+ .lo-ui-field__label {
126
+ color: var(--lo-color-text);
127
+ font-size: var(--lo-font-size-label);
128
+ font-weight: var(--lo-font-weight-medium);
129
+ }
130
+
131
+ .lo-ui-field__input {
132
+ width: 100%;
133
+ min-width: 0;
134
+ min-height: var(--lo-size-control);
135
+ padding: 10px var(--lo-space-3);
136
+ color: var(--lo-color-text);
137
+ background: var(--lo-color-surface);
138
+ border: 1px solid var(--lo-color-border);
139
+ border-radius: var(--lo-radius-md);
140
+ font-size: 16px;
141
+ transition:
142
+ border-color var(--lo-motion-fast) var(--lo-ease-out),
143
+ box-shadow var(--lo-motion-fast) var(--lo-ease-out);
144
+ }
145
+
146
+ .lo-ui-field__input::placeholder {
147
+ color: var(--lo-color-text-secondary);
148
+ opacity: 1;
149
+ }
150
+
151
+ .lo-ui-field__input[aria-invalid="true"] {
152
+ border-color: var(--lo-color-danger);
153
+ }
154
+
155
+ .lo-ui-field__input:disabled {
156
+ color: var(--lo-color-disabled);
157
+ background: var(--lo-color-border-subtle);
158
+ cursor: not-allowed;
159
+ }
160
+
161
+ .lo-ui-field__description,
162
+ .lo-ui-field__error {
163
+ font-size: var(--lo-font-size-caption);
164
+ line-height: var(--lo-line-height-body);
165
+ }
166
+
167
+ .lo-ui-field__description {
168
+ color: var(--lo-color-text-secondary);
169
+ }
170
+
171
+ .lo-ui-field__error {
172
+ color: var(--lo-color-danger);
173
+ }
174
+
175
+ .lo-ui-selection {
176
+ min-height: var(--lo-size-cell);
177
+ display: flex;
178
+ align-items: center;
179
+ gap: var(--lo-space-4);
180
+ color: var(--lo-color-text);
181
+ cursor: pointer;
182
+ }
183
+
184
+ .lo-ui-selection:has(input:disabled) {
185
+ color: var(--lo-color-disabled);
186
+ cursor: default;
187
+ }
188
+
189
+ .lo-ui-selection__copy {
190
+ flex: 1 1 auto;
191
+ min-width: 0;
192
+ display: flex;
193
+ flex-direction: column;
194
+ gap: 2px;
195
+ }
196
+
197
+ .lo-ui-selection__label {
198
+ font-size: var(--lo-font-size-body);
199
+ }
200
+
201
+ .lo-ui-selection__description {
202
+ color: var(--lo-color-text-secondary);
203
+ font-size: var(--lo-font-size-label);
204
+ }
205
+
206
+ .lo-ui-selection--control-only {
207
+ min-width: var(--lo-size-control);
208
+ min-height: var(--lo-size-control);
209
+ justify-content: center;
210
+ }
211
+
212
+ .lo-ui-selection--control-only .lo-ui-selection__copy {
213
+ position: absolute;
214
+ width: 1px;
215
+ height: 1px;
216
+ margin: -1px;
217
+ overflow: hidden;
218
+ clip-path: inset(50%);
219
+ white-space: nowrap;
220
+ }
221
+
222
+ .lo-ui-switch {
223
+ appearance: none;
224
+ flex: 0 0 auto;
225
+ width: 51px;
226
+ height: 31px;
227
+ margin: 0;
228
+ padding: 2px;
229
+ border: 0;
230
+ border-radius: var(--lo-radius-full);
231
+ background: var(--lo-color-border);
232
+ cursor: pointer;
233
+ transition: background-color var(--lo-motion-fast) var(--lo-ease-out);
234
+ }
235
+
236
+ .lo-ui-switch::after {
237
+ content: "";
238
+ display: block;
239
+ width: 27px;
240
+ height: 27px;
241
+ border-radius: var(--lo-radius-full);
242
+ background: #ffffff;
243
+ box-shadow: 0 2px 5px rgb(20 24 34 / 22%);
244
+ transition: transform var(--lo-motion-fast) var(--lo-ease-out);
245
+ }
246
+
247
+ .lo-ui-switch:checked {
248
+ background: var(--lo-color-accent);
249
+ }
250
+
251
+ .lo-ui-switch:checked::after {
252
+ transform: translateX(20px);
253
+ }
254
+
255
+ .lo-ui-switch:disabled {
256
+ cursor: default;
257
+ opacity: 0.55;
258
+ }
259
+
260
+ .lo-ui-checkbox {
261
+ appearance: none;
262
+ width: 22px;
263
+ height: 22px;
264
+ flex: 0 0 auto;
265
+ margin: 0;
266
+ display: grid;
267
+ place-items: center;
268
+ border: 1.5px solid var(--lo-color-border);
269
+ border-radius: 7px;
270
+ background: var(--lo-color-surface);
271
+ cursor: pointer;
272
+ }
273
+
274
+ .lo-ui-checkbox::after {
275
+ content: "";
276
+ width: 10px;
277
+ height: 6px;
278
+ border: solid var(--lo-color-accent-contrast);
279
+ border-width: 0 0 2px 2px;
280
+ transform: translateY(-1px) rotate(-45deg) scale(0);
281
+ transition: transform var(--lo-motion-fast) var(--lo-ease-out);
282
+ }
283
+
284
+ .lo-ui-checkbox:checked {
285
+ background: var(--lo-color-accent);
286
+ border-color: var(--lo-color-accent);
287
+ }
288
+
289
+ .lo-ui-checkbox:checked::after {
290
+ transform: translateY(-1px) rotate(-45deg) scale(1);
291
+ }
292
+
293
+ .lo-ui-list-section {
294
+ display: flex;
295
+ flex-direction: column;
296
+ gap: var(--lo-space-2);
297
+ }
298
+
299
+ .lo-ui-list-label {
300
+ margin: 0;
301
+ padding-inline: var(--lo-space-1);
302
+ color: var(--lo-color-text-secondary);
303
+ font-size: var(--lo-font-size-caption);
304
+ font-weight: var(--lo-font-weight-medium);
305
+ letter-spacing: 0.04em;
306
+ text-transform: uppercase;
307
+ }
308
+
309
+ .lo-ui-list {
310
+ margin: 0;
311
+ padding: 0;
312
+ overflow: hidden;
313
+ list-style: none;
314
+ border: 1px solid var(--lo-color-border-subtle);
315
+ border-radius: var(--lo-radius-lg);
316
+ background: var(--lo-color-surface);
317
+ box-shadow: var(--lo-shadow-raised);
318
+ }
319
+
320
+ .lo-ui-cell + .lo-ui-cell {
321
+ border-top: 1px solid var(--lo-color-border-subtle);
322
+ }
323
+
324
+ .lo-ui-cell__actions {
325
+ display: flex;
326
+ align-items: center;
327
+ }
328
+
329
+ .lo-ui-cell__actions > .lo-ui-cell__control,
330
+ .lo-ui-cell__actions > .lo-ui-cell__content {
331
+ flex: 1;
332
+ min-width: 0;
333
+ }
334
+
335
+ .lo-ui-cell__actions > .lo-ui-cell__trailing-action {
336
+ padding-inline-end: var(--lo-space-4);
337
+ }
338
+
339
+ .lo-ui-cell__content,
340
+ .lo-ui-cell__control {
341
+ width: 100%;
342
+ min-height: var(--lo-size-cell);
343
+ padding: 10px var(--lo-space-4);
344
+ display: flex;
345
+ align-items: center;
346
+ gap: var(--lo-space-3);
347
+ color: var(--lo-color-text);
348
+ background: var(--lo-color-surface);
349
+ border: 0;
350
+ text-align: left;
351
+ }
352
+
353
+ .lo-ui-cell__control:focus-visible {
354
+ outline-offset: -3px;
355
+ }
356
+
357
+ .lo-ui-cell__control {
358
+ cursor: pointer;
359
+ transition: background-color var(--lo-motion-fast) var(--lo-ease-out);
360
+ }
361
+
362
+ .lo-ui-cell__control:hover:not(:disabled) {
363
+ background: var(--lo-color-accent-soft);
364
+ }
365
+
366
+ .lo-ui-cell__control:disabled {
367
+ color: var(--lo-color-disabled);
368
+ cursor: default;
369
+ }
370
+
371
+ .lo-ui-cell__body {
372
+ flex: 1 1 auto;
373
+ min-width: 0;
374
+ display: flex;
375
+ flex-direction: column;
376
+ gap: 2px;
377
+ }
378
+
379
+ .lo-ui-cell__title,
380
+ .lo-ui-cell__subtitle {
381
+ overflow: hidden;
382
+ overflow-wrap: anywhere;
383
+ }
384
+
385
+ .lo-ui-cell__title {
386
+ font-size: var(--lo-font-size-body);
387
+ }
388
+
389
+ .lo-ui-cell__subtitle {
390
+ color: var(--lo-color-text-secondary);
391
+ font-size: var(--lo-font-size-label);
392
+ }
393
+
394
+ .lo-ui-cell__leading,
395
+ .lo-ui-cell__trailing,
396
+ .lo-ui-cell__trailing-action {
397
+ flex: 0 0 auto;
398
+ display: inline-flex;
399
+ align-items: center;
400
+ }
401
+
402
+ .lo-ui-cell__trailing {
403
+ color: var(--lo-color-text-secondary);
404
+ font-size: var(--lo-font-size-label);
405
+ }
406
+
407
+ .lo-ui-cell__chevron {
408
+ width: 18px;
409
+ height: 18px;
410
+ flex: 0 0 auto;
411
+ fill: none;
412
+ stroke: var(--lo-color-text-secondary);
413
+ stroke-width: 1.8;
414
+ stroke-linecap: round;
415
+ stroke-linejoin: round;
416
+ }
417
+
418
+ .lo-ui-app-icon {
419
+ width: 44px;
420
+ height: 44px;
421
+ flex: 0 0 auto;
422
+ overflow: hidden;
423
+ display: inline-grid;
424
+ place-items: center;
425
+ border-radius: 11px;
426
+ color: var(--lo-color-accent-contrast);
427
+ background: var(--lo-color-accent);
428
+ }
429
+
430
+ .lo-ui-app-icon--small {
431
+ width: 32px;
432
+ height: 32px;
433
+ border-radius: 8px;
434
+ }
435
+
436
+ .lo-ui-app-icon--large {
437
+ width: 64px;
438
+ height: 64px;
439
+ border-radius: 16px;
440
+ }
441
+
442
+ .lo-ui-app-icon > img {
443
+ width: 100%;
444
+ height: 100%;
445
+ display: block;
446
+ object-fit: cover;
447
+ }
448
+
449
+ .lo-ui-app-icon > svg {
450
+ width: 55%;
451
+ height: 55%;
452
+ }
453
+
454
+ .lo-ui-empty-state {
455
+ min-height: 240px;
456
+ padding: var(--lo-space-8) var(--lo-space-6);
457
+ display: flex;
458
+ flex-direction: column;
459
+ align-items: center;
460
+ justify-content: center;
461
+ gap: var(--lo-space-2);
462
+ text-align: center;
463
+ border: 1px dashed var(--lo-color-border);
464
+ border-radius: var(--lo-radius-lg);
465
+ background: var(--lo-color-surface);
466
+ }
467
+
468
+ .lo-ui-empty-state__icon {
469
+ width: 52px;
470
+ height: 52px;
471
+ margin-bottom: var(--lo-space-2);
472
+ display: grid;
473
+ place-items: center;
474
+ border-radius: var(--lo-radius-md);
475
+ color: var(--lo-color-accent);
476
+ background: var(--lo-color-accent-soft);
477
+ }
478
+
479
+ .lo-ui-empty-state__icon > svg {
480
+ width: 28px;
481
+ height: 28px;
482
+ }
483
+
484
+ .lo-ui-empty-state__title,
485
+ .lo-ui-empty-state__description,
486
+ .lo-ui-heading,
487
+ .lo-ui-text {
488
+ margin: 0;
489
+ }
490
+
491
+ .lo-ui-empty-state__title {
492
+ font-size: var(--lo-font-size-body);
493
+ line-height: var(--lo-line-height-tight);
494
+ }
495
+
496
+ .lo-ui-empty-state__description {
497
+ max-width: 36ch;
498
+ color: var(--lo-color-text-secondary);
499
+ font-size: var(--lo-font-size-label);
500
+ }
501
+
502
+ .lo-ui-empty-state__action {
503
+ margin-top: var(--lo-space-3);
504
+ }
505
+
506
+ .lo-ui-stack,
507
+ .lo-ui-inline {
508
+ display: flex;
509
+ gap: var(--lo-layout-gap);
510
+ }
511
+
512
+ .lo-ui-stack {
513
+ flex-direction: column;
514
+ }
515
+
516
+ .lo-ui-inline {
517
+ flex-flow: row wrap;
518
+ align-items: center;
519
+ }
520
+
521
+ .lo-ui-heading {
522
+ font-size: var(--lo-font-size-title);
523
+ line-height: var(--lo-line-height-tight);
524
+ }
525
+
526
+ .lo-ui-text {
527
+ line-height: var(--lo-line-height-body);
528
+ }
529
+
530
+ .lo-ui-text--secondary {
531
+ color: var(--lo-color-text-secondary);
532
+ }
533
+
534
+ .lo-ui-text--danger {
535
+ color: var(--lo-color-danger);
536
+ }
537
+
538
+ .lo-ui-text--caption {
539
+ font-size: var(--lo-font-size-caption);
540
+ }
541
+
542
+ .lo-ui-text--label {
543
+ font-size: var(--lo-font-size-label);
544
+ }
545
+
546
+ .lo-ui-text--body {
547
+ font-size: var(--lo-font-size-body);
548
+ }
549
+
550
+ @keyframes lo-ui-spin {
551
+ to {
552
+ transform: rotate(1turn);
553
+ }
554
+ }
555
+
556
+ @media (prefers-reduced-motion: reduce) {
557
+ .lo-ui-spinner {
558
+ animation: none;
559
+ }
560
+ }
package/src/utils.ts ADDED
@@ -0,0 +1,5 @@
1
+ export function classes(
2
+ ...values: Array<string | false | null | undefined>
3
+ ): string {
4
+ return values.filter(Boolean).join(" ");
5
+ }