The temptation to dive right into new and exciting CSS tricks is strong – you might even do it without knowing it through learning by example or implementing that hot new framework everyone is talking about. It is more important to fully understand new CSS classes and properties, experiment with them and learn their limitations before putting them into practice, especially when working on commercial projects. In this article I’ll cover some common CSS mishaps and pitfalls of newer techniques to help you make informed decisions when building your stylesheets.

Misused Properties

font-size-adjust

If you have issues with your chosen web font and its fallback rendering with totally different x-heights, this property is for you. However, it only benefits Firefox users right now, despite being part of the CSS3 Spec. That may be largely to do with the variying ways in which different browsers render fonts to begin with, making it difficult to create a real standard for how this property’s value and expected result can be determined.

The font-size-adjust property works by taking the original font-size and dividing it by the font-size-adjust value for any fallback fonts defined. What that value should be to get the best results takes trial and error for the most part and depends on the fallback font, the initial size it is being displayed at and which browser is rendering it. General example:


h1{
  font-family: 'Alagreya SC', 'georgia', serif;
  font-size: 2rem;  
  font-size-adjust: 0.5;
}

If Alagreya doesn’t load and the heading is displayed in Georgia or the browser’s default serif, it will be shown at 1rem, or .5 of 2rem / 50%.

The font-size-adjust property should not be confused with text-size-adjust, which is specifically for mobile display of text. Some mobile browsers apply a text inflation algorithm to increase text size inside an element without modifying the layout unless CSS is present to deter this. In most cases you only use text-size-adjust to reset this when it actually causes design issues on mobile devices for your website. For example:


body{ 
  -webkit-text-size-adjust: 100%; 
  -ms-text-size-adjust: 100%;
} 

If -webkit-text-size-adjust is set to none or unset, Webkit-based desktop and tablet browsers like Chrome or Safari will prevent zooming rather than simply ignoring the property. This is a big problem for desktop users, so if you must set this property to none because 100% is not working, it is best to do it with media queries.

font-stretch

This property is often confused with font-size-adjust or other methods of manipulating how the font is rendered. In fact, it only allows you to choose an alternate font-face or for the declared font, if it happens to have one. For example, you might include the Roboto OTF in your document, which has a condensed version. The font-stretch property allows you to set this without re-declaring the font-family:

h1{
  font-stretch: condensed;
}

In a similar way, you don’t want to confuse this with font-variant, which only allows you to enable things like ligatures, small caps or ornaments that are part of an open-type font you have included in your stylsheet.

Note that neither of these properties are supported in Safari.

font-smoothing

-webkit-font-smoothing, -moz-osx-font-smoothing and font-smooth all attempt to anti-alias fonts to fix jagged rendering on some devices and browsers for some fonts. Other than being totally situational and thus unpredictable from a design standpoint, all of these properties have been juggled around the spec and should be removed from your style-sheets or not used in the first place.

While this property doesn’t lead to any obvious problems if present, only -webkit-font-smoothing and -moz-osx-font-smoothing still have partial support in newer webkit browsers but are considered non-standard.

Any time you need to reach for CSS to fix a design problem rather than do the designing, get to the root of the problem and fix it there. In this case, a font that is rendering poorly in a scenario you expect a lot of visitors to be in should probably just be replaced with a better font.

clip-path

The clip-path property in CSS4 will allow us to set a geometric value, box value or SVG file path that defines the shape of the element. This is useful for placing background fills on complex shapes including text, creating abstract layouts or wrapping text around something other than a box or circle. The declaration is really simple (you’ll need Chrome or Safari to see this):

See the Pen Clip-path: Browser support by Karen Menezes (@imohkay) on CodePen.27486

…but as you can see, external SVG files are not currently supported, and for clip-path to work at all you need experimental features enabled in your webkit browser. Note that Firefox does not support shapes yet, and it doesn’t work at all in IE.

clip-path on CanIUse

object-fit

This helpful new property allows you to handle img and video elements in much the same way as background images by providing three primary ways the image can fit inside it’s wrapper: scale-down, contain or cover. Its partner property object-position works just like background-position, allowing you to manipulate how the image or video sits inside its wrapper without margin or text-align hacks.

