Assets

Images, icons, logos and fonts in a design.

Fonts

  • Any Google font is available. Put the family in a font token: --font-display: 'Fraunces', ui-serif, Georgia, serif. Knighn writes one Google Fonts link for the families and the weights that the design uses.
  • Set weights with classes such as font-medium and font-semibold. Knighn loads the weights that the classes use.
  • --font-sans is the default font of all text. Headings use font-display, and code uses font-mono.
  • Always add fallbacks after the family, so that the text shows before the font loads.
  • A system font, such as SF Pro, is only a name. Knighn never uploads it, because of its licence. It shows only on devices that have it. Add system fallbacks: 'SF Pro Text', -apple-system, BlinkMacSystemFont, system-ui, sans-serif.
  • A custom font file is an asset. The user adds it in the app, and Knighn writes an @font-face rule in the theme block.
  • Find fonts with the search tool, kind font.

Two families are enough for most designs: one for headings and one for body text. Use one family in different weights for a quiet design.

Images

  • Use images that the user gave you or asked for. Never make up an image URL.
  • Put each image into Knighn storage with UploadImage. It returns a URL such as https://share.knighn.com/a/<sha256>.jpg. Use that URL in the design, so the design does not depend on the site of the image.
  • The URL comes from the content of the file. The same bytes always give the same URL, and the URL never changes.
  • Anyone with the URL can see the image. Ask the user before you upload a private image.
  • When you have no image, put a placeholder with a name and a muted fill: <div data-name="Product photo" className="aspect-video rounded-xl bg-muted" />.
  • When the person asks for a new picture, GenerateImage(prompt, { kind: 'photo' }) or { kind: 'logo' } makes one with Codex on their Mac and returns its Knighn URL. It works only in the Knighn app on a Mac and on the Knighn daemon.

Show an image with img, object-cover and a box with a size:

const src = await UploadImage('https://images.example.com/team.jpg') // a URL that the user gave you
const [media] = Find(page, '[data-name="Hero image"]')
Replace(
  media,
  `<img data-name="Hero image" src="${src}" alt="Four people around a table with laptops" className="aspect-[4/3] w-full rounded-2xl object-cover" />`
)

For an image behind content, use a background class:

const bg = await UploadImage('https://images.example.com/coast.jpg') // a URL that the user gave you
const [hero] = Find(page, '[data-name="Hero"]')
Update(hero, { classes: `bg-[url(${bg})] bg-cover bg-center` })

When you export a design as one file, Knighn puts the images into the file. When Knighn opens the file again, it uploads them again and puts the URLs back.

SVG

Inline <svg> elements are allowed. Use the JSX spelling of the attributes, such as strokeWidth and strokeLinecap.

  • Use fill="currentColor" or stroke="currentColor", so that text colour classes set the colour.
  • Set the size with classes: size-5.
  • Always keep the viewBox.
  • Do not draw logos or illustrations as paths. For a brand logo, use InsertLogo. For other art, use an image, or a placeholder with a name.
  • To make a logo image into paths, use TraceImage(image, { replace: true }). potrace on the person's Mac traces it.
<svg
  viewBox="0 0 24 24"
  fill="none"
  stroke="currentColor"
  strokeWidth={2}
  strokeLinecap="round"
  strokeLinejoin="round"
  aria-hidden="true"
  className="size-5 text-accent"
>
  <path d="M20 6 9 17l-5-5" />
</svg>

Icons

Write <Icon name="shield-check" className="size-5 text-accent" /> in the TSX that you give to Define, AddScreen, Insert, Replace or a tsx block. Knighn puts the Lucide icon there as an inline <svg> before it reads the TSX, so no call is necessary for each icon. Find icon names with the search tool, kind icon.

  • name is a Lucide name. Common names of other sets also work, such as the Font Awesome arrow-rotate-right (Lucide rotate-cw), gear, xmark and magnifying-glass. An unknown name stops the call and gives the close names.
  • size={16} adds size-4. Without a size class, the icon is 24 px (size-6). A text colour class colours it.
  • strokeWidth={1.5} changes the stroke. aria-label="Reload" gives the icon a name; otherwise it gets aria-hidden.
  • Write the name as text. name={icon} does not work, because an instance cannot choose an icon by a prop.
  • In a slot, such as icon={<Icon name="plus" className="size-4" />}, Knighn makes a small icon component, such as IconPlus, and gives the slot an instance of it.
  • When the design defines its own Icon component, Knighn leaves <Icon> as it is.

InsertIcon(parent, name, { className }) puts an icon into a layer that exists:

const [feature] = Find(page, '[data-name="Feature icon"]')
await InsertIcon(feature, 'shield-check', { className: 'size-5 text-accent' })
  • Use one icon set and one stroke width in a design.
  • Use 16 px icons with 14 px text, and 20 px icons with 16 px text.
  • For an icon in a slot, write <Icon> in the slot: icon={<Icon name="bell" />}.

Logos

For the logo of a company, a product or a tool (Stripe, GitHub, Figma, Vercel), use the real logo from svgl (svgl.app). InsertLogo(parent, name, { theme, wordmark, className }) puts it into a layer as an inline <svg> with the brand colours. Find logos with the search tool, kind logo, or with await SearchLogos(name).

const [row] = Find(page, '[data-name="Customer logos"]')
for (const brand of ['Stripe', 'GitHub', 'Figma', 'Vercel']) {
  await InsertLogo(row, brand, { className: 'h-8 w-auto' })
}
  • Never use an emoji, a Lucide icon or a drawn shape for a brand logo. Lucide icons are for actions and ideas, not for brands.
  • Use theme: 'dark' on a dark background. A logo that is black on white has a white version for dark backgrounds.
  • Use wordmark: true for the logo with the name, when svgl has one. Otherwise put the name next to the mark as text.
  • Set the height and keep the width automatic: h-8 w-auto. Do not stretch a logo.
  • When svgl has no logo for the brand, ask the person for the logo file and bring it in with UploadImage(url). Do not draw one.
  • InsertLogo adds role="img" and an aria-label with the brand name.

Alt text and labels

  • Give each img an alt that says what the image shows. Knighn warns with lint/img-alt when it is missing.
  • Use alt="" for an image that is only decoration.
  • Give each button that has only an icon an aria-label: <button type="button" aria-label="Close">.
  • Give each decorative <svg> the attribute aria-hidden="true".
  • Use real headings in order: one h1 for each screen, then h2 and h3.