@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
package/docs/scaffold.md
ADDED
|
@@ -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.
|
package/docs/surface.md
ADDED
|
@@ -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).
|