Andrés Galante – CSS-Tricks https://css-tricks.com Tips, Tricks, and Techniques on using Cascading Style Sheets. Wed, 01 May 2024 17:35:07 +0000 en-US hourly 1 https://wordpress.org/?v=6.6.1 https://i0.wp.com/css-tricks.com/wp-content/uploads/2021/07/star.png?fit=32%2C32&ssl=1 Andrés Galante – CSS-Tricks https://css-tricks.com 32 32 45537868 A Complete Guide to CSS Media Queries https://css-tricks.com/a-complete-guide-to-css-media-queries/ https://css-tricks.com/a-complete-guide-to-css-media-queries/#comments Fri, 02 Oct 2020 15:13:37 +0000 https://css-tricks.com/?p=319964 Media queries can modify the appearance (and even behavior) of a website or app based on a matched set of conditions about the user's device, browser or system settings.


A Complete Guide to CSS Media Queries originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
CSS Media queries are a way to target browser by certain characteristics, features, and user preferences, then apply styles or run other code based on those things. Perhaps the most common media queries in the world are those that target particular viewport ranges and apply custom styles, which birthed the whole idea of responsive design.

/* When the browser is at least 600px and above */
@media screen and (min-width: 600px) {
  .element {
    /* Apply some styles */
  }
}

There are lots of other things we can target beside viewport width. That might be screen resolution, device orientation, operating system preference, or even more among a whole bevy of things we can query and use to style content.

Looking for a quick list of media queries based on the viewports of standard devices, like phones, tablets and laptops? Check out our collection of snippets.

Using media queries

Media queries are commonly associated with CSS, but they can be used in HTML and JavaScript as well.

HTML

There are a few ways we can use media queries directly in HTML.

There’s the <link> element that goes right in the document <head>. In this example. we’re telling the browser that we want to use different stylesheets at different viewport sizes:

<html>
  <head>
    <!-- Served to all users -->
    <link rel="stylesheet" href="all.css" media="all" />
    <!-- Served to screens that are at least 20em wide -->
    <link rel="stylesheet" href="small.css" media="(min-width: 20em)" />
    <!-- Served to screens that are at least 64em wide -->
    <link rel="stylesheet" href="medium.css" media="(min-width: 64em)" />
    <!-- Served to screens that are at least 90em wide -->
    <link rel="stylesheet" href="large.css" media="(min-width: 90em)" />
    <!-- Served to screens that are at least 120em wide -->
    <link rel="stylesheet" href="extra-large.css" media="(min-width: 120em)" />
    <!-- Served to print media, like printers -->
    <link rel="stylesheet" href="print.css" media="print" />
  </head>
  <!-- ... -->
</html>

Why would you want to do that? It can be a nice way to fine-tune the performance of your site by splitting styles up in a way that they’re downloaded and served by the devices that need them.

But just to be clear, this doesn’t always prevent the stylesheets that don’t match those media queries from downloading, it just assigns them a low loading priority level.

So, if a small screen device like a phone visits the site, it will only download the stylesheets in the media queries that match its viewport size. But if a larger desktop screen comes along, it will download the entire bunch because it matches all of those queries (well, minus the print query in this specific example).

That’s just the <link> element. As our guide to responsive images explains, we can use media queries on <source> element, which informs the <picture> element what version of an image the browser should use from a set of image options.

<picture>
  <!-- Use this image if the screen is at least 800px wide -->
  <source srcset="cat-landscape.png" media="(min-width: 800px)">
  <!-- Use this image if the screen is at least 600px wide -->
  <source srcset="cat-cropped.png" media="(min-width: 600px)">

  <!-- Use this image if nothing matches -->
  <img src="cat.png" alt="A calico cat with dark aviator sunglasses.">
</picture>

Again, this can be a nice performance win because we can serve smaller images to smaller devices — which presumably (but not always) will be low powered devices that might be limited to a data plan.

And let’s not forget that we can use media queries directly on the <style> element as well:

<style>
  p {
    background-color: blue;
    color: white;
  }
</style>

<style media="all and (max-width: 500px)">
  p {
    background-color: yellow;
    color: blue;
  }
</style>
CSS

Again, CSS is the most common place to spot a media query in the wild. They go right in the stylesheet in an @media rule that wraps elements with conditions for when and where to apply a set of styles when a browser matches those conditions.

/* Viewports between 320px and 480px wide */
@media only screen and (min-device-width: 320px) and (max-device-width: 480px) {
  .card {
    background: #bada55;
  }
}

It’s also possible to scope imported style sheet but as a general rule avoid using @import since it performs poorly.

/* Avoid using @import if possible! */

/* Base styles for all screens */
@import url("style.css") screen;
/* Styles for screens in a portrait (narrow) orientation */
@import url('landscape.css') screen and (orientation: portrait);
/* Print styles */
@import url("print.css") print;
JavaScript

We can use media queries in JavaScript, too! And guess, what? They’re work a lot like they do in CSS. The difference? We start by using the window.matchMedia() method to define the conditions first.

So, say we want to log a message to the console when the browser is at least 768px wide. We can create a constant that calls matchMedia() and defines that screen width:

// Create a media condition that targets viewports at least 768px wide
const mediaQuery = window.matchMedia( '( min-width: 768px )' )

Then we can fire log to the console when that condition is matched:

// Create a media condition that targets viewports at least 768px wide
const mediaQuery = window.matchMedia( '( min-width: 768px )' )


// Note the `matches` property
if ( mediaQuery.matches ) {
  console.log('Media Query Matched!')
}

Unfortunately, this only fires once so if the alert is dismissed, it won’t fire again if we change the screen width and try again without refreshing. That’s why it’s a good idea to use a listener that checks for updates.

// Create a condition that targets viewports at least 768px wide
const mediaQuery = window.matchMedia('(min-width: 768px)')


function handleTabletChange(e) {
  // Check if the media query is true
  if (e.matches) {
    // Then log the following message to the console
    console.log('Media Query Matched!')
  }
}


// Register event listener
mediaQuery.addListener(handleTabletChange)

// Initial check
handleTabletChange(mediaQuery)

Check out Marko Ilic’s full post on “Working with JavaScript Media Queries” for a deeper dive on this, including a comparison of using media queries with an older JavaScript approach that binds a resize event listener that checks window.innerWidth or window.innerHeight to fire changes.


Anatomy of a Media Query

Now that we’ve seen several examples of where media queries can be used, let’s pick them apart and see what they’re actually doing.

Syntax for CSS media queries.
@media
@media [media-type] ([media-feature]) {
  /* Styles! */
}

The first ingredient in a media query recipe is the @media rule itself, which is one of many CSS at-rules. Why does @media get all the attention? Because it’s geared to the type of media that a site is viewed with, what features that media type supports, and operators that can be combined to mix and match simple and complex conditions alike.

Media types
@media screen {
  /* Styles! */
}

What type of media are we trying to target? In many (if not most) cases, you’ll see a screen value used here, which makes sense since many of the media types we’re trying to match are devices with screens attached to them.

But screens aren’t the only type of media we can target, of course. We have a few, including:

  • all: Matches all devices
  • print: Matches documents that are viewed in a print preview or any media that breaks the content up into pages intended to print.
  • screen: Matches devices with a screen
  • speech: Matches devices that read the content audibly, such as a screenreader. This replaces the now deprecated aural type since Media Queries Level 4.

To preview print styles in a screen all major browsers can emulate the output of a print stylesheet using DevTools. Other media types such as tty, tv,  projection,  handheld, braille, embossed and aural have been deprecated and, while the spec continues to advise browsers to recognize them, they must evaluate to nothing. If you are using one of these consider changing it for a modern approach.

Media features

Once we define the type of media we’re trying to match, we can start defining what features we are trying to match it to. We’ve looked at a lot of examples that match screens to width, where screen is the type and both min-width and max-width are features with specific values.

But there are many, many (many!) more “features” we can match. Media Queries Level 4 groups 18 media features into 5 categories.

Viewport/Page Characteristics

FeatureSummaryValuesAdded
widthDefines the widths of the viewport. This can be a specific number (e.g. 400px) or a range (using min-width and max-width).<length>
heightDefines the height of the viewport. This can be a specific number (e.g. 400px) or a range (using min-height and max-height).<length>
aspect-ratioDefines the width-to-height aspect ratio of the viewport<ratio>
orientationThe way the screen is oriented, such as tall (portrait) or wide (landscape) based on how the device is rotated.portrait

landscape
overflow-blockChecks how the device treats content that overflows the viewport in the block direction, which can be scroll (allows scrolling), optional-paged (allows scrolling and manual page breaks), paged (broken up into pages), and none (not displayed).scroll

optional-paged

paged
Media Queries Level 4
overflow-inlineChecks if content that overflows the viewport along the inline axis be scrolled, which is either none (no scrolling) or scroll (allows scrolling).scroll

none
Media Queries Level 4

Display Quality

FeatureSummaryValuesAdded
resolutionDefines the target pixel density of the device<resolution>

infinite
scanDefines the scanning process of the device, which is the way the device paints an image onto the screen (where interlace draws odd and even lines alternately, and progressive draws them all in sequence).interlace

progressive
gridDetermines if the device uses a grid (1) or bitmap (0) screen0 = Bitmap
1 = Grid
Media Queries Level 5
updateChecks how frequently the device can modify the appearance of content (if it can at all), with values including none, slow and fast.slow

fast

none
Media Queries Level 4
environment-blendingA method for determining the external environment of a device, such as dim or excessively bright places.opaque

additive

subtractive
display-modeTests the display mode of a device, including fullscreen(no browsers chrome), standalone (a standalone application), minimal-ui (a standalone application, but with some navigation), and browser (a more traditional browser window)fullscreen

standalone

minimal-ui

browser
Web App Manifest

Color

FeatureSummaryValuesAdded
colorDefines the color support of a device, expressed numerically as bits. So, a value of 12 would be the equivalent of a device that supports 12-bit color, and a value of zero indicates no color support.<integer>
color-indexDefines the number of values the device supports. This can be a specific number (e.g. 10000) or a range (e.g. min-color-index: 10000, max-color-index: 15000), just like width.<integer>
monochromeThe number of bits per pixel that a device’s monochrome supports, where zero is no monochrome support.<integer>
color-gamutDefines the range of colors supported by the browser and device, which could be srgb, p3 or rec2020srgb

p3

rec2020
Media Queries Level 4
dynamic-rangeThe combination of how much brightness, color depth, and contrast ratio supported by the video plane of the browser and user device.standard

high
inverted-colorsChecks if the browser or operating system is set to invert colors (which can be useful for optimizing accessibility for sight impairments involving color)inverted

none
Media Queries Level 5

Interaction

FeatureSummaryValuesAdded
pointerSort of like any-pointer but checks if the primary input mechanism is a pointer and, if so, how accurate it is (where coarse is less accurate, fine is more accurate, and none is no pointer).coarse

fine

none
Media Queries Level 4
hoverSort of like any-hover but checks if the primary input mechanism (e.g. mouse of touch) allows the user to hover over elementshover

none
Media Queries Level 4
any-pointerChecks if the device uses a pointer, such as a mouse or styles, as well as how accurate it is (where coarse is less accurate and fine is more accurate)coarse

fine

none
Media Queries Level 4
any-hoverChecks if the device is capable of hovering elements, like with a mouse or stylus. In some rare cases, touch devices are capable of hovers.hover

none
Media Queries Level 4

Video Prefixed

The spec references user agents, including TVs, that render video and graphics in two separate planes that each have their own characteristics. The following features describe those planes.

FeatureSummaryValuesAdded
video-color-gamutDescribes the approximate range of colors supported by the video plane of the browser and user devicesrgb

p3

rec2020
Media Queries Level 5
video-dynamic-rangeThe combination of how much brightness, color depth, and contrast ratio supported by the video plane of the browser and user device.standard

high
Media Queries Level 5
video-width¹The width of the video plane area of the targeted display<length>Media Queries Level 5
video-height¹The height of the video plane area of the targeted display<length>Media Queries Level 5
video-resolution¹The resolution of the video plane area of the targeted display<resolution>

inifinite
Media Queries Level 5
¹ Under discussion (Issue #5044)

Scripting

FeatureSummaryValuesAdded
scriptingChecks whether the device allows scripting (i.e. JavaScript) where enabled allows scripting, iniital-only enabled

initial-only

Media Queries Level 5

User Preference

FeatureSummaryValuesAdded
prefers-reduced-motionDetects if the user’s system settings are set to reduce motion on the page, which is a great accessibility check.no-preference

reduce
Media Queries Level 5
prefers-reduced-transparencyDetects if the user’s system settings prevent transparent across elements.no-preference

reduce
Media Queries Level 5
prefers-contrastDetects if the user’s system settings are set to either increase or decrease the amount of contrast between colors.no-preference

high

low

forced
Media Queries Level 5
prefers-color-schemeDetects if the user prefers a light or dark color scheme, which is a rapidly growing way to go about creating “dark mode” interfaces.light

dark
Media Queries Level 5
forced-colorsTests whether the browser restricts the colors available to use (which is none or active)active

none
Media Queries Level 5
prefers-reduced-dataDetects if the user prefers to use less data for the page to be rendered.no-preference

reduce
Media Queries Level 5

Deprecated

NameSummaryRemoved
device-aspect-ratioThe width-to-height aspect ratio of the output deviceMedia Queries Level 4
device-heightThe height of the device’s surface that displays rendered elementsMedia Queries Level 4
device-widthThe width of the device’s surface that displays rendered elementsMedia Queries Level 4
Operators

Media queries support logical operators like many programming languages so that we can match media types based on certain conditions. The @media rule is itself a logical operator that is basically stating that “if” the following types and features are matches, then do some stuff.

and

But we can use the and operator if we want to target screens within a range of widths:

/* Matches screen between 320px AND 768px */
@media screen (min-width: 320px) and (max-width: 768px) {
  .element {
    /* Styles! */
  }
}

or (or comma-separated)

We can also comma-separate features as a way of using an or operator to match different ones:

/* 
  Matches screens where either the user prefers dark mode or the screen is at least 1200px wide */
@media screen (prefers-color-scheme: dark), (min-width 1200px) {
  .element {
    /* Styles! */
  }
}

not

Perhaps we want to target devices by what they do not support or match. This declaration removes the body’s background color when the device is a printer and can only show one color.

@media print and ( not(color) ) {
  body {
    background-color: none;
  }
}

Want to go deeper? Check out “CSS Media Queries: Quick Reference & Guide” from the DigitalOcean community for more examples that follow the syntax for media quieries.


Do you really need CSS media queries?

Media queries are a powerful tool in your CSS toolbox with exciting hidden gems. But if you accomodate your design to every possible situation you’ll end up with a codebase that’s too complex to maintain and, as we all know, CSS is like a bear cub: cute and inoffensive but when it grows it will eat you alive.

That’s why I recommend following Ranald Mace’s concept of Universal Design which is “the design of products to be usable by all people, to the greatest extent possible, without the need for adaptation or specialized design.” 

In “Accessibility for Everyone” Laura Kalbag explains that the difference between accessible and universal design is subtle but important. An accessible designer would create a large door for people on a wheel chair to enter, while a universal designer would produce an entry that anyone would fit disregarding of their abilities.

I know that talking about universal design on the web is hard and almost sound utopian, but think about it, there are around 150 different browsers, around 50 different combinations of user preferences, and as we mentioned before more than 24000 different and unique Android devices alone.

This means that there are at least 18 million possible cases in which your content might be displayed. In the words of the fantastic Miriam Suzanne, “CSS out here trying to do graphic design of unknown content on an infinite and unknown canvas, across operating systems, interfaces, & languages. There’s no possible way for any of us to know what we’re doing.”

That’s why assuming is really dangerous, so when you design, develop and think about your products leave assumptions behind and use media queries to make sure that your content is displayed correctly in any contact and before any user.


Matching value ranges

Many of the media features outlined in the previous section — including widthheight, color and color-index — can be prefixed with min- or max- to express minimum or maximum constraints. We’ve already seen these in use throughout many of the examples, but the point is that we can create a range of value to match instead of having to declare specific values.

In the following snippet, we’re painting the body’s background purple when the viewport width is wider than 30em and narrower than 80em. If the viewport width does not match that range of values, then it will fallback to white.

body {
  background-color: #fff;
}

@media (min-width: 30em) and (max-width: 80em) {
  body {
    background-color: purple;
  }
}

Media Queries Level 4 specifies a new and simpler syntax using less then (<), greater than (>) and equals (=) operators. So, that last example can be converted to the new syntax, like so:

@media (30em <= width <= 80em) {
  /* ... */
}

Nesting and complex decision making

CSS allows you to nest at-rules or group statements using parentheses, making it possible to go as deep as we want to evaluate complex operations.

@media (min-width: 20em), not all and (min-height: 40em) {  
  @media not all and (pointer: none) { ... }
  @media screen and ( (min-width: 50em) and (orientation: landscape) ), print and ( not (color) ) { ... }
}

Be careful! even thought it’s possible to create powerful and complex expressions, you might end up with a very opinionated, hard to maintain query. As Brad Frost puts it: “The more complex our interfaces are, the more we have to think to maintain them properly.”


Accessibility

Many of the features added in Media Queries Level 4 are centered around accessibility.

prefers-reduced-motion

prefers-reduced-motion detects if the user has the reduced motion preference activated to minimize the amount of movements and animations. It takes two values:

  • no-preference: Indicates that the user has made no preference known to the system.
  • reduce: Indicates that user has notified the system that they prefer an interface that minimizes the amount of movement or animation, preferably to the point where all non-essential movement is removed.

This preference is generally used by people who suffer from vestibular disorder or vertigo, where different movements result in loss of balance, migraine, nausea or hearing loss. If you ever tried to spin quickly and got dizzy, you know what it feels like.

In a fantastic article by Eric Bailey, he suggests stopping all animations with this code:

@media screen and (prefers-reduced-motion: reduce) {  
  * {
    /* Very short durations means JavaScript that relies on events still works */
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
  }
}

Popular frameworks like Bootstrap have this feature on by default. In my opinion there is no excuse not to use prefers-reduced-motion — just use it. 

prefers-contrast

The prefers-contrast feature informs whether the user has chosen to increase or reduce contrast in their system preferences or the browser settings. It takes three values:

  • no-preference: When a user has made no preference known to the system. If you use it as a boolean it’ll evaluate false.
  • high: When a user has selected the option to display a higher level of contrast.
  • low: When a user has selected the option to display a lower level of contrast.

At the moment of writing this feature is not supported by any browser. Microsoft has done a non-standard earlier implementation with the -ms-high-contrast feature that works only on Microsoft Edge v18 or earlier (but not Chromium-based versions).

.button {
  background-color: #0958d8;
  color: #fff;
}

@media (prefers-contrast: high) {
  .button {
    background-color: #0a0db7;
  }
}

This example is increasing the contrast of a the class button from AA to AAA when the user has high contrast on.

inverted-colors

The inverted-colors feature informs whether the user has chosen to invert the colors on their system preferences or the browser settings. Sometimes this option is used as an alternative to high contrast. It takes three values:

  • none: When colors are displayed normally
  • inverted: When a user has selected the option to invert colors
MacOS accessibility preferences.

The problem with inverted colors is that it’ll also invert the colors of images and videos, making them look like x-ray images. By using a CSS invert filter you can select all images and videos and invert them back.

@media (inverted-colors) {
  img, video { 
    filter: invert(100%);
  }
}

At the time of writing this feature is only supported by Safari.

prefers-color-scheme

Having a “dark mode” color scheme is something we’re seeing a lot more of these days, and thanks to the prefers-color-scheme feature, we can tap into a user’s system or browser preferences to determine whether we serve a “dark” or a “light” theme based on the ir preferences.

It takes two values:

  • light: When a user has selected that they prefer a light theme or has no active preferences
  • dark: When a user has selected a dark display in their settings
body {
  --bg-color: white; 
  --text-color: black;

  background-color: var(--bg-color);
  color: var(--text-color);
}

@media screen and (prefers-color-scheme: dark) {
  body {
    --bg-color: black;
    --text-color: white;
  }
}

As Adhuham explains in the complete guide to Dark Mode there is way more to it than just changing the color of the background. Before you jump into doing dark mode remember that if you don’t have a very smart implementation strategy you might end up with a code base that’s really hard to maintain. CSS variables can do wonders for it but that’s a subject for another article.


What lies ahead?

Media Queries Level 5 is currently in Working Draft status, which means a lot can change between now and when it becomes a recommendation. But it includes interesting features that are worth mentioning because they open up new ways to target screens and adapt designs to very specific conditions.

User preference media features

Hey, we just covered these in the last section! Oh well. These features are exciting because they’re informed by a user’s actual settings, whether they are from the user agent or even at the operating system level.

Detecting a forced color palette

This is neat. Some browsers will limit the number of available colors that can be used to render styles. This is called “forced colors mode” and, if enabled in the browser settings, the user can choose a limited set of colors to use on a page. As a result, the user is able to define color combinations and contrasts that make content more comfortable to read.

The forced-colors feature allows us to detect if a forced color palette is in use with the active value. If matched, the browser must provide the required color palette through the CSS system colors. The browser is also given the leeway to determine if the background color of the page is light or dark and, if appropriate, trigger the appropriate prefers-color-scheme value so we can adjust the page.

Detecting the maximum brightness, color depth, and contrast ratio

Some devices (and browsers) are capable of super bright displays, rendering a wide range of colors, and high contrast ratios between colors. We can detect those devices using the dynamic-range feature, where the high keyword matches these devices and standard matches everything else.

We’re likely to see changes to this because, as of right now, there’s still uncertainty about what measurements constitute “high” levels of brightness and contrast. The browser may get to make that determination.

Video prefixed features

The spec talks about some screens, like TVs, that are capable of displaying video and graphics on separate “planes” which might be a way of distinguishing the video frame from other elements on the screen. As such, Media Queries Level 5 is proposing a new set of media features aimed at detecting video characteristics, including color gamut and dynamic range.

There are also proposals to detect video height, width and resolution, but the jury’s still out on whether those are the right ways to address video.


Browser support

Browsers keep evolving and since by the time you are reading this post chances are that browser support for this feature might change, please check MDN updated browser compatibility table.


A note on container queries

Wouldn’t be cool if components could adapt themselves on their own size instead of the browser’s? That’s what the concept of CSS Container Queries is all about. We currently only have the browser screen to make those changes via media queries.

That’s unfortunate, as the viewport isn’t always a direct relationship to how big the element itself is. Imagine a widget that renders in many different contexts on a site: sometimes in a sidebar, sometimes in a full-width footer, sometimes in a grid with unknown columns.

This is the problem that container queries try to solve. Ideally we could adapt styles of an element according to the size of itself instead of of the size of the viewport. Chrome 105 released support for CSS Container Queries. Same deal with Safari 16.1. Firefox is all we’re really waiting at the time of writing to get broad support.

This browser support data is from Caniuse, which has more detail. A number indicates that browser supports the feature at that version and up.

Desktop

ChromeFirefoxIEEdgeSafari
106110No10616.0

Mobile / Tablet

Android ChromeAndroid FirefoxAndroidiOS Safari
12712712716.0

Examples

Let’s look at a bunch of media query examples. There are so many combinations of media types, features, and operators that the number of possibilities we could show would be exhaustive. Instead, we’ll highlight a handful based on specific media features.

Adjust layout at different viewport widths

More info

This is the probably the most widely used media feature. It informs the width of the browser’s viewport including the scrollbar. It unlocked the CSS implementation of what Ethan Marcotte famously coined responsive design: a process by which a design responds to the size of the viewport using a combination of a fluid grid, flexible images, and responsive typesetting.

Later, Luke Wroblewski evolved the concept of responsive design by introducing the term mobile-first, encouraging designers and developers to start with the small-screen experience first then progressively enhance the experience as the screen width and device capabilities expand.

A mobile-first can usually be spotted by it’s use of min-width instead of max-width. If we start with min-width, we’re essentially saying, “hey, browser, start here and work up.” On the flip side, max-width is sort of like prioritizing larger screens.

One approach for defining breakpoints by width is using the dimensions of standard devices, like the exact pixel width of an iPhone. But there are many, many (many), many different phones, tables, laptops, and desktops. Looking at Android alone, there are more than 24,000 variations of viewport sizes, resolutions, operating systems, and browsers, as of August 2015.

So, while targeting the precise width of a specific device might be helpful for troubleshooting or one-off fixes, it’s probably not the most robust solution for maintaining a responsive architecture. This isn’t a new idea by any stretch. Brad Frost was already preaching the virtues of letting content — not devices — determine breakpoints in his post “7 habits of highly effective media queries” published back in 2013.

And even though media queries are still a valid tool to create responsive interfaces, there are many situations where it’s possible to avoid using width at all. Modern CSS allow us to create flexible layouts with CSS grid and flex that adapts our content to the viewport size without a need to add breakpoints. For example, here is a grid layout that adapts how many columns it will have without any media queries at all.

.container {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
}

There are many articles about thinking beyond width, I wrote about it a few years ago and I recommend checking out Una Kravet’s Ten modern layouts in one line of CSS.


Dark mode

More info

This example is pulled straight from our Guide to Dark Mode on the Web. The idea is that we can detect whether a user’s system settings are configured to light or dark mode using the prefers-color-scheme feature and then define an alternate set of colors for the rendered UI.

Combining this technique with CSS custom properties makes things even easier because they act like variables that we only need to define once, then use throughout the code. Need to swap colors? Change the custom property value and it updates everywhere. That’s exactly what prefers-color-scheme does. We define a set of colors as custom properties, then redefine them inside a media query using the prefer-color-scheme feature to change colors based on the user’s settings.


More info

This gallery is responsive without using the width feature.

It detects the orientation of the viewport. If it’s a portrait viewport, the sidebar will became a header; if it’s landscape it stays off to the side.

Using the pointer media feature, it decides if the main input device is coarse — like a finger — or fine — like a mouse cursor — to set the size of the clickable areas of the checkboxes.

Then, by using the hover media feature, the example checks if the device is capable of hovering (like a mouse cursor) and display a checkbox in each card.

The animations are removed when prefers-reduced-motion is set to reduce.

And did you notice something? We’re actually not using media queries for the actual layout and sizing of the cards! That’s handled using the minmax() function on the .container element to show how responsive design doesn’t always mean using media queries.

In short, this is a fully responsive app without ever measuring width or making assumptions.

Target an iPhone in landscape mode

/* iPhone X Landscape */
@media only screen 
  and (min-device-width: 375px) 
  and (max-device-width: 812px) 
  and (-webkit-min-device-pixel-ratio: 3)
  and (orientation: landscape) { 
  /* Styles! */
}
More info

The orientation media feature tests whether a device is rotated the wide way (landscape) or the tall way (portrait).

While media queries are unable to know exactly which device is being used, we can use the exact dimensions of a specific device. The snippet above is targets the iPhone X.

Apply a sticky header for large viewports

More info

In the example above, we’re using height to detached fixed elements and avoid taking up too much screen real estate when the screen is too short. A horizontal navigation bar is in a fixed position when the screen is tall, but detaches itself on shorter screens.

Like the width feature, height detects the height of the viewport, including the scrollbar. Many of us browse the web on small devices with narrow viewports, making designing for different heights more relevant than ever. Anthony Colangelo describes how Apple uses the height media feature in a meaningful way to deal with the size of the hero image as the viewport’s height changes.


Responsive (fluid) typography

More info

A font can look either too big or too small, depending on the size of the screen that’s showing it. If we’re working on a small screen, then chances are that we’ll want to use smaller type than what we’d use on a much larger screen.

The idea here is that we’re using the browser’s width to scale the font size. We set a default font size on the <html> that acts as the “small” font size, then set another font size using a media query that acts as the “large” font size. In the middle? We set the font size again, but inside another media query that calculates a size based on the browser width.

The beauty of this is that it allows the font size to adjust based on the browser width, but never go above or below certain sizes. However, there is a much simpler way to go about this that requires no media queries at all, thanks to newer CSS features, like min(), max(), and clamp().


Provide bigger touch targets when devices have a course pointer

More info

Have you ever visited a site that had super tiny buttons? Some of us have fat fingers making it tough to tap an object accurately without inadvertently tapping something else instead.

Sure, we can rely on the width feature to tell if we’re dealing with a small screen, but we can also detect if the device is capable of hovering over elements. If it isn’t then it’s probably a touch device, or perhaps a device that supports both, like the Microsoft Surface.

The demo above uses checkboxes as an example. Checkboxes can be a pain to tap on when viewing them on a small screen, so we’re increasing the size and not requiring a hover if the device is incapable of hover events.

Again, this approach isn’t always accurate. Check out Patrick Lauke’s thorough article that details potential issues working with hover, pointer, any-hover and any-pointer.

Specifications


Special thanks to Sarah Rambacher who helped to review this guide.


A Complete Guide to CSS Media Queries originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
https://css-tricks.com/a-complete-guide-to-css-media-queries/feed/ 6 319964
Accessible Font Sizing, Explained https://css-tricks.com/accessible-font-sizing-explained/ https://css-tricks.com/accessible-font-sizing-explained/#comments Thu, 23 Apr 2020 14:56:32 +0000 https://css-tricks.com/?p=306222 The Web Content Accessibility Guidelines (WCAG), an organization that defines standards for web content accessibility, does not specify a minimum font size for the web.

But we know there’s such a thing as text that is too small to be …


Accessible Font Sizing, Explained originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
The Web Content Accessibility Guidelines (WCAG), an organization that defines standards for web content accessibility, does not specify a minimum font size for the web.

But we know there’s such a thing as text that is too small to be legible, just as text that can be too large to consume. So, how can we make sure our font sizes are accessible? What sort of best practices can we rely on to make for an accessible reading experience?

The answer: it’s not up to us. It Depends™. We’ll get into some specific a bit later but, for now, let’s explore the WCAG requirements for fonts.

Sizing, contrast, and 300 alphabets

First, resizing text.  We want to provide users with low vision a way to choose how fonts are displayed. Not in a crazy way. More like the ability to increase the size by 200% while maintaining readability and avoiding content collisions and overlaps.

Secondly, there’s contrast. This is why I said “it depends” on what makes an accessible font size. Text has to follow a contrast ratio of at least 4.5:1, with the exception of a large-scale text that should have a contrast ratio of at least 3:1. You can use tools like WebAIM’s Contrast Checker to ensure your text meets the guidelines. Stacy Arrelano’s deep dive on color contrast provides an excellent explanation of how contrast ratios are calculated.

A contrast ratio of 2.39 to 1 would not pass any of the WCAG checks, a ratio of 4.65 to 1 would pass only the AA check for normal text sizes and a 10.09 to 1 contrast ratio would pass both AA and AAA WCAG checks.
Example of three color contrast measurements and their WCAG test results according to WebAIM’s contrast checker.

There are around 300 alphabets in the world. Some characters are simple and readable in smaller sizes, others are incredibly complex and would lose vital details at the same size. That’s why specs cannot define a font size that meets the specification for contrast ratios.

And when we talk about “text” and “large text” sizes, we’re referring to what the spec calls “the minimum large print size used for those languages and the next larger standard large print size.” To meet AAA criteria using Roman text, for example, “large” is 18 points. Since we live in a world with different screen densities, specs measure sizes in points, not pixels, and in some displays, 18pt is equal to 24px. For other fonts, like CJK (Chinese, Japanese, Korean) or Arabic languages, the actual size in pixel would be different. Here’s the word “Hello” compared next to three other languages:

Hello สวัสดี مرحبا 你好

In short, WCAG specifies contrast instead of size.

The WCAG recommended font size for large text has greater contrast than something half the size. Notice how a larger font size lets in more of the background that sits behind the text.

Here is the good news: a browser’s default styles are accessible and we can leverage them to build an accessible font size strategy. Let’s see how.

Think about proportions, not size

The browser first loads its default styles (also known as the “User Agent stylesheet”), then those cascade to the author’s styles (the ones we define), and they both cascade and get overwritten by the user’s styles.

As Adrian Sandu mentions in his article about rem CSS units:

[…] there is an empirical study run by the people behind the Internet Archive showing that there is a significant amount of users who change their default font size in the browser settings.

We don’t fully control the font-family property, either. The content might be translated, the custom font family might fail to load, or it might even be changed. For example, OpenDyslexic is a typeface created to increase readability for readers with dyslexia. In some situations, we may even explicitly allow switching between a limited set of fonts

Therefore, when defining fonts, we have to avoid hindering the ability of a user or a device to change our styles and let go of assumptions: we just don’t know where our content is going to land and we can’t be sure about the exact size, language, or font that’s used to display content.

But there is one thing that we can control: proportions.

By using CSS relative units, we can set our content to be proportional to whatever the environment tells it to be. WCAG recommends using em units to define font size. There are several publications discussing the benefits of using ems and rems and it’s beyond the scope of this article. What I’d say here is to use rems and ems for everything, even for other properties besides font-size (with the exception of borders, where I use pixels).

Avoid setting a base font-size

My recommendation is to avoid setting font-size on the :root, <html> or <body> elements in favor of letting the browser’s default size serve as a baseline from where we can cascade our own styles. Since this default is accessible, the content will also be accessible. The WACAG 2.2 working draft states that:

When using text without specifying the font size, the smallest font size used on major browsers for unspecified text would be a reasonable size to assume for the font.

Of course, there is an exception to the rule. When using an intricate, thin, or super short x-height font, for example, you might consider bumping up the font size base to get the correct contrast. Remember that the spec defines contrast, not size:

Fonts with extraordinarily thin strokes or unusual features and characteristics that reduce the familiarity of their letter forms are harder to read, especially at lower contrast levels.

In the same manner, a user might change the base font size to fit their needs. A person with low vision would want to choose a larger size, while someone with an excellent vision can go smaller to gain real estate on their screens.

It’s all about proportions: we define how much larger or smaller parts of the content should be by leveraging the default base to set the main text size.

:root {
  /* Do not set a font-size on a :root, body nor html level */
  /* Let your main text size be decided by the browser or the user settings */ 
}
.small {
  font-size: .8rem;
}
.large {
  font-size: 2rem;
}

What about headings?

Since headings create a document outline that helps screenreaders navigate a document, we aren’t defining type selectors for heading sizes. Heading order is a WCAG criteria: the heading elements should be organized in descending order without skipping a level, meaning that an h4 should come right after an h3.

Sometimes resetting the font sizing of all headings to 1rem is a good strategy to make the separation of the visual treatment from the meaning mandatory.

How can we work with pixels?

Both rem or em sizing is relative to something else. For example, rem  calculates size relative to the <html>  element, where em is calculated by the sizing of its own element. It can be confusing, particularly since many of us came up working exclusively in pixels.

So, how can we still think in pixels but implement relative units?

More often than not, a typographical hierarchy is designed in pixels. Since we know about user agent stylesheets and that all major browsers have a default font size of 16px, we can set that size for the main text and calculate the rest proportionately with rem units.

Browser NameBase Font Size
Chrome v80.016px
FireFox v74.016px
Safari v13.0.416px
Edge v80.0 (Chromium based)16px
Android (Samsung, Chrome, Firefox)16px
Safari iOS16px
Kindle Touch26px (renders as 16px since it’s a high density screen)

Now let’s explore three methods for using relative sizing in CSS by converting those pixels to rem units.

Method 1: The 62.5% rule

In order to seamlessly convert pixels to rem, we can set the root sizing to 62.5%. That means 1rem equals 10px:

:root {
  font-size: 62.5%; /* (62.5/100) * 16px = 10px */
  --font-size--small: 1.4rem; /* 14px */
  --font-size--default: 1.6rem; /* 16px */
  --font-size--large: 2.4rem; /* 24px */
}


.font-size--small {
  font-size: var(--font-size--small);
}

.font-size--default {
  font-size: var(--font-size--default);
}

.font-size--large {
  font-size: var(--font-size--large);
}

Method 2: Using the calc() function

We can also calculate sizes with CSS calc() by dividing the pixel value by the font base we assume most browsers have:

:root {
  --font-size--small: calc((14/16) * 1rem); /* 14px */
  --font-size--default: calc((16/16) * 1rem); /* 16px */
  --font-size--large: calc((24/16) * 1rem); /* 24px */
}


.font-size--small {
  font-size: var(--font-size--small);
}

.font-size--default {
  font-size: var(--font-size--default);
}

.font-size--large {
  font-size: var(--font-size--large);
}

Method 3: Using a “pixel-to-rem” function

Similar to calc() , we can leverage a preprocessor to create a “pixel-to-rem” function. There are implementations of this in many flavors, including this Sass mixin and styled-components polish.

:root {
  --font-size--small: prem(14); /* 14px */
  --font-size--default: prem(16); /* 16px */
  --font-size--large: prem(24); /* 24px */
}


.font-size--small {
  font-size: var(--font-size--small);
}

.font-size--default {
  font-size: var(--font-size--default);
}

.font-size--large {
  font-size: var(--font-size--large);
}

It’s even possible to create a “pixel-to-rem” function with vanilla CSS:

Embrace a diverse web!

The bottom line is this: we don’t have control over how content is consumed. Users have personal browser settings, the ability to zoom in and out, and various other ways to customize their reading experience. But we do have best CSS best practices we can use to maintain a good user experience alongside those preferences:

  • Work with proportions instead of explicit sizes.
  • Rely on default browser font sizes instead of setting it on the :root, <html> or <body>.
  • Use rem units to help scale content with a user’s personal preferences.
  • Avoid making assumptions and let the environment decide how your content is being consumed.

Special thanks to Franco Correa for all the help writing this post.


Accessible Font Sizing, Explained originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
https://css-tricks.com/accessible-font-sizing-explained/feed/ 13 306222
Theming With Variables: Globals and Locals https://css-tricks.com/theming-with-variables-globals-and-locals/ https://css-tricks.com/theming-with-variables-globals-and-locals/#comments Fri, 16 Mar 2018 14:04:46 +0000 http://css-tricks.com/?p=268217 Setting CSS variables to theme a design system can be tricky: if they are too scoped, the system will lose consistency. If they are too global, you lose granularity.

Maybe we can fix both issues. I’d like to try to boil design system variables down to two types: Global and Component variables. Global variables will give us consistency across components. Component variables will give us granularity and isolation. Let me show you how to do it by taking a fairly simple component as an example.


Theming With Variables: Globals and Locals originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
Cliff Pyles contributed to this post.

Setting CSS variables to theme a design system can be tricky: if they are too scoped, the system will lose consistency. If they are too global, you lose granularity.

Maybe we can fix both issues. I’d like to try to boil design system variables down to two types: Global and Component variables. Global variables will give us consistency across components. Component variables will give us granularity and isolation. Let me show you how to do it by taking a fairly simple component as an example.

Heads up, I’ll be using CSS variables for this article but the concept applies to preprocessor variables as well.

Global-scoped variables

System-wide variables are general concepts defined to keep consistency across your components.

Starting with an .alert component as an example, let’s say we want to keep consistency for all of our spaces on margins and paddings. We can first define global spacers:

:root {
  --spacer-sm: .5rem;
  --spacer-md: 1rem;
  --spacer-lg: 2rem;
}

And then use on our components:

/* Defines the btn component */
.btn {
  padding: var(--spacer-sm) var(--spacer-md);
}

/* Defines the alert component */
.alert {
  padding: var(--spacer-sm) var(--spacer-md);
}

The main benefits of this approach are:

  • It generates a single source of truth for spacers, and a single point for the author using our system to customize it.
  • It achieves consistency since every component follows the same spacing.
  • It produces a common point of reference for designers and developers to work from. As long as the designers follow the same spacing restrictions, the translation to code is seamless.

But it also presents a few problems:

  • The system loses modularity by generating a dependency tree. Since components depend on global variables, they are no longer isolated.
  • It doesn’t allow authors to customize a single component without overwriting the CSS. For example, to change the padding of the alert without generating a system wide shift, they’d have to overwrite the alert component:
.alert {
  padding-left: 1rem;
  padding-right: 1rem;
}

Chris Coyier explains the idea of theming with global variables using custom elements in this article.

Component-scoped variables

As Robin Rendle explain in his article, component variables are scoped to each module. If we generate the alert with these variables, we’d get:

.alert {
  --alert-color: #222;
  
  color: var(--alert-color);
  border-color: var(--alert-color);
}

The main advantages are:

  • It creates a modular system with isolated components.
  • Authors get granular control over components without overwriting them. They’d just redefine the value of the variable.

There is no way to keep consistency across components or to make a system-wide change following this method.

Let’s see how we can get the best of both worlds!

The two-tier theming system

The solution is a two-layer theming system where global variables always inform component variables. Each one of those layers follow a set of very specific rules.

First tier: Global variables

The main reason to have global variables is to maintain consistency, and they adhere to these rules:

  • They are prefixed with the word global and follow the formula --global--concept--modifier--state--PropertyCamelCase
    • a concept is something like a spacer or main-title
    • a state is something like hover, or expanded
    • a modifier is something like sm, or lg
    • and a PropertyCamelCase is something like BackgroundColor or FontSize
  • They are concepts, never tied to an element or component
    • this is wrong: --global-h1-font-size
    • this is right: --global--main-title--FontSize

For example, a global variable setup would look like:

:root {
  /* --global--concept--size */
  --global--spacer--sm: .5rem;
  --global--spacer--md: 1rem;
  --global--spacer--lg: 2rem;

  /* --global--concept--PropertyCamelCase */
  --global--main-title--FontSize: 2rem;
  --global--secondary-title--FontSize: 1.8rem;
  --global--body--FontSize: 1rem;

  /* --global--state--PropertyCamelCase */
  --global--hover--BackgroundColor: #ccc;
}

Second tier: Component variables

The second layer is scoped to theme-able component properties and follow these rules:

  • Assuming we are writing BEM, they follow this formula: --block__element--modifier--state--PropertyCamelCase
    • The block__element--modifier the selector name is something like alert__actions or alert--primary
    • a state is something like hover or active
    • and if you are not writing BEM class names the same principles apply, just replace the block__element--modifier with your classname
  • The value of component scoped variables is always defined by a global variable
  • A component variable always has a default value as a fallback in case the component doesn’t have the dependency on the global variables

For example:

.alert {
  /* Component scoped variables are always defined by global variables */
  --alert--Padding: var(--global--spacer--md);
  --alert--primary--BackgroundColor: var(--global--primary-color);
  --alert__title--FontSize: var(--global--secondary-title--FontSize);

  /* --block--PropertyCamelCase */
  padding: var(--alert--Padding, 1rem); /* Sets the fallback to 1rem. */
}

/* --block--state--PropertyCamelCase */
.alert--primary {
  background-color: var(--alert--primary--BackgroundColor, #ccc);
}

/* --block__element--PropertyCamelCase */
.alert__title {
  font-size: var(--alert__title--FontSize, 1.8rem);
}

You’ll notice that we are defining locally-scoped variables with global variables. This is key for the system to work since it allows authors to theme the system as a whole. For example, if they want to change the primary color across all components they just need to redefine --global--primary-color.

On the other hand, each component variable has a default value so a component can stand on its own, it doesn’t depend on anything and authors can use it in isolation.

This setup allows for consistency across components, it generates a common language between designers and developers since we can set the same global variables in Sketch as bumpers for designers, and it gives granular control to authors.

Why does this system work?

In an ideal world, we as creators of a design system, expect “authors” or users of our system to implement it without modifications, but of course, the world is not ideal, and that never happens.

If we allow authors to easily theme the system without having to overwrite CSS, we’ll not only make their lives easier but also reduce the risk of breaking modules. At the end of the day, a maintainable system is a good system.

The two-tier theming system generates modular and isolated components where authors have the possibility to customize them at a global and at a component level. For example:

:root {
  /* Changes the secondary title size across the system */
  --global--secondary-title--FontSize: 2rem;
}

.alert {
  /* Changes the padding on the alert only */
  --alert--Padding: 3rem;
}

What values should become variables?

CSS variables open windows to the code. The more we allow authors in, the more vulnerable the system is to implementation issues.

To keep consistency, set global variables for everything except layout values; you wouldn’t want authors to break the layout. And as a general rule, I’d recommend allowing access to components for everything you are willing to give support.

For the next version of PatternFly, an open-source design system I work on, we’ll allow customization for almost everything that’s not layout related: colors, spacer, typography treatment, shadows, etc.

Putting everything together

To show this concept in action I’ve created a CodePen project.

Global variables are nestled in _global-variables.scss. They are the base to keep consistency across the system and will allow the author to make global changes.

There are two components: alert and button. They are isolated and modular entities with scoped variables that allow authors to fine-tune components.

Remember that authors will use our system as a dependency in their project. By letting them modify the look and feel of the system through CSS variables, we are creating a solid code base that’s easier to maintain for the creators of the system and better to implement, modify, and upgrade to authors using the system.

For example, if an author wants to:

  • change the primary color to pink across the system;
  • change the danger color to orange just on the buttons;
  • and change the padding left to 2.3rem only on the alert…

…then this is how it’s done:

:root {
  /* Changes the primary color on both the alert and the button */
  --global--primary--Color: hotpink;
}

.button {
  /* Changes the danger color on the button only without affecting the alert */
  --button--danger--BackgroundColor: orange;
  --button--danger--hover--BorderColor: darkgoldenrod;
}

.alert {
  /* Changes the padding left on the alert only without affecting the button */
  --alert--PaddingLeft: 2.3rem;
}

The design system code base is intact and it’s just a better dependency to have.

I am aware that this is just one way to do it and I am sure there are other ways to successfully set up variables on a system. Please let me know what you think in the comments or send me a tweet. I’d love to hear about what you are doing and learn from it.


Theming With Variables: Globals and Locals originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
https://css-tricks.com/theming-with-variables-globals-and-locals/feed/ 13 268217
5 things CSS developers wish they knew before they started https://css-tricks.com/5-things-css-developers-wish-knew-started/ https://css-tricks.com/5-things-css-developers-wish-knew-started/#comments Mon, 25 Sep 2017 12:54:51 +0000 http://css-tricks.com/?p=260250

You can learn anything, but you can't learn everything 🙃

So accept that, and focus on what matters to you

— Una Kravets 👩🏻‍💻 (@Una) September 1, 2017

Una Kravets is absolutely right. In modern CSS development, there are so …


5 things CSS developers wish they knew before they started originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>

You can learn anything, but you can't learn everything 🙃

So accept that, and focus on what matters to you

— Una Kravets 👩🏻‍💻 (@Una) September 1, 2017

Una Kravets is absolutely right. In modern CSS development, there are so many things to learn. For someone starting out today, it’s hard to know where to start.

Here is a list of things I wish I had known if I were to start all over again.

1. Don’t underestimate CSS

It looks easy. After all, it’s just a set of rules that selects an element and modifies it based on a set of properties and values.

CSS is that, but also so much more!

A successful CSS project requires the most impeccable architecture. Poorly written CSS is brittle and quickly becomes difficult to maintain. It’s critical you learn how to organize your code in order to create maintainable structures with a long lifespan.

But even an excellent code base has to deal with the insane amount of devices, screen sizes, capabilities, and user preferences. Not to mention accessibility, internationalization, and browser support!

CSS is like a bear cub: cute and inoffensive but as he grows, he’ll eat you alive.

  • Learn to read code before writing and delivering code.
  • It’s your responsibility to stay up to date with best practice. MDN, W3C, A List Apart, and CSS-Tricks are your source of truth.
  • The web has no shape; each device is different. Embrace diversity and understand the environment we live in.

2. Share and participate

Sharing is so important! How I wish someone had told me that when I started. It took me ten years to understand the value of sharing; when I did, it completely changed how I viewed my work and how I collaborate with others.

You’ll be a better developer if you surround yourself with good developers, so get involved in open source projects. The CSS community is full of kind and generous developers. The sooner the better.

Share everything you learn. The path is as important as the end result; even the tiniest things can make a difference to others.

  • Learn Git. Git is the language of open source and you definitely want to be part of it.
  • Get involved in an open source project.
  • Share! Write a blog, documentation, or tweets; speak at meetups and conferences.
  • Find an accountability partner, someone that will push you to share consistently.

3. Pick the right tools

Your code editor should be an extension of your mind.

It doesn’t matter if you use Atom, VSCode or old school Vim; the better you shape your tool to your thought process, the better developer you’ll become. You’ll not only gain speed but also have an uninterrupted thought line that results in fluid ideas.

The terminal is your friend.

There is a lot more about being a CSS developer than actually writing CSS. Building your code, compiling, linting, formatting, and browser live refresh are only a small part of what you’ll have to deal with on a daily basis.

  • Research which IDE is best for you. There are high performance text editors like Vim or easier to use options like Atom or VSCode.
  • Pick up your way around the terminal and learn CLI as soon as possible. The short book “Working the command line” is a great starting point.

4. Get to know the browser

The browser is not only your canvas, but also a powerful inspector to debug your code, test performance, and learn from others.

Learning how the browser renders your code is an eye-opening experience that will take your coding skills to the next level.

Every browser is different; get to know those differences and embrace them. Love them for what they are. (Yes, even IE.)

  • Spend time looking around the inspector.
  • You’ll not be able to own every single device; get a BrowserStack or CrossBrowserTesting account, it’s worth it.
  • Install every browser you can and learn how each one of them renders your code.

5. Learn to write maintainable CSS

It’ll probably take you years, but if there is just one single skill a CSS developer should have, it is to write maintainable structures.

This means knowing exactly how the cascade, the box model, and specificity works. Master CSS architecture models, learn their pros and cons and how to implement them.

Remember that a modular architecture leads to independent modules, good performance, accessible structures, and responsive components (AKA: CSS happiness).

The future looks bright

Modern CSS is amazing. Its future is even better. I love CSS and enjoy every second I spend coding.

If you need help, you can reach out to me or probably any of the CSS developers mentioned in this article. You might be surprised by how kind and generous the CSS community can be.

What do you think about my advice? What other advice would you give? Let me know what you think in the comments.


5 things CSS developers wish they knew before they started originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
https://css-tricks.com/5-things-css-developers-wish-knew-started/feed/ 31 260250
Mobile, Small, Portrait, Slow, Interlace, Monochrome, Coarse, Non-Hover, First https://css-tricks.com/mobile-small-portrait-slow-interlace-monochrome-coarse-non-hover-first/ https://css-tricks.com/mobile-small-portrait-slow-interlace-monochrome-coarse-non-hover-first/#comments Fri, 05 May 2017 11:59:18 +0000 http://css-tricks.com/?p=254168 A month ago I explored the importance of relying on Interaction Media Features to identify the user’s ability to hover over elements or to detect the accuracy of their pointing device, meaning a fine pointer like a mouse or a …


Mobile, Small, Portrait, Slow, Interlace, Monochrome, Coarse, Non-Hover, First originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
A month ago I explored the importance of relying on Interaction Media Features to identify the user’s ability to hover over elements or to detect the accuracy of their pointing device, meaning a fine pointer like a mouse or a coarse one like a finger.

But it goes beyond the input devices or the ability to hover; the screen refresh rate, the color of the screen, or the orientation. Making assumptions about these factors based on the width of the viewport is not reliable and can lead to a broken interface.

I’ll take you on a journey through the land of Media Query Level 4 and explore the opportunities that the W3C CSS WG has drafted to help us deal with all the device fruit salad madness.

Media queries

Media queries, in a nutshell, inform us about the context in which our content is being displayed, allowing us to scope and optimize our styles. Meaning, we can display the same content in different ways depending on the context.

The Media Queries Level 4 spec answers two questions:

  • What is the device viewport size and resolution?
  • What is the device capable of doing?

We can detect the device type where the document is being displayed using the media type keywords all, print, screen or speech or you can get more granular using Media Features.

Media Features

Each Media Feature tests a single, specific feature of the device. There are five types:

We can use these features on their own or combine them using the keyword and or a comma to mean “or”. It’s also possible to negate them with the keyword not.

For example:

@media screen and ((min-width: 50em) and (orientation: landscape)), print and (not(color)) {
  ...
}

Scopes the styles to landscape orientated screens that are less than or equal to 50em wide, or monochrome print outputs.

The best way to understand something is by actually doing it. Let’s delve into the corner cases of a navigation bar to test these concepts.

The Unnecessarily Complicated Navbar

One of the best pieces of advice that Brad Frost gave us on “7 Habits of Highly Effective Media Queries” is not to go overboard.

The more complex we make our interfaces the more we have to think in order to properly maintain them. Brad Frost

And that’s exactly what I’m about to do. Let’s go overboard!

Be aware that the following demo was designed as an example to understand what each Media Feature does: if you want to use it (and maintain it), do it at your own risk (and let me know!).

With that in mind, let’s start with the less capable smaller experience, also know as “The mobile, small, portrait, slow, interlace, monochrome, coarse, non-hover first” approach.

The HTML structure

To test the media query features, I started with a very simple structure. On one side: a header with an h1 for a brand name and a nav with an unordered list. On the other side: a main area with a placeholder title and text.

See the Pen Part 1: Mobile, coarse, portrait, slow, monochromatic, non-hover first by Andres Galante (@andresgalante) on CodePen.

Default your CSS for less capable devices and smaller viewport

As I mentioned before, we are thinking of the less capable smaller devices first. Even though we are not scoping styles yet, I am considering an experience that is:

  • max-width: 45em small viewport, less than or equal to 45em wide
  • orientation: portrait portrait viewport, height is larger than width
  • update: slow the output device is not able to render or display changes quickly enough for them to be perceived as a smooth animation.
  • monochrome all monochrome devices
  • pointer: coarse the primary input mechanism has limited accuracy, like a finger
  • hover: none indicates that the primary pointing system can’t hover

Let’s take care of positioning. For portrait, small, touchscreen devices, I want to pin the menu at the bottom of the viewport so users have comfortable access to the menu with their thumbs.

nav {
  position: fixed;
  bottom: 0;
  left: 0;
  right: 0;
}

Since we are targeting touchscreen devices, it is important to increase the touch target. On Inclusive Design Patterns, Heydon Pickering mentions that it’s still unclear what the magical size of a touch area is, different vendors recommend different sizes.

Pickering mentions Anthony Thomas’s article about finger-friendly experiences and Patrick H Lauke research for The W3C Mobile Accessibility Taskforce into touch / pointer target size, and the main takeaway is, “…to make each link larger than the diameter of an adult finger pad”.

That’s why I’ve increased the height of the menu items to 4em. Since this is not scoped, it’ll be applied to any viewport size, so both large touchscreen devices like an iPad Pro and tiny smartphones alike will have comfortable touch targets.

li a {
  min-height: 4em;
}

To help readability on monochromatic or slow devices, like a Kindle, I haven’t removed the underlines from links or added animations. I’ll do that later on.

See the Pen Part 2: Mobile, coarse, portrait, slow, monochromatic, non-hover first by Andres Galante (@andresgalante) on CodePen.

Small landscape viewports, vertical large displays, or mouse pointers

For landscape viewports orientation: landscape, large portrait viewports like vertical monitors or tablets min-width: 45em, or small portrait devices with fine pointers like a stylus pointer: fine, users will no longer be using their thumbs on a handheld device; that’s why I unpinned the menu and put it at the top right of the header.

@media (orientation: landscape), (pointer: fine), (min-width: 45em) {
  main {
    padding-bottom: 1em;
    padding-top: 1em;
  }
  h1, nav {
    position: static;
  }
}

Since the menu and the brand name are already flexed and stretched, then they’ll accommodate themselves nicely.

For users that have a fine pointer like a mouse or a stylus, we want to decrease the hit target to gain the real estate on the main area:

@media (pointer: fine) {
  h1, li a {
    min-height: 2.5em;
  }
}

See the Pen Part 3: Mobile, coarse, portrait, slow, monochromatic, non-hover first by Andres Galante (@andresgalante) on CodePen.

Vertical nav for large landscape viewports

Vertical navigations are great for large landscape viewports (orientation: landscape) and (min-width: 45em), like a tablet or a computer display. To do that I’ll flex the container:

@media (orientation: landscape) and (min-width: 45em) {
  .container {
    display: flex;
  }
  ...
}

Notice that hit targets have nothing to do with the size of the viewport or style of navigation. If the user is on a large touchscreen device with a vertical tab, they’ll see larger targets regardless of the size of the width of the screen.

See the Pen Part 4: Mobile, coarse, portrait, slow, monochromatic, non-hover first by Andres Galante (@andresgalante) on CodePen.

Animations, decorations and edge cases

Animations are a great way to enhance interactions and help users achieve their goals quickly and easily. But some devices are incapable of producing smooth animations – like e-readers. That’s why I am limiting animations to devices that are capable of generating a good experience (update: fast), (scan: progressive), (hover: hover).

@media (update: fast), (scan: progressive), (hover: hover) {
  li a {
    transition: all 0.3s ease-in-out;
  }
}

I am also removing the text decoration on color devices:

@media (color) {
  li a { text-decoration: none; }
}

Removing underlines (via text-decoration) is tricky territory. Our accessibility consultant Marcy Sutton put it well:

Some users really benefit from link underlines, especially in body copy. But since these particular links are part of the navigation with a distinct design treatment, the link color just needs adequate contrast from the background color for users with low vision or color blindness.

We made sure the colors had enough color contrast to pass WCAG AAA.

I’m also increasing the border width to 2px to avoid “twitter” (real term!) on interlace devices like plasma TVs:

@media (scan: interlace) {
  li a, li:first-child a {
    border-width: 2px;
  }
}

And here is the final result:

See the Pen Part 5: Mobile, coarse, portrait, slow, monochromatic, non-hover first by Andres Galante (@andresgalante) on CodePen.

Test it out

Testing all this may not be that easy!. This example relies on flexbox, and some browsers have limited support for other modern CSS features. A Kindle, for example, won’t read @media, @support, or flexbox properties.

I’ve added float fallbacks here:

See the Pen Part 6: Mobile, coarse, portrait, slow, monochromatic, non-hover first by Andres Galante (@andresgalante) on CodePen.

You can open the full page example in different devices, landscape, or portrait and test it out!

How soon will we realistically be able to use all these features?

Now! That is, if you are ok offering different experiences on different browsers.

Today, Firefox doesn’t support Interaction Media Queries. A Firefox user with a fine pointer mechanism, like a mouse, will see large hit areas reducing the main area real estate.

Browser support for most of these features is already available and support for Interaction Media Features, support isn’t bad! I am sure that we will see it supported across the board soon.

Remember to test as much as you can and don’t assume that any of this will just work, especially in less capable or older devices.

There is more!

I’ve covered some of the Media Features along the example, but I left others behind. For example the Resolution Media Feature that describes the resolution of the output device.

My goal is to make you think beyond your almighty MacBook or iPhone with a retina display. The web is so much more and it’s everywhere. We have the tools to create the most amazing, flexible, inclusive, and adaptable experiences; let’s use them.


Mobile, Small, Portrait, Slow, Interlace, Monochrome, Coarse, Non-Hover, First originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
https://css-tricks.com/mobile-small-portrait-slow-interlace-monochrome-coarse-non-hover-first/feed/ 7 254168
Touch Devices Should Not Be Judged By Their Size https://css-tricks.com/touch-devices-not-judged-size/ https://css-tricks.com/touch-devices-not-judged-size/#comments Mon, 27 Feb 2017 11:47:13 +0000 http://css-tricks.com/?p=251891 Front-end developers and web designers live in an insane multi-device reality.

A few months ago, the Red Hat UXD team discussed how to design enterprise applications for mobile environments. Sarah and Jenn, my talented colleagues, pointed out that touch …


Touch Devices Should Not Be Judged By Their Size originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
Front-end developers and web designers live in an insane multi-device reality.

A few months ago, the Red Hat UXD team discussed how to design enterprise applications for mobile environments. Sarah and Jenn, my talented colleagues, pointed out that touch devices should not be judged by their size alone.

Assumptions are beguiling. If only we could agree on certain boundaries, then wouldn’t web design be so much easier to control?” – Jeremy Keith, Resilient Web Design

Today, there is a new layer of complexity to the already complicated world of interaction design and front-end development.

The hardware industry has created massive touchscreen TVs, really large tablets (like the iPad Pro), and even huge touch desktop PCs (like the new, jaw-dropping Surface Studio). This means we can no longer assume that a small viewport is a touch screen and a large viewport isn’t. Sometimes large screens are touch, requiring the user to use their finger, and small screens have a stylus.

Responsive viewport media queries are great but they’re not enough.

We can detect a touch screen with JS tools like Modernizr, but CSS has a hidden gem that is smarter and more flexible.

Interaction Media Features

Thanks to the W3C CSS Working Group and the CSS community, we have a cleaner solution.

On the Media Queries Level 4 Working Draft, there is a spec for Interaction Media Features that includes three definitions:

These provide the capability to query a document based on the presence and accuracy of the user’s pointing device and whether it has the ability to hover over elements.

Let’s take a closer look at each one:

Pointing Device Quality: The pointer Feature

The pointer media feature is used to query about the presence and accuracy of a pointing device such as a mouse. If a device has multiple input mechanisms, the pointer media feature must reflect the characteristics of the “primary” input mechanism, as determined by the user agent.” – W3C

The key word here is “accuracy” of the pointing device.

  • A mouse or a drawing stylus is very accurate and defines the value of fine.
  • A finger or a Kinect peripheral isn’t, and takes the value of coarse.

Therefore, we can adapt our UI elements to the user’s pointer capabilities. This is useful for making hit areas larger, if the user’s main input mechanism is a finger.

/* The primary input mechanism of the device includes a pointing device of limited accuracy. */
@media (pointer: coarse) { ... }

/* The primary input mechanism of the device includes an accurate pointing device. */
@media (pointer: fine) { ... }

/* The primary input mechanism of the device does not include a pointing device. */
@media (pointer: none) { ... }

An example use case for this query is to size the click area of a checkbox or radio.

Hover Capability: The hover Feature

The hover media feature is used to query the user’s ability to hover over elements on the page. If a device has multiple input mechanisms, the hover media feature must reflect the characteristics of the “primary” input mechanism, as determined by the user agent.” – W3C

It’s important to notice that it only evaluates the primary input mechanism. If the primary input mechanism is not able to hover, but the secondary input can, then the query will resolve to none:

For example, a touchscreen where a long press is treated as hovering would match hover: none.” – W3C

  • A touch screen device, where the primary pointer system is the finger and can’t hover, will take the value of none.
  • A device where the primary input is a mouse and can easily hover parts of the page takes the value of hover.
/* Primary input mechanism system can 
   hover over elements with ease */
@media (hover: hover) { ... }

/* Primary input mechanism cannot hover 
   at all or cannot conveniently hover 
   (e.g., many mobile devices emulate hovering
   when the user performs an inconvenient long tap), 
   or there is no primary pointing input mechanism */
@media (hover: none) { ... }

A good use of this query is a drop-down menu.

Rare Interaction Capabilities: The any-pointer and any-hover Features

On devices that are both touch and have a mouse or a stylus, like the Microsoft Surface, the hover and pointer media query will evaluate the primary input mechanism only.

As Andrea Giammarc pointed out, his Dell XPS 13″ touch takes the value of fine, even though it does have a touch screen because the primary input mechanism is a mouse.

If we want a device like that to take the value of coarse or hover, we can use the Rare Interaction Capabilities.

The any-pointer and any-hover media features are identical to the pointer and hover media features, but they correspond to the union of capabilities of all the pointing devices available to the user. More than one of their values can match, if different pointing devices have different characteristics. They must only match none if all of the pointing devices would match none for the corresponding query, or there are no pointing devices at all.” – W3C

/* One or more available input mechanism(s) 
   can hover over elements with ease */
@media (any-hover: hover) { ... }

/* One or more available input mechanism(s) can hover, 
   but not easily (e.g., many mobile devices emulate 
   hovering when the user performs a long tap) */
@media (any-hover: on-demand) { ... }

/* One or more available input mechanism(s) cannot 
   hover (or there are no pointing input mechanisms) */
@media (any-hover: none) { ... }


/* At least one input mechanism of the device 
   includes a pointing device of limited accuracy. */
@media (any-pointer: coarse) { ... }

/* At least one input mechanism of the device 
   includes an accurate pointing device. */
@media (any-pointer: fine) { ... }

/* The device does not include any pointing device. */
@media (any-pointer: none) { ... }

Device Examples

Typical examples of devices matching combinations of pointer and hover:

pointer: coarse pointer: fine
hover: none smartphones, touch screens stylus-based screens (Cintiq, Wacom, etc)
hover: hover Nintendo Wii controller, Kinect mouse, touch pad

W3C

Patrick H. Lauke has written a great guide about how each device type evaluates interaction media queries.

This is really cool, right? I hear you shouting: what about browser support?

Browser Support Isn’t Bad at All!

Even though this is a working draft, it has pretty good support.

My simple test proved successful on Chrome, Chrome for Android, Safari, Edge, Opera, Samsung browser, and Android Browser, but it didn’t work on FireFox, Opera Mini or IE.

See the Pen Touch screen test by Andres Galante (@andresgalante) on CodePen.

FireFox and IE represent only a bit more than 2% mobile/tablet browser market share. I couldn’t find information about touch TVs or other touch screen devices that are not mobile or tablets.

I think we are ready to use this feature, and as FireFox adds support for it and IE dies once and for all, we will have full support.

The “Cards Selection” Use Case

A month ago, we worked on implementing a multi-select cards component for the new version of PatternFly, an open source design system to which I contribute. It was a perfect case to use the hover and pointer media query.

To select a card, when the user hovers over it, a checkbox is displayed. If the user is not able to hover over elements, then we show the checkbox at all times.

To improve this interaction, we increased the hit area of the checkbox if the primary input mechanism is coarse.

See the Pen Multi select cards by Andres Galante (@andresgalante) on CodePen.

Firefox and IE will display default checkboxes at all times.

Size Isn’t Everything

Devices should be judged by their capabilities since, in the end, it is those capabilities that define them.

This is an underused feature, and it opens the door to exciting new challenges. I can’t wait to see what we, as a community, can do with it.

References

All the descriptions commented on the code are from Mozilla Developer Network.


Touch Devices Should Not Be Judged By Their Size originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
https://css-tricks.com/touch-devices-not-judged-size/feed/ 6 251891