skillet-shared 0.3.0 → 0.4.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.
@@ -0,0 +1,19 @@
1
+ ---
2
+ import type { SiteInfo } from '../dist/site-info.js'
3
+
4
+ interface Props {
5
+ site: SiteInfo
6
+ }
7
+ const { site } = Astro.props
8
+ ---
9
+
10
+ <footer class="siteFoot">
11
+ <div class="footIn">
12
+ <nav aria-label="Site">
13
+ {site.footer.map(([href, label]) => (
14
+ <a href={href}>{label}</a>
15
+ ))}
16
+ <a href="#top">Back to top ↑</a>
17
+ </nav>
18
+ </div>
19
+ </footer>
@@ -0,0 +1,28 @@
1
+ ---
2
+ interface Props {
3
+ hint: string
4
+ }
5
+ const { hint: SIGNED_IN_HINT } = Astro.props
6
+ ---
7
+ <script is:inline define:vars={{ SIGNED_IN_HINT }}>
8
+ try {
9
+ if (localStorage.getItem(SIGNED_IN_HINT) === '1') {
10
+ const root = document.documentElement
11
+ root.dataset.home = 'pending'
12
+ for (const host of [
13
+ 'identitytoolkit.googleapis.com',
14
+ 'securetoken.googleapis.com',
15
+ 'firestore.googleapis.com',
16
+ ]) {
17
+ const link = document.createElement('link')
18
+ link.rel = 'preconnect'
19
+ link.href = `https://${host}`
20
+ link.crossOrigin = 'anonymous'
21
+ document.head.append(link)
22
+ }
23
+ setTimeout(() => {
24
+ delete root.dataset.home
25
+ }, 15000)
26
+ }
27
+ } catch {}
28
+ </script>
@@ -0,0 +1,77 @@
1
+ ---
2
+ import type { SiteInfo } from '../dist/site-info.js'
3
+
4
+ interface Props {
5
+ site: SiteInfo
6
+ account?: boolean
7
+ current?: string
8
+ }
9
+ const { site, account = true, current } = Astro.props
10
+ ---
11
+
12
+ <header class="siteBar">
13
+ <div class="barIn">
14
+ <a class="barBrand" href={site.home}>
15
+ <img src={site.icon} width="24" height="24" alt="" />
16
+ <span>{site.name}</span>
17
+ </a>
18
+ <nav class="barNav" aria-label="Site">
19
+ {site.nav.map(([href, label]) => (
20
+ <a href={href} aria-current={href === current ? 'page' : undefined}>
21
+ {label}
22
+ </a>
23
+ ))}
24
+ </nav>
25
+ <div class="barEnd">
26
+ {account && (
27
+ <Fragment>
28
+ {/* The question beside the ask, where the reader meets it. */}
29
+ <button class="why" id="why" type="button">
30
+ Why sign in?
31
+ </button>
32
+ <button
33
+ class="btn slim"
34
+ id="signIn"
35
+ type="button"
36
+ aria-label="Sign in"
37
+ >
38
+ <svg
39
+ class="ico"
40
+ viewBox="0 0 24 24"
41
+ fill="none"
42
+ stroke="currentColor"
43
+ stroke-width="1.7"
44
+ stroke-linecap="round"
45
+ stroke-linejoin="round"
46
+ aria-hidden="true"
47
+ >
48
+ <circle cx="12" cy="8" r="3.6" />
49
+ <path d="M4.8 20a7.2 7.2 0 0 1 14.4 0" />
50
+ </svg>
51
+ <span class="barLabel">Sign in</span>
52
+ </button>
53
+ <div class="acct" id="acct" hidden>
54
+ <button
55
+ class="acctBtn"
56
+ id="acctBtn"
57
+ type="button"
58
+ aria-controls="acctMenu"
59
+ aria-expanded="false"
60
+ >
61
+ <span class="avatar" id="avatar" />
62
+ </button>
63
+ <div class="acctMenu" id="acctMenu" hidden>
64
+ <p class="acctName" id="acctName" />
65
+ <button class="acctOut" id="signOut" type="button">
66
+ Sign out
67
+ </button>
68
+ </div>
69
+ </div>
70
+ </Fragment>
71
+ )}
72
+ <a class="btn primary slim" href={site.app}>
73
+ Open the app →
74
+ </a>
75
+ </div>
76
+ </div>
77
+ </header>
@@ -0,0 +1,32 @@
1
+ ---
2
+ // How a link to this page unfurls when somebody shares it. The values differ
3
+ // per product and per page; the tags do not, so the whole file is shared.
4
+ //
5
+ // The description here is the one a reader meets in a chat window rather than
6
+ // in a search result, so a page may hand in a shorter one than it gives
7
+ // `<meta name="description">`.
8
+ interface Props {
9
+ siteName: string
10
+ url: string
11
+ title: string
12
+ description: string
13
+ image: string
14
+ imageWidth: number
15
+ imageHeight: number
16
+ }
17
+ const { siteName, url, title, description, image, imageWidth, imageHeight } =
18
+ Astro.props
19
+ ---
20
+
21
+ <meta property="og:type" content="website" />
22
+ <meta property="og:site_name" content={siteName} />
23
+ <meta property="og:url" content={url} />
24
+ <meta property="og:title" content={title} />
25
+ <meta property="og:description" content={description} />
26
+ <meta property="og:image" content={image} />
27
+ <meta property="og:image:width" content={imageWidth} />
28
+ <meta property="og:image:height" content={imageHeight} />
29
+ <meta name="twitter:card" content="summary_large_image" />
30
+ <meta name="twitter:title" content={title} />
31
+ <meta name="twitter:description" content={description} />
32
+ <meta name="twitter:image" content={image} />
package/css/footer.css ADDED
@@ -0,0 +1,28 @@
1
+ /* The band at the bottom of every site page, landing, guide and privacy alike. */
2
+ .siteFoot {
3
+ border-top: 1px solid var(--border-faint);
4
+ background: var(--surface);
5
+ }
6
+ .footIn {
7
+ max-width: 63rem;
8
+ margin: 0 auto;
9
+ padding: 1.75rem max(1.15rem, env(safe-area-inset-left))
10
+ calc(2rem + env(safe-area-inset-bottom));
11
+ font-size: 0.86rem;
12
+ }
13
+ .footIn nav {
14
+ display: flex;
15
+ flex-wrap: wrap;
16
+ gap: 0.5rem 1.35rem;
17
+ margin-bottom: 0.9rem;
18
+ }
19
+ .footIn a {
20
+ color: var(--fg3);
21
+ text-decoration: none;
22
+ }
23
+ .footIn a:hover {
24
+ color: var(--fg);
25
+ }
26
+ .footIn small {
27
+ color: var(--fg4);
28
+ }
package/css/site.css ADDED
@@ -0,0 +1,717 @@
1
+ /* The static site's shared components, identical in videoskillet and bender:
2
+ the page base, buttons, the site bar, the account menu, the why-sign-in card,
3
+ the privacy, about and blog pages, the signed-in home and its skeleton.
4
+ Colours come from tokens.css and each product's --accent and --accent-ink. */
5
+ :root {
6
+ --measure: 46rem;
7
+ }
8
+ body {
9
+ margin: 0;
10
+ background: var(--bg);
11
+ color: var(--fg);
12
+ line-height: 1.65;
13
+ font-family: var(--font);
14
+ font-size: 16px;
15
+ -webkit-text-size-adjust: 100%;
16
+ }
17
+ a {
18
+ color: var(--accent);
19
+ }
20
+ /* `display` on a class beats the browser's own `[hidden]` rule, and the bar
21
+ hides an inline-flex button and a flex box by setting the attribute. */
22
+ [hidden] {
23
+ display: none !important;
24
+ }
25
+ .wrap {
26
+ max-width: var(--measure);
27
+ margin: 0 auto;
28
+ /* Inline only: `padding` here is a shorthand, and it used to zero the
29
+ `padding-block` a page's own sheet had set. The inset keeps the text clear
30
+ of a cutout, the way .barIn and .footIn already do — it reads as zero on
31
+ every screen that has no cutout. */
32
+ padding-inline: max(1.25rem, env(safe-area-inset-left));
33
+ }
34
+ .wide {
35
+ max-width: 72rem;
36
+ }
37
+
38
+ section {
39
+ padding: 3.5rem 0;
40
+ }
41
+ .head {
42
+ font-size: 1.35rem;
43
+ font-weight: 600;
44
+ color: var(--fg);
45
+ margin: 0 0 0.5rem;
46
+ }
47
+ .sub {
48
+ color: var(--fg4);
49
+ font-size: 0.95rem;
50
+ margin: 0 0 1.1rem;
51
+ max-width: var(--measure);
52
+ }
53
+
54
+ .btn {
55
+ display: inline-block;
56
+ padding: 0.72rem 1.35rem;
57
+ border-radius: 0.5rem;
58
+ border: 1px solid var(--border-faint);
59
+ background: var(--surface);
60
+ color: var(--fg);
61
+ text-decoration: none;
62
+ font-weight: 600;
63
+ font-size: 1rem;
64
+ }
65
+ .btn:hover {
66
+ border-color: var(--fg4);
67
+ }
68
+ .btn.primary {
69
+ background: var(--accent);
70
+ border-color: var(--accent);
71
+ color: var(--accent-ink);
72
+ }
73
+ .btn.primary:hover {
74
+ filter: brightness(1.08);
75
+ }
76
+
77
+ /* ── the site bar ──────────────────────────────────────────────────────────
78
+ A slim row over both states of `/`: the site's three destinations on the
79
+ left, the account and the app on the right. The guide's own bar is in
80
+ guide.css and keeps its shape; the two share the palette and nothing else,
81
+ because the middle of the guide's bar is the pages of one group and the
82
+ middle of this one is the site. */
83
+ .siteBar {
84
+ border-bottom: 1px solid var(--border-faint);
85
+ background: var(--bg);
86
+ }
87
+ .barIn {
88
+ display: flex;
89
+ align-items: center;
90
+ gap: 0 1.25rem;
91
+ height: 56px;
92
+ max-width: 72rem;
93
+ margin: 0 auto;
94
+ padding: 0 max(1.15rem, env(safe-area-inset-left));
95
+ }
96
+ .barBrand {
97
+ display: flex;
98
+ align-items: center;
99
+ gap: 0.5rem;
100
+ font-weight: 700;
101
+ font-size: 15px;
102
+ color: var(--fg);
103
+ text-decoration: none;
104
+ white-space: nowrap;
105
+ }
106
+ .barNav {
107
+ display: flex;
108
+ gap: 1.15rem;
109
+ margin-right: auto;
110
+ }
111
+ .barNav a {
112
+ color: var(--fg3);
113
+ text-decoration: none;
114
+ font-size: 14px;
115
+ white-space: nowrap;
116
+ }
117
+ .barNav a:hover {
118
+ color: var(--fg);
119
+ }
120
+ .barEnd {
121
+ display: flex;
122
+ align-items: center;
123
+ gap: 0.6rem;
124
+ margin-left: auto;
125
+ }
126
+ /* The bar's own size for `.btn`, which the hero sets at full size. */
127
+ .btn.slim {
128
+ display: inline-flex;
129
+ align-items: center;
130
+ gap: 0.4rem;
131
+ padding: 0.4rem 0.85rem;
132
+ font-size: 0.875rem;
133
+ line-height: 1.25;
134
+ }
135
+ .ico {
136
+ width: 1.05rem;
137
+ height: 1.05rem;
138
+ flex: none;
139
+ }
140
+ /* On a phone the bar keeps the brand and the two buttons it exists for. The
141
+ destinations are in the footer and the rail, "Why sign in?" is asked again
142
+ under the hero, and the sign-in button keeps its icon and its label for
143
+ screen readers. The narrowest phones keep the logo without the name. */
144
+ .barEnd .btn {
145
+ white-space: nowrap;
146
+ justify-content: center;
147
+ min-width: 8.5rem;
148
+ }
149
+ @media (max-width: 44rem) {
150
+ .barIn {
151
+ gap: 0 0.75rem;
152
+ }
153
+ .barNav,
154
+ #why,
155
+ .barLabel {
156
+ display: none;
157
+ }
158
+ .barBrand {
159
+ margin-right: auto;
160
+ }
161
+ .barEnd {
162
+ gap: 0.5rem;
163
+ }
164
+ .barEnd .btn {
165
+ min-width: 0;
166
+ }
167
+ #signIn {
168
+ padding-inline: 0.55rem;
169
+ }
170
+ }
171
+ @media (max-width: 23rem) {
172
+ .barBrand span {
173
+ display: none;
174
+ }
175
+ }
176
+
177
+ .acct {
178
+ position: relative;
179
+ }
180
+ .acctBtn {
181
+ display: block;
182
+ padding: 0;
183
+ border: 0;
184
+ background: none;
185
+ cursor: pointer;
186
+ }
187
+ .avatar {
188
+ display: grid;
189
+ place-items: center;
190
+ width: 28px;
191
+ height: 28px;
192
+ border-radius: 50%;
193
+ overflow: hidden;
194
+ border: 1px solid var(--border-faint);
195
+ background: var(--surface);
196
+ color: var(--fg2);
197
+ font-size: 0.8rem;
198
+ font-weight: 600;
199
+ }
200
+ .avatar img {
201
+ width: 100%;
202
+ height: 100%;
203
+ object-fit: cover;
204
+ display: block;
205
+ }
206
+ .acctBtn:hover .avatar,
207
+ .acctBtn:focus-visible .avatar {
208
+ border-color: var(--accent);
209
+ }
210
+ .acctMenu {
211
+ position: absolute;
212
+ right: 0;
213
+ top: calc(100% + 0.45rem);
214
+ z-index: 20;
215
+ min-width: 11rem;
216
+ padding: 0.5rem;
217
+ border: 1px solid var(--border-faint);
218
+ border-radius: 0.5rem;
219
+ background: var(--surface);
220
+ }
221
+ .acctName {
222
+ margin: 0 0 0.3rem;
223
+ padding: 0.15rem 0.4rem;
224
+ font-size: 0.85rem;
225
+ color: var(--fg2);
226
+ overflow-wrap: anywhere;
227
+ }
228
+ .acctOut {
229
+ width: 100%;
230
+ padding: 0.35rem 0.4rem;
231
+ border: 0;
232
+ border-radius: 0.35rem;
233
+ background: none;
234
+ color: var(--fg3);
235
+ font: inherit;
236
+ font-size: 0.85rem;
237
+ text-align: left;
238
+ cursor: pointer;
239
+ }
240
+ .acctOut:hover {
241
+ background: var(--bg);
242
+ color: var(--fg);
243
+ }
244
+
245
+ /* ── why sign in ───────────────────────────────────────────────────────────
246
+ The question beside the bar's sign-in button and under the hero, and the card
247
+ both of them open. The card's prose is src/ui/whySignIn.ts, the same strings
248
+ the app's own dialog renders. */
249
+ .why {
250
+ font: inherit;
251
+ font-size: 0.9rem;
252
+ padding: 0.35rem 0.15rem;
253
+ background: none;
254
+ color: var(--fg4);
255
+ border: none;
256
+ text-decoration: underline;
257
+ text-underline-offset: 2px;
258
+ cursor: pointer;
259
+ }
260
+ .why:hover,
261
+ .why:focus-visible {
262
+ color: var(--fg);
263
+ }
264
+ .whyCard {
265
+ width: min(34rem, calc(100vw - 2rem));
266
+ padding: 1.4rem 1.5rem;
267
+ background: var(--surface);
268
+ color: var(--fg);
269
+ border: 1px solid var(--border-faint);
270
+ border-radius: 0.6rem;
271
+ }
272
+ .whyCard::backdrop {
273
+ background: #000a;
274
+ }
275
+ .whyTop {
276
+ display: flex;
277
+ align-items: baseline;
278
+ justify-content: space-between;
279
+ gap: 1rem;
280
+ }
281
+ .whyHead {
282
+ margin: 0 0 0.9rem;
283
+ font-size: 1.4rem;
284
+ }
285
+ /* Why the last sign-in did not finish, above the pitch and the retry. */
286
+ .whyTrouble {
287
+ margin: 0 0 0.9rem;
288
+ padding: 0.6rem 0.8rem;
289
+ border: 1px solid var(--danger);
290
+ border-radius: 0.4rem;
291
+ color: var(--danger);
292
+ font-size: 0.95rem;
293
+ }
294
+ /* The answer, at the size of the thing being answered. */
295
+ .whyPitch {
296
+ margin: 0 0 0.7rem;
297
+ font-size: 1.05rem;
298
+ color: var(--fg);
299
+ }
300
+ .whyLine {
301
+ margin: 0 0 0.7rem;
302
+ color: var(--fg3);
303
+ font-size: 0.95rem;
304
+ }
305
+ .whyLine a {
306
+ color: var(--accent);
307
+ white-space: nowrap;
308
+ }
309
+ .whyCta {
310
+ display: flex;
311
+ flex-wrap: wrap;
312
+ gap: 0.6rem;
313
+ margin: 1.1rem 0 0.8rem;
314
+ }
315
+ /* ── the privacy page ──────────────────────────────────────────────────────
316
+ One column of prose under the site bar, at the width the rest of the site
317
+ reads at. */
318
+ .privacy {
319
+ padding-top: 2.5rem;
320
+ padding-bottom: 4rem;
321
+ }
322
+ .privacy h1 {
323
+ margin: 0 0 0.6rem;
324
+ font-size: 2rem;
325
+ }
326
+ .privacy h2 {
327
+ margin: 2rem 0 0.5rem;
328
+ font-size: 1.25rem;
329
+ }
330
+ .privacy p {
331
+ margin: 0;
332
+ color: var(--fg3);
333
+ line-height: 1.65;
334
+ }
335
+ .privacy p + p {
336
+ margin-top: 0.8rem;
337
+ }
338
+ .privacy a {
339
+ color: var(--accent);
340
+ }
341
+ .privacyLead {
342
+ font-size: 1.1rem;
343
+ }
344
+
345
+ /* ── the about page and the blog ───────────────────────────────────────── */
346
+ .about,
347
+ .blog {
348
+ padding-top: 2.5rem;
349
+ padding-bottom: 4rem;
350
+ }
351
+ .about h1,
352
+ .blog h1 {
353
+ margin: 0 0 0.6rem;
354
+ font-size: 2rem;
355
+ }
356
+ .about p,
357
+ .blog p,
358
+ .blog li {
359
+ color: var(--fg3);
360
+ }
361
+ .about p {
362
+ margin: 0 0 0.8rem;
363
+ }
364
+ .aboutLead,
365
+ .blogLead {
366
+ font-size: 1.1rem;
367
+ }
368
+ .aboutLinks {
369
+ margin: 1.5rem 0 0;
370
+ padding-left: 1.2rem;
371
+ }
372
+ .aboutLinks li {
373
+ margin-bottom: 0.4rem;
374
+ }
375
+ .postMeta {
376
+ margin: 0.2rem 0 0;
377
+ font-variant-numeric: tabular-nums;
378
+ font-size: 0.85rem;
379
+ color: var(--fg4);
380
+ }
381
+ .postCard {
382
+ display: grid;
383
+ grid-template-columns: 14rem minmax(0, 1fr);
384
+ gap: 1.2rem;
385
+ margin-top: 2rem;
386
+ padding-top: 1.5rem;
387
+ border-top: 1px solid var(--border-faint);
388
+ }
389
+ .postCard img,
390
+ .postHero,
391
+ .postBody img {
392
+ display: block;
393
+ width: 100%;
394
+ height: auto;
395
+ border-radius: 4px;
396
+ }
397
+ .postCard h2 {
398
+ margin: 0;
399
+ font-size: 1.25rem;
400
+ }
401
+ .postCard h2 a {
402
+ color: var(--fg);
403
+ text-decoration: none;
404
+ }
405
+ .postCard h2 a:hover {
406
+ text-decoration: underline;
407
+ }
408
+ .postCard p {
409
+ margin: 0.4rem 0 0;
410
+ }
411
+ @media (max-width: 560px) {
412
+ .postCard {
413
+ grid-template-columns: 1fr;
414
+ }
415
+ }
416
+ .postFull h1 {
417
+ margin: 0.4rem 0 1rem;
418
+ }
419
+ .postLook {
420
+ font-weight: 600;
421
+ }
422
+ .postBody {
423
+ line-height: 1.6;
424
+ }
425
+ .postBody h2 {
426
+ margin: 2rem 0 0.6rem;
427
+ font-size: 1.25rem;
428
+ }
429
+ .postBody p {
430
+ margin: 0 0 0.9rem;
431
+ }
432
+ .postBody .anchor {
433
+ margin-left: 0.45em;
434
+ color: var(--fg4);
435
+ text-decoration: none;
436
+ font-weight: 400;
437
+ opacity: 0;
438
+ }
439
+ .postBody h2:hover .anchor,
440
+ .postBody .anchor:focus-visible {
441
+ opacity: 1;
442
+ }
443
+ .postBody img {
444
+ margin: 0.4rem 0 1rem;
445
+ }
446
+
447
+ /* ── the signed-in home ────────────────────────────────────────────────────
448
+ A rail of links and one column of sections, built by site/scripts/home.ts
449
+ once Firebase has answered. The cards are the gallery's own anatomy, so a
450
+ saved look and a demo read as the same kind of thing. */
451
+ .homeIn {
452
+ display: grid;
453
+ grid-template-columns: 200px minmax(0, 1fr);
454
+ gap: 2rem;
455
+ max-width: 88rem;
456
+ margin: 0 auto;
457
+ padding: 2rem max(1.15rem, env(safe-area-inset-left)) 3.5rem;
458
+ }
459
+ .rail {
460
+ display: flex;
461
+ flex-direction: column;
462
+ gap: 0.15rem;
463
+ position: sticky;
464
+ top: 1rem;
465
+ align-self: start;
466
+ }
467
+ .rail a {
468
+ padding: 0.4rem 0.6rem;
469
+ border-radius: 0.4rem;
470
+ color: var(--fg3);
471
+ text-decoration: none;
472
+ font-size: 0.92rem;
473
+ white-space: nowrap;
474
+ }
475
+ .rail a:hover {
476
+ background: var(--surface);
477
+ color: var(--fg);
478
+ }
479
+ .rail a.on {
480
+ background: var(--surface);
481
+ color: var(--accent);
482
+ }
483
+ .homeMain {
484
+ display: flex;
485
+ flex-direction: column;
486
+ gap: 2.75rem;
487
+ }
488
+ /* `section`'s 3.5rem belongs to the landing page's full-bleed bands; the
489
+ column spaces itself. */
490
+ .homeSec {
491
+ padding: 0;
492
+ scroll-margin-top: 1rem;
493
+ }
494
+ .homeSec .head {
495
+ font-size: 1.15rem;
496
+ }
497
+ /* The rail becomes the row under the bar: 200px of links beside a single
498
+ column of cards is most of a phone's width. */
499
+ @media (max-width: 52rem) {
500
+ .homeIn {
501
+ grid-template-columns: minmax(0, 1fr);
502
+ gap: 1.35rem;
503
+ }
504
+ .rail {
505
+ position: static;
506
+ flex-direction: row;
507
+ gap: 0.35rem;
508
+ overflow-x: auto;
509
+ scrollbar-width: none;
510
+ }
511
+ }
512
+
513
+ .resumeCard {
514
+ display: grid;
515
+ grid-template-columns: minmax(0, 20rem) minmax(0, 1fr);
516
+ border: 1px solid var(--border-faint);
517
+ border-radius: 0.6rem;
518
+ background: var(--surface);
519
+ overflow: hidden;
520
+ }
521
+ .resumeCard .shot {
522
+ position: relative;
523
+ display: block;
524
+ aspect-ratio: 5 / 4;
525
+ background: #000;
526
+ }
527
+ .resumeCard .shot .still {
528
+ position: absolute;
529
+ inset: 0;
530
+ width: 100%;
531
+ height: 100%;
532
+ display: block;
533
+ object-fit: cover;
534
+ }
535
+ .resumeBody {
536
+ display: flex;
537
+ flex-direction: column;
538
+ justify-content: center;
539
+ gap: 0.45rem;
540
+ padding: 1.35rem;
541
+ }
542
+ .when {
543
+ margin: 0;
544
+ color: var(--fg4);
545
+ font-size: 0.85rem;
546
+ }
547
+ .resumeSays {
548
+ margin: 0;
549
+ color: var(--fg2);
550
+ font-size: 0.95rem;
551
+ text-wrap: pretty;
552
+ }
553
+ .resumeCta {
554
+ display: flex;
555
+ flex-wrap: wrap;
556
+ gap: 0.6rem;
557
+ margin: 0.7rem 0 0;
558
+ }
559
+ @media (max-width: 44rem) {
560
+ .resumeCard {
561
+ grid-template-columns: minmax(0, 1fr);
562
+ }
563
+ }
564
+
565
+ /* A look with no still: the app mark on the card's own black, so the row of
566
+ cards keeps its shape. */
567
+ .shot.blank {
568
+ display: grid;
569
+ place-items: center;
570
+ background: #0a0b0d;
571
+ }
572
+ .resumeCard .shot.blank .blankMark {
573
+ position: static;
574
+ width: 2.5rem;
575
+ height: 2.5rem;
576
+ object-fit: contain;
577
+ opacity: 0.55;
578
+ }
579
+
580
+ /* A saved card with its verbs under it. The <li> carries the card's border so
581
+ the link and the row of buttons read as one card, and the row sits at the
582
+ bottom however long the name above it runs. */
583
+ .grid li:has(> .cardActions) {
584
+ display: flex;
585
+ flex-direction: column;
586
+ overflow: hidden;
587
+ border: 1px solid var(--border-faint);
588
+ border-radius: 0.6rem;
589
+ background: var(--surface);
590
+ }
591
+ .grid li:has(> .cardActions) > a:first-child {
592
+ flex: 1;
593
+ border: 0;
594
+ border-radius: 0;
595
+ }
596
+ .grid li:has(> a:first-child:hover, > a:first-child:focus-visible) {
597
+ border-color: var(--accent);
598
+ }
599
+ .cardActions {
600
+ display: flex;
601
+ flex-wrap: wrap;
602
+ align-items: center;
603
+ gap: 0.3rem 0.8rem;
604
+ padding: 0.45rem 0.85rem 0.6rem;
605
+ border-top: 1px solid var(--border-faint);
606
+ font-size: 0.8rem;
607
+ }
608
+ .cardAct {
609
+ padding: 0.15rem 0;
610
+ border: 0;
611
+ background: none;
612
+ color: var(--fg4);
613
+ font: inherit;
614
+ text-decoration: underline;
615
+ text-underline-offset: 2px;
616
+ cursor: pointer;
617
+ }
618
+ .cardAct:hover,
619
+ .cardAct:focus-visible {
620
+ color: var(--fg);
621
+ }
622
+ .cardAct:disabled {
623
+ opacity: 0.5;
624
+ cursor: default;
625
+ }
626
+ .cardAsk {
627
+ flex-basis: 100%;
628
+ color: var(--fg2);
629
+ overflow-wrap: anywhere;
630
+ }
631
+ .cardRename {
632
+ flex: 1 1 100%;
633
+ min-width: 0;
634
+ padding: 0.3rem 0.45rem;
635
+ border: 1px solid var(--border-faint);
636
+ border-radius: 0.35rem;
637
+ background: var(--bg);
638
+ color: var(--fg);
639
+ font: inherit;
640
+ }
641
+ .cardStatus {
642
+ flex-basis: 100%;
643
+ color: var(--fg3);
644
+ }
645
+ .cardStatus:empty {
646
+ display: none;
647
+ }
648
+
649
+ .empty {
650
+ padding: 2.25rem 1.5rem;
651
+ border: 1px dashed var(--border-faint);
652
+ border-radius: 0.6rem;
653
+ text-align: center;
654
+ }
655
+ .emptyHead {
656
+ margin: 0 0 0.5rem;
657
+ font-size: 1.05rem;
658
+ font-weight: 600;
659
+ }
660
+ .emptySays {
661
+ max-width: 34rem;
662
+ margin: 0 auto 1.15rem;
663
+ color: var(--fg4);
664
+ font-size: 0.92rem;
665
+ text-wrap: pretty;
666
+ }
667
+
668
+ /* ── waiting for Firebase ──────────────────────────────────────────────────
669
+ SignedInHint.astro's head script sets `data-home="pending"` on a browser that
670
+ has signed in before. The landing page and the sign-in controls stay out of
671
+ sight and the home's outline stands in until home.ts paints one state. */
672
+ .homeWait {
673
+ display: none;
674
+ }
675
+ html[data-home='pending'] .homeWait {
676
+ display: block;
677
+ min-height: 100vh;
678
+ }
679
+ html[data-home='pending'] #landing,
680
+ html[data-home='pending'] #signIn,
681
+ html[data-home='pending'] #why {
682
+ display: none;
683
+ }
684
+ .vh {
685
+ position: absolute;
686
+ width: 1px;
687
+ height: 1px;
688
+ overflow: hidden;
689
+ clip-path: inset(50%);
690
+ white-space: nowrap;
691
+ }
692
+ .bone {
693
+ display: block;
694
+ animation: bone 1.4s ease-in-out infinite;
695
+ }
696
+ @keyframes bone {
697
+ 50% {
698
+ opacity: 0.5;
699
+ }
700
+ }
701
+ @media (prefers-reduced-motion: reduce) {
702
+ .bone {
703
+ animation: none;
704
+ }
705
+ }
706
+ .bone {
707
+ border-radius: 0.4rem;
708
+ background: var(--surface);
709
+ }
710
+ .railBone {
711
+ width: 7rem;
712
+ height: 1.9rem;
713
+ }
714
+ .headBone {
715
+ width: 14rem;
716
+ height: 1.4rem;
717
+ }
@@ -0,0 +1,9 @@
1
+ export type NavLink = readonly [href: string, label: string];
2
+ export interface SiteInfo {
3
+ name: string;
4
+ home: string;
5
+ icon: string;
6
+ app: string;
7
+ nav: readonly NavLink[];
8
+ footer: readonly NavLink[];
9
+ }
@@ -0,0 +1 @@
1
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "skillet-shared",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "Code shared by bender and videoskillet",
5
5
  "license": "MIT",
6
6
  "author": "Colin Diesh",
@@ -31,10 +31,22 @@
31
31
  "types": "./dist/wire-route.d.ts",
32
32
  "default": "./dist/wire-route.js"
33
33
  },
34
- "./tokens.css": "./dist/tokens.css"
34
+ "./tokens.css": "./dist/tokens.css",
35
+ "./site-info": {
36
+ "types": "./dist/site-info.d.ts",
37
+ "default": "./dist/site-info.js"
38
+ },
39
+ "./components/SiteBar.astro": "./components/SiteBar.astro",
40
+ "./components/Footer.astro": "./components/Footer.astro",
41
+ "./components/SocialMeta.astro": "./components/SocialMeta.astro",
42
+ "./components/SignedInHint.astro": "./components/SignedInHint.astro",
43
+ "./site.css": "./css/site.css",
44
+ "./footer.css": "./css/footer.css"
35
45
  },
36
46
  "files": [
37
- "dist"
47
+ "dist",
48
+ "components",
49
+ "css"
38
50
  ],
39
51
  "publishConfig": {
40
52
  "access": "public"