@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.
Files changed (68) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +59 -0
  3. package/bin/jtk.mjs +41 -0
  4. package/docs/booking.md +164 -0
  5. package/docs/catalogue.md +459 -0
  6. package/docs/collections.md +249 -0
  7. package/docs/css.md +86 -0
  8. package/docs/gallery.md +127 -0
  9. package/docs/hero-motion.md +189 -0
  10. package/docs/kit.md +454 -0
  11. package/docs/languages.md +182 -0
  12. package/docs/lead-form.md +109 -0
  13. package/docs/pages.md +193 -0
  14. package/docs/photos.md +314 -0
  15. package/docs/scaffold.md +75 -0
  16. package/docs/shapes.md +140 -0
  17. package/docs/surface.md +187 -0
  18. package/lib/catalogue.mjs +1678 -0
  19. package/lib/codes.mjs +171 -0
  20. package/lib/create.mjs +282 -0
  21. package/package.json +16 -0
  22. package/template/astro.config.mjs +84 -0
  23. package/template/figures.mjs +122 -0
  24. package/template/gitignore +16 -0
  25. package/template/jtakeit-meta.mjs +112 -0
  26. package/template/jtk/content/index.json +38 -0
  27. package/template/jtk/design.json +24 -0
  28. package/template/markdown.mjs +36 -0
  29. package/template/package-lock.json +5320 -0
  30. package/template/package.json +26 -0
  31. package/template/specimens.mjs +46 -0
  32. package/template/src/components/Blocks.astro +151 -0
  33. package/template/src/components/BookingForm.astro +506 -0
  34. package/template/src/components/Clip.astro +155 -0
  35. package/template/src/components/Hero.astro +66 -0
  36. package/template/src/components/LeadForm.astro +347 -0
  37. package/template/src/components/OpeningHours.astro +69 -0
  38. package/template/src/components/Pile.astro +185 -0
  39. package/template/src/components/Shot.astro +472 -0
  40. package/template/src/components/gallery/Gallery.astro +381 -0
  41. package/template/src/components/gallery/galleries.ts +139 -0
  42. package/template/src/components/motion/HeroField.astro +520 -0
  43. package/template/src/components/motion/fields.ts +430 -0
  44. package/template/src/components/surface/Pattern.astro +278 -0
  45. package/template/src/components/surface/patterns.ts +187 -0
  46. package/template/src/content/blocks.ts +758 -0
  47. package/template/src/content.config.ts +19 -0
  48. package/template/src/copy/LOCALE.ts +324 -0
  49. package/template/src/data/site.ts +137 -0
  50. package/template/src/layouts/Layout.astro +282 -0
  51. package/template/src/lib/alive.ts +49 -0
  52. package/template/src/lib/entries.ts +106 -0
  53. package/template/src/lib/entryLoader.ts +315 -0
  54. package/template/src/lib/noise.ts +26 -0
  55. package/template/src/lib/page.ts +287 -0
  56. package/template/src/lib/photos.ts +168 -0
  57. package/template/src/lib/under.ts +32 -0
  58. package/template/src/lib/uploads.ts +85 -0
  59. package/template/src/pages/[...entry].astro +207 -0
  60. package/template/src/pages/[...feed].xml.ts +64 -0
  61. package/template/src/pages/index.astro +90 -0
  62. package/template/src/pages/llms.txt.ts +50 -0
  63. package/template/src/pages/privacy.astro +59 -0
  64. package/template/src/pages/robots.txt.ts +21 -0
  65. package/template/src/pages/sitemap.xml.ts +50 -0
  66. package/template/src/styles/global.css +411 -0
  67. package/template/src/styles/surface.css +375 -0
  68. package/template/tsconfig.json +5 -0
@@ -0,0 +1,375 @@
1
+ /*
2
+ * Edges and entrances.
3
+ *
4
+ * Two of the three catalogues in `docs/surface.md` of @jtakeit/astro.
5
+ * The third — the ground a page is printed on — is a component, because it is
6
+ * a figure with a size and a place; these two are properties of blocks that
7
+ * already exist, so they are attributes rather than wrappers:
8
+ *
9
+ * <section class="band" data-edge="corners" data-reveal="wipe">
10
+ *
11
+ * Which is also why they degrade well. Delete the attribute and the block is a
12
+ * block; delete this file and the page still reads. Nothing here changes what a
13
+ * section *is*.
14
+ *
15
+ * ── the rules that stop this becoming a demo ────────────────────────────────
16
+ *
17
+ * · One reveal per page. Four different entrances is a component gallery, not
18
+ * a design, and it is the loudest tell that nobody chose.
19
+ * · Nothing in the first screen is revealed. It delays the largest paint and
20
+ * it shows a visitor on a slow line an empty page. `fl-check` fails on it.
21
+ * · An animated edge belongs to something that can be clicked. Furniture that
22
+ * performs is furniture being looked at instead of the words.
23
+ * · One continuously moving thing per page, and the hero usually already
24
+ * spent it.
25
+ *
26
+ * Every hidden start state hangs on `html.js`, which the layout sets before
27
+ * first paint — otherwise a visitor with JavaScript off is left staring at
28
+ * `opacity: 0` forever. `.is-in` is added by the observer in Layout.astro.
29
+ */
30
+
31
+ /* ── the ground's host ───────────────────────────────────────────────────────
32
+ *
33
+ * A block-scope <Pattern> sits at z-index -1: above this section's background,
34
+ * below its content. That only holds where the section is a stacking context,
35
+ * and these are the two properties that make it one. Without them the ground
36
+ * disappears behind the band's own colour, which looks exactly like a ground
37
+ * that was never added.
38
+ */
39
+ .has-pattern {
40
+ position: relative;
41
+ isolation: isolate;
42
+ }
43
+
44
+ /* ── the hand ────────────────────────────────────────────────────────────────
45
+ *
46
+ * One element, laid down slightly off square. `--tilt` is the magnitude and it
47
+ * is the same everywhere on the page; this only chooses the direction.
48
+ *
49
+ * <figure class="shot" data-tilt="left">
50
+ *
51
+ * ── two things worth knowing ────────────────────────────────────────────────
52
+ *
53
+ * It is the `rotate` property, not `transform: rotate()`. They are separate
54
+ * properties now, which means a tilted element can still be moved by an
55
+ * entrance — `data-tilt="left" data-reveal="rise"` works, where a transform
56
+ * would have overwritten the other one and the block would arrive straight.
57
+ *
58
+ * And the rule that makes it worth anything: **the tilt is the exception.** A
59
+ * page tilts its pile, or its one signature card, and sets everything else
60
+ * square — the hand reads because the rest is a straight edge to read it
61
+ * against. See references/shapes.md.
62
+ */
63
+
64
+ [data-tilt='left'] {
65
+ rotate: calc(var(--tilt) * -1);
66
+ }
67
+
68
+ [data-tilt='right'] {
69
+ rotate: var(--tilt);
70
+ }
71
+
72
+ /* ── edges ───────────────────────────────────────────────────────────────────
73
+ *
74
+ * Drawn on a pseudo-element rather than as a border, so that adding an edge
75
+ * never changes a block's size and never fights the padding a design already
76
+ * settled. `notch` is the exception — a cut corner is the box, not a line on
77
+ * it.
78
+ */
79
+
80
+ [data-edge] {
81
+ position: relative;
82
+ }
83
+
84
+ [data-edge]::after {
85
+ content: '';
86
+ position: absolute;
87
+ inset: 0;
88
+ border-radius: inherit;
89
+ pointer-events: none;
90
+ }
91
+
92
+ /* The hairline. Everything else on this list is a departure from it. */
93
+ [data-edge='hairline']::after {
94
+ border: var(--edge-weight) solid var(--edge-ink);
95
+ }
96
+
97
+ /* Sewn, ruled, provisional. */
98
+ [data-edge='stitch']::after {
99
+ border: var(--edge-weight) dashed var(--edge-ink);
100
+ }
101
+
102
+ /*
103
+ * Crop marks: the corners are drawn and the sides are not, so the block reads
104
+ * as registered rather than as boxed. The mask is four squares, one per corner,
105
+ * each revealing the two border runs that meet there.
106
+ */
107
+ [data-edge='corners']::after {
108
+ border: var(--edge-weight) solid var(--edge-ink);
109
+ -webkit-mask-image: linear-gradient(#000 0 0), linear-gradient(#000 0 0), linear-gradient(#000 0 0),
110
+ linear-gradient(#000 0 0);
111
+ mask-image: linear-gradient(#000 0 0), linear-gradient(#000 0 0), linear-gradient(#000 0 0),
112
+ linear-gradient(#000 0 0);
113
+ -webkit-mask-size: var(--edge-corner) var(--edge-corner);
114
+ mask-size: var(--edge-corner) var(--edge-corner);
115
+ -webkit-mask-position: top left, top right, bottom left, bottom right;
116
+ mask-position: top left, top right, bottom left, bottom right;
117
+ -webkit-mask-repeat: no-repeat;
118
+ mask-repeat: no-repeat;
119
+ }
120
+
121
+ /*
122
+ * A ticket, a label, a price tag. The cut is on the box itself, so a block with
123
+ * a background of its own is the one to put it on — on a transparent block
124
+ * there is nothing to cut.
125
+ */
126
+ [data-edge='notch'] {
127
+ clip-path: polygon(
128
+ 0 0,
129
+ calc(100% - var(--edge-corner)) 0,
130
+ 100% var(--edge-corner),
131
+ 100% 100%,
132
+ 0 100%
133
+ );
134
+ }
135
+
136
+ [data-edge='notch']::after {
137
+ display: none;
138
+ }
139
+
140
+ /*
141
+ * Two frames, one offset from the other: a poster's register. The outer one is
142
+ * an outline, which sits outside the box without taking space — and which is
143
+ * why this belongs on blocks rather than on anything focusable, whose own focus
144
+ * ring is drawn the same way.
145
+ */
146
+ [data-edge='offset'] {
147
+ outline: var(--edge-weight) solid var(--accent);
148
+ outline-offset: var(--edge-offset);
149
+ }
150
+
151
+ [data-edge='offset']::after {
152
+ border: var(--edge-weight) solid var(--edge-ink);
153
+ }
154
+
155
+ /*
156
+ * The perimeter draws itself once, clockwise from the top. A conic gradient as
157
+ * a mask is the whole trick: one angle animated from nothing to a full turn
158
+ * reveals the border in the order a pen would draw it.
159
+ *
160
+ * Without JavaScript the border is simply there — the hidden state is under
161
+ * `html.js`, and the finished state is the default.
162
+ */
163
+ @property --edge-p {
164
+ syntax: '<number>';
165
+ inherits: false;
166
+ initial-value: 1;
167
+ }
168
+
169
+ [data-edge='trace']::after {
170
+ border: var(--edge-weight) solid var(--edge-ink);
171
+ }
172
+
173
+ html.js [data-edge='trace']::after {
174
+ --edge-p: 0;
175
+ -webkit-mask-image: conic-gradient(#000 calc(var(--edge-p) * 1turn), transparent 0);
176
+ mask-image: conic-gradient(#000 calc(var(--edge-p) * 1turn), transparent 0);
177
+ }
178
+
179
+ html.js [data-edge='trace'].is-in::after {
180
+ animation: edge-trace 0.9s var(--ease) forwards;
181
+ }
182
+
183
+ @keyframes edge-trace {
184
+ to {
185
+ --edge-p: 1;
186
+ }
187
+ }
188
+
189
+ /*
190
+ * A light travelling around the edge. This is the loud one: it never stops, so
191
+ * it is the page's single continuous animation and it belongs on the one thing
192
+ * a visitor is supposed to press. `fl-check` counts them and checks what they
193
+ * are attached to.
194
+ *
195
+ * The ring is a conic gradient with the middle masked out — `exclude` leaves
196
+ * only the padding, which is the border. Under the light there is a plain ring
197
+ * in the edge colour: without it the block has no edge for three quarters of
198
+ * every turn, which reads as a border that keeps breaking.
199
+ */
200
+ @property --edge-a {
201
+ syntax: '<angle>';
202
+ inherits: false;
203
+ initial-value: 0deg;
204
+ }
205
+
206
+ [data-edge='sweep']::after {
207
+ padding: var(--edge-weight);
208
+ background-color: var(--edge-ink);
209
+ background-image: conic-gradient(
210
+ from var(--edge-a),
211
+ transparent 0 52%,
212
+ var(--accent) 76%,
213
+ transparent 90% 100%
214
+ );
215
+ -webkit-mask-image: linear-gradient(#000 0 0), linear-gradient(#000 0 0);
216
+ mask-image: linear-gradient(#000 0 0), linear-gradient(#000 0 0);
217
+ -webkit-mask-clip: content-box, border-box;
218
+ mask-clip: content-box, border-box;
219
+ -webkit-mask-composite: xor;
220
+ mask-composite: exclude;
221
+ animation: edge-sweep 4.5s linear infinite;
222
+ }
223
+
224
+ @keyframes edge-sweep {
225
+ to {
226
+ --edge-a: 360deg;
227
+ }
228
+ }
229
+
230
+ /*
231
+ * The card answers the hand. `focus-within` as well as hover, or the same card
232
+ * is dead to a keyboard.
233
+ */
234
+ [data-edge='lift'] {
235
+ transition:
236
+ box-shadow var(--dur) var(--ease),
237
+ transform var(--dur) var(--ease);
238
+ }
239
+
240
+ [data-edge='lift']:hover,
241
+ [data-edge='lift']:focus-within {
242
+ box-shadow: var(--shadow-2, 0 12px 34px rgb(0 0 0 / 12%));
243
+ transform: translateY(-2px);
244
+ }
245
+
246
+ [data-edge='none']::after {
247
+ display: none;
248
+ }
249
+
250
+ /* ── entrances ───────────────────────────────────────────────────────────────
251
+ *
252
+ * One per page. They are not seven flavours of fade: a page of photographs
253
+ * wants `develop`, a list wants `stagger`, a dense page wants `settle` because
254
+ * travel makes a long column feel like it is bouncing.
255
+ *
256
+ * `data-reveal` with no value is `rise`, so the plain attribute keeps working
257
+ * wherever it was already written.
258
+ */
259
+
260
+ html.js [data-reveal=''],
261
+ html.js [data-reveal='rise'] {
262
+ opacity: 0;
263
+ transform: translateY(var(--reveal-dist));
264
+ transition:
265
+ opacity var(--reveal-dur) var(--ease),
266
+ transform var(--reveal-dur) var(--ease);
267
+ }
268
+
269
+ /* No travel: it arrives where it already was. For pages with a lot of blocks. */
270
+ html.js [data-reveal='settle'] {
271
+ opacity: 0;
272
+ transform: scale(1.015);
273
+ transition:
274
+ opacity var(--reveal-dur) var(--ease),
275
+ transform var(--reveal-dur) var(--ease);
276
+ }
277
+
278
+ /* A print register: the block is uncovered rather than moved. */
279
+ html.js [data-reveal='wipe'] {
280
+ clip-path: inset(0 100% 0 0);
281
+ transition: clip-path calc(var(--reveal-dur) * 1.2) var(--ease);
282
+ }
283
+
284
+ /* Photographic: out of focus, then not. Expensive on a large box — put it on
285
+ the picture, not on the section around it. */
286
+ html.js [data-reveal='develop'] {
287
+ opacity: 0;
288
+ filter: blur(12px) saturate(0.6);
289
+ transition:
290
+ opacity var(--reveal-dur) var(--ease),
291
+ filter var(--reveal-dur) var(--ease);
292
+ }
293
+
294
+ /*
295
+ * The children, in order. `--i` is set by the observer as the row enters, so a
296
+ * list rendered from a document does not have to carry an index into its
297
+ * markup — which is the version of this that gets forgotten on the second list.
298
+ */
299
+ html.js [data-reveal='stagger'] > * {
300
+ opacity: 0;
301
+ transform: translateY(var(--reveal-dist));
302
+ transition:
303
+ opacity var(--reveal-dur) var(--ease),
304
+ transform var(--reveal-dur) var(--ease);
305
+ transition-delay: calc(var(--i, 0) * var(--reveal-step));
306
+ }
307
+
308
+ html.js [data-reveal='stagger'].is-in > * {
309
+ opacity: 1;
310
+ transform: none;
311
+ }
312
+
313
+ /*
314
+ * Line art draws itself. Every figure needs `pathLength="1"`, and then one rule
315
+ * covers a circle, a line and a 56-point contour alike — nobody measures a path
316
+ * by hand, and a figure edited next month still draws.
317
+ */
318
+ html.js [data-reveal='draw'] :is(path, line, circle, rect, polyline, ellipse) {
319
+ stroke-dasharray: 1;
320
+ stroke-dashoffset: 1;
321
+ }
322
+
323
+ html.js [data-reveal='draw'].is-in :is(path, line, circle, rect, polyline, ellipse) {
324
+ animation: reveal-draw 1.4s var(--ease) forwards;
325
+ animation-delay: calc(var(--i, 0) * 180ms);
326
+ }
327
+
328
+ @keyframes reveal-draw {
329
+ to {
330
+ stroke-dashoffset: 0;
331
+ }
332
+ }
333
+
334
+ html.js [data-reveal=''].is-in,
335
+ html.js [data-reveal='rise'].is-in,
336
+ html.js [data-reveal='settle'].is-in,
337
+ html.js [data-reveal='develop'].is-in {
338
+ opacity: 1;
339
+ transform: none;
340
+ filter: none;
341
+ }
342
+
343
+ html.js [data-reveal='wipe'].is-in {
344
+ clip-path: inset(0 0 0 0);
345
+ }
346
+
347
+ /*
348
+ * Reduced motion means the finished state, never a broken one — and never a
349
+ * missing one either: the drawing is complete, the block is in place, the
350
+ * border is closed.
351
+ */
352
+ @media (prefers-reduced-motion: reduce) {
353
+ html.js [data-reveal],
354
+ html.js [data-reveal] > * {
355
+ opacity: 1;
356
+ transform: none;
357
+ filter: none;
358
+ clip-path: none;
359
+ transition: none;
360
+ }
361
+
362
+ html.js [data-reveal='draw'] :is(path, line, circle, rect, polyline, ellipse) {
363
+ stroke-dashoffset: 0;
364
+ animation: none;
365
+ }
366
+
367
+ html.js [data-edge='trace']::after {
368
+ --edge-p: 1;
369
+ animation: none;
370
+ }
371
+
372
+ [data-edge='sweep']::after {
373
+ animation: none;
374
+ }
375
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "extends": "astro/tsconfigs/strict",
3
+ "include": [".astro/types.d.ts", "**/*"],
4
+ "exclude": ["dist", "functions"]
5
+ }