Skip to content

feat(skills): add responsive-images skill - #7

Merged
Plopix merged 1 commit into
mainfrom
feat/responsive-images-skill
Sep 15, 2026
Merged

Plopix merged 1 commit into
mainfrom
feat/responsive-images-skill

Conversation

@papelipe

Copy link
Copy Markdown
Member
Q A
Branch? main
Bug fix? no
New feature? yes
BC breaks? no
Fixed tickets #...

Nothing in the skills covered rendering images. Two passing sentences said Crystallize serves "responsive sizes" in WebP/AVIF — neither said how to use them. Building a srcset is close to the first thing a frontend developer does with Crystallize media.

Adds responsive-images, plus references/without-react.md for non-React frontends.

Three findings, measured rather than assumed

1. WebP beats AVIF here, nearly always.

Same-width comparisons, 2 unrelated production tenants 347
WebP smaller 343
AVIF smaller 4
Average advantage to WebP ~25–30%

On one tenant AVIF averaged larger than the original JPEG. AVIF wins at equal visual quality in general, but payload is decided by file size at the quality the encoder actually targeted.

So the guidance is: ship one modern format, not both. Two <source> elements do not make a page faster — the browser takes the first it supports, and if that is the larger AVIF you have paid for the privilege.

2. The width ladder is per-image. Widths are drawn from 100/200/500/768/1024/1366/1600/1920/3200, but capped at the source image's own width: a 1200px original yields five sizes, a 500px original three. Eight distinct width sets appeared across 80 images. Hardcoding the ladder — or using the component's _availableSizes, which synthesizes URLs from the @{width} scheme — emits URLs that 404.

3. Querying size is load-bearing. reactjs-components' Image compares the first AVIF and WebP variants to decide whether AVIF is worth emitting. Omit size from the GraphQL query and that comparison cannot run, so it ships both formats — the more expensive outcome, silently. This one ties the other two together and is the most likely thing to be getting wrong in production right now.

Also covered

  • Rule 0: do not route these URLs through next/image or another framework optimizer. The variants are already static files on the CDN, so re-optimizing yields no smaller file while billing for the transform and the platform egress. The escape hatch (unoptimized, or a custom loader) is documented for projects already committed to it.
  • sizes has no default. Omit it and the browser assumes 100vw, so a 180px thumbnail downloads the 1024px file. Worked examples for grids, heroes and fixed thumbnails, and how to verify with currentSrc.
  • The component's <figure>/<figcaption> wrapper, which always renders — worth knowing before styling.
  • LCP and CLS: fetchPriority on the hero, never loading="lazy" on the LCP element, always width/height.
  • focalPoint for crops that survive changing aspect ratios.
  • A failure-modes table mapping each symptom to its cause.

The skill leads with the component, as requested — the hand-rolled <picture> is the fallback in references/without-react.md, not the recommendation.

Verified against

@crystallize/reactjs-components v5.0.0 source (not just its README — the README does not mention the AVIF/WebP comparison), and live Catalogue and Discovery responses on two tenants.

Merge note

Conflicts with #3 on README.md — both add a name to the same skills-list line. Trivial to resolve: keep both responsive-images and vector-ranking. Clean against #4, #5 and #6, verified by test-merge.

No plugin version bump, for the same reason as #6#3 already moves it to 3.6.0. Worth bumping on whichever merges last.

🤖 Generated with Claude Code

Nothing in the skills covered rendering images. Two passing sentences said
Crystallize serves "responsive sizes" in WebP/AVIF; neither said how to use
them. Building a srcset is close to the first thing a frontend developer
does with Crystallize media, and no skill said how.

New skill `responsive-images`, plus references/without-react.md for
non-React frontends.

Three findings drive the guidance, all measured rather than assumed:

WebP beats AVIF here, nearly always. Across 347 same-width comparisons on
two unrelated production tenants, WebP produced the smaller file 343 times,
averaging 25-30% smaller; on one tenant AVIF averaged larger than the
original JPEG. AVIF wins at equal visual quality in general, but payload is
decided by file size at the quality the encoder actually targeted. So: ship
one modern format, not both — two <source> elements only guarantee the
browser takes the first it supports, which is the larger file.

The width ladder is per-image. Widths are drawn from 100/200/500/768/1024/
1366/1600/1920/3200, but capped at the source image's own width: a 1200px
original yields five sizes, a 500px original three. Eight distinct width
sets appeared across 80 images. Hardcoding the ladder — or using the
component's _availableSizes, which synthesizes URLs from the @{width}
scheme — emits URLs that 404.

Querying `size` is load-bearing. The reactjs-components Image compares the
first AVIF and WebP variants to decide whether AVIF is worth emitting. Omit
`size` from the query and the comparison cannot run, so it ships both
formats — the more expensive outcome, silently.

Also documented: why not to route these URLs through next/image or another
framework optimizer (the variants are already static CDN files, so
re-optimizing yields no smaller file while billing for the transform and
the egress); that `sizes` has no default and omitting it makes the browser
assume 100vw; the component's <figure>/<figcaption> wrapper; LCP and CLS
handling; and focalPoint for cropping.

Verified against @crystallize/reactjs-components v5.0.0 source and live
Catalogue/Discovery responses.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@Plopix
Plopix force-pushed the feat/responsive-images-skill branch from 2e33b9e to d5c45de Compare September 15, 2026 19:51
@Plopix
Plopix merged commit 2e0c2f4 into main Sep 15, 2026
1 check passed
@Plopix
Plopix deleted the feat/responsive-images-skill branch September 15, 2026 20:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants