hby

用Ready-to-Use模板实现响应式图片的介绍

原文链接: medium.freecodecamp.org

document.body.className = document.body.className.replace(/(^|\s)is-noJs(\s|$)/, "$1is-js$2")

主页

freeCodeCamp

接下来

登录 开始

去Maciej Nowakowski的个人主页

Maciej Nowakowski:不要停止关注以下内容

codecamps网站的建立者--这是一个用React, Angular, GraphQL和Meteor进行一周远程编程的地方。

1月 14


用Ready-to-Use模板实现响应式图片的介绍

为什么要为了2个媒体查询做的事情而去生成12个同样版本的图片呢?用户也许不会注意到。

可是谷歌(浏览器)会。

错误格式的响应式图片,没有适当压缩的图片,还有就是图片太大会极大的减小页面(反应)速度并且影响你的SEO。

根据谷歌, 任何大于2秒的下载时间将会使你的用户失去耐心并且不利于爬虫对你的网站进行信息抓取。

当我重建我的网站时,我发现这个事情很难。 我的目标是想创建一个一眨眼就能下载好的简单网站。为了实现这效果,我采用了Gatsby.js ——据我了解,它很快。

在打了几天代码之后,网站建立并运行了起来。可是,让我失望的是,在PageSpeed Insights 中发现,(网站)在移动设备获得了中等得分78/100,但在桌面设备获得了偏低的得分57/100。这一点也不好。

PageSpeed Insights对问题提出了一个很容易的补救建议。只是下载用工具创建好的压缩图片,然后一切就会变好。

这激起了我的好奇心。我越想到大小、格式和压缩等级,我就越对多样的选择感到迷茫。PNG,JPG,SVG, 内联的base64编码的字符串或者webPs等等?

为了解除心头的疑惑,我“潜入”像素(px)的世界,艰难地通过在这个问题上胡乱提示的模糊的水域,并且提出了一个系统研究方法。

而且一旦我将我学到的应用起来,我的网页速度评级移动设备从78增加到91并且桌面设备从57增加到99!

在这篇文章,我会向你展示怎样快速的生成能够在全部浏览器工作的响应式图片并且极大地减少你的下载速度。

在你开始之前

如果你想优化一张图片,你不得不从一个高质量的图片(大小和格式要正确)开始。:

  • 将JPG用于照片和PNG用于需要透明度的图形或其他图像
  • 对于具有有限数量颜色的图形,请使用较小的PNG-8而不是PNG-24。为了进一步减小尺寸,你还可以将颜色数从256减少到16
  • 使用SVG(矢量图形图像)作为图标和徽标。它们将很好地缩放而不会增加你文件的大小
  • 使用10KB以下的内联图像作为64base编码的字符串(节省)
  • 图片的实际宽度不应超过将显示的最大容器的宽度,否则会以2倍展示(对于视网膜显示)

硬件和逻辑像素

占用全宽15''的 Macbook Pro的屏幕图像宽度为1440像素,但retina 屏的实际分辨率是2880x1800的两倍。 为什么? 由于其像素密度因子为2。

旧显示器的像素密度为1.但是由于近年来屏幕分辨率的增加,硬件像素不再等于逻辑或CSS像素。

硬件和CSS像素之间的关系可由以下公式描述:

CSS像素= 硬件像素 / 像素密度

因此,2880像素的硬件分辨率转换为 retina 屏上的1440个CSS像素。这也解释了为什么当你在开发工具中检查全宽图像时,你会看到它只有1440像素而不是原来的2880像素。

retina 屏是几年前的一个重大突破。 今天,移动设备甚至有“密集”显示的3,甚至是三星Galaxy S8 +的4。!

为了我实验的目的,我决定要犀利,全宽图像的最大宽度应该是2880像素。

随着图像高度设置为600px,质量达到75%,Photoshop制作了一个庞大939KB文件。 这是不可接受的。

经过几个压缩级别的实验后,很明显,将JPG压缩到质量的60%以下会导致明显的质量损失。我将质量设置为60%作为起点,图像尺寸降至681KB。 尽管如此,还不够体面。

WebP格式

根据 谷歌,“WebP是一种现代的图像格式,可以为网络上的图像提供卓越的无损和有损压缩”

在转换成WebP格式后,我的图像不仅更小,而且更清晰! WebP再次削减了JPEG压缩的34%。我想,“我在正确的道路上”!

不幸的是,根据 Can I use,Chrome和Opera支持WebP格式,仅占所有浏览器的60%。 所以我知道我必须考虑回退选项。

最后,确定了限制:

  • 60%的压缩级别
  • 尽可能webP格式

我也选择支持三个断点:600px和900px(here is why)和2像素比重 - 1倍和2倍的 retina 屏。 这意味着6个不同的图像,而不是两个。支持WebP格式的数量增加了一倍。

在网站上放置图片有两种主要方式,一种是在HTML中使用HTML的img元素或在CSS中用 background-image 属性。

HTML中的响应式图像

基本的HTML的 img 元素具有指向图像URL的 src 属性:

“image

但是你可以更进一步,根据屏幕的像素密度和 srcset 属性决定使用哪个图像:

在这里,我使用了两种不同的屏幕密度:1x2x 。根据实际的显示密度,浏览器会选择正确的那个。src 属性指向回退选项。

目前,除了IE,Edge和Opera Mini以外的大多数浏览器都实现了 srcset 属性。

这个解决方案似乎是朝着正确方向迈出的一步。不幸的是,对于相同的像素密度,无论显示大小如何,您的浏览器将始终选择相同的图像。相同的图像将在桌面和移动设备上结束。

我们需要更多的控制。我们可以拥有它。除了像素密度之外,scrset 属性接受宽度单位 w,相当于CSS像素。

宽度单位使浏览器能够为给定的显示功能选择正确的图像大小。

有了两个断点(600px和900px),我们可以使用三种不同的图像大小:

这里有一个警告。当浏览器决定要获取哪个图片时,它不知道我们的CSS!这时CSS文件还没有被提取。它假定图像将显示在窗口的整个宽度。

如果全宽图像是你想要的,那就好了。但是如果你想在一个只有 50vw 宽的容器中放置一个图像呢?这里就要提到 sizes 属性。让我们来看看:

通过添加 sizes =“50vw” 属性,您告诉浏览器图像将显示为 50vw,根据这些信息,浏览器将决定显示哪个图像。

但是如果你想在移动设备上的大屏幕上以 100vw 的全宽显示你的图像,sizes 属性也接受媒体查询!

您可以指定在 600px 的移动断点之下,您希望浏览器以全屏宽度显示图像,而对于高于移动断点的宽度,您希望浏览器以 50vw 显示图像。

您可以通过添加媒体查询来做到这一点:

请记住,在上面的代码行中,您正在指导浏览器选择哪个图像,因为浏览器不知道相应的CSS。您仍然必须显式地在CSS中添加断点。

这个解决方案工作得很好,但是我们在这里缺少像素密度!如果我们停在这里,我们会发送相同的图像到像素密度为 1x 的显示器和 retain 屏。幸运的是,这里有一个简单的解决办法。

图片元素

满足HTML5 picture 元素。 它接受 sourceimg 元素作为子元素。 我们可以使用 source 元素来列出我们想要提供给浏览器的额外的图片格式。

但是在我们修复像素密度之前,让我们先介绍一下WebP格式的更小更清晰的图像。

让我们在 picture 元素中添加 source 元素作为WebP格式的第一个选项,然后是指向常规JPG图像的 img 。 现在,当浏览器不支持WebP时,它将优雅地回退到 img 元素(例如Safari)。

<picture>
<source
srcset=“image.webp”
type=“image/webp” >
”image
</picture>

source 元素打开了一个全新的可能性世界。它接受媒体查询!

首先,在 media 属性中,我们使用媒体查询,然后在 srcset 属性中,我们放置合适的图像。我们可以根据需要使用尽可能多的source元素:

<picture>
<source
media=”(min-width: 900px)”
srcset=“image-lg.webp”
type=“image/webp” >
<source
media=”(min-width: 600px)”
srcset=“image-md.webp”
type=“image/webp” >
<source
srcset=“image-sm.webp”
type=“image/webp” >
”image
</picture>

以上,我们已经准备了三个WebP格式的图像,这取决于显示器的大小,并且一个JPG图像作为回退选项。

srcset 属性的最后一个秘密是它也接受像素密度。我们可以决定在哪个屏幕上以及在哪个像素密度下提供哪个图像。诀窍是将图像文件列在scrset中,后跟一个空格和像素密度因子,例如:1x2x3x 甚至是4x

<picture>
<source
media=”(min-width: 900px)”
srcset=“image-lg_1x.webp 1x, image-lg_2x.webp 2x”
type=“image/webp” >
<source
media=”(min-width: 601px)”
srcset=“image-md_1x.webp 1x, image-md_2x.webp 2x”
type=“image/webp” >
<source
media=”(max-width: 600px)”
srcset=“image-sm_1x.webp 1x, image-sm_1x.webp 1x”
type=“image/webp” >
”image
</picture>

由于我们整理了WebP格式的屏幕尺寸和像素密度,让我们仔细看看回退选项。最后,有些浏览器不支持WebP格式。

在这里,我们必须决定是否要使用1或2像素密度的图像。下面,我采取了第一个选项:

<picture>
<source
media=”(min-width: 900px)”
srcset=“image-lg_1x.webp 1x, image-lg_2x.webp 2x”
type=“image/webp” >
<source
media=”(min-width: 601px)”
srcset=“image-md_1x.webp 1x, image-md_2x.webp 2x”
type=“image/webp” >
<source
srcset=“image-sm_1x.webp 1x, image-sm_2x.webp 2x”
type=“image/webp” >
”image
</picture>

我们用 picture 元素替换了 img 元素。在可能的情况下,我们希望根据显示尺寸和2种不同的像素密度,以三种不同的大小提供WebP格式的图像。如果览器不支持 picture 元素或WebP格式,它将回落到具有三种不同大小JPG的标准 img 元素。

重要:请注意,在 img 元素中,srcset 属性应放置在 src 属性之前。否则,浏览器会首先下载 src 图像,如果在srcset 中找到了更好的图像,它也会下载这个图像。这样我们最终会得到两个图像。

我们可以更进一步,为不支持WebP格式并提供JPG文件的浏览器创建另外3个 source元素。

虽然它对Firefox很好,但我注意到Safari会同时下载 source 中列出的JPG和 img 中的JPG。再次,我们最终会得到两个图像,而不是一个。

在CSS中的响应式图片

如果我们不知道要用图像覆盖的容器的高度和宽度,我们可以使用 div 这样的通用元素,通过 background-image 属性指向图像的URL:

background-image: url(“/images/image.jpg”);

CSS与HTML类似,可以优化图像大小。

CSS中的 image-set 与HTML中的 srcset 等效。目前,它已在Chrome,Android版Chrome,Safari,iOS Safari和其他一些浏览器中得到支持。您可以添加polyfills在其他浏览器上设置 image-set ,但考虑到今天Chrome和Safari组合是70%的用户的选择,这是一个好的时机--大多数浏览器将会在不久的将来实现这个属性。

但是不要担心,常规的 background-image 作为回退选项将会起到一定作用。

这个结构与我们刚才在 srcset 属性中使用的非常相似。

要创建一个高度为 500px 的全宽图像元素,我们必须从回退选项开始--下面代码示例中的第一个 background-image 。然后,使用 -webkit-image-set ,我们需要列出不同像素密度的WebP图片。我们必须重复使用媒体查询不同断点这个过程。

一个需要重点记住的事情是,Chrome和Safari都使用WebKit布局引擎,但Safari不支持WebP格式。这就是为什么我们必须在JPG图像中添加最后一组 image-set 属性(即使它不以 -webkit 开头,它将被Safari使用)。

.bg-image {
width: 100vw;
height: 500px;

background-size: cover;
background-position: center;

background-image: url(/images/image-lg_1x.jpg)
background-image: -webkit-image-set(
url(/images/image-lg_1x.webp) 1x,
url(/images/image-lg_2x.webp) 2x
);
background-image: image-set(
url(/images/image-lg_1x.jpg) 1x,
url(/images/image-lg_2x.jpg) 2x
);

@media(max-width: 900px) {
background-image: url(/images/image-md_2x.jpg);
background-image: -webkit-image-set(
url(/images/image-md_1x.webp) 1x,
url(/images/image-md_2x.webp) 2x
);
background-image: image-set(
url(/images/image-md_1x.jpg) 1x,
url(/images/image-md_2x.jpg) 2x
);
}

@media (max-width: 600px) {
background-image: url(/images/image-sm_2x.jpg);
background-image: -webkit-image-set(
url(/images/image-sm_1x.webp) 1x,
url(/images/image-sm_2x.webp) 2x
);
background-image: image-set(
url(/images/image-sm_1x.jpg) 1x,
url(/images/image-sm_2x.jpg) 2x
);
}
}

在这里,背景图像以 div 元素为中心,并且覆盖整个区域。使用 image-set 属性,我们将两个不同的图像分配给两个不同的像素密度。

具有标准 url 的回退选项负责处理不支持 image-set 属性的浏览器。

image-set 属性的回退选项放在 background-images 前面是非常重要的。如果将其放在 image-set 属性之后,Safari会从 image-set 下载图像,如果找到具有不同文件名的图像,则会从回退选项下载图像。

其余的代码遵循相同的模式。上面,我已经添加了针对600px和900px断点的媒体查询和一组较小尺寸的相应图片。

回退选项总是必须使用JPG格式,以避免图像无法显示的情况,即浏览器不支持 image-set 属性或WebP格式。

如何内嵌小图片

为了改善用户体验,我们不仅要压缩和提供最小可能的图像,还要减少发送到服务器的请求数量。

浏览器必须为每个图像发送一个单独的请求。当发送到服务器时,请求必须首先在队列中等待,这需要时间。浏览器的请求越多,用户需要等待的时间就越长。

当你必须下载许多小图像时尤其如此。如有可能,应将徽标和图标另存为矢量图形(SVG)。小图像可以直接作为base64编码的字符串嵌入到HTML或CSS中。

我们可以将图像作为字符串传递,而不是将常规URL传递给 img 元素的 src 属性:

”img

在CSS中:

.small-image {
background-image: url(data:image/png;base64,encoded string);
}

在大多数情况下,生成的字符串将比原始图像大30%左右,但是你将节省另一次到服务器的往返时间。反对在CSS文件中使用base64编码图像的最常见理由是图像是非阻塞资源,而CSS文件是。这意味着如果将太多的小图片嵌入到CSS中,将会增加CSS文件的大小,并延长网站第一次绘制的时间。这反过来会使用户在看到任何内容之前等待更长的时间。

这里 有一个很棒的文章,关于为什么你可能会考虑放弃完全使用编码的字符串的想法。

事实可能在中间的某个地方,将一个或两个小文件作为base64字符串注入到CSS或HTML中应该不会造成任何伤害。

在本文结尾处,您将学习如何生成它们。起初可能会感到奇怪,因为这些字符串是数千个字符。你的 .logo 类可能看起来像这样,也许更长:

.logo {
background-image: url(data:image/png;base64,iVBORw0KGgoAAAA
NSUhEUgAABqIAAAFvCAMAAAAWmCq0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZS
BJbWFnZVJlYWR5ccllPAAAA3hpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAA
Dw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5U
Y3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0…);
}

如何生成响应式图片

让我们假设你刚刚保存了完美的图片,并且想要创建所有变化,这样你就可以在你的网站上使用它。

有很多工具可以帮助你。简单的工具包括compressjpeg.comcompresspng.comtinyjpg.com。更高级的工具包括用于JPEG,PNG和GIF的ImageOptim 和用于PNG的 ImageAlpha

为了完全控制压缩级别,格式和缩放比例,我需要一个工具来帮助我自动化整个过程。我不喜欢拖放几十个图片。

ImageMagicGraphicsMagick都是免费且强大的软件,与Grunt ,JavaScript任务运行器。

更好的是,有Grunt插件可以进一步简化任务。几个快速测试显示,在相同的压缩级别下,GraphicsMagick比ImageMagic生成的JPG图像小20%。所以选择是明确的。

在我们开始切断像素丛林之前,我们必须准备好工具并磨好我们的斧头。从这里下载GraphicsMagick或使用Homebrew进行安装。

brew安装graphicsmagick

接下来,全局安装Grunt的CLI:

npm install -g grunt-cli

创建一个单独的文件夹 responsive-images 并初始化项目:

mkdir responsive-images
cd responsive-images
npm init

最后,安装Grunt的本地版本:

npm install grunt --save-dev

创建两个文件夹:src / 为原始图像,dest / 为Grunt和GraphicsMagick将生成的响应图像:

mkdir src
mkdir dest

原始图像应保存在等于或大于您想在 src / 文件夹中生成的最大图像的分辨率。我以100%的质量和2880像素的宽度保存为JPG。大约是2.5MB。

首先,我们使用grunt-responsive-images插件生成响应式图像。安装它:

npm install grunt-responsive-images --save-dev

现在,在项目的根目录中,创建一个附加文件 Gruntfile.js

点击Gruntfile.js

这是我们必须配置插件的地方。

将代码复制并粘贴到 Gruntfile.js 并让我指导你完成代码:

module.exports = function(grunt) {
grunt.initConfig({
responsive_images: {
dev: {
options: {
engine: “gm”,
sizes: [
{ name: “sm”, suffix: “_1x”, quality: 60, width: 600 },
{ name: “sm”, suffix: “_2x”, quality: 60, width: 1200 },
{ name: “md”, suffix: “_1x”, quality: 60, width: 900 },
{ name: “md”, suffix: “_2x”, quality: 60, width: 1800 },
{ name: “lg”, suffix: “_1x”, quality: 60, width: 1440 },
{ name: “lg”, suffix: “_2x”, quality: 60, width: 2880 }
]
},
files: [
{
expand: true,
src: [“*/.{jpg,png}”],
cwd: “src/”,
dest: “dest/”
}
]
}
}
});

grunt.loadNpmTasks(“grunt-responsive-images”);
grunt.registerTask(“default”, [“responsive_images”]);
};

options 中,我们将GraphicsMagick设置为我们选择的引擎 engine:“gm”。您也可以通过将ImageMagick更改为 engine:“im” 来测试。

接下来,在 sizes 数组中,我们必须指定想要生成的图像的参数,例如将被追加到原始名称的 name,将被添加到名称的 suffix,还有 qualitywidth

生成的图像将具有以下命名结构:

original-[name]_[suffix}.jpg

例如,使用第一个 sizes 对象,Grunt将从原始的 my-image.jpg 生成60%压缩级别和600像素宽度的 my-image-sm_1x.jpg 图像。

在选项下面,我们需要列出源文件夹和目标文件夹,以及我们要处理的文件名称的模式。

为了启用文件对象的动态构建,我们将 expand 属性设置为 true 并定义:

  • cwd  —  源文件夹
  • src  —  要匹配的模式数组。在我们的例子中,我们要匹配源文件夹内的任何文件夹(**)以及所有带有 jpgpng 扩展名的文件
  • dest  —  目标文件夹

上面的Grunt任务将根据源图像文件扩展名生成一组JPG和/或PNG文件。我们也想产生一组相应的WebP图像。

我们需要另外一个插件来完成这个工作:grunt-cwebp。让我们来安装它:

npm install grunt-cwebp --save-dev

将以下配置添加到Gruntfile.js:

module.exports = function(grunt) {
grunt.initConfig({
responsive_images: {

},
cwebp: {
dynamic: {
options: {
q: 60
},
files: [
{
expand: true,
cwd: “dest/”,
src: [“*/.{jpg,png}”],
dest: “dest/”
}
]
}
}
});

grunt.loadNpmTasks(“grunt-responsive-images”);
grunt.loadNpmTasks(“grunt-cwebp”);

grunt.registerTask(“default”, [“responsive_images”, “cwebp”]);
};

grunt-cwebp 插件使用 dest / 文件夹作为图像的来源。 我们希望所有新生成的JPG都有自己的WebP兄弟,我们应该把它们放在同一个文件夹中。

现在,我们可以处理图像:

grunt

对于 src / 文件夹中的每个图像,Grunt将生成12张图像,包括所有必需的大小,像素密度以及JPG和WebP格式!

如何生成base64字符串

如果你想生成base64字符串内嵌你的图片,这是怎么做。

这一次,我们使用Grunt插件:grunt-base64

在一个单独的文件夹 base64-images 中创建一个新项目。 用 npm 初始化并安装Grunt的本地版本:

mkdir base64-images
cd base64-images
npm init
npm install grunt --save-dev

安装 grunt-base64 插件:

npm install grunt-base64 --save-dev

在根目录下,创建一个新的 images / 文件夹和 Gruntfile.js

mkdir images
touch Gruntfile.js

并将代码复制并粘贴到 Gruntfile.js 中:

module.exports = function(grunt) {
grunt.initConfig({
base64: {
dev: {
files: {
“images/output.b64”: [“images/*.{jpg,png}”]
}
}
}
});

grunt.loadNpmTasks(“grunt-base64”);
grunt.registerTask(“default”, [“base64”]);
};

将小的原始图像放在 images / 文件夹中并运行Grunt:

grunt

任务完成后,复制 output.b64 文件中的全部内容--这是base64字符串,你可以粘贴到 background-imageurl 或者 img 元素的 src 属性中。

还有一个更简单的方法(在Mac OS X或Linux上):

uuencode -m image-file-name remotename

remotename 没有被使用,你甚至可以放置 xyz 来获得打印到标准输出中的base64字符串—大多数情况下是在终端窗口中。

你必须使用 -m 选项来获得base64编码。

结论

响应式图像一开始可能会感到压倒性的,但使用Grunt和图片处理引擎,您可以创建一个平滑的过程,并自动执行大部分重复性任务。我保证这是值得的。您不仅会在PageSpeed Insights中大放异彩,而且还会削减您网站的第一次绘制。

就我来说,原来的939KB图像缩小了60%到380KB(JPG),在WebP格式中缩小了77%到218KB。

最后,我的像素十字军得到了回报 - 我的网站的PageSpeed Insight评级变成了绿色。

如果您发现这篇文章有用,tweet让我知道您设置了多少像素,以及它如何改进您的网站。为什么不与朋友分享?

一个点赞,两个点赞,三个点赞,四十个?

通过或多或少的点赞,您可以向我们表明哪些故事真正脱颖而出。

1K

3

  • 不要停止关注以下内容

    Go to the profile of Maciej Nowakowski

    Maciej Nowakowski

    codecamps网站的建立者——这是一个用React, Angular, GraphQL和Meteor进行一周远程编程的地方。

  • 关注

    freeCodeCamp

    freeCodeCamp

    我们社区发布值得阅读的开发、设计和数据科学的故事。

  • 1K

freeCodeCamp

当你注册Medium时,不要错过freeCodeCamp的故事。 Learn more

不要错过freeCodeCamp的故事。

获取更新 获取更新

  .u-accentColor--borderLight {border-color: #429A35 !important;} .u-accentColor--borderNormal {border-color: #429A35 !important;} .u-accentColor--borderDark {border-color: #3E8432 !important;} .u-accentColor--iconLight .svgIcon,.u-accentColor--iconLight.svgIcon {fill: #429A35 !important;} .u-accentColor--iconNormal .svgIcon,.u-accentColor--iconNormal.svgIcon {fill: #429A35 !important;} .u-accentColor--iconDark .svgIcon,.u-accentColor--iconDark.svgIcon {fill: #3E8432 !important;} .u-accentColor--textNormal {color: #3E8432 !important;} .u-accentColor--hoverTextNormal:hover {color: #3E8432 !important;} .u-accentColor--textNormal.u-accentColor--textDarken:hover {color: #3B7830 !important;} .u-accentColor--textDark {color: #3B7830 !important;} .u-accentColor--backgroundLight {background-color: #429A35 !important;} .u-accentColor--backgroundNormal {background-color: #429A35 !important;} .u-accentColor--backgroundDark {background-color: #3E8432 !important;} .u-accentColor--buttonDark {border-color: #3E8432 !important; color: #3B7830 !important;} .u-accentColor--buttonDark:hover {border-color: #3B7830 !important;} .u-accentColor--buttonDark .icon:before,.u-accentColor--buttonDark .svgIcon{color: #3E8432 !important; fill: #3E8432 !important;} .u-accentColor--buttonNormal:not(.clapButton--largePill) {border-color: #429A35 !important; color: #3E8432 !important;} .u-accentColor--buttonNormal:hover {border-color: #3E8432 !important;} .u-accentColor--buttonNormal .icon:before,.u-accentColor--buttonNormal .svgIcon{color: #429A35 !important; fill: #429A35 !important;} .u-accentColor--buttonNormal.button--filled .icon:before,.u-accentColor--buttonNormal.button--filled .svgIcon{color: rgba(255, 255, 255, 1) !important; fill: rgba(255, 255, 255, 1) !important;} .u-accentColor--buttonDark.button--filled,.u-accentColor--buttonDark.button--withChrome.is-active,.u-accentColor--fillWhenActive.is-active {background-color: #3E8432 !important; border-color: #3E8432 !important; color: rgba(255, 255, 255, 1) !important; fill: rgba(255, 255, 255, 1) !important;} .u-accentColor--buttonNormal.button--filled:not(.clapButton--largePill),.u-accentColor--buttonNormal.button--withChrome.is-active:not(.clapButton--largePill) {background-color: #429A35 !important; border-color: #429A35 !important; color: rgba(255, 255, 255, 1) !important; fill: rgba(255, 255, 255, 1) !important;} .postArticle.is-withAccentColors .markup--user,.postArticle.is-withAccentColors .markup--query {color: #3E8432 !important;}.u-tintBgColor {background-color: rgba(0, 100, 0, 1) !important;}.u-tintBgColor .u-fadeLeft:before {background-image: linear-gradient(to right, rgba(0, 100, 0, 1) 0%, rgba(0, 100, 0, 0) 100%) !important;}.u-tintBgColor .u-fadeRight:after {background-image: linear-gradient(to right, rgba(0, 100, 0, 0) 0%, rgba(0, 100, 0, 1) 100%) !important;} .u-tintSpectrum .u-baseColor--borderLight {border-color: #8ABF7C !important;} .u-tintSpectrum .u-baseColor--borderNormal {border-color: #B5DDA8 !important;} .u-tintSpectrum .u-baseColor--borderDark {border-color: #DFF8D4 !important;} .u-tintSpectrum .u-baseColor--iconLight .svgIcon,.u-tintSpectrum .u-baseColor--iconLight.svgIcon {fill: #8ABF7C !important;} .u-tintSpectrum .u-baseColor--iconNormal .svgIcon,.u-tintSpectrum .u-baseColor--iconNormal.svgIcon {fill: #B5DDA8 !important;} .u-tintSpectrum .u-baseColor--iconDark .svgIcon,.u-tintSpectrum .u-baseColor--iconDark.svgIcon {fill: #DFF8D4 !important;} .u-tintSpectrum .u-baseColor--textNormal {color: #B5DDA8 !important;} .u-tintSpectrum .u-baseColor--textNormal.u-baseColor--textDarken:hover {color: #F3FFEA !important;} .u-tintSpectrum .u-baseColor--textDark {color: #F3FFEA !important;} .u-tintSpectrum .u-baseColor--textDarker {color: #F3FFEA !important;} .u-tintSpectrum .u-baseColor--backgroundLight {background-color: #8ABF7C !important;} .u-tintSpectrum .u-baseColor--backgroundNormal {background-color: #B5DDA8 !important;} .u-tintSpectrum .u-baseColor--backgroundDark {background-color: #DFF8D4 !important;} .u-tintSpectrum .u-baseColor--buttonLight {border-color: #8ABF7C !important; color: #8ABF7C !important;} .u-tintSpectrum .u-baseColor--buttonLight:hover {border-color: #8ABF7C !important;} .u-tintSpectrum .u-baseColor--buttonLight .icon:before,.u-tintSpectrum .u-baseColor--buttonLight .svgIcon {color: #8ABF7C !important; fill: #8ABF7C !important;} .u-tintSpectrum .u-baseColor--buttonDark {border-color: #DFF8D4 !important; color: #F3FFEA !important;} .u-tintSpectrum .u-baseColor--buttonDark:hover {border-color: #F3FFEA !important;} .u-tintSpectrum .u-baseColor--buttonDark .icon:before,.u-tintSpectrum .u-baseColor--buttonDark .svgIcon {color: #DFF8D4 !important; fill: #DFF8D4 !important;} .u-tintSpectrum .u-baseColor--buttonNormal {border-color: #B5DDA8 !important; color: #B5DDA8 !important;} .u-tintSpectrum .u-baseColor--buttonNormal:hover {border-color: #DFF8D4 !important;} .u-tintSpectrum .u-baseColor--buttonNormal .icon:before,.u-tintSpectrum .u-baseColor--buttonNormal .svgIcon {color: #B5DDA8 !important; fill: #B5DDA8 !important;} .u-tintSpectrum .u-baseColor--buttonDark.button--filled,.u-tintSpectrum .u-baseColor--buttonDark.button--withChrome.is-active {background-color: #DFF8D4 !important; border-color: #DFF8D4 !important; color: rgba(0, 100, 0, 1) !important; fill: rgba(0, 100, 0, 1) !important;} .u-tintSpectrum .u-baseColor--buttonNormal.button--filled,.u-tintSpectrum .u-baseColor--buttonNormal.button--withChrome.is-active {background-color: #B5DDA8 !important; border-color: #B5DDA8 !important; color: rgba(0, 100, 0, 1) !important; fill: rgba(0, 100, 0, 1) !important;} .u-tintSpectrum .u-baseColor--link {color: #B5DDA8 !important;} .u-tintSpectrum .u-baseColor--link.link--darkenOnHover:hover {color: #F3FFEA !important;} .u-tintSpectrum .u-baseColor--link.link--darken:hover,.u-tintSpectrum .u-baseColor--link.link--darken:focus,.u-tintSpectrum .u-baseColor--link.link--darken:active {color: #F3FFEA !important;} .u-tintSpectrum .u-baseColor--link.link--dark {color: #F3FFEA !important;} .u-tintSpectrum .u-baseColor--link.link--dark.link--darken:hover,.u-tintSpectrum .u-baseColor--link.link--dark.link--darken:focus,.u-tintSpectrum .u-baseColor--link.link--dark.link--darken:active {color: #F3FFEA !important;} .u-tintSpectrum .u-baseColor--link.link--darker {color: #F3FFEA !important;} .u-tintSpectrum .u-baseColor--placeholderNormal ::-webkit-input-placeholder {color: #8ABF7C;} .u-tintSpectrum .u-baseColor--placeholderNormal ::-moz-placeholder {color: #8ABF7C;} .u-tintSpectrum .u-baseColor--placeholderNormal :-ms-input-placeholder {color: #8ABF7C;} .u-tintSpectrum .svgIcon--logoNew path:nth-child(1) {stroke: none !important; fill: #458C39 !important;} .u-tintSpectrum .svgIcon--logoNew path:nth-child(2) {stroke: none !important; fill: #5D9E50 !important;} .u-tintSpectrum .svgIcon--logoNew path:nth-child(3) {stroke: none !important; fill: #8ABF7C !important;} .u-tintSpectrum .svgIcon--logoNew path:nth-child(4) {stroke: none !important; fill: #B5DDA8 !important;} .u-tintSpectrum .svgIcon--logoWordmark {stroke: none !important; fill: #F3FFEA !important;} .u-tintSpectrum .svgIcon--logoMonogram {stroke: none !important; fill: #F3FFEA !important;} .u-tintSpectrum .ui-h1,.u-tintSpectrum .ui-h2,.u-tintSpectrum .ui-h3,.u-tintSpectrum .ui-h4,.u-tintSpectrum .ui-brand1,.u-tintSpectrum .ui-brand2,.u-tintSpectrum .ui-captionStrong {color: #F3FFEA !important; fill: #F3FFEA !important;} .u-tintSpectrum .ui-body,.u-tintSpectrum .ui-caps {color: #F3FFEA !important; fill: #F3FFEA !important;} .u-tintSpectrum .ui-summary,.u-tintSpectrum .ui-caption {color: #8ABF7C !important; fill: #8ABF7C !important;} .u-tintSpectrum .u-accentColor--borderLight {border-color: #8ABF7C !important;} .u-tintSpectrum .u-accentColor--borderNormal {border-color: #B5DDA8 !important;} .u-tintSpectrum .u-accentColor--borderDark {border-color: #DFF8D4 !important;} .u-tintSpectrum .u-accentColor--iconLight .svgIcon,.u-tintSpectrum .u-accentColor--iconLight.svgIcon {fill: #8ABF7C !important;} .u-tintSpectrum .u-accentColor--iconNormal .svgIcon,.u-tintSpectrum .u-accentColor--iconNormal.svgIcon {fill: #B5DDA8 !important;} .u-tintSpectrum .u-accentColor--iconDark .svgIcon,.u-tintSpectrum .u-accentColor--iconDark.svgIcon {fill: #DFF8D4 !important;} .u-tintSpectrum .u-accentColor--textNormal {color: #B5DDA8 !important;} .u-tintSpectrum .u-accentColor--hoverTextNormal:hover {color: #B5DDA8 !important;} .u-tintSpectrum .u-accentColor--textNormal.u-accentColor--textDarken:hover {color: #F3FFEA !important;} .u-tintSpectrum .u-accentColor--textDark {color: #F3FFEA !important;} .u-tintSpectrum .u-accentColor--backgroundLight {background-color: #8ABF7C !important;} .u-tintSpectrum .u-accentColor--backgroundNormal {background-color: #B5DDA8 !important;} .u-tintSpectrum .u-accentColor--backgroundDark {background-color: #DFF8D4 !important;} .u-tintSpectrum .u-accentColor--buttonDark {border-color: #DFF8D4 !important; color: #F3FFEA !important;} .u-tintSpectrum .u-accentColor--buttonDark:hover {border-color: #F3FFEA !important;} .u-tintSpectrum .u-accentColor--buttonDark .icon:before,.u-tintSpectrum .u-accentColor--buttonDark .svgIcon{color: #DFF8D4 !important; fill: #DFF8D4 !important;} .u-tintSpectrum .u-accentColor--buttonNormal:not(.clapButton--largePill) {border-color: #B5DDA8 !important; color: #B5DDA8 !important;} .u-tintSpectrum .u-accentColor--buttonNormal:hover {border-color: #DFF8D4 !important;} .u-tintSpectrum .u-accentColor--buttonNormal .icon:before,.u-tintSpectrum .u-accentColor--buttonNormal .svgIcon{color: #B5DDA8 !important; fill: #B5DDA8 !important;} .u-tintSpectrum .u-accentColor--buttonNormal.button--filled .icon:before,.u-tintSpectrum .u-accentColor--buttonNormal.button--filled .svgIcon{color: rgba(0, 100, 0, 1) !important; fill: rgba(0, 100, 0, 1) !important;} .u-tintSpectrum .u-accentColor--buttonDark.button--filled,.u-tintSpectrum .u-accentColor--buttonDark.button--withChrome.is-active,.u-tintSpectrum .u-accentColor--fillWhenActive.is-active {background-color: #DFF8D4 !important; border-color: #DFF8D4 !important; color: rgba(0, 100, 0, 1) !important; fill: rgba(0, 100, 0, 1) !important;} .u-tintSpectrum .u-accentColor--buttonNormal.button--filled:not(.clapButton--largePill),.u-tintSpectrum .u-accentColor--buttonNormal.button--withChrome.is-active:not(.clapButton--largePill) {background-color: #B5DDA8 !important; border-color: #B5DDA8 !important; color: rgba(0, 100, 0, 1) !important; fill: rgba(0, 100, 0, 1) !important;} .u-tintSpectrum .postArticle.is-withAccentColors .markup--user,.u-tintSpectrum .postArticle.is-withAccentColors .markup--query {color: #B5DDA8 !important;} .u-accentColor--highlightFaint {background-color: rgba(222, 250, 211, 1) !important;} .u-accentColor--highlightStrong.is-active .svgIcon {fill: rgba(172, 246, 153, 1) !important;} .postArticle.is-withAccentColors .markup--quote.is-other {background-color: rgba(222, 250, 211, 1) !important;} body.is-withMagicUnderlines .postArticle.is-withAccentColors .markup--quote.is-other {background-color: transparent !important; background-image: linear-gradient(to bottom, rgba(222, 250, 211, 1), rgba(222, 250, 211, 1));} .postArticle.is-withAccentColors .markup--quote.is-me {background-color: rgba(195, 248, 179, 1) !important;} body.is-withMagicUnderlines .postArticle.is-withAccentColors .markup--quote.is-me {background-color: transparent !important; background-image: linear-gradient(to bottom, rgba(195, 248, 179, 1), rgba(195, 248, 179, 1));} .postArticle.is-withAccentColors .markup--quote.is-targeted {background-color: rgba(172, 246, 153, 1) !important;} body.is-withMagicUnderlines .postArticle.is-withAccentColors .markup--quote.is-targeted {background-color: transparent !important; background-image: linear-gradient(to bottom, rgba(172, 246, 153, 1), rgba(172, 246, 153, 1));} .postArticle.is-withAccentColors .markup--quote.is-selected {background-color: rgba(172, 246, 153, 1) !important;} body.is-withMagicUnderlines .postArticle.is-withAccentColors .markup--quote.is-selected {background-color: transparent !important; background-image: linear-gradient(to bottom, rgba(172, 246, 153, 1), rgba(172, 246, 153, 1));} .postArticle.is-withAccentColors .markup--highlight {background-color: rgba(172, 246, 153, 1) !important;} body.is-withMagicUnderlines .postArticle.is-withAccentColors .markup--highlight {background-color: transparent !important; background-image: linear-gradient(to bottom, rgba(172, 246, 153, 1), rgba(172, 246, 153, 1));}.u-baseColor--iconNormal.avatar-halo {fill: rgba(0, 0, 0, 0.4980392156862745) !important;}.u-imageBgColor {background-color: rgba(0, 0, 0, 0.24705882352941178);} .u-imageSpectrum .u-baseColor--borderLight {border-color: rgba(255, 255, 255, 0.6980392156862745) !important;} .u-imageSpectrum .u-baseColor--borderNormal {border-color: rgba(255, 255, 255, 0.8980392156862745) !important;} .u-imageSpectrum .u-baseColor--borderDark {border-color: rgba(255, 255, 255, 0.9490196078431372) !important;} .u-imageSpectrum .u-baseColor--iconLight .svgIcon,.u-imageSpectrum .u-baseColor--iconLight.svgIcon {fill: rgba(255, 255, 255, 0.8) !important;} .u-imageSpectrum .u-baseColor--iconNormal .svgIcon,.u-imageSpectrum .u-baseColor--iconNormal.svgIcon {fill: rgba(255, 255, 255, 0.9490196078431372) !important;} .u-imageSpectrum .u-baseColor--iconDark .svgIcon,.u-imageSpectrum .u-baseColor--iconDark.svgIcon {fill: rgba(255, 255, 255, 1) !important;} .u-imageSpectrum .u-baseColor--textNormal {color: rgba(255, 255, 255, 0.9490196078431372) !important;} .u-imageSpectrum .u-baseColor--textNormal.u-baseColor--textDarken:hover {color: rgba(255, 255, 255, 1) !important;} .u-imageSpectrum .u-baseColor--textDark {color: rgba(255, 255, 255, 1) !important;} .u-imageSpectrum .u-baseColor--textDarker {color: rgba(255, 255, 255, 1) !important;} .u-imageSpectrum .u-baseColor--backgroundLight {background-color: rgba(255, 255, 255, 0.8980392156862745) !important;} .u-imageSpectrum .u-baseColor--backgroundNormal {background-color: rgba(255, 255, 255, 0.9490196078431372) !important;} .u-imageSpectrum .u-baseColor--backgroundDark {background-color: rgba(255, 255, 255, 1) !important;} .u-imageSpectrum .u-baseColor--buttonLight {border-color: rgba(255, 255, 255, 0.6980392156862745) !important; color: rgba(255, 255, 255, 0.8) !important;} .u-imageSpectrum .u-baseColor--buttonLight:hover {border-color: rgba(255, 255, 255, 0.6980392156862745) !important;} .u-imageSpectrum .u-baseColor--buttonLight .icon:before,.u-imageSpectrum .u-baseColor--buttonLight .svgIcon {color: rgba(255, 255, 255, 0.8) !important; fill: rgba(255, 255, 255, 0.8) !important;} .u-imageSpectrum .u-baseColor--buttonDark {border-color: rgba(255, 255, 255, 0.9490196078431372) !important; color: rgba(255, 255, 255, 1) !important;} .u-imageSpectrum .u-baseColor--buttonDark:hover {border-color: rgba(255, 255, 255, 1) !important;} .u-imageSpectrum .u-baseColor--buttonDark .icon:before,.u-imageSpectrum .u-baseColor--buttonDark .svgIcon {color: rgba(255, 255, 255, 1) !important; fill: rgba(255, 255, 255, 1) !important;} .u-imageSpectrum .u-baseColor--buttonNormal {border-color: rgba(255, 255, 255, 0.8980392156862745) !important; color: rgba(255, 255, 255, 0.9490196078431372) !important;} .u-imageSpectrum .u-baseColor--buttonNormal:hover {border-color: rgba(255, 255, 255, 0.9490196078431372) !important;} .u-imageSpectrum .u-baseColor--buttonNormal .icon:before,.u-imageSpectrum .u-baseColor--buttonNormal .svgIcon {color: rgba(255, 255, 255, 0.9490196078431372) !important; fill: rgba(255, 255, 255, 0.9490196078431372) !important;} .u-imageSpectrum .u-baseColor--buttonDark.button--filled,.u-imageSpectrum .u-baseColor--buttonDark.button--withChrome.is-active {background-color: rgba(255, 255, 255, 1) !important; border-color: rgba(255, 255, 255, 1) !important; color: rgba(0, 0, 0, 0.24705882352941178) !important; fill: rgba(0, 0, 0, 0.24705882352941178) !important;} .u-imageSpectrum .u-baseColor--buttonNormal.button--filled,.u-imageSpectrum .u-baseColor--buttonNormal.button--withChrome.is-active {background-color: rgba(255, 255, 255, 0.9490196078431372) !important; border-color: rgba(255, 255, 255, 0.9490196078431372) !important; color: rgba(0, 0, 0, 0.24705882352941178) !important; fill: rgba(0, 0, 0, 0.24705882352941178) !important;} .u-imageSpectrum .u-baseColor--link {color: rgba(255, 255, 255, 0.9490196078431372) !important;} .u-imageSpectrum .u-baseColor--link.link--darkenOnHover:hover {color: rgba(255, 255, 255, 1) !important;} .u-imageSpectrum .u-baseColor--link.link--darken:hover,.u-imageSpectrum .u-baseColor--link.link--darken:focus,.u-imageSpectrum .u-baseColor--link.link--darken:active {color: rgba(255, 255, 255, 1) !important;} .u-imageSpectrum .u-baseColor--link.link--dark {color: rgba(255, 255, 255, 1) !important;} .u-imageSpectrum .u-baseColor--link.link--dark.link--darken:hover,.u-imageSpectrum .u-baseColor--link.link--dark.link--darken:focus,.u-imageSpectrum .u-baseColor--link.link--dark.link--darken:active {color: rgba(255, 255, 255, 1) !important;} .u-imageSpectrum .u-baseColor--link.link--darker {color: rgba(255, 255, 255, 1) !important;} .u-imageSpectrum .u-baseColor--placeholderNormal ::-webkit-input-placeholder {color: rgba(255, 255, 255, 0.8);} .u-imageSpectrum .u-baseColor--placeholderNormal ::-moz-placeholder {color: rgba(255, 255, 255, 0.8);} .u-imageSpectrum .u-baseColor--placeholderNormal :-ms-input-placeholder {color: rgba(255, 255, 255, 0.8);} .u-imageSpectrum .svgIcon--logoNew path:nth-child(1) {stroke: none !important; fill: rgba(255, 255, 255, 0.4) !important;} .u-imageSpectrum .svgIcon--logoNew path:nth-child(2) {stroke: none !important; fill: rgba(255, 255, 255, 0.4980392156862745) !important;} .u-imageSpectrum .svgIcon--logoNew path:nth-child(3) {stroke: none !important; fill: rgba(255, 255, 255, 0.6980392156862745) !important;} .u-imageSpectrum .svgIcon--logoNew path:nth-child(4) {stroke: none !important; fill: rgba(255, 255, 255, 0.8980392156862745) !important;} .u-imageSpectrum .svgIcon--logoWordmark {stroke: none !important; fill: rgba(255, 255, 255, 1) !important;} .u-imageSpectrum .svgIcon--logoMonogram {stroke: none !important; fill: rgba(255, 255, 255, 1) !important;} .u-imageSpectrum .ui-h1,.u-imageSpectrum .ui-h2,.u-imageSpectrum .ui-h3,.u-imageSpectrum .ui-h4,.u-imageSpectrum .ui-brand1,.u-imageSpectrum .ui-brand2,.u-imageSpectrum .ui-captionStrong {color: rgba(255, 255, 255, 1) !important; fill: rgba(255, 255, 255, 1) !important;} .u-imageSpectrum .ui-body,.u-imageSpectrum .ui-caps {color: rgba(255, 255, 255, 1) !important; fill: rgba(255, 255, 255, 1) !important;} .u-imageSpectrum .ui-summary,.u-imageSpectrum .ui-caption {color: rgba(255, 255, 255, 0.8) !important; fill: rgba(255, 255, 255, 0.8) !important;} .u-imageSpectrum .u-accentColor--borderLight {border-color: rgba(255, 255, 255, 0.6980392156862745) !important;} .u-imageSpectrum .u-accentColor--borderNormal {border-color: rgba(255, 255, 255, 0.8980392156862745) !important;} .u-imageSpectrum .u-accentColor--borderDark {border-color: rgba(255, 255, 255, 0.9490196078431372) !important;} .u-imageSpectrum .u-accentColor--iconLight .svgIcon,.u-imageSpectrum .u-accentColor--iconLight.svgIcon {fill: rgba(255, 255, 255, 0.8) !important;} .u-imageSpectrum .u-accentColor--iconNormal .svgIcon,.u-imageSpectrum .u-accentColor--iconNormal.svgIcon {fill: rgba(255, 255, 255, 0.9490196078431372) !important;} .u-imageSpectrum .u-accentColor--iconDark .svgIcon,.u-imageSpectrum .u-accentColor--iconDark.svgIcon {fill: rgba(255, 255, 255, 1) !important;} .u-imageSpectrum .u-accentColor--textNormal {color: rgba(255, 255, 255, 0.9490196078431372) !important;} .u-imageSpectrum .u-accentColor--hoverTextNormal:hover {color: rgba(255, 255, 255, 0.9490196078431372) !important;} .u-imageSpectrum .u-accentColor--textNormal.u-accentColor--textDarken:hover {color: rgba(255, 255, 255, 1) !important;} .u-imageSpectrum .u-accentColor--textDark {color: rgba(255, 255, 255, 1) !important;} .u-imageSpectrum .u-accentColor--backgroundLight {background-color: rgba(255, 255, 255, 0.8980392156862745) !important;} .u-imageSpectrum .u-accentColor--backgroundNormal {background-color: rgba(255, 255, 255, 0.9490196078431372) !important;} .u-imageSpectrum .u-accentColor--backgroundDark {background-color: rgba(255, 255, 255, 1) !important;} .u-imageSpectrum .u-accentColor--buttonDark {border-color: rgba(255, 255, 255, 0.9490196078431372) !important; color: rgba(255, 255, 255, 1) !important;} .u-imageSpectrum .u-accentColor--buttonDark:hover {border-color: rgba(255, 255, 255, 1) !important;} .u-imageSpectrum .u-accentColor--buttonDark .icon:before,.u-imageSpectrum .u-accentColor--buttonDark .svgIcon{color: rgba(255, 255, 255, 1) !important; fill: rgba(255, 255, 255, 1) !important;} .u-imageSpectrum .u-accentColor--buttonNormal:not(.clapButton--largePill) {border-color: rgba(255, 255, 255, 0.8980392156862745) !important; color: rgba(255, 255, 255, 0.9490196078431372) !important;} .u-imageSpectrum .u-accentColor--buttonNormal:hover {border-color: rgba(255, 255, 255, 0.9490196078431372) !important;} .u-imageSpectrum .u-accentColor--buttonNormal .icon:before,.u-imageSpectrum .u-accentColor--buttonNormal .svgIcon{color: rgba(255, 255, 255, 0.9490196078431372) !important; fill: rgba(255, 255, 255, 0.9490196078431372) !important;} .u-imageSpectrum .u-accentColor--buttonNormal.button--filled .icon:before,.u-imageSpectrum .u-accentColor--buttonNormal.button--filled .svgIcon{color: rgba(0, 0, 0, 0.24705882352941178) !important; fill: rgba(0, 0, 0, 0.24705882352941178) !important;} .u-imageSpectrum .u-accentColor--buttonDark.button--filled,.u-imageSpectrum .u-accentColor--buttonDark.button--withChrome.is-active,.u-imageSpectrum .u-accentColor--fillWhenActive.is-active {background-color: rgba(255, 255, 255, 1) !important; border-color: rgba(255, 255, 255, 1) !important; color: rgba(0, 0, 0, 0.24705882352941178) !important; fill: rgba(0, 0, 0, 0.24705882352941178) !important;} .u-imageSpectrum .u-accentColor--buttonNormal.button--filled:not(.clapButton--largePill),.u-imageSpectrum .u-accentColor--buttonNormal.button--withChrome.is-active:not(.clapButton--largePill) {background-color: rgba(255, 255, 255, 0.9490196078431372) !important; border-color: rgba(255, 255, 255, 0.9490196078431372) !important; color: rgba(0, 0, 0, 0.24705882352941178) !important; fill: rgba(0, 0, 0, 0.24705882352941178) !important;} .u-imageSpectrum .postArticle.is-withAccentColors .markup--user,.u-imageSpectrum .postArticle.is-withAccentColors .markup--query {color: rgba(255, 255, 255, 0.9490196078431372) !important;} .u-imageSpectrum .u-accentColor--highlightFaint {background-color: rgba(255, 255, 255, 0.2) !important;} .u-imageSpectrum .u-accentColor--highlightStrong.is-active .svgIcon {fill: rgba(255, 255, 255, 0.6) !important;} .postArticle.is-withAccentColors .u-imageSpectrum .markup--quote.is-other {background-color: rgba(255, 255, 255, 0.2) !important;} body.is-withMagicUnderlines .postArticle.is-withAccentColors .u-imageSpectrum .markup--quote.is-other {background-color: transparent !important; background-image: linear-gradient(to bottom, rgba(255, 255, 255, 0.2), rgba(255, 255, 255, 0.2));} .postArticle.is-withAccentColors .u-imageSpectrum .markup--quote.is-me {background-color: rgba(255, 255, 255, 0.4) !important;} body.is-withMagicUnderlines .postArticle.is-withAccentColors .u-imageSpectrum .markup--quote.is-me {background-color: transparent !important; background-image: linear-gradient(to bottom, rgba(255, 255, 255, 0.4), rgba(255, 255, 255, 0.4));} .postArticle.is-withAccentColors .u-imageSpectrum .markup--quote.is-targeted {background-color: rgba(255, 255, 255, 0.6) !important;} body.is-withMagicUnderlines .postArticle.is-withAccentColors .u-imageSpectrum .markup--quote.is-targeted {background-color: transparent !important; background-image: linear-gradient(to bottom, rgba(255, 255, 255, 0.6), rgba(255, 255, 255, 0.6));} .postArticle.is-withAccentColors .u-imageSpectrum .markup--quote.is-selected {background-color: rgba(255, 255, 255, 0.6) !important;} body.is-withMagicUnderlines .postArticle.is-withAccentColors .u-imageSpectrum .markup--quote.is-selected {background-color: transparent !important; background-image: linear-gradient(to bottom, rgba(255, 255, 255, 0.6), rgba(255, 255, 255, 0.6));} .postArticle.is-withAccentColors .u-imageSpectrum .markup--highlight {background-color: rgba(255, 255, 255, 0.6) !important;} body.is-withMagicUnderlines .postArticle.is-withAccentColors .u-imageSpectrum .markup--highlight {background-color: transparent !important; background-image: linear-gradient(to bottom, rgba(255, 255, 255, 0.6), rgba(255, 255, 255, 0.6));}.u-resetSpectrum .u-tintBgColor {background-color: rgba(255, 255, 255, 1) !important;}.u-resetSpectrum .u-tintBgColor .u-fadeLeft:before {background-image: linear-gradient(to right, rgba(255, 255, 255, 1) 0%, rgba(255, 255, 255, 0) 100%) !important;}.u-resetSpectrum .u-tintBgColor .u-fadeRight:after {background-image: linear-gradient(to right, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 1) 100%) !important;} .u-resetSpectrum .u-baseColor--borderLight {border-color: rgba(0, 0, 0, 0.2980392156862745) !important;} .u-resetSpectrum .u-baseColor--borderNormal {border-color: rgba(0, 0, 0, 0.4980392156862745) !important;} .u-resetSpectrum .u-baseColor--borderDark {border-color: rgba(0, 0, 0, 0.6) !important;} .u-resetSpectrum .u-baseColor--iconLight .svgIcon,.u-resetSpectrum .u-baseColor--iconLight.svgIcon {fill: rgba(0, 0, 0, 0.2980392156862745) !important;} .u-resetSpectrum .u-baseColor--iconNormal .svgIcon,.u-resetSpectrum .u-baseColor--iconNormal.svgIcon {fill: rgba(0, 0, 0, 0.4980392156862745) !important;} .u-resetSpectrum .u-baseColor--iconDark .svgIcon,.u-resetSpectrum .u-baseColor--iconDark.svgIcon {fill: rgba(0, 0, 0, 0.6) !important;} .u-resetSpectrum .u-baseColor--textNormal {color: rgba(0, 0, 0, 0.4980392156862745) !important;} .u-resetSpectrum .u-baseColor--textNormal.u-baseColor--textDarken:hover {color: rgba(0, 0, 0, 0.6) !important;} .u-resetSpectrum .u-baseColor--textDark {color: rgba(0, 0, 0, 0.6) !important;} .u-resetSpectrum .u-baseColor--textDarker {color: rgba(0, 0, 0, 0.8) !important;} .u-resetSpectrum .u-baseColor--backgroundLight {background-color: rgba(0, 0, 0, 0.09803921568627451) !important;} .u-resetSpectrum .u-baseColor--backgroundNormal {background-color: rgba(0, 0, 0, 0.2) !important;} .u-resetSpectrum .u-baseColor--backgroundDark {background-color: rgba(0, 0, 0, 0.2980392156862745) !important;} .u-resetSpectrum .u-baseColor--buttonLight {border-color: rgba(0, 0, 0, 0.2980392156862745) !important; color: rgba(0, 0, 0, 0.2980392156862745) !important;} .u-resetSpectrum .u-baseColor--buttonLight:hover {border-color: rgba(0, 0, 0, 0.2980392156862745) !important;} .u-resetSpectrum .u-baseColor--buttonLight .icon:before,.u-resetSpectrum .u-baseColor--buttonLight .svgIcon {color: rgba(0, 0, 0, 0.2980392156862745) !important; fill: rgba(0, 0, 0, 0.2980392156862745) !important;} .u-resetSpectrum .u-baseColor--buttonDark {border-color: rgba(0, 0, 0, 0.6) !important; color: rgba(0, 0, 0, 0.6) !important;} .u-resetSpectrum .u-baseColor--buttonDark:hover {border-color: rgba(0, 0, 0, 0.8) !important;} .u-resetSpectrum .u-baseColor--buttonDark .icon:before,.u-resetSpectrum .u-baseColor--buttonDark .svgIcon {color: rgba(0, 0, 0, 0.6) !important; fill: rgba(0, 0, 0, 0.6) !important;} .u-resetSpectrum .u-baseColor--buttonNormal {border-color: rgba(0, 0, 0, 0.4980392156862745) !important; color: rgba(0, 0, 0, 0.4980392156862745) !important;} .u-resetSpectrum .u-baseColor--buttonNormal:hover {border-color: rgba(0, 0, 0, 0.6) !important;} .u-resetSpectrum .u-baseColor--buttonNormal .icon:before,.u-resetSpectrum .u-baseColor--buttonNormal .svgIcon {color: rgba(0, 0, 0, 0.4980392156862745) !important; fill: rgba(0, 0, 0, 0.4980392156862745) !important;} .u-resetSpectrum .u-baseColor--buttonDark.button--filled,.u-resetSpectrum .u-baseColor--buttonDark.button--withChrome.is-active {background-color: rgba(0, 0, 0, 0.2980392156862745) !important; border-color: rgba(0, 0, 0, 0.2980392156862745) !important; color: rgba(255, 255, 255, 1) !important; fill: rgba(255, 255, 255, 1) !important;} .u-resetSpectrum .u-baseColor--buttonNormal.button--filled,.u-resetSpectrum .u-baseColor--buttonNormal.button--withChrome.is-active {background-color: rgba(0, 0, 0, 0.2) !important; border-color: rgba(0, 0, 0, 0.2) !important; color: rgba(255, 255, 255, 1) !important; fill: rgba(255, 255, 255, 1) !important;} .u-resetSpectrum .u-baseColor--link {color: rgba(0, 0, 0, 0.4980392156862745) !important;} .u-resetSpectrum .u-baseColor--link.link--darkenOnHover:hover {color: rgba(0, 0, 0, 0.6) !important;} .u-resetSpectrum .u-baseColor--link.link--darken:hover,.u-resetSpectrum .u-baseColor--link.link--darken:focus,.u-resetSpectrum .u-baseColor--link.link--darken:active {color: rgba(0, 0, 0, 0.6) !important;} .u-resetSpectrum .u-baseColor--link.link--dark {color: rgba(0, 0, 0, 0.6) !important;} .u-resetSpectrum .u-baseColor--link.link--dark.link--darken:hover,.u-resetSpectrum .u-baseColor--link.link--dark.link--darken:focus,.u-resetSpectrum .u-baseColor--link.link--dark.link--darken:active {color: rgba(0, 0, 0, 0.8) !important;} .u-resetSpectrum .u-baseColor--link.link--darker {color: rgba(0, 0, 0, 0.8) !important;} .u-resetSpectrum .u-baseColor--placeholderNormal ::-webkit-input-placeholder {color: rgba(0, 0, 0, 0.2980392156862745);} .u-resetSpectrum .u-baseColor--placeholderNormal ::-moz-placeholder {color: rgba(0, 0, 0, 0.2980392156862745);} .u-resetSpectrum .u-baseColor--placeholderNormal :-ms-input-placeholder {color: rgba(0, 0, 0, 0.2980392156862745);} .u-resetSpectrum .svgIcon--logoNew path:nth-child(1) {stroke: none !important; fill: rgba(0, 0, 0, 0.2) !important;} .u-resetSpectrum .svgIcon--logoNew path:nth-child(2) {stroke: none !important; fill: rgba(0, 0, 0, 0.2980392156862745) !important;} .u-resetSpectrum .svgIcon--logoNew path:nth-child(3) {stroke: none !important; fill: rgba(0, 0, 0, 0.4) !important;} .u-resetSpectrum .svgIcon--logoNew path:nth-child(4) {stroke: none !important; fill: rgba(0, 0, 0, 0.4980392156862745) !important;} .u-resetSpectrum .svgIcon--logoWordmark {stroke: none !important; fill: rgba(0, 0, 0, 0.6) !important;} .u-resetSpectrum .svgIcon--logoMonogram {stroke: none !important; fill: rgba(0, 0, 0, 0.6) !important;} .u-resetSpectrum .ui-h1,.u-resetSpectrum .ui-h2,.u-resetSpectrum .ui-h3,.u-resetSpectrum .ui-h4,.u-resetSpectrum .ui-brand1,.u-resetSpectrum .ui-brand2,.u-resetSpectrum .ui-captionStrong {color: rgba(0, 0, 0, 0.8) !important; fill: rgba(0, 0, 0, 0.8) !important;} .u-resetSpectrum .ui-body,.u-resetSpectrum .ui-caps {color: rgba(0, 0, 0, 0.6) !important; fill: rgba(0, 0, 0, 0.6) !important;} .u-resetSpectrum .ui-summary,.u-resetSpectrum .ui-caption {color: rgba(0, 0, 0, 0.2980392156862745) !important; fill: rgba(0, 0, 0, 0.2980392156862745) !important;} .u-resetSpectrum .u-accentColor--borderLight {border-color: rgba(2, 184, 117, 1) !important;} .u-resetSpectrum .u-accentColor--borderNormal {border-color: rgba(2, 184, 117, 1) !important;} .u-resetSpectrum .u-accentColor--borderDark {border-color: rgba(0, 171, 107, 1) !important;} .u-resetSpectrum .u-accentColor--iconLight .svgIcon,.u-resetSpectrum .u-accentColor--iconLight.svgIcon {fill: rgba(2, 184, 117, 1) !important;} .u-resetSpectrum .u-accentColor--iconNormal .svgIcon,.u-resetSpectrum .u-accentColor--iconNormal.svgIcon {fill: rgba(0, 171, 107, 1) !important;} .u-resetSpectrum .u-accentColor--iconDark .svgIcon,.u-resetSpectrum .u-accentColor--iconDark.svgIcon {fill: rgba(28, 153, 99, 1) !important;} .u-resetSpectrum .u-accentColor--textNormal {color: rgba(0, 171, 107, 1) !important;} .u-resetSpectrum .u-accentColor--hoverTextNormal:hover {color: rgba(0, 171, 107, 1) !important;} .u-resetSpectrum .u-accentColor--textNormal.u-accentColor--textDarken:hover {color: rgba(28, 153, 99, 1) !important;} .u-resetSpectrum .u-accentColor--textDark {color: rgba(28, 153, 99, 1) !important;} .u-resetSpectrum .u-accentColor--backgroundLight {background-color: rgba(2, 184, 117, 1) !important;} .u-resetSpectrum .u-accentColor--backgroundNormal {background-color: rgba(0, 171, 107, 1) !important;} .u-resetSpectrum .u-accentColor--backgroundDark {background-color: rgba(28, 153, 99, 1) !important;} .u-resetSpectrum .u-accentColor--buttonDark {border-color: rgba(0, 171, 107, 1) !important; color: rgba(28, 153, 99, 1) !important;} .u-resetSpectrum .u-accentColor--buttonDark:hover {border-color: rgba(28, 153, 99, 1) !important;} .u-resetSpectrum .u-accentColor--buttonDark .icon:before,.u-resetSpectrum .u-accentColor--buttonDark .svgIcon{color: rgba(28, 153, 99, 1) !important; fill: rgba(28, 153, 99, 1) !important;} .u-resetSpectrum .u-accentColor--buttonNormal:not(.clapButton--largePill) {border-color: rgba(2, 184, 117, 1) !important; color: rgba(0, 171, 107, 1) !important;} .u-resetSpectrum .u-accentColor--buttonNormal:hover {border-color: rgba(0, 171, 107, 1) !important;} .u-resetSpectrum .u-accentColor--buttonNormal .icon:before,.u-resetSpectrum .u-accentColor--buttonNormal .svgIcon{color: rgba(0, 171, 107, 1) !important; fill: rgba(0, 171, 107, 1) !important;} .u-resetSpectrum .u-accentColor--buttonNormal.button--filled .icon:before,.u-resetSpectrum .u-accentColor--buttonNormal.button--filled .svgIcon{color: rgba(255, 255, 255, 1) !important; fill: rgba(255, 255, 255, 1) !important;} .u-resetSpectrum .u-accentColor--buttonDark.button--filled,.u-resetSpectrum .u-accentColor--buttonDark.button--withChrome.is-active,.u-resetSpectrum .u-accentColor--fillWhenActive.is-active {background-color: rgba(28, 153, 99, 1) !important; border-color: rgba(28, 153, 99, 1) !important; color: rgba(255, 255, 255, 1) !important; fill: rgba(255, 255, 255, 1) !important;} .u-resetSpectrum .u-accentColor--buttonNormal.button--filled:not(.clapButton--largePill),.u-resetSpectrum .u-accentColor--buttonNormal.button--withChrome.is-active:not(.clapButton--largePill) {background-color: rgba(0, 171, 107, 1) !important; border-color: rgba(0, 171, 107, 1) !important; color: rgba(255, 255, 255, 1) !important; fill: rgba(255, 255, 255, 1) !important;} .u-resetSpectrum .postArticle.is-withAccentColors .markup--user,.u-resetSpectrum .postArticle.is-withAccentColors .markup--query {color: rgba(0, 171, 107, 1) !important;}

// <![CDATA[ window["obvInit"] = function (opt_embedded) {window["obvInit"]["embedded"] = opt_embedded; window["obvInit"]["ready"] = true;} // ]]>// <![CDATA[ var GLOBALS = {"audioUrl":"https://d1fcbxp97j4nb2.cloudfront.net","baseUrl":"https://medium.freecodecamp.org","buildLabel":"32186-3d58009","currentUser":{"userId":"lo\_3fdutBtaowJr","isVerified":false,"subscriberEmail":"","hasPastMemberships":false,"isEnrolledInHightower":false,"isEligibleForHightower":false,"hightowerLastLockedAt":0},"currentUserHasUnverifiedEmail":false,"isAuthenticated":false,"isCurrentUserVerified":false,"mediumTwitterScreenName":"medium","miroUrl":"https://cdn-images-1.medium.com","moduleUrls":{"base":"https://cdn-static-1.medium.com/\_/fp/gen-js/main-base.bundle.Gy6PdjVuTCIft7ova\_WDfw.js","common-async":"https://cdn-static-1.medium.com/\_/fp/gen-js/main-common-async.bundle.IlHACqjPkxpGqs9\_pAxXfw.js","home-screens":"https://cdn-static-1.medium.com/\_/fp/gen-js/main-home-screens.bundle.xTD4-k9ixFlOPqIOA56GbA.js","misc-screens":"https://cdn-static-1.medium.com/_/fp/gen-js/main-misc-screens.bundle.wlAsNFEQF8T6h\_\_XNKzDbA.js","notes":"https://cdn-static-1.medium.com/\_/fp/gen-js/main-notes.bundle.9trLv1ULaoOqI1YfdK9pOQ.js","payments":"https://cdn-static-1.medium.com/_/fp/gen-js/main-payments.bundle.bqcfaXaEvKWsMBgvvg4Jrw.js","posters":"https://cdn-static-1.medium.com/_/fp/gen-js/main-posters.bundle.ILtAAjXQFaUJ1kJkzSk2IA.js","pubs":"https://cdn-static-1.medium.com/_/fp/gen-js/main-pubs.bundle.pyaf8xodVzZ37jWN-FECgA.js","stats":"https://cdn-static-1.medium.com/_/fp/gen-js/main-stats.bundle.wZKJkAAwnEnN87HuNF2XvQ.js"},"previewConfig":{"weightThreshold":1,"weightImageParagraph":0.51,"weightIframeParagraph":0.8,"weightTextParagraph":0.08,"weightEmptyParagraph":0,"weightP":0.003,"weightH":0.005,"weightBq":0.003,"minPTextLength":60,"truncateBoundaryChars":20,"detectTitle":true,"detectTitleLevThreshold":0.15},"productName":"Medium","supportsEdit":true,"termsUrl":"//medium.com/policy/9db0094a1e0f","textshotHost":"textshot.medium.com","transactionId":"1516074900722:865624dde6df","useragent":{"browser":"chrome","family":"chrome","os":"mac","version":51,"supportsDesktopEdit":true,"supportsInteract":true,"supportsView":true,"isMobile":false,"isTablet":false,"isNative":false,"supportsFileAPI":true,"isTier1":true,"clientVersion":"","unknownParagraphsBad":false,"clientChannel":"","supportsRealScrollEvents":true,"supportsVhUnits":true,"ruinsViewportSections":false,"supportsHtml5Video":true,"supportsMagicUnderlines":true,"isWebView":false,"isFacebookWebView":false,"supportsProgressiveMedia":true,"supportsPromotedPosts":true,"isBot":false,"isNativeIphone":false,"supportsCssVariables":true,"supportsVideoSections":false,"emojiSupportLevel":3,"isSearchBot":false,"supportsScrollableMetabar":true},"variants":{"allow\_access":true,"allow\_signup":true,"allow\_test\_auth":"disallow","signin\_services":"twitter,facebook,google,email,google-fastidv","signup\_services":"twitter,facebook,google,email,google-fastidv","android\_rating\_prompt\_recommend\_threshold":5,"google\_sign\_in\_android":true,"reengagement\_notification\_duration":3,"browsable\_stream\_config\_bucket":"universal-nav-bar","enable\_series\_creation":true,"enable\_your\_series\_pages":true,"enable\_productionized\_series":true,"enable\_dedicated\_series\_tab\_api\_ios":true,"enable\_clap\_milestone\_notifications":true,"enable\_series\_stats\_page":true,"enable\_post\_import":true,"enable\_sponsored\_post\_labelling":true,"enable\_logged\_in\_follow\_on\_collection\_post":true,"enable\_export\_members":true,"enable\_series\_card\_background\_creation":true,"available\_monthly\_plan":"60e220181034","available\_annual\_plan":"2c754bcc2995","enable\_sms":true,"is\_not\_medium\_subscriber":true,"disable\_followed\_tag\_fan\_out":true,"enable\_emoji\_in\_editor":true,"enable\_glyph":true,"glyph\_font\_set":"m2","enable\_branding":true,"enable\_branding\_fonts":true,"enable\_branding\_buttons":true,"enable\_sequence\_carousel":true,"enable\_multirecommends":true,"enable\_user\_post\_metering":true,"enable\_login\_modal\_for\_all":true,"max\_premium\_content\_per\_user\_under\_metering":3,"enable\_topic\_writer\_onboarding":true,"enable\_algolia\_search\_reporting":true,"enable\_strong\_graph\_chp\_reorder":true,"enable\_top\_stories\_for\_you":true,"enable\_ios\_member\_post\_labeling":true,"enable\_featured\_hero":true,"enable\_li\_search\_collection":true,"enable\_revamped\_nav\_bar":true,"enable\_audio\_playlists":true,"enable\_platform\_934\_daily\_digest":true,"enable\_platform\_934\_welcome":true,"enable\_standalone\_profile\_edit\_page":true,"enable\_standalone\_user\_follow\_pages":true,"enable\_post\_footer\_copy":true,"app\_download\_email\_template":"routine"},"xsrfToken":"","iosAppId":"828256236","supportEmail":"yourfriends@medium.com","fp":{"/icons/monogram-mask.svg":"https://cdn-static-1.medium.com/\_/fp/icons/monogram-mask.KPLCSFEZviQN0jQ7veN2RQ.svg","/icons/favicon-dev-editor.ico":"https://cdn-static-1.medium.com/_/fp/icons/favicon-dev-editor.YKKRxBO8EMvIqhyCwIiJeQ.ico","/icons/favicon-hatch-editor.ico":"https://cdn-static-1.medium.com/_/fp/icons/favicon-hatch-editor.BuEyHIqlyh2s\_XEk4Rl32Q.ico","/icons/favicon-medium-editor.ico":"https://cdn-static-1.medium.com/\_/fp/icons/favicon-medium-editor.PiakrZWB7Yb80quUVQWM6g.ico"},"authBaseUrl":"https://medium.com","imageUploadSizeMb":25,"isAuthDomainRequest":false,"domainCollectionSlug":"free-code-camp","algoliaApiEndpoint":"https://MQ57UUUQZ2-dsn.algolia.net","algoliaAppId":"MQ57UUUQZ2","algoliaSearchOnlyApiKey":"394474ced050e3911ae2249ecc774921","iosAppStoreUrl":"https://itunes.apple.com/app/medium-everyones-stories/id828256236?pt=698524&mt=8","iosAppLinkBaseUrl":"medium:","algoliaIndexPrefix":"medium_","androidPlayStoreUrl":"https://play.google.com/store/apps/details?id=com.medium.reader","googleClientId":"216296035834-k1k6qe060s2tp2a2jam4ljdcms00sttg.apps.googleusercontent.com","androidPackage":"com.medium.reader","androidPlayStoreMarketScheme":"market://details?id=com.medium.reader","googleAuthUri":"https://accounts.google.com/o/oauth2/auth","androidScheme":"medium","layoutData":{"useDynamicScripts":false,"googleAnalyticsTrackingCode":"UA-24232453-2","jsShivUrl":"https://cdn-static-1.medium.com/_/fp/js/shiv.RI2ePTZ5gFmMgLzG5bEVAA.js","useDynamicCss":false,"faviconUrl":"https://cdn-static-1.medium.com/_/fp/icons/favicon-rebrand-medium.3Y6xpZ-0FSdWDnPM3hSBIA.ico","faviconImageId":"1*8I-HPL0bfoIzGied-dzOvA.png","fontSets":\[{"id":8,"url":"https://glyph.medium.com/css/e/sr/latin/e/ssr/latin/e/ssb/latin/m2.css"},{"id":9,"url":"https://glyph.medium.com/css/mkt.css"}\],"editorFaviconUrl":"https://cdn-static-1.medium.com/_/fp/icons/favicon-rebrand-medium-editor.3Y6xpZ-0FSdWDnPM3hSBIA.ico","glyphUrl":"https://glyph.medium.com"},"authBaseUrlRev":"moc.muidem//:sptth","isDnt":false,"stripePublishableKey":"pk\_live\_7FReX44VnNIInZwrIIx6ghjl","archiveUploadSizeMb":100,"paymentData":{"currencies":{"1":{"label":"US Dollar","external":"usd"}},"countries":{"1":{"label":"United States of America","external":"US"}},"accountTypes":{"1":{"label":"Individual","external":"individual"},"2":{"label":"Company","external":"company"}}},"previewConfig2":{"weightThreshold":1,"weightImageParagraph":0.05,"raiseImage":true,"enforceHeaderHierarchy":true,"isImageInsetRight":true},"isAmp":false,"iosScheme":"medium","isSwBoot":false,"lightstep":{"accessToken":"ce5be895bef60919541332990ac9fef2","carrier":"{\"ot-tracer-spanid\":\"72e7889f797a7824\",\"ot-tracer-traceid\":\"6a8d222f1058d2fc\",\"ot-tracer-sampled\":\"true\"}","host":"collector-medium.lightstep.com"},"facebook":{"key":"542599432471018","namespace":"medium-com","scope":{"default":["public_profile","email","user_friends"],"connect":["public_profile","email","user_friends"],"login":["public_profile","email","user_friends"],"share":["public_profile","email","user_friends","publish_actions"]}},"mailingListArchiveUploadSizeMb":2,"editorsPicksTopicId":"3985d2a191c5","popularOnMediumTopicId":"9d34e48ecf94","memberContentTopicId":"13d7efd82fb2","audioContentTopicId":"3792abbd134","brandedSequenceId":"7d337ddf1941","isDoNotAuth":false,"goldfinchUrl":"https://goldfinch.medium.com","buggle":{"url":"https://buggle.medium.com","videoUrl":"https://cdn-videos-1.medium.com","audioUrl":"https://cdn-audio-1.medium.com"},"referrerType":3,"isMeteredOut":false,"meterConfig":{"maxUnlockCount":3,"windowLength":"MONTHLY"},"partnerProgramEmail":"partnerprogram@medium.com","userResearchPrompts":\[\]} // ]]>// <![CDATA[ window["obvInit"]({"value":{"id":"c400bd65c433","versionId":"a62f74b04ab7","creatorId":"4e9454ed12f1","creator":{"userId":"4e9454ed12f1","name":"Maciej Nowakowski","username":"nowakowskipl","createdAt":1437675503054,"lastPostCreatedAt":1516041330938,"imageId":"1*u0TLLSmxJd2v_fHbMy_trw.jpeg","backgroundImageId":"","bio":"Founder at https://www.codecamps.com — a one-week coding camps on React, Angular, GraphQL and Meteor in remote locations.","twitterScreenName":"nowakowskipl","socialStats":{"userId":"4e9454ed12f1","usersFollowedCount":96,"usersFollowedByCount":55,"type":"SocialStats"},"social":{"userId":"lo_3fdutBtaowJr","targetUserId":"4e9454ed12f1","type":"Social"},"facebookAccountId":"1080269385416499","allowNotes":1,"type":"User"},"homeCollection":{"id":"336d898217ee","name":"freeCodeCamp","slug":"free-code-camp","tags":["TECHNOLOGY","DESIGN","TECH","STARTUP","PRODUCTIVITY"],"creatorId":"8b318225c16a","description":"Our community publishes stories worth reading on development, design, and data science.","shortDescription":"Our community publishes stories worth reading on…","image":{"imageId":"1*MotlWcSa2n6FrOx3ul89kw.png","filter":"","backgroundSize":"","originalWidth":0,"originalHeight":0,"strategy":"resample","height":0,"width":0},"metadata":{"followerCount":374268,"activeAt":1516038055493},"virtuals":{"permissions":{"canPublish":false,"canPublishAll":false,"canRepublish":false,"canRemove":false,"canManageAll":false,"canSubmit":false,"canEditPosts":false,"canAddWriters":false,"canViewStats":false,"canSendNewsletter":false,"canViewLockedPosts":false,"canViewCloaked":false,"canEditOwnPosts":false,"canBeAssignedAuthor":false,"canEnrollInHightower":false,"canLockPostsForMediumMembers":false},"isSubscribed":false,"isNewsletterSubscribed":false,"memberOfMembershipPlanId":"","isEnrolledInHightower":false,"isEligibleForHightower":false},"logo":{"imageId":"1*wViBNJ1o9rM5p6b-gf3vxg.png","filter":"","backgroundSize":"","originalWidth":600,"originalHeight":72,"strategy":"resample","height":0,"width":0},"twitterUsername":"freecodecamp","facebookPageName":"freecodecamp","publicEmail":"quincy@freecodecamp.com","collectionMastheadId":"af94c9c16d4","domain":"medium.freecodecamp.org","sections":[{"type":2,"collectionHeaderMetadata":{"backgroundImage":{},"logoImage":{"id":"1*JAG6GC1APQdyo2PegDDi2g@2x.png","originalWidth":601,"originalHeight":81,"alt":"freeCodeCamp"},"alignment":2,"layout":4}},{"type":1,"postListMetadata":{"source":3,"layout":6,"number":1,"postIds":["64306eb6bb27"]}},{"type":1,"postListMetadata":{"source":1,"layout":6,"number":25,"postIds":[]}},{"type":1,"postListMetadata":{"source":2,"layout":6,"number":25,"postIds":[],"sectionHeader":"Trending"}}],"tintColor":"#FF006400","lightText":true,"favicon":{"imageId":"1*B6_f-_SxscJ9FCuIjOrQAQ.jpeg","filter":"","backgroundSize":"","originalWidth":657,"originalHeight":654,"strategy":"resample","height":0,"width":0},"colorPalette":{"defaultBackgroundSpectrum":{"colorPoints":[{"color":"#FF429A35","point":0},{"color":"#FF408F33","point":0.1},{"color":"#FF3E8432","point":0.2},{"color":"#FF3B7830","point":0.3},{"color":"#FF376C2D","point":0.4},{"color":"#FF33602A","point":0.5},{"color":"#FF2E5426","point":0.6},{"color":"#FF284721","point":0.7},{"color":"#FF223A1C","point":0.8},{"color":"#FF1A2C15","point":0.9},{"color":"#FF111E0D","point":1}],"backgroundColor":"#FFFFFFFF"},"tintBackgroundSpectrum":{"colorPoints":[{"color":"#FF006400","point":0},{"color":"#FF2A7920","point":0.1},{"color":"#FF458C39","point":0.2},{"color":"#FF5D9E50","point":0.3},{"color":"#FF74AF66","point":0.4},{"color":"#FF8ABF7C","point":0.5},{"color":"#FFA0CE92","point":0.6},{"color":"#FFB5DDA8","point":0.7},{"color":"#FFCAEBBE","point":0.8},{"color":"#FFDFF8D4","point":0.9},{"color":"#FFF3FFEA","point":1}],"backgroundColor":"#FF006400"},"highlightSpectrum":{"colorPoints":[{"color":"#FFE3FBD9","point":0},{"color":"#FFDEFAD3","point":0.1},{"color":"#FFD9FACC","point":0.2},{"color":"#FFD3F9C6","point":0.3},{"color":"#FFCEF9C0","point":0.4},{"color":"#FFC9F8B9","point":0.5},{"color":"#FFC3F8B3","point":0.6},{"color":"#FFBDF7AD","point":0.7},{"color":"#FFB8F7A6","point":0.8},{"color":"#FFB2F6A0","point":0.9},{"color":"#FFACF699","point":1}],"backgroundColor":"#FFFFFFFF"}},"navItems":[{"type":1,"title":"Dev","tagSlug":"web-development","url":"https://medium.freecodecamp.org/tagged/web-development","source":"tagSlug"},{"type":1,"title":"Design","tagSlug":"design","url":"https://medium.freecodecamp.org/tagged/design","source":"tagSlug"},{"type":1,"title":"Data","tagSlug":"data-science","url":"https://medium.freecodecamp.org/tagged/data-science","source":"tagSlug"},{"type":3,"title":"Learn to code for free","url":"https://freecodecamp.com?ref=mn"}\],"colorBehavior":2,"instantArticlesState":0,"acceleratedMobilePagesState":0,"googleAnalyticsId":"UA-55446531-3","ampLogo":{"imageId":"","filter":"","backgroundSize":"","originalWidth":0,"originalHeight":0,"strategy":"resample","height":0,"width":0},"header":{"backgroundImage":{},"logoImage":{"id":"1\*JAG6GC1APQdyo2PegDDi2g@2x.png","originalWidth":601,"originalHeight":81,"alt":"freeCodeCamp"},"alignment":2,"layout":4},"type":"Collection"},"homeCollectionId":"336d898217ee","title":"A Guide to Responsive Images with Ready-to-Use Templates","detectedLanguage":"en","latestVersion":"a62f74b04ab7","latestPublishedVersion":"a62f74b04ab7","hasUnpublishedEdits":true,"latestRev":1605,"createdAt":1515005088661,"updatedAt":1516074463397,"acceptedAt":0,"firstPublishedAt":1515956530377,"latestPublishedAt":1515956630420,"vote":false,"experimentalCss":"","displayAuthor":"","content":{"subtitle":"Why generate 12 versions of the same image when just 2 media-queries do the job? The users won’t notice.","bodyModel":{"paragraphs":[{"name":"b790","type":3,"text":"A Guide to Responsive Images with Ready-to-Use Templates","markups":[]},{"name":"c16a","type":4,"text":"","markups":[],"layout":5,"metadata":{"id":"1*34Fn007F_nnaX4oTvB7S8A.jpeg","originalWidth":2880,"originalHeight":1200}},{"name":"d282","type":1,"text":"Why generate 12 versions of the same image when just 2 media-queries do the job? The users won’t notice.","markups":[]},{"name":"25ce","type":1,"text":"But Google will.","markups":[]},{"name":"d425","type":1,"text":"Responsive images that are in the wrong format, images that are not compressed properly, and images that are too big will all decrease the page speed and impact your SEO.","markups":[]},{"name":"ec18","type":1,"text":"According to Google, anything above 2 seconds of download time will put off your users and discourage the crawlers from indexing your website.","markups":[{"type":3,"start":13,"end":19,"href":"https://www.seroundtable.com/google-crawl-slow-tw0-seconds-20070.html","title":"","rel":"nofollow","anchorType":0}\]},{"name":"5c87","type":1,"text":"I learned that the hard way when I was rebuilding my website. My goal was to create a simple website that downloads in the blink of an eye. To do so, I went for Gatsby.js — it’s fast and I know it.","markups":[{"type":3,"start":53,"end":60,"href":"https://www.codecamps.com","title":"","rel":"nofollow","anchorType":0},{"type":3,"start":161,"end":170,"href":"https://www.gatsbyjs.org/","title":"","rel":"nofollow","anchorType":0}\]},{"name":"7f3f","type":1,"text":"After a few days of coding, the website was up and running. But, to my disappointment, it scored a modest 78/100 on mobile and a disastrous 57/100 on the desktop on PageSpeed Insights. Not good at all.","markups":[{"type":3,"start":165,"end":183,"href":"https://developers.google.com/speed/pagespeed/insights/","title":"","rel":"nofollow noopener nofollow","anchorType":0}]},{"name":"05c1","type":4,"text":"","markups":[],"layout":1,"metadata":{"id":"1*MJiKsF-ZdH6fdc4w53NfmA.jpeg","originalWidth":2236,"originalHeight":1294}},{"name":"0892","type":1,"text":"PageSpeed Insights suggested an easy fix to the problem. Just download the compressed images the tool has created, and you’ll be fine.","markups":[]},{"name":"e622","type":1,"text":"That sparked my curiosity. And the more I thought about sizes, formats, and compression levels, the more I felt overwhelmed with the abundance of choices. PNGs, JPGs, SVGs, inline base64 encoded strings… or maybe WebPs?","markups":[]},{"name":"67c1","type":1,"text":"To wrap my head around it, I dived into the world of pixels, waded through the muddy waters of random tips on the subject, and came up with a systematic approach.","markups":[]},{"name":"5643","type":1,"text":"And once I applied what I learned, my PageSpeed rating increased from 78 to 91 on mobile and from 57 to 99 on desktop!","markups":[]},{"name":"0f00","type":4,"text":"","markups":[],"layout":1,"metadata":{"id":"1*sNBWl0ziAVbdQubrcuhdjg.jpeg","originalWidth":2276,"originalHeight":1236}},{"name":"5855","type":1,"text":"In this post, I will show you how to quickly generate responsive images that work in all browsers and massively reduce your download speeds.","markups":[]},{"name":"4b92","type":3,"text":"Before you start","markups":[]},{"name":"dc09","type":1,"text":"If you want to optimize an image, you have to start with a high-quality image that has the right format and the right size:","markups":[]},{"name":"344b","type":9,"text":"Use JPGs for photos and PNGs for graphics or other images that require transparency","markups":[]},{"name":"e93c","type":9,"text":"Use smaller PNG-8 instead of PNG-24 for graphics with a limited number of colors. To decrease the size even further, you can also reduce the number of colors, from 256 to 16","markups":[]},{"name":"26c6","type":9,"text":"Use SVGs (vector graphic images) for icons and logos. They will scale nicely without increasing the size of your file","markups":[]},{"name":"10e4","type":9,"text":"Use inline images below 10KB as 64base encoded strings (sparingly)","markups":[]},{"name":"5e0d","type":9,"text":"The actual width of an image shouldn’t exceed the width of the largest container it will be displayed in, multiplied by two (for retina displays)","markups":[]},{"name":"c2dc","type":3,"text":"Hardware and software pixels","markups":[]},{"name":"3e65","type":1,"text":"The image that takes the full width of the 15” Macbook Pro screen is 1440 pixels wide but the actual resolution of the retina display is double that, at 2880x1800. Why? Because of its pixel density factor of 2.","markups":[{"type":3,"start":94,"end":111,"href":"http://pixensity.com/","title":"","rel":"nofollow","anchorType":0}\]},{"name":"ea34","type":1,"text":"Old monitors have a pixel density of 1. But since screen resolutions have increased in recent years, the hardware pixel is no longer equal to the software or CSS pixel.","markups":[]},{"name":"2815","type":1,"text":"The relationship between hardware and CSS pixels is described by the following formula:","markups":[]},{"name":"a946","type":1,"text":"CSS Pixels = Hardware Pixels / Pixel density","markups":[{"type":1,"start":0,"end":44}]},{"name":"36b1","type":1,"text":"Therefore, hardware resolution of 2880 pixels translates to1440 CSS pixels on the retina display. This also explains why, when you inspect the full-width image in Developer Tools, you will see it as only 1440 pixels wide instead of the original 2880 pixels.","markups":[]},{"name":"a863","type":1,"text":"The retina display was a major breakthrough a few years ago. Today, mobile devices have even “denser” displays of 3 and even 4 for Samsung Galaxy S8+!","markups":[]},{"name":"89bd","type":1,"text":"For the purpose of my experiment, I decided that to be razor-sharp, the full-width image should have the maximum width of 2880 pixels.","markups":[]},{"name":"4693","type":1,"text":"With the image height set to 600px and the quality to 75%, Photoshop produced a massive 939KB file. That’s hardly acceptable.","markups":[]},{"name":"256d","type":1,"text":"After a few experiments with compression levels, it became clear that compressing JPGs below 60% quality resulted in a visible loss of quality. I set the quality to 60% as a starting point and the image size dropped to 681KB. Still, far from decent.","markups":[]},{"name":"dfed","type":3,"text":"WebP format","markups":[]},{"name":"f1b2","type":1,"text":"“WebP is a modern image format that provides superior lossless and lossy compression for images on the web,” according to Google.","markups":[{"type":3,"start":122,"end":128,"href":"https://developers.google.com/speed/webp/","title":"","rel":"nofollow","anchorType":0}\]},{"name":"e7f0","type":1,"text":"After conversion to the WebP format, my image was not only smaller but also\nsharper! WebP shaved another 34% of the compressed JPEG size. “I’m on the right path”, I thought!","markups":[]},{"name":"375b","type":1,"text":"Unfortunately, the WebP format is supported by Chrome and Opera, just about 60% of all browsers, according to Can I use. So I knew I’d have to think of fallback options.","markups":[{"type":3,"start":110,"end":119,"href":"https://caniuse.com/#search=webp","title":"","rel":"nofollow","anchorType":0}\]},{"name":"86b7","type":1,"text":"At last, the limits were set:","markups":[]},{"name":"92b9","type":9,"text":"60% compression level","markups":[]},{"name":"c4bc","type":9,"text":"WebP format where possible","markups":[]},{"name":"22d2","type":1,"text":"I also chose to support three breakpoints: 600px and 900px\n(here is why) and 2-pixel densities — 1x and 2x for retina displays. That meant 6 different images instead of just two. Supporting the WebP format doubled the number.","markups":[{"type":3,"start":60,"end":71,"href":"https://medium.freecodecamp.org/the-100-correct-way-to-do-css-breakpoints-88d6a5ba1862","title":"","rel":"nofollow","anchorType":0}\]},{"name":"5298","type":1,"text":"There are two primary ways to put an image on a website, either by using HTML’s img element or a background-image in CSS.","markups":[{"type":10,"start":80,"end":83},{"type":10,"start":97,"end":113}]},{"name":"daee","type":3,"text":"Responsive images in HTML","markups":[]},{"name":"5340","type":1,"text":"The basic HTML img element has the src attribute that points to the image URL:","markups":[{"type":10,"start":15,"end":18},{"type":10,"start":35,"end":38}]},{"name":"aed8","type":8,"text":"\x3cimg src=“image.jpg” alt=“image description”/\x3e","markups":[]},{"name":"582d","type":1,"text":"But you can go a step further and decide which image to serve depending on the screen’s pixel density with the srcset attribute:","markups":[{"type":10,"start":111,"end":117}]},{"name":"757c","type":8,"text":"\x3cimg srcset=“image_1x.jpg 1x, image_2x.jpg 2x” src=”image_1x.jpg”/\x3e","markups":[]},{"name":"5730","type":1,"text":"Here, I’ve used two different screen densities: 1x and 2x. Depending on the actual display density, the browser will choose the right one. The src attribute points to the fallback option.","markups":[{"type":10,"start":48,"end":50},{"type":10,"start":55,"end":57},{"type":10,"start":143,"end":147}]},{"name":"64bc","type":1,"text":"At the moment, most browsers except IE, Edge, and Opera Mini have the srcset attribute implemented.","markups":[{"type":10,"start":70,"end":76}]},{"name":"61ca","type":1,"text":"This solution seems like a step in the right direction. Unfortunately, your browser will always select the same image, with the same pixel density, regardless of the display size. And the same image will end up on both the desktop and the mobile device.","markups":[]},{"name":"9d34","type":1,"text":"We need more control. And we can have it. Apart from pixel densities, the scrset attribute accepts width units w, an equivalent of CSS pixels.","markups":[{"type":10,"start":74,"end":80},{"type":10,"start":111,"end":112}]},{"name":"464c","type":1,"text":"The width unit enables the browser to choose the right image size for the given display capabilities.","markups":[]},{"name":"dd9e","type":1,"text":"With two breakpoints (600px and 900px), we can go with three different image sizes:","markups":[]},{"name":"ceb5","type":8,"text":"\x3cimg\n srcset=“image-sm.jpg 600w,\n image-md.jpg 900w,\n image-lg.jpg 1440w”\n src=”image_1x.jpg”/\x3e","markups":[]},{"name":"be47","type":1,"text":"There is a caveat here. When the browser decides which image to fetch, it has no knowledge of our CSS! The CSS file hasn’t been fetched at this point. And it assumes that the image will be displayed at the full width of the window.","markups":[]},{"name":"f4a7","type":1,"text":"If a full-width image is what you want, then fine. But what if you want to place an image in a container that is only 50vw wide? Here comes the sizes attribute into play. Let’s take a look:","markups":[{"type":10,"start":118,"end":122},{"type":10,"start":144,"end":150}]},{"name":"7245","type":8,"text":"\x3cimg\n srcset=“image-sm.jpg 600w,\n image-md.jpg 900w,\n image-lg.jpg 1440w”\n sizes=”50vw”\n src=”image_1x.jpg”/\x3e","markups":[]},{"name":"9840","type":1,"text":"By adding the sizes=”50vw” attribute, you are telling the browser that the image will be displayed at 50vw, and based on this information, the browser will decide which image to display.","markups":[{"type":10,"start":14,"end":26},{"type":10,"start":102,"end":106}]},{"name":"db34","type":1,"text":"But what if you want to display your image at 50vw on a big screen and at the full width of 100vw on a mobile device? The sizes attribute accepts also media queries!","markups":[{"type":10,"start":46,"end":50},{"type":10,"start":92,"end":97},{"type":10,"start":122,"end":127}]},{"name":"bc18","type":1,"text":"You can specify that below the mobile breakpoint of 600px you want the browser to display your image at a full-screen width. And for the width higher than the mobile breakpoint you want the browser to display your image at 50vw.","markups":[{"type":10,"start":52,"end":57},{"type":10,"start":223,"end":227}]},{"name":"c463","type":1,"text":"You can do this by adding the media query:","markups":[]},{"name":"abf6","type":8,"text":"\x3cimg\n srcset=“image-sm.jpg 600w,\n image-md.jpg 900w,\n image-lg.jpg 1440w”\n size=”(max-width: 600px) 100vw, 50vw”\n src=”image_1x.jpg” /\x3e","markups":[]},{"name":"6761","type":1,"text":"Remember that in the above line of code you are instructing the browser which image to choose because the browser doesn’t know the corresponding CSS. You still have to add the breakpoints in CSS explicitly.","markups":[]},{"name":"857d","type":1,"text":"This solution works really well but we are missing pixel densities here! If we stopped here, we would be sending the same image both to the displays with 1x pixel density and to the retina screens. Luckily, there is an easy fix to it.","markups":[{"type":10,"start":154,"end":156}]},{"name":"8399","type":3,"text":"Picture element","markups":[]},{"name":"8b7e","type":1,"text":"Meet the HTML5 picture element. It accepts the source and img elements as its children. We can use the source element to list additional image formats that we want to serve to the browser.","markups":[{"type":10,"start":15,"end":22},{"type":10,"start":47,"end":53},{"type":10,"start":58,"end":61},{"type":10,"start":103,"end":109}]},{"name":"92a6","type":1,"text":"But before we fix pixel densities, let’s introduce smaller and sharper images in WebP format.","markups":[]},{"name":"9c6e","type":1,"text":"Let’s add the source element as the first option inside the picture element with your image in the WebP format followed by the img pointing at the regular JPG image. Now, when the browser is not WebP-ready, it will gracefully fall back on to the img element (e.g. Safari).","markups":[{"type":10,"start":14,"end":20},{"type":10,"start":60,"end":67},{"type":10,"start":127,"end":130},{"type":10,"start":246,"end":249}]},{"name":"bb75","type":8,"text":"\x3cpicture\x3e\n \x3csource\n srcset=“image.webp”\n type=“image/webp” \x3e\n \x3cimg\n src=“image.jpg”\n type=“image/jpeg”\n alt=”image description”\x3e\n\x3c/picture\x3e","markups":[]},{"name":"558e","type":1,"text":"The source element opens up a whole new world of possibilities. It accepts media queries!","markups":[{"type":10,"start":4,"end":10}]},{"name":"6a50","type":1,"text":"First, in the media attribute, we use the media query and then, in the srcsetattribute, we place the appropriate image. And we can use as many sourceelements as we wish:","markups":[{"type":10,"start":14,"end":19},{"type":10,"start":71,"end":77},{"type":10,"start":143,"end":149}]},{"name":"c2b2","type":8,"text":"\x3cpicture\x3e\n \x3csource\n media=”(min-width: 900px)”\n srcset=“image-lg.webp”\n type=“image/webp” \x3e\n \x3csource\n media=”(min-width: 600px)”\n srcset=“image-md.webp”\n type=“image/webp” \x3e\n \x3csource\n srcset=“image-sm.webp”\n type=“image/webp” \x3e\n \x3cimg\n src=“image-lg.jpg”\n type=“image/jpeg”\n alt=”image description”\x3e\n\x3c/picture\x3e","markups":[]},{"name":"232d","type":1,"text":"Above, we have prepared three images in the WebP format, depending on the size of the display, and one JPG image as a fallback option.","markups":[]},{"name":"6066","type":1,"text":"The last secret of the srcset attribute is that it also accepts pixel densities. We can decide which image we want to serve on which screen and at which pixel density. The trick is to list image files in the scrset followed by a space and the pixel density factor, for example: 1x, 2x, 3x, or even 4x.","markups":[{"type":10,"start":23,"end":29},{"type":10,"start":208,"end":214},{"type":10,"start":278,"end":280},{"type":10,"start":282,"end":284},{"type":10,"start":286,"end":288},{"type":10,"start":298,"end":300}]},{"name":"6f93","type":8,"text":"\x3cpicture\x3e\n \x3csource\n media=”(min-width: 900px)”\n srcset=“image-lg_1x.webp 1x, image-lg_2x.webp 2x”\n type=“image/webp” \x3e\n \x3csource\n media=”(min-width: 601px)”\n srcset=“image-md_1x.webp 1x, image-md_2x.webp 2x”\n type=“image/webp” \x3e\n \x3csource\n media=”(max-width: 600px)”\n srcset=“image-sm_1x.webp 1x, image-sm_1x.webp 1x”\n type=“image/webp” \x3e\n \x3cimg\n src=“image-lg_1x.jpg”\n type=“image/jpeg”\n alt=”image description”\x3e\n\x3c/picture\x3e\n`","markups":[]},{"name":"4986","type":1,"text":"Since we sorted out the screen sizes and pixel densities for the WebP format, let’s have a closer look at the fallback option. In the end, some browsers don’t support the WebP format.","markups":[]},{"name":"8a01","type":1,"text":"Here, we have to decide if we want to use the 1 or 2-pixel-dense images. Below, I went for the first option:","markups":[]},{"name":"e023","type":8,"text":"\x3cpicture\x3e\n \x3csource\n media=”(min-width: 900px)”\n srcset=“image-lg_1x.webp 1x, image-lg_2x.webp 2x”\n type=“image/webp” \x3e\n \x3csource\n media=”(min-width: 601px)”\n srcset=“image-md_1x.webp 1x, image-md_2x.webp 2x”\n type=“image/webp” \x3e\n \x3csource\n srcset=“image-sm_1x.webp 1x, image-sm_2x.webp 2x”\n type=“image/webp” \x3e\n \x3cimg \n srcset=“image-sm_1x.jpg 600w,\n image-md_1x.jpg 900w,\n image-lg_1x.jpg 1440w”\n src=“image_lg_1x.jpg”\n type=“image/jpeg”\n alt=”image description”\x3e\n\x3c/picture\x3e","markups":[]},{"name":"ae44","type":1,"text":"We have replaced the img element with the picture element. Where possible, we want to deliver images in the WebP format in three different sizes, depending on the display size, and 2 different pixel densities. If the browser doesn’t support the picture element or the WebP format, it will fall back on to the standard img element with three different sizes of JPGs.","markups":[{"type":10,"start":21,"end":24},{"type":10,"start":42,"end":49},{"type":10,"start":245,"end":252},{"type":10,"start":318,"end":321}]},{"name":"4c73","type":1,"text":"Important: Notice that in the img element the srcset attribute should be placed before the src attribute. Otherwise, the browser will download the src image first and then, if it finds a better image in the srcset, it will download this one as well. This way we would end up with two images.","markups":[{"type":10,"start":30,"end":33},{"type":10,"start":46,"end":52},{"type":10,"start":91,"end":94},{"type":10,"start":147,"end":150},{"type":10,"start":207,"end":213},{"type":1,"start":0,"end":10}]},{"name":"6877","type":1,"text":"We could go one step further and create another 3 source elements for browsers that don’t support the WebP format and deliver JPG files instead.","markups":[{"type":10,"start":50,"end":56}]},{"name":"63c4","type":1,"text":"Although it works great for Firefox, I’ve noticed that Safari will download both files: the JPG listed in the source and the JPG from the img element. Again, we would end up with two images instead of one.","markups":[{"type":10,"start":110,"end":116},{"type":10,"start":138,"end":141},{"type":1,"start":117,"end":120}]},{"name":"643a","type":3,"text":"Responsive images in CSS","markups":[]},{"name":"86c6","type":1,"text":"If we don’t know the exact height and width of the container we want to cover with an image, we can use generic elements like div with the background-imageproperty pointing to the image URL:","markups":[{"type":10,"start":126,"end":129},{"type":10,"start":139,"end":155}]},{"name":"597a","type":8,"text":"background-image: url(“/images/image.jpg”);","markups":[]},{"name":"7038","type":1,"text":"CSS, similarly to HTML, enables image size optimization.","markups":[]},{"name":"846f","type":1,"text":"The image-set in CSS is the equivalent of the srcset in HTML . At the moment, it is implemented in Chrome, Chrome for Android, Safari, iOS Safari, and a few other browsers. You can add polyfills to make the image-setwork on other browsers, but given that Chrome and Safari combined are the browsers of choice for 70% of users today, there is a good chance that most browsers will implement the attribute in the near future.","markups":[{"type":10,"start":4,"end":13},{"type":10,"start":46,"end":52},{"type":10,"start":207,"end":216},{"type":3,"start":185,"end":194,"href":"https://github.com/wtfil/image-set-polyfill","title":"","rel":"nofollow","anchorType":0}\]},{"name":"35d2","type":1,"text":"But worry not, the regular background-image as a fallback option will do the trick.","markups":[{"type":10,"start":27,"end":43}]},{"name":"5005","type":1,"text":"The structure is very similar to what we’ve just used in a srcset attribute.","markups":[{"type":10,"start":59,"end":65}]},{"name":"3018","type":1,"text":"To create a full-width image element with a height of 500px, we have to start with the fallback option — the first background-image in the code example below. Then, using the -webkit-image-set, we need to list the WebP images for different pixel densities. And we have to repeat the process for different breakpoints using media queries.","markups":[{"type":10,"start":115,"end":131},{"type":10,"start":175,"end":192}]},{"name":"e650","type":1,"text":"One important thing to remember is that both Chrome and Safari use the WebKit layout engine but Safari doesn’t support the WebP format. That’s why we have to add the last set of image-set attributes with JPG images (it will be used by Safari even though it doesn’t start with -webkit).","markups":[{"type":10,"start":178,"end":187},{"type":10,"start":276,"end":283}]},{"name":"7c33","type":8,"text":".bg-image {\n width: 100vw;\n height: 500px;\n \n background-size: cover;\n background-position: center;\n \n background-image: url(/images/image-lg_1x.jpg) \n background-image: -webkit-image-set(\n url(/images/image-lg_1x.webp) 1x,\n url(/images/image-lg_2x.webp) 2x\n );\n background-image: image-set(\n url(/images/image-lg_1x.jpg) 1x,\n url(/images/image-lg_2x.jpg) 2x\n );","markups":[]},{"name":"bd51","type":8,"text":"@media(max-width: 900px) {\n background-image: url(/images/image-md_2x.jpg);\n background-image: -webkit-image-set(\n url(/images/image-md_1x.webp) 1x,\n url(/images/image-md_2x.webp) 2x\n );\n background-image: image-set(\n url(/images/image-md_1x.jpg) 1x,\n url(/images/image-md_2x.jpg) 2x\n );\n }","markups":[]},{"name":"f68a","type":8,"text":"@media (max-width: 600px) {\n background-image: url(/images/image-sm_2x.jpg);\n background-image: -webkit-image-set(\n url(/images/image-sm_1x.webp) 1x,\n url(/images/image-sm_2x.webp) 2x\n );\n background-image: image-set(\n url(/images/image-sm_1x.jpg) 1x,\n url(/images/image-sm_2x.jpg) 2x\n );\n }\n}","markups":[]},{"name":"67d7","type":1,"text":"Here, the background image is centered in the div element and covers its whole area. Using the image-set attribute, we are assigning two different images to two different pixel densities.","markups":[{"type":10,"start":46,"end":49},{"type":10,"start":95,"end":104}]},{"name":"9076","type":1,"text":"The fallback option with a standard url takes care of the browsers that don’t support the image-set attribute.","markups":[{"type":10,"start":36,"end":39},{"type":10,"start":90,"end":99}]},{"name":"d0f1","type":1,"text":"It’s very important to place the fallback option before the background-imageswith the image-set attribute. If you place it after the image-setattribute, for example, Safari would download both, the image from image-set and the image from the fallback option if it found an image with a different file name.","markups":[{"type":10,"start":60,"end":77},{"type":10,"start":86,"end":95},{"type":10,"start":133,"end":142},{"type":10,"start":209,"end":218},{"type":1,"start":49,"end":55}]},{"name":"6b13","type":1,"text":"The rest of the code follows the same pattern. Above, I have added media queries for 600px and 900px breakpoints and a set of corresponding images in smaller sizes.","markups":[]},{"name":"0c02","type":1,"text":"The fallback option always has to use the JPG format to avoid the situation where an image cannot be shown at all, that is when the browser doesn’t support the image-set attribute or the WebP format.","markups":[{"type":10,"start":160,"end":169}]},{"name":"578e","type":3,"text":"How to inline small images","markups":[]},{"name":"1640","type":1,"text":"To improve user experience, we should not only compress and serve the smallest possible images, but we should also decrease the number of requests we send to the server.","markups":[]},{"name":"c7b5","type":1,"text":"The browser has to send a separate request for every single image. When sent to the server, the request has to first wait in a queue, which takes time. The more calls the browser makes, the longer the user has to wait.","markups":[]},{"name":"1140","type":1,"text":"That’s especially true when you have to download many small images. If possible, logos and icons should be saved as vector graphics (SVG). Small images can be embedded either in HTML or in CSS directly as base64 encoded strings.","markups":[]},{"name":"c83f","type":1,"text":"Instead of passing a regular URL to the src attribute in the img element, we can pass the image as a string:","markups":[{"type":10,"start":40,"end":43},{"type":10,"start":61,"end":64}]},{"name":"ac8f","type":8,"text":"\x3cimg\n src=”data:image/png;base64,encoded string”\n alt=”img description” /\x3e","markups":[]},{"name":"34ca","type":1,"text":"and in CSS:","markups":[]},{"name":"6bfb","type":8,"text":".small-image {\n background-image: url(data:image/png;base64,encoded string);\n}","markups":[]},{"name":"7bc4","type":1,"text":"In most cases, the generated string will be around 30% bigger than the original image, but you will save time on another round trip to the server.","markups":[]},{"name":"4929","type":1,"text":"The most common argument against using base64 encoded images in CSS files is that images are non-blocking resources whereas CSS files are. It means that if you embed too many small images into your CSS, it will increase the size of the CSS file and lengthen the time to the first paint of the website. That, in turn, will make the user wait longer before he or she can see any content.","markups":[]},{"name":"81e0","type":1,"text":"Here is a great article on why you may consider dropping the idea of using encoded strings for images entirely.","markups":[{"type":3,"start":0,"end":4,"href":"https://csswizardry.com/2017/02/base64-encoding-and-performance/","title":"","rel":"nofollow","anchorType":0}\]},{"name":"f212","type":1,"text":"The truth lays probably somewhere in the middle, and injecting one or two small files as base64 strings into CSS or HTML shouldn’t do any harm.","markups":[]},{"name":"c70e","type":1,"text":"At the end of this article, you will learn how to generate them. It can feel strange at first because these strings are thousands of characters long. Your .logo class may look like this, but longer:","markups":[{"type":10,"start":155,"end":160}]},{"name":"7446","type":8,"text":".logo {\n background-image: url(data:image/png;base64,iVBORw0KGgoAAAA\n NSUhEUgAABqIAAAFvCAMAAAAWmCq0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZS\n BJbWFnZVJlYWR5ccllPAAAA3hpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAA\n Dw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5U\n Y3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0…);\n}How to generate responsive images","markups":[]},{"name":"cb7d","type":3,"text":"How to generate responsive images","markups":[]},{"name":"1c8c","type":1,"text":"Let’s assume that you’ve just saved a perfect image and you’d like to create all the variations so you can use it on your website.","markups":[]},{"name":"282b","type":1,"text":"There are many tools that can help. Simple tools include compressjpeg.com, compresspng.com, and tinyjpg.com. More advanced tools include ImageOptim for JPEG, PNGs, and GIFs and ImageAlpha for PNGs.","markups":[{"type":3,"start":57,"end":73,"href":"http://compressjpeg.com/","title":"","rel":"nofollow","anchorType":0},{"type":3,"start":75,"end":90,"href":"http://compresspng.com/","title":"","rel":"nofollow","anchorType":0},{"type":3,"start":96,"end":107,"href":"https://tinyjpg.com/","title":"","rel":"nofollow","anchorType":0},{"type":3,"start":137,"end":147,"href":"https://imageoptim.com","title":"","rel":"nofollow","anchorType":0},{"type":3,"start":177,"end":187,"href":"https://pngmini.com/","title":"","rel":"nofollow","anchorType":0}\]},{"name":"1dcc","type":1,"text":"On my quest to take full control of compression levels, formats, and scaling, I needed a tool that would help me automate the whole process. And I didn’t fancy drag-and-dropping dozens of images.","markups":[]},{"name":"d0a4","type":1,"text":"Both ImageMagic and GraphicsMagick are free and powerful pieces of software that painlessly pair with Grunt, the JavaScript task runner.","markups":[{"type":3,"start":5,"end":15,"href":"http://www.imagemagick.org/script/index.php","title":"","rel":"nofollow","anchorType":0},{"type":3,"start":20,"end":34,"href":"http://www.graphicsmagick.org/","title":"","rel":"nofollow","anchorType":0},{"type":3,"start":102,"end":107,"href":"https://gruntjs.com/","title":"","rel":"nofollow","anchorType":0}\]},{"name":"78e3","type":1,"text":"Even better, there are Grunt plugins that simplify the task further. Several quick tests showed that GraphicsMagick generates 20% smaller JPG images than ImageMagic at the same compression level. So the choice was clear.","markups":[]},{"name":"b263","type":1,"text":"Before we start to cut our way through the jungle of pixels, we have to prepare our tools and sharpen our axe. Download GraphicsMagick from here or use Homebrew to install it.","markups":[{"type":3,"start":140,"end":144,"href":"http://www.graphicsmagick.org/download.html","title":"","rel":"nofollow","anchorType":0},{"type":3,"start":152,"end":160,"href":"http://www.graphicsmagick.org/download.html","title":"","rel":"nofollow","anchorType":0}\]},{"name":"8d8c","type":8,"text":"brew install graphicsmagick","markups":[]},{"name":"93a9","type":1,"text":"Next, install Grunt’s CLI globally:","markups":[]},{"name":"0cc4","type":8,"text":"npm install -g grunt-cli","markups":[]},{"name":"a025","type":1,"text":"Create a separate folder responsive-images and init the project:","markups":[{"type":10,"start":25,"end":42}]},{"name":"16c5","type":8,"text":"mkdir responsive-images\ncd responsive-images\nnpm init","markups":[]},{"name":"f867","type":1,"text":"And finally, install the local version of Grunt:","markups":[]},{"name":"7505","type":8,"text":"npm install grunt --save-dev","markups":[]},{"name":"03ed","type":1,"text":"Create two folders: src/ for original images and dest/ for the responsive images that Grunt and GraphicsMagick will generate:","markups":[{"type":10,"start":20,"end":24},{"type":10,"start":49,"end":54}]},{"name":"88b2","type":8,"text":"mkdir src\nmkdir dest","markups":[]},{"name":"6e61","type":1,"text":"The original image should be saved at the resolution equal to or greater than the largest image you want to generate in the src/ folder. I saved mine as JPG at 100% quality and 2880 pixels wide. It was around 2.5MB.","markups":[{"type":10,"start":124,"end":128}]},{"name":"ca92","type":1,"text":"First, let’s generate responsive images using the grunt-responsive-imagesplugin. Install it:","markups":[{"type":3,"start":50,"end":73,"href":"http://www.andismith.com/grunt-responsive-images/","title":"","rel":"nofollow","anchorType":0}\]},{"name":"cc2d","type":8,"text":"npm install grunt-responsive-images --save-dev","markups":[]},{"name":"ea8a","type":1,"text":"Now, in the root directory of the project, create an additional file Gruntfile.js:","markups":[{"type":10,"start":69,"end":81}]},{"name":"26b0","type":8,"text":"touch Gruntfile.js","markups":[]},{"name":"baeb","type":1,"text":"This is where we have to configure the plugin.","markups":[]},{"name":"50e6","type":1,"text":"Copy and paste the code to the Gruntfile.js and let me walk you through the code:","markups":[{"type":10,"start":31,"end":43}]},{"name":"2a7d","type":8,"text":"module.exports = function(grunt) {\n grunt.initConfig({\n responsive_images: {\n dev: {\n options: {\n engine: “gm”,\n sizes: [\n { name: “sm”, suffix: “_1x”, quality: 60, width: 600 },\n { name: “sm”, suffix: “_2x”, quality: 60, width: 1200 },\n { name: “md”, suffix: “_1x”, quality: 60, width: 900 },\n { name: “md”, suffix: “_2x”, quality: 60, width: 1800 },\n { name: “lg”, suffix: “_1x”, quality: 60, width: 1440 },\n { name: “lg”, suffix: “_2x”, quality: 60, width: 2880 }\n ]\n },\n files: [\n {\n expand: true,\n src: [“**/.{jpg,png}”],\n cwd: “src/”,\n dest: “dest/”\n }\n ]\n }\n }\n });","markups":[]},{"name":"0e4a","type":8,"text":"grunt.loadNpmTasks(“grunt-responsive-images”);\n grunt.registerTask(“default”, [“responsive_images”]);\n};","markups":[]},{"name":"3f02","type":1,"text":"In options, we set GraphicsMagick as our engine of choice: engine: “gm”. You can also test ImageMagick by changing it to engine: “im”.","markups":[{"type":10,"start":3,"end":10},{"type":10,"start":59,"end":71},{"type":10,"start":121,"end":133}]},{"name":"f3e7","type":1,"text":"Next, in the sizes array, we have to specify the parameters of the images we want to produce, such as a name that will be appended to the original name, a suffix that will be added to the name as well, a quality and a width.","markups":[{"type":10,"start":13,"end":18},{"type":10,"start":104,"end":108},{"type":10,"start":155,"end":161},{"type":10,"start":204,"end":211},{"type":10,"start":218,"end":223}]},{"name":"49e8","type":1,"text":"The resulting images will have the following naming structure:","markups":[]},{"name":"0ab5","type":8,"text":"original-[name]_[suffix}.jpg","markups":[]},{"name":"ff2d","type":1,"text":"For example, using the first sizes object, Grunt will generate from the original my-image.jpg the my-image-sm_1x.jpg image at 60% compression level and 600 pixels wide.","markups":[{"type":10,"start":29,"end":34},{"type":10,"start":81,"end":93},{"type":10,"start":98,"end":116}]},{"name":"0286","type":1,"text":"Below the options, we need to list source and destination folders as well as patterns of file names that we want to process.","markups":[]},{"name":"d516","type":1,"text":"To enable the dynamic build of file objects, let’s set the expand attribute to true and define:","markups":[{"type":10,"start":59,"end":65},{"type":10,"start":79,"end":83}]},{"name":"3d03","type":9,"text":"cwd — source folder","markups":[{"type":10,"start":0,"end":3}]},{"name":"d79e","type":9,"text":"src — an array of patterns to match. In our case, we want to match any folder () inside the source folder and all files with extensions jpg or png","markups":[{"type":10,"start":0,"end":3},{"type":10,"start":79,"end":81},{"type":10,"start":138,"end":141},{"type":10,"start":145,"end":148}]},{"name":"a48b","type":9,"text":"dest — destination folder","markups":[{"type":10,"start":0,"end":4}]},{"name":"d59b","type":1,"text":"The above Grunt task will generate a set of JPG and/or PNG files, depending on the source image file extensions.","markups":[]},{"name":"7ea8","type":1,"text":"We also want to produce a corresponding set of WebP images.","markups":[]},{"name":"3edf","type":1,"text":"We need another plugin to do the job: grunt-cwebp. Let’s install it:","markups":[{"type":10,"start":38,"end":49}]},{"name":"7752","type":8,"text":"npm install grunt-cwebp --save-dev","markups":[]},{"name":"e7d5","type":1,"text":"Append the Gruntfile.js with the following configuration:","markups":[]},{"name":"8903","type":8,"text":"module.exports = function(grunt) {\n grunt.initConfig({\n responsive_images: {\n …\n },\n cwebp: {\n dynamic: {\n options: {\n q: 60\n },\n files: [\n {\n expand: true,\n cwd: “dest/”,\n src: [“/.{jpg,png}”],\n dest: “dest/”\n }\n ]\n }\n }\n });","markups":[]},{"name":"f7bc","type":8,"text":"grunt.loadNpmTasks(“grunt-responsive-images”);\n grunt.loadNpmTasks(“grunt-cwebp”);","markups":[]},{"name":"0a6d","type":8,"text":"grunt.registerTask(“default”, [“responsive_images”, “cwebp”]);\n};","markups":[]},{"name":"2f4b","type":1,"text":"The grunt-cwebp plugin uses the dest/ folder as the source of images. We want all the newly produced JPGs to have their WebP siblings and we should place them in the same folder.","markups":[{"type":10,"start":4,"end":15},{"type":10,"start":32,"end":37}]},{"name":"6958","type":1,"text":"Now, we can process the images:","markups":[]},{"name":"c943","type":8,"text":"grunt","markups":[]},{"name":"6242","type":1,"text":"For every image in the src/ folder, Grunt will generate 12 images in all the necessary sizes, pixel densities and in both JPG and WebP format!","markups":[{"type":10,"start":23,"end":27}]},{"name":"80f9","type":3,"text":"How to generate base64 strings","markups":[]},{"name":"b500","type":1,"text":"If you want to generate base64 strings for inlining your images, here is how to do.","markups":[]},{"name":"b35b","type":1,"text":"This time, let’s use the Grunt plugin: grunt-base64.","markups":[{"type":10,"start":39,"end":51}]},{"name":"cbeb","type":1,"text":"Create a new project in a separate folder base64-images. Init it with npm and install the local version of Grunt:","markups":[{"type":10,"start":42,"end":55},{"type":10,"start":70,"end":73}]},{"name":"9448","type":8,"text":"mkdir base64-images\ncd base64-images\nnpm init\nnpm install grunt --save-dev","markups":[]},{"name":"dc36","type":1,"text":"Install the grunt-base64 plugin:","markups":[{"type":10,"start":12,"end":24}]},{"name":"6739","type":8,"text":"npm install grunt-base64 --save-dev","markups":[]},{"name":"af39","type":1,"text":"In the root directory, create a new images/ folder and the Gruntfile.js:","markups":[{"type":10,"start":36,"end":43},{"type":10,"start":59,"end":71}]},{"name":"c084","type":8,"text":"mkdir images\ntouch Gruntfile.js","markups":[]},{"name":"438c","type":1,"text":"and copy and paste the code into the Gruntfile.js:","markups":[{"type":10,"start":37,"end":49}]},{"name":"aad3","type":8,"text":"module.exports = function(grunt) {\n grunt.initConfig({\n base64: {\n dev: {\n files: {\n “images/output.b64”: [“images/.{jpg,png}”]\n }\n }\n }\n });","markups":[]},{"name":"966c","type":8,"text":"grunt.loadNpmTasks(“grunt-base64”);\n grunt.registerTask(“default”, [“base64”]);\n};","markups":[]},{"name":"f6c1","type":1,"text":"Place the small original image in the images/ folder and run Grunt:","markups":[{"type":10,"start":38,"end":45}]},{"name":"3271","type":8,"text":"grunt","markups":[]},{"name":"cb36","type":1,"text":"After the task is finished, copy the whole content from the output.b64 file — that’s the base64 string that you can paste into the url of the background-image or into the src attribute of the img element.","markups":[{"type":10,"start":60,"end":70},{"type":10,"start":131,"end":134},{"type":10,"start":142,"end":158},{"type":10,"start":171,"end":174},{"type":10,"start":192,"end":195}]},{"name":"e589","type":1,"text":"There is also an easier way (on Mac OS X or Linux):","markups":[]},{"name":"3af3","type":8,"text":"uuencode -m image-file-name remotename","markups":[]},{"name":"be2d","type":1,"text":"The remotename is not used and you can place even xyz to get base64 string printed into the standard output — in most cases into the terminal window.\nYou have to use -m option to get the base64 encoding.","markups":[{"type":10,"start":4,"end":14},{"type":10,"start":50,"end":53},{"type":10,"start":166,"end":168}]},{"name":"6165","type":3,"text":"Conclusion","markups":[]},{"name":"57f2","type":1,"text":"Responsive images might feel overwhelming at first, but with Grunt and image processing engines on your side, you can create a smooth process and automate most of the repetitive tasks. And I promise it’s worth it. You will not only shine in PageSpeed Insights, but you will also slash the time to the first paint of your website.","markups":[]},{"name":"b2d9","type":1,"text":"In my case, the original 939KB image shrank by 60% to 380KB (JPG) and by 77% to 218KB in the WebP format.","markups":[]},{"name":"1a9c","type":1,"text":"In the end, my pixel-crusade paid off — the PageSpeed Insight rating for my website turned green.","markups":[]},{"name":"fdf1","type":1,"text":"If you found this article helpful, tweet to let me know how many pixels you managed to shave off and how it improved your website. And why not to share it with your friends?","markups":[{"type":3,"start":35,"end":40,"href":"https://twitter.com/codecampshq","title":"","rel":"nofollow","anchorType":0}\]}\],"sections":\[{"name":"076e","startIndex":0}\]},"postDisplay":{"coverless":true}},"virtuals":{"statusForCollection":"APPROVED","allowNotes":true,"previewImage":{"imageId":"1\34Fn007F_nnaX4oTvB7S8A.jpeg","filter":"","backgroundSize":"","originalWidth":2880,"originalHeight":1200,"strategy":"resample","height":0,"width":0},"wordCount":4018,"imageCount":3,"readingTime":15.712264150943398,"subtitle":"Why generate 12 versions of the same image when just 2 media-queries do the job? The users won’t notice.","publishedInCount":1,"usersBySocialRecommends":[],"recommends":132,"socialRecommends":[],"isBookmarked":false,"tags":[{"slug":"web-development","name":"Web Development","postCount":61269,"virtuals":{"isFollowing":false},"metadata":{"followerCount":45245,"postCount":61269,"coverImage":{"id":"1*41XiwBL9NXDfGtIXbc3UsQ.jpeg","originalWidth":3000,"originalHeight":2000,"isFeatured":true}},"type":"Tag"},{"slug":"image","name":"Image","postCount":5007,"virtuals":{"isFollowing":false},"metadata":{"followerCount":10,"postCount":5007,"coverImage":{"id":"1*34Fn007F_nnaX4oTvB7S8A.jpeg","originalWidth":2880,"originalHeight":1200}},"type":"Tag"},{"slug":"responsive-design","name":"Responsive Design","postCount":2565,"virtuals":{"isFollowing":false},"metadata":{"followerCount":1990,"postCount":2565,"coverImage":{"id":"1*egtZCVQirK8mJiacL98eBA.png","originalWidth":1488,"originalHeight":420}},"type":"Tag"},{"slug":"software","name":"Software","postCount":10151,"virtuals":{"isFollowing":false},"metadata":{"followerCount":1045,"postCount":10151,"coverImage":{"id":"1*34Fn007F_nnaX4oTvB7S8A.jpeg","originalWidth":2880,"originalHeight":1200}},"type":"Tag"},{"slug":"tech","name":"Tech","postCount":112000,"virtuals":{"isFollowing":false},"metadata":{"followerCount":1071094,"postCount":112000,"coverImage":{"id":"1*5pa7-lgBJW1DQpFC68biuA.png","originalWidth":1344,"originalHeight":1288}},"type":"Tag"}],"socialRecommendsCount":0,"responsesCreatedCount":3,"links":{"entries":[{"url":"https://gruntjs.com/","alts":\[\],"httpStatus":200},{"url":"https://www.gatsbyjs.org/","alts":\[\],"httpStatus":200},{"url":"https://developers.google.com/speed/pagespeed/insights/","alts":\[\],"httpStatus":200},{"url":"https://www.seroundtable.com/google-crawl-slow-tw0-seconds-20070.html","alts":\[{"type":1,"url":"https://cdn.ampproject.org/c/s/www.seroundtable.com/amp/google-crawl-slow-tw0-seconds-20070.html"}\],"httpStatus":200},{"url":"https://www.codecamps.com","alts":\[\],"httpStatus":200},{"url":"http://www.andismith.com/grunt-responsive-images/","alts":\[\],"httpStatus":404},{"url":"https://csswizardry.com/2017/02/base64-encoding-and-performance/","alts":\[\],"httpStatus":200},{"url":"http://www.imagemagick.org/script/index.php","alts":\[\],"httpStatus":200},{"url":"https://github.com/wtfil/image-set-polyfill","alts":\[\],"httpStatus":200},{"url":"https://tinyjpg.com/","alts":\[\],"httpStatus":200},{"url":"http://www.graphicsmagick.org/download.html","alts":\[\],"httpStatus":200},{"url":"http://compresspng.com/","alts":\[\],"httpStatus":200},{"url":"http://www.graphicsmagick.org/","alts":\[\],"httpStatus":200},{"url":"http://compressjpeg.com/","alts":\[\],"httpStatus":200},{"url":"https://developers.google.com/speed/webp/","alts":\[\],"httpStatus":200},{"url":"https://caniuse.com/#search=webp","alts":\[\],"httpStatus":200},{"url":"http://pixensity.com/","alts":\[\],"httpStatus":200},{"url":"https://medium.freecodecamp.org/the-100-correct-way-to-do-css-breakpoints-88d6a5ba1862","alts":\[{"type":2,"url":"medium://p/88d6a5ba1862"},{"type":3,"url":"medium://p/88d6a5ba1862"}\],"httpStatus":200},{"url":"https://twitter.com/codecampshq","alts":\[{"type":2,"url":"twitter://user?screen\_name=codecampshq"},{"type":3,"url":"twitter://user?screen\_name=codecampshq"}\],"httpStatus":200},{"url":"https://pngmini.com/","alts":\[\],"httpStatus":200},{"url":"https://imageoptim.com","alts":\[\],"httpStatus":200}\],"version":"0.3","generatedAt":1515956631596},"isLockedPreviewOnly":false,"takeoverId":"","metaDescription":"","totalClapCount":1064,"sectionCount":1,"readingList":0},"coverless":true,"slug":"a-guide-to-responsive-images-with-ready-to-use-templates","translationSourcePostId":"","translationSourceCreatorId":"","isApprovedTranslation":false,"inResponseToPostId":"","inResponseToRemovedAt":0,"isTitleSynthesized":true,"allowResponses":true,"importedUrl":"","importedPublishedAt":0,"visibility":0,"uniqueSlug":"a-guide-to-responsive-images-with-ready-to-use-templates-c400bd65c433","previewContent":{"bodyModel":{"paragraphs":\[{"name":"b790","type":3,"text":"A Guide to Responsive Images with Ready-to-Use Templates","markups":[],"alignment":1},{"name":"c16a","type":4,"text":"","markups":[],"layout":10,"metadata":{"id":"1*34Fn007F_nnaX4oTvB7S8A.jpeg","originalWidth":2880,"originalHeight":1200}}],"sections":[{"startIndex":0}]},"isFullContent":false},"license":0,"inResponseToMediaResourceId":"","canonicalUrl":"https://medium.freecodecamp.org/a-guide-to-responsive-images-with-ready-to-use-templates-c400bd65c433","approvedHomeCollectionId":"336d898217ee","approvedHomeCollection":{"id":"336d898217ee","name":"freeCodeCamp","slug":"free-code-camp","tags":\["TECHNOLOGY","DESIGN","TECH","STARTUP","PRODUCTIVITY"\],"creatorId":"8b318225c16a","description":"Our community publishes stories worth reading on development, design, and data science.","shortDescription":"Our community publishes stories worth reading on…","image":{"imageId":"1*MotlWcSa2n6FrOx3ul89kw.png","filter":"","backgroundSize":"","originalWidth":0,"originalHeight":0,"strategy":"resample","height":0,"width":0},"metadata":{"followerCount":374268,"activeAt":1516038055493},"virtuals":{"permissions":{"canPublish":false,"canPublishAll":false,"canRepublish":false,"canRemove":false,"canManageAll":false,"canSubmit":false,"canEditPosts":false,"canAddWriters":false,"canViewStats":false,"canSendNewsletter":false,"canViewLockedPosts":false,"canViewCloaked":false,"canEditOwnPosts":false,"canBeAssignedAuthor":false,"canEnrollInHightower":false,"canLockPostsForMediumMembers":false},"isSubscribed":false,"isNewsletterSubscribed":false,"memberOfMembershipPlanId":"","isEnrolledInHightower":false,"isEligibleForHightower":false},"logo":{"imageId":"1*wViBNJ1o9rM5p6b-gf3vxg.png","filter":"","backgroundSize":"","originalWidth":600,"originalHeight":72,"strategy":"resample","height":0,"width":0},"twitterUsername":"freecodecamp","facebookPageName":"freecodecamp","publicEmail":"quincy@freecodecamp.com","collectionMastheadId":"af94c9c16d4","domain":"medium.freecodecamp.org","sections":[{"type":2,"collectionHeaderMetadata":{"backgroundImage":{},"logoImage":{"id":"1*JAG6GC1APQdyo2PegDDi2g@2x.png","originalWidth":601,"originalHeight":81,"alt":"freeCodeCamp"},"alignment":2,"layout":4}},{"type":1,"postListMetadata":{"source":3,"layout":6,"number":1,"postIds":["64306eb6bb27"]}},{"type":1,"postListMetadata":{"source":1,"layout":6,"number":25,"postIds":[]}},{"type":1,"postListMetadata":{"source":2,"layout":6,"number":25,"postIds":[],"sectionHeader":"Trending"}}],"tintColor":"#FF006400","lightText":true,"favicon":{"imageId":"1*B6_f-_SxscJ9FCuIjOrQAQ.jpeg","filter":"","backgroundSize":"","originalWidth":657,"originalHeight":654,"strategy":"resample","height":0,"width":0},"colorPalette":{"defaultBackgroundSpectrum":{"colorPoints":[{"color":"#FF429A35","point":0},{"color":"#FF408F33","point":0.1},{"color":"#FF3E8432","point":0.2},{"color":"#FF3B7830","point":0.3},{"color":"#FF376C2D","point":0.4},{"color":"#FF33602A","point":0.5},{"color":"#FF2E5426","point":0.6},{"color":"#FF284721","point":0.7},{"color":"#FF223A1C","point":0.8},{"color":"#FF1A2C15","point":0.9},{"color":"#FF111E0D","point":1}],"backgroundColor":"#FFFFFFFF"},"tintBackgroundSpectrum":{"colorPoints":[{"color":"#FF006400","point":0},{"color":"#FF2A7920","point":0.1},{"color":"#FF458C39","point":0.2},{"color":"#FF5D9E50","point":0.3},{"color":"#FF74AF66","point":0.4},{"color":"#FF8ABF7C","point":0.5},{"color":"#FFA0CE92","point":0.6},{"color":"#FFB5DDA8","point":0.7},{"color":"#FFCAEBBE","point":0.8},{"color":"#FFDFF8D4","point":0.9},{"color":"#FFF3FFEA","point":1}],"backgroundColor":"#FF006400"},"highlightSpectrum":{"colorPoints":[{"color":"#FFE3FBD9","point":0},{"color":"#FFDEFAD3","point":0.1},{"color":"#FFD9FACC","point":0.2},{"color":"#FFD3F9C6","point":0.3},{"color":"#FFCEF9C0","point":0.4},{"color":"#FFC9F8B9","point":0.5},{"color":"#FFC3F8B3","point":0.6},{"color":"#FFBDF7AD","point":0.7},{"color":"#FFB8F7A6","point":0.8},{"color":"#FFB2F6A0","point":0.9},{"color":"#FFACF699","point":1}],"backgroundColor":"#FFFFFFFF"}},"navItems":[{"type":1,"title":"Dev","tagSlug":"web-development","url":"https://medium.freecodecamp.org/tagged/web-development","source":"tagSlug"},{"type":1,"title":"Design","tagSlug":"design","url":"https://medium.freecodecamp.org/tagged/design","source":"tagSlug"},{"type":1,"title":"Data","tagSlug":"data-science","url":"https://medium.freecodecamp.org/tagged/data-science","source":"tagSlug"},{"type":3,"title":"Learn to code for free","url":"https://freecodecamp.com?ref=mn"}\],"colorBehavior":2,"instantArticlesState":0,"acceleratedMobilePagesState":0,"googleAnalyticsId":"UA-55446531-3","ampLogo":{"imageId":"","filter":"","backgroundSize":"","originalWidth":0,"originalHeight":0,"strategy":"resample","height":0,"width":0},"header":{"backgroundImage":{},"logoImage":{"id":"1\*JAG6GC1APQdyo2PegDDi2g@2x.png","originalWidth":601,"originalHeight":81,"alt":"freeCodeCamp"},"alignment":2,"layout":4},"type":"Collection"},"newsletterId":"","webCanonicalUrl":"https://medium.freecodecamp.org/a-guide-to-responsive-images-with-ready-to-use-templates-c400bd65c433","mediumUrl":"https://medium.freecodecamp.org/a-guide-to-responsive-images-with-ready-to-use-templates-c400bd65c433","migrationId":"","notifyFollowers":true,"notifyTwitter":false,"isSponsored":false,"isRequestToPubDisabled":false,"notifyFacebook":false,"responseHiddenOnParentPostAt":0,"isSeries":false,"isSubscriptionLocked":false,"seriesLastAppendedAt":0,"audioVersionDurationSec":0,"sequenceId":"","isNsfw":false,"isEligibleForRevenue":false,"isBlockedFromHightower":false,"deletedAt":0,"lockedPostSource":0,"hightowerMinimumGuaranteeStartsAt":0,"hightowerMinimumGuaranteeEndsAt":0,"type":"Post"},"mentionedUsers":\[\],"collaborators":\[\],"membershipPlans":\[\],"collectionUserRelations":\[\],"mode":null,"references":{"User":{"4e9454ed12f1":{"userId":"4e9454ed12f1","name":"Maciej Nowakowski","username":"nowakowskipl","createdAt":1437675503054,"lastPostCreatedAt":1516041330938,"imageId":"1*u0TLLSmxJd2v_fHbMy_trw.jpeg","backgroundImageId":"","bio":"Founder at https://www.codecamps.com — a one-week coding camps on React, Angular, GraphQL and Meteor in remote locations.","twitterScreenName":"nowakowskipl","socialStats":{"userId":"4e9454ed12f1","usersFollowedCount":96,"usersFollowedByCount":55,"type":"SocialStats"},"social":{"userId":"lo_3fdutBtaowJr","targetUserId":"4e9454ed12f1","type":"Social"},"facebookAccountId":"1080269385416499","allowNotes":1,"type":"User"}},"Collection":{"336d898217ee":{"id":"336d898217ee","name":"freeCodeCamp","slug":"free-code-camp","tags":["TECHNOLOGY","DESIGN","TECH","STARTUP","PRODUCTIVITY"],"creatorId":"8b318225c16a","description":"Our community publishes stories worth reading on development, design, and data science.","shortDescription":"Our community publishes stories worth reading on…","image":{"imageId":"1*MotlWcSa2n6FrOx3ul89kw.png","filter":"","backgroundSize":"","originalWidth":0,"originalHeight":0,"strategy":"resample","height":0,"width":0},"metadata":{"followerCount":374268,"activeAt":1516038055493},"virtuals":{"permissions":{"canPublish":false,"canPublishAll":false,"canRepublish":false,"canRemove":false,"canManageAll":false,"canSubmit":false,"canEditPosts":false,"canAddWriters":false,"canViewStats":false,"canSendNewsletter":false,"canViewLockedPosts":false,"canViewCloaked":false,"canEditOwnPosts":false,"canBeAssignedAuthor":false,"canEnrollInHightower":false,"canLockPostsForMediumMembers":false},"isSubscribed":false,"isNewsletterSubscribed":false,"memberOfMembershipPlanId":"","isEnrolledInHightower":false,"isEligibleForHightower":false},"logo":{"imageId":"1*wViBNJ1o9rM5p6b-gf3vxg.png","filter":"","backgroundSize":"","originalWidth":600,"originalHeight":72,"strategy":"resample","height":0,"width":0},"twitterUsername":"freecodecamp","facebookPageName":"freecodecamp","publicEmail":"quincy@freecodecamp.com","collectionMastheadId":"af94c9c16d4","domain":"medium.freecodecamp.org","sections":[{"type":2,"collectionHeaderMetadata":{"backgroundImage":{},"logoImage":{"id":"1*JAG6GC1APQdyo2PegDDi2g@2x.png","originalWidth":601,"originalHeight":81,"alt":"freeCodeCamp"},"alignment":2,"layout":4}},{"type":1,"postListMetadata":{"source":3,"layout":6,"number":1,"postIds":["64306eb6bb27"]}},{"type":1,"postListMetadata":{"source":1,"layout":6,"number":25,"postIds":[]}},{"type":1,"postListMetadata":{"source":2,"layout":6,"number":25,"postIds":[],"sectionHeader":"Trending"}}],"tintColor":"#FF006400","lightText":true,"favicon":{"imageId":"1*B6_f-_SxscJ9FCuIjOrQAQ.jpeg","filter":"","backgroundSize":"","originalWidth":657,"originalHeight":654,"strategy":"resample","height":0,"width":0},"colorPalette":{"defaultBackgroundSpectrum":{"colorPoints":[{"color":"#FF429A35","point":0},{"color":"#FF408F33","point":0.1},{"color":"#FF3E8432","point":0.2},{"color":"#FF3B7830","point":0.3},{"color":"#FF376C2D","point":0.4},{"color":"#FF33602A","point":0.5},{"color":"#FF2E5426","point":0.6},{"color":"#FF284721","point":0.7},{"color":"#FF223A1C","point":0.8},{"color":"#FF1A2C15","point":0.9},{"color":"#FF111E0D","point":1}],"backgroundColor":"#FFFFFFFF"},"tintBackgroundSpectrum":{"colorPoints":[{"color":"#FF006400","point":0},{"color":"#FF2A7920","point":0.1},{"color":"#FF458C39","point":0.2},{"color":"#FF5D9E50","point":0.3},{"color":"#FF74AF66","point":0.4},{"color":"#FF8ABF7C","point":0.5},{"color":"#FFA0CE92","point":0.6},{"color":"#FFB5DDA8","point":0.7},{"color":"#FFCAEBBE","point":0.8},{"color":"#FFDFF8D4","point":0.9},{"color":"#FFF3FFEA","point":1}],"backgroundColor":"#FF006400"},"highlightSpectrum":{"colorPoints":[{"color":"#FFE3FBD9","point":0},{"color":"#FFDEFAD3","point":0.1},{"color":"#FFD9FACC","point":0.2},{"color":"#FFD3F9C6","point":0.3},{"color":"#FFCEF9C0","point":0.4},{"color":"#FFC9F8B9","point":0.5},{"color":"#FFC3F8B3","point":0.6},{"color":"#FFBDF7AD","point":0.7},{"color":"#FFB8F7A6","point":0.8},{"color":"#FFB2F6A0","point":0.9},{"color":"#FFACF699","point":1}],"backgroundColor":"#FFFFFFFF"}},"navItems":[{"type":1,"title":"Dev","tagSlug":"web-development","url":"https://medium.freecodecamp.org/tagged/web-development","source":"tagSlug"},{"type":1,"title":"Design","tagSlug":"design","url":"https://medium.freecodecamp.org/tagged/design","source":"tagSlug"},{"type":1,"title":"Data","tagSlug":"data-science","url":"https://medium.freecodecamp.org/tagged/data-science","source":"tagSlug"},{"type":3,"title":"Learn to code for free","url":"https://freecodecamp.com?ref=mn"}\],"colorBehavior":2,"instantArticlesState":0,"acceleratedMobilePagesState":0,"googleAnalyticsId":"UA-55446531-3","ampLogo":{"imageId":"","filter":"","backgroundSize":"","originalWidth":0,"originalHeight":0,"strategy":"resample","height":0,"width":0},"header":{"backgroundImage":{},"logoImage":{"id":"1\*JAG6GC1APQdyo2PegDDi2g@2x.png","originalWidth":601,"originalHeight":81,"alt":"freeCodeCamp"},"alignment":2,"layout":4},"type":"Collection"}},"Social":{"4e9454ed12f1":{"userId":"lo_3fdutBtaowJr","targetUserId":"4e9454ed12f1","type":"Social"}},"SocialStats":{"4e9454ed12f1":{"userId":"4e9454ed12f1","usersFollowedCount":96,"usersFollowedByCount":55,"type":"SocialStats"}}}}) // ]]>