Images, Fonts and View Configuration
view.xml sets product image dimensions; where theme assets live, and why a resized image needs a command to appear.
<theme_dir>/etc/view.xml → <media><images module="Magento_Catalog"><image id="..." type="...">Product images are not rendered at their uploaded size. Each place an image appears asks for a named size, and the sizes are declared in view.xml — view configuration, the theme's file of display settings. It lives at <theme_dir>/etc/view.xml and merges with the modules' and the parent themes' copies, so you declare only the entries you change.
How an image size is chosen
Inside <media>, each <image> has an id and a type. The id is the name the template or block asks for, such as category_page_grid or product_page_main_image. The type is the image role on the product: image, small_image, swatch_image, swatch_thumb or thumbnail. Width and height are pixels; constrain, aspect_ratio, frame, transparency and background control the fitting. Resized copies are cached under pub/media/catalog/product/cache.
Where the other assets live
Fonts go in <theme_dir>/web/fonts, images in <theme_dir>/web/images, scripts in <theme_dir>/web/js. These are static files, so they are served from pub/static and need a deploy outside developer mode.
What trips people up
A size change in view.xml does not touch images already cached. Run bin/magento cache:flush and then bin/magento catalog:images:resize, which rebuilds the cache from the current configuration. And the id must be the one that page uses: editing category_page_grid changes nothing on the product page.
Example
<!-- app/design/frontend/Acme/storefront/etc/view.xml -->
<view xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:framework:Config/etc/view.xsd">
<media>
<images module="Magento_Catalog">
<!-- Product tiles in a category grid. Luma ships 240x300. -->
<image id="category_page_grid" type="small_image">
<width>300</width>
<height>375</height>
</image>
<!-- Main image on the product page. Luma ships 700x560; Blank ships 700x700. -->
<image id="product_page_main_image" type="image">
<width>900</width>
<height>900</height>
</image>
</images>
</media>
<vars module="Magento_Catalog">
<!-- Blank sets 1, which pads non-square images with white -->
<var name="product_image_white_borders">0</var>
</vars>
</view>
<!--
On the storefront: category tiles and the product page image are served at
the new dimensions, without white padding.
Apply it with:
bin/magento cache:flush
bin/magento catalog:images:resize
The second command rewrites pub/media/catalog/product/cache and takes a
while on a large catalog.
-->When to use it
- Product photos look soft on a retina layout because the theme serves 240px tiles.
- Non-square product images arrive padded with white borders in the category grid.
- Add a licensed webfont to a theme without loading it from a third-party host.
- Swap the header logo for one shipped with the theme rather than uploaded in the admin.
More examples
A font that ships with the theme
Put the font files in <theme_dir>/web/fonts and declare them with the UI library's .lib-font-face() mixin. @{baseDir} is resolved by Magento's preprocessor to the theme's own web directory, so the same declaration keeps working when the file is served from pub/static under a different path. Give @font-path without an extension; the mixin adds .woff2 and .woff. Outside developer mode the font files need a static deploy before the browser can fetch them.
// app/design/frontend/Acme/storefront/web/css/source/_extend.less
// Files: web/fonts/acme-sans/acme-sans-regular.woff2 (and .woff)
// web/fonts/acme-sans/acme-sans-bold.woff2 (and .woff)
.lib-font-face(
@family-name: 'Acme Sans',
@font-path: '@{baseDir}fonts/acme-sans/acme-sans-regular',
@font-weight: 400,
@font-style: normal,
@font-display: swap
);
.lib-font-face(
@family-name: 'Acme Sans',
@font-path: '@{baseDir}fonts/acme-sans/acme-sans-bold',
@font-weight: 700,
@font-style: normal,
@font-display: swap
);
body {
font-family: 'Acme Sans', @font-family__sans-serif;
}A logo from the theme instead of the admin
The header logo block falls back to images/logo.svg inside the theme, so the simplest route is to place your file at <theme_dir>/web/images/logo.svg. When the filename differs, pass it as a layout argument: logo_file is a path relative to the theme's web directory, and logo_alt sets the alt text. A logo uploaded in Content > Design > Configuration takes precedence over both, which is the usual reason a layout change appears to do nothing.
<!-- app/design/frontend/Acme/storefront/Magento_Theme/layout/default.xml -->
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
<body>
<referenceBlock name="logo">
<arguments>
<argument name="logo_file" xsi:type="string">images/acme-logo.svg</argument>
<argument name="logo_alt" xsi:type="string">Acme</argument>
</arguments>
</referenceBlock>
</body>
</page>
<!--
File: app/design/frontend/Acme/storefront/web/images/acme-logo.svg
Apply: bin/magento cache:flush
bin/magento setup:static-content:deploy -f en_US (outside developer mode)
-->Gallery behaviour, not just sizes
view.xml also carries display variables, read through the view configuration rather than core_config_data. The Magento_Catalog gallery block uses these for the product page gallery: navigation style, looping, arrows and fullscreen behaviour. Changing them is a theme decision, which is why they sit here and not in the admin. A cache flush is enough, since no images are regenerated.
<!-- app/design/frontend/Acme/storefront/etc/view.xml -->
<view xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:framework:Config/etc/view.xsd">
<vars module="Magento_Catalog">
<var name="gallery">
<var name="nav">thumbs</var>
<var name="loop">false</var>
<var name="arrows">false</var>
<var name="navdir">vertical</var>
<var name="transition">
<var name="effect">dissolve</var>
<var name="duration">300</var>
</var>
</var>
</vars>
</view>
Discussion