repos
/ orchard main

orchard

mirror

Every site I host, in one repo, along with the Cloudflare Tunnel and Caddy that front them. It's all Go, Vite, and SQLite, and it runs on a desktop at home with nothing listening on an inbound port.

blogbuncaddycloudflare-tunneldockergogolanghomelabhtml-templatemonorepoself-hostedseosqlitestatic-sitetypstuptime-monitoringviteweb-analytics

4.6 KB · 138 lines · CSS Raw History
  1:root {
  2  --color-primary: #20232e;
  3  --color-blue: #0e3ff4;
  4  --color-purple: #842bff;
  5  --radius-2: 3px;
  6  --transition-250: 250ms;
  7  --animation-1500: 1500ms;
  8}
  9
 10body {
 11  font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Helvetica, Arial,
 12    sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
 13  color: white;
 14  background-color: var(--color-primary);
 15  min-height: 100vh;
 16  width: 100%;
 17  padding: 0;
 18  margin: 0;
 19  overflow-x: hidden;
 20  text-shadow: rgba(0, 0, 0, 0.01) 0 0 1px;
 21  text-rendering: optimizeLegibility;
 22  user-select: none;
 23}
 24
 25/* The custom cursor replaces the real one, so hiding the real one is only safe
 26   where the replacement exists. It is display:none below 1024px (see
 27   components/cursor.css), and it is drawn by a deferred module, so an
 28   unconditional rule left a real-pointer visitor with no pointer at all between
 29   first paint and hydration, and permanently in any window narrower than
 30   1024px: a half-screen desktop window, a 1366px laptop at 125% scaling, an
 31   iPad with a trackpad. It also discarded every OS pointer accessibility
 32   setting for anyone relying on one. */
 33@media (min-width: 1024px) and (pointer: fine) {
 34  html[data-js] body,
 35  html[data-js] a,
 36  html[data-js] button {
 37    cursor: none;
 38  }
 39}
 40
 41/* The Next.js version cross-faded two overlapping page trees with
 42   react-transition-group, which is why the old rule was position: absolute:
 43   during a route change both the outgoing and incoming page were mounted at
 44   once and had to occupy the same space.
 45   
 46   Real navigation only ever has one document, so this is a plain fade-in on
 47   load in normal flow. Same effect to the eye, and it fixes a side effect of
 48   the old approach: an absolutely positioned wrapper contributes nothing to
 49   body height, so the page scrolled only because overflow happened to extend
 50   the scroll area.
 51
 52   position: relative is required here. The full bleed white panels on about,
 53   code and art are position: absolute with top/bottom 0, so they size to the
 54   nearest positioned ancestor. With nothing positioned between them and the
 55   root they fall back to the initial containing block, which is the viewport,
 56   so the white stops at 100vh and the dark body colour shows through on
 57   scroll. */
 58.transition {
 59  position: relative;
 60  min-height: 100vh;
 61  width: 100%;
 62  animation: pageFadeIn var(--transition-250) ease-in-out both;
 63}
 64
 65@keyframes pageFadeIn {
 66  from {
 67    opacity: 0;
 68  }
 69  to {
 70    opacity: 1;
 71  }
 72}
 73
 74@keyframes pageFadeOut {
 75  from {
 76    opacity: 1;
 77  }
 78  to {
 79    opacity: 0;
 80  }
 81}
 82
 83/* Page to page: fade out to white, then fade in.
 84
 85   The opening curtain now plays once per visit rather than on every navigation
 86   (see loader.css), so everything after it needs a quieter transition of its
 87   own. This is a cross document view transition, which means the browser
 88   drives it: it holds the outgoing page on screen until the incoming one is
 89   ready to paint. That is the reason to use it over the obvious hand rolled
 90   version, where JS fades to white, waits a fixed delay and then sets
 91   location. That version bets on a fixed guess about latency and loses the bet
 92   in both directions, either cutting the fade short or parking the visitor on
 93   a blank white screen. Here there is no guess and no added delay.
 94
 95   Where it is unsupported nothing below applies, navigation is instant, and
 96   the pageFadeIn above is still the whole effect. That is an acceptable
 97   floor: a gentle fade in with no fade out. */
 98@view-transition {
 99  navigation: auto;
100}
101
102/* The transition overlay is painted above the real document for its whole
103   duration, so the white has to live here. Anything set on the page
104   underneath would be covered up by the very thing we want it to show
105   through. */
106::view-transition {
107  background-color: white;
108}
109
110/* The default UA style cross fades the two snapshots with plus-lighter, which
111   is correct for an overlapping fade and wrong for a sequential one. These
112   never overlap: the old is gone before the new starts. */
113::view-transition-old(root) {
114  mix-blend-mode: normal;
115  animation: pageFadeOut calc(var(--transition-250) / 2) ease-in both;
116}
117
118::view-transition-new(root) {
119  mix-blend-mode: normal;
120  animation: pageFadeIn calc(var(--transition-250) / 2)
121    calc(var(--transition-250) / 2) ease-out both;
122}
123
124@media (prefers-reduced-motion: reduce) {
125  .transition {
126    animation: none;
127  }
128}
129
130/* Opting out at the source rather than zeroing the animations. Same result to
131   look at, but the browser skips capturing the snapshots at all instead of
132   capturing them and then playing nothing. */
133@media (prefers-reduced-motion: reduce) {
134  @view-transition {
135    navigation: none;
136  }
137}