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.
- package/components/Footer.astro +19 -0
- package/components/SignedInHint.astro +28 -0
- package/components/SiteBar.astro +77 -0
- package/components/SocialMeta.astro +32 -0
- package/css/footer.css +28 -0
- package/css/site.css +717 -0
- package/dist/site-info.d.ts +9 -0
- package/dist/site-info.js +1 -0
- package/package.json +15 -3
|
@@ -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 @@
|
|
|
1
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "skillet-shared",
|
|
3
|
-
"version": "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"
|