Media / foldkit-media
Icons
An icon is data too: IconData holds its viewBox, what its root paints
(fill, stroke), and its drawing as nodes, never markup to set as HTML. An
application imports icons by name, and the build reads them:
// vite.config.ts
import { icons } from 'foldkit-media/vite'
import { defineConfig } from 'vite'
export default defineConfig({
plugins: [icons({ iconify: ['lucide'], folders: { app: './src/icons' } })],
})import { arrowLeft } from 'virtual:foldkit-icons/lucide'
import { logo } from 'virtual:foldkit-icons/app'iconify names Iconify sets by prefix, each read from the application's
@iconify-json/<prefix> (any set: lucide, uil, mdi); folders names
the application's own sets, a folder of SVG files each named by its file
(star-filled.svg is starFilled). The import is the selection: the bundler
drops every icon nothing imports. The plugin writes foldkit-icons.d.ts at
the root (dts moves it, false writes none), declaring every icon of every
set, so a misspelt name is a type error; include it in the tsconfig.
A view draws an icon with Icon.draw(icon, h): inline SVG through the view's
builder (Foldkit's h, or anything of its shape, SvgBuilder), every
attribute in the server's markup, 1em square unless size is given, and
aria-hidden, since the control it sits in carries the name.
foldkit-mixins-ui's Icon.view fills its glyph with the drawing, and names
it when given a label. A style masks with Icon.maskUrl(icon), a CSS url().
Every icon is parsed against a presentational allowlist, because it is drawn
inline. The parse refuses, naming the icon, an element or attribute outside
it (script, style, foreignObject, an animation, an event, a class),
an href or url() that leaves the icon, and a CSS escape; it drops only the
root's size and the words of title and desc, which a label replaces. A
refused SVG in a folder fails the build. A refused icon of an Iconify set is
left out, the declaration says why, and only importing it fails.
A package cannot rely on an application's plugin, so it commits its icons as
constants, read by foldkit-media/icons in a script (in this repository,
pnpm icons writes them from a package's icons.json):
import { Icon } from 'foldkit-media'
import { parse } from 'foldkit-media/icons'
const check = parse(
'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M20 6 9 17l-5-5"/></svg>',
'check',
)
Icon.maskUrl(check) // url("data:image/svg+xml,…") for a maskfromIconify(set, name) reads one icon of an Iconify set's JSON (decoded
with the IconifySet Schema), through its aliases. Icon.key(icon) is a
short name from the icon's content, the same on a server and in a browser;
Icon.prefixIds(icon, prefix) renames the ids the icon defines and every
href and url() naming one, so two icons drawn on one page keep their own.
Icon.draw does both; its key option sets two copies of one icon apart,
because a gradient defined in a hidden copy paints nothing for a shown one.
Validation
Facts are untrusted input. ImageAsset refuses an image with no pixels, and a
Placeholder.Color that is anything but one CSS color (no ;, no url(),
since it lands in a style the page writes.