@jtakeit/astro 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/LICENSE +21 -0
- package/README.md +59 -0
- package/bin/jtk.mjs +41 -0
- package/docs/booking.md +164 -0
- package/docs/catalogue.md +459 -0
- package/docs/collections.md +249 -0
- package/docs/css.md +86 -0
- package/docs/gallery.md +127 -0
- package/docs/hero-motion.md +189 -0
- package/docs/kit.md +454 -0
- package/docs/languages.md +182 -0
- package/docs/lead-form.md +109 -0
- package/docs/pages.md +193 -0
- package/docs/photos.md +314 -0
- package/docs/scaffold.md +75 -0
- package/docs/shapes.md +140 -0
- package/docs/surface.md +187 -0
- package/lib/catalogue.mjs +1678 -0
- package/lib/codes.mjs +171 -0
- package/lib/create.mjs +282 -0
- package/package.json +16 -0
- package/template/astro.config.mjs +84 -0
- package/template/figures.mjs +122 -0
- package/template/gitignore +16 -0
- package/template/jtakeit-meta.mjs +112 -0
- package/template/jtk/content/index.json +38 -0
- package/template/jtk/design.json +24 -0
- package/template/markdown.mjs +36 -0
- package/template/package-lock.json +5320 -0
- package/template/package.json +26 -0
- package/template/specimens.mjs +46 -0
- package/template/src/components/Blocks.astro +151 -0
- package/template/src/components/BookingForm.astro +506 -0
- package/template/src/components/Clip.astro +155 -0
- package/template/src/components/Hero.astro +66 -0
- package/template/src/components/LeadForm.astro +347 -0
- package/template/src/components/OpeningHours.astro +69 -0
- package/template/src/components/Pile.astro +185 -0
- package/template/src/components/Shot.astro +472 -0
- package/template/src/components/gallery/Gallery.astro +381 -0
- package/template/src/components/gallery/galleries.ts +139 -0
- package/template/src/components/motion/HeroField.astro +520 -0
- package/template/src/components/motion/fields.ts +430 -0
- package/template/src/components/surface/Pattern.astro +278 -0
- package/template/src/components/surface/patterns.ts +187 -0
- package/template/src/content/blocks.ts +758 -0
- package/template/src/content.config.ts +19 -0
- package/template/src/copy/LOCALE.ts +324 -0
- package/template/src/data/site.ts +137 -0
- package/template/src/layouts/Layout.astro +282 -0
- package/template/src/lib/alive.ts +49 -0
- package/template/src/lib/entries.ts +106 -0
- package/template/src/lib/entryLoader.ts +315 -0
- package/template/src/lib/noise.ts +26 -0
- package/template/src/lib/page.ts +287 -0
- package/template/src/lib/photos.ts +168 -0
- package/template/src/lib/under.ts +32 -0
- package/template/src/lib/uploads.ts +85 -0
- package/template/src/pages/[...entry].astro +207 -0
- package/template/src/pages/[...feed].xml.ts +64 -0
- package/template/src/pages/index.astro +90 -0
- package/template/src/pages/llms.txt.ts +50 -0
- package/template/src/pages/privacy.astro +59 -0
- package/template/src/pages/robots.txt.ts +21 -0
- package/template/src/pages/sitemap.xml.ts +50 -0
- package/template/src/styles/global.css +411 -0
- package/template/src/styles/surface.css +375 -0
- package/template/tsconfig.json +5 -0
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "{{SLUG}}",
|
|
3
|
+
"type": "module",
|
|
4
|
+
"version": "0.1.0",
|
|
5
|
+
"private": true,
|
|
6
|
+
"description": "{{NAME}} — a site on jtakeit",
|
|
7
|
+
"engines": {
|
|
8
|
+
"node": ">=22.12.0"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"dev": "astro dev",
|
|
12
|
+
"build": "astro build",
|
|
13
|
+
"check": "astro check",
|
|
14
|
+
"preview": "astro preview"
|
|
15
|
+
},
|
|
16
|
+
"dependencies": {
|
|
17
|
+
"@astrojs/markdown-satteri": "0.3.7",
|
|
18
|
+
"astro": "7.2.4",
|
|
19
|
+
"libphonenumber-js": "1.13.11"
|
|
20
|
+
},
|
|
21
|
+
"devDependencies": {
|
|
22
|
+
"@astrojs/check": "0.9.10",
|
|
23
|
+
"@types/node": "26.2.0",
|
|
24
|
+
"typescript": "6.0.3"
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where the specimens live, said once for the two things that need it.
|
|
3
|
+
*
|
|
4
|
+
* The build draws one page per arrangement so the admin can lift this site's
|
|
5
|
+
* own markup for a block that has just been put into a post (see
|
|
6
|
+
* `src/lib/entryLoader.ts`). Two other things have to know those addresses: the
|
|
7
|
+
* meta plugin, which tells the edge they are not for strangers, and the admin,
|
|
8
|
+
* which asks for them by name.
|
|
9
|
+
*
|
|
10
|
+
* So the rule is here and not in three places. It is a string format rather
|
|
11
|
+
* than an index or an order — the shapes that drift.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
/** The id of one specimen, which is also its address under the collection. */
|
|
15
|
+
export function specimenId(type, view) {
|
|
16
|
+
return view === undefined || view === '' ? `_fl-${type}` : `_fl-${type}-${view}`;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Every specimen this site builds, as addresses.
|
|
21
|
+
*
|
|
22
|
+
* One per arrangement of every type a post may hold — and one per language,
|
|
23
|
+
* because a preview of a Russian post must lift its markup from a Russian page:
|
|
24
|
+
* anything else puts the wrong words and an address that does not exist around
|
|
25
|
+
* the block.
|
|
26
|
+
*/
|
|
27
|
+
export function specimenAddresses(collections, blocks, locales = []) {
|
|
28
|
+
const out = [];
|
|
29
|
+
|
|
30
|
+
for (const collection of collections) {
|
|
31
|
+
for (const kind of collection.body ?? []) {
|
|
32
|
+
const type = blocks.find((one) => one.type === kind);
|
|
33
|
+
if (type === undefined) continue;
|
|
34
|
+
|
|
35
|
+
for (const view of type.views ?? [undefined]) {
|
|
36
|
+
const id = specimenId(kind, view?.key);
|
|
37
|
+
for (const locale of ['', ...locales]) {
|
|
38
|
+
const at = locale === '' ? collection.prefix : `/${locale}${collection.prefix}`;
|
|
39
|
+
out.push(`${at}/${id}/`);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return out.sort();
|
|
46
|
+
}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
---
|
|
2
|
+
/**
|
|
3
|
+
* The page's sections, rendered from the content document.
|
|
4
|
+
*
|
|
5
|
+
* ── why this is one component and not eleven ────────────────────────────────
|
|
6
|
+
*
|
|
7
|
+
* It is the starting point, not the design. A generated site arrives with a
|
|
8
|
+
* shape somebody chose — hero, then the person, then what they do, then the
|
|
9
|
+
* questions — and something has to render it before anybody can look at it.
|
|
10
|
+
* This renders every type the catalogue has, in the document's order, correctly
|
|
11
|
+
* annotated, with no opinion beyond a sensible heading level and some air.
|
|
12
|
+
*
|
|
13
|
+
* The design pass replaces it. That is the intended path: split it, restyle it,
|
|
14
|
+
* give a type its own component when it earns one. What must survive any of
|
|
15
|
+
* that is the annotation — every element whose text comes from content carries
|
|
16
|
+
* `data-jtk-path`, and `annotation-lint.mjs` fails the build without it, because
|
|
17
|
+
* an element that loses its path stops opening the editor *silently*.
|
|
18
|
+
*
|
|
19
|
+
* Repeats are ordinary. Two feature grids and three prose sections is a normal
|
|
20
|
+
* landing page, which is why this walks the document rather than looking blocks
|
|
21
|
+
* up by name.
|
|
22
|
+
*/
|
|
23
|
+
import { PAGE, pathAt } from '../copy/{{LOCALE}}';
|
|
24
|
+
import LeadForm from './LeadForm.astro';
|
|
25
|
+
import design from '../../jtk/design.json';
|
|
26
|
+
|
|
27
|
+
/*
|
|
28
|
+
* The edge and the entrance, out of the design document rather than typed here.
|
|
29
|
+
* Both are attributes, so a block that has neither is exactly the block it was
|
|
30
|
+
* — and a design pass that replaces this component keeps them by keeping two
|
|
31
|
+
* attributes. `docs/surface.md` of @jtakeit/astro.
|
|
32
|
+
*
|
|
33
|
+
* `stagger` is the one that does not go on the section: it sequences a
|
|
34
|
+
* container's children, and the container worth sequencing is the grid, not the
|
|
35
|
+
* heading above it.
|
|
36
|
+
*
|
|
37
|
+
* The band immediately under the hero gets no entrance. On a phone, and on any
|
|
38
|
+
* page whose hero is not a full screen, that band is *in* the first screen —
|
|
39
|
+
* and an entrance above the fold delays the largest paint and shows an empty
|
|
40
|
+
* page on a slow line. `fl-check` fails on it; this is the default that does
|
|
41
|
+
* not walk into it.
|
|
42
|
+
*/
|
|
43
|
+
const surface = design.surface ?? { edge: '', reveal: '' };
|
|
44
|
+
const staggered = surface.reveal === 'stagger';
|
|
45
|
+
const reveal = (surface.reveal && !staggered ? surface.reveal : undefined) as string | undefined;
|
|
46
|
+
const edge = (surface.edge || undefined) as string | undefined;
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Which block the page rendered as its first screen — and only that one is
|
|
50
|
+
* skipped here.
|
|
51
|
+
*
|
|
52
|
+
* Skipping *every* hero was how a document with two of them lost one: the page
|
|
53
|
+
* renders the first, this dropped both, and the second block's text reached no
|
|
54
|
+
* markup at all — so it carried no `data-jtk-path`, and `annotation-lint` failed
|
|
55
|
+
* the build on fields nobody could see were missing. The generator will not
|
|
56
|
+
* produce a second hero any more; a template that silently swallows one is still
|
|
57
|
+
* the wrong template.
|
|
58
|
+
*/
|
|
59
|
+
const rendered = PAGE.findIndex((of) => of.type === 'hero');
|
|
60
|
+
|
|
61
|
+
/** The band under the hero: the one that is in the first screen on a phone. */
|
|
62
|
+
const first = PAGE.findIndex((_, at) => at !== rendered);
|
|
63
|
+
const arrives = (at: number) => (at === first ? undefined : reveal);
|
|
64
|
+
|
|
65
|
+
const str = (of: Record<string, unknown>, key: string): string => (of[key] as string) ?? '';
|
|
66
|
+
const rows = (of: Record<string, unknown>, key: string): Record<string, unknown>[] =>
|
|
67
|
+
(of[key] as Record<string, unknown>[]) ?? [];
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
{
|
|
71
|
+
PAGE.map((of, at) => {
|
|
72
|
+
const type = of.type;
|
|
73
|
+
|
|
74
|
+
if (at === rendered) return null; // the first screen is the page's own
|
|
75
|
+
|
|
76
|
+
// A block that carries the form does not also render its own heading and
|
|
77
|
+
// button: `LeadForm` renders both, from this same block, and annotating a
|
|
78
|
+
// field twice means two elements claiming one path.
|
|
79
|
+
const carriesForm = of.form === true;
|
|
80
|
+
|
|
81
|
+
return (
|
|
82
|
+
<section
|
|
83
|
+
class={`band band--${type}`}
|
|
84
|
+
id={carriesForm || at === PAGE.length - 1 ? 'contact' : undefined}
|
|
85
|
+
data-reveal={arrives(at)}
|
|
86
|
+
>
|
|
87
|
+
<div class="band__inner">
|
|
88
|
+
{str(of, 'eyebrow') && (
|
|
89
|
+
<p class="eyebrow" data-jtk-path={pathAt(at, 'eyebrow')}>{str(of, 'eyebrow')}</p>
|
|
90
|
+
)}
|
|
91
|
+
{!carriesForm && str(of, 'title') && (
|
|
92
|
+
<h2 data-jtk-path={pathAt(at, 'title')}>{str(of, 'title')}</h2>
|
|
93
|
+
)}
|
|
94
|
+
{str(of, 'name') && (
|
|
95
|
+
<h2 data-jtk-path={pathAt(at, 'name')}>{str(of, 'name')}</h2>
|
|
96
|
+
)}
|
|
97
|
+
{str(of, 'role') && (
|
|
98
|
+
<p class="quiet" data-jtk-path={pathAt(at, 'role')}>{str(of, 'role')}</p>
|
|
99
|
+
)}
|
|
100
|
+
{!carriesForm && str(of, 'lead') && (
|
|
101
|
+
<p class="lead" data-jtk-path={pathAt(at, 'lead')}>{str(of, 'lead')}</p>
|
|
102
|
+
)}
|
|
103
|
+
{str(of, 'body') && (
|
|
104
|
+
<div class="prose" data-jtk-path={pathAt(at, 'body')} set:html={str(of, 'body')} />
|
|
105
|
+
)}
|
|
106
|
+
{str(of, 'bio') && (
|
|
107
|
+
<p class="prose" data-jtk-path={pathAt(at, 'bio')}>{str(of, 'bio')}</p>
|
|
108
|
+
)}
|
|
109
|
+
|
|
110
|
+
{rows(of, 'items').length > 0 && (
|
|
111
|
+
<ul class="items" data-reveal={staggered && at !== first ? 'stagger' : undefined}>
|
|
112
|
+
{rows(of, 'items').map((item, i) => (
|
|
113
|
+
<li class="item" data-edge={edge}>
|
|
114
|
+
{str(item, 'title') && (
|
|
115
|
+
<h3 data-jtk-path={pathAt(at, 'items', i, 'title')}>{str(item, 'title')}</h3>
|
|
116
|
+
)}
|
|
117
|
+
{str(item, 'question') && (
|
|
118
|
+
<h3 data-jtk-path={pathAt(at, 'items', i, 'question')}>{str(item, 'question')}</h3>
|
|
119
|
+
)}
|
|
120
|
+
{str(item, 'text') && (
|
|
121
|
+
<p data-jtk-path={pathAt(at, 'items', i, 'text')}>{str(item, 'text')}</p>
|
|
122
|
+
)}
|
|
123
|
+
{str(item, 'answer') && (
|
|
124
|
+
<p data-jtk-path={pathAt(at, 'items', i, 'answer')}>{str(item, 'answer')}</p>
|
|
125
|
+
)}
|
|
126
|
+
{str(item, 'label') && (
|
|
127
|
+
<p data-jtk-path={pathAt(at, 'items', i, 'label')}>{str(item, 'label')}</p>
|
|
128
|
+
)}
|
|
129
|
+
</li>
|
|
130
|
+
))}
|
|
131
|
+
</ul>
|
|
132
|
+
)}
|
|
133
|
+
|
|
134
|
+
{!carriesForm && str(of, 'cta_label') && (
|
|
135
|
+
<p class="actions">
|
|
136
|
+
<a
|
|
137
|
+
class="btn"
|
|
138
|
+
href={str(of, 'cta_href') || '#contact'}
|
|
139
|
+
data-jtk-path={pathAt(at, 'cta_label')}
|
|
140
|
+
>
|
|
141
|
+
{str(of, 'cta_label')}
|
|
142
|
+
</a>
|
|
143
|
+
</p>
|
|
144
|
+
)}
|
|
145
|
+
|
|
146
|
+
{carriesForm && <LeadForm />}
|
|
147
|
+
</div>
|
|
148
|
+
</section>
|
|
149
|
+
);
|
|
150
|
+
})
|
|
151
|
+
}
|