Minification Techniques - contivero/hasmin GitHub Wiki

Minification Techniques

Table of Contents

Erasing Unnecesary Whitespace

@media screen and (min-width: 767px) {
  .icon-next {
    height: 20px;
    width: 20px
  }
}

Can be reduced to:

@media screen and (min-width:767px){.icon-next{height:20px;width:20px}}

Removing Comments

/* headings */
h1 /*, h2, h3 */, h4, h5 {
  color: cyan; /* TODO change */
  /* margin: 0; */
}
becomes:
h1, h4, h5 {
  color: cyan;
}

Removing Semicolons at the End of Blocks

As specified in §4.1.8 of CSS2.1, and later also in §2 of CSS Syntax Module Level 3, semicolons are separators, not terminators. Hence, semicolons at the end of blocks may be removed. E.g.:

h1 {
  font-family: Helvetica;
  font-style: normal;
}
becomes:
h1 {
  font-family: Helvetica;
  font-style: normal
}

Normalizing Dimensions of Absolute Units

Whenever using absolute units, we may replace them for an equivalent shorter dimension. This also works on media queries:

@media (min-width: 768px) {
  .horizontal dt {
    width: 160px;
  }
Can be changed to:
@media (min-width: 8in) {
  .horizontal dt {
    width: 10pc;
  }
This can be done with every dimension, i.e. <length>, <time>, <angle>, <frequency>, and <resolution>.

Removing Units from Null Dimensions

Since the beginning of CSS (see §6.1), zero <length> values can be written without units. Following a discussion, the specification was updated in 2016 to allow also zero <angle> values to be written without units.

Keep in mind these are the only two dimension types that accept 0 as a value. In other words, zero values of type <frequency>, <resolution>, or <time> should not be stripped of their units.

padding:   0px;
transform: skew(0deg);
becomes:
padding:   0;
transform: skew(0);

Normalizing Numbers

To normalize a number, what is done is

  1. Remove any leading zeros

  2. Remove any trailing zeros

  3. Remove the positive number sign (+)

.weirdClass {
  /* examples of 1. */
  margin:       0250px;
  border:       0.8in;
  padding:      -0.5em;
  /* examples of 2. */
  border-right: 4.50pt;
  border-top:   2.0px;
  /* example of 3. */
  border-left:  +5px;
}
becomes:
.weirdClass { /* examples of 1. */
  margin: 250px;
  border: .8in;
  padding: -.5em;
  /* examples of 2. */
  border-right: 4.5pt;
  border-top: 2px;
  /* example of 3. */
  border-left: 5px; }

Removing Quotation Characters

In the following cases explained below, quotations characters may be removed.

Font Families

font-family: "Courier New", "serif";
becomes:
font-family: Courier New, "serif";
Note that the quotes from "serif" aren’t removed because serif is a <generic-family> font. Unquoting "serif" would make user-agents interpret the value as the serif keyword instead of the intended homonymous font family.

Attribute Selectors

Values in attribute selectors are either <string>s or identifiers. Thus, when a string can be represented as an identifier, the quotation marks may be removed. E.g.:

a[href="foo"]
a[href~='-bar']
a[href^="http://"]
can be rewritten as:
a[href=foo]
a[href~=-bar]
a[href^="http://"]
Note that quotation marks remain for the "http://" string, since http:// is not a valid identifier, hence quotes or the appropriate escape sequences must be used.

URL Values

url("pagelogo.png")
url('//s.ytimg.com/img/vfljun.png')
url("fonts/BebasNeue Bold.eot")
becomes:
url(pagelogo.png)
url(//s.ytimg.com/img/vfljun.png)
url("fonts/BebasNeue Bold.eot")
Quotes remain in the last url() because there is a space between BebasNeue and Bold. To remove the quotes, the space could be replaced by %20 following URI percentage encodings (as specified in 2.1 of RFC 3986), or an equivalent escape sequence such as \20.

Using the Older Pseudo-Element Syntax

CSS2 prefixed both pseudo-classes and pseudo-elements with a single colon. To distinguish between the two, CSS2.1 changed the convention for pseudo-elements, prefixing them with two colons. However, since most browsers still support the old notation, the following pseudo-elements:

::after
::before
::first-line
::first-letter
can be changed for their old counterparts:
:after
:before
:first-line
:first-letter

Replacing 0% for 0

In several places either a <percentage>, or another data type inhabited by 0 can be used (such as <number>, or <length>). In these cases, it is usually—though not always—possible to drop the percentage sign, and have it mean the same.

@keyframes a {
  0% { margin: 0% }
  100% { margin: 20%}
}
May be rewritten as:
@keyframes a {
  0% { margin: 0 }
  100% { margin: 20%}
}
Note that only the 0% in margin gets turned into 0, because keyframe selectors accept only <percentage>s (aside from to and from, which are just synonyms for 100% and 0%), thus replacing it would be invalid.

Known exceptions to this are height and max-height, where 0 and %0 mean different things, so Hasmin doesn’t apply the replacement in those cases.

Minifying <color>

There are many different equivalent ways to denote colors, so we look for the shortest. For example, the following values:

rgb(238, 130, 238)
rgba(238, 130, 238, 1)
hsl(200, 100%, 1%)
hsla(200, 100%, 1%, 0.5)
mediumSpringGreen
transparent
#ffccdd
May be written shorter as:
violet
violet
#000305
#00030580
#00fa9a
#0000
#fcd

Minifying <gradient>

Linear Gradient

All the following values are equivalent.

linear-gradient(180deg, #0000 0%, red 6%, blue 50%, aqua 50%, gold 75%, pink 100%)
linear-gradient(#0000 0%, red 6%, blue 50%, aqua 50%, gold 75%, pink 100%)
linear-gradient(#0000, red 6%, blue 50%, aqua 50%, gold 75%, pink)
linear-gradient(#0000, red 6%, blue 50%, aqua 0, gold 75%, pink)
linear-gradient(#0000, red 6%, blue 50%, aqua 0, gold, pink)
Hasmin understands this, and is able to look for the shortest representation of a linear-gradient().

Radial Gradient

As with linear gradients, hasmin supports reducing radial gradients. For example, the following values:

radial-gradient(farthest-corner ellipse at 50%,red,pink)
radial-gradient(ellipse at center,peru,blue)
radial-gradient(farthest-corner,lime,teal)
radial-gradient(circle 1in,peru,pink)
radial-gradient(circle farthest-corner,peru,pink)
radial-gradient(ellipse 1in 1in,peru,pink)
radial-gradient(#000 0%,#abc 50%,#fff 100%)
get reduced to:
radial-gradient(red,pink)
radial-gradient(peru,blue)
radial-gradient(lime,teal)
radial-gradient(1in,peru,pink)
radial-gradient(circle,peru,pink)
radial-gradient(1in 1in,peru,pink)
radial-gradient(#000,#abc,#fff)

Minifying <position>

Examples of this are converting these:

perspective-origin: 0% 50%;
perspective-origin: 50% 0%;
background-position: left 0% top 0%;
background-position: right 0% center;
into:
perspective-origin: 0;
perspective-origin: top;
background-position: 0 0;
background-position: 100%;

Minifying <repeat-style>

Whenever a <repeat-style> value is used, such as in the background-repeat and background properties, the following conversions can be applied:

Table 1. <repeat-style> equivalences
Two-keyword syntax Single keyword

no-repeat repeat

repeat-y

repeat no-repeat

repeat-x

no-repeat no-repeat

no-repeat

repeat repeat

repeat

space space

space

Minifying <timing-function>

cubic-bezier(0.25, 0.1, 0.25, 1)
cubic-bezier(0, 0, 1, 1)
cubic-bezier(0.42, 0, 1, 1)
cubic-bezier(0, 0, 0.58, 1)
cubic-bezier(0.42, 0, 0.58, 1)
steps(1, start)
steps(1, end)
steps(2, end)
into these:
ease
linear
ease-in
ease-out
ease-in-out
step-start
step-end
steps(2)
The last one is valid because end is the default second value, so when it isn’t present (because it is optional), that’s the value UAs should assume.

Minifying the <An+B> Microsyntax

The An+B microsyntax is used by some functional pseudo classes, such as :nth-child(). Some examples of changes that may be done with the <an+b\> data type are:

:nth-child(even)
:nth-last-child(2n+1)
:nth-last-of-type(+1n+0)
:nth-of-type(-1n-0)
:nth-child(0n+0)
becomes:
:nth-child(2n)
:nth-last-child(odd)
:nth-last-of-type(n)
:nth-of-type(-n)
:nth-child(0)

Minifying <shadow>

This data type is used in the box-shadow property and defined in 7.3 of CSS Backgrounds and Borders Level 3. In it, the 3rd and 4th <length> values are optional and zero by default. Thus:

box-shadow: 3px 3px 0 3px red;
box-shadow: red 3px 3px 3px 0;
box-shadow: inset 2px 2px 0 0;
box-shadow: 1px 1px 0 inset;
may be rewritten as:
box-shadow: 3px 3px 0 3px red;
box-shadow: red 3px 3px 3px;
box-shadow: inset 2px 2px;
box-shadow: 1px 1px inset;

Minifying <transform-function>

Every transform function has associated a 4x4 matrix. Hasmin finds the shortests equivalent function able to represent a given matrix. For example, the following functions:

translate(0)
matrix(1,0,0,0,0,0)
matrix(1,0,0,1,35,0)
scale3d(0,1,1)
are rewritten as:
skew(0)
scaleY(1)
translate(35px)
scaleX(0)

Minifying the transform property

A list of <transform-function\> can be multiplied (that is, their corresponding 4x4 matrices are multiplied), to look for a shorter equivalent representation, e.g.:

translate(10px) skew(45deg) scaleX(5)
skew(45deg) translate(100%) scaleX(5) scaleX(2)
becomes:
matrix(5,0,1,1,10,0)
skew(45deg) translate(100%) scale(10)
Note that for it to work, absolute values must be used. If a percentage, or some relative unit (e.g. em) is used with a value other than 0, then there is no way to know the computed value by only reading the CSS file, so the optimization can’t be done. That’s why in the second case, translate is left untouched.

Also, since matrix multiplication isn’t conmutative, skew(45deg) and scale(10) can’t be multiplied, since that would change the original transform.

Minifying <filter-function>

brightness(0%)
contrast(100%)
grayscale(110%)
invert(111%)
opacity(50%)
sepia(.08)
gets converted into:
brightness(0)
contrast(1)
grayscale(1.1)
invert(1.11)
opacity(.5)
sepia(8%)

Minifying <bg-size>

mask-size: 1em auto;
background-size: auto 1em;
into:
mask-size: 1em;
background-size: auto 1em;
Note that auto can only be removed when in the second place, that’s why in the example the background-size declaration remains the same.

Minifying <border-radius>

Something like:

border-radius: 1px 2px 1px 2px / 1px 1px 1px 1px;
becomes:
border-radius: 1px 2px / 1px;

Minifying <basic-shape>

Some simple examples of the four functions are:

inset(1px 1px round 4px / 4px)
circle(closest-side at center)
ellipse(10px 10px at left bottom)
polygon(nonzero, 10px 10px)
which become:
inset(1px round 4px)
circle()
ellipse(10px 10px at 0 100%)
polygon(10px 10px)

For some more examples, check the test suite.

Minifying Selectors

Replacing >> for ' ' (whitespace)

§14.1 of Selectors Level 4 introduced the >> combinator, which just like ' ' (whitespace), represents the descendant combinator. Thus, a selector such as h1 >> .class may be replaced for h1 .class.

Replacing Functional Pseudoclasses

As per Selectors Level 4, Hasmin replaces:

  1. :nth-of-type(1) for :first-of-type

  2. :nth-last-of-type(1) for :last-of-type

  3. :nth-child(1) for :first-child

  4. :nth-last-child(1) for :first-child

Replacing [class~="x"] for .x

If the attribute value is without quotes, or the quotes may be removed, the replacement is applied. Otherwise (i.e., the attribute value is not a valid class identifier), the selector is left as is.

Using Property Traits

Given the following:

.class {
  line-height: inherit;
  box-sizing: border-box;
  border-collapse: separate;
}
this may be turned into:
.class {
  line-height: unset;
  box-sizing: unset;
  border-collapse: initial;
}
In this particular case, these changes may be done because:
  1. The line-height property inherits, therefore unset equals inherit.

  2. For box-sizing, the value border-box is its initial value. It could be replaced for initial, but box-sizing doesn’t inherit, so unset is synonymous to initial for this property, and shorter.

  3. Lastly, separate is the initial value of border-collapse, so it can just be replaced for initial which is shorter. Note that since this property inherits, unset can’t be used because that would mean inherit, not initial.

Hasmin has information about almost every property (save some of the latest experimental ones), i.e. if it inherits, what its initial value is, etc.

Simplifying Shorthands

TRBL Shorthands

margin: 1px auto 3px auto;
padding: 1px 2px 1px;
border-color: red red;
border-width: thin thin thin thin;
border-style: none solid none solid;
gets turned into:
margin: 1px auto 3px;
padding: 1px 2px;
border-color: red;
border-width: thin;
border-style: none solid;

Normal Shorthands

Initial values are removed from shorthands, e.g.:

list-style: disc url(img/x.png) inside;
outline: thin none invert;
text-decoration: #eee solid none;
border-top: medium none currentColor;
becomes:
list-style: url(img/x.png) inside;
outline: thin;
text-decoration: #eee;
border-top: none;

Replacing Property Specific Synonyms

font-weight

font-weight: normal;
font: italic small-caps bold 12px/4;
becomes:
font-weight: 400;
font: italic small-caps 700 12px/4;

word-spacing

word-spacing: normal;
word-spacing: initial;
becomes:
word-spacing: 0;
word-spacing: 0;

vertical-align

vertical-align: baseline;
vertical-align: initial;
vertical-align: unset;
becomes:
vertical-align: 0;
vertical-align: 0;
vertical-align: 0;

text-shadow

If the third <length> value is 0, it may be removed, e.g.:

text-shadow: 0 20px 0 #0f7d7d;
becomes:
text-shadow: 0 20px #0f7d7d;

transform-origin

Hasmin understands about the different possibilities for this property, and is able to minify it. For example, these:

transform-origin: left top;
transform-origin: bottom center;
transform-origin: center center;
transform-origin: center center 1px;
become:
transform-origin: 0 0;
transform-origin: bottom;
transform-origin: 50%;
transform-origin: 50% 50% 1px;

Converting Escaped Characters to Unicode

Hasmin can convert escaped characters into their unicode equivalents, e.g.:

content: "\7121";
content: '\0610F';
content: "\005473";
becomes:
content: "無";
content: '意';
content: "味";
By default, this is disabled because even though it can lead to big reductions in the sizes of minified files, it tends to worsen gzip results. Of course, for some files the opposite is true, so enable at discretion.

Minifying the @import rule

The @import rule is followed by either a <url>, or <string> (see §2 of Cascading and Inheritance Level 4). That means that whenever a url() value is found, the function notation may be removed in favor of the bare <string>, e.g.

@import url("landscape.css");
@import url(narrow.css);
can be rewritten as:
@import "landscape.css";
@import "narrow.css";

The @keyframes rule

Minifying keyframe selectors

Keyframe selectors are percentages, the keyword from (equivalent to 0%), or the keyword to (equivalent to 100%). Therefore, if we have something like this:

@keyframes slide-right {
  from {
    margin-left: 0px;
  }
  50% {
    margin-left: 110px;
  }
  100% {
    margin-left: 200px;
  }
becomes:
@keyframes slide-right {
  0% {
    margin-left: 0px;
  }
  50% {
    margin-left: 110px;
  }
  to {
    margin-left: 200px;
  }

The @supports rule

Double negations are simplified, e.g.:

@supports not (not (display: flex)){/* */}
is replaced for:
@supports (display: flex){/* */}

The @media rule

The all media type is the default one and may be omitted. Thus, the following rules are equivalent:

@media all {/*..*/}
@media {/*..*/}
as are these ones:
@media all and (min-width: 500px) {/*..*/}
@media (min-width: 500px) {/*..*/}
or these ones:
@media (min-width:500px),all and (min-device-pixel-ratio:0){/*..*/}
@media (min-width:500px),(min-device-pixel-ratio:0){/*..*/}
⚠️ **GitHub.com Fallback** ⚠️