mykiwi/mykiwi.blogpublic Fork 0
1ce0eef91b735b1a8e1c1a3977d48214117e2921
Commits
Clone
git clone https://git.rickub.com/mykiwi/mykiwi.blog.git
git clone ssh://git@rickub.com/mykiwi/mykiwi.blog.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

style.scss · 1545 lines · 28.7 KBSCSS Blame HistoryRaw
Port fzakaria.com design to Hugo and add a homepage profile 1bdbd06 Romain Gautier 21h ago1// ---------------------------------------------------------------------------
2// mykiwi.blog — core design ported from fzakaria.com (MIT-licensed).
3// Prose in Newsreader, structural chrome in JetBrains Mono, warm paper/ink
4// palette with a dark-mode swap. Personal-only sections (bio colophon,
5// readership/talks pages, WordPress archive) were left out.
6// ---------------------------------------------------------------------------
7
8@use "sass:list";
9
10$wide: 67.5625rem;
11
12%chrome {
13 font-family: var(--mono);
14 font-size: 0.72rem;
15 font-weight: 500;
16 letter-spacing: 0.11em;
17 text-transform: uppercase;
18 font-variant-numeric: tabular-nums;
19 color: var(--ink-faint);
20}
21
22// --- Fonts -----------------------------------------------------------------
23
24@font-face {
25 font-family: "Newsreader";
26 font-style: normal;
27 font-weight: 400 700;
28 font-display: swap;
29 src: url("/fonts/Newsreader-latin.woff2") format("woff2");
30 unicode-range:
31 u+0000-00ff, u+0131, u+0152-0153, u+02bb-02bc, u+02c6, u+02da, u+02dc,
32 u+0304, u+0308, u+0329, u+2000-206f, u+20ac, u+2122, u+2191, u+2193, u+2212,
33 u+2215, u+feff, u+fffd;
34}
35
36@font-face {
37 font-family: "Newsreader";
38 font-style: normal;
39 font-weight: 400 700;
40 font-display: swap;
41 src: url("/fonts/Newsreader-latin-ext.woff2") format("woff2");
42 unicode-range:
43 u+0100-02ba, u+02bd-02c5, u+02c7-02cc, u+02ce-02d7, u+02dd-02ff, u+0304,
44 u+0308, u+0329, u+1d00-1dbf, u+1e00-1e9f, u+1ef2-1eff, u+2020, u+20a0-20ab,
45 u+20ad-20c0, u+2113, u+2c60-2c7f, u+a720-a7ff;
46}
47
48@font-face {
49 font-family: "Newsreader";
50 font-style: italic;
51 font-weight: 400 700;
52 font-display: swap;
53 src: url("/fonts/Newsreader-italic-latin.woff2") format("woff2");
54 unicode-range:
55 u+0000-00ff, u+0131, u+0152-0153, u+02bb-02bc, u+02c6, u+02da, u+02dc,
56 u+0304, u+0308, u+0329, u+2000-206f, u+20ac, u+2122, u+2191, u+2193, u+2212,
57 u+2215, u+feff, u+fffd;
58}
59
60@font-face {
61 font-family: "Newsreader";
62 font-style: italic;
63 font-weight: 400 700;
64 font-display: swap;
65 src: url("/fonts/Newsreader-italic-latin-ext.woff2") format("woff2");
66 unicode-range:
67 u+0100-02ba, u+02bd-02c5, u+02c7-02cc, u+02ce-02d7, u+02dd-02ff, u+0304,
68 u+0308, u+0329, u+1d00-1dbf, u+1e00-1e9f, u+1ef2-1eff, u+2020, u+20a0-20ab,
69 u+20ad-20c0, u+2113, u+2c60-2c7f, u+a720-a7ff;
70}
71
72@font-face {
73 font-family: "JetBrains Mono";
74 font-style: normal;
75 font-weight: 400;
76 font-display: swap;
77 src: url("/fonts/JetBrainsMono-Regular.woff2") format("woff2");
78}
79
80// --- Tokens ------------------------------------------------------------
81
82:root {
83 --serif:
84 "Newsreader", "Iowan Old Style", "Palatino Linotype", Palatino, Georgia,
85 "Times New Roman", serif;
86 --mono:
87 "JetBrains Mono", ui-monospace, "SFMono-Regular", "Cascadia Mono", Menlo,
88 Consolas, "Liberation Mono", monospace;
89
90 --paper: #f5f2ec;
91 --paper-raised: #ebe6da;
92 --ink: #1a1815;
93 --ink-muted: #565046;
94 --ink-faint: #6f685b;
95 --rule: #dcd5c5;
96 --rule-strong: #c2b9a6;
97 --accent: #9e3413;
98 --accent-wash: #9e34131f;
99 --glow: #fffdf6;
100 --image-mat: #ebe6da;
101
102 --measure: 36rem;
103 --gutter: 0rem;
104 --gap: 0rem;
105 --margin-col: 0rem;
106 --pad: 1.5rem;
107
108 --root-max: 19.5px;
109 --step: 1.6rem;
110
111 color-scheme: light dark;
112}
113
114@media (prefers-color-scheme: dark) {
115 :root {
116 --paper: #15171a;
117 --paper-raised: #1d2025;
118 --ink: #ddd8cd;
119 --ink-muted: #9b958a;
120 --ink-faint: #8a8377;
121 --rule: #2b2f35;
122 --rule-strong: #3f454d;
123 --accent: #e08a45;
124 --accent-wash: #e08a4526;
125 --glow: #1f242b;
126 }
127}
128
129@media (min-width: $wide) {
130 :root {
131 --gutter: 4.5rem;
132 --gap: 2rem;
133 --margin-col: 13.5rem;
134 }
135}
136
137@media (min-width: 87.5rem) {
138 :root {
139 --gutter: 6rem;
140 --gap: 2.5rem;
141 --margin-col: 16rem;
142 }
143}
144
145@media (max-width: 34rem) {
146 :root {
147 --pad: 1.15rem;
148 --step: 1.35rem;
149 }
150}
151
152// --- Reset -----------------------------------------------------------------
153
154*,
155*::before,
156*::after {
157 box-sizing: border-box;
158}
159
160body,
161h1,
162h2,
163h3,
164h4,
165h5,
166h6,
167p,
168ul,
169ol,
170dl,
171dd,
172figure,
173blockquote,
174pre {
175 margin: 0;
176}
177
178html {
179 font-size: clamp(17px, 0.95rem + 0.2vw, var(--root-max));
180 -webkit-text-size-adjust: 100%;
181 scroll-behavior: smooth;
182}
183
184@media (prefers-reduced-motion: reduce) {
185 html {
186 scroll-behavior: auto;
187 }
188}
189
190body {
191 font-family: var(--serif);
192 font-weight: 400;
193 font-size: 1rem;
194 line-height: 1.62;
195 color: var(--ink);
196 background-color: var(--paper);
197 background-image: radial-gradient(
198 75rem 32rem at 14% -6%,
199 var(--glow) 0%,
200 transparent 68%
201 );
202 background-repeat: no-repeat;
203 text-rendering: optimizeLegibility;
204 -webkit-font-smoothing: antialiased;
205 min-height: 100vh;
206 overflow-wrap: break-word;
207}
208
209::selection {
210 background: var(--accent);
211 color: var(--paper);
212}
213
214// --- Layout ------------------------------------------------------------
215
216.wrap {
217 width: 100%;
218 max-width: calc(
219 var(--gutter) + var(--measure) + var(--gap) + var(--margin-col) + 2 *
220 var(--pad)
221 );
222 margin-inline: auto;
223 padding-inline: var(--pad);
224 padding-bottom: calc(var(--step) * 2);
225}
226
227.column {
228 max-width: var(--measure);
229 margin-left: var(--gutter);
230}
231
232.skip-link {
233 position: absolute;
234 left: -9999px;
235 top: 0;
236 padding: 0.5rem 0.9rem;
237 background: var(--accent);
238 color: var(--paper);
239 font-family: var(--mono);
240 font-size: 0.8rem;
241 z-index: 10;
242
243 &:focus {
244 left: 0.5rem;
245 top: 0.5rem;
246 }
247}
248
249:focus-visible {
250 outline: 2px solid var(--accent);
251 outline-offset: 3px;
252 border-radius: 1px;
253}
254
255.visually-hidden {
256 position: absolute;
257 width: 1px;
258 height: 1px;
259 margin: -1px;
260 padding: 0;
261 overflow: hidden;
262 clip-path: inset(50%);
263 white-space: nowrap;
264 border: 0;
265}
266
267// --- Masthead ----------------------------------------------------------
268
269.masthead {
270 padding-top: calc(var(--step) * 1.6);
271 padding-bottom: var(--step);
272 margin-bottom: calc(var(--step) * 1.4);
273 position: relative;
274 display: flex;
275 align-items: baseline;
276 justify-content: space-between;
277 gap: 0 1rem;
278
279 &__signal {
280 position: absolute;
281 left: 0;
282 right: 0;
283 bottom: 0;
284 height: 26px;
285 -webkit-mask-image: linear-gradient(
286 to right,
287 #000 0%,
288 #000 62%,
289 transparent 96%
290 );
291 mask-image: linear-gradient(to right, #000 0%, #000 62%, transparent 96%);
292
293 svg {
294 display: block;
295 width: 100%;
296 height: 100%;
297 }
298
299 path {
300 fill: none;
301 stroke: url("#signal-ramp");
302 stroke-width: 1.5;
303 stroke-linecap: round;
304 stroke-linejoin: round;
305 vector-effect: non-scaling-stroke;
306 }
307 }
308
309 .signal-ramp__lead {
310 stop-color: var(--accent);
311 }
312
313 .signal-ramp__body,
314 .signal-ramp__tail {
315 stop-color: var(--rule-strong);
316 }
317}
318
319.masthead__title {
320 font-size: 1.5rem;
321 font-weight: 600;
322 line-height: 1.1;
323 letter-spacing: -0.018em;
324
325 a {
326 color: inherit;
327 text-decoration: none;
328 }
329
330 a:hover {
331 color: var(--accent);
332 }
333}
334
335// Signpost to the colophon, shown only at widths where the colophon is
336// reordered below the post list and nothing on first screen hints it is
337// there. Turned on alongside that reorder, further down.
338.masthead__about {
339 @extend %chrome;
340 display: none;
341 flex: none;
342 color: var(--ink-muted);
343 text-decoration: none;
344 border-bottom: 1px solid var(--rule);
345
346 &:hover {
347 color: var(--accent);
348 border-bottom-color: var(--accent);
349 }
350}
351
352// --- Prose ---------------------------------------------------------------
353
354.prose {
355 > * + * {
356 margin-top: var(--step);
357 }
358
359 p,
360 ul,
361 ol,
362 dl {
363 line-height: 1.65;
364 }
365
366 div > * + *,
367 details > * + *,
368 li > * + *,
369 td > * + *,
370 th > * + * {
371 margin-top: 0.7rem;
372 }
373
374 h1,
375 h2,
376 h3,
377 h4,
378 h5,
379 h6 {
380 font-weight: 600;
381 line-height: 1.2;
382 letter-spacing: -0.012em;
383 margin-bottom: 0;
384 position: relative;
385 scroll-margin-top: 1.5rem;
386 }
387
388 :is(h1, h2, h3, h4, h5, h6) + :not(h1, h2, h3, h4, h5, h6) {
389 margin-top: 0.55rem;
390 }
391
392 :is(h1, h2, h3, h4, h5, h6) + :is(h1, h2, h3, h4, h5, h6) {
393 margin-top: calc(var(--step) * 1.1);
394 }
395
396 h1 {
397 font-size: 1.62rem;
398 margin-top: calc(var(--step) * 2);
399 }
400 h2 {
401 font-size: 1.4rem;
402 margin-top: calc(var(--step) * 1.7);
403 }
404 h3 {
405 font-size: 1.18rem;
406 margin-top: calc(var(--step) * 1.35);
407 }
408 h4,
409 h5,
410 h6 {
411 font-size: 1rem;
412 font-family: var(--mono);
413 font-weight: 600;
414 letter-spacing: 0.02em;
415 margin-top: calc(var(--step) * 1.1);
416 }
417
418 .hanchor {
419 position: absolute;
420 right: calc(100% + 0.9rem);
421 top: 0.06em;
422 font-family: var(--mono);
423 font-size: 0.78em;
424 font-weight: 400;
425 color: var(--accent);
426 opacity: 0.4;
427 text-decoration: none;
428 transition: opacity 0.15s ease;
429
430 &:hover,
431 &:focus-visible {
432 opacity: 1;
433 }
434 }
435
436 @media (max-width: $wide) {
437 .hanchor {
438 position: static;
439 margin-right: 0.4em;
440 font-size: 0.7em;
441 vertical-align: 0.1em;
442 }
443 }
444
445 ul,
446 ol {
447 padding-left: 1.35rem;
448 }
449
450 li + li {
451 margin-top: 0.35rem;
452 }
453
454 li > ul,
455 li > ol {
456 margin-top: 0.35rem;
457 }
458
459 ul {
460 list-style: none;
461 padding-left: 1.15rem;
462 }
463
464 ul > li {
465 position: relative;
466
467 &::before {
468 content: "";
469 position: absolute;
470 left: -1.05rem;
471 top: 0.66em;
472 width: 0.38rem;
473 height: 1px;
474 background: var(--accent);
475 opacity: 0.75;
476 }
477 }
478
479 ul ul > li::before {
480 background: var(--rule-strong);
481 }
482
483 ol {
484 list-style: none;
485 counter-reset: ol;
486 padding-left: 1.85rem;
487 }
488
489 ol > li {
490 counter-increment: ol;
491 position: relative;
492
493 &::before {
494 content: counter(ol) ".";
495 position: absolute;
496 right: calc(100% + 0.5rem);
497 top: 0.06em;
498 font-family: var(--mono);
499 font-size: 0.75em;
500 color: var(--ink-faint);
501 font-variant-numeric: tabular-nums;
502 }
503 }
504
505 dt {
506 font-weight: 600;
507 }
508
509 dd {
510 margin-left: 1.35rem;
511 }
512
513 strong,
514 b {
515 font-weight: 700;
516 }
517
518 hr {
519 border: 0;
520 height: 1px;
521 background: linear-gradient(
522 to right,
523 var(--rule-strong),
524 var(--rule) 45%,
525 transparent
526 );
527 margin-block: calc(var(--step) * 1.6);
528 }
529
530 sup,
531 sub {
532 line-height: 0;
533 font-size: 0.7em;
534 }
535}
536
537a {
538 color: inherit;
539 text-decoration: underline;
540 text-decoration-color: var(--accent);
541 text-decoration-thickness: 1px;
542 text-underline-offset: 0.17em;
543 transition:
544 color 0.15s ease,
545 text-decoration-color 0.15s ease;
546
547 &:hover {
548 color: var(--accent);
549 text-decoration-thickness: 2px;
550 }
551}
552
553// --- Blockquote --------------------------------------------------------
554
555blockquote {
556 padding-left: 1.15rem;
557 border-left: 2px solid var(--rule-strong);
558 color: var(--ink-muted);
559
560 > * + * {
561 margin-top: 0.7rem;
562 }
563}
564
565// --- Code ----------------------------------------------------------------
566
567code,
568kbd,
569samp {
570 font-family: var(--mono);
571 font-variant-ligatures: none;
572 font-feature-settings:
573 "liga" 0,
574 "calt" 0;
575}
576
577:not(pre) > code {
578 font-size: 0.82em;
579 background: var(--paper-raised);
580 border: 1px solid var(--rule);
581 border-radius: 3px;
582 padding: 0.08em 0.26em;
583 color: var(--ink);
584 white-space: break-spaces;
585 word-break: break-word;
586}
587
588a :not(pre) > code,
589:not(pre) > code:has(a) {
590 color: inherit;
591}
592
593kbd {
594 font-size: 0.78em;
595 background: var(--paper);
596 border: 1px solid var(--rule-strong);
597 border-bottom-width: 2px;
598 border-radius: 3px;
599 padding: 0.08em 0.36em;
600 color: var(--ink);
601 white-space: nowrap;
602}
603
604// Hugo/Chroma wraps a fenced code block in <div class="highlight"><pre
605// class="chroma"><code class="language-x" data-lang="x">. The language tag
606// is read off that data-lang attribute and floats over the block like a
607// fieldset legend.
608.highlight {
609 position: relative;
610
611 code[data-lang]::before {
612 @extend %chrome;
613 content: attr(data-lang);
614 position: absolute;
615 z-index: 1;
616 top: 0;
617 right: 0.85rem;
618 transform: translateY(-50%);
619 padding-inline: 0.4rem;
620 background: var(--paper);
621 font-size: 0.6rem;
622 line-height: 1;
623 color: var(--ink-faint);
624 pointer-events: none;
625 }
626}
627
628pre {
629 font-family: var(--mono);
630 font-size: 0.78rem;
631 line-height: 1.55;
632 background: var(--paper-raised);
633 border: 1px solid var(--rule);
634 border-left: 2px solid var(--rule-strong);
635 border-radius: 3px;
636 padding: 0.85rem 1rem;
637 overflow-x: auto;
638 tab-size: 4;
639 color: var(--ink);
640
641 code {
642 background: none;
643 border: 0;
644 padding: 0;
645 font-size: inherit;
646 color: inherit;
647 }
648}
649
650// --- Collapsible blocks --------------------------------------------------
651
652.prose details {
653 border: 1px solid var(--rule);
654 border-left: 2px solid var(--rule-strong);
655 border-radius: 3px;
656 padding: 0 0.9rem;
657
658 > summary {
659 font-family: var(--mono);
660 font-size: 0.78rem;
661 color: var(--ink);
662 padding: 0.6rem 0;
663 cursor: pointer;
664 list-style: none;
665 display: flex;
666 align-items: baseline;
667 gap: 0.5rem;
668
669 &::-webkit-details-marker {
670 display: none;
671 }
672
673 &::before {
674 content: "";
675 width: 0;
676 height: 0;
677 border-left: 0.5rem solid var(--accent);
678 border-top: 0.29rem solid transparent;
679 border-bottom: 0.29rem solid transparent;
680 align-self: center;
681 transition: transform 0.15s ease;
682 display: inline-block;
683 }
684
685 &:hover {
686 color: var(--accent);
687 }
688 }
689
690 &[open] {
691 padding-bottom: 0.9rem;
692
693 > summary {
694 margin-bottom: 0.9rem;
695 border-bottom: 1px solid var(--rule);
696 color: var(--accent);
697
698 &::before {
699 transform: rotate(90deg);
700 }
701 }
702 }
703
704 .highlight {
705 margin-inline: -0.9rem;
706 }
707
708 pre {
709 border-inline: 0;
710 border-radius: 0;
711 }
712}
713
714@media (prefers-reduced-motion: reduce) {
715 .prose details > summary::before {
716 transition: none;
717 }
718}
719
720// --- Tables ----------------------------------------------------------------
721
722.table-scroll {
723 overflow-x: auto;
724 max-width: 100%;
725}
726
727table {
728 border-collapse: collapse;
729 width: 100%;
730 font-size: 0.85rem;
731 font-variant-numeric: tabular-nums;
732
733 th,
734 td {
735 padding: 0.45rem 0.7rem;
736 text-align: left;
737 border: 0;
738 border-bottom: 1px solid var(--rule);
739 vertical-align: top;
740 }
741
742 th:first-child,
743 td:first-child {
744 padding-left: 0;
745 }
746
747 th:last-child,
748 td:last-child {
749 padding-right: 0;
750 }
751
752 th {
753 @extend %chrome;
754 color: var(--ink-muted);
755 border-bottom: 1px solid var(--rule-strong);
756 letter-spacing: 0.055em;
757 line-height: 1.35;
758 }
759
760 tbody tr:last-child td {
761 border-bottom: 0;
762 }
763
764 tbody tr:hover td {
765 background: var(--accent-wash);
766 }
767}
768
769// --- Figures and media -----------------------------------------------------
770
771img,
772video,
773canvas,
774svg {
775 max-width: 100%;
776 height: auto;
777}
778
779.prose img,
780.prose video {
781 display: block;
782 max-width: min(var(--image-width, 100%), 100%);
783 margin-inline: auto;
784 border: 1px solid var(--rule);
785 border-radius: 3px;
786 background: var(--image-mat);
787}
788
789.prose a img {
790 border-color: var(--rule-strong);
791}
792
793iframe,
794embed,
795object {
796 max-width: 100%;
797 border: 0;
798}
799
800iframe[src*="youtube"],
801iframe[src*="vimeo"] {
802 display: block;
803 width: 100%;
804 aspect-ratio: 16 / 9;
805 height: auto;
806 margin-inline: auto;
807 border: 1px solid var(--rule);
808 border-radius: 3px;
809 background: var(--image-mat);
810}
811
812// Goldmark's own footnote list, styled plainly — the margin-sidenote
813// treatment from fzakaria.com needs a custom relocation pass this port
814// does not carry.
815.footnotes {
816 margin-top: calc(var(--step) * 2);
817 padding-top: var(--step);
818 border-top: 1px solid var(--rule);
819 font-size: 0.85rem;
820 color: var(--ink-muted);
821}
822
823// --- Alerts ------------------------------------------------------------
824
825$alerts: (
826 "note": (
827 #0b5fa5,
828 #6fb2e8,
829 ),
830 "tip": (
831 #1a7f37,
832 #79c98d,
833 ),
834 "important": (
835 #7145c4,
836 #b79ded,
837 ),
838 "warning": (
839 #8a6100,
840 #dfb051,
841 ),
842 "caution": (
843 #b1201d,
844 #ec8b7f,
845 ),
846);
847
848.alert {
849 --alert: var(--rule-strong);
850 border-left: 2px solid var(--alert);
851 background: var(--paper-raised);
852 padding: 0.85rem 1.1rem;
853 border-radius: 0 3px 3px 0;
854 color: var(--ink);
855
856 > * + * {
857 margin-top: 0.65rem;
858 }
859
860 p:first-child > strong:first-child {
861 @extend %chrome;
862 display: block;
863 color: var(--alert);
864 margin-bottom: 0.3rem;
865 }
866
867 :not(pre) > code {
868 background: transparent;
869 border-color: transparent;
870 padding: 0;
871 }
872
873 @each $name, $pair in $alerts {
874 &.alert-#{$name} {
875 --alert: #{list.nth($pair, 1)};
876
877 @media (prefers-color-scheme: dark) {
878 --alert: #{list.nth($pair, 2)};
879 }
880 }
881 }
882}
883
884blockquote.alert {
885 border-left-color: var(--alert);
886 color: var(--ink);
887}
888
889// --- Post header -----------------------------------------------------------
890
891.post-header {
892 margin-bottom: calc(var(--step) * 1.5);
893}
894
895.post-kicker {
896 @extend %chrome;
897 display: flex;
898 flex-wrap: wrap;
899 gap: 0 0.5rem;
900 margin-bottom: 0.7rem;
901
902 .sep {
903 color: var(--rule-strong);
904 }
905
906 a {
907 color: inherit;
908 text-decoration: none;
909 border-bottom: 1px solid var(--rule-strong);
910
911 &:hover {
912 color: var(--accent);
913 border-bottom-color: var(--accent);
914 }
915 }
916}
917
918.post-title {
919 font-size: clamp(1.85rem, 1.3rem + 2.1vw, 2.85rem);
920 font-weight: 600;
921 line-height: 1.08;
922 letter-spacing: -0.025em;
923 text-wrap: balance;
924}
925
926.post-rule {
927 width: 3.5rem;
928 height: 2px;
929 background: var(--accent);
930 margin: 1.1rem auto 0 0;
931 border: 0;
932}
933
934.post-body {
935 margin-top: calc(var(--step) * 1.5);
936}
937
938// --- Site footer -------------------------------------------------------
939
940.site-foot {
941 @extend %chrome;
942 margin-top: calc(var(--step) * 2.5);
943 padding-top: var(--step);
944 border-top: 1px solid var(--rule);
945 text-transform: none;
946 letter-spacing: 0.02em;
947 font-size: 0.7rem;
948 line-height: 1.75;
949 color: var(--ink-faint);
950 display: flex;
951 flex-wrap: wrap;
952 justify-content: center;
953 text-align: center;
954 gap: 0 0.5rem;
955
956 a {
957 color: var(--ink-muted);
958 text-decoration: none;
959 border-bottom: 1px solid var(--rule);
960
961 &:hover {
962 color: var(--accent);
963 border-bottom-color: var(--accent);
964 }
965 }
966
967 .sep {
968 color: var(--rule-strong);
969 }
970
971 .rss {
972 display: inline-flex;
973 align-items: center;
974 gap: 0.3rem;
975 border-bottom: 0;
976
977 svg {
978 width: 0.85em;
979 height: 0.85em;
980 fill: currentColor;
981 }
982 }
983
Move language switcher to its own row in the footer 1ce0eef Romain Gautier 13h ago984 // Own row below everything else (license, RSS, store path), rather than
985 // competing for space inline with them.
Add French/Portuguese/Japanese support, block indexing until launch f8c7bc5 Romain Gautier 17h ago986 .lang-switch {
987 display: inline-flex;
Move language switcher to its own row in the footer 1ce0eef Romain Gautier 13h ago988 justify-content: center;
989 flex-basis: 100%;
Add French/Portuguese/Japanese support, block indexing until launch f8c7bc5 Romain Gautier 17h ago990 gap: 0.5rem;
991 }
992
993 .lang-switch__current {
994 color: var(--ink-muted);
995 }
996
Port fzakaria.com design to Hugo and add a homepage profile 1bdbd06 Romain Gautier 21h ago997 // Own row, so the path gets the whole measure rather than whatever is left
998 // over beside the RSS link.
999 .store-path-row {
1000 flex-basis: 100%;
1001 }
1002
1003 // A store path is a single 56-character token: broken across lines it is
1004 // unreadable and awkward to select. Hold it on one line and let it shrink
1005 // instead.
1006 .store-path {
1007 font-size: min(0.7rem, 2.3vw);
1008 letter-spacing: 0;
1009 white-space: nowrap;
1010 color: inherit;
1011 background: none;
1012 border: 0;
1013 padding: 0;
1014 }
1015}
1016
1017// --- Index ---------------------------------------------------------------
1018
1019.page-index {
1020 display: flow-root;
1021}
1022
1023// Bio card. Floats into the margin column beside the post list; below $wide
1024// there is no margin column to float into, so it drops below the list
1025// instead (further down).
1026.colophon {
1027 float: right;
1028 width: var(--margin-col);
1029 margin-right: calc((var(--gap) + var(--margin-col)) * -1);
1030 margin-bottom: var(--step);
1031 // Jumped to from the masthead; leave air above it on arrival.
1032 scroll-margin-top: var(--step);
1033 font-size: 0.8rem;
1034 line-height: 1.55;
1035 color: var(--ink-muted);
1036}
1037
1038.colophon__avatar {
1039 width: 100%;
1040 max-width: 8.5rem;
1041 border-radius: 3px;
1042 border: 1px solid var(--rule-strong);
1043 display: block;
1044 margin-bottom: 0.85rem;
1045 filter: grayscale(0.35);
1046 transition: filter 0.3s ease;
Add blurhash placeholder for the colophon avatar 78c3f9f Romain Gautier 13h ago1047 background-size: cover;
1048 background-position: center;
Port fzakaria.com design to Hugo and add a homepage profile 1bdbd06 Romain Gautier 21h ago1049
1050 &:hover {
1051 filter: grayscale(0);
1052 }
1053}
1054
1055.colophon__name {
1056 font-weight: 600;
1057 font-size: 0.95rem;
1058 line-height: 1.3;
1059 color: var(--ink);
1060 margin-bottom: 0.35rem;
1061
1062 &-last {
1063 font-weight: 500;
1064 color: var(--ink-faint);
1065 }
1066}
1067
1068.colophon__bio {
1069 margin-bottom: 1rem;
1070
1071 a {
1072 text-decoration-color: var(--rule-strong);
1073 }
1074}
1075
1076.colophon__links {
1077 list-style: none;
1078 margin: 0;
1079 padding: 0;
1080 border-top: 1px solid var(--rule);
1081
1082 // The key sits above its value rather than beside it: a two-column split
1083 // leaves too little room for an email address in monospace without
1084 // breaking it across lines.
1085 li {
1086 padding-block: 0.4rem;
1087 border-bottom: 1px solid var(--rule);
1088 }
1089
1090 .key {
1091 @extend %chrome;
1092 display: block;
1093 font-size: 0.6rem;
1094 margin-bottom: 0.05rem;
1095 }
1096
1097 .val {
1098 display: block;
1099 font-family: var(--mono);
1100 font-size: 0.68rem;
1101
1102 a {
1103 color: var(--ink-muted);
1104 text-decoration: none;
1105 border-bottom: 1px solid var(--rule-strong);
1106
1107 &:hover {
1108 color: var(--accent);
1109 border-bottom-color: var(--accent);
1110 }
1111 }
1112 }
1113}
1114
1115// Narrow: the float is dropped and the card moves below the article list.
1116@media (max-width: $wide) {
1117 .masthead__about {
1118 display: inline-block;
1119 }
1120
1121 .page-index {
1122 display: flex;
1123 flex-direction: column;
1124 }
1125
1126 .colophon {
1127 float: none;
1128 width: 100%;
1129 margin-right: 0;
1130 margin-bottom: 0;
1131 margin-top: calc(var(--step) * 2);
1132 padding-top: var(--step);
1133 border-top: 1px solid var(--rule);
1134 order: 3;
1135 display: grid;
1136 grid-template-columns: 8.5rem 1fr;
1137 gap: 0 1.25rem;
1138 align-items: start;
1139 }
1140
1141 .colophon__avatar {
1142 margin-bottom: 0;
1143 grid-row: span 2;
1144 }
1145
1146 .index-body {
1147 order: 2;
1148 }
1149
1150 @media (max-width: 30rem) {
1151 .colophon {
1152 grid-template-columns: 1fr;
1153 gap: 1rem;
1154 }
1155
1156 .colophon__avatar {
1157 max-width: 6.5rem;
1158 }
1159 }
1160}
1161
1162.index-lead {
1163 display: block;
1164 text-decoration: none;
1165 margin-bottom: calc(var(--step) * 2);
1166 padding-left: 1rem;
1167 border-left: 2px solid var(--accent);
1168 position: relative;
1169
1170 .label {
1171 @extend %chrome;
1172 color: var(--accent);
1173 display: block;
1174 margin-bottom: 0.4rem;
1175 }
1176
1177 .title {
1178 font-size: 1.5rem;
1179 font-weight: 600;
1180 line-height: 1.15;
1181 letter-spacing: -0.02em;
1182 text-wrap: balance;
1183 display: block;
1184 color: inherit;
1185 text-decoration: none;
1186
1187 &::after {
1188 content: "";
1189 position: absolute;
1190 inset: 0;
1191 }
1192 }
1193
1194 .excerpt a {
1195 position: relative;
1196 z-index: 1;
1197 }
1198
1199 .excerpt {
1200 display: block;
1201 color: var(--ink-muted);
1202 font-size: 0.92rem;
1203 margin-top: 0.5rem;
1204 }
1205
1206 .more {
1207 @extend %chrome;
1208 color: var(--accent);
1209 display: block;
1210 margin-top: 0.7rem;
1211 }
1212
1213 &:hover .title {
1214 color: var(--accent);
1215 }
1216}
1217
1218.year {
1219 margin-top: calc(var(--step) * 2);
1220 position: relative;
1221
1222 &:first-of-type {
1223 margin-top: 0;
1224 }
1225}
1226
1227.year__label {
1228 @extend %chrome;
1229 font-size: 0.78rem;
1230 color: var(--ink-faint);
1231 padding-bottom: 0.4rem;
1232 margin-bottom: 0.6rem;
1233 border-bottom: 1px solid var(--rule);
1234 display: flex;
1235 justify-content: space-between;
1236 align-items: baseline;
1237
1238 .count {
1239 font-size: 0.66rem;
1240 color: var(--rule-strong);
1241 }
1242}
1243
1244@media (min-width: $wide) {
1245 .year__label {
1246 position: absolute;
1247 right: calc(100% + 0.9rem);
1248 top: 0.1rem;
1249 width: calc(var(--gutter) - 0.9rem);
1250 display: block;
1251 border-bottom: 0;
1252 padding-bottom: 0;
1253 margin-bottom: 0;
1254 text-align: right;
1255 letter-spacing: 0.04em;
1256
1257 .count {
1258 display: none;
1259 }
1260 }
1261
1262 .year__list {
1263 border-top: 1px solid var(--rule);
1264 padding-top: 0.35rem;
1265 }
1266}
1267
1268.year__list {
1269 list-style: none;
1270 margin: 0;
1271 padding: 0;
1272}
1273
1274.entry {
1275 display: grid;
1276 grid-template-columns: 4.25rem 1fr;
1277 gap: 0 0.85rem;
1278 align-items: baseline;
1279 padding-block: 0.42rem;
1280
1281 @media (max-width: 34rem) {
1282 grid-template-columns: 1fr;
1283 gap: 0.12rem;
1284 padding-block: 0.5rem;
1285 }
1286
1287 + .entry {
1288 border-top: 1px solid var(--rule);
1289 }
1290
1291 .date {
1292 @extend %chrome;
1293 font-size: 0.68rem;
1294 white-space: nowrap;
1295 }
1296
1297 a {
1298 text-decoration: none;
1299 line-height: 1.35;
1300 color: var(--ink);
1301 text-decoration: underline;
1302 text-decoration-color: transparent;
1303 text-underline-offset: 0.17em;
1304 }
1305
1306 &:hover a {
1307 color: var(--accent);
1308 text-decoration-color: var(--accent);
1309 }
1310
1311 &:hover .date {
1312 color: var(--accent);
1313 }
1314}
1315
1316// --- Motion --------------------------------------------------------------
1317
1318@media (prefers-reduced-motion: no-preference) {
1319 @keyframes rise {
1320 from {
1321 opacity: 0;
1322 transform: translateY(0.5rem);
1323 }
1324 to {
1325 opacity: 1;
1326 transform: none;
1327 }
1328 }
1329
1330 .rise {
1331 animation: rise 0.5s cubic-bezier(0.22, 0.68, 0.35, 1) backwards;
1332 animation-delay: calc(var(--i, 0) * 60ms);
1333 }
1334
1335 @keyframes signal-sweep {
1336 to {
1337 stroke-dashoffset: 0;
1338 }
1339 }
1340
1341 .masthead__signal path {
1342 stroke-dasharray: 100;
1343 stroke-dashoffset: 100;
1344 animation: signal-sweep 1.1s cubic-bezier(0.32, 0.5, 0.3, 1) 0.18s forwards;
Smooth page transitions with htmx boost, morph, and preload ca8b900 Romain Gautier 18h ago1345 // Every page's signal is the same number of curve commands (32 bytes ->
1346 // fixed M + 30 C), just different coordinates - so the browser can
1347 // interpolate straight from one `d` to the next when htmx morphs it in
1348 // on navigation, instead of snapping. Only fires on a same-node update;
1349 // a fresh page load has no prior `d` to transition from.
1350 transition: d 0.7s cubic-bezier(0.32, 0.5, 0.3, 1);
1351 }
1352}
1353
1354// htmx's boosted navigation wraps every swap in document.startViewTransition
1355// (see hx-boost:inherited in baseof.html); browsers don't honor
1356// prefers-reduced-motion for that on their own, so without this the default
1357// cross-fade would still run for anyone who has asked for less motion.
1358@media (prefers-reduced-motion: reduce) {
1359 ::view-transition-group(*),
1360 ::view-transition-old(*),
1361 ::view-transition-new(*) {
1362 animation: none !important;
Port fzakaria.com design to Hugo and add a homepage profile 1bdbd06 Romain Gautier 21h ago1363 }
1364}
1365
1366// --- Print -----------------------------------------------------------------
1367
1368@media print {
1369 :root {
1370 --paper: #fff;
1371 --paper-raised: #fff;
1372 --ink: #000;
1373 --ink-muted: #333;
1374 --ink-faint: #555;
1375 --rule: #bbb;
1376 --rule-strong: #888;
1377 --accent: #000;
1378 --gutter: 0rem;
1379 --margin-col: 0rem;
1380 --gap: 0rem;
1381 }
1382
1383 body {
1384 background: #fff;
1385 }
1386
1387 .colophon,
1388 .skip-link {
1389 display: none;
1390 }
1391
1392 details {
1393 border: 0;
1394 padding: 0;
1395 }
1396
1397 details > summary::before {
1398 content: none;
1399 }
1400
1401 pre,
1402 blockquote,
1403 details,
1404 .alert {
1405 break-inside: avoid;
1406 }
1407
1408 a::after {
1409 content: " (" attr(href) ")";
1410 font-size: 0.75em;
1411 color: #555;
1412 }
1413
1414 a[href^="#"]::after {
1415 content: "";
1416 }
1417}
1418
1419// ---------------------------------------------------------------------------
1420// Rouge/Chroma syntax theme — "Ochre" (unchanged from fzakaria.com).
1421// ---------------------------------------------------------------------------
1422
1423:root {
1424 --syn-comment: #6f685b;
1425 --syn-keyword: #9a3d05;
1426 --syn-string: #3f6b32;
1427 --syn-number: #6b4fa8;
1428 --syn-func: #1c5570;
1429 --syn-type: #7a6114;
1430 --syn-var: #7c4a2d;
1431 --syn-punct: #6b6558;
1432 --syn-error: #b1201d;
1433 --syn-error-bg: #b1201d1a;
1434 --syn-ins-bg: #1a7f3714;
1435 --syn-del-bg: #b1201d14;
1436 --syn-ins-fg: #1a6d33;
1437 --syn-del-fg: #a3231f;
1438}
1439
1440@media (prefers-color-scheme: dark) {
1441 :root {
1442 --syn-comment: #8d867a;
1443 --syn-keyword: #e08a45;
1444 --syn-string: #a6c98d;
1445 --syn-number: #c3a2ec;
1446 --syn-func: #7fb6d6;
1447 --syn-type: #ddc477;
1448 --syn-var: #dda58c;
1449 --syn-punct: #8b857a;
1450 --syn-error: #ec8b7f;
1451 --syn-error-bg: #ec8b7f1f;
1452 --syn-ins-bg: #79c98d1a;
1453 --syn-del-bg: #ec8b7f1a;
1454 --syn-ins-fg: #8fd0a1;
1455 --syn-del-fg: #ef9a8f;
1456 }
1457}
1458
1459.highlight {
1460 .c, .ch, .cd, .cm, .cp, .cpf, .c1, .cs {
1461 color: var(--syn-comment);
1462 font-style: italic;
1463 }
1464
1465 .k, .kc, .kd, .kn, .kp, .kr, .kv {
1466 color: var(--syn-keyword);
1467 }
1468
1469 .kt, .nc, .nn, .no, .ne {
1470 color: var(--syn-type);
1471 }
1472
1473 .nf, .fm, .nb, .bp, .na, .nt {
1474 color: var(--syn-func);
1475 }
1476
1477 .nv, .vc, .vg, .vi, .vm, .nl, .py {
1478 color: var(--syn-var);
1479 }
1480
1481 .s, .sa, .sb, .sc, .dl, .sd, .s2, .se, .sh, .si, .sx, .s1, .ss, .sr {
1482 color: var(--syn-string);
1483 }
1484
1485 .m, .mb, .mf, .mh, .mi, .mo, .il {
1486 color: var(--syn-number);
1487 }
1488
1489 .o, .ow, .p, .pi, .ni {
1490 color: var(--syn-punct);
1491 }
1492
1493 .err {
1494 color: var(--syn-error);
1495 background-color: var(--syn-error-bg);
1496 border-radius: 2px;
1497 }
1498
1499 .gd {
1500 color: var(--syn-del-fg);
1501 background-color: var(--syn-del-bg);
1502 }
1503
1504 .gi {
1505 color: var(--syn-ins-fg);
1506 background-color: var(--syn-ins-bg);
1507 }
1508
1509 .gh, .gu {
1510 color: var(--syn-func);
1511 font-weight: 600;
1512 }
1513
1514 .gp {
1515 color: var(--syn-comment);
1516 user-select: none;
1517 }
1518
1519 .go {
1520 color: var(--ink-muted);
1521 }
1522
1523 .ge {
1524 font-style: italic;
1525 }
1526
1527 .gs {
1528 font-weight: 700;
1529 }
1530
1531 .gr, .gt {
1532 color: var(--syn-error);
1533 }
1534
1535 .w {
1536 color: var(--rule-strong);
1537 }
1538
1539 .lineno, .gl {
1540 color: var(--ink-faint);
1541 opacity: 0.6;
1542 user-select: none;
1543 padding-right: 0.85rem;
1544 }
1545}