@atelic-action/ui 0.1.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/README.md +79 -0
- package/package.json +54 -0
- package/src/chrome/BrandLockup.tsx +29 -0
- package/src/chrome/CreditBar.tsx +18 -0
- package/src/chrome/Footer.tsx +243 -0
- package/src/chrome/SiteHeader.tsx +152 -0
- package/src/chrome/SiteMenu.tsx +130 -0
- package/src/chrome/SkipLink.tsx +16 -0
- package/src/chrome/StickyCTABar.tsx +50 -0
- package/src/chrome/index.ts +20 -0
- package/src/hooks/index.ts +1 -0
- package/src/hooks/useScrollSpy.ts +44 -0
- package/src/lib/newTabProps.ts +14 -0
- package/src/styles/base.css +221 -0
- package/src/styles/chrome.css +460 -0
- package/src/types.ts +88 -0
|
@@ -0,0 +1,460 @@
|
|
|
1
|
+
/* =================================================================
|
|
2
|
+
@atelic-action/ui CHROME: the skip link, the brand lockup, the
|
|
3
|
+
header, the menu, the sticky CTA bar, the footer, and the credit
|
|
4
|
+
band. Tokens are read here and never defined. Everything sits in
|
|
5
|
+
the atelic-ui layer, so any rule a site writes outside a layer wins.
|
|
6
|
+
The chrome's buttons wear the site's own .btn classes.
|
|
7
|
+
================================================================= */
|
|
8
|
+
@layer atelic-ui {
|
|
9
|
+
/* ---- Skip link (a11y) ---- */
|
|
10
|
+
.mkt .skip-link {
|
|
11
|
+
position: absolute;
|
|
12
|
+
left: -9999px;
|
|
13
|
+
top: 0;
|
|
14
|
+
background: var(--ink);
|
|
15
|
+
color: #fff;
|
|
16
|
+
padding: 12px 18px;
|
|
17
|
+
z-index: 200;
|
|
18
|
+
border-radius: 0 0 8px 0;
|
|
19
|
+
}
|
|
20
|
+
.mkt .skip-link:focus {
|
|
21
|
+
left: 0;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/* ---- Brand lockup ---- */
|
|
25
|
+
/* The brand's trailing fade bar after the wordmark (logo.run), worn in the
|
|
26
|
+
header and the footer. */
|
|
27
|
+
.mkt .wordmark-run {
|
|
28
|
+
display: inline-block;
|
|
29
|
+
width: 1.7em;
|
|
30
|
+
height: 2px;
|
|
31
|
+
margin-left: 0.38em;
|
|
32
|
+
vertical-align: middle;
|
|
33
|
+
background: linear-gradient(90deg, var(--primary), transparent);
|
|
34
|
+
}
|
|
35
|
+
/* The glyph tile mark (logo.tile): the favicon's design as real DOM text,
|
|
36
|
+
so the brand webfont renders it, which an SVG loaded through an img
|
|
37
|
+
cannot do. Worn in the header and the footer. */
|
|
38
|
+
.mkt .logo-tile {
|
|
39
|
+
display: inline-flex;
|
|
40
|
+
align-items: center;
|
|
41
|
+
justify-content: center;
|
|
42
|
+
width: 30px;
|
|
43
|
+
height: 30px;
|
|
44
|
+
margin-right: 0.45em;
|
|
45
|
+
border-radius: 8px;
|
|
46
|
+
background: var(--surface-dark);
|
|
47
|
+
color: var(--surface);
|
|
48
|
+
font-weight: 600;
|
|
49
|
+
font-size: 1.05rem;
|
|
50
|
+
line-height: 1;
|
|
51
|
+
position: relative;
|
|
52
|
+
flex: none;
|
|
53
|
+
}
|
|
54
|
+
.mkt .logo-tile::after {
|
|
55
|
+
content: "";
|
|
56
|
+
position: absolute;
|
|
57
|
+
left: 7px;
|
|
58
|
+
right: 7px;
|
|
59
|
+
bottom: 4px;
|
|
60
|
+
height: 2.5px;
|
|
61
|
+
border-radius: 2px;
|
|
62
|
+
background: var(--primary);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/* ---- Header ---- */
|
|
66
|
+
/* Fixed header: transparent over a hero, solid once scrolled, or dark. */
|
|
67
|
+
.mkt .nav {
|
|
68
|
+
position: fixed;
|
|
69
|
+
top: 0;
|
|
70
|
+
left: 0;
|
|
71
|
+
right: 0;
|
|
72
|
+
z-index: 100;
|
|
73
|
+
height: var(--nav-height);
|
|
74
|
+
display: flex;
|
|
75
|
+
align-items: center;
|
|
76
|
+
transition:
|
|
77
|
+
background 0.3s ease,
|
|
78
|
+
box-shadow 0.3s ease,
|
|
79
|
+
backdrop-filter 0.3s ease;
|
|
80
|
+
}
|
|
81
|
+
.mkt .nav-inner {
|
|
82
|
+
width: 100%;
|
|
83
|
+
max-width: var(--max-width);
|
|
84
|
+
margin-inline: auto;
|
|
85
|
+
padding-inline: var(--gutter);
|
|
86
|
+
display: flex;
|
|
87
|
+
align-items: center;
|
|
88
|
+
justify-content: space-between;
|
|
89
|
+
gap: 20px;
|
|
90
|
+
}
|
|
91
|
+
.mkt .nav-logo {
|
|
92
|
+
display: flex;
|
|
93
|
+
align-items: center;
|
|
94
|
+
gap: 10px;
|
|
95
|
+
font-family: var(--font-brand);
|
|
96
|
+
font-weight: 800;
|
|
97
|
+
letter-spacing: -0.02em;
|
|
98
|
+
font-size: 1.02rem;
|
|
99
|
+
}
|
|
100
|
+
.mkt .nav-logo .mark {
|
|
101
|
+
height: 30px;
|
|
102
|
+
width: 30px;
|
|
103
|
+
}
|
|
104
|
+
.mkt .nav-links {
|
|
105
|
+
display: none;
|
|
106
|
+
align-items: center;
|
|
107
|
+
gap: 4px;
|
|
108
|
+
}
|
|
109
|
+
.mkt .nav-links a {
|
|
110
|
+
padding: 8px 14px;
|
|
111
|
+
border-radius: var(--radius-pill);
|
|
112
|
+
font-weight: 600;
|
|
113
|
+
font-size: 0.95rem;
|
|
114
|
+
transition:
|
|
115
|
+
background 0.15s,
|
|
116
|
+
color 0.15s;
|
|
117
|
+
}
|
|
118
|
+
.mkt .nav-links a:hover {
|
|
119
|
+
background: rgba(33, 28, 23, 0.06);
|
|
120
|
+
}
|
|
121
|
+
/* A page's own stops: the scroll spied active one. */
|
|
122
|
+
.mkt .nav-links a.active {
|
|
123
|
+
color: var(--primary);
|
|
124
|
+
}
|
|
125
|
+
/* Desktop only, and hidden until the bar is solid: on small screens the
|
|
126
|
+
sticky CTA bar and the menu carry the action, and the wordmark needs the
|
|
127
|
+
width. The class sits on a wrapper around the button, because a site's
|
|
128
|
+
.btn display rule lives outside this layer and would outrank a rule here
|
|
129
|
+
that hid the button itself. */
|
|
130
|
+
.mkt .nav .nav-cta {
|
|
131
|
+
display: none;
|
|
132
|
+
}
|
|
133
|
+
.mkt .nav-burger {
|
|
134
|
+
display: inline-flex;
|
|
135
|
+
flex-direction: column;
|
|
136
|
+
gap: 5px;
|
|
137
|
+
padding: 10px;
|
|
138
|
+
}
|
|
139
|
+
.mkt .nav-burger span {
|
|
140
|
+
width: 24px;
|
|
141
|
+
height: 2px;
|
|
142
|
+
background: currentColor;
|
|
143
|
+
border-radius: 2px;
|
|
144
|
+
transition:
|
|
145
|
+
transform 0.25s,
|
|
146
|
+
opacity 0.2s;
|
|
147
|
+
}
|
|
148
|
+
/* A white logo image is tinted to ink when the bar is solid on light. */
|
|
149
|
+
.mkt .nav.is-solid .nav-logo .mark {
|
|
150
|
+
filter: brightness(0) saturate(1);
|
|
151
|
+
}
|
|
152
|
+
.mkt .nav.is-transparent {
|
|
153
|
+
color: #fff;
|
|
154
|
+
}
|
|
155
|
+
.mkt .nav.is-transparent .nav-logo .wordmark {
|
|
156
|
+
color: #fff;
|
|
157
|
+
}
|
|
158
|
+
.mkt .nav.is-solid {
|
|
159
|
+
background: color-mix(in srgb, var(--surface) 86%, transparent);
|
|
160
|
+
backdrop-filter: saturate(1.4) blur(14px);
|
|
161
|
+
-webkit-backdrop-filter: saturate(1.4) blur(14px);
|
|
162
|
+
box-shadow: 0 1px 0 var(--hairline-strong);
|
|
163
|
+
color: var(--ink);
|
|
164
|
+
}
|
|
165
|
+
/* The dark bar (an artifact's stage, the audit): it matches the page
|
|
166
|
+
instead of capping it with a light band. The white logo mark stays
|
|
167
|
+
untinted, and the active stop lifts to the bright primary. */
|
|
168
|
+
.mkt .nav.is-solid.nav-dark {
|
|
169
|
+
background: color-mix(in srgb, var(--surface-dark) 88%, transparent);
|
|
170
|
+
box-shadow: 0 1px 0 color-mix(in srgb, var(--surface) 12%, transparent);
|
|
171
|
+
color: color-mix(in srgb, var(--surface) 96%, white);
|
|
172
|
+
}
|
|
173
|
+
.mkt .nav.nav-dark .nav-logo .mark {
|
|
174
|
+
filter: none;
|
|
175
|
+
}
|
|
176
|
+
.mkt .nav.nav-dark .nav-links a:hover {
|
|
177
|
+
background: rgba(255, 255, 255, 0.08);
|
|
178
|
+
}
|
|
179
|
+
.mkt .nav.nav-dark .nav-links a.active {
|
|
180
|
+
color: var(--primary-bright);
|
|
181
|
+
}
|
|
182
|
+
@media (min-width: 900px) {
|
|
183
|
+
.mkt .nav-links {
|
|
184
|
+
display: flex;
|
|
185
|
+
}
|
|
186
|
+
.mkt .nav-burger {
|
|
187
|
+
display: none;
|
|
188
|
+
}
|
|
189
|
+
.mkt .nav.is-solid .nav-cta {
|
|
190
|
+
display: inline-flex;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
/* The burger folds into an X while the menu is open. State lives on the
|
|
194
|
+
button's own aria-expanded, so no extra class plumbing. */
|
|
195
|
+
.mkt .nav-burger[aria-expanded="true"] span:nth-child(1) {
|
|
196
|
+
transform: translateY(7px) rotate(45deg);
|
|
197
|
+
}
|
|
198
|
+
.mkt .nav-burger[aria-expanded="true"] span:nth-child(2) {
|
|
199
|
+
opacity: 0;
|
|
200
|
+
}
|
|
201
|
+
.mkt .nav-burger[aria-expanded="true"] span:nth-child(3) {
|
|
202
|
+
transform: translateY(-7px) rotate(-45deg);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/* ---- Menu ---- */
|
|
206
|
+
/* A native modal dialog, sized to the whole viewport over the dialog's own
|
|
207
|
+
box. It renders in the top layer above the header, so it carries its own
|
|
208
|
+
close button where the burger sits. */
|
|
209
|
+
.mkt .mobile-menu {
|
|
210
|
+
position: fixed;
|
|
211
|
+
inset: 0;
|
|
212
|
+
width: 100%;
|
|
213
|
+
height: 100%;
|
|
214
|
+
max-width: none;
|
|
215
|
+
max-height: none;
|
|
216
|
+
margin: 0;
|
|
217
|
+
border: 0;
|
|
218
|
+
background: var(--surface-dark);
|
|
219
|
+
color: var(--surface);
|
|
220
|
+
flex-direction: column;
|
|
221
|
+
padding: calc(var(--nav-height) + 20px) var(--gutter) 40px;
|
|
222
|
+
overflow-y: auto;
|
|
223
|
+
transform: translateY(-100%);
|
|
224
|
+
transition: transform 0.4s cubic-bezier(0.7, 0, 0.2, 1);
|
|
225
|
+
}
|
|
226
|
+
.mkt .mobile-menu[open] {
|
|
227
|
+
display: flex;
|
|
228
|
+
transform: translateY(0);
|
|
229
|
+
}
|
|
230
|
+
@starting-style {
|
|
231
|
+
.mkt .mobile-menu[open] {
|
|
232
|
+
transform: translateY(-100%);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
.mkt .mobile-menu::backdrop {
|
|
236
|
+
background: transparent;
|
|
237
|
+
}
|
|
238
|
+
@media (prefers-reduced-motion: reduce) {
|
|
239
|
+
.mkt .mobile-menu {
|
|
240
|
+
transition: none;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
/* Page scroll holds still while the menu is open. */
|
|
244
|
+
html:has(dialog.mobile-menu[open]) {
|
|
245
|
+
overflow: hidden;
|
|
246
|
+
}
|
|
247
|
+
.mkt .mobile-menu .mm-close {
|
|
248
|
+
position: absolute;
|
|
249
|
+
top: calc(var(--nav-height) / 2 - 14.5px);
|
|
250
|
+
right: var(--gutter);
|
|
251
|
+
display: inline-flex;
|
|
252
|
+
flex-direction: column;
|
|
253
|
+
gap: 5px;
|
|
254
|
+
padding: 10px;
|
|
255
|
+
}
|
|
256
|
+
.mkt .mobile-menu .mm-close span {
|
|
257
|
+
width: 24px;
|
|
258
|
+
height: 2px;
|
|
259
|
+
background: currentColor;
|
|
260
|
+
border-radius: 2px;
|
|
261
|
+
}
|
|
262
|
+
.mkt .mobile-menu .mm-close span:nth-child(1) {
|
|
263
|
+
transform: translateY(3.5px) rotate(45deg);
|
|
264
|
+
}
|
|
265
|
+
.mkt .mobile-menu .mm-close span:nth-child(2) {
|
|
266
|
+
transform: translateY(-3.5px) rotate(-45deg);
|
|
267
|
+
}
|
|
268
|
+
/* Menu entries only (.mm-link), never bare anchors, so the .btn CTA below
|
|
269
|
+
keeps its standard button styling. */
|
|
270
|
+
.mkt .mobile-menu .mm-link {
|
|
271
|
+
font-size: 1.8rem;
|
|
272
|
+
font-weight: 800;
|
|
273
|
+
letter-spacing: -0.02em;
|
|
274
|
+
padding: 14px 0;
|
|
275
|
+
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
|
|
276
|
+
display: flex;
|
|
277
|
+
justify-content: space-between;
|
|
278
|
+
align-items: center;
|
|
279
|
+
}
|
|
280
|
+
.mkt .mobile-menu .mm-link .idx {
|
|
281
|
+
font-family: var(--font-mono);
|
|
282
|
+
font-size: 0.8rem;
|
|
283
|
+
color: var(--primary-bright);
|
|
284
|
+
font-weight: 500;
|
|
285
|
+
}
|
|
286
|
+
.mkt .mobile-menu .mm-cta {
|
|
287
|
+
margin-top: 28px;
|
|
288
|
+
}
|
|
289
|
+
.mkt .mobile-menu .mm-contact {
|
|
290
|
+
margin-top: auto;
|
|
291
|
+
font-family: var(--font-mono);
|
|
292
|
+
font-size: 0.8rem;
|
|
293
|
+
color: var(--neutral-300);
|
|
294
|
+
letter-spacing: 0.04em;
|
|
295
|
+
line-height: 1.9;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
/* ---- Sticky CTA bar (phones only) ---- */
|
|
299
|
+
.mkt .sticky-cta {
|
|
300
|
+
position: fixed;
|
|
301
|
+
left: 0;
|
|
302
|
+
right: 0;
|
|
303
|
+
bottom: 0;
|
|
304
|
+
z-index: 95;
|
|
305
|
+
display: flex;
|
|
306
|
+
gap: 10px;
|
|
307
|
+
padding: 12px var(--gutter) max(12px, env(safe-area-inset-bottom));
|
|
308
|
+
background: color-mix(in srgb, var(--surface) 90%, transparent);
|
|
309
|
+
backdrop-filter: blur(12px);
|
|
310
|
+
-webkit-backdrop-filter: blur(12px);
|
|
311
|
+
border-top: 1px solid var(--hairline-strong);
|
|
312
|
+
transform: translateY(120%);
|
|
313
|
+
transition: transform 0.35s cubic-bezier(0.7, 0, 0.2, 1);
|
|
314
|
+
}
|
|
315
|
+
.mkt .sticky-cta.show {
|
|
316
|
+
transform: translateY(0);
|
|
317
|
+
}
|
|
318
|
+
.mkt .sticky-cta .btn {
|
|
319
|
+
flex: 1;
|
|
320
|
+
}
|
|
321
|
+
.mkt .sticky-cta .btn-call {
|
|
322
|
+
flex: 0 0 auto;
|
|
323
|
+
}
|
|
324
|
+
@media (min-width: 900px) {
|
|
325
|
+
.mkt .sticky-cta {
|
|
326
|
+
display: none;
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
/* ---- Footer ---- */
|
|
331
|
+
.mkt .footer {
|
|
332
|
+
background: var(--surface-dark);
|
|
333
|
+
color: var(--neutral-200);
|
|
334
|
+
padding-block: clamp(48px, 7vw, 84px) 28px;
|
|
335
|
+
}
|
|
336
|
+
/* biome-ignore lint/style/noDescendingSpecificity: scoped to .footer, so it never meets the header's link rules */
|
|
337
|
+
.mkt .footer a:hover {
|
|
338
|
+
color: #fff;
|
|
339
|
+
}
|
|
340
|
+
.mkt .footer-grid {
|
|
341
|
+
display: grid;
|
|
342
|
+
gap: 40px;
|
|
343
|
+
grid-template-columns: 1fr;
|
|
344
|
+
}
|
|
345
|
+
.mkt .footer h4 {
|
|
346
|
+
color: #fff;
|
|
347
|
+
font-family: var(--font-mono);
|
|
348
|
+
font-size: 0.74rem;
|
|
349
|
+
letter-spacing: 0.16em;
|
|
350
|
+
text-transform: uppercase;
|
|
351
|
+
font-weight: 500;
|
|
352
|
+
margin-bottom: 16px;
|
|
353
|
+
}
|
|
354
|
+
.mkt .footer-brand img {
|
|
355
|
+
height: 38px;
|
|
356
|
+
margin-bottom: 18px;
|
|
357
|
+
}
|
|
358
|
+
.mkt .footer-brand p {
|
|
359
|
+
color: var(--neutral-300);
|
|
360
|
+
max-width: 30ch;
|
|
361
|
+
}
|
|
362
|
+
.mkt .footer nav a {
|
|
363
|
+
display: block;
|
|
364
|
+
padding: 6px 0;
|
|
365
|
+
color: var(--neutral-200);
|
|
366
|
+
font-weight: 500;
|
|
367
|
+
}
|
|
368
|
+
.mkt .footer .nap {
|
|
369
|
+
font-style: normal;
|
|
370
|
+
line-height: 1.9;
|
|
371
|
+
color: var(--neutral-200);
|
|
372
|
+
}
|
|
373
|
+
.mkt .footer .nap a {
|
|
374
|
+
color: #fff;
|
|
375
|
+
font-weight: 600;
|
|
376
|
+
}
|
|
377
|
+
.mkt .footer-hours {
|
|
378
|
+
font-family: var(--font-mono);
|
|
379
|
+
font-size: 0.82rem;
|
|
380
|
+
line-height: 1.9;
|
|
381
|
+
color: var(--neutral-300);
|
|
382
|
+
}
|
|
383
|
+
.mkt .footer-social {
|
|
384
|
+
display: flex;
|
|
385
|
+
gap: 10px;
|
|
386
|
+
margin-top: 20px;
|
|
387
|
+
}
|
|
388
|
+
.mkt .footer .footer-social-link {
|
|
389
|
+
display: inline-flex;
|
|
390
|
+
align-items: center;
|
|
391
|
+
justify-content: center;
|
|
392
|
+
width: 38px;
|
|
393
|
+
height: 38px;
|
|
394
|
+
border: 1px solid rgba(255, 255, 255, 0.16);
|
|
395
|
+
border-radius: var(--radius-sm);
|
|
396
|
+
color: var(--neutral-300);
|
|
397
|
+
}
|
|
398
|
+
.mkt .footer .footer-social-link:hover {
|
|
399
|
+
color: var(--primary-bright);
|
|
400
|
+
border-color: var(--primary-bright);
|
|
401
|
+
}
|
|
402
|
+
.mkt .footer-bottom {
|
|
403
|
+
margin-top: 48px;
|
|
404
|
+
padding-top: 24px;
|
|
405
|
+
border-top: 1px solid rgba(255, 255, 255, 0.1);
|
|
406
|
+
display: flex;
|
|
407
|
+
flex-wrap: wrap;
|
|
408
|
+
gap: 12px 28px;
|
|
409
|
+
justify-content: space-between;
|
|
410
|
+
align-items: center;
|
|
411
|
+
font-size: 0.82rem;
|
|
412
|
+
color: var(--neutral-500);
|
|
413
|
+
}
|
|
414
|
+
.mkt .footer .footer-bottom a {
|
|
415
|
+
color: var(--neutral-300);
|
|
416
|
+
}
|
|
417
|
+
.mkt .footer-legal {
|
|
418
|
+
display: flex;
|
|
419
|
+
gap: 20px;
|
|
420
|
+
flex-wrap: wrap;
|
|
421
|
+
}
|
|
422
|
+
@media (min-width: 760px) {
|
|
423
|
+
.mkt .footer-grid {
|
|
424
|
+
grid-template-columns: 1.4fr 1fr 1fr 1.1fr;
|
|
425
|
+
gap: 48px;
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
/* Text wordmark for a site with no mark image. */
|
|
429
|
+
.mkt .footer-wordmark {
|
|
430
|
+
color: #fff;
|
|
431
|
+
font-size: 1.3rem;
|
|
432
|
+
font-weight: 600;
|
|
433
|
+
letter-spacing: -0.045em;
|
|
434
|
+
margin-bottom: 18px;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
/* ---- Credit band ---- */
|
|
438
|
+
/* The developer credit below the footer: its own full width band, centered,
|
|
439
|
+
small, and muted. Purely token themed (the hairline and hover lift derive
|
|
440
|
+
from --surface through color-mix), so it rebrands with theme.css on any
|
|
441
|
+
page it renders under, light or dark. */
|
|
442
|
+
.mkt .credit-bar {
|
|
443
|
+
background: var(--surface-dark);
|
|
444
|
+
border-top: 1px solid color-mix(in srgb, var(--surface) 10%, transparent);
|
|
445
|
+
color: var(--neutral-500);
|
|
446
|
+
font-size: 0.78rem;
|
|
447
|
+
text-align: center;
|
|
448
|
+
text-wrap: balance;
|
|
449
|
+
padding: 16px 20px;
|
|
450
|
+
}
|
|
451
|
+
/* biome-ignore lint/style/noDescendingSpecificity: scoped to .credit-bar, so it never meets the footer's link rules */
|
|
452
|
+
.mkt .credit-bar a {
|
|
453
|
+
color: var(--neutral-300);
|
|
454
|
+
font-weight: 500;
|
|
455
|
+
}
|
|
456
|
+
/* biome-ignore lint/style/noDescendingSpecificity: scoped to .credit-bar, so it never meets the header's link rules */
|
|
457
|
+
.mkt .credit-bar a:hover {
|
|
458
|
+
color: color-mix(in srgb, var(--surface) 96%, white);
|
|
459
|
+
}
|
|
460
|
+
}
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The shapes the chrome renders from. Every value arrives as a prop, so a
|
|
3
|
+
* site maps its own config onto these and the components never read a
|
|
4
|
+
* config file, a router, or a brand value of their own.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* The chrome's mark. `src` is the mark image; omit it for a text wordmark.
|
|
9
|
+
* `tile` renders a CSS glyph tile instead of an image (a dark rounded square
|
|
10
|
+
* bearing the character in the brand's own webfont, which an SVG loaded
|
|
11
|
+
* through an img cannot do). `wordmark` overrides the rendered text when it
|
|
12
|
+
* should differ from the name, and `run` appends the trailing fade bar.
|
|
13
|
+
*/
|
|
14
|
+
export interface BrandLogo {
|
|
15
|
+
src?: string;
|
|
16
|
+
/** Alt text where the mark stands alone, as in the footer. */
|
|
17
|
+
alt?: string;
|
|
18
|
+
tile?: string;
|
|
19
|
+
wordmark?: string;
|
|
20
|
+
run?: boolean;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** The identity a header or footer wears. */
|
|
24
|
+
export interface Brand {
|
|
25
|
+
name: string;
|
|
26
|
+
/** Where the lockup points. Defaults to "/". */
|
|
27
|
+
href?: string;
|
|
28
|
+
logo?: BrandLogo;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** An entry in a site's primary nav. */
|
|
32
|
+
export interface NavLink {
|
|
33
|
+
label: string;
|
|
34
|
+
href: string;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** A conversion action rendered as a button link. */
|
|
38
|
+
export interface CallToAction {
|
|
39
|
+
label: string;
|
|
40
|
+
href: string;
|
|
41
|
+
/** Opens in a new tab, except on mailto, tel, and sms links. */
|
|
42
|
+
external?: boolean;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** A footer or legal link that may open in a new tab. */
|
|
46
|
+
export interface SiteLink {
|
|
47
|
+
label: string;
|
|
48
|
+
href: string;
|
|
49
|
+
external?: boolean;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** An icon link in the footer's brand column. */
|
|
53
|
+
export interface SocialLink {
|
|
54
|
+
/** Accessible name, e.g. "GitHub". */
|
|
55
|
+
label: string;
|
|
56
|
+
href: string;
|
|
57
|
+
icon: "github" | "linkedin" | "mail" | "user";
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export interface Phone {
|
|
61
|
+
/** The dialable number, e.g. "+15555550100". */
|
|
62
|
+
e164: string;
|
|
63
|
+
/** The number as printed, e.g. "(555) 555-0100". */
|
|
64
|
+
display: string;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export interface PostalAddress {
|
|
68
|
+
/** Leave empty for a business that publishes a service area but no storefront. */
|
|
69
|
+
street?: string;
|
|
70
|
+
locality: string;
|
|
71
|
+
region: string;
|
|
72
|
+
postalCode?: string;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** One row of opening hours, already formatted by the site. */
|
|
76
|
+
export interface HoursRow {
|
|
77
|
+
/** The day label as printed, e.g. "Mon" or "Fri to Sun". */
|
|
78
|
+
label: string;
|
|
79
|
+
/** The range as printed, e.g. "11a to 7p" or "Closed". */
|
|
80
|
+
range: string;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/** The contact block at the foot of the menu. */
|
|
84
|
+
export interface MenuContact {
|
|
85
|
+
phone?: Phone;
|
|
86
|
+
email?: string;
|
|
87
|
+
address?: PostalAddress;
|
|
88
|
+
}
|