Through these values, images and videos can achieve a level of responsiveness that is more adaptive to the screen size, helping us move away from Javascript solutions such as fitvid and solving aspect ratio issues with videos requiring more complex CSS rules to resolve. For WordPress theme developers, object-fit means we don’t need to setup additional image sizes – we can let srcset and CSS do the work.

Using object-fit is safe if you don’t care about IE support, but it is important to test your implementation using a tool like Google Resizer to ensure your layouts respond appropriately to elements scaled this way.

object-fit on CanIUse

CSS Compositing and Blending

background-blend-mode and mix-blend-mode are exciting new properties that allow blending between images, gradients, and colors similar to Photoshop layer blend modes, as you can (hopefully) see in this CodePen demo:

27486

Full Screen Demo

At its most simple, blend modes take any overlapping elements (typically parent and child element) and blend them using the modes specified. In this example, we have a background image on the body, and a colored header on top of it with a mix-blend-mode of screen. This allows the colored header background to blend with the image background below it without affecting its children the way opacity does.

The techniques used to get the effects aren’t a problem in most cases, but blend modes are currently not supported in IE and some mobile browsers, and have only partial support in Safari.

There are also performance concerns, as anything that requires a browser to manipulate or calculate before rendering the page will cause it to struggle more. Performance should also be a consideration with more widely-used CSS properties like box-shadows, transparency, transforms and filters, though the impact is negligible in most cases.

Blend modes are best used in cases where using an actual image prepared in Photoshop is not practical, such as applications with user-submitted content where a uniform style needs to be applied, or to style web text in preference of using images with embedded text.

CSS Grid

Grid is probably the most highly anticipated CSS addition and is already being put into use by cutting-edge designers and talked about everywhere. This can be really confusing for those new to CSS or who tend to learn by example, as grid is not fully supported anywhere yet except Chrome 57+ and Firefox 52+, neither of which are full releases at the time of this post. Furthermore, grid is often confused with flex.

CSS grid aims to provide us with a simple means of controlling overall layout, in the way tables used to. Flexbox allows us to control how content behaves inside those layouts.

Google Developer Expert Ranchel Andrew explains it best:

Do you want to let your content control the way it is displayed, on a row by row or column by column basis? Thats flexbox.

Or, do you want to define a grid, and either allow items to be auto-placed into the cells defined by that grid, or control their positioning using line-based positioning or grid template areas. Thats grid.

calc()

CSS calc() has been around awhile now and is supported in most browsers – but that doesn’t mean it is totally stable. Unfortunately calc() still has a host of issues due to how it works – it applies the defined equation to the CSS property it is paired with, leading to a wide margin for error. Be aware of the following:

  • IE11 is reported to not support calc() correctly in generated content
  • IE11 is reported to have trouble with calc() with nested expressions, e.g. width: calc((100% – 10px) / 3); (i.e. it rounds differently)
  • Firefox <48 does not support calc() inside the line-height, stroke-width, stroke-dashoffset, and stroke-dasharray properties.
  • IE & Edge are reported to not support calc inside a ‘flex’. (Not tested on older versions)
  • This example does not work: flex: 1 1 calc(50% – 20px);
  • Safari & iOS Safari (both 6 and 7) does not support viewport units (vw, vh, etc) in calc().
  • IE10, IE11, and Edge < 14 don't support using calc() inside a transform.
  • IE 9 – 11 don’t render box-shadow when calc() is used for any of the values
  • IE10 crashes when a div with a property using calc() has a child with same property with inherit.
  • IE11 does not support transitioning values set with calc()

We see calc() used most often with the width property to create responsive elements, which is probably the safest use:


input {
  padding: 2px;
  display: block;
  width: 98%;  /* fallback for browsers without support for calc() */
  width: calc(100% - 20px);
}

So using calc() is not in itself a problem, but how you use it and knowing the math is.

Learn how to mix units in Calc()

New Pseudo-Classes

:dir()

The :dir pseudo-class matches elements based on what direction the text inside is read. Normally, this is determined by the dir attribute.

Unlike similar pseudo-classes, this one is not the same as using an equivalent attribute selector like [dir]. Where [dir] is exacting and needs a value, :dir() can calculate more intelligently which element content to effect even if an initial or auto value is set on the attribute. While this is an improvement in some areas, :dir() still only considers the explicit direction set in the HTML document, ie <article dir="rtl">. It can’t tell if you have set a text direction using CSS, so you would still need to reset CSS directional styles or avoid using them altogether if designing for RTL languages.

:dir() aims to actually be super-useful, and once implemented fully will be a big improvement for designers of multi-lingual websites. For now, it just doesn’t have great support, is considered experimental, and is unfortunately quite buggy.

:dir() on the MDN

:matches() and :any()

Both of these selectors do the same thing, with :any() being the former supported pseudo-class (with -webkit) and :matches() being its replacement in CSS4. Other than being confusing due to name changes, this pseudo-class needs to be fully understood before you can put it to really good use.

Essentially :matches() can currently help you condense your CSS declarations to make the stylesheet easier to read. This should also lead to performance increases as well in some cases. For example:


section section h1, section article h1, section aside h1, section nav h1,
article section h1, article article h1, article aside h1, article nav h1,
aside section h1, aside article h1, aside aside h1, aside nav h1,
nav section h1, nav article h1, nav aside h1, nav nav h1, {
  font-size: 20px;
}

…becomes:


:-webkit-matches(section, article, aside, nav)
:-webkit-matches(section, article, aside, nav) h1 {
  font-size: 20px;
}

Be sure to replace any instance of :any with -webkit-:matches and fallbacks, as :any is deprecated for handling specificity incorrectly.

CSS Variables

I’ll cut right to the chase on this one: you absolutely should use CSS Variables. If you have no idea what a CSS Variable is, it is the spec’s way of making CSS more like LESS. This allows you to do very cool things like create a set of defaults at the beginning of your document:

:root{
  --main-color: #777;
  --dark-color: #333;
  --main-font: 'Source Sans Pro';
  --link-color: #f00;
  --gap: 20px;
}
body{
 color: var(--main-color);
 font-family: var(--main-font);
}
a{
  color: var(--link-color);
}
article strong{
  color: var(--dark-color);
}
.sidebar .widget{
  margin: var(--gap);
}

So as you can see, variables offer a way to build far cleaner and more semantic stylesheets. They also have an advantage over both traditional CSS and preprocessors like LESS in their ability to be used inside of media queries. Here is one great example of using a variable to set a default gutter, then adjust it for mobiles:

:root {
  --gutter: 1vh;
}

section {
  margin: var(--gutter);
}

@media (min-width: 600px) {
  :root {
    --gutter: 1.5vh;
  }
}

Variables can also be nested, which can really help you build more flexible WordPress themes. In this example, you would only need one customizer control for the primary color variable, and can more elegantly apply it to other properties without overdoing inline styles or using PHP.

:root {
  --primary-color: #333;
  --logo-text: var(--primary-color);
}

CSS Variables are not without a few caviats. You need to be careful to format them properly and ensure they translate to a valid declaration. For example, you can’t use var() as the property like this:


h2{
  --side: margin-top;
  var(--side): 20px;
}

Or as only part of a value:


h2{
  --gap: 20;
  margin-top: var(--gap)px;
}

Unconventional Font Size Units

In CSS there are a large number of units to choose from, but we tend to fall into the habit of using a particular one because that is how we learned CSS, or we’ve just gotten used to working with it. The most common one is px, which can be used on just about anything – it isn’t the best choice for text, though, as pixel density can vary across devices. The solution for this was relative units, which are defined as em, ex, ch and rem and a more universal percentage %. The problem is that none of these are created equal either, so which is the best?

Relative Units

The em unit is also widely used with text and margins but has an important niggle – it multiplies itself by each declared em size. So if your default size is the equivalent of 14px, the first element’s em value of 1.2 might scale the text to 16.8px, and if another declaration sets the em for a child element, it adds it on to the 16.8px. For this reason, em is not really the best choice for font sizing either.

The rem unit was introduced to allow you to declare font sizes that only scale against the root or default, no matter where the element is in the cascade. This is a lot more logical and gives you more control over scaling fonts for small screens. It otherwise works like em by considering the font’s m characfter width, but does need the original font-size declaration to use a px value (ie don’t set it to 100%).

Next is ex and ch. Like the em unit, ex and ch are relative to the font being used, with ex being the x-height of the font, and ch being the width of the 0. Neither of these are good replacements for rem except in special circumstances – monospaced fonts can benefit from using ch units, for example.

Viewport Units

But wait, there’s more. We now also have vh, vw, vmin and vmax, which aim to give us ultimate control over responsive sizing. These length units represent 1% of the viewport size for viewport width (vw), height (vh), the smaller of the two (vmin), or the larger of the two (vmax). For example, if the browser’s height is 750px, 1vh would evaluate to 7.5px. Similarly, if the viewport width is 1024px, 1vw would evaluate to 10.24px.

h2 {
  font-size: 4vw;
}

Is more or less the same as:

h2{
 font-size: 5rem;
}

…but only if you’re in a desktop browser at 1920px wide resolution.

When working with text, vw is probably the best choice, as it allows the text to scale relative to actual viewing experience, requiring less font size declarations and responsive overrides, with rem being a close second. You’ll need to be aware of the drawbacks though:

  • Chrome does not print elements defined with viewport units.
  • Currently all browsers but Firefox incorrectly consider 100vw to be the entire page width, including vertical scroll bar, which can cause a horizontal scroll bar when overflow: auto is set.
  • iOS 7 Safari recalculates widths set in vh as vw, and heights set in vw as vh, when orientation changes.
  • Safari & iOS Safari (both 6 and 7) does not support viewport units for border widths, column gaps, transform values, box shadows or in calc().
  • Chrome does not support viewport units for border widths, column gaps, transform values, box shadows or in calc() until version 34.
  • IE Support is really poor

rem has less issues so is the safer, though less flexible choice. Wondering about pt? Forget about it.

Finding Solutions

In most cases, the success of the selectors and properties you use comes down to knowledge and good planning, but what about situations where support is the only thing holding you back? If we rewind back to the mix-blend-mode example, we have a property that has potential to do some really great things. You don’t have to pass up an opportunity to be a little cutting-edge just because a property isn’t fully supported everywhere yet – just ensure your properties have the right prefixes or fallbacks. You can take this one step further by implementing feature queries.

@supports

This query type takes the CSS it wraps and applies it only when the condition is met, similar to how media queries only apply the CSS it wraps when the defined screen width is detected.

@supports ( (property: value) or (property:value) {
     /* specific CSS applied when these properties are supported */
}

For example, here we check whether mix-blend-mode is supported before declaring styles that use it:

@supports (mix-blend-mode: exclusion) {
  .element::before {
    z-index: 1;
    background: cyan;
    mix-blend-mode: exclusion;
  }
}

You can also use @supports the opposite way to write in fallbacks instead:


@supports not (mix-blend-mode: exclusion) {
  .element::before {
    z-index: 1;
    background: transparent;
  }
  .element{
    background-image: url(image.jpg);
  }
} 

Feature queries like @supports {} can be placed anywhere in your stylesheet, unlike media queries. Use them anytime you want to include something that isn’t fully supported across all major browsers, or which needs a fallback for other reasons, and you’ll save yourself a lot of headache and refactoring later on.

If you weren’t familiar with some of these properties or selectors in CSS before reading this article, or were unsure how or when to use them, I hope you feel more confident and inspired to dig deeper.

2017 is bringing a host of new features to CSS, and many of their biggest strengths (and pitfalls) have yet to be discovered. Tools like CanIUse and both the Mozilla Developer Network and Google Developer Network are your greatest assets in staying on top of issues and inventions, and should be an essential part of ongoing learning.

Know of a CSS use case others should avoid? Share in the comments!

And if you missed it, check out The Power of CSS Selectors and How to Use Them.

2 Comments

John Michael Wilson
Dec 20, 2016 at 12:55 am

Great article! well explained and will be very helpful in web designing

David Millar
Dec 22, 2016 at 5:45 pm

New or misunderstood features aren’t ‘tricks’.

Post Comment or Questions

Your email address will not be published. Required fields are marked *