A calm place to write long-form, and publish it to the open social web. skypress.blog/
0

Configure Feed

Select the types of activity you want to include in your feed.

1--- 2/** 3 * Static loading scene for the `client:only` Studio + Dashboard islands. 4 * 5 * Those islands are browser-only (auth + editor) and can't render server-side, 6 * so until their JS bundle loads Astro shows this `slot="fallback"` markup. 7 * Rather than a bare, left-aligned "Loading…", render the durable page chrome 8 * a logo-only header (matching AppBar's `status === 'loading'` state, so the 9 * swap to the live bar is seamless) plus a content skeleton so the page has 10 * shape immediately and hydration doesn't reflow into an empty viewport. 11 * 12 * The fallback is plain server-rendered markup (it is NOT inside the island's 13 * DOM), so this component's scoped styles reach it normally. The header reuses 14 * the global `.app-bar` classes (app-bar.css is imported by both page shells); 15 * only the static logo is shown nav/identity depend on auth state we don't 16 * have yet. 17 */ 18import { skypressMark } from '../lib/brand/skypress-mark'; 19 20interface Props { 21 /** Which island is loading; picks the skeleton shape + content width. */ 22 variant: 'dashboard' | 'editor'; 23} 24const { variant } = Astro.props; 25--- 26 27<div class="loading-scene" aria-busy="true"> 28 <header class="app-bar"> 29 <a class="app-bar__home" href="/" aria-label="SkyPress home"> 30 <span class="app-bar__mark" set:html={skypressMark( 24 )} /> 31 <span class="app-bar__word">SkyPress</span> 32 </a> 33 </header> 34 35 { 36 variant === 'dashboard' ? ( 37 <div class="loading-scene__body loading-scene__body--dash"> 38 <div class="loading-scene__head"> 39 <span class="sk sk-heading" /> 40 <span class="sk sk-button" /> 41 </div> 42 <ul class="loading-scene__rows"> 43 {[ 0, 1, 2 ].map( () => ( 44 <li class="loading-scene__row"> 45 <span class="sk sk-logo" /> 46 <span class="loading-scene__rowtext"> 47 <span class="sk sk-line sk-line--name" /> 48 <span class="sk sk-line sk-line--slug" /> 49 </span> 50 </li> 51 ) )} 52 </ul> 53 </div> 54 ) : ( 55 <div class="loading-scene__body loading-scene__body--editor"> 56 <span class="sk sk-mode" /> 57 <span class="sk sk-headline" /> 58 <span class="sk sk-lede" /> 59 <span class="sk sk-surface" /> 60 </div> 61 ) 62 } 63 64 <p class="loading-scene__sr">Loading</p> 65</div> 66 67<style> 68 /* Skeleton block: a tinted bar with a sweeping shimmer highlight. */ 69 .sk { 70 display: block; 71 border-radius: var(--radius-sm); 72 background: var(--panel); 73 position: relative; 74 overflow: hidden; 75 } 76 .sk::after { 77 content: ''; 78 position: absolute; 79 inset: 0; 80 transform: translateX(-100%); 81 background: linear-gradient( 82 90deg, 83 transparent, 84 color-mix(in srgb, var(--paper-raised) 70%, transparent), 85 transparent 86 ); 87 animation: loading-shimmer 1.5s ease-in-out infinite; 88 } 89 @keyframes loading-shimmer { 90 100% { 91 transform: translateX(100%); 92 } 93 } 94 @media (prefers-reduced-motion: reduce) { 95 .sk::after { 96 animation: none; 97 } 98 } 99 100 /* Visually-hidden live label the skeleton conveys "loading" visually, but 101 screen readers get an explicit announcement (paired with aria-busy). */ 102 .loading-scene__sr { 103 position: absolute; 104 width: 1px; 105 height: 1px; 106 margin: -1px; 107 padding: 0; 108 overflow: hidden; 109 clip: rect(0, 0, 0, 0); 110 white-space: nowrap; 111 border: 0; 112 } 113 114 /* Content column mirrors the loaded layouts so the swap doesn't shift. */ 115 .loading-scene__body { 116 max-width: 48rem; 117 margin: 0 auto; 118 padding: 0 1.25rem 5rem; 119 } 120 .loading-scene__body--editor { 121 max-width: var(--studio-measure, 60rem); 122 } 123 124 /* Dashboard skeleton mirrors `.dash__section-head` + a few `.dash__pub` rows. */ 125 .loading-scene__head { 126 display: flex; 127 align-items: center; 128 justify-content: space-between; 129 gap: 1rem; 130 margin: 1rem 0 1.5rem; 131 } 132 .sk-heading { 133 width: clamp(10rem, 40vw, 16rem); 134 height: clamp(1.6rem, 4vw, 2.2rem); 135 } 136 .sk-button { 137 width: 9.5rem; 138 height: 2.2rem; 139 flex: none; 140 } 141 .loading-scene__rows { 142 list-style: none; 143 margin: 0; 144 padding: 0; 145 } 146 .loading-scene__row { 147 display: flex; 148 align-items: center; 149 gap: 1rem; 150 padding: 0.9rem 0; 151 border-top: 1px solid var(--line); 152 } 153 .sk-logo { 154 width: 48px; 155 height: 48px; 156 border-radius: 10px; 157 flex: none; 158 } 159 .loading-scene__rowtext { 160 display: flex; 161 flex-direction: column; 162 gap: 0.5rem; 163 flex: 1; 164 min-width: 0; 165 } 166 .sk-line { 167 height: 0.85rem; 168 } 169 .sk-line--name { 170 width: 45%; 171 height: 1rem; 172 } 173 .sk-line--slug { 174 width: 22%; 175 height: 0.7rem; 176 } 177 178 /* Editor skeleton mode line, title, lede, then the framed writing surface. */ 179 .sk-mode { 180 width: 11rem; 181 height: 0.9rem; 182 margin: 0.5rem 0 1.25rem; 183 } 184 .sk-headline { 185 width: 70%; 186 height: clamp(1.9rem, 4vw, 2.6rem); 187 margin-bottom: 0.75rem; 188 } 189 .sk-lede { 190 width: 48%; 191 height: 1.2rem; 192 } 193 .sk-surface { 194 height: 60vh; 195 min-height: 18rem; 196 margin-top: 1.5rem; 197 border-radius: var(--radius); 198 border: 1px solid var(--line-strong); 199 } 200</style>