hermes-hub/.agents/skills/frontend-design/imagery.md

10 KiB
Raw Blame History

Imagery & Icons — No Stock, No Emoji

Pictures are where generated sites collapse. The AI default is: stock photo with a gradient overlay, emoji instead of icons, and blobs for "visual interest." All three are instant tells (anti-patterns.md §3, §7, §8, §10). This file is what to do instead — in order of preference.


The Imagery Decision Tree

Before adding any image, ask in order:

  1. Does this need an image at all? Most marketing pages are improved by removing images. Typography is the design (SKILL.md principle 2). A strong headline on generous whitespace beats a mediocre photo.
  2. Can it be a CSS/SVG composition? Covers, mockups, product visuals, data — abstract compositions read as designed and cost kilobytes (performance.md).
  3. Can it be a real photo with real art direction? Only if real photographs exist (client photos, product shots, documentary sources). Never invented stock.
  4. Nothing above works? Then the section is the wrong section. Cut it.

The tell: if you're searching a stock site for "team collaborating laptop" — the image has no reason to exist.


CSS/SVG Art Direction (the default)

Abstract compositions are the house style for generated UI: they always match the token system, they never look stock, and they ship in bytes. Build them from the same tokens as the page — same surface, ink, hairline, accent.

The vocabulary

Composition Build Use for
Rules & columns 1px lines, repeating-linear-gradient Architecture, editorial, "structure"
Concentric circles Nested border circles, one accent ring Sound, music, focus
Halftone / dot grid radial-gradient repeated Print heritage, texture
Grid artifacts Visible column rules + one filled cell Swiss, data, "system"
Layered planes 23 offset rectangles, one in accent Product surfaces, layers
Chart as image Simple SVG bars/lines with mono labels Metrics, proof
Poster crop Big numeral or letter, cropped by overflow Covers, features
/* Dot grid — pure CSS texture */
.art--halftone {
  aspect-ratio: 4 / 5;
  background-image: radial-gradient(var(--ink) 1px, transparent 1.2px);
  background-size: 14px 14px;
  /* fade it: one clean idea, not wallpaper */
  -webkit-mask-image: linear-gradient(#000 40%, transparent);
          mask-image: linear-gradient(#000 40%, transparent);
}

/* Concentric — one accent ring as the "subject" */
.art--rings {
  aspect-ratio: 1 / 1;
  border-radius: 50%;
  border: 1px solid var(--hairline);
  display: grid; place-items: center;
}
.art--rings::before {
  content: ''; width: 62%; height: 62%;
  border-radius: 50%;
  border: 1px solid var(--accent);
}

Rules:

  • One idea per composition. Rules + circles + dots + gradient = mush. Pick one, execute precisely.
  • Compositions live inside a defined box (aspect-ratio), like a print plate — not floating decor behind text.
  • The accent gets one moment: one ring, one filled cell, one label. (color.md 510% rule still applies.)
  • Mark decorative art aria-hidden="true" (accessibility.md); if it carries information, it's an <svg> with a <title> or adjacent text.
  • Working examples: the cover plates in examples/example-magazine.html, the album covers in examples/example-brutalist.html, the CSS dashboard in examples/example-saas.html.

If Photography Is Real

Photography is only an option when real photographs exist. Then direct it like a photo editor, not a stock buyer:

The art direction brief (write it before choosing)

  • One light source, one lens, one palette. Mixed light and mixed lenses read as assembled, not shot.
  • Documentary, not posed. The workshop, not the handshake. Hands on work, not people pointing at whiteboards.
  • No smiling-person-with-laptop. Ever. (anti-patterns.md §10.)
  • Crop with intent. Full-bleed, hard edges, cropped off-grid — a brave crop is design; a centered subject is a placeholder.
  • Treatment is a system: same ratio family, same caption style, same edge treatment across the page. Two ratios maximum.

Sourcing, honestly

Source Verdict
Client/team photos (even phone-shot) Best — real beats polished
Real product photography Required for products
Public archives (museum/library, CC-licensed) Great for editorial and history
UGC with permission Good for lifestyle and community
Any stock site, any "similar images" No

Treatment rules

  • No gradient overlays on text. If text needs a scrim to be readable, the photo is wrong or the text is misplaced. (Scrim = gradient = anti-patterns.md §1's family.)
  • Captions are design: mono or small italic, real information — who, where, when. "Image: ..." with a real fact, not "photo."
  • Duotone/grayscale only as a system across all photos, using tokens.
  • Grain/texture: once per page, subtle. (Also aesthetics.md §5.)

Iconography

Icons are typography for concepts: one voice, measured precisely.

The system

Rule Value
Sets Lucide, Phosphor, Tabler, Feather — pick ONE per project
Stroke 1.5px (2px at 24px+), stroke-linecap="round" or square — consistent
Sizes 16px (inline), 20px (UI), 24px (feature) — one size per context
Color currentColor, always — icons inherit ink/muted like text
Alignment Optically centered; 16px icons align to the x-height of body text
In UI chrome Icon + label for anything ambiguous; icon-only with aria-label

The correct way to ship an icon

<!-- Icon + text label (default) -->
<a href="/docs" class="nav-link">
  <svg aria-hidden="true" width="16" height="16" viewBox="0 0 24 24"
       fill="none" stroke="currentColor" stroke-width="1.5"
       stroke-linecap="round" stroke-linejoin="round">
    <path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20"/>
    <path d="M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z"/>
  </svg>
  <span>Docs</span>
</a>

<!-- Icon-only button (needs a name) -->
<button aria-label="Close menu" class="icon-btn"></button>
  • Inline SVG, not icon fonts (fonts break, shift, and announce garbage).
  • Same set means same grid (24×24 viewBox), same stroke, same corner philosophy. Never mix Lucide with Font Awesome on one page.
  • aria-hidden="true" on decorative icons; labels do the naming (accessibility.md).
  • Emoji are not icons. In product UI: never. In content (a genuinely playful brand voice): maybe once, on purpose. (anti-patterns.md §3.)

Avatars & Logo Bars

Avatars

  • Initials, not mystery silhouettes. Two letters in a circle using tokens beat every default placeholder.
.avatar {
  width: 32px; height: 32px;
  border-radius: 50%;
  display: grid; place-items: center;
  background: var(--surface-sunken);
  color: var(--ink);
  font-size: var(--text-xs);
  font-weight: 500;
  letter-spacing: 0.02em;
}
  • Real photos only if they're real people (testimonials: real quotes or no testimonials — checklist.md §Content).

Logo bars ("trusted by")

  • Only logos of real, permissioned customers. Invented logos for invented companies is the definition of anti-patterns.md §15 — lying.
  • Treatment: monochrome at ~60% ink, hover restores full ink; uniform optical height (1824px), real wordmarks, no fake " Inc.".
  • No logo bar at all > a fake one. A single specific sentence ("Vercel's design team uses this weekly") beats twelve gray rectangles.

Favicon & Social Image (the 10-minute craft pass)

Agents ship pages with no favicon and a blank social card — the two places everyone will look.

Favicon

<link rel="icon" href="/favicon.svg" type="image/svg+xml">
<link rel="icon" href="/favicon.png" sizes="32x32"> <!-- fallback -->
<link rel="apple-touch-icon" href="/apple-touch-icon.png"> <!-- 180×180 -->

Design it like a poster at 16px: the mark's single element (one letter, one ring, one bar) in accent or ink on surface. Test at 16px — if unreadable, simplify.

Open Graph / Twitter card

<meta property="og:image" content="https://example.com/og/issue-14.png">
<meta name="twitter:card" content="summary_large_image">
  • 1200×630, designed like a print cover: brand type, issue/product name, one accent moment, real metadata.
  • It should look like the site — same face, same tokens. Not a screenshot of the hero, not a logo centered in gray.
  • Zero-OG-image pages render as blank gray rectangles in every share. That's the first impression most visitors get.

Imagery Anti-Patterns

Don't Do
Stock photo + gradient overlay + headline on top CSS/SVG composition, or photo with real crop and real caption
Emoji as feature/status icons One icon set, inline SVG, currentColor
Blob/mesh backgrounds "for depth" Whitespace, hairlines, one composition per section
Mystery-man avatar placeholder Initials in a token circle
Fake logo bar of invented companies Real customers, or a specific sentence, or nothing
Icon fonts / emoji symbols for UI glyphs Inline SVG from one set
Mixing icon styles (solid + outline, two sets) One set, one stroke, one size per context
alt="image" / alt="IMG_2841" Real alt text or alt="" when decorative
Photos in 5 aspect ratios across one page One ratio family, treated as a system
No favicon, no og:image Designed 16px mark + 1200×630 social cover
AI-generated "photo of our team" No photography exists → composition or no image

Ship Gate

  • Every image passed the decision tree (needed? composition? real photo? otherwise cut)
  • Compositions: one idea each, built from tokens, aria-hidden or titled
  • Photos (if any): real, one light, one crop system, captioned with facts
  • Icons: one set, one stroke, currentColor, labeled or aria-label-ed
  • Favicon designed and tested at 16px
  • og:image designed like a cover, same type system
  • No emoji in UI, no stock, no blobs — zero exceptions

See also: anti-patterns.md (§3, §7, §8, §10, §15, §16), performance.md §Images, accessibility.md §Images & Media, content.md (captions are copy).