@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,75 @@
1
+ # The scaffold, and what the platform holds it to
2
+
3
+ ```bash
4
+ npx @jtakeit/astro create <slug> --name "The Business" --locale de-CH --vertical salon
5
+ ```
6
+
7
+ Copies `template/`, fills in the tokens, names the copy module for the locale,
8
+ writes a README and makes the first commit (`--no-git` to skip it). It decides
9
+ nothing about how the site looks: every component is a starting point, and the
10
+ contract below is the only thing the platform will hold the repository to.
11
+
12
+ ## The tokens
13
+
14
+ | token | example |
15
+ | --- | --- |
16
+ | `{{SLUG}}` | `duckit` — the directory and the package name |
17
+ | `{{NAME}}` | `DUCK IT` |
18
+ | `{{LOCALE}}` | `de-CH` — the copy module's name, `src/copy/de-CH.ts` |
19
+ | `{{LANG}}` | `de-CH` — `<html lang>` |
20
+ | `{{OG_LOCALE}}` | `de_CH` |
21
+ | `{{PHONE_REGION}}` | `CH` — the region `libphonenumber` parses the number in |
22
+ | `{{SCHEMA_TYPE}}` | `LocalBusiness`, or `--schema-type HairSalon` |
23
+ | `{{PREVIEW_URL}}` | `http://localhost:4321/` — where `astro dev` says it is; every build the platform makes passes `SITE_URL` |
24
+ | `{{UI_*}}` | the form's and the privacy page's sentences in the site's language |
25
+
26
+ An unreplaced `{{` anywhere in the tree is a scaffold bug; the package's smoke
27
+ test fails on one.
28
+
29
+ ## The `jtk/` contract
30
+
31
+ ```
32
+ jtk/catalogue.json what the owner may edit — derived from src/content/blocks.ts by `jtk catalogue`
33
+ jtk/content/*.json the values, one document per page — the panel's, written by a publish
34
+ jtk/bookings.json the diary's settings, when the catalogue turns bookings on — seed it here
35
+ jtk/design.json the design document the panel reads for the editor's chrome
36
+ ```
37
+
38
+ The repository decides what a site *is*; the panel fills in the values. A commit
39
+ under `jtk/` **is** the published state, and a publish writes these files back.
40
+ `import_branch` reads them into the panel after you commit; `publish` makes them
41
+ what the preview offers.
42
+
43
+ ## The build contract
44
+
45
+ The platform builds every commit in a container: `npm ci`, `npm run build`, the
46
+ annotation lint over `dist/`, upload. Three flags arrive as environment, read as
47
+ `import.meta.env`:
48
+
49
+ | flag | what |
50
+ | --- | --- |
51
+ | `SITE_URL` | the site's address, `https://<host>/` — the canonical and the sitemap follow it; under the preview it carries a path, `/p/<slug>/`, and `astro.config.mjs` derives `base` from it |
52
+ | `PUBLIC_INDEXABLE` | `true` once the site is launched; feeds the robots meta tag, `robots.txt` and the sitemap |
53
+ | `PUBLIC_JTK_ANNOTATE` | whether `data-jtk-path` is emitted — always on; the edge strips it for visitors |
54
+
55
+ `jtakeit-meta.mjs` writes `dist/_meta.json` beside the pages: the redirects and
56
+ the unfinished entries, which the edge cannot read off a page.
57
+
58
+ **The lockfile is pinned and committed.** `npm ci`, never `npm install`, in a
59
+ site: the platform's runner bakes the scaffold's `node_modules` so a build takes
60
+ twenty seconds rather than ninety, and a lockfile that drifted from the
61
+ scaffold's is a build that installs from scratch.
62
+
63
+ **There is no deploy.** The platform builds and serves; a site has no
64
+ `wrangler`, no Pages Functions, no server code of its own. The form posts to
65
+ `/api/lead` and the diary to `/api/availability`, `/api/book` and the rest, all
66
+ of which the platform answers beside the site ([lead-form.md](lead-form.md),
67
+ [booking.md](booking.md)).
68
+
69
+ ## Astro only
70
+
71
+ Today Astro is the only generator the platform supports: collecting a
72
+ collection's entries reads their frontmatter with the site's own Astro,
73
+ pictures go through `astro:assets`, the flags are read as `import.meta.env`,
74
+ and the runner's baked `node_modules` are this scaffold's. A repository on
75
+ another generator does not build on the platform.
package/docs/shapes.md ADDED
@@ -0,0 +1,140 @@
1
+ # The shapes a page can take
2
+
3
+ Three catalogues: how the first screen is built, how the page is built under it,
4
+ and how the type is paired. Pick one from each, deliberately, and **not the one
5
+ the last project used**.
6
+
7
+ This file exists because of a measurement. Three spec builds for three unrelated
8
+ businesses — a massage practice, a manual therapist, a children's clinic — came
9
+ out with the same hero, class for class:
10
+
11
+ ```
12
+ section.hero > .hero__body > p.eyebrow h1 p.hero__lead p.hero__actions > a.btn + a.link
13
+ + div.hero__media
14
+ ```
15
+
16
+ Different words, different palettes, different fonts, one page. Under it, all
17
+ three were a stack of full-bleed bands — one of them literally names its
18
+ sections `band band band band band band band band`. What varied was the skin and
19
+ one bespoke component each.
20
+
21
+ Nothing chose that. It is what you get when the only worked example in the
22
+ documentation is a split hero, and the alternative would have had to be invented
23
+ under time pressure. So the alternatives are written down here instead.
24
+
25
+ ## Hero anatomies
26
+
27
+ | | What it is | Wants | Costs |
28
+ | --- | --- | --- | --- |
29
+ | **split** | Text column beside a media column. | A photograph worth half the screen and a headline that needs room. | It is our default and everybody else's — using it needs a reason, not a shrug. |
30
+ | **overlay** | One full-bleed photograph; the words sit on it. | A genuinely good picture, and a scrim. | Contrast work in every frame, and the picture has to survive text across its middle. |
31
+ | **typographic** | No photograph at all above the fold. The headline is the image. | A sentence strong enough to carry a screen, and type that has been chosen rather than picked. | Nowhere to hide: if the words are ordinary, the page opens on ordinary. |
32
+ | **portrait-led** | The person is the hero and the words are its caption — inverted hierarchy, photograph dominant. | A soloist, and a frame of them worth looking at. | Fails badly on a weak portrait, which on the outbound track is common. |
33
+ | **masthead** | A journal's top: kicker, rules, a headline across the full width, one dense row of facts, no media in the first screen. | A business whose credibility is the argument — clinics, legal, technical trades. | Reads as cold where warmth is the product. |
34
+ | **object** | One thing — the work, the tool, the dish — centred and framed, text orbiting it. | A product, and a photograph that can be cut out or framed cleanly. | Needs a good silhouette; a busy background kills it. |
35
+ | **pile** | Four or five photographs laid over each other, each a few degrees off square, the words beside them. `src/components/Pile.astro`. | A feed rather than a photograph — several usable frames and no outstanding one. | Reads as a collage past five cards, and as a mess if the cards have no edge: the overlap needs a hairline, a shadow or a plate to read as layers. |
36
+
37
+ **`pile` is the answer to the row above it.** `portrait-led` fails on a weak
38
+ portrait, and on the outbound track that is the ordinary case: a scraped feed
39
+ often has no single frame that can carry a screen. A pile puts four on the
40
+ screen so that none of them has to — the strongest use we have for material
41
+ nobody would call good. `<Pile>` owns the arrangement because the fault it
42
+ prevents is specific: a rotated card whose corner leaves the viewport takes the
43
+ whole page sideways on a phone, which is what the site this was measured off
44
+ ships with (393px viewport, 404px of page).
45
+
46
+ Two rules across all seven. The one action stays inside the first screen unless
47
+ the anatomy makes that dishonest (a masthead may legitimately put it one line
48
+ below), and whatever the anatomy, `fl-check` still measures the fold, the wide
49
+ screen and the crop.
50
+
51
+ ## Scattered, then square
52
+
53
+ A tilt is worth something only against a straight edge.
54
+
55
+ The composition this comes from reads as *photographs at an angle that then line
56
+ up* — and there is no animation anywhere in it: the rotations are identical at
57
+ the top of the page and a thousand pixels down. It is a pile in the hero and a
58
+ grid under it, and the eye supplies the rest. That is the whole trick, and it
59
+ costs no script, no scroll timeline, and nothing at all for a visitor with
60
+ JavaScript off.
61
+
62
+ So: **one register of the page tilts and the rest is set square.** The pile, or
63
+ the one signature card, or the price list pinned to the wall — one of them, and
64
+ everything around it on a straight line. A page where three unrelated things sit
65
+ at three angles nobody chose does not read as casual, it reads as accidental.
66
+
67
+ The magnitude is `--tilt`, and it is a token because it has to be the same
68
+ number everywhere: ±3–6°, measured off the sites that do this well. Past about
69
+ 8° it stops being a hand and starts being a template. `data-tilt="left"` and
70
+ `data-tilt="right"` are in surface.css — they set `rotate` rather than
71
+ `transform`, so a tilted block can still be moved by an entrance.
72
+
73
+ ## Page forms
74
+
75
+ The hero gets attention and the rest of the page gets copied. All three builds
76
+ were a vertical stack of full-bleed bands with alternating backgrounds, which is
77
+ one form out of several.
78
+
79
+ - **band stack** — full-bleed sections, alternating grounds. Simple, safe, ours,
80
+ and recognisable from across the room as ours.
81
+ - **shell** — everything inside one measure, sections as panels with air between
82
+ them, nothing full-bleed except perhaps a single photograph. Reads as a
83
+ document rather than as a landing page.
84
+ - **two-rail** — one column scrolls, one stays: the photograph, the price card
85
+ or the contact details sit still while the argument moves past them. Wants a
86
+ wide screen and degrades to stacked on a phone.
87
+ - **single scene** — one environment holds the whole page — their room, their
88
+ wall, one continuous photograph — with the content floating over it in
89
+ panels. The most memorable and the most demanding of the material.
90
+ - **index** — the page is a list: services as rows, each with its own line and
91
+ price, like a menu or a treatment card. For a business whose customer already
92
+ knows what they want and is choosing *which*.
93
+ - **letter** — one column, one voice, photographs interleaved. A letter from the
94
+ person who does the work. Wants a real writer's material — the captions of
95
+ someone who explains their craft — and nothing else on the page.
96
+
97
+ ## Type pairings
98
+
99
+ Serif display over sans body is a good pairing and it was used in all three
100
+ builds. It is one of six.
101
+
102
+ - **serif display + sans body** — the default. Warm, editorial, safe.
103
+ - **one grotesk** — a single family, contrast made from weight and size only.
104
+ Confident and modern; nowhere to hide a weak layout.
105
+ - **all serif** — display serif over a text serif. Slow, human, expensive-looking.
106
+ - **mono or technical display + humanist sans** — for trades that measure things.
107
+ - **condensed display + wide body** — the tension is the point; strong for
108
+ posters and for food.
109
+ - **system stack, deliberately, with one bought display face** — the fastest
110
+ page in the set, and it only works if the display face is genuinely good.
111
+
112
+ Whatever the pairing: two families at most, both loaded from the same host,
113
+ preconnect set. A third family is nearly always why mobile Lighthouse is at 84.
114
+
115
+ ## And what it is printed on
116
+
117
+ Three catalogues decide what the page *is*. Three more decide what it is made
118
+ of — the ground under the content, the edge its blocks are drawn with, and how
119
+ they arrive — and they are in [surface.md](surface.md), with the same rule
120
+ attached: not the one the last project used. A shape and a surface chosen
121
+ together is a direction; either one alone is a skin.
122
+
123
+ ## Recording the choice
124
+
125
+
126
+ ## The one thing that cannot be reused
127
+
128
+ A catalogue produces variety, not identity: seven anatomies over fifty projects is
129
+ still eight sites that look like each other. So one element on every page has to
130
+ be **impossible to move to another site** — made from this business's own
131
+ material rather than from a component.
132
+
133
+ Their handwriting off a caption, set as a heading. A photograph of their actual
134
+ price card. The stripe of colour taken from their wall. Their sign, photographed
135
+ and used as the wordmark. The tool they hold in every third post, cut out and
136
+ used as the bullet.
137
+
138
+ That is the difference between a page that is different and a page that is
139
+ theirs, and it is the answer to "why does this not look like a template" that a
140
+ client can see in one second.
@@ -0,0 +1,187 @@
1
+ # The surface: grounds, edges and entrances
2
+
3
+ Three catalogues, and one decision. The hero is where a page spends its loud
4
+ moment — [hero-motion.md](hero-motion.md) is that. This is everything under it:
5
+ the paper the page is printed on, the edge a block is drawn with, and how a
6
+ block arrives when it is scrolled to.
7
+
8
+ They are written down together because they are chosen together, and because
9
+ each of them is a place where a page quietly acquires somebody else's taste. A
10
+ ground picked because it was the example in this file is how fifty sites end up
11
+ sharing a background.
12
+
13
+ ```jsonc
14
+ // jtk/design.json — the surface, beside the palette
15
+ "surface": {
16
+ "pattern": "rules", // the ground. "" is none, and none is a choice
17
+ "scope": "page", // one layer behind the document, or one per section
18
+ "opacity": 0.06, // the volume knob. fl-check measures this one
19
+ "draw": false, // a drawn ground sets itself out once
20
+ "edge": "corners", // how a block is drawn
21
+ "reveal": "settle" // how a block arrives
22
+ }
23
+ ```
24
+
25
+ Everything is a document rather than props typed into a page, for the same
26
+ reason the copy is: it can be changed without an agent, and the studio bar's
27
+ live accent drives it.
28
+
29
+ ## The grounds
30
+
31
+ `src/components/surface/Pattern.astro`, figures in `patterns.ts` beside it.
32
+ Six, and they are not six skins of one look — each does something the others
33
+ cannot.
34
+
35
+ | `pattern` | What it is | Wants | Costs |
36
+ | --- | --- | --- | --- |
37
+ | **none** | The token ground, and nothing else. | A page carrying strong photographs, where anything behind them is noise. | Nothing. It is chosen by leaving `pattern` empty, and it is a real choice — say it out loud rather than defaulting into it. |
38
+ | `grain` | Film grain, paper fibre, plaster. No figure at all: its whole job is that a flat token stops reading as a fill. | Any page whose grounds are large and plain. It is the one that goes with everything. | Turned up, it is a grey wash rather than a texture. Under 0.1. |
39
+ | `rules` | The ruled plane: ledger paper, a form, a chart. | Trades that measure — clinics, legal, technical, anything whose credibility is the argument. | Reads as cold where warmth is the product. |
40
+ | `hatch` | Engraved hatching, one direction, the way a burin goes. | Print, craft, food, barbers, workshops. | On a busy page it fights the photographs. |
41
+ | `contour` | Contour lines, the way a landscape is drawn. | Bodies and calm: massage, wellness, anything organic. | Needs height to read — in a 180px band you see two lines and a shrug. |
42
+ | `construction` | Setting-out: axes, circles, dotted guides. The register of a studio that shows its process. | Studios, ateliers, anywhere the work is watched being made. | It is the loudest of the six, and it is not anybody's signature — see the last section. |
43
+ | `motif` | The client's own mark, tiled: their tile, their fabric, their stamp, the shape off their sign. | Anywhere that mark exists. | **No default.** It does not build without the figure, and the figure is not in this repository. |
44
+
45
+ Two mechanisms, because they do different work. `grain`, `rules` and `hatch`
46
+ are **stencils** — a repeating figure with no beginning and no end, drawn as a
47
+ CSS mask, no markup and no bytes past `patterns.ts`. `contour`, `construction`
48
+ and `motif` are **drawings** — one composition that bleeds off its box, in SVG,
49
+ because a figure has a size and a place and because strokes can draw themselves.
50
+
51
+ ### It is in their ink, or it is somebody else's page
52
+
53
+ **A ground is a stencil and the ink is a token.** Nothing in `patterns.ts` names
54
+ a colour. A stencil is an alpha mask over a layer painted in `--pattern-ink`; a
55
+ drawing is line art in `currentColor`, which is the same token. So a variant
56
+ that changes its palette repaints its ground with it, the live accent picker
57
+ drives it, and a ground can never arrive in a colour somebody else chose — which
58
+ is the failure a shipped background image makes inevitable.
59
+
60
+ The volume is `--pattern-opacity`, and it is the property most likely to be set
61
+ too high. A ground is read at a glance and never read *at*. `fl-check` composites
62
+ the ink over the paper and fails the page above 1.6:1, because "is the texture
63
+ too strong" is a question two people answer differently on two monitors and the
64
+ contrast between figure and paper is a number.
65
+
66
+ ### Where the layer goes
67
+
68
+ `scope="page"` pins one fixed layer behind the whole document — one composited
69
+ surface, no repaint on scroll. It is only visible through sections that let it
70
+ through: on a stack of full-bleed bands with their own backgrounds, half of them
71
+ cover it. Use `scope="block"` there, on the sections that should carry it, and
72
+ give each of those `class="has-pattern"` — two properties that make the section a
73
+ stacking context, without which the layer disappears behind the band's own
74
+ colour and looks exactly like a ground nobody added.
75
+
76
+ ### Every way it fails ends at the paper
77
+
78
+ No JavaScript, no mask support, a printer, reduced motion: all of them end at
79
+ the plain token ground, which is a design rather than a hole. Nothing readable is
80
+ ever inside the layer, nothing is positioned against it, it is `aria-hidden`, and
81
+ it is never the LCP element. A drawn ground with `draw` sets itself out once and
82
+ stops; reduced motion gets the finished drawing, never a missing one.
83
+
84
+ Unlike a hero field this costs no canvas and no script, which is why it is also
85
+ the answer where WebGL is not available, and why a still of the page shows
86
+ exactly what a visitor sees.
87
+
88
+ ### And the photographs on it
89
+
90
+ A ground and the photographs printed on it are one decision. The fourth
91
+ catalogue lives in [photos.md](photos.md) — six treatments, chosen once for the
92
+ site in the same document — and the pairs that belong together are obvious once
93
+ both are on the table: `grain` with `film`, `riso` or `hatch` with `press`,
94
+ `construction` with an untouched photograph that has something to say.
95
+
96
+ The rule there is the same shape as the one here: **one treatment per site**, and
97
+ never a strong one on their face.
98
+
99
+ ## The edges
100
+
101
+ Attributes, not wrappers: `<article class="card" data-edge="corners">`. Delete
102
+ the attribute and the block is the block. Drawn on a pseudo-element rather than
103
+ as a border, so adding an edge never changes a block's size and never fights the
104
+ padding a design already settled.
105
+
106
+ | `edge` | What it is |
107
+ | --- | --- |
108
+ | `none` | The edge is where the block's ground stops. |
109
+ | `hairline` | One line. Everything else here is a departure from it. |
110
+ | `stitch` | Dashed: sewn, ruled, provisional. |
111
+ | `corners` | Crop marks — the corners drawn, the sides not. Registered rather than boxed. |
112
+ | `notch` | A cut corner: a ticket, a label, a price tag. On the box itself, so the block needs a ground of its own to cut. |
113
+ | `offset` | Two frames, one offset from the other, the outer in the accent. A poster's register. |
114
+ | `trace` | The perimeter draws itself once, clockwise from the top, as the block arrives. |
115
+ | `sweep` | A light travelling around the edge, forever. |
116
+ | `lift` | The block answers the hand: shadow and a couple of pixels on hover and focus. |
117
+
118
+ Two rules, and `fl-check` holds the page to both:
119
+
120
+ - **An animated edge belongs to something that can be pressed.** Furniture that
121
+ performs is furniture being looked at instead of the words.
122
+ - **One thing on a page moves forever**, and the hero has usually already spent
123
+ it. `sweep` on six cards is not a design, it is a component gallery.
124
+
125
+ `offset` draws its outer frame as an `outline`, which is also how a focus ring
126
+ is drawn — keep it on blocks rather than on anything focusable.
127
+
128
+ ## The entrances
129
+
130
+ Also attributes: `data-reveal`. The hidden start state hangs on `html.js`, which
131
+ the layout sets before first paint, so a visitor with JavaScript off gets the
132
+ finished page rather than a column of invisible sections. The observer that adds
133
+ `.is-in` is in `Layout.astro`; it watches each block once and never again.
134
+
135
+ | `reveal` | What it is | Wants |
136
+ | --- | --- | --- |
137
+ | `none` | The page is already there. | Short pages, and any page where the entrance was the only idea. |
138
+ | `rise` | Up and in. The default, and the one every site has. | A page with a handful of large blocks. |
139
+ | `settle` | Fades and settles a little closer, with no travel. | A dense page: travel makes a long column feel like it is bouncing. |
140
+ | `wipe` | Uncovered from one edge rather than moved. | A graphic page, a print register, anything already linear. |
141
+ | `develop` | Out of focus, then not. Photographic. | Pages carrying photographs — put it on the picture, not on the section around it. |
142
+ | `stagger` | The children in order. `--i` is set by the observer, so a list rendered from a document does not carry an index into its markup. | Grids, price lists, an `index` page form. |
143
+ | `draw` | Line art draws itself. Every figure needs `pathLength="1"`, and then one rule covers a circle, a line and a 56-point contour alike. | A page whose signature is drawn rather than photographed. |
144
+
145
+ Three rules:
146
+
147
+ - **One entrance per page.** Two is defensible where the second is on the
148
+ photographs. Three is a component gallery, and `fl-check` fails it.
149
+ - **Nothing in the first screen is revealed.** It delays the largest paint and
150
+ shows a visitor on a slow line an empty page. Reveal what is scrolled to,
151
+ never what is landed on.
152
+ - **It never replays.** A block that animates again on the way back up is what
153
+ clients report as "it keeps flashing".
154
+
155
+ `Blocks.astro` gives the band immediately under the hero no entrance at all, for
156
+ the second of those rules: on a phone, and on any page whose hero is not a full
157
+ screen, that band is *in* the first screen. A design that replaces the component
158
+ keeps the exception — or gives the hero the height that makes it unnecessary.
159
+
160
+ Reduced motion means the finished state everywhere: the block in place, the
161
+ border closed, the drawing complete.
162
+
163
+ **`hold` is not in this list on purpose.** A column that stays while the argument
164
+ scrolls past it is `position: sticky` — it is the `two-rail` page form in
165
+ [shapes.md](shapes.md), a layout decision, not an entrance. Putting it here
166
+ would let a page pick it instead of choosing a shape.
167
+
168
+ ## Recording the choice
169
+
170
+
171
+ ## A catalogue is not an identity
172
+
173
+ This file makes it cheap to look designed, and that is exactly its risk: the
174
+ easier a page is to finish, the less likely anybody makes the one element that
175
+ could not be moved to another site.
176
+
177
+ `construction` is the case to be careful with. It exists because a build had its
178
+ own construction geometry — the guides one tattooist leaves in red inside her own
179
+ work, redrawn as the furniture of her page — and that was the best thing on the
180
+ site. What is in `patterns.ts` is the generic article, not her drawing. Using it
181
+ buys a register; it does not buy a signature.
182
+
183
+ `motif` is the entry that does. It has no default, it does not build without the
184
+ figure, and the figure is theirs: their tile, their stamp, their sign, their
185
+ handwriting. If a page's whole visual identity is three values out of these
186
+ tables, it is a page that is different from the last one rather than a page that
187
+ is theirs — see the last section of [shapes.md](shapes.md).