feat(skills): add responsive-images skill - #7
Merged
Merged
Conversation
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
force-pushed
the
feat/responsive-images-skill
branch
from
September 15, 2026 19:51
2e33b9e to
d5c45de
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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
srcsetis close to the first thing a frontend developer does with Crystallize media.Adds
responsive-images, plusreferences/without-react.mdfor non-React frontends.Three findings, measured rather than assumed
1. WebP beats AVIF here, nearly always.
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
sizeis load-bearing.reactjs-components'Imagecompares the first AVIF and WebP variants to decide whether AVIF is worth emitting. Omitsizefrom 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
next/imageor 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.sizeshas no default. Omit it and the browser assumes100vw, so a 180px thumbnail downloads the 1024px file. Worked examples for grids, heroes and fixed thumbnails, and how to verify withcurrentSrc.<figure>/<figcaption>wrapper, which always renders — worth knowing before styling.fetchPriorityon the hero, neverloading="lazy"on the LCP element, alwayswidth/height.focalPointfor crops that survive changing aspect ratios.The skill leads with the component, as requested — the hand-rolled
<picture>is the fallback inreferences/without-react.md, not the recommendation.Verified against
@crystallize/reactjs-componentsv5.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 bothresponsive-imagesandvector-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