@odla-ai/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.
Files changed (43) hide show
  1. package/README.md +88 -0
  2. package/css/components/buttons.css +118 -0
  3. package/css/components/cards.css +66 -0
  4. package/css/components/chart.css +24 -0
  5. package/css/components/chat.css +167 -0
  6. package/css/components/feedback.css +77 -0
  7. package/css/components/forms.css +132 -0
  8. package/css/components/nav.css +96 -0
  9. package/css/components/table.css +34 -0
  10. package/css/tokens.css +138 -0
  11. package/dist/index.d.ts +50 -0
  12. package/dist/index.js +72 -0
  13. package/dist/index.js.map +1 -0
  14. package/fonts/editorial.css +46 -0
  15. package/fonts/fira-code.css +2 -0
  16. package/fonts/lora.css +2 -0
  17. package/fonts/plex.css +4 -0
  18. package/fonts/satoshi.css +2 -0
  19. package/fonts/system.css +3 -0
  20. package/index.css +14 -0
  21. package/js/canvas.js +113 -0
  22. package/js/index.js +24 -0
  23. package/js/palette.js +37 -0
  24. package/js/theme.js +51 -0
  25. package/js/tokens.js +104 -0
  26. package/llms.txt +201 -0
  27. package/odla-ui.css +863 -0
  28. package/package.json +73 -0
  29. package/themes/chalk/styles.css +720 -0
  30. package/themes/chalk/theme.json +6 -0
  31. package/themes/chalk/ui.css +51 -0
  32. package/themes/clay/styles.css +726 -0
  33. package/themes/clay/theme.json +6 -0
  34. package/themes/clay/ui.css +40 -0
  35. package/themes/juniper/styles.css +660 -0
  36. package/themes/juniper/theme.json +6 -0
  37. package/themes/juniper/ui.css +50 -0
  38. package/themes/paper/styles.css +129 -0
  39. package/themes/paper/theme.json +6 -0
  40. package/themes/paper/ui.css +30 -0
  41. package/themes/salt/styles.css +728 -0
  42. package/themes/salt/theme.json +6 -0
  43. package/themes/salt/ui.css +48 -0
@@ -0,0 +1,40 @@
1
+ /* clay/ui.css — maps clay onto the @odla-ai/ui token contract.
2
+ Load alongside styles.css. Pure var() aliases track dark mode for free;
3
+ any LITERAL value must be overridden identically in BOTH dark blocks. */
4
+ :root {
5
+ --ui-bg: var(--bg);
6
+ --ui-surface: var(--panel);
7
+ --ui-surface-2: var(--panel-raised);
8
+ --ui-text: var(--text);
9
+ --ui-text-muted: var(--muted);
10
+ --ui-text-faint: var(--faint);
11
+ --ui-border: var(--border);
12
+ --ui-border-strong: color-mix(in srgb, var(--text) 24%, var(--bg));
13
+ --ui-accent: var(--accent);
14
+ --ui-accent-strong: var(--accent-deep);
15
+ --ui-accent-soft: var(--accent-dim);
16
+ --ui-on-accent: #ffffff;
17
+ --ui-good: var(--good);
18
+ --ui-good-soft: color-mix(in srgb, var(--good) 13%, transparent);
19
+ --ui-warn: var(--warn);
20
+ --ui-warn-soft: color-mix(in srgb, var(--warn) 13%, transparent);
21
+ --ui-danger: #b3402e;
22
+ --ui-danger-soft: color-mix(in srgb, var(--ui-danger) 11%, transparent);
23
+ --ui-code-bg: var(--code-bg);
24
+ --ui-code-text: var(--code-text);
25
+ --ui-shadow: var(--shadow);
26
+ --ui-font-sans: var(--sans);
27
+ --ui-font-serif: var(--serif);
28
+ --ui-font-mono: var(--mono);
29
+ --ui-font-display: var(--serif);
30
+ }
31
+
32
+ [data-theme="dark"] {
33
+ --ui-danger: #d0684d;
34
+ }
35
+
36
+ @media (prefers-color-scheme: dark) {
37
+ :root:not([data-theme="light"]) {
38
+ --ui-danger: #d0684d;
39
+ }
40
+ }
@@ -0,0 +1,660 @@
1
+ /* ══════════════════════════════════════════════════════════════
2
+ JUNIPER — quiet long-form reading
3
+ Lora serif body · Gill Sans titles · deep violet / lavender
4
+ Light: warm gray paper, deep-violet accent.
5
+ Dark: near-black evergreen, lavender accent (juniper berries).
6
+ ══════════════════════════════════════════════════════════════ */
7
+ @import url("https://fonts.googleapis.com/css2?family=Lora:ital,wght@0,400;0,500;0,600;0,700;1,400;1,700&display=swap");
8
+
9
+ /* ─── Tokens ─── */
10
+ :root {
11
+ --bg: #eaeaea;
12
+ --surface: #f5f5f5;
13
+ --text: #0a0a0a;
14
+ --text-secondary: #666666;
15
+ --accent: #250653;
16
+ --accent-2: #324444;
17
+ --border: #d9d9d9;
18
+
19
+ --code-bg: #f5f5f5;
20
+ --code-text: #24292e;
21
+ --hl-keyword: #581c87;
22
+ --hl-string: #1a7f4b;
23
+ --hl-comment: #6e6e6e;
24
+ --hl-number: #0f5132;
25
+ --hl-title: #250653;
26
+ --hl-attr: #92400e;
27
+ --hl-type: #324444;
28
+ --hl-meta: #92400e;
29
+ --hl-addition: #15803d;
30
+ --hl-deletion: #b91c1c;
31
+
32
+ --font-sans: Helvetica, "Helvetica Neue", -apple-system, BlinkMacSystemFont, Arial, sans-serif;
33
+ --font-body: "Lora", Georgia, serif;
34
+ --font-titles: "Gill Sans", "Gill Sans MT", Calibri, "Trebuchet MS", sans-serif;
35
+ --font-mono: "SF Mono", Monaco, "Cascadia Code", "Roboto Mono", Consolas, monospace;
36
+
37
+ --max-width: 42rem;
38
+ --header-width: var(--max-width);
39
+ }
40
+
41
+ [data-theme="dark"] {
42
+ --bg: #0a1a0a;
43
+ --surface: #1a1a1a;
44
+ --text: #e5e5e5;
45
+ --text-secondary: #a3a3a3;
46
+ --accent: #d3c5fc;
47
+ --accent-2: #581c87;
48
+ --border: #262626;
49
+
50
+ --code-bg: #1a1a1a;
51
+ --code-text: #d6d6d6;
52
+ --hl-keyword: #d3c5fc;
53
+ --hl-string: #9ae6b4;
54
+ --hl-comment: #8a8a8a;
55
+ --hl-number: #86d7b6;
56
+ --hl-title: #ede9fe;
57
+ --hl-attr: #a78bfa;
58
+ --hl-type: #99d6c0;
59
+ --hl-meta: #d8b48a;
60
+ --hl-addition: #7bc98a;
61
+ --hl-deletion: #ef8783;
62
+ }
63
+
64
+ @media (prefers-color-scheme: dark) {
65
+ :root:not([data-theme="light"]) {
66
+ --bg: #0a1a0a;
67
+ --surface: #1a1a1a;
68
+ --text: #e5e5e5;
69
+ --text-secondary: #a3a3a3;
70
+ --accent: #d3c5fc;
71
+ --accent-2: #581c87;
72
+ --border: #262626;
73
+
74
+ --code-bg: #1a1a1a;
75
+ --code-text: #d6d6d6;
76
+ --hl-keyword: #d3c5fc;
77
+ --hl-string: #9ae6b4;
78
+ --hl-comment: #8a8a8a;
79
+ --hl-number: #86d7b6;
80
+ --hl-title: #ede9fe;
81
+ --hl-attr: #a78bfa;
82
+ --hl-type: #99d6c0;
83
+ --hl-meta: #d8b48a;
84
+ --hl-addition: #7bc98a;
85
+ --hl-deletion: #ef8783;
86
+ }
87
+ }
88
+
89
+ /* ─── Base ─── */
90
+ * {
91
+ box-sizing: border-box;
92
+ }
93
+
94
+ html {
95
+ font-family: var(--font-sans);
96
+ font-size: 21px;
97
+ line-height: 1.5;
98
+ color: var(--text);
99
+ background-color: var(--bg);
100
+ transition: color 0.2s ease, background-color 0.2s ease;
101
+ }
102
+
103
+ body {
104
+ margin: 0;
105
+ min-height: 100vh;
106
+ display: flex;
107
+ flex-direction: column;
108
+ }
109
+
110
+ main {
111
+ width: 100%;
112
+ max-width: var(--max-width);
113
+ margin: 0 auto;
114
+ padding: 2.5rem 1.25rem 4rem;
115
+ flex: 1;
116
+ }
117
+
118
+ /* ─── Header ─── */
119
+ .site-header {
120
+ position: sticky;
121
+ top: 0;
122
+ z-index: 100;
123
+ background: var(--bg);
124
+ border-bottom: 1px solid var(--border);
125
+ padding: 0.9rem calc(max(0px, (100% - var(--header-width)) / 2) + 1.25rem);
126
+ display: flex;
127
+ align-items: center;
128
+ justify-content: space-between;
129
+ gap: 1rem;
130
+ flex-wrap: wrap;
131
+ }
132
+
133
+ .site-title {
134
+ font-family: var(--font-titles);
135
+ font-size: 1.4rem;
136
+ font-weight: 500;
137
+ text-transform: uppercase;
138
+ letter-spacing: 0.05em;
139
+ color: var(--accent);
140
+ text-decoration: none;
141
+ }
142
+
143
+ .site-title:hover {
144
+ opacity: 0.7;
145
+ }
146
+
147
+ .site-nav {
148
+ display: flex;
149
+ align-items: baseline;
150
+ gap: 1rem;
151
+ }
152
+
153
+ .site-nav a {
154
+ font-size: 0.8rem;
155
+ color: var(--text-secondary);
156
+ text-decoration: none;
157
+ }
158
+
159
+ .site-nav a:hover {
160
+ color: var(--text);
161
+ text-decoration: underline;
162
+ text-underline-offset: 0.2em;
163
+ }
164
+
165
+ .theme-toggle {
166
+ border: none;
167
+ background: none;
168
+ cursor: pointer;
169
+ padding: 0;
170
+ font-size: 1rem;
171
+ color: var(--text-secondary);
172
+ line-height: 1;
173
+ }
174
+
175
+ .theme-toggle:hover {
176
+ color: var(--text);
177
+ }
178
+
179
+ .theme-toggle-icon::before {
180
+ content: "☾";
181
+ }
182
+
183
+ [data-theme="dark"] .theme-toggle-icon::before {
184
+ content: "☀";
185
+ }
186
+
187
+ @media (prefers-color-scheme: dark) {
188
+ :root:not([data-theme="light"]) .theme-toggle-icon::before {
189
+ content: "☀";
190
+ }
191
+ }
192
+
193
+ /* ─── Footer ─── */
194
+ .site-footer {
195
+ max-width: var(--max-width);
196
+ width: 100%;
197
+ margin: 0 auto;
198
+ padding: 1.5rem 1.25rem;
199
+ text-align: center;
200
+ border-top: 1px solid var(--border);
201
+ font-size: 0.75rem;
202
+ color: var(--text-secondary);
203
+ }
204
+
205
+ .site-footer p {
206
+ margin: 0;
207
+ }
208
+
209
+ /* ─── Post list (home) ─── */
210
+ .post-list {
211
+ display: flex;
212
+ flex-direction: column;
213
+ }
214
+
215
+ .post-item {
216
+ padding-bottom: 2.5rem;
217
+ margin-bottom: 2.5rem;
218
+ position: relative;
219
+ }
220
+
221
+ .post-item::after {
222
+ content: "";
223
+ position: absolute;
224
+ bottom: 0;
225
+ left: 0;
226
+ right: 0;
227
+ height: 3px;
228
+ background-color: var(--accent);
229
+ opacity: 0.3;
230
+ }
231
+
232
+ .post-item:last-child::after {
233
+ display: none;
234
+ }
235
+
236
+ a.post-title {
237
+ font-family: var(--font-titles);
238
+ font-size: 1.75rem;
239
+ font-weight: 500;
240
+ line-height: 1.1;
241
+ color: var(--accent);
242
+ text-decoration: none;
243
+ transition: opacity 0.2s;
244
+ }
245
+
246
+ a.post-title:hover {
247
+ opacity: 0.7;
248
+ }
249
+
250
+ .post-meta {
251
+ display: flex;
252
+ align-items: baseline;
253
+ flex-wrap: wrap;
254
+ gap: 1rem;
255
+ font-size: 0.75rem;
256
+ color: var(--text-secondary);
257
+ margin-top: 0.75rem;
258
+ }
259
+
260
+ .post-tags {
261
+ display: inline;
262
+ }
263
+
264
+ .post-tag {
265
+ color: var(--text-secondary);
266
+ text-decoration: none;
267
+ opacity: 0.8;
268
+ }
269
+
270
+ .post-tag:hover {
271
+ opacity: 1;
272
+ text-decoration: underline;
273
+ text-underline-offset: 0.2em;
274
+ }
275
+
276
+ .post-description {
277
+ font-family: var(--font-body);
278
+ font-size: 0.95rem;
279
+ color: var(--text);
280
+ margin: 0.75rem 0 0;
281
+ }
282
+
283
+ /* ─── Single post / page ─── */
284
+ h1.post-title,
285
+ .page-title {
286
+ font-family: var(--font-titles);
287
+ font-size: 1.9rem;
288
+ font-weight: 500;
289
+ line-height: 1.1;
290
+ color: var(--accent);
291
+ margin: 0 0 0.5rem;
292
+ }
293
+
294
+ .post > .post-header {
295
+ margin-bottom: 2rem;
296
+ }
297
+
298
+ /* ─── Prose ─── */
299
+ .prose {
300
+ font-family: var(--font-body);
301
+ font-size: 1rem;
302
+ line-height: 1.5;
303
+ overflow-wrap: break-word;
304
+ }
305
+
306
+ /* Index/listTag excerpts read slightly larger and looser than story view,
307
+ matching the source design. */
308
+ .prose.post-excerpt {
309
+ font-size: 1.05rem;
310
+ line-height: 1.7;
311
+ }
312
+
313
+ .prose h1,
314
+ .prose h2,
315
+ .prose h3,
316
+ .prose h4,
317
+ .prose h5,
318
+ .prose h6 {
319
+ font-family: var(--font-titles);
320
+ font-weight: 500;
321
+ line-height: 1.2;
322
+ color: var(--accent);
323
+ margin: 1.75rem 0 0.75rem;
324
+ }
325
+
326
+ .prose h1 { font-size: 1.5rem; }
327
+ .prose h2 { font-size: 1.35rem; }
328
+ .prose h3 { font-size: 1.2rem; }
329
+ .prose h4 { font-size: 1.1rem; }
330
+
331
+ .prose p {
332
+ margin: 0 0 1.4rem;
333
+ }
334
+
335
+ .prose a {
336
+ color: var(--text);
337
+ opacity: 0.75;
338
+ text-decoration: underline;
339
+ text-decoration-thickness: 1px;
340
+ text-underline-offset: 0.2em;
341
+ }
342
+
343
+ .prose a:hover {
344
+ opacity: 1;
345
+ text-decoration-thickness: 2px;
346
+ }
347
+
348
+ .prose ul,
349
+ .prose ol {
350
+ margin: 0 0 1.4rem;
351
+ padding-left: 1.5rem;
352
+ }
353
+
354
+ .prose li {
355
+ margin-bottom: 0.4rem;
356
+ }
357
+
358
+ .prose blockquote {
359
+ margin: 2rem 0;
360
+ padding-left: 1.5rem;
361
+ border-left: 3px solid var(--accent);
362
+ color: var(--text-secondary);
363
+ font-style: italic;
364
+ }
365
+
366
+ .prose img {
367
+ max-width: 100%;
368
+ height: auto;
369
+ border-radius: 0.5rem;
370
+ }
371
+
372
+ .prose hr {
373
+ border: none;
374
+ border-top: 1px solid var(--border);
375
+ margin: 3rem 0;
376
+ }
377
+
378
+ .prose table {
379
+ width: 100%;
380
+ border-collapse: collapse;
381
+ margin: 1.5rem 0;
382
+ font-size: 0.9rem;
383
+ }
384
+
385
+ .prose th,
386
+ .prose td {
387
+ text-align: left;
388
+ padding: 0.5rem;
389
+ border-bottom: 1px solid var(--border);
390
+ }
391
+
392
+ .prose th {
393
+ font-weight: 600;
394
+ }
395
+
396
+ /* ─── Code ─── */
397
+ code {
398
+ font-family: var(--font-mono);
399
+ font-size: 0.875em;
400
+ background-color: var(--code-bg);
401
+ color: var(--code-text);
402
+ padding: 0.125em 0.25em;
403
+ border-radius: 0.25rem;
404
+ }
405
+
406
+ pre {
407
+ font-family: var(--font-mono);
408
+ font-size: 0.8rem;
409
+ line-height: 1.55;
410
+ background-color: var(--code-bg);
411
+ padding: 1.25rem;
412
+ border-radius: 0.5rem;
413
+ overflow-x: auto;
414
+ margin: 0 0 1.4rem;
415
+ }
416
+
417
+ pre code {
418
+ background: none;
419
+ padding: 0;
420
+ font-size: 1em;
421
+ }
422
+
423
+ .hljs { color: var(--code-text); }
424
+ .hljs-keyword, .hljs-selector-tag, .hljs-tag { color: var(--hl-keyword); }
425
+ .hljs-string, .hljs-regexp { color: var(--hl-string); }
426
+ .hljs-comment, .hljs-quote { color: var(--hl-comment); font-style: italic; }
427
+ .hljs-number, .hljs-literal, .hljs-symbol, .hljs-bullet { color: var(--hl-number); }
428
+ .hljs-title, .hljs-section, .hljs-name { color: var(--hl-title); font-weight: 600; }
429
+ .hljs-attr, .hljs-attribute, .hljs-variable, .hljs-template-variable, .hljs-property { color: var(--hl-attr); }
430
+ .hljs-type, .hljs-built_in, .hljs-selector-class, .hljs-selector-id { color: var(--hl-type); }
431
+ .hljs-meta, .hljs-doctag { color: var(--hl-meta); }
432
+ .hljs-addition { color: var(--hl-addition); }
433
+ .hljs-deletion { color: var(--hl-deletion); }
434
+ .hljs-emphasis { font-style: italic; }
435
+ .hljs-strong { font-weight: 700; }
436
+
437
+ /* ─── Archive & tags ─── */
438
+ .archive-year {
439
+ font-family: var(--font-titles);
440
+ font-size: 1.6rem;
441
+ font-weight: 600;
442
+ color: var(--text);
443
+ margin: 3rem 0 1rem;
444
+ padding-bottom: 0.5rem;
445
+ border-bottom: 2px solid var(--border);
446
+ }
447
+
448
+ .archive-list {
449
+ list-style: none;
450
+ margin: 0;
451
+ padding: 0;
452
+ display: flex;
453
+ flex-direction: column;
454
+ gap: 0.6rem;
455
+ }
456
+
457
+ .archive-item {
458
+ display: flex;
459
+ align-items: baseline;
460
+ gap: 1rem;
461
+ }
462
+
463
+ .archive-item time {
464
+ flex: none;
465
+ font-size: 0.75rem;
466
+ color: var(--text-secondary);
467
+ min-width: 8.5rem;
468
+ }
469
+
470
+ .archive-item a {
471
+ color: var(--text);
472
+ text-decoration: none;
473
+ }
474
+
475
+ .archive-item a:hover {
476
+ color: var(--accent);
477
+ text-decoration: underline;
478
+ text-underline-offset: 0.2em;
479
+ }
480
+
481
+ .tag-cloud {
482
+ list-style: none;
483
+ margin: 0;
484
+ padding: 0;
485
+ display: flex;
486
+ flex-wrap: wrap;
487
+ gap: 0.75rem 1.25rem;
488
+ }
489
+
490
+ .tag-cloud .post-tag {
491
+ font-size: 1rem;
492
+ color: var(--accent);
493
+ }
494
+
495
+ .tag-count {
496
+ font-size: 0.75rem;
497
+ color: var(--text-secondary);
498
+ }
499
+
500
+ /* ─── Responsive ─── */
501
+ @media (max-width: 768px) {
502
+ html {
503
+ font-size: 18px;
504
+ }
505
+ }
506
+
507
+ @media (max-width: 640px) {
508
+ html {
509
+ font-size: 17px;
510
+ }
511
+
512
+ .archive-item {
513
+ flex-direction: column;
514
+ gap: 0.1rem;
515
+ }
516
+ }
517
+
518
+ /* ─── Footnotes & excerpts ─── */
519
+ .footnotes {
520
+ margin-top: 3rem;
521
+ padding-top: 1rem;
522
+ border-top: 1px solid var(--border);
523
+ font-size: 0.85em;
524
+ }
525
+
526
+ .footnote-ref a {
527
+ text-decoration: none;
528
+ }
529
+
530
+ .footnote-backref {
531
+ text-decoration: none;
532
+ }
533
+
534
+ .post-excerpt {
535
+ margin-top: 1rem;
536
+ }
537
+
538
+ .read-more a {
539
+ font-size: 0.85em;
540
+ }
541
+
542
+ .site-footer a {
543
+ color: inherit;
544
+ text-decoration: underline;
545
+ text-decoration-thickness: 1px;
546
+ text-underline-offset: 0.2em;
547
+ }
548
+
549
+ .site-footer a:hover {
550
+ color: var(--accent);
551
+ }
552
+
553
+ .site-footer .copyright a {
554
+ text-decoration: none;
555
+ }
556
+
557
+ /* ─── Scroll position indicator (nav bar) ─── */
558
+ .scroll-indicator {
559
+ position: absolute;
560
+ bottom: -1px;
561
+ left: 0;
562
+ right: 0;
563
+ height: 1px;
564
+ pointer-events: none;
565
+ }
566
+
567
+ .scroll-line {
568
+ position: absolute;
569
+ bottom: 0;
570
+ left: 0;
571
+ width: 0;
572
+ height: 1px;
573
+ background: var(--accent);
574
+ }
575
+
576
+ .scroll-dot {
577
+ position: absolute;
578
+ left: 0;
579
+ bottom: -2px;
580
+ width: 5px;
581
+ height: 5px;
582
+ border-radius: 50%;
583
+ background: var(--accent);
584
+ transform: translateX(-50%);
585
+ }
586
+
587
+ [id] {
588
+ scroll-margin-top: 5rem;
589
+ }
590
+
591
+ /* ─── Recent-posts sidebar (config.sidebarRecentPosts) ─── */
592
+ .content-wrapper {
593
+ display: flex;
594
+ justify-content: center;
595
+ gap: 2.5rem;
596
+ flex: 1;
597
+ }
598
+
599
+ .content-wrapper main {
600
+ margin: 0;
601
+ }
602
+
603
+ .recent-posts-sidebar {
604
+ width: 280px;
605
+ flex-shrink: 0;
606
+ display: none;
607
+ margin-top: 2.5rem;
608
+ padding-right: 1.25rem;
609
+ }
610
+
611
+ @media (min-width: 1200px) {
612
+ .recent-posts-sidebar {
613
+ display: block;
614
+ }
615
+ }
616
+
617
+ .recent-posts-sidebar .section-title {
618
+ font-size: 0.7rem;
619
+ text-transform: uppercase;
620
+ letter-spacing: 0.1em;
621
+ color: var(--text-secondary);
622
+ margin: 0 0 0.5rem;
623
+ }
624
+
625
+ .recent-posts {
626
+ display: flex;
627
+ flex-direction: column;
628
+ gap: 0.5rem;
629
+ }
630
+
631
+ .recent-posts .post-link {
632
+ display: flex;
633
+ flex-direction: column;
634
+ gap: 0.125rem;
635
+ padding: 0.375rem 0;
636
+ color: var(--text);
637
+ text-decoration: none;
638
+ }
639
+
640
+ .recent-posts .post-title {
641
+ font-size: 0.72rem;
642
+ line-height: 1.3;
643
+ }
644
+
645
+ .recent-posts .post-link:hover .post-title {
646
+ color: var(--accent);
647
+ }
648
+
649
+ .recent-posts .post-date {
650
+ font-size: 0.62rem;
651
+ color: var(--text-secondary);
652
+ }
653
+
654
+ /* Header aligns with the content column; when the sidebar is on (wide
655
+ viewports) the centered column is wider by sidebar + gap. */
656
+ @media (min-width: 1200px) {
657
+ body.has-sidebar .site-header {
658
+ --header-width: calc(var(--max-width) + 280px + 2.5rem);
659
+ }
660
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "name": "juniper",
3
+ "label": "Juniper",
4
+ "description": "Quiet long-form reading. Lora serif body, deep-violet accent on warm gray; lavender on near-black evergreen in dark mode. Drawn from cory.news.",
5
+ "fonts": "Lora (Google Fonts); Gill Sans system stack for titles"
6
+ }