rastrillo / fiesta Public

The design system's new words, reviewed

Paul approved all 25 strings of the copy review unchanged (task-1-copy-result.json, 25 approved, none edited), and this applies them where each belongs.

The 21 gallery.* strings join copy.go's text table under their reviewed ids, grouped by the page that uses them: the Overview's intro and its routes into Getting started and the Icons page, the demo section's callout, Getting started's lead, section bodies, headings and the blurbs for Fiesta's own files, and the Icons page's two sections. The Icons row in pageKinds now routes through gallery.routes.icons instead of standing in with the page's own lead.

The password screen's warning becomes a callout of its own in examples/prose.json: examples.signin.warnings.email-and-password carries the title and body, and the state's note keeps its first sentence. Rather than editing prose.json by hand, the prose exporter learnt the shape: a screen's blurb is the state's note, and its WarningTitle and WarningBody are slots of their own. The title and the note carry verbatim from Rastrillo at the seed commit; the body is queued (it names Rastrillo) and approvedProse carries the reviewed replacement, so hack/seed/run-prose.sh reproduces the file. Run twice against e14b745a: the output is byte-identical both times, and the README's checksums are updated to it. prose-review.json follows: the joined note leaves the queue and the warning body joins it.

hack/seed/README.md gains the paragraph on run-translations.sh, after the one on run-prose.sh.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
claude agent, operated by paul@keymail.dev 8b86380d5db6fc164ed2389d9a7ea1b8b4a4e2c7 parent 908bc6b
6 files changed, +74 −19
  • examples/prose.json +7 −1
  • hack/seed/README.md +4 −2
  • hack/seed/_rastrillo/prose_fiestaseed_test.go +31 −9
  • hack/seed/prose-review.json +6 −6
  • internal/gallery/copy.go +25 −0
  • internal/gallery/pages.go +1 −1

CI passed — run details

CI log
=== gofmt ===
=== no-rastrillo ===
go test -run '^(TestFiestaNeverImportsRastrillo|TestThePublicPackagesLinkNoBrowser)$' -count=1 .
ok  	amadan.net/rastrillo/fiesta	0.098s
=== size ===
go test ./internal/build -run '^TestTheOneLinkIsUnder25KiBGzipped$' -count=1 -v
=== RUN   TestTheOneLinkIsUnder25KiBGzipped
    build_test.go:138: fiesta-plain.css: 152856 bytes, 22303 bytes gzipped (21.8 KiB of the 25.0 KiB limit)
    build_test.go:138: fiesta-signal.css: 152951 bytes, 22414 bytes gzipped (21.9 KiB of the 25.0 KiB limit)
    build_test.go:138: fiesta.css: 155770 bytes, 23018 bytes gzipped (22.5 KiB of the 25.0 KiB limit)
--- PASS: TestTheOneLinkIsUnder25KiBGzipped (0.02s)
PASS
ok  	amadan.net/rastrillo/fiesta/internal/build	0.019s
=== suite ===
--- shard 0 of 12 ---
==> shard 0 of 12: make root
go build ./...
go vet ./...
FIESTA_TEST_REQUIRE_NODE=1 FIESTA_TEST_REQUIRE_GIT=1 go test -count=1 ./...
ok  	amadan.net/rastrillo/fiesta	1.594s
ok  	amadan.net/rastrillo/fiesta/check	5.274s
ok  	amadan.net/rastrillo/fiesta/cmd/fiesta	39.158s
ok  	amadan.net/rastrillo/fiesta/example	0.191s
ok  	amadan.net/rastrillo/fiesta/internal/axe	0.124s
ok  	amadan.net/rastrillo/fiesta/internal/build	1.050s
ok  	amadan.net/rastrillo/fiesta/internal/codeview	0.020s
ok  	amadan.net/rastrillo/fiesta/internal/gallery	23.509s
?   	amadan.net/rastrillo/fiesta/internal/harness	[no test files]
ok  	amadan.net/rastrillo/fiesta/internal/markup	0.014s
ok  	amadan.net/rastrillo/fiesta/internal/nodetest	0.386s
ok  	amadan.net/rastrillo/fiesta/internal/site	3.216s
ok  	amadan.net/rastrillo/fiesta/internal/tokens	0.032s
ok  	amadan.net/rastrillo/fiesta/internal/www	32.836s
--- shard 1 of 12 ---
==> shard 1 of 12: make staticcheck
go run honnef.co/go/tools/cmd/staticcheck@v0.8.2 -tags browser ./...
--- shard 2 of 12 ---
==> shard 2 of 12: make govulncheck
go run golang.org/x/vuln/cmd/govulncheck@v1.8.0 ./...
=== Symbol Results ===

No vulnerabilities found.

Your code is affected by 0 vulnerabilities.
This scan also found 2 vulnerabilities in packages you import and 5
vulnerabilities in modules you require, but your code doesn't appear to call
these vulnerabilities.
Use '-show verbose' for more details.
--- shard 3 of 12 ---
==> shard 3 of 12: make gitleaks
go run github.com/zricethezav/gitleaks/v8@v8.30.1 git --no-banner --redact --log-opts='--full-history --diff-filter=tuxdb HEAD' .
12:24AM INF Unknown SCM platform. Use --platform to include links in findings. host=amadan.net
12:24AM INF 1 commits scanned.
12:24AM INF scanned ~5929367 bytes (5.93 MB) in 4.13s
12:24AM INF no leaks found
--- shard 4 of 12 ---
==> shard 4 of 12: make browser.internal.harness
./hack/gotest.sh browser 'internal.harness'
gotest: on DOCS-T1, driving the harness's own choice (/usr/bin/chromium)
ok  	amadan.net/rastrillo/fiesta/internal/harness	12.584s
--- shard 5 of 12 ---
==> shard 5 of 12: make browser.check~1of3
./hack/gotest.sh browser 'check~1of3'
gotest: on DOCS-T1, driving the harness's own choice (/usr/bin/chromium)
gotest: slice 1 of 3 of ./check/ runs 95 of 286 tests
ok  	amadan.net/rastrillo/fiesta/check	210.235s
--- shard 6 of 12 ---
==> shard 6 of 12: make browser.check~2of3
./hack/gotest.sh browser 'check~2of3'
gotest: on DOCS-T1, driving the harness's own choice (/usr/bin/chromium)
gotest: slice 2 of 3 of ./check/ runs 95 of 286 tests
ok  	amadan.net/rastrillo/fiesta/check	221.072s
--- shard 7 of 12 ---
==> shard 7 of 12: make browser.check~3of3
./hack/gotest.sh browser 'check~3of3'
gotest: on DOCS-T1, driving the harness's own choice (/usr/bin/chromium)
gotest: slice 3 of 3 of ./check/ runs 96 of 286 tests
ok  	amadan.net/rastrillo/fiesta/check	222.941s
--- shard 8 of 12 ---
==> shard 8 of 12: make browser.internal.gallery~1of2
./hack/gotest.sh browser 'internal.gallery~1of2'
gotest: on DOCS-T1, driving the harness's own choice (/usr/bin/chromium)
gotest: slice 1 of 2 of ./internal/gallery/ runs 1 of 46 tests
ok  	amadan.net/rastrillo/fiesta/internal/gallery	92.857s
--- shard 9 of 12 ---
==> shard 9 of 12: make browser.internal.gallery~2of2
./hack/gotest.sh browser 'internal.gallery~2of2'
gotest: on DOCS-T1, driving the harness's own choice (/usr/bin/chromium)
gotest: slice 2 of 2 of ./internal/gallery/ runs 45 of 46 tests
ok  	amadan.net/rastrillo/fiesta/internal/gallery	61.002s
--- shard 10 of 12 ---
==> shard 10 of 12: make browser.internal.www~1of2
./hack/gotest.sh browser 'internal.www~1of2'
gotest: on DOCS-T1, driving the harness's own choice (/usr/bin/chromium)
gotest: slice 1 of 2 of ./internal/www/ runs 17 of 34 tests
ok  	amadan.net/rastrillo/fiesta/internal/www	73.066s
--- shard 11 of 12 ---
==> shard 11 of 12: make browser.internal.www~2of2
./hack/gotest.sh browser 'internal.www~2of2'
gotest: on DOCS-T1, driving the harness's own choice (/usr/bin/chromium)
gotest: slice 2 of 2 of ./internal/www/ runs 17 of 34 tests
ok  	amadan.net/rastrillo/fiesta/internal/www	68.104s
diff --git a/examples/prose.json b/examples/prose.json
index 8fea216..57e891a 100644
--- a/examples/prose.json
+++ b/examples/prose.json
@@ -330,11 +330,17 @@
"asking-for-an-address": "One field and one button. Nothing to remember and nothing to steal.",
"coming-back-with-an-email-link": "The address is on the button, so there is nothing to type.",
"coming-back-with-keymail": "For someone coming back: the address they used last time is on the button, so one tap sends it. Remembering that address, and checking it again, is your app's job.",
- "email-and-password": "If you do use passwords, use the same one-way-in layout, and put a way to reset directly under the button rather than hiding it. We do not recommend passwords: people reuse them, they leak, and you inherit the job of storing them safely. Use a link in your email plus a passkey, or passkeys on their own. The markup is here because some products still need it.",
+ "email-and-password": "If you do use passwords, use the same one-way-in layout, and put a way to reset directly under the button rather than hiding it.",
"google-apple-and-the-rest": "We give you the buttons, drawn the way each company requires. We do not give you the sign-in itself: you wire that up to whichever provider you use.",
"on-the-way-to-keymail": "A holding page while someone is sent on to sign in. Your app moves the page on by itself; the link is there in case it does not.",
"when-keymail-could-not-confirm-it": "The button changes to send a link instead, so nobody is stuck.",
"when-keymail-was-offered-and-a-link-went-out": "One line says a link was sent this time. It does not guess why."
+ },
+ "warnings": {
+ "email-and-password": {
+ "title": "We do not recommend passwords",
+ "body": "People reuse them, they leak, and you inherit the job of storing them safely. Use a link in your email plus a passkey, or passkeys on their own. The markup is here because some products still need it."
+ }
}
},
"stat": {
diff --git a/hack/seed/README.md b/hack/seed/README.md
index a749961..0070f7d 100644
--- a/hack/seed/README.md
+++ b/hack/seed/README.md
@@ -10,6 +10,8 @@ run.sh <rastrillo checkout> <seed commit> does the seeding. It adds a scratch wo
run-prose.sh <rastrillo checkout> <seed commit> does the same for the prose: it reads the gallery's explanations at the seed commit, writes the ones that carry into examples/prose.json, and lists the rest in prose-review.json with the reason and, once reviewed, the approved replacement.
+run-translations.sh <rastrillo checkout> <commit> carries the gallery's translations: it dumps Fiesta's English strings with their ids, matches each against Rastrillo's prose and sample tables and its locale catalogs at the commit, writes the hits into internal/gallery/i18n/, and lists the misses in translations-todo.json for drafting.
+
sources.json says where every example state came from: the partial, idiom, shell, format or screen in Rastrillo's gallery, and which of its states. check/testdata/fixtures/index.json does the same for the fixtures, and marks the page builders that were not seeded, with the reason.
The hashes below are the files copied byte for byte, so a later sync can tell what moved.
@@ -68,5 +70,5 @@ f7b196f6eaaaedb39c051ef784a910665da6df2dc2f269372e778c7bf163a0e3 examples/asset
The checksums of run-prose.sh's output at its last run, so the next run shows whether any explanation changed.
-e90f894f4173eb7073f0995a6b91e655247e9584655a874a6863682c04b3f8c2 examples/prose.json
-f6019f286c4e0fa3d9ffc594cf7fa31e128c982cd34daef60e850f1078b7c610 hack/seed/prose-review.json
+1dd8eacef05c2b90163b79565ec62734b247b3aab4508959f228e6fc5da572eb examples/prose.json
+3d5ba9dc038a03e6d9beace7c3485e0a55c0373a11f3591d8686c03d2507d32e hack/seed/prose-review.json
diff --git a/hack/seed/_rastrillo/prose_fiestaseed_test.go b/hack/seed/_rastrillo/prose_fiestaseed_test.go
index f30cb75..84d6172 100644
--- a/hack/seed/_rastrillo/prose_fiestaseed_test.go
+++ b/hack/seed/_rastrillo/prose_fiestaseed_test.go
@@ -9,7 +9,8 @@
// and hack/seed/sources.json, and writes two files there:
//
// - examples/prose.json, every explanation the gallery shows, keyed
-// by family, example and state;
+// by family, example and state, with a screen's warning callout as
+// a title and body of its own beside the state's note;
// - hack/seed/prose-review.json, the strings that could not carry
// verbatim, each with the rastrillo text, the reason, and the
// approved replacement once there is one.
@@ -72,8 +73,16 @@ type proseFamily struct {
}
type proseExample struct {
- Blurb string `json:"blurb"`
- States map[string]string `json:"states,omitempty"`
+ Blurb string `json:"blurb"`
+ States map[string]string `json:"states,omitempty"`
+ Warnings map[string]proseWarning `json:"warnings,omitempty"`
+}
+
+// proseWarning is the callout after one state's note: a screen's
+// WarningTitle and WarningBody, each a slot of its own.
+type proseWarning struct {
+ Title string `json:"title"`
+ Body string `json:"body"`
}
type prosePages struct {
@@ -219,8 +228,8 @@ var approvedProse = map[string]string{
"examples.signin.blurb": "The sign-in screen in every state it passes through: asking for an address, coming back, after the link is sent, when something went wrong, and the other ways in.",
"examples.signin.states.asking-for-an-address": "One field and one button. Nothing to remember and nothing to steal.",
"examples.signin.states.coming-back-with-keymail": "For someone coming back: the address they used last time is on the button, so one tap sends it. Remembering that address, and checking it again, is your app's job.",
- "examples.signin.states.email-and-password": "If you do use passwords, use the same one-way-in layout, and put a way to reset directly under the button rather than hiding it. We do not recommend passwords: people reuse them, they leak, and you inherit the job of storing them safely. Use a link in your email plus a passkey, or passkeys on their own. The markup is here because some products still need it.",
"examples.signin.states.on-the-way-to-keymail": "A holding page while someone is sent on to sign in. Your app moves the page on by itself; the link is there in case it does not.",
+ "examples.signin.warnings.email-and-password.body": "People reuse them, they leak, and you inherit the job of storing them safely. Use a link in your email plus a passkey, or passkeys on their own. The markup is here because some products still need it.",
"examples.stat.states.a-shortened-number": "Showing 4.1k instead of 4120? Put the full number in a <data> tag's value. Screen readers only read what is on screen, so keep anything a person needs in the number itself.",
"examples.stat.states.the-big-number": "Give one stat in the row rst-stat=\"lead\" to make it big. It is the same component, just larger.",
"examples.status-pill.states.neutral": "Neutral is the default tone, so rst-tone=\"neutral\" could be left off.",
@@ -398,7 +407,8 @@ func (p *proser) examples() {
p.t.Fatalf("sources.json has no %s", key)
}
noteID := "examples." + e.Name + ".states." + st.ID
- var blurb, note string
+ warnID := "examples." + e.Name + ".warnings." + st.ID
+ var blurb, note, warnTitle, warnBody string
switch src.Kind {
case "partial":
doc, ok := partials[src.Source]
@@ -425,11 +435,12 @@ func (p *proser) examples() {
if src.State >= len(screens) || screens[src.State].Key != src.Source {
p.t.Fatalf("%s: screen %d is not %s", key, src.State, src.Source)
}
+ // The screen's blurb is the state's note; its warning,
+ // where it has one, is the callout after the note, with
+ // the title and body each a slot of its own.
doc := screens[src.State]
note = doc.Blurb
- if doc.WarningBody != "" {
- note += " " + doc.WarningTitle + ": " + doc.WarningBody
- }
+ warnTitle, warnBody = doc.WarningTitle, doc.WarningBody
default:
p.t.Fatalf("%s: unknown source kind %q", key, src.Kind)
}
@@ -441,7 +452,7 @@ func (p *proser) examples() {
}
origin := fmt.Sprintf("%s/%s/%d", src.Kind, src.Source, src.State)
if noted[origin] && proseAlways[noteID] == "" {
- note = ""
+ note, warnTitle, warnBody = "", "", ""
}
if note != "" {
noted[origin] = true
@@ -451,6 +462,15 @@ func (p *proser) examples() {
} else if _, queued := proseAlways[noteID]; queued {
ex.States[st.ID] = ""
}
+ if warnBody != "" {
+ if ex.Warnings == nil {
+ ex.Warnings = map[string]proseWarning{}
+ }
+ ex.Warnings[st.ID] = proseWarning{
+ Title: p.slot(warnID+".title", warnTitle),
+ Body: p.slot(warnID+".body", warnBody),
+ }
+ }
}
if len(ex.States) == 0 {
ex.States = nil
@@ -489,6 +509,8 @@ func (p *proser) check() {
known["examples."+e.Name+".blurb"] = true
for _, st := range e.States {
known["examples."+e.Name+".states."+st.ID] = true
+ known["examples."+e.Name+".warnings."+st.ID+".title"] = true
+ known["examples."+e.Name+".warnings."+st.ID+".body"] = true
}
}
for name := range idiomRules {
diff --git a/hack/seed/prose-review.json b/hack/seed/prose-review.json
index c203423..5dfc2da 100644
--- a/hack/seed/prose-review.json
+++ b/hack/seed/prose-review.json
@@ -281,18 +281,18 @@
"reason": "claims behaviour the static example lacks: remembering and checking the address",
"text": "For someone coming back: the address they used last time is on the button, so one tap sends it. Remembering that address, and checking it again, is your app's job."
},
- {
- "id": "examples.signin.states.email-and-password",
- "seed": "If you do use passwords, use the same one-way-in layout, and put a way to reset directly under the button rather than hiding it. We do not recommend passwords: Rastrillo does not ship this screen. People reuse passwords, they leak, and you inherit the job of storing them safely. Use a link in your email plus a passkey, or passkeys on their own. The markup is here because some products still need it.",
- "reason": "mentions rastrillo",
- "text": "If you do use passwords, use the same one-way-in layout, and put a way to reset directly under the button rather than hiding it. We do not recommend passwords: people reuse them, they leak, and you inherit the job of storing them safely. Use a link in your email plus a passkey, or passkeys on their own. The markup is here because some products still need it."
- },
{
"id": "examples.signin.states.on-the-way-to-keymail",
"seed": "The page moves on by itself, with a link in case it does not.",
"reason": "claims behaviour the static example lacks: moving on by itself",
"text": "A holding page while someone is sent on to sign in. Your app moves the page on by itself; the link is there in case it does not."
},
+ {
+ "id": "examples.signin.warnings.email-and-password.body",
+ "seed": "Rastrillo does not ship this screen. People reuse passwords, they leak, and you inherit the job of storing them safely. Use a link in your email plus a passkey, or passkeys on their own. The markup is here because some products still need it.",
+ "reason": "mentions rastrillo",
+ "text": "People reuse them, they leak, and you inherit the job of storing them safely. Use a link in your email plus a passkey, or passkeys on their own. The markup is here because some products still need it."
+ },
{
"id": "examples.stat.states.a-shortened-number",
"seed": "Showing 4.1k instead of 4120? Put the full number in Exact. Screen readers only read what is on screen, so keep anything a person needs in the number itself.",
diff --git a/internal/gallery/copy.go b/internal/gallery/copy.go
index 2665c88..c7430c0 100644
--- a/internal/gallery/copy.go
+++ b/internal/gallery/copy.go
@@ -40,6 +40,10 @@ var text = map[string]string{
// The Overview: its lead, and the heading over its routes.
"gallery.index.lead": "Every state of every example in the {theme} theme, each as a page of its own. Open an example to see its states live, with the markup beside each one.",
"gallery.overview.next": "Where to go next",
+ // The Overview: its intro, and its routes into Getting started and the Icons page.
+ "gallery.overview.intro": "Fiesta is Rastrillo's design system as a project of its own: consistent, polished, accessible components for any website or app, in any stack or none, with no or minimal JavaScript, in twelve languages, in clean, modern HTML and CSS. One stylesheet and done; the markup is yours to copy and remix.",
+ "gallery.routes.getting-started": "What ships: the one-link stylesheet, each theme, the optional scripts and the strings, with what each file weighs.",
+ "gallery.routes.icons": "Every icon slug at the size components draw it, with the file to copy and its lucide.dev name.",
// The previous and next pair at the foot of every page.
"gallery.step.prev": "Previous: {page}",
@@ -101,6 +105,8 @@ var text = map[string]string{
"gallery.primitives.modal.live": "See it live at the URL it belongs to",
"gallery.primitives.sidebar.live": "Open the sidebar shell demo, a standalone preview.",
"gallery.primitives.topbar.live": "Open the topbar shell demo, a dedicated preview.",
+ // The Overview's demo section: the callout under its lead.
+ "gallery.demo.callout.body": "Each screen is a page of its own, plain HTML in the sidebar shell, and works the same with JavaScript off. On a phone, the sections are a list and each screen has a way back.",
// The modal route: the page under it and the panel over it.
"gallery.modal.close": "Close settings",
@@ -133,6 +139,21 @@ var text = map[string]string{
"gallery.started.file.select-js": "field-select's searchable combobox. Inert until a select opts in with data-rst-select, and deletable on its own.",
"gallery.started.file.datetime-js": "The date fields' natural-language input. Inert until a field opts in with data-rst-date or data-rst-time, and deletable on its own.",
"gallery.started.file.calendar-js": "The month grid that field's calendar button opens. Inert until datetime.js asks it for a panel, and deletable on its own: without it the button falls back to the browser's own picker.",
+ // Getting started: its lead, its sections' bodies and headings, the blurbs for Fiesta's own files, and the total.
+ "gallery.started.lead": "Fiesta is one stylesheet and a handful of optional scripts. Everything works with scripts off; the scripts are enhancements, each its own file, so you load the ones your page uses and nothing else.",
+ "gallery.started.stylesheets.body": "fiesta.css is tokens.css and the Day theme joined, comments stripped, under 25 KiB gzipped. The tokens hold every component's layout and behaviour; the theme paints them. fiesta-plain.css and fiesta-signal.css are the same tokens with the Plain and Signal themes. A page that uses the sidebar or console shell links shell.css as well; nothing else needs it.",
+ "gallery.started.scripts.body": "fiesta.js closes menus, lets a switch's knob be dragged, gives iOS its pressed state and runs the colour-scheme menu; load it first. busy.js, light.js, anchor.js, shell.js, select.js, datetime.js and calendar.js are each inert until a page uses what they enhance, and each deletable on its own. Load calendar.js last.",
+ "gallery.started.file.bundle": "Tokens and the {theme} theme in one file: every component class, every scale, and the colours, each declared once as a light-dark() pair.",
+ "gallery.started.file.fiesta-js": "The enhancements most pages want: menus that close on an outside click and on Escape, the switch's drag, iOS's pressed state, and the colour-scheme menu. Load it first.",
+ "gallery.started.file.busy-js": "The busy rule: while a form sends, its button shows a spinner for at least 650ms and refuses a second submit, and Back hands it back.",
+ "gallery.started.file.strings": "The words select.js and datetime.js put on screen, in {language}. Load the one that matches your page's lang.",
+ "gallery.started.file.sprite": "A sprite with one symbol per icon, for a site that serves its own copy of Fiesta. From the CDN, inline the SVG from icons/<slug>.svg instead.",
+ "gallery.started.total": "A page that links fiesta.css and every script gets {raw} of CSS and JavaScript as written, {gzip} gzipped.",
+ "gallery.started.link.heading": "The one link",
+ "gallery.started.link.body": "Link fiesta.css from the CDN and your markup is styled. /1/ is the newest 1.x; a change that would break existing markup is a new major. Pin the exact version when you need bytes that never move.",
+ "gallery.started.own.heading": "Serving your own copy",
+ "gallery.started.own.body": "Take tokens.css and one theme, or the joined file, from a release and serve them with your site; the files under releases/<version>/ in the repository carry a SHA256SUMS. Link tokens.css first.",
+ "gallery.started.upgrading.body": "A page on /1/ picks up every 1.x release the day it ships; the changelog says what changed. A page pinned to an exact version moves when you change the link, and not before.",
// The tokens page: lead, the note on the palette, and a heading per
// group of tokens.Group.Key.
@@ -156,6 +177,10 @@ var text = map[string]string{
"gallery.icons.source": "Where the glyphs come from",
"gallery.icons.lucide": "The whole set is at {link}.",
"gallery.icons.kebab": "kebab and menu are the pair worth keeping straight: kebab is the three dots that mean more actions on this row, menu the three lines that mean navigation. The shells use menu when they collapse.",
+ // The Icons page: the section on the names, and the body of the section on the source.
+ "gallery.icons.names.heading": "The names are Fiesta's own",
+ "gallery.icons.names.body": "{renamed} of the {total} differ from the name lucide.dev publishes. Where the last line under an icon does not repeat its name, that is one of them. The names stay the same whatever a later icon set is drawn from.",
+ "gallery.icons.source.body": "Lucide, under the ISC licence, copied into the repository rather than fetched. The release carries the inline SVGs and a sprite; the gallery draws each one the way a component does.",
}
// say returns the English filed under id, with each {name} replaced by
diff --git a/internal/gallery/pages.go b/internal/gallery/pages.go
index cf0b08e..362faf5 100644
--- a/internal/gallery/pages.go
+++ b/internal/gallery/pages.go
@@ -41,7 +41,7 @@ func (s *site) pageKinds() []pageKind {
kinds := []pageKind{
{Kind: "overview", File: "index.html", Title: "gallery.kind.overview", Body: s.overviewBody},
{Kind: "tokens", File: "tokens.html", Title: "gallery.kind.tokens", Blurb: "gallery.route.tokens", Nav: s.tokenNav, Body: s.tokensBody},
- {Kind: "icons", File: "icons.html", Title: "gallery.kind.icons", Blurb: "gallery.icons.lead", Nav: s.iconNav, Body: s.iconsBody},
+ {Kind: "icons", File: "icons.html", Title: "gallery.kind.icons", Blurb: "gallery.routes.icons", Nav: s.iconNav, Body: s.iconsBody},
}
for _, f := range s.families {
kinds = append(kinds, pageKind{