package gallery import ( "fmt" "html/template" "strings" ) // The Icons page: one chip per icon, drawn the way a component draws // it, with its slug, the file to copy and where the glyph comes from. // Nothing in this file names an icon or counts them: the chips are // s.icons, read off icons/*.svg, and both numbers in the sentence // under the grid are counted from the set. The one list here is the // names Fiesta gives glyphs that Lucide names otherwise. // lucideHome is the one address in this tree that is not in this tree: // where the glyphs come from, and where a reader goes to see the rest // of the set. outboundLinks is the gate's side of the same decision. const lucideHome = "https://lucide.dev" // lucideNames is Fiesta's slug to the name lucide.dev publishes, for // the slugs where the two differ; every other slug is Lucide's own // name. Each was checked at lucide.dev/icons/ on 2026-10-29. // help-circle's published name is circle-question-mark: the address // lucide.dev/icons/circle-help now lands on that page. var lucideNames = map[string]string{ "kebab": "ellipsis-vertical", "alert-triangle": "triangle-alert", "check-circle": "circle-check", "help-circle": "circle-question-mark", "x-circle": "circle-x", } // outboundLinks is the whole list of addresses the tree may point at // that are not in it, each with the reason it is there. The table is // exact strings, not prefixes, and every entry has to be linked by some // page; TestEveryOutboundLinkIsAllowedAndUsed holds both halves, so it // cannot become a licence for the next link somebody feels like adding. var outboundLinks = map[string]string{ lucideHome: "where the glyphs come from: the set the icons are copied from, under the ISC licence, and the only place a reader can see the rest of it", } // lucideName is what lucide.dev calls the icon Fiesta calls slug. func lucideName(slug string) string { if name, ok := lucideNames[slug]; ok { return name } return slug } // iconView is one chip: its anchor, its slug, the glyph, and its // provenance, an address rather than a sentence (lucide.dev/icons/…) // so it needs no translation and a reader can type it. type iconView struct { ID, Slug, Provenance string Markup template.HTML } // iconsView is the page's data. Total and Renamed are counted from // the set rather than written into the sentence, so the sentence // cannot be wrong about the set it describes. Lucide is the line that // links the set, with the link in it. type iconsView struct { List []iconView Total, Renamed string Lucide template.HTML } // buildIcons reads the set for one locale's page. func (s *site) buildIcons(locale string) iconsView { out := iconsView{Total: fmt.Sprint(len(s.icons))} renamed := 0 for _, ic := range s.icons { name := lucideName(ic.Slug) if name != ic.Slug { renamed++ } out.List = append(out.List, iconView{ ID: anchorID("icon", ic.Slug), Slug: ic.Slug, Provenance: "lucide.dev/icons/" + name, Markup: ic.Markup, }) } out.Renamed = fmt.Sprint(renamed) link := `lucide.dev (` + template.HTMLEscapeString(s.say(locale, "gallery.newtab")) + `)` out.Lucide = template.HTML(strings.Replace(template.HTMLEscapeString(s.say(locale, "gallery.icons.lucide")), "{link}", link, 1)) return out } // iconsBody is every slug, drawn at the size tokens.css gives .icon, // each with its file and its lucide.dev address; then the names that // are Fiesta's own, with the count; then where the glyphs come from, // with the one link that leaves the tree. const iconsBody = `{{define "ds-body-icons"}}

{{say "gallery.icons.lead" "total" .Icons.Total}}

{{say "gallery.icons.names.heading"}}

{{say "gallery.icons.names.body" "renamed" .Icons.Renamed "total" .Icons.Total}}

{{say "gallery.icons.kebab"}}

{{say "gallery.icons.source"}}

{{say "gallery.icons.source.body"}}

{{.Icons.Lucide}}

{{end}}` // iconsBody renders the Icons page's body for p. func (s *site) iconsBody(p page) (template.HTML, error) { return s.body(p.Locale, "ds-body-icons", struct { page Icons iconsView }{p, s.buildIcons(p.Locale)}) } // iconMarkup is one of Fiesta's icons, inline, for the gallery's own // furniture; the icon template func. func (s *site) iconMarkup(slug string) template.HTML { for _, ic := range s.icons { if ic.Slug == slug { return ic.Markup } } return "" } func init() { registerTemplate(iconsBody) }