Month: November 2019

Testing React Hooks With Enzyme and React Testing Library

As you begin to make use of React hooks in your applications, you’ll want to be certain the code you write is nothing short of solid. There’s nothing like shipping buggy code. One way to be certain your code is bug-free is to write tests. And testing React hooks is not much different from how React applications are tested in general.

In this tutorial, we will look at how to do that by making use of a to-do application built with hooks. We’ll cover writing of tests using Ezyme and React Testing Library, both of which are able to do just that. If you’re new to Enzyme, we actually posted about it a little while back showing how it can be used with Jest in React applications. It’s not a bad idea to check that as we dig into testing React hooks.

Here’s what we want to test

A pretty standard to-do component looks something like this:

import React, { useState, useRef } from "react"; const Todo = () => {   const [todos, setTodos] = useState([     { id: 1, item: "Fix bugs" },     { id: 2, item: "Take out the trash" }   ]);   const todoRef = useRef();   const removeTodo = id => {     setTodos(todos.filter(todo => todo.id !== id));   };   const addTodo = data => {     let id = todos.length + 1;     setTodos([       ...todos,       {         id,         item: data       }     ]);   };   const handleNewTodo = e => {     e.preventDefault();     const item = todoRef.current;     addTodo(item.value);     item.value = "";   };   return (     <div className="container">       <div className="row">         <div className="col-md-6">           <h2>Add Todo</h2>         </div>       </div>       <form>         <div className="row">           <div className="col-md-6">             <input               type="text"               autoFocus               ref={todoRef}               placeholder="Enter a task"               className="form-control"               data-testid="input"             />           </div>         </div>         <div className="row">           <div className="col-md-6">             <button               type="submit"               onClick={handleNewTodo}               className="btn btn-primary"             >               Add Task             </button>           </div>         </div>       </form>       <div className="row todo-list">         <div className="col-md-6">           <h3>Lists</h3>           {!todos.length ? (             <div className="no-task">No task!</div>           ) : (             <ul data-testid="todos">               {todos.map(todo => {                 return (                   <li key={todo.id}>                     <div>                       <span>{todo.item}</span>                       <button                         className="btn btn-danger"                         data-testid="delete-button"                         onClick={() => removeTodo(todo.id)}                       >                         X                       </button>                     </div>                   </li>                 );               })}             </ul>           )}         </div>       </div>     </div>   ); }; export default Todo; 

Testing with Enzyme

We need to install the packages before we can start testing. Time to fire up the terminal!

npm install --save-dev enzyme enzyme-adapter-16 

Inside the src directory, create a file called setupTests.js. This is what we’ll use to configure Enzyme’s adapter.

import Enzyme from "enzyme"; import Adapter from "enzyme-adapter-react-16"; Enzyme.configure({ adapter: new Adapter() }); 

Now we can start writing our tests! We want to test four things:

  1. That the component renders
  2. That the initial to-dos get displayed when it renders
  3. That we can create a new to-do and get back three others
  4. That we can delete one of the initial to-dos and have only one to-do left

In your src directory, create a folder called __tests__ and create the file where you’ll write your Todo component’s tests in it. Let’s call that file Todo.test.js.

With that done, we can import the packages we need and create a describe block where we’ll fill in our tests.

import React from "react"; import { shallow, mount } from "enzyme"; import Todo from "../Todo";  describe("Todo", () => {   // Tests will go here using `it` blocks });

Test 1: The component renders

For this, we’ll make use of shallow render. Shallow rendering allows us to check if the render method of the component gets called — that’s what we want to confirm here because that’s the proof we need that the component renders.

it("renders", () => {   shallow(<Todo />); });

Test 2: Initial to-dos get displayed

Here is where we’ll make use of the mount method, which allows us to go deeper than what shallow gives us. That way, we can check the length of the to-do items.

it("displays initial to-dos", () => {   const wrapper = mount(<Todo />);   expect(wrapper.find("li")).toHaveLength(2); });

Test 3: We can create a new to-do and get back three others

Let’s think about the process involved in creating a new to-do:

  1. The user enters a value into the input field.
  2. The user clicks the submit button.
  3. We get a total of three to-do items, where the third is the newly created one.
it("adds a new item", () => {   const wrapper = mount(<Todo />);   wrapper.find("input").instance().value = "Fix failing test";   expect(wrapper.find("input").instance().value).toEqual("Fix failing test");   wrapper.find('[type="submit"]').simulate("click");   expect(wrapper.find("li")).toHaveLength(3);   expect(     wrapper       .find("li div span")       .last()       .text()   ).toEqual("Fix failing test"); });

We mount the component then we make use of find() and instance() methods to set the value of the input field. We assert that the value of the input field is set to “Fix failing test” before going further to simulate a click event, which should add the new item to the to-do list.

We finally assert that we have three items on the list and that the third item is equal to the one we created.

Test 4: We can delete one of the initial to-dos and have only one to-do left

it("removes an item", () => {   const wrapper = mount(<Todo />);   wrapper     .find("li button")     .first()     .simulate("click");   expect(wrapper.find("li")).toHaveLength(1);   expect(wrapper.find("li span").map(item => item.text())).toEqual([     "Take out the trash"   ]); });

In this scenario, we return the to-do with a simulated click event on the first item. It’s expected that this will call the removeTodo() method, which should delete the item that was clicked. Then we’re checking the numbers of items we have, and the value of the one that gets returned.

The source code for these four tests are here on GitHub for you to check out.

Testing With react-testing-library

We’ll write three tests for this:

  1. That the initial to-do renders
  2. That we can add a new to-do
  3. That we can delete a to-do

Let’s start by installing the packages we need:

npm install --save-dev @testing-library/jest-dom @testing-library/react

Next, we can import the packages and files:

import React from "react"; import { render, fireEvent } from "@testing-library/react"; import Todo from "../Todo"; import "@testing-library/jest-dom/extend-expect";  test("Todo", () => {   // Tests go here }

Test 1: The initial to-do renders

We’ll write our tests in a test block. The first test will look like this:

it("displays initial to-dos", () => {   const { getByTestId } = render(<Todo />);   const todos = getByTestId("todos");   expect(todos.children.length).toBe(2); });

What’s happening here? We’re making use of getTestId to return the node of the element where data-testid matches the one that was passed to the method. That’s the <ul> element in this case. Then, we’re checking that it has a total of two children (each child being a <li> element inside the unordered list). This will pass as the initial to-do is equal to two.

Test 2: We can add a new to-do

We’re also making use of getTestById here to return the node that matches the argument we’re passing in.

it("adds a new to-do", () => {   const { getByTestId, getByText } = render(<Todo />);   const input = getByTestId("input");   const todos = getByTestId("todos");   input.value = "Fix failing tests";   fireEvent.click(getByText("Add Task"));   expect(todos.children.length).toBe(3); });

We use getByTestId to return the input field and the ul element like we did before. To simulate a click event that adds a new to-do item, we’re using fireEvent.click() and passing in the getByText() method, which returns the node whose text matches the argument we passed. From there, we can then check to see the length of the to-dos by checking the length of the children array.

Test 3: We can delete a to-do

This will look a little like what we did a little earlier:

it("deletes a to-do", () => {   const { getAllByTestId, getByTestId } = render(<Todo />);   const todos = getByTestId("todos");   const deleteButton = getAllByTestId("delete-button");   const first = deleteButton[0];   fireEvent.click(first);   expect(todos.children.length).toBe(1); });

We’re making use of getAllByTestId to return the nodes of the delete button. Since we only want to delete one item, we fire a click event on the first item in the collection, which should delete the first to-do. This should then make the length of todos children equal to one.

These tests are also available on GitHub.

Linting

There are two lint rules to abide by when working with hooks:

Rule 1: Call hooks at the top level

…as opposed to inside conditionals, loops or nested functions.

// Don't do this! if (Math.random() > 0.5) {   const [invalid, updateInvalid] = useState(false); }

This goes against the first rule. According to the official documentation, React depends on the order in which hooks are called to associate state and the corresponding useState call. This code breaks the order as the hook will only be called if the conditions are true.

This also applies to useEffect and other hooks. Check out the documentation for more details.

Rule 2: Call hooks from React functional components

Hooks are meant to be used in React functional components — not in React’s class component or a JavaScript function.

We’ve basically covered what not to do when it comes to linting. We can avoid these missteps with an npm package that specifically enforces these rules.

npm install eslint-plugin-react-hooks --save-dev

Here’s what we add to the package’s configuration file to make it do its thing:

{   "plugins": [     // ...     "react-hooks"   ],   "rules": {     // ...     "react-hooks/rules-of-hooks": "error",     "react-hooks/exhaustive-deps": "warn"   } }

If you are making use of Create React App, then you should know that the package supports the lint plugin out of the box as of v3.0.0.

Go forth and write solid React code!

React hooks are equally prone to error as anything else in your application and you’re gonna want to ensure that you use them well. As we just saw, there’s a couple of ways we can go about it. Whether you use Enzyme or You can either make use of enzyme or React Testing Library to write tests is totally up to you. Either way, try making use of linting as you go, and no doubt, you’ll be glad you did.

The post Testing React Hooks With Enzyme and React Testing Library appeared first on CSS-Tricks.

CSS-Tricks

, , , ,

Simplified Fluid Typography

Fluid typography is the idea that font-size (and perhaps other attributes of type, like line-height) change depending on the screen size (or perhaps container queries if we had them).

The core trickery comes from viewport units. You can literally set type in viewport units (e.g. font-size: 4vw), but the fluctuations in size are so extreme that it’s usually undesirable. That’s tampered by doing something like font-size: calc(16px + 1vw). But while we’re getting fancy with calculations anyway, the most common implementation ended up being an equation to calculate plain English:

I want the type to go between being 16px on a 320px screen to 22px on a 1000px screen.

Which ended up like this:

html {   font-size: 16px; } @media screen and (min-width: 320px) {   html {     font-size: calc(16px + 6 * ((100vw - 320px) / 680));   } } @media screen and (min-width: 1000px) {   html {     font-size: 22px;   } } 

That’s essentially setting a minimum and maximum font size so the type won’t shrink or grow to anything too extreme. “CSS locks” was a term coined by Tim Brown.

Minimum and maximum you say?! Well it so happens that functions for these have made their way into the CSS spec in the form of min() and max().

So we can simplify our fancy setup above with a one-liner and maintain the locks:

html {   font-size: min(max(16px, 4vw), 22px); }

We actually might want to stop there because even though both Safari (11.1+) and Chrome (79+) support this at the current moment, that’s as wide as support will get today. Speaking of which, you’d probably want to slip a font-size declaration before this to set an acceptable fallback value with no fancy functions.

But as long as we’re pushing the limits, there is another function to simplify things even more: clamp()! Clamp takes three values, a min, max, and a flexible unit (or calculation or whatever) in the middle that it will use in case the value is between the min and max. So, our one-liner gets even smaller:

body {   font-size: clamp(16px, 4vw, 22px); } 

That’ll be Chrome 79+ (which doesn’t hasn’t even dropped to stable but will very soon).

Uncle Dave is very happy that FitText is now a few bytes instead of all-of-jQuery plus 40 more lines. Here is us chucking CSS custom properties at it:

See the Pen
FitText in CSS with clamp()
by Dave Rupert (@davatron5000)
on CodePen.

The post Simplified Fluid Typography appeared first on CSS-Tricks.

CSS-Tricks

, ,
[Top]

Web Scraping Made Simple With Zenscrape

Web scraping has always been taken care of by actual developers, since a lot of coding, proxy management and CAPTCHA-solving is involved. However, the scraped data is very often needed by people that are non-coders: Marketers, Analysts, Business Developers etc.

Zenscrape is an easy-to-use web scraping tool that allows people to scrape websites without having to code.

Let’s run through a quick example together:

Select the data you need

The setup wizard guides you through the process of setting up your data extractor. It allows you to select the information you want to scrape visually. Click on the desired piece of content and specify what type of element you have. Depending on the package you have bought (they also offer a free plan), you can select up to 30 data elements per page.

The scraper is also capable of handling element lists.

Schedule your extractor

Perhaps, you want to scrape the selected data at a specific time interval. Depending on your plan, you can choose any time span between one minute to one hour. Also, decide what is supposed to happen with the scraped data after it has been gathered.

Use your data

In this example, we have chosen the .csv-export method and have selected a 10 minute scraping interval. Our first set of data should be ready by now. Let’s take a look:

Success! Our data is ready for us to be downloaded. We can now access all individual data sets or download all previously gathered data at once, in one file.

Need more flexibility?

Zenscrape also offers a web scraping API that returns the HTML markup of any website. This is especially useful for complicated scraping projects, that require the scraped content to be integrated into a software application for further processing.

Just like the web scraping suite, the API does not forward failed requests and takes care of proxy management, Capotcha-solving and all other maintenance tasks that are usually involved with DIY web scrapers.

Since the API returns the full HTML markup of the related website, you have full flexibility in terms of data selection and further processing.

Try Zenscrape

The post Web Scraping Made Simple With Zenscrape appeared first on CSS-Tricks.

CSS-Tricks

, , ,
[Top]

iOS 13 Broke the Classic Pure CSS Parallax Technique

I know. You hate parallax. You know what we should hate more? When things that used to work on the web stop working without any clear warning or idea why.

Way back in 2014, Keith Clark blogged an exceptionally clever CSS trick where you essentially use a CSS transform to scale an element down affecting how it appears to scroll, then “depth correcting” it back to “normal” size. Looks like we got five years of fun out of that, but it’s stopped working in iOS 13.

Here’s a video of official simulators and the problem:

I’d like to raise my hand for un-breaking this. If we don’t watchdog for the web, everything will suffer.

The post iOS 13 Broke the Classic Pure CSS Parallax Technique appeared first on CSS-Tricks.

CSS-Tricks

, , , ,
[Top]

The Power (and Fun) of Scope with CSS Custom Properties

You’re probably already at least a little familiar with CSS variables. If not, here’s a two-second overview: they are really called custom properties, you set them in declaration blocks like --size: 1em and use them as values like font-size: var(--size);, they differ from preprocessor variables (e.g. they cascade), and here’s a guide with way more information.

But are we using them to their full potential? Do we fall into old habits and overlook opportunities where variables could significantly reduce the amount of code we write?

This article was prompted by a recent tweet I made about using CSS variables to create dynamic animation behavior.

Let’s look at a couple of instances where CSS variables can be used to do some pretty cool things that we may not have considered.

Basic scoping wins

The simplest and likely most common example would be scoped colors. And what’s our favorite component to use with color? The button. 😅

Consider the standard setup of primary and secondary buttons. Let’s start with some basic markup that uses a BEM syntax.

<button class="button button--primary">Primary</button> <button class="button button--secondary">Secondary</button>

Traditionally, we might do something like this to style them up:

.button {   padding: 1rem 1.25rem;   color: #fff;   font-weight: bold;   font-size: 1.25rem;   margin: 4px;   transition: background 0.1s ease; }  .button--primary {   background: hsl(233, 100%, 50%);   outline-color: hsl(233, 100%, 80%); }  .button--primary:hover {   background: hsl(233, 100%, 40%); }  .button--primary:active {   background: hsl(233, 100%, 30%); }  .button--secondary {   background: hsl(200, 100%, 50%);   outline-color: hsl(200, 100%, 80%); }  .button--secondary:hover {   background: hsl(200, 100%, 40%); }  .button--secondary:active {   background: hsl(200, 100%, 30%); }

See the Pen
Basic buttons
by Jhey (@jh3y)
on CodePen.

That’s an awful lot of code for something not particularly complex. We haven’t added many styles and we’ve added a lot of rules to cater to the button’s different states and colors. We could significantly reduce the code with a scoped variable.

In our example, the only differing value between the two button variants is the hue. Let’s refactor that code a little then. We won’t change the markup but cleaning up the styles a little, we get this:

.button {   padding: 1rem 1.25rem;   color: #fff;   font-weight: bold;   font-size: 1.25rem;   margin: 1rem;   transition: background 0.1s ease;   background: hsl(var(--hue), 100%, 50%);   outline-color: hsl(var(--hue), 100%, 80%);  } .button:hover {   background: hsl(var(--hue), 100%, 40%); }  .button:active {   background: hsl(var(--hue), 100%, 30%); }  .button--primary {   --hue: 233; }  .button--secondary {   --hue: 200; }

See the Pen
Refactoring styles with a scoped variable
by Jhey (@jh3y)
on CodePen.

This not only reduces the code but makes maintenance so much easier. Change the core button styles in one place and it will update all the variants! 🙌

I’d likely leave it there to make it easier for devs wanting to use those buttons. But, we could take it further. We could inline the variable on the actual element and remove the class declarations completely. 😲

<button class="button" style="--hue: 233;">Primary</button> <button class="button" style="--hue: 200;">Secondary</button>

Now we don’t need these. 👍

.button--primary {   --hue: 233; }  .button--secondary {   --hue: 200; }

See the Pen
Scoping w/ inline CSS variables
by Jhey (@jh3y)
on CodePen.

Inlining those variables might not be best for your next design system or app but it does open up opportunities. Like, for example, if we had a button instance where we needed to override the color.

button.button.button--primary(style=`--hue: 20;`) Overridden

See the Pen
Overridden with inline scope
by Jhey (@jh3y)
on CodePen.

Having fun with inline variables

Another opportunity is to have a little fun with it. This is a technique I use for many of the Pens I create over on CodePen. 😉

You may be writing straightforward HTML, but in many cases, you may be using a framework, like React or a preprocessor like Pug, to write your markup. These solutions allow you to leverage JavaScript to create random inline variables. For the following examples, I’ll be using Pug. Pug is an indentation-based HTML templating engine. If you aren’t familiar with Pug, do not fear! I’ll try to keep the markup simple.

Let’s start by randomizing the hue for our buttons:

button.button(style=`--hue: $ {Math.random() * 360}`) First

With Pug, we can use ES6 template literals to inline randomized CSS variables. 💪

See the Pen
Random inline CSS variable hues
by Jhey (@jh3y)
on CodePen.

Animation alterations

So, now that we have the opportunity to define random characteristics for an element, what else could we do? Well, one overlooked opportunity is animation. True, we can’t animate the variable itself, like this:

@keyframes grow {   from { --scale: 1; }   to   { --scale: 2; } }

But we can create dynamic animations based on scoped variables. We can change the behavior of animation on the fly! 🤩

Example 1: The excited button

Let’s create a button that floats along minding its own business and then gets excited when we hover over it.

Start with the markup:

button.button(style=`--hue: $ {Math.random() * 360}`) Show me attention

A simple floating animation may look like this:

@keyframes flow {   0%, 100% {     transform: translate(0, 0);   }   50% {     transform: translate(0, -25%);   } }

This will give us something like this:

See the Pen
The excited button foundation
by Jhey (@jh3y)
on CodePen.

I’ve added a little shadow as an extra but it’s not vital. 👍

Let’s make it so that our button gets excited when we hover over it. Now, we could simply change the animation being used to something like this:

.button:hover {   animation: shake .1s infinite ease-in-out; }  @keyframes shake {   0%, 100% {     transform: translate(0, 0) rotate(0deg);   }   25% {     transform: translate(-1%, 3%) rotate(-2deg);   }   50% {     transform: translate(1%, 2%) rotate(2deg);   }   75% {     transform: translate(1%, -2%) rotate(-1deg);   } }

And it works:

See the Pen
The excited button gets another keyframes definition
by Jhey (@jh3y)
on CodePen.

But, we need to introduce another keyframes definition. What if we could merge the two animations into one? They aren’t too far off from each other in terms of structure.

We could try:

@keyframes flow-and-shake {   0%, 100% {     transform: translate(0, 0) rotate(0deg);   }   25%, 75% {     transform: translate(0, -12.5%) rotate(0deg);   }   50% {     transform: translate(0, -25%) rotate(0deg);   } }

Although this works, we end up with an animation that isn’t quite as smooth because of the translation steps. So what else could we do? Let’s find a compromise by removing the steps at 25% and 75%.

@keyframes flow-and-shake {   0%, 100% {     transform: translate(0, 0) rotate(0deg);   }   50% {     transform: translate(0, -25%) rotate(0deg);   } }

It works fine, as we expected, but here comes the trick: Let’s update our button with some variables.

.button {   --y: -25;   --x: 0;   --rotation: 0;   --speed: 2; }

Now let’s plug them into the animation definition, along with the button’s animation properties.

.button {   animation-name: flow-and-shake;   animation-duration: calc(var(--speed) * 1s);   animation-iteration-count: infinite;   animation-timing-function: ease-in-out; }  @keyframes flow-and-shake {   0%, 100% {     transform: translate(calc(var(--x) * -1%), calc(var(--y) * -1%))       rotate(calc(var(--rotation) * -1deg));   }   50% {     transform: translate(calc(var(--x) * 1%), calc(var(--y) * 1%))       rotate(calc(var(--rotation) * 1deg));   } }

All is well. 👍

Let’s change those values when the button is hovered:

.button:hover {   --speed: .1;   --x: 1;   --y: -1;   --rotation: -1; }

See the Pen
The excited button with refactored keyframes & scoped variables
by Jhey (@jh3y)
on CodePen.

Nice! Now our button has two different types of animations but defined via one set of keyframes. 🤯

Let’s have a little more fun with it. If we take it a little further, we can make the button a little more playful and maybe stop animating altogether when it’s active. 😅

See the Pen
The Excited Button w/ dynamic animation 🤓
by Jhey (@jh3y)
on CodePen.

Example 2: Bubbles

Now that we’ve gone through some different techniques for things we can do with the power of scope, let’s put it all together. We are going to create a randomly generated bubble scene that heavily leverages scoped CSS variables.

Let’s start by creating a bubble. A static bubble.

.bubble {   background: radial-gradient(100% 115% at 25% 25%, #fff, transparent 33%),     radial-gradient(15% 15% at 75% 75%, #80dfff, transparent),     radial-gradient(100% 100% at 50% 25%, transparent, #66d9ff 98%);   border: 1px solid #b3ecff;   border-radius: 100%;   height: 50px;   width: 50px; }

We are using background with multiple values and a border to make the bubble effect — but it’s not very dynamic. We know the border-radius will always be the same. And we know the structure of the border and background will not change. But the values used within those properties and the other property values could all be random.

Let’s refactor the CSS to make use of variables:

.bubble {   --size: 50;   --hue: 195;   --bubble-outline: hsl(var(--hue), 100%, 50%);   --bubble-spot: hsl(var(--hue), 100%, 75%);   --bubble-shade: hsl(var(--hue), 100%, 70%);   background: radial-gradient(100% 115% at 25% 25%, #fff, transparent 33%),     radial-gradient(15% 15% at 75% 75%, var(--bubble-spot), transparent),     radial-gradient(100% 100% at 50% 25%, transparent, var(--bubble-shade) 98%);   border: 1px solid var(--bubble-outline);   border-radius: 100%;   height: calc(var(--size) * 1px);   width: calc(var(--size) * 1px); }

That’s a good start. 👍

See the Pen
Bubbles foundation
by Jhey (@jh3y)
on CodePen.

Let’s add some more bubbles and leverage the inline scope to position them as well as size them. Since we are going to start randomizing more than one value, it’s handy to have a function to generate a random number in range for our markup.

- const randomInRange = (max, min) => Math.floor(Math.random() * (max - min + 1)) + min

With Pug, we can utilize iteration to create a large set of bubbles:

- const baseHue = randomInRange(0, 360) - const bubbleCount = 50 - let b = 0 while b < bubbleCount   - const size = randomInRange(10, 50)   - const x = randomInRange(0, 100)   .bubble(style=`--x: $ {x}; --size: $ {size}; --hue: $ {baseHue}`)   - b++

Updating our .bubble styling allows us to make use of the new inline variables.

.bubble {   left: calc(var(--x) * 1%);   position: absolute;   transform: translate(-50%, 0); }

Giving us a random set of bubbles:

See the Pen
Adding bubbles
by Jhey (@jh3y)
on CodePen.

Let’s take it even further and animate those bubbles so they float from top to bottom and fade out.

.bubble {   animation: float 5s infinite ease-in-out;   top: 100%; }  @keyframes float {   from {     opacity: 1;     transform: translate(0, 0) scale(0);   }   to {     opacity: 0;     transform: translate(0, -100vh) scale(1);   } }

See the Pen
Bubbles rising together
by Jhey (@jh3y)
on CodePen.

That’s pretty boring. They all do the same thing at the same time. So let’s randomize the speed, delay, end scale and distance each bubble is going to travel.

- const randomInRange = (max, min) => Math.floor(Math.random() * (max - min + 1)) + min - const baseHue = randomInRange(0, 360) - const bubbleCount = 50 - let b = 0 while b < bubbleCount   - const size = randomInRange(10, 50)   - const delay = randomInRange(1, 10)   - const speed = randomInRange(2, 20)   - const distance = randomInRange(25, 150)   - const scale = randomInRange(100, 150) / 100   - const x = randomInRange(0, 100)   .bubble(style=`--x: $ {x}; --size: $ {size}; --hue: $ {baseHue}; --distance: $ {distance}; --speed: $ {speed}; --delay: $ {delay}; --scale: $ {scale}`)   - b++

And now, let’s update our styles

.bubble {   animation-name: float;   animation-duration: calc(var(--speed) * 1s);   animation-delay: calc(var(--delay) * -1s);   animation-iteration-count: infinite;   animation-timing-function: ease-in-out; }  @keyframes float {   from {     opacity: 1;     transform: translate(-50%, 0) scale(0);   }   to {     opacity: 0;     transform: translate(-50%, calc(var(--distance) * -1vh)) scale(var(--scale));   } }

And we will get this:

See the Pen
Random bubble scene using variable scope 😎
by Jhey (@jh3y)
on CodePen.

With around 50 lines of code, you can create a randomly generated animated scene by honing the power of the scope! 💪

That’s it!

We can create some pretty cool things with very little code by putting CSS variables to use and leveraging some little tricks.

I do hope this article has raised some awareness for the power of CSS variable scope and I do hope you will hone the power and pass it on 😎

All the demos in this article are available in this CodePen collection.

The post The Power (and Fun) of Scope with CSS Custom Properties appeared first on CSS-Tricks.

CSS-Tricks

, , ,
[Top]

The Thought Process Behind a Flexbox Layout

I just need to put two boxes side-by-side and I hear flexbox is good at stuff like that.

Just adding display: flex; to the parent element lays out the children in a row.

Well, that’s cool. I guess I could have floated them, but this is easier.

They should probably take up the full space they have though. Can I just stretch the parent to 100% wide? Well, I can, but that’s apparently not going to affect the child elements.

If the parent element has more space than the children need, it doesn’t do anything to help the children fill the space alone.

Maybe I can use width: 50%; on the children? That works, but I thought the point of flexbox is that you don’t have to be all specific about width. Ah yes, flexbox has all of these ways of expressing the growy-shrinky-initial behavior of children. Looks like flex: 1; is about as easy as it gets here.

Applying flex: 1; to the children allow them to grow and fill the space.

I like how I haven’t had to do any math or hard code values so far. Can I make it a three-up pattern without touching CSS?

Nice.

Hmmm, wait. The sizing is a bit, uhhhh, flexy? Nothing is forcing these boxes to be one-third of the container all the time.

Looks like flex-basis: 33% doesn’t fix this. flex: 0 0 33%; also doesn’t do the trick. Looks like width: 33%; flex-shrink: 0; does though, if we’re really wanting to strongarm it.

Sometimes a design calls for exactly equal size boxes. This is maybe CSS Grid territory, but whatever.

The long word forcing that sizing behavior at narrow widths is perhaps an uncommon scenario. We could have also solved it with word-break: break-word; hyphens: auto; on the child.

Another thing we could do it just let the dang items wrap instead of being so strict about it. Flexbox can do that, right?

Oh hey, that reminds me how cool it is that those first two items are the same height. That’s the default stretch behavior, but it can be controlled as well. And it’s by row, which is why the second row has its own different height.

What if I want that first “Love” block to be on top instead? Looks like I can re-order it, eh? Let’s see, the default order is 0, so to be first, I do order: -1;.

Ha! That kinda worked. But I meant that I want it to take up the full width on the top row. I guess I can just kick it up to flex-basis: 100%; and the others will wrap accordingly.

It’s pretty weird to have the source order and visual order different like this. Maybe that should go in the “don’t ever do this” category.

What if I wanna bail on the whole flexbox thing at a certain point? Part of me wants to change the direction to go vertical with flex-direction: column; and force the children to be full-width. Ah, a simple display: block; on the parent does that in one swoop.

Rather than changing all the flexbox stuff to handle a column layout, we just turn flexbox off.

Flexbox can do way more stuff! One of my favorites is how auto margins work to “push” other elements away when there is space. This is just one little journey playing with some UI and seeing the useful things flexible does along with things that can make it confusing.

The post The Thought Process Behind a Flexbox Layout appeared first on CSS-Tricks.

CSS-Tricks

, , , ,
[Top]

Product Search and Filters Are a Snap With WooCommerce

Let’s say you visit an e-commerce site because you want to buy the latest banana peeler model. Bananas are hard enough to peel, right? Only a tool will do!

What’s the first thing you’re going to do on the site? Chances are, it’s entering something into the (hopefully) prominent search field. You need a way to tell the store what you’re looking for in order to get recommendations and that’s the first step.

What’s next? Again, chances are you will need to get more specific with what you’re looking for. You may need the search results to be filtered to get a more precise and increase the chance that your banana peeler surfaces to the top. That might be filtering by a specific brand, color, size, availability, or whatever.

Developing an e-commerce site is already a difficult task and throwing in things like search and filters make it even tougher. That’s why WooCommerce is worth looking into. It’s a WordPress plugin that transforms any site into a full-fledged e-commerce machine, complete with product inventory, shipping methods, payment gateways and, yes, front-end functionality for things like search and filtering.

Take the latest WooCommerce release for example. It introduces a new “All Products” block for the Gutenburg block editor. You can literally drop this block on any WordPress page and it will render a fully customizable grid of products from your inventory. Pretty slick!

Photo courtesy of WooCommerce

And what about filtering? There are specific blocks for that too. Drop any number of filtering options on the same page as the All Products block and the filters will automatically know to filter from the products in the block. And there’s a ton of ways to filter products, including price and attributes (e.g. color, size, etc.). Oh, and filters can be combined, of course, so they can be used together for even more fine-grained search results.

Photo courtesy of WooCommerce

This is just one of the many, many ways WooCommerce is lowering the bar to entry for e-commerce while setting high standards for what to expect on an e-commerce site. It’s free and has been downloaded more than 40 million times… now it’s your turn to give it a spin.

If you’re thinking of pulling the trigger on WooCommerce and think you might need to buy some plugins, it’s about to the absolute perfect time of year to do that. Kicking off on Black Friday and running through Cyber Monday, literally everything WooCommerce is 40% off. And if you’d rather do eCommerce on a totally hosted platform, WordPress.com can do that and that’s all 20% off.

The post Product Search and Filters Are a Snap With WooCommerce appeared first on CSS-Tricks.

CSS-Tricks

, , , ,
[Top]

An Introduction to the Picture-in-Picture Web API

Picture-in-Picture made its first appearance on the web in the Safari browser with the release of macOS Sierra in 2016. It made it possible for a user to pop a video out into a small floating window that stays above all others, so that they can keep watching while doing other things. It’s an idea that came from TV, where, for example, you might want to keep watching your Popular Sporting Event even as you browse the guide or even other channels.

Not long after that, Android 8.0 was released which included picture-in-picture support via native APIs. Chrome for Android was able to play videos in picture-in-picture mode through this API even though its desktop counterpart was unable to do so.

This lead to the drafting of a standard Picture-in-Picture Web API which makes it possible for websites to initiate and control this behavior.

At the time of writing, only Chrome (version 70+) and Edge (version 76+) support this feature. Firefox, Safari, and Opera all use proprietary APIs for their implementations.

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

Chrome Opera Firefox IE Edge Safari
70 64 72 No 76 TP

Mobile / Tablet

iOS Safari Opera Mobile Opera Mini Android Android Chrome Android Firefox
13.3 No No No 78 68

How to use the Picture-In-Picture API

Let’s start by adding a video to a webpage.

<video controls src="video.mp4"></video>

In Chrome, there will already be a toggle for entering and exiting Picture-in-Picture mode.

Showing a video with the picture-in-picture option in the bottom right of the screen.

To test Firefox’s implementation, you’ll need to enable the media.videocontrols.picture-in-picture.enabled flag in about:config first, then right-click on the video to find the picture-in-picture option.

Showing the Firefox settings that enable picture-in-picture.

While this works, in many cases, you want your video controls to be consistent across browsers and you might want control over which videos can be entered into picture-in-picture mode and which ones cannot.

We can replace the default method of entering picture-in-picture mode in the browser with our own method using the Picture-in-Picture Web API. For example, let’s add a button that, when clicked, enables it:

<button id="pipButton" class="hidden" disabled>Enter Picture-in-Picture mode</button>

Then select both the video and the button in JavaScript:

const video = document.getElementById('video'); const pipButton = document.getElementById('pipButton');

The button is hidden and disabled by default because we need to know if the Picture-in-Picture API is supported and enabled in the user’s browser before displaying the it. This is a form of progressive enhancement which helps avoid a broken experience in browsers that do not support the feature.

We can check that the API is supported and enable the button as shown below:

if ('pictureInPictureEnabled' in document) {   pipButton.classList.remove('hidden')   pipButton.disabled = false; }

Entering picture-in-picture mode

Let’s say our JavaScript has determined that the browser has picture-in-picture support enabled. Let’s call requestPictureInPicture() on the video element when the button with #pipButton is clicked. This method returns a promise that places the video in a mini window on the bottom-right side of the screen by default when resolved, although it can be moved around by the user.

if ('pictureInPictureEnabled' in document) {   pipButton.classList.remove('hidden')   pipButton.disabled = false;    pipButton.addEventListener('click', () => {     video.requestPictureInPicture();   }); }

We cannot leave the code above as is because requestPictureInPicture() returns a promise and it’s possible for the promise to reject if, for example, the video metadata is not yet loaded or if the disablePictureInPicture attribute is present on the video.

Let’s add a catch block to capture this potential error and let the user know what is going on:

pipButton.addEventListener('click', () => {   video     .requestPictureInPicture()     .catch(error => {       // Error handling     }); });

Exiting picture-in-picture mode

The browser helpfully provides a close button on the picture-in-picture window, which enables the window to be close when clicked. However, we can also provide another way to exit picture-in-picture mode as well. For example, we can make clicking our #pipButton close any active picture-in-picture window.

pipButton.addEventListener('click', () => {   if (document.pictureInPictureElement) {     document       .exitPictureInPicture()       .catch(error => {       // Error handling     })   } else {     // Request Picture-in-Picture   } });

One other situation where you might want to close the picture-in-picture window is when the video enters full-screen mode. Chrome already does this automatically without having to write any code.

Picture-in-Picture events

The browser allows us to detect when a video enters or leaves picture-in-picture mode. Since there are many ways picture-in-picture mode can be entered or exited, it is better to rely on event detection to update media controls.

The events are enterpictureinpicture and leavepictureinpicture which, as their names imply, fire when a video enters or exits picture-in-picture mode, respectively.

In our example, we need to update the #pipButton label depending on whether the video is or is not currently in picture-in-picture mode.

Here’s the code that helps us achieve that:

video.addEventListener('enterpictureinpicture', () => {   pipButton.textContent = 'Exit Picture-in-Picture mode'; });  video.addEventListener('leavepictureinpicture', () => {   pipButton.textContent = 'Enter Picture-in-Picture mode'; });

Here’s a demo of that:

See the Pen “>@ayoisaiah)
on MediaStream object (produced by a virtual video source such as a camera, video recording device, screen sharing service, or other hardware sources).

It’s also possible to add controls that go to the previous or next track straight from the picture-in-picture window:

navigator.mediaSession.setActionHandler('previoustrack', () => {   // Go to previous track });  navigator.mediaSession.setActionHandler('nexttrack', () => {   // Go to next track });

Displaying a webcam feed in a picture-in-picture window

Video meeting web applications could benefit from placing a webcam feed in picture-in-picture mode when a user switches back and forth between the app and other browser tabs or windows.

Here’s an example:

See the Pen “>@ayoisaiah)
on <video disablePictureInPicture controls src="video.mp4>"></video>

Wrapping up

That’s pretty much all you need to about the Picture-in-Picture Web API at this time! Currently, the API only supports the <video> element but it is meant to be extensible to other elements as well.

Although browser support is spotty right now, you can still use it as a way to progressively enhance the video experience on your website.

Further reading

The post An Introduction to the Picture-in-Picture Web API appeared first on CSS-Tricks.

CSS-Tricks

,
[Top]

Design Deals for the Week

[Top]

Playing Sounds with CSS

CSS is the domain of styling, layout, and presentation. It is full of colors, sizes, and animations. But did you know that it could also control when a sound plays on a web page?

This article is about a little trick to pull that off. It’s actually a strict implementation of the HTML and CSS, so it’s not really a hack. But… let’s be honest, it’s still kind of a hack. I wouldn’t recommend necessarily using it in production, where audio should probably be controlled with native <audio> elements and/or JavaScript.

The trick

There are a few alternatives to playing sounds with CSS, but the underlying idea is the same: inserting the audio file as a hidden object/document within the web page, and displaying it whenever an action happens. Something like this:

<style>   embed { display: none; }   button:active + embed { display: block; } </style>  <button>Play Sound</button> <embed src="path-to-audio-file.mp3" />

View Demo

This code uses an <embed> tag, but we could also use <object> with similar results:

<object data="path-to-audio-file.mp3"></object>

A quick note on the demo and this technique. I developed a small piano on CodePen just with HTML and CSS using this technique about a year ago. It worked great, but since then, some things have changed and the demo doesn’t work on CodePen anymore.

The biggest change was related to security. As it uses embed or object instead of audio, the imported file is subject to stricter security checks. Cross-origin access control policies (CORS) force the audio file to be on the same protocol and domain as the page it is imported into. Even putting the sound in base64 will not work anymore. Also, you (and users) may need to activate autoplay on their browser settings for this trick to work. It is often enabled behind a flag.

Another change is that browsers now only play the sounds once. I could have sworn that past browsers played the sound every time that it was shown. But that doesn’t appear to be the case anymore, which considerably limits the scope of the trick (and bares the piano demo almost useless).

The CORS issue can be worked around if you have control over the servers and files, but the disabled autoplay is a per-user thing that is out of our control.

View Demo

Why it works

The theory behind this behavior can be found buried in the definition of the embed tag:

Whenever an embed element that was not potentially active becomes potentially active, and whenever a potentially active embed element that is remaining potentially active and has its src attribute set, changed, or removed or its type attribute set, changed, or removed, the user agent must queue a task using the embed task source to run the embed element setup steps for that element.

Same goes for the definition of the object tag:

Whenever one of the following conditions occur:

[…]

  • the element changes from being rendered to not being rendered, or vice versa,

[…] the user agent must queue a task to run the following steps to (re)determine what the object element represents. [and eventually process and run it]

While it is clearer for object (the file is processed and run on render), we have this concept of “potentially active” for embed that may seem a bit more complicated. And while there are some additional conditions, it will run on initial render similarly as how it does with object.

As you can see, technically this is not a trick at all, but how all browsers should behave… although they don’t.

Browser support

As with many of these hacks and tricks, the support of this feature is not great and varies considerably from browser to browser.

It works like a charm on Opera and Chrome, which means a big percentage of the browser market. However, the support is spotty for other Chromium-based browsers. For example, Edge on Mac will play the audio correctly, while the Brave browser won’t unless you have the latest version.

Safari was a non-starter, and the same can be said for Internet Explorer or Edge on Windows. Nothing close to working on any of these browsers.

Firefox will play all the sounds at once on page load, but then won’t play them after they are hidden and shown again. It will trigger a security warning in the console as the sounds attempt to be play “without user interaction,” blocking them unless the user approves the site first.

Overall, this is a fun trick with CSS but one of those “don’t do this at home” kind of things… which in software development, means this is the perfect thing to do at home. 🙂

The post Playing Sounds with CSS appeared first on CSS-Tricks.

CSS-Tricks

,
[Top]