Retina-Ready Graphics Website Markup

Our company is engaged in the development, support and maintenance of sites of any complexity. From simple one-page sites to large-scale cluster systems built on micro services. Experience of developers is confirmed by certificates from vendors.
Development and maintenance of all types of websites:
Informational websites or web applications
Business card websites, landing pages, corporate websites, online catalogs, quizzes, promo websites, blogs, news resources, informational portals, forums, aggregators
E-commerce websites or web applications
Online stores, B2B portals, marketplaces, online exchanges, cashback websites, exchanges, dropshipping platforms, product parsers
Business process management web applications
CRM systems, ERP systems, corporate portals, production management systems, information parsers
Electronic service websites or web applications
Classified ads platforms, online schools, online cinemas, website builders, portals for electronic services, video hosting platforms, thematic portals

These are just some of the technical types of websites we work with, and each of them can have its own specific features and functionality, as well as be customized to meet the specific needs and goals of the client.

Our competencies:
Development stages
Latest works
  • image_web-applications_feedme_466_0.webp
    Development of a web application for FEEDME
    1161
  • image_ecommerce_furnoro_435_0.webp
    Development of an online store for the company FURNORO
    1041
  • image_crm_enviok_479_0.webp
    Development of a web application for Enviok
    822
  • image_crm_chasseurs_493_0.webp
    CRM development for Chasseurs
    847
  • image_website-sbh_0.png
    Website development for SBH Partners
    999
  • image_website-_0.png
    Website development for Red Pear
    451

Retina-ready graphics markup for websites

Screens with pixel density 2x and above have long become the standard — MacBook Pro, iPhone, flagship Android devices. A website marked up without retina in mind looks blurry on these devices: logos "swim," icons lose sharpness, product screenshots look low-quality. Retina-ready markup eliminates this problem systematically without sacrificing performance.

What makes graphics "retina-ready"

A regular raster PNG 100×100 px on a display with devicePixelRatio: 2 stretches to 200 physical pixels. The browser interpolates — hence the blurriness. There are several solutions, each applied in its own context:

Method When to apply
srcset + sizes for <img> Content images
CSS image-set() Background images
SVG Icons, logos, illustrations
<picture> + <source> When art direction is needed
CSS sprites 2x Legacy projects with IE11

SVG as the first choice

For interface elements, SVG is the only correct approach. Vector graphics scale losslessly at any DPI. Icons via <use xlink:href> or componentization via React:

// Icon.tsx — system icon via SVG sprite
import { FC } from 'react';

interface IconProps {
  name: string;
  size?: number;
  className?: string;
}

const Icon: FC<IconProps> = ({ name, size = 24, className }) => (
  <svg width={size} height={size} className={className} aria-hidden="true">
    <use href={`/sprites/icons.svg#${name}`} />
  </svg>
);

srcset for raster images

When SVG is not available — photos, screenshots, complex illustrations — srcset is used:

<img
  src="hero-800.jpg"
  srcset="hero-800.jpg 1x, hero-1600.jpg 2x, hero-2400.jpg 3x"
  width="800"
  height="450"
  alt="Main application screen"
  loading="lazy"
  decoding="async"
/>

For responsive cases with different frames:

<picture>
  <source
    media="(min-width: 1024px)"
    srcset="hero-desktop-1x.webp 1x, hero-desktop-2x.webp 2x"
    type="image/webp"
  />
  <source
    media="(min-width: 1024px)"
    srcset="hero-desktop-1x.jpg 1x, hero-desktop-2x.jpg 2x"
  />
  <source
    srcset="hero-mobile-1x.webp 1x, hero-mobile-2x.webp 2x"
    type="image/webp"
  />
  <img
    src="hero-mobile-1x.jpg"
    srcset="hero-mobile-1x.jpg 1x, hero-mobile-2x.jpg 2x"
    width="390"
    height="260"
    alt="Main screen"
  />
</picture>

Background images via CSS image-set

.hero-banner {
  background-image: image-set(
    url('/img/banner-1x.webp') 1x,
    url('/img/banner-2x.webp') 2x
  );
  background-size: cover;
}

/* Fallback for older browsers */
@supports not (background-image: image-set(url('') 1x)) {
  .hero-banner {
    background-image: url('/img/banner-2x.webp');
  }
}

Image preparation toolchain

Sharp for automatic size generation

In production pipeline, images are not prepared manually. The builder generates all variants from the source:

// scripts/optimize-images.mjs
import sharp from 'sharp';
import { glob } from 'glob';
import path from 'path';

const SOURCE_DIR = 'src/assets/images';
const OUTPUT_DIR = 'public/img';

const files = await glob(`${SOURCE_DIR}/**/*.{png,jpg}`);

for (const file of files) {
  const name = path.basename(file, path.extname(file));
  const outDir = path.dirname(file).replace(SOURCE_DIR, OUTPUT_DIR);

  // 1x WebP
  await sharp(file).resize({ width: 800 }).webp({ quality: 82 })
    .toFile(`${outDir}/${name}-1x.webp`);

  // 2x WebP
  await sharp(file).resize({ width: 1600 }).webp({ quality: 78 })
    .toFile(`${outDir}/${name}-2x.webp`);

  // 1x JPEG fallback
  await sharp(file).resize({ width: 800 }).jpeg({ quality: 85, mozjpeg: true })
    .toFile(`${outDir}/${name}-1x.jpg`);

  // 2x JPEG fallback
  await sharp(file).resize({ width: 1600 }).jpeg({ quality: 80, mozjpeg: true })
    .toFile(`${outDir}/${name}-2x.jpg`);
}

Vite plugin for automatic srcset

For Next.js, there is built-in optimization via next/image. For Vite projects — vite-imagetools:

// vite.config.ts
import { defineConfig } from 'vite';
import { imagetools } from 'vite-imagetools';

export default defineConfig({
  plugins: [
    imagetools({
      defaultDirectives: new URLSearchParams({
        format: 'webp;jpg',
        quality: '82',
      }),
    }),
  ],
});

Usage in a component:

import heroSrcset from './hero.jpg?w=800;1600&format=webp&as=srcset';

<img srcSet={heroSrcset} src="/hero-fallback.jpg" alt="Hero" />

SVG sprite: building and optimization

SVGO removes unnecessary attributes and shortens paths. The sprite is built from separate files:

// scripts/build-sprite.mjs
import { optimize } from 'svgo';
import { glob } from 'glob';
import fs from 'fs/promises';

const icons = await glob('src/assets/icons/*.svg');
let symbols = '';

for (const file of icons) {
  const id = path.basename(file, '.svg');
  const raw = await fs.readFile(file, 'utf8');
  const { data } = optimize(raw, {
    plugins: ['preset-default', { name: 'removeViewBox', active: false }],
  });

  // Convert <svg> to <symbol>
  const symbol = data
    .replace(/<svg([^>]*)>/, `<symbol id="${id}"$1>`)
    .replace('</svg>', '</symbol>');
  symbols += symbol;
}

const sprite = `<svg xmlns="http://www.w3.org/2000/svg" style="display:none">${symbols}</svg>`;
await fs.writeFile('public/sprites/icons.svg', sprite);

Testing on retina displays

Tools:

  • Chrome DevTools → Sensors → Device pixel ratio: set to 2 or 3
  • Firefox: via about:configlayout.css.devPixelsPerPx
  • Real devices: iPhone 13+, MacBook Pro M1+, Samsung Galaxy S22+

Checklist before submission:

  • All icons are SVG or have a 2x version
  • Logo in SVG format
  • Content images use srcset with 2x
  • Background images use image-set()
  • CSS does not contain background-image with a single 1x file without fallback
  • Fonts are system or web fonts, not raster

Execution timeline

Basic retina adaptation of existing markup (replacing raster icons with SVG, adding srcset to images): 1–2 days. Full retina-ready markup of a new project from scratch is included in the overall markup timeline without significant increase in duration — provided that source files are provided in vector or 2x resolution.