+6
-6
flake.lock
+6
-6
flake.lock
···
69
69
"nixpkgs": "nixpkgs_2"
70
70
},
71
71
"locked": {
72
-
"lastModified": 1758224598,
73
-
"narHash": "sha256-Ai+kyEpZVPTuk0IP34kE8ZaXxhI+Z97msUFPe82k0Ic=",
72
+
"lastModified": 1768109018,
73
+
"narHash": "sha256-ePHsZ62UURGy44rkLva16RILZKI7PWcnGzyrP5Qmqt8=",
74
74
"ref": "refs/heads/master",
75
-
"rev": "e67e553dc237e41adc9ceae4d834fc02d44e4eb4",
76
-
"revCount": 96,
75
+
"rev": "0368173f7a3672916d26ac7c3183dd9998d1a514",
76
+
"revCount": 98,
77
77
"type": "git",
78
-
"url": "https://tangled.sh/@icyphox.sh/vite"
78
+
"url": "https://tangled.org/oppi.li/vite"
79
79
},
80
80
"original": {
81
81
"type": "git",
82
-
"url": "https://tangled.sh/@icyphox.sh/vite"
82
+
"url": "https://tangled.org/oppi.li/vite"
83
83
}
84
84
}
85
85
},
+1
-1
flake.nix
+1
-1
flake.nix
+856
-27
input.css
+856
-27
input.css
···
66
66
font-display: swap;
67
67
}
68
68
69
-
h1 {
70
-
@apply text-2xl;
71
-
@apply text-black;
72
-
@apply font-bold;
73
-
}
74
-
75
69
::selection {
76
70
@apply bg-yellow-400 text-black bg-opacity-30 dark:bg-yellow-600 dark:bg-opacity-50 dark:text-white;
77
71
}
···
84
78
@supports (font-variation-settings: normal) {
85
79
html {
86
80
font-feature-settings:
87
-
"ss01" 1,
88
81
"kern" 1,
89
82
"liga" 1,
90
83
"cv05" 1,
···
92
85
}
93
86
}
94
87
95
-
a {
96
-
@apply no-underline text-black hover:underline hover:text-gray-800 dark:text-white dark:hover:text-gray-300;
97
-
}
98
-
99
88
img {
100
89
@apply border border-gray-200 rounded dark:border-gray-700;
101
90
}
···
104
93
@apply border-0 dark:brightness-100 dark:opacity-100;
105
94
}
106
95
96
+
a {
97
+
@apply no-underline text-black hover:underline hover:text-gray-800 dark:text-white dark:hover:text-gray-300;
98
+
}
99
+
107
100
label {
108
-
@apply block mb-2 text-gray-900 text-sm font-bold py-2 uppercase dark:text-gray-100;
101
+
@apply block text-gray-900 text-sm font-bold py-2 uppercase dark:text-gray-100;
109
102
}
110
103
input {
111
-
@apply bg-white border border-gray-400 rounded-sm focus:ring-black p-3 dark:bg-gray-800 dark:border-gray-600 dark:text-white dark:focus:ring-gray-400;
104
+
@apply border border-gray-400 block rounded bg-gray-50 focus:ring-black p-3 dark:bg-gray-800 dark:border-gray-600 dark:text-white dark:focus:ring-gray-400;
112
105
}
113
106
textarea {
114
-
@apply bg-white border border-gray-400 rounded-sm focus:ring-black p-3 dark:bg-gray-800 dark:border-gray-600 dark:text-white dark:focus:ring-gray-400;
107
+
@apply border border-gray-400 block rounded bg-gray-50 focus:ring-black p-3 dark:bg-gray-800 dark:border-gray-600 dark:text-white dark:focus:ring-gray-400;
115
108
}
116
109
details summary::-webkit-details-marker {
117
110
display: none;
118
111
}
112
+
113
+
code {
114
+
@apply font-mono rounded bg-gray-100 dark:bg-gray-700 text-black dark:text-white;
115
+
}
119
116
}
120
117
121
118
@layer components {
122
119
.btn {
123
-
@apply relative z-10 inline-flex min-h-[30px] cursor-pointer items-center
124
-
justify-center bg-transparent px-2 pb-[0.2rem] text-base
125
-
text-gray-900 before:absolute before:inset-0 before:-z-10
126
-
before:block before:rounded-sm before:border before:border-gray-200
127
-
before:bg-white before:drop-shadow-sm
128
-
before:content-[''] hover:before:border-gray-300
129
-
hover:before:bg-gray-50
130
-
hover:before:shadow-[0_2px_2px_0_rgba(20,20,96,0.1),inset_0_-2px_0_0_#f5f5f5]
131
-
focus:outline-none focus-visible:before:outline
132
-
focus-visible:before:outline-4 focus-visible:before:outline-gray-500
133
-
active:before:shadow-[inset_0_2px_2px_0_rgba(20,20,96,0.1)];
120
+
@apply relative z-10 inline-flex min-h-[30px] cursor-pointer items-center justify-center
121
+
bg-transparent px-2 pb-[0.2rem] text-sm text-gray-900
122
+
before:absolute before:inset-0 before:-z-10 before:block before:rounded
123
+
before:border before:border-gray-200 before:bg-white
124
+
before:shadow-[inset_0_-2px_0_0_rgba(0,0,0,0.1),0_1px_0_0_rgba(0,0,0,0.04)]
125
+
before:content-[''] before:transition-all before:duration-150 before:ease-in-out
126
+
hover:before:shadow-[inset_0_-2px_0_0_rgba(0,0,0,0.15),0_2px_1px_0_rgba(0,0,0,0.06)]
127
+
hover:before:bg-gray-50
128
+
dark:hover:before:bg-gray-700
129
+
active:before:shadow-[inset_0_2px_2px_0_rgba(0,0,0,0.1)]
130
+
focus:outline-none focus-visible:before:outline focus-visible:before:outline-2 focus-visible:before:outline-gray-400
131
+
disabled:cursor-not-allowed disabled:opacity-50
132
+
dark:text-gray-100 dark:before:bg-gray-800 dark:before:border-gray-700;
133
+
}
134
+
135
+
.btn-create {
136
+
@apply btn text-white
137
+
before:bg-green-600 hover:before:bg-green-700
138
+
dark:before:bg-green-700 dark:hover:before:bg-green-800
139
+
before:border before:border-green-700 hover:before:border-green-800
140
+
focus-visible:before:outline-green-500
141
+
disabled:before:bg-green-400 dark:disabled:before:bg-green-600;
142
+
}
143
+
144
+
.prose hr {
145
+
@apply my-2;
146
+
}
147
+
148
+
.prose li:has(input) {
149
+
@apply list-none;
150
+
}
151
+
152
+
.prose ul:has(input) {
153
+
@apply pl-2;
154
+
}
155
+
156
+
.prose .heading .anchor {
157
+
@apply no-underline mx-2 opacity-0;
158
+
}
159
+
160
+
.prose .heading:hover .anchor {
161
+
@apply opacity-70;
162
+
}
163
+
164
+
.prose .heading .anchor:hover {
165
+
@apply opacity-70;
166
+
}
167
+
168
+
.prose a.footnote-backref {
169
+
@apply no-underline;
170
+
}
171
+
172
+
.prose a.mention {
173
+
@apply no-underline hover:underline font-bold;
174
+
}
175
+
176
+
.prose li {
177
+
@apply my-0 py-0;
178
+
}
179
+
180
+
.prose ul,
181
+
.prose ol {
182
+
@apply my-1 py-0;
183
+
}
184
+
185
+
.prose img {
186
+
display: inline;
187
+
margin: 0;
188
+
vertical-align: middle;
189
+
}
190
+
191
+
.prose input {
192
+
@apply inline-block my-0 mb-1 mx-1;
193
+
}
194
+
195
+
.prose input[type="checkbox"] {
196
+
@apply disabled:accent-blue-500 checked:accent-blue-500 disabled:checked:accent-blue-500;
197
+
}
198
+
199
+
/* Base callout */
200
+
details[data-callout] {
201
+
@apply border-l-4 pl-3 py-2 text-gray-800 dark:text-gray-200 my-4;
202
+
}
203
+
204
+
details[data-callout] > summary {
205
+
@apply font-bold cursor-pointer mb-1;
206
+
}
207
+
208
+
details[data-callout] > .callout-content {
209
+
@apply text-sm leading-snug;
210
+
}
211
+
212
+
/* Note (blue) */
213
+
details[data-callout="note" i] {
214
+
@apply border-blue-400 dark:border-blue-500;
215
+
}
216
+
details[data-callout="note" i] > summary {
217
+
@apply text-blue-700 dark:text-blue-400;
218
+
}
219
+
220
+
/* Important (purple) */
221
+
details[data-callout="important" i] {
222
+
@apply border-purple-400 dark:border-purple-500;
223
+
}
224
+
details[data-callout="important" i] > summary {
225
+
@apply text-purple-700 dark:text-purple-400;
226
+
}
227
+
228
+
/* Warning (yellow) */
229
+
details[data-callout="warning" i] {
230
+
@apply border-yellow-400 dark:border-yellow-500;
231
+
}
232
+
details[data-callout="warning" i] > summary {
233
+
@apply text-yellow-700 dark:text-yellow-400;
234
+
}
235
+
236
+
/* Caution (red) */
237
+
details[data-callout="caution" i] {
238
+
@apply border-red-400 dark:border-red-500;
239
+
}
240
+
details[data-callout="caution" i] > summary {
241
+
@apply text-red-700 dark:text-red-400;
242
+
}
243
+
244
+
/* Tip (green) */
245
+
details[data-callout="tip" i] {
246
+
@apply border-green-400 dark:border-green-500;
247
+
}
248
+
details[data-callout="tip" i] > summary {
249
+
@apply text-green-700 dark:text-green-400;
134
250
}
251
+
252
+
/* Optional: hide the disclosure arrow like GitHub */
253
+
details[data-callout] > summary::-webkit-details-marker {
254
+
display: none;
255
+
}
256
+
135
257
}
136
258
@layer utilities {
137
259
.error {
138
-
@apply py-1 text-red-400;
260
+
@apply py-1 text-red-400 dark:text-red-300;
139
261
}
140
262
.success {
141
-
@apply py-1 text-black;
263
+
@apply py-1 text-gray-900 dark:text-gray-100;
142
264
}
143
265
}
266
+
267
+
}
268
+
269
+
/* Background */
270
+
.bg {
271
+
color: #4c4f69;
272
+
background-color: #eff1f5;
273
+
}
274
+
/* PreWrapper */
275
+
.chroma {
276
+
color: #4c4f69;
277
+
}
278
+
/* Error */
279
+
.chroma .err {
280
+
color: #d20f39;
281
+
}
282
+
/* LineLink */
283
+
.chroma .lnlinks {
284
+
outline: none;
285
+
text-decoration: none;
286
+
color: inherit;
287
+
}
288
+
/* LineTableTD */
289
+
.chroma .lntd {
290
+
vertical-align: top;
291
+
padding: 0;
292
+
margin: 0;
293
+
border: 0;
294
+
}
295
+
/* LineTable */
296
+
.chroma .lntable {
297
+
border-spacing: 0;
298
+
padding: 0;
299
+
margin: 0;
300
+
border: 0;
301
+
}
302
+
/* LineHighlight */
303
+
.chroma .hl {
304
+
@apply bg-amber-400/30 dark:bg-amber-500/20;
305
+
}
306
+
307
+
/* LineNumbersTable */
308
+
.chroma .lnt {
309
+
white-space: pre;
310
+
-webkit-user-select: none;
311
+
user-select: none;
312
+
margin-right: 0.4em;
313
+
padding: 0 0.4em 0 0.4em;
314
+
color: #8c8fa1;
315
+
}
316
+
/* LineNumbers */
317
+
.chroma .ln {
318
+
white-space: pre;
319
+
-webkit-user-select: none;
320
+
user-select: none;
321
+
margin-right: 0.4em;
322
+
padding: 0 0.4em 0 0.4em;
323
+
color: #8c8fa1;
324
+
}
325
+
/* Line */
326
+
.chroma .line {
327
+
display: flex;
328
+
}
329
+
/* Keyword */
330
+
.chroma .k {
331
+
color: #8839ef;
332
+
}
333
+
/* KeywordConstant */
334
+
.chroma .kc {
335
+
color: #fe640b;
336
+
}
337
+
/* KeywordDeclaration */
338
+
.chroma .kd {
339
+
color: #d20f39;
340
+
}
341
+
/* KeywordNamespace */
342
+
.chroma .kn {
343
+
color: #179299;
344
+
}
345
+
/* KeywordPseudo */
346
+
.chroma .kp {
347
+
color: #8839ef;
348
+
}
349
+
/* KeywordReserved */
350
+
.chroma .kr {
351
+
color: #8839ef;
352
+
}
353
+
/* KeywordType */
354
+
.chroma .kt {
355
+
color: #d20f39;
356
+
}
357
+
/* NameAttribute */
358
+
.chroma .na {
359
+
color: #1e66f5;
360
+
}
361
+
/* NameBuiltin */
362
+
.chroma .nb {
363
+
color: #04a5e5;
364
+
}
365
+
/* NameBuiltinPseudo */
366
+
.chroma .bp {
367
+
color: #04a5e5;
368
+
}
369
+
/* NameClass */
370
+
.chroma .nc {
371
+
color: #df8e1d;
372
+
}
373
+
/* NameConstant */
374
+
.chroma .no {
375
+
color: #df8e1d;
376
+
}
377
+
/* NameDecorator */
378
+
.chroma .nd {
379
+
color: #1e66f5;
380
+
font-weight: bold;
381
+
}
382
+
/* NameEntity */
383
+
.chroma .ni {
384
+
color: #179299;
385
+
}
386
+
/* NameException */
387
+
.chroma .ne {
388
+
color: #fe640b;
389
+
}
390
+
/* NameFunction */
391
+
.chroma .nf {
392
+
color: #1e66f5;
393
+
}
394
+
/* NameFunctionMagic */
395
+
.chroma .fm {
396
+
color: #1e66f5;
397
+
}
398
+
/* NameLabel */
399
+
.chroma .nl {
400
+
color: #04a5e5;
401
+
}
402
+
/* NameNamespace */
403
+
.chroma .nn {
404
+
color: #fe640b;
405
+
}
406
+
/* NameProperty */
407
+
.chroma .py {
408
+
color: #fe640b;
409
+
}
410
+
/* NameTag */
411
+
.chroma .nt {
412
+
color: #8839ef;
413
+
}
414
+
/* NameVariable */
415
+
.chroma .nv {
416
+
color: #dc8a78;
417
+
}
418
+
/* NameVariableClass */
419
+
.chroma .vc {
420
+
color: #dc8a78;
421
+
}
422
+
/* NameVariableGlobal */
423
+
.chroma .vg {
424
+
color: #dc8a78;
425
+
}
426
+
/* NameVariableInstance */
427
+
.chroma .vi {
428
+
color: #dc8a78;
429
+
}
430
+
/* NameVariableMagic */
431
+
.chroma .vm {
432
+
color: #dc8a78;
433
+
}
434
+
/* LiteralString */
435
+
.chroma .s {
436
+
color: #40a02b;
437
+
}
438
+
/* LiteralStringAffix */
439
+
.chroma .sa {
440
+
color: #d20f39;
441
+
}
442
+
/* LiteralStringBacktick */
443
+
.chroma .sb {
444
+
color: #40a02b;
445
+
}
446
+
/* LiteralStringChar */
447
+
.chroma .sc {
448
+
color: #40a02b;
449
+
}
450
+
/* LiteralStringDelimiter */
451
+
.chroma .dl {
452
+
color: #1e66f5;
453
+
}
454
+
/* LiteralStringDoc */
455
+
.chroma .sd {
456
+
color: #9ca0b0;
457
+
}
458
+
/* LiteralStringDouble */
459
+
.chroma .s2 {
460
+
color: #40a02b;
461
+
}
462
+
/* LiteralStringEscape */
463
+
.chroma .se {
464
+
color: #1e66f5;
465
+
}
466
+
/* LiteralStringHeredoc */
467
+
.chroma .sh {
468
+
color: #9ca0b0;
469
+
}
470
+
/* LiteralStringInterpol */
471
+
.chroma .si {
472
+
color: #40a02b;
473
+
}
474
+
/* LiteralStringOther */
475
+
.chroma .sx {
476
+
color: #40a02b;
477
+
}
478
+
/* LiteralStringRegex */
479
+
.chroma .sr {
480
+
color: #179299;
481
+
}
482
+
/* LiteralStringSingle */
483
+
.chroma .s1 {
484
+
color: #40a02b;
485
+
}
486
+
/* LiteralStringSymbol */
487
+
.chroma .ss {
488
+
color: #40a02b;
489
+
}
490
+
/* LiteralNumber */
491
+
.chroma .m {
492
+
color: #fe640b;
493
+
}
494
+
/* LiteralNumberBin */
495
+
.chroma .mb {
496
+
color: #fe640b;
497
+
}
498
+
/* LiteralNumberFloat */
499
+
.chroma .mf {
500
+
color: #fe640b;
501
+
}
502
+
/* LiteralNumberHex */
503
+
.chroma .mh {
504
+
color: #fe640b;
505
+
}
506
+
/* LiteralNumberInteger */
507
+
.chroma .mi {
508
+
color: #fe640b;
509
+
}
510
+
/* LiteralNumberIntegerLong */
511
+
.chroma .il {
512
+
color: #fe640b;
513
+
}
514
+
/* LiteralNumberOct */
515
+
.chroma .mo {
516
+
color: #fe640b;
517
+
}
518
+
/* Operator */
519
+
.chroma .o {
520
+
color: #04a5e5;
521
+
font-weight: bold;
522
+
}
523
+
/* OperatorWord */
524
+
.chroma .ow {
525
+
color: #04a5e5;
526
+
font-weight: bold;
527
+
}
528
+
/* Comment */
529
+
.chroma .c {
530
+
color: #9ca0b0;
531
+
font-style: italic;
532
+
}
533
+
/* CommentHashbang */
534
+
.chroma .ch {
535
+
color: #9ca0b0;
536
+
font-style: italic;
537
+
}
538
+
/* CommentMultiline */
539
+
.chroma .cm {
540
+
color: #9ca0b0;
541
+
font-style: italic;
542
+
}
543
+
/* CommentSingle */
544
+
.chroma .c1 {
545
+
color: #9ca0b0;
546
+
font-style: italic;
547
+
}
548
+
/* CommentSpecial */
549
+
.chroma .cs {
550
+
color: #9ca0b0;
551
+
font-style: italic;
552
+
}
553
+
/* CommentPreproc */
554
+
.chroma .cp {
555
+
color: #9ca0b0;
556
+
font-style: italic;
557
+
}
558
+
/* CommentPreprocFile */
559
+
.chroma .cpf {
560
+
color: #9ca0b0;
561
+
font-weight: bold;
562
+
font-style: italic;
563
+
}
564
+
/* GenericDeleted */
565
+
.chroma .gd {
566
+
color: #d20f39;
567
+
background-color: oklch(93.6% 0.032 17.717);
568
+
}
569
+
/* GenericEmph */
570
+
.chroma .ge {
571
+
font-style: italic;
572
+
}
573
+
/* GenericError */
574
+
.chroma .gr {
575
+
color: #d20f39;
576
+
}
577
+
/* GenericHeading */
578
+
.chroma .gh {
579
+
color: #fe640b;
580
+
font-weight: bold;
581
+
}
582
+
/* GenericInserted */
583
+
.chroma .gi {
584
+
color: #40a02b;
585
+
background-color: oklch(96.2% 0.044 156.743);
586
+
}
587
+
/* GenericStrong */
588
+
.chroma .gs {
589
+
font-weight: bold;
590
+
}
591
+
/* GenericSubheading */
592
+
.chroma .gu {
593
+
color: #fe640b;
594
+
font-weight: bold;
595
+
}
596
+
/* GenericTraceback */
597
+
.chroma .gt {
598
+
color: #d20f39;
599
+
}
600
+
/* GenericUnderline */
601
+
.chroma .gl {
602
+
text-decoration: underline;
603
+
}
604
+
605
+
@media (prefers-color-scheme: dark) {
606
+
/* Background */
607
+
.bg {
608
+
color: #cad3f5;
609
+
background-color: #24273a;
610
+
}
611
+
/* PreWrapper */
612
+
.chroma {
613
+
color: #cad3f5;
614
+
}
615
+
/* Error */
616
+
.chroma .err {
617
+
color: #ed8796;
618
+
}
619
+
/* LineLink */
620
+
.chroma .lnlinks {
621
+
outline: none;
622
+
text-decoration: none;
623
+
color: inherit;
624
+
}
625
+
/* LineTableTD */
626
+
.chroma .lntd {
627
+
vertical-align: top;
628
+
padding: 0;
629
+
margin: 0;
630
+
border: 0;
631
+
}
632
+
/* LineTable */
633
+
.chroma .lntable {
634
+
border-spacing: 0;
635
+
padding: 0;
636
+
margin: 0;
637
+
border: 0;
638
+
}
639
+
/* LineHighlight */
640
+
.chroma .hl {
641
+
background-color: #494d64;
642
+
}
643
+
/* LineNumbersTable */
644
+
.chroma .lnt {
645
+
white-space: pre;
646
+
-webkit-user-select: none;
647
+
user-select: none;
648
+
margin-right: 0.4em;
649
+
padding: 0 0.4em 0 0.4em;
650
+
color: #8087a2;
651
+
}
652
+
/* LineNumbers */
653
+
.chroma .ln {
654
+
white-space: pre;
655
+
-webkit-user-select: none;
656
+
user-select: none;
657
+
margin-right: 0.4em;
658
+
padding: 0 0.4em 0 0.4em;
659
+
color: #8087a2;
660
+
}
661
+
/* Line */
662
+
.chroma .line {
663
+
display: flex;
664
+
}
665
+
/* Keyword */
666
+
.chroma .k {
667
+
color: #c6a0f6;
668
+
}
669
+
/* KeywordConstant */
670
+
.chroma .kc {
671
+
color: #f5a97f;
672
+
}
673
+
/* KeywordDeclaration */
674
+
.chroma .kd {
675
+
color: #ed8796;
676
+
}
677
+
/* KeywordNamespace */
678
+
.chroma .kn {
679
+
color: #8bd5ca;
680
+
}
681
+
/* KeywordPseudo */
682
+
.chroma .kp {
683
+
color: #c6a0f6;
684
+
}
685
+
/* KeywordReserved */
686
+
.chroma .kr {
687
+
color: #c6a0f6;
688
+
}
689
+
/* KeywordType */
690
+
.chroma .kt {
691
+
color: #ed8796;
692
+
}
693
+
/* NameAttribute */
694
+
.chroma .na {
695
+
color: #8aadf4;
696
+
}
697
+
/* NameBuiltin */
698
+
.chroma .nb {
699
+
color: #91d7e3;
700
+
}
701
+
/* NameBuiltinPseudo */
702
+
.chroma .bp {
703
+
color: #91d7e3;
704
+
}
705
+
/* NameClass */
706
+
.chroma .nc {
707
+
color: #eed49f;
708
+
}
709
+
/* NameConstant */
710
+
.chroma .no {
711
+
color: #eed49f;
712
+
}
713
+
/* NameDecorator */
714
+
.chroma .nd {
715
+
color: #8aadf4;
716
+
font-weight: bold;
717
+
}
718
+
/* NameEntity */
719
+
.chroma .ni {
720
+
color: #8bd5ca;
721
+
}
722
+
/* NameException */
723
+
.chroma .ne {
724
+
color: #f5a97f;
725
+
}
726
+
/* NameFunction */
727
+
.chroma .nf {
728
+
color: #8aadf4;
729
+
}
730
+
/* NameFunctionMagic */
731
+
.chroma .fm {
732
+
color: #8aadf4;
733
+
}
734
+
/* NameLabel */
735
+
.chroma .nl {
736
+
color: #91d7e3;
737
+
}
738
+
/* NameNamespace */
739
+
.chroma .nn {
740
+
color: #f5a97f;
741
+
}
742
+
/* NameProperty */
743
+
.chroma .py {
744
+
color: #f5a97f;
745
+
}
746
+
/* NameTag */
747
+
.chroma .nt {
748
+
color: #c6a0f6;
749
+
}
750
+
/* NameVariable */
751
+
.chroma .nv {
752
+
color: #f4dbd6;
753
+
}
754
+
/* NameVariableClass */
755
+
.chroma .vc {
756
+
color: #f4dbd6;
757
+
}
758
+
/* NameVariableGlobal */
759
+
.chroma .vg {
760
+
color: #f4dbd6;
761
+
}
762
+
/* NameVariableInstance */
763
+
.chroma .vi {
764
+
color: #f4dbd6;
765
+
}
766
+
/* NameVariableMagic */
767
+
.chroma .vm {
768
+
color: #f4dbd6;
769
+
}
770
+
/* LiteralString */
771
+
.chroma .s {
772
+
color: #a6da95;
773
+
}
774
+
/* LiteralStringAffix */
775
+
.chroma .sa {
776
+
color: #ed8796;
777
+
}
778
+
/* LiteralStringBacktick */
779
+
.chroma .sb {
780
+
color: #a6da95;
781
+
}
782
+
/* LiteralStringChar */
783
+
.chroma .sc {
784
+
color: #a6da95;
785
+
}
786
+
/* LiteralStringDelimiter */
787
+
.chroma .dl {
788
+
color: #8aadf4;
789
+
}
790
+
/* LiteralStringDoc */
791
+
.chroma .sd {
792
+
color: #6e738d;
793
+
}
794
+
/* LiteralStringDouble */
795
+
.chroma .s2 {
796
+
color: #a6da95;
797
+
}
798
+
/* LiteralStringEscape */
799
+
.chroma .se {
800
+
color: #8aadf4;
801
+
}
802
+
/* LiteralStringHeredoc */
803
+
.chroma .sh {
804
+
color: #6e738d;
805
+
}
806
+
/* LiteralStringInterpol */
807
+
.chroma .si {
808
+
color: #a6da95;
809
+
}
810
+
/* LiteralStringOther */
811
+
.chroma .sx {
812
+
color: #a6da95;
813
+
}
814
+
/* LiteralStringRegex */
815
+
.chroma .sr {
816
+
color: #8bd5ca;
817
+
}
818
+
/* LiteralStringSingle */
819
+
.chroma .s1 {
820
+
color: #a6da95;
821
+
}
822
+
/* LiteralStringSymbol */
823
+
.chroma .ss {
824
+
color: #a6da95;
825
+
}
826
+
/* LiteralNumber */
827
+
.chroma .m {
828
+
color: #f5a97f;
829
+
}
830
+
/* LiteralNumberBin */
831
+
.chroma .mb {
832
+
color: #f5a97f;
833
+
}
834
+
/* LiteralNumberFloat */
835
+
.chroma .mf {
836
+
color: #f5a97f;
837
+
}
838
+
/* LiteralNumberHex */
839
+
.chroma .mh {
840
+
color: #f5a97f;
841
+
}
842
+
/* LiteralNumberInteger */
843
+
.chroma .mi {
844
+
color: #f5a97f;
845
+
}
846
+
/* LiteralNumberIntegerLong */
847
+
.chroma .il {
848
+
color: #f5a97f;
849
+
}
850
+
/* LiteralNumberOct */
851
+
.chroma .mo {
852
+
color: #f5a97f;
853
+
}
854
+
/* Operator */
855
+
.chroma .o {
856
+
color: #91d7e3;
857
+
font-weight: bold;
858
+
}
859
+
/* OperatorWord */
860
+
.chroma .ow {
861
+
color: #91d7e3;
862
+
font-weight: bold;
863
+
}
864
+
/* Comment */
865
+
.chroma .c {
866
+
color: #6e738d;
867
+
font-style: italic;
868
+
}
869
+
/* CommentHashbang */
870
+
.chroma .ch {
871
+
color: #6e738d;
872
+
font-style: italic;
873
+
}
874
+
/* CommentMultiline */
875
+
.chroma .cm {
876
+
color: #6e738d;
877
+
font-style: italic;
878
+
}
879
+
/* CommentSingle */
880
+
.chroma .c1 {
881
+
color: #6e738d;
882
+
font-style: italic;
883
+
}
884
+
/* CommentSpecial */
885
+
.chroma .cs {
886
+
color: #6e738d;
887
+
font-style: italic;
888
+
}
889
+
/* CommentPreproc */
890
+
.chroma .cp {
891
+
color: #6e738d;
892
+
font-style: italic;
893
+
}
894
+
/* CommentPreprocFile */
895
+
.chroma .cpf {
896
+
color: #6e738d;
897
+
font-weight: bold;
898
+
font-style: italic;
899
+
}
900
+
/* GenericDeleted */
901
+
.chroma .gd {
902
+
color: #ed8796;
903
+
background-color: oklch(44.4% 0.177 26.899 / 0.5);
904
+
}
905
+
/* GenericEmph */
906
+
.chroma .ge {
907
+
font-style: italic;
908
+
}
909
+
/* GenericError */
910
+
.chroma .gr {
911
+
color: #ed8796;
912
+
}
913
+
/* GenericHeading */
914
+
.chroma .gh {
915
+
color: #f5a97f;
916
+
font-weight: bold;
917
+
}
918
+
/* GenericInserted */
919
+
.chroma .gi {
920
+
color: #a6da95;
921
+
background-color: oklch(44.8% 0.119 151.328 / 0.5);
922
+
}
923
+
/* GenericStrong */
924
+
.chroma .gs {
925
+
font-weight: bold;
926
+
}
927
+
/* GenericSubheading */
928
+
.chroma .gu {
929
+
color: #f5a97f;
930
+
font-weight: bold;
931
+
}
932
+
/* GenericTraceback */
933
+
.chroma .gt {
934
+
color: #ed8796;
935
+
}
936
+
/* GenericUnderline */
937
+
.chroma .gl {
938
+
text-decoration: underline;
939
+
}
940
+
}
941
+
942
+
actor-typeahead {
943
+
--color-background: #ffffff;
944
+
--color-border: #d1d5db;
945
+
--color-shadow: #000000;
946
+
--color-hover: #f9fafb;
947
+
--color-avatar-fallback: #e5e7eb;
948
+
--radius: 0.0;
949
+
--padding-menu: 0.0rem;
950
+
z-index: 1000;
951
+
}
952
+
953
+
actor-typeahead::part(handle) {
954
+
color: #111827;
955
+
}
956
+
957
+
actor-typeahead::part(menu) {
958
+
box-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
959
+
}
960
+
961
+
@media (prefers-color-scheme: dark) {
962
+
actor-typeahead {
963
+
--color-background: #1f2937;
964
+
--color-border: #4b5563;
965
+
--color-shadow: #000000;
966
+
--color-hover: #374151;
967
+
--color-avatar-fallback: #4b5563;
968
+
}
969
+
970
+
actor-typeahead::part(handle) {
971
+
color: #f9fafb;
972
+
}
144
973
}
+90
-46
pages/blog/6-months.md
+90
-46
pages/blog/6-months.md
···
2
2
atroot: true
3
3
template:
4
4
slug: 6-months
5
-
title: 6 months of tangling
6
-
subtitle: new domain, a quick recap, and notes on the future
7
-
date: 2025-09-18
8
-
image: /static/img/logo_with_text.jpeg
5
+
title: 6 months of Tangled
6
+
subtitle: a quick recap, and notes on the future
7
+
date: 2025-10-21
8
+
image: /static/img/6-months.png
9
9
authors:
10
10
- name: Anirudh
11
-
email: anirudh@tangled.sh
12
-
handle: icyphox.sh
11
+
email: anirudh@tangled.org
12
+
handle: anirudh.fi
13
13
- name: Akshay
14
-
email: akshay@tangled.sh
14
+
email: akshay@tangled.org
15
15
handle: oppi.li
16
-
draft: true
16
+
draft: false
17
17
---
18
18
19
19
Hello Tanglers! It's been over 6 months since we first announced
20
-
Tangled, so we figured we'd do a quick retrospective on what we built so
20
+
Tangled, so we figured we'd do a quick retrospective of what we built so
21
21
far and what's next.
22
22
23
-
But before that, we've got a big announcement!
24
-
25
-
## tangled.sh is now tangled.org!
26
-
27
-
[Chris Maytag](https://bsky.app/profile/cpm5280.bsky.social) very kindly
28
-
let us have the tangled.org domain!
29
-
30
-
We're still in the process of migrating and there may be stray
31
-
references to the .sh domain in places so bear with us. Rest assured:
32
-
your current links to tangled.sh will be redirected to tangled.org.
23
+
If you're new here, here's a quick overview: Tangled is a git hosting
24
+
and collaboration platform built on top of the [AT
25
+
Protocol](https://atproto.com). You can read a bit more about our
26
+
architecture [here](/intro).
33
27
34
28
## new logo and mascot: dolly!
35
29
36
30
Tangled finally has a logo! Designed by Akshay himself, Dolly is in
37
-
reference to the first ever *cloned* mammal.
31
+
reference to the first ever *cloned* mammal. For a full set of brand assets and guidelines, see our new [branding page](https://tangled.org/brand).
38
32
39
33

40
34
41
35
With that, let's recap the major platform improvements so far!
42
36
43
-
### pull requests
37
+
## pull requests: doubling down on jujutsu
44
38
45
-
One of the first major features we built was our pull requests system,
46
-
which follows a unique round-based submission & review approach. This
47
-
was really fun to innovate on -- it remains one of Tangled's core
48
-
differentiators, and one we plan to keep improving.
39
+
One of the first major features we built was our [pull requests
40
+
system](/pulls), which follows a unique round-based submission & review
41
+
approach. This was really fun to innovate on -- it remains one of
42
+
Tangled's core differentiators, and one we plan to keep improving.
49
43
50
44
In the same vein, we're the first ever code forge to support [stacking
51
45
pull requests](/stacking) using Jujutsu! We're big fans of the tool and
52
46
we use it everyday as we hack on
53
-
[core](https://tangled.sh/@tangled.sh/core). Ultimately, we think
54
-
PR-based collaboration should evolve beyond the traditional model, and
55
-
we're excited to keep experimenting with new ideas that make code review
56
-
and contribution easier!
47
+
[tangled.org/core](https://tangled.org/@tangled.org/core).
57
48
58
-
### spindle
49
+
Ultimately, we think PR-based collaboration should evolve beyond the
50
+
traditional model, and we're excited to keep experimenting with new
51
+
ideas that make code review and contribution easier!
52
+
53
+
## spindle
59
54
60
55
CI was our most requested feature, and we spent a *lot* of time debating
61
56
how to approach it. We considered integrating with existing platforms,
62
57
but none were good fits. So we gave in to NIH and [built spindle
63
-
ourselves](/ci)! This turned out great -- we could go all in on Nix and
64
-
make it "atproto native".
58
+
ourselves](/ci)! This allowed us to go in on Nix using Nixery to build
59
+
CI images on the fly and cache them.
65
60
66
-
Spindle is still early but designed to be extensible. The current
67
-
Docker-based engine is limiting -- we plan to switch to micro VMs
68
-
eventually. Meanwhile, if you've got ideas for other spindle backends
69
-
(Kubernetes?!), we'd love to [hear from you](https://chat.tangled.sh).
61
+
Spindle is still early but designed to be extensible and is AT-native.
62
+
The current Docker/Nixery-based engine is limiting -- we plan to switch
63
+
to micro VMs down the line to run full-fledged NixOS (and other base
64
+
images). Meanwhile, if you've got ideas for other spindle backends
65
+
(Kubernetes?!), we'd love to [hear from you](https://chat.tangled.org).
70
66
71
-
### XRPC APIs
67
+
## XRPC APIs
72
68
73
69
We introduced a complete migration of the knotserver to an
74
70
[XRPC](https://atproto.com/specs/xrpc) API. Alongside this, we also
···
80
76
81
77
[lexicons]: https://tangled.sh/@tangled.sh/core/tree/master/lexicons
82
78
83
-
### issues rework
79
+
## issues rework
84
80
85
81
Issues got a major rework (and facelift) too! They are now threaded:
86
82
top-level comments with replies. This makes Q/A style discussions much
···
88
84
89
85

90
86
91
-
### hosted PDS
87
+
## hosted PDS
92
88
93
89
A complaint we often recieved was the need for a Bluesky account to use
94
90
Tangled; and besides, we realised that the overlap between Bluesky users
95
91
and possible Tangled users only goes so far -- we aim to be a generic
96
-
code forge after all, atproto just happens to be an implementation
92
+
code forge after all, AT just happens to be an implementation
97
93
detail.
98
94
99
95
To address this, we spun up the tngl.sh PDS hosted right here in
···
102
98
experience as a generic PDS host, but we're still working out details
103
99
around that.
104
100
101
+
## labels
102
+
103
+
You can easily categorize issues and pulls via labels! There is plenty
104
+
of customization available:
105
+
106
+
- labels can be basic, or they can have a key and value set, for example:
107
+
`wontfix` or `priority/high`
108
+
- labels can be constrained to a set of values: `priority: [high medium low]`
109
+
- there can be multiple labels of a given type: `reviewed-by: @oppi.li`,
110
+
`reviewed-by: @anirudh.fi`
111
+
112
+
The options are endless! You can access them via your repo's settings page.
113
+
114
+
<div class="flex justify-center items-center gap-2">
115
+
<figure class="w-full m-0 flex flex-col items-center">
116
+
<a href="static/img/labels_vignette.webp">
117
+
<img class="my-1 w-full h-auto cursor-pointer" src="static/img/labels_vignette.webp" alt="A set of labels applied to an issue.">
118
+
</a>
119
+
<figcaption class="text-center">A set of labels applied to an issue.</figcaption>
120
+
</figure>
121
+
122
+
<figure class="w-1/3 m-0 flex flex-col items-center">
123
+
<a href="static/img/new_label_modal.png">
124
+
<img class="my-1 w-full h-auto cursor-pointer" src="static/img/new_label_modal.png" alt="Create custom key-value type labels.">
125
+
</a>
126
+
<figcaption class="text-center">Create custom key-value type labels.</figcaption>
127
+
</figure>
128
+
</div>
129
+
130
+
131
+
## notifications
132
+
133
+
In-app notifications now exist! You get notifications for a variety of events now:
134
+
135
+
* new issues/pulls on your repos (also for collaborators)
136
+
* comments on your issues/pulls (also for collaborators)
137
+
* close/reopen (or merge) of issues/pulls
138
+
* new stars
139
+
* new follows
140
+
141
+
All of this can be fine-tuned in [/settings/notifications](https://tangled.org/settings/notifications).
142
+
143
+

144
+
145
+
105
146
## the future
106
147
107
148
We're working on a *lot* of exciting new things and possibly some big
108
-
announcements to come:
149
+
announcements to come. Be on the lookout for:
109
150
110
-
* labels
111
-
* notifications: both in-app and emails
151
+
* email notifications
112
152
* preliminary support for issue and PR search
113
-
* total federation
114
-
* network playback
153
+
* total "atprotation" [^1] -- the last two holdouts here are repo and pull records
154
+
* total federation -- i.e. supporting third-party appviews by making it
155
+
reproducible
156
+
* achieve complete independence from Bluesky PBC by hosting our own relay
115
157
116
-
That's all for now; we'll see you in the atmosphere!
158
+
That's all for now; we'll see you in the atmosphere! Meanwhile, if you'd like to contribute to projects on Tangled, make sure to check out the [good first issues page](https://tangled.org/goodfirstissues) to get started!
159
+
160
+
[^1]: atprotation implies a two-way sync between the PDS and appview. Currently, pull requests and repositories are not ingested -- so writing/updating either records on your PDS will not show up on the appview.
+1
-1
pages/blog/ci.md
+1
-1
pages/blog/ci.md
+241
pages/blog/docs.md
+241
pages/blog/docs.md
···
1
+
---
2
+
atroot: true
3
+
template:
4
+
slug: docs
5
+
title: why we rolled our own documentation site
6
+
subtitle: you don't need mintlify
7
+
date: 2026-01-06
8
+
authors:
9
+
- name: Akshay
10
+
email: akshay@tangled.org
11
+
handle: oppi.li
12
+
draft: true
13
+
---
14
+
15
+
We recently organized our documentation and put it up on
16
+
https://docs.tangled.org, using just pandoc. For several
17
+
reasons, using pandoc to roll your own static sites is more
18
+
than sufficient for small projects.
19
+
20
+

21
+
22
+
## requirements
23
+
24
+
- Lives in [our
25
+
monorepo](https://tangled.org/tangled.org/core).
26
+
- No JS: a collection of pages containing just text
27
+
should not require JS to view!
28
+
- Searchability: in practice, documentation engines that
29
+
come bundled with a search-engine have always been lack
30
+
lustre. I tend to Ctrl+F or use an actual search engine in
31
+
most scenarios.
32
+
- Low complexity: building, testing, deploying should be
33
+
easy.
34
+
- Easy to style
35
+
36
+
## evaluating the ecosystem
37
+
38
+
I took the time to evaluate several documentation engine
39
+
solutions:
40
+
41
+
- [Mintlify](https://www.mintlify.com/): It is quite obvious
42
+
from their homepage that mintlify is performing an AI
43
+
pivot for the sake of doing so.
44
+
- [Docusaurus](https://docusaurus.io/): The generated
45
+
documentation site is quite nice, but the value of pages
46
+
being served as a full-blown React SPA is questionable.
47
+
- [MkDocs](https://www.mkdocs.org/): Works great with JS
48
+
disabled, however the table of contents needs to be
49
+
maintained via `mkdocs.yml`, which can be quite tedious.
50
+
- [MdBook](https://rust-lang.github.io/mdBook/index.html):
51
+
As above, you need a `SUMMARY.md` file to control the
52
+
table-of-contents.
53
+
54
+
MkDocs and MdBook are still on my radar however, in case we
55
+
need a bigger feature set.
56
+
57
+
## using pandoc
58
+
59
+
[pandoc](https://pandoc.org/) is a wonderfully customizable
60
+
markup converter. It provides a "chunkedhtml" output format,
61
+
which is perfect for generating documentation sites. Without
62
+
any customization,
63
+
[this](https://pandoc.org/demo/example33/) is the generated
64
+
output, for this [markdown file
65
+
input](https://pandoc.org/demo/MANUAL.txt).
66
+
67
+
- You get an autogenerated TOC based on the document layout
68
+
- Each section is turned into a page of its own
69
+
70
+
Massaging pandoc to work for us was quite straightforward:
71
+
72
+
- I first combined all our individual markdown files into
73
+
[one big
74
+
`DOCS.md`](https://tangled.org/tangled.org/core/blob/master/docs/DOCS.md)
75
+
file.
76
+
- Modified the [default
77
+
template](https://github.com/jgm/pandoc-templates/blob/master/default.chunkedhtml)
78
+
to put the TOC on every page, to form a "sidebar", see
79
+
[`docs/template.html`](https://tangled.org/tangled.org/core/blob/master/docs/template.html)
80
+
- Inserted tailwind `prose` classes where necessary, such
81
+
that markdown content is rendered the same way between
82
+
`tangled.org` and `docs.tangled.org`
83
+
84
+
Generating the docs is done with one pandoc command:
85
+
86
+
```bash
87
+
pandoc docs/DOCS.md \
88
+
-o out/ \
89
+
-t chunkedhtml \
90
+
--variable toc \
91
+
--toc-depth=2 \
92
+
--css=docs/stylesheet.css \
93
+
--chunk-template="%i.html" \
94
+
--highlight-style=docs/highlight.theme \
95
+
--template=docs/template.html
96
+
```
97
+
98
+
## avoiding javascript
99
+
100
+
The "sidebar" style table-of-contents needs to be collapsed
101
+
on mobile displays. Most of the engines I evaluated seem to
102
+
require JS to collapse and expand the sidebar, with MkDocs
103
+
being the outlier, it uses a checkbox with the
104
+
[`:checked`](https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Selectors/:checked)
105
+
pseudo-class trick to avoid JS.
106
+
107
+
The other ways to do this are:
108
+
109
+
- Use `<details` and `<summary>`: this is definitely a
110
+
"hack", clicking outside the sidebar does not collapse it.
111
+
Using Ctrl+F or "Find in page" still works through the
112
+
details tag though.
113
+
- Use the new `popover` API: this seems like the perfect fit
114
+
for a "sidebar" component.
115
+
116
+
The bar at the top includes a button to trigger the popover:
117
+
118
+
```html
119
+
<button popovertarget="toc-popover">Table of Contents</button>
120
+
```
121
+
122
+
And a `fixed` position div includes the TOC itself:
123
+
124
+
```html
125
+
<div id="toc-popover" popover class="fixed top-0">
126
+
<ul>
127
+
Quick Start
128
+
<li>...</li>
129
+
<li>...</li>
130
+
<li>...</li>
131
+
</ul>
132
+
</div>
133
+
```
134
+
135
+
The TOC is scrollable independently and can be collapsed by
136
+
clicking anywhere on the screen outside the sidebar.
137
+
Searching for content in the page via "Find in page" does
138
+
not show any results that are present in the popover
139
+
however. The collapsible TOC is only available on smaller
140
+
viewports, the TOC is not hidden on larger viewports.
141
+
142
+
## search
143
+
144
+
There is native search on the site for now. Taking
145
+
inspiration from https://htmx.org's search bar, our search
146
+
bar also simply redirects to Google:
147
+
148
+
```
149
+
<form action="https://google.com/search">
150
+
<input type="hidden" name="q" value="+[inurl:https://docs.tangled.org]">
151
+
...
152
+
</form>
153
+
```
154
+
155
+
I mentioned earlier that Ctrl+F has typically worked better
156
+
for me than, say, the search engine provided by Docusaurus.
157
+
To that end, the same docs have been exported to a ["single
158
+
page" format](https://docs.tangled.org/single-page.html), by
159
+
just removing the `chunkedhtml` related options:
160
+
161
+
```diff
162
+
pandoc docs/DOCS.md \
163
+
-o out/ \
164
+
- -t chunkedhtml \
165
+
--variable toc \
166
+
--toc-depth=2 \
167
+
--css=docs/stylesheet.css \
168
+
- --chunk-template="%i.html" \
169
+
--highlight-style=docs/highlight.theme \
170
+
--template=docs/template.html
171
+
```
172
+
173
+
With all the content on a single page, it is trivial to
174
+
search through the entire site with the browser. If the docs
175
+
do outgrow this, I will consider other options!
176
+
177
+
## building and deploying
178
+
179
+
We use [nix](https://nixos.org) and
180
+
[colmena](https://colmena.cli.rs/) to build and deploy all
181
+
Tangled services. A nix derivation to [build the
182
+
documentation](https://tangled.org/tangled.org/core/blob/master/nix/pkgs/docs.nix)
183
+
site is written very easily with the `runCommandLocal`
184
+
helper:
185
+
186
+
```nix
187
+
runCommandLocal "docs" {} ''
188
+
.
189
+
.
190
+
.
191
+
${pandoc}/bin/pandoc ${src}/docs/DOCS.md ...
192
+
.
193
+
.
194
+
.
195
+
''
196
+
```
197
+
198
+
The nixos machine is configured to serve the site [via
199
+
nginx](https://tangled.org/tangled.org/infra/blob/master/hosts/nixery/services/nginx.nix#L7):
200
+
201
+
```nix
202
+
services.nginx = {
203
+
enable = true;
204
+
virtualHosts = {
205
+
"docs.tangled.org" = {
206
+
root = "${tangled-pkgs.docs}";
207
+
locations."/" = {
208
+
tryFiles = "$uri $uri/ =404";
209
+
index = "index.html";
210
+
};
211
+
};
212
+
};
213
+
};
214
+
```
215
+
216
+
And deployed using `colmena`:
217
+
218
+
```bash
219
+
nix run nixpkgs#colmena -- apply
220
+
```
221
+
222
+
To update the site, I first run:
223
+
224
+
```bash
225
+
nix flake update tangled
226
+
```
227
+
228
+
Which bumps the `tangled` flake input, and thus
229
+
`tangled-pkgs.docs`. The above `colmena` invocation applies
230
+
the changes to the machine serving the site.
231
+
232
+
## notes
233
+
234
+
Going homegrown has made it a lot easier to style the
235
+
documentation site to match the main site. Unfortunately
236
+
there are still a few discrepancies between pandoc's
237
+
markdown rendering and
238
+
[goldmark's](https://pkg.go.dev/github.com/yuin/goldmark/)
239
+
markdown rendering (which is what we use in Tangled). We may
240
+
yet roll our own SSG,
241
+
[TigerStyle](https://tigerbeetle.com/blog/2025-02-27-why-we-designed-tigerbeetles-docs-from-scratch/)!
static/img/6-months.png
static/img/6-months.png
This is a binary file and will not be displayed.
static/img/docs_homepage.png
static/img/docs_homepage.png
This is a binary file and will not be displayed.
static/img/labels_vignette.webp
static/img/labels_vignette.webp
This is a binary file and will not be displayed.
static/img/new_label_modal.png
static/img/new_label_modal.png
This is a binary file and will not be displayed.
static/img/notifications.png
static/img/notifications.png
This is a binary file and will not be displayed.
+30
-32
templates/index.html
+30
-32
templates/index.html
···
10
10
{{ .Meta.title }}
11
11
</title>
12
12
13
-
<body class="bg-slate-100 dark:bg-gray-900 flex flex-col min-h-screen">
14
-
{{ template "partials/nav.html" }}
15
-
<div class="prose dark:prose-invert mx-auto px-1 pt-4 flex-grow flex flex-col container">
16
-
<main>
17
-
<header class="px-6">
18
-
<h1 class="mb-0">{{ index .Meta "title" }}</h1>
19
-
<h2 class="font-light mt-1 mb-0 text-lg">{{ index .Meta "subtitle" }}</h2>
20
-
</header>
13
+
<body class="bg-slate-100 dark:bg-gray-900 flex flex-col items-center min-h-screen">
14
+
{{ template "partials/nav.html" }}
15
+
<div class="px-1 pt-4 flex-grow flex flex-col w-full max-w-[75ch]">
16
+
<main>
17
+
<header class="px-6">
18
+
<h1 class="mb-0 text-2xl font-bold text-black dark:text-white">{{ index .Meta "title" }}</h1>
19
+
<h2 class="font-light text-gray-600 dark:text-gray-400 mt-1 mb-0 text-lg">{{ index .Meta "subtitle" }}</h2>
20
+
</header>
21
21
22
-
{{ .Body }}
22
+
{{ .Body }}
23
23
24
-
<section class="py-4">
25
-
<ul class="px-0">
24
+
<section class="py-4">
25
+
<ul class="px-0 space-y-4">
26
26
{{ $posts := .Extra.blog }}
27
27
{{ range $posts }}
28
-
{{ if .Allowed }}
29
-
<li class="mt-5 bg-white dark:bg-gray-800 py-4 px-6 rounded drop-shadow-sm list-none">
30
-
{{ $dateStr := .Meta.date }}
31
-
{{ $date := parsedate $dateStr }}
32
-
<div class="post-date py-1 mb-0 text-sm">{{ $date.Format "02 Jan, 2006" }}</div>
33
-
<div>
34
-
<a class="title mb-0 text-xl no-underline font-bold" href="/{{ .Meta.slug }}.html">{{ .Meta.title }}</a>
35
-
{{ if .Meta.draft }}
36
-
<span class="text-red-500">[draft]</span>
37
-
{{ end }}
38
-
<p class="italic mt-1 mb-0">{{ .Meta.subtitle }}</p>
39
-
</div>
40
-
</li>
41
-
{{ end }}
28
+
<li class="bg-white dark:bg-gray-800 py-4 px-6 rounded drop-shadow-sm list-none">
29
+
{{ $dateStr := .Meta.date }}
30
+
{{ $date := parsedate $dateStr }}
31
+
<div class="post-date py-1 mb-0 text-sm text-gray-600 dark:text-gray-400">{{ $date.Format "02 Jan, 2006" }}</div>
32
+
<div>
33
+
<a class="title mb-0 text-xl no-underline font-bold" href="/{{ .Meta.slug }}.html">{{ .Meta.title }}</a>
34
+
{{ if .Meta.draft }}
35
+
<span class="text-red-500">[draft]</span>
36
+
{{ end }}
37
+
<p class="italic mt-1 mb-0 text-gray-600 dark:text-gray-400">{{ .Meta.subtitle }}</p>
38
+
</div>
39
+
</li>
42
40
{{ end }}
43
41
</ul>
44
-
</section>
45
-
</main>
46
-
</div>
47
-
<footer class="w-full">
48
-
{{ template "partials/footer.html" }}
49
-
</footer>
50
-
</body>
42
+
</section>
43
+
</main>
44
+
</div>
45
+
<footer class="w-full">
46
+
{{ template "partials/footer.html" }}
47
+
</footer>
48
+
</body>
51
49
52
50
</html>
+10
-2
templates/text.html
+10
-2
templates/text.html
···
5
5
<meta name="description" content="{{ index .Meta "subtitle" }}">
6
6
<meta property="og:title" content="{{ .Meta.title }}" />
7
7
<meta property="og:description" content="{{ .Meta.subtitle }}" />
8
-
<meta property="og:url" content="https://blog.tangled.sh/{{ .Meta.slug }}" />
9
-
<meta property="og:image" content="{{ .Meta.image }}" />
8
+
<meta property="og:url" content="https://blog.tangled.org/{{ .Meta.slug }}" />
9
+
<meta property="og:image" content="https://blog.tangled.org{{ .Meta.image }}" />
10
10
<meta property="og:type" content="website" />
11
+
<meta property="og:image:width" content="1200" />
12
+
<meta property="og:image:height" content="630" />
13
+
14
+
<meta name="twitter:card" content="summary_large_image" />
15
+
<meta name="twitter:title" content="{{ .Meta.title }}" />
16
+
<meta name="twitter:description" content="{{ .Meta.subtitle }}" />
17
+
<meta name="twitter:image" content="https://blog.tangled.org{{ .Meta.image }}" />
18
+
11
19
<title>
12
20
{{ index .Meta "title" }}
13
21
</title>