Gutenberg 9.1 Adds Patterns Category Dropdown and Reverts Block-Based Widgets in the Customizer

Gutenberg 9.1 was released to the public on Wednesday. The team announced over 200 commits from 77 contributors in its release post yesterday. One of the biggest changes to the interface was the addition of a new dropdown selector for block pattern categories. The team also reverted the block-based widgets section in the customizer and added an image size control to the Media & Text block.

One of the main focuses of this release was improving the block-based widgets editor. The feature was taken out of the experimental stage in Gutenberg 8.9 and continues to improve. The widgets screen now uses the same inserter UI as the post-editing screen. However, users can currently only insert regular blocks. Patterns and reusable blocks are still not included.

Theme authors can now control aspects of the block editor via a custom theme.json file. This is part of the ongoing Global Styles project, which will allow theme authors to configure features for their users.

The development team has also added an explicit box-sizing style rule to the Cover and Group blocks. This is to avoid any potential issues with the new padding/spacing options. Theme authors who rely on the block editor styles should test their themes to make sure this change does not break anything.

Better Pattern Organization

Adding a block pattern in the WordPress editor.
New block patterns UI in the inserter.

I have been calling for the return of the tabbed pattern categories since Gutenberg 8.0, which was a regression from previous versions. For 11 versions, users have had to scroll and scroll and scroll through every block pattern just to find the one they wanted. The development team has sought to address this issue by using a category dropdown selector. When selecting a specific category, its patterns will appear.

At first, I was unsure about this method over the old tabbed method. However, after some use, it feels like the right direction.

As more and more theme and plugin authors add block pattern categories to users’ sites, the dropdown is a more sensible route. Even tabs could become unwieldy over time. The dropdown better organizes the list of categories and makes the UI cleaner. More than anything, I am enjoying the experience and look forward to this eventually landing in WordPress 5.6 later this year.

Customizer Widgets Reverted

Widgets panel in the WordPress customizer.
Reverted widgets panel in the customizer.

On the subject of WordPress 5.6, one of its flagship features has been hitting some roadblocks. Block-based widgets are expected to land in core with the December release, but the team just reverted part of the feature. They had to remove the widgets block editor from the customizer they added just two major releases ago.

It was for the best. The customizer’s block-based widgets editor was fundamentally broken. It was not ready for primetime and should have remained in the experimental stage until it was somewhat usable.

“I will approve this since the current state of the customizer in the Gutenberg plugin is broken, and there is no clear path forward about how to fix that,” wrote Andrei Draganescu in the reversion ticket. “With this patch, the normal widgets can still be edited in the customizer and the block ones don’t break it anymore. This is NOT to mean that we won’t proceed with fixing the block editor in the customizer, that is still an ongoing discussion.”

The current state of editing widgets via the customizer is at least workable with this change. If end-users add a block via the admin-side widgets editor, it will merely appear as an uneditable, faux widget named “Block” in the customizer. They will need to edit blocks via the normal widgets screen.

There is no way that WordPress can ship the current solution when 5.6 rolls out. However, we are still two months out. This leaves plenty of time for a fix, but Draganescu’s note that “there is no clear path forward” may make some people a bit uneasy at this stage of development.

Control Image Size for Media & Text

Media & Text block in the WordPress editor.
Image size dropdown selector for the Media & Text block.

One of the bright spots in this update is the addition of an image size control to the Media & Text block. Like the normal Image block, end-users can choose from any registered image size created for their uploaded image.

This is a feature I have been looking forward to in particular. Previously, using the full-sized image often made the page weight a bit heftier than necessary. It is also nice to go along with themes that register sizes for both landscape and portrait orientations, giving users more options.

Parsel: A tiny, permissive CSS selector parser

If you’ve ever thought to yourself, gosh, self, I wish I could have an Abstract Syntax Tree (AST) of this CSS selector, Lea has your back.

If you’ve ever thought that same thing for an entire CSS file, that’s what PostCSS is, which has gone v8. PostCSS doesn’t do anything by itself, remember. It just makes an AST out of CSS and gives it a plugin interface so plugins can be written to transform CSS with it. No shade on PostCSS, but it is funny how saying “We use PostCSS” doesn’t mean anything the way “We use Sass” does.

Direct Link to ArticlePermalink


The post Parsel: A tiny, permissive CSS selector parser appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.

A Complete Guide to CSS Media Queries

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 )' )


// Check if the media query is true
if ( mediaQuery ) {
  // Then log the following message to the console
  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) {
    // 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.

@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;
  }
}

Do you really need media queries?

Media Queries is 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.” 

On 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.


Using min- and max- to match 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. Unfortunately, at the time of writing, it isn’t supported by any major browser.


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

The problem with invested 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: light) {
  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 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 the elusive container queries idea is trying to solve. Ideally we could adapt styles of an element according to the size of itself instead of of the size of the viewport. But container queries don’t exist yet. The WICG is looking for use cases and it’s a highly requested feature. We see occasional movement, but it’s unsure if we’ll ever get it. But when we do, you can bet that it will have an impact on how we approach media queries as well.

In the meantime, you can catch up on the origin story of container queries for more context.


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.


Detecting orientation, hover and motion on a responsive card gallery

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.


The post A Complete Guide to CSS Media Queries appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.

How To Use Face Motion To Interact With Typography

Web designers are always looking for new ways to improve the presentation of a page’s content. Sometimes, this can lead to ingenious solutions or to interact with technologies that are often kept away from the design field. In this article we will bring typography in contact with Artificial Intelligence, using machine learning to detect such things as the proximity of the user’s face in order to improve the legibility of the text.

We will experiment on how to use face recognition with Tensorflow in order to extract some information from the camera, such as the distance between the screen and user’s face or the amount of people reading the page. Then, we will pass those data to CSS in order to adapt typography and to adjust the page layout.

What Is Tensorflow?

Tensorflow is an open-source platform from Google for Machine Learning. Machine Learning is a field of Computer Science that studies algorithms that learn to recognize complex relations and recurring patterns out of images, audio tracks, time series, natural text, and data in general. These algorithms generate mathematical models (also called trained models), which are a sort of schema that can be used to make decisions based on input data. If you’d like to approach the topic, Charlie Gerard wrote about ML for frontend developers here on Smashing Mag.

Tensorflow provides a lot of tools for AI developers, data scientists, mathematicians, but don’t panic if data analysis is not your daily bread! The good news is that you have not to be an expert to use it, as long as you are using pre-built models, just as we are going to.

Tensorflow models are available to be used on the Web with their JavaScript SDK.

Setup

In order to start using face recognition algorithms, we need to follow a few steps:

  • load the Tensorflow SDK.
  • load the Facemesh library which contains the mathematical model.
  • access the user’s camera and stream it to an HTML video element. Facemesh will analyze frames from the video tag to detect the presence of faces.

In this projects we are going to use Tensorflow via CDN, but it is also available on NPM if you prefer the bundler-way:

Tensorflow does not do the trick itself, so we need to add Facemesh, a library that is built on the top of the ML framework and provides an already trained model for face recognition:

<script src="https://cdn.jsdelivr.net/npm/@tensorflow-models/facemesh"></script>

The next step is to setup the Facemesh library in order to load the trained model and to define the function that will evaluate face data from a video stream:

// create and place the video
const video = document.createElement('video');
document.body.appendChild(video);

// setup facemesh
const model = await facemesh.load({
    backend: 'wasm',
    maxFaces: 1,
});

async function detectFaces() {
    const faces = await model.estimateFaces(video);
    console.log(faces);

    // recursively detect faces
    requestAnimationFrame(detectFaces);
}

Now we are ready to ask the user the permission to access to its camera stream using a video tag:

// enable autoplay
video.setAttribute('autoplay', '');
video.setAttribute('muted', '');
video.setAttribute('playsinline', '');
// start face detection when ready
video.addEventListener('canplaythrough', detectFaces);
// stream the camera
video.srcObject = await navigator.mediaDevices.getUserMedia({
    audio: false,
    video: {
        facingMode: 'user',
    },
});
// let’s go!
video.play();

The navigator.mediaDevices.getUserMedia method will prompt the permission and will start to stream the camera into the video element. Once accepted, the camera will start to stream to the video tag, while the browser console will log face information detected by Facemesh.

Please note that camera permissions require a secure https connection or localhost: you can not simply open the index.html file. If you are not sure how to set up a local server checkout http-server for Node or follow this guide for Python or this one for PHP.

Case 1. Adjust Typography Using The Smartphone Camera

We navigate the web everywhere with our smartphone. There was a time, not so long ago, where we used to take crowded trains or buses and we kept the smartphone very close to our eyes because there was no space. In many moments and places of our day, we often change the position and the inclination of the smartphone, even if we are watching the same site. The distance between eyes and smartphone affects our reading capabilities. Evaluating that distance we can adjust microtypography in order to optimize glyphs for a closer or more distant reading.

Face detection means, of course, eyes position detection too. We can use data provided by Facemesh to calculate the size of our face in relation with the whole picture captured by the camera. We can assume that the larger our face, the closer we are to the screen. We can set up a scale from 0 (one arm distant apart — the face approximately occupies one half of the camera) to 1 (glued to the screen) and detect the current value with a division of segments:

async function detectFaces() {
    const faces = await model.estimateFaces(video);
    if (faces.length === 0) {
        // is somebody out there?
        return requestAnimationFrame(detectFaces);
    }

    const [face] = faces;

    // extract face surface corners
    let { bottomRight, topLeft} = face.boundingBox;

    // calculate face surface size
    let width = bottomRight[0] - topLeft[0];
    let height = bottomRight[1] - topLeft[1];
    let videoWidth = video.videoWidth;
    let videoHeight = video.videoHeight;
    let adjustWidth = videoWidth / 2;
    let adjustHeight = videoHeight / 2;

    // detect the ratio between face and full camera picture
    let widthRatio = Math.max(Math.min((width - adjustWidth) / (videoWidth - adjustWidth), 1), 0);
    let heightRatio = Math.max(Math.min((height - adjustHeight) / (videoHeight - adjustHeight), 1), 0);
    let ratio = Math.max(widthRatio, heightRatio);


    // recursively detect faces
    requestAnimationFrame(detectFaces);
}

Now that we have calculated the ratio, it’s time to make some magic happens, passing the value to the stylesheet:

document.documentElement.style.setProperty('--user-distance', ratio);

With this value and a bit of calc we could easily apply slight changes to font weight, size, and maybe style too, but we can do something even better. Using a variable font, a font that has parameterized shapes and spaces of the glyphs, we can adjust the perception of every glyph by updating its optical size variation.

Since every variable font uses its own scale for optical size values, we need to relate our ratio value to that scale. Furthermore, we may want to move just between a subset of available optical size, in order to provide just little enhancements.

.main-text {
    --min-opsz: 10;
    --max-opsz: 15;
    --opsz: calc(var(--min-opsz) + (var(--user-distance) * (var(--max-opsz) - var(--min-opsz))));

    ...
    font-family: 'Amstelvar', serif;
    font-variation-settings: 'opsz' var(--opsz);
}

You can see it live here. Please note that this example is just a demonstration of how the technology works. Typographical changes should be almost imperceptible to the user’s eyes in order to really provide a better reader experience. Here we leveraged glyphs shapes, but using colors to increase or decrease contrasts is just another good solution to try. Another experiment was to detect the angle of the face in order to calculate the perspective of the reading, modifying ascenders, descenders ad the height of letters:

When a large number of people are watching, we need to privilege the long-form reading context, giving more space to the main column, increasing its font size, and removing disturbing elements. To do that, we increase the number of spanned columns, moving the aside below the main text.

:root {
    --watching: 10;
}

section {
    /** The maximum number of people watching for the default layout */
    --switch: 10;
    /** The default number of columns for the text */
    --text: 8;
    /** The default number of columns for the aside */
    --aside: 4;

    grid-template-columns: repeat(calc(var(--text) + var(--aside)), 1fr);
}

section article {
    /**
     * Kinda magic calculation.
     * When the number of people watching is lower than --switch, it returns -2
     * When the number of people watching is greater than --switch, it returns -1
     * We are going to use this number for negative span calculation
     */
    --layout: calc(min(2, (max(var(--switch), var(--watching)) - var(--switch) + 1)) - 3);
    /**
     * Calculate the position of the end column.
     * When --layout is -1, the calculation just returns -1
     * When --layout is -2, the calculation is lower than -1
     */
    --layout-span: calc((var(--aside) * var(--layout)) + var(--aside) - 1);
    /**
     * Calculate the maximum index of the last column (the one "before" the aside)
     */
    --max-span: calc(-1 * var(--aside) - 1);
    /**
     * get the max between --layout-span and the latest column index.
     * -1 means full width
     * --max-span means default layout
     */
    --span: max(var(--max-span), var(--span));

    grid-column-start: 1;
    grid-column-end: var(--span);
}

Viceversa, when a small group of students is experiencing the text near the board, we could give more details, such as media files and interactive action triggers.

Beyond Face Recognition

The cases we faced (😉) are just two examples of how we can use face recognition technology for layout or typographical scopes. Tensorflow provides other models and libraries that can transform the camera stream into variables for our pages. Furthermore, we should not forget that in our smartphones there are a lot of other sensors that we could exploit using the Sensor APIs: GPS, accelerometer, ambient light, etc.

Since mood influences the way we read, study and search for information, with machine learning we can also analyze user’s expressions to switch from minimal to detailed layouts according to the user’s spirits.

For many years we have been used to using CSS Media queries for responsive web design. However, the size of the viewport is only one of the variables of the user experience. Recently, a new kind of media query designed to respect user preferences landed the browsers, such as the prefers-color-scheme and prefers-reduced-motion. This gives designers and developers a way to move a step forward in web design practices, allowing the web page to adapt to the whole environment instead of just the user’s device. In the age of big data, we have the opportunity to go beyond responsive and adaptive design. Our web pages can finally “leave the screen” and become part of the user’s global experience. Interaction design is going to involve all these possibilities, so keeping experimenting with the possible combinations between technology and web design will be crucial in the following years.

Get Text from HTML Attribute Tags

need to get the values from below following html snippet. So far I came up with this regex which helps me trim it down to the values I needed, but to automate this I need to join 2 regex statements to get the result "18" which is where I am stuck at. Or Please suggest a better method for me get the values.

First Regex Statement

9 Best Social Proof Plugins for WordPress & WooCommerce (2020)

Are you looking for the best social proof plugins for your WordPress website or WooCommerce store?

Social proof helps you earn customer trust and make more sales by showcasing your interactions with previous customers.

In this article, we will take a look at the best social proof WordPress plugins to help you make more sales and win customers.

The best social proof plugins for WordPress

What Is Social Proof and Why Does it Matter?

Social proof is the natural inclination that we have to copy other people’s actions. We are more likely to try things that we see other people buying, using, or recommending.

Social proof can take several different forms. It could be a review or testimonial. It could simply be a number, such as the number of Facebook fans or email newsletter subscribers your company has.

You can also use social proof for your online store to boost overall conversion rate. It can also be a great way to encourage donations, downloads, comments, or anything else that helps you reach your goals.

Since WordPress is the most popular website builder on the planet, there many different types of social proof tools available for you to use.

With that said, let’s take a look the best WordPress social proof plugins.

1. TrustPulse

The TrustPulse website

TrustPulse is the best WordPress social proof plugin on the market. It uses the FOMO effect by displaying real-time notifications of user activities on your site.

TrustPulse can show a notification bubble with recent purchases, form submissions, signs up for a free trial, and more.

You can setup TrustPulse on your site within a few minutes, and it is proven to instantly increase conversions by as much as 15%.

TrustPulse has lots of powerful features, including smart targeting. This lets you display social proof to the right people at the right time.

The TrustPulse plugin is ready to use straight out of the box.

It can automatically detect your eCommerce platform and start showing recent customer interactions / sales. Alternatively, you can also customize it to detect specific user activities and display on your site.

The TrustPulse notification popups are unobtrusive. They will not get in the way of your content or your user’s experience of your site. At the same time, they’re highly effective.

An example of a TrustPulse notification on OptinMonster's site

It also comes with built-in analytics that let you easily see which pages are getting the best conversion rate. This can help you optimize your content and boost conversions even further.

Price:

TrustPulse is free for up to 500 sessions per month. After that, pricing starts from $4/month (paid upfront annually).

2. Smash Balloon

The Smash Balloon website

Smash Balloon‘s plugins let you add social media feeds to your WordPress site. They can be used to show testimonials and other social proof.

Smash Balloon has plugins for Facebook, Instagram, Twitter, and YouTube. These let you easily gather social media testimonials or other powerful user-generated content.

For instance, you could use Smash Balloon’s Custom Twitter Feeds Pro plugin to add Twitter testimonials to your site. This lets you show what real people are saying about your products or services.

Bringing your social feed onto your site is more powerful than simply copying and pasting testimonials. It’s immediately clear to your site’s visitors that the tweets are real testimonials with real people behind them.

You can use their Instagram Feeds Pro plugin to automatically display new user photos of your product, or the YouTube Feeds Pro plugin to automatically display new user video reviews of your brand.

With Smash Balloon, it’s easy to choose exactly what elements to include in your social media feeds. For example, you could include the number of likes and comments on your Facebook posts. This can be a useful form of social proof as it shows that your brand has lots of followers and fans.

Price:

Smash Balloon offers an ‘All Access Bundle’ for all their plugins. This costs $299/year. Alternatively, you can buy an individual plugin for just $49/year.

There’s also lite free version of all Smash Balloon feed plugins.

3. Shared Counts

Shared Counts

Shared Counts is a straightforward WordPress plugin that lets you add social media share buttons to your site.

Many of these buttons can display your post or page‘s share count on the relevant social networks. If your blog content gets shared a lot, this is an easy form of social proof that shows just how popular your website is.

Shared Counts gives your readers an easy way to share your content on their favorite social networks. All they need to do is click a button.

All the most popular social media services are covered, including Facebook, Pinterest, Twitter, and LinkedIn. The plugin also lets you add a button that people can use to email your content to someone else.

The Shared Counts plugin uses the SharedCount.com API to provide the numbers for your shared counts. We have a step by step guide to Shared Counts that takes you through signing up for the SharedCount.com API.

Alternatively, you can use the shared counts from each of the social media apps, but this is more likely to slow down your site.

Price:

Shared Counts is completely free.

4. OptinMonster

The OptinMonster website

OptinMonster is a fantastic lead-generation tool that lets you easily convert visitors into email subscribers through popups and other eye-catching signup forms.

You can use it for social proof as well, by creating a popup that highlights key figures. For instance, you might create a popup that lets readers know how many people are on your email list.

OptinMonster lets you create a wide range of different popups and optin forms. These include regular-sized popups, floating bars, fullscreen popups, inline campaigns, and slide-ins.

Best of all, OptinMonster comes with lots of professionally designed and fully customizable templates. This lets you create great-looking popups in just a few minutes.

OptinMonster offers extra ways to boost your conversions, too. It’s easy to use countdown timers to create a sense of urgency and use the power of FOMO (fear of missing out). You can also boost engagement with special lead generation campaigns, such as yes/no optins and ‘spin the wheel’ optins.

Price:

The ‘Pro’ plan for OptinMonster costs $29/month (billed annually). This has lots of useful features like countdown timers and Exit Intent® technology.

The cheapest plan available is from $9/month, but this has limited features.

5. Thrive Ovation

The Thrive Ovation website

Thrive Ovation is a popular testimonial plugin for WordPress. It’s an effective way to add social proof by showing what people have said about your products or services.

With it, you can convert comments into testimonials with a single click. Thrive Ovation captures the name and photo of the person who left the comment.

It can also be integrated with Facebook and Twitter. This lets you take testimonials directly from those networks and add them to your own site.

Thrive Ovation even lets you create a landing page where you can collect testimonials through a simple online form. It can also send out automated emails to your customers to encourage them to leave a testimonial for you.

Price:

Thrive Ovation costs $39, which includes unlimited updates. You will need to pay a small extra fee after a year to continue receiving support.

6. WPForms

The WPForms website

WPForms is another easy way to collect testimonials and reviews. It’s the best forms plugin for WordPress and is designed to be really easy to use, even if you’re a complete beginner.

The WPForms template pack includes a testimonials form template. This lets you quickly set up a form to gather reviews and testimonials.

You can customize your testimonials form in any way you want. For instance, you could add an upload field so that customers can share a photo. You could even add a ratings field so customers can give your product or service a score.

Of course, there are lots of other ways to use WPForms too. You might decide to run a survey or poll. This can provide social proof through the results, or even through the number of participants.

Price:

The lite version of WPForms is free, but has limited functionality. WPForms premium version prices start from $39/year.

7. Constant Contact

The Constant Contact website

Constant Contact is our recommended email marketing service. There are lots of other services you can choose from too.

Having an email newsletter list is really important for all websites. Your email list is a great way to promote your products or services.

Once you have lots of signups to your email newsletter, it’s easy to use that number as social proof. For instance, you might encourage new visitors to join your email list by telling them, ‘50,000 people are already getting our emails every day. Are you missing out?’

Of course, you can use social proof within your emails as well. You could share testimonials when promoting a product or you could let your subscribers know how many places have already sold on an online course or class.

Price:

Constant Contact costs from $20/month. However, you can get a 20% discount by using our Constant Contact coupon. This makes it just $16/month.

8. NotificationX

The NotificationX website

NotificationX is a notifications plugin that you can use as an alternative to TrustPulse. You can use it to show download counts, comments, sales, reviews, and more.

You can use NotificationX on specific pages or across your whole site. Plus, there’s an analytics tool that lets you see which of your notification popups are the most effective.

With NotificationX, your site can show a small popup notification to all visitors each time someone comments. If you’re using MailChimp, you can show a notification whenever someone subscribes to your email list.

NotificationX can show reviews from WooCommerce or even from WordPress.org, if you sell a theme or plugin. It also has the option to create timer countdown notification bars.

Price

There’s a free version of NotificationX. This offers a great range of features if you’re just getting started. The pro version costs from $39/year.

9. WPfomify

The WPfomify website

WPfomify is another good notifications plugin. It shows recent sales and signups, and much more. If you have lots of traffic, you could use the Visitor Count notification to show how many people are visiting a page currently.

You can customize how WPfomify looks to make it a great match for your site and brand. It’s designed to be completely responsive and work on all devices.

WPfomify can also be used to show reviews. Like NotificationX, it lets you create timer countdown bars to use the power of FOMO.

WPfomify integrates with a range of other plugins and services. These include Give, a popular donations plugin, ConvertKit, Zapier, and many more.

Price:

WPfomify costs from $99/year.

We hope this article helped you find the best social proof plugins for WordPress and WooCommerce. You might also want to see our guide on the best plugins for business websites, and our comparison of the best business phone services providers.

If you liked this article, then please subscribe to our YouTube Channel for WordPress video tutorials. You can also find us on Twitter and Facebook.

The post 9 Best Social Proof Plugins for WordPress & WooCommerce (2020) appeared first on WPBeginner.