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

Post pobrano z: 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.

CSS variables are awesome, right? But scope power is often overlooked. For example, take this demo, 3 different animations but only 1 animation defined 💪 That means dynamic animations 😎 https://t.co/VN02NlC4G8 via @CodePen #CSS #animation #webdev #webdesign #coding pic.twitter.com/ig8baxr7F3

— Jhey @ NodeConfEU 2019 📍🇮🇪⌨️ (@jh3yy) November 5, 2019

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.

ebay: The best gift

Post pobrano z: ebay: The best gift

Film
Ebay

Advertising Agency:Moonlike – M&C SAATCHI, France
Production Company:Left Productions
Director:Jules Renault
Producer:Robin Psl
Production Manager:Suzon Thommen
Director Assistant:Jordan Clement
Production Coordinator:Benoit Berland
Production Assistant:Magali Mennessier
DoP:Manu Bernard
1st AC:Titi Mouth
2nd AC:Timothé Thomasson
Machinery:Guillaume Grandin
Chef electro:Pauline Raimbault
Electro:Thomas Duprez
Electro reinforcement:Morgan Dandre
Regie:Clelia Tipi
Set Designer:Jérôme Nicol
Decoration assistants:Pierre Bertet, Hugo Minonne
Stylist:Clara Lola
Makeup:Gauthier Joseph
Trainer:José Cabreira
Postproduction Manager:Morgane Hnnt
Editing:Paume, Tuan Hô, Bissane Kim, Damien Leclercq
Animation:Paume, Tuan Hô, Bissane Kim, Damien Leclercq
Color Grading:Florian Martiny
Composition:Po Gtr, Jim Calamel, Bon Enfant Production
Mix & Sound Design:Po Gtr, Jim Calamel, Bon Enfant Production

ebay: The best gift

Post pobrano z: ebay: The best gift

Film
Ebay

Advertising Agency:Moonlike – M&C SAATCHI, France
Production Company:Left Productions
Director:Jules Renault
Producer:Robin Psl
Production Manager:Suzon Thommen
Director Assistant:Jordan Clement
Production Coordinator:Benoit Berland
Production Assistant:Magali Mennessier
DoP:Manu Bernard
1st AC:Titi Mouth
2nd AC:Timothé Thomasson
Machinery:Guillaume Grandin
Chef electro:Pauline Raimbault
Electro:Thomas Duprez
Electro reinforcement:Morgan Dandre
Regie:Clelia Tipi
Set Designer:Jérôme Nicol
Decoration assistants:Pierre Bertet, Hugo Minonne
Stylist:Clara Lola
Makeup:Gauthier Joseph
Trainer:José Cabreira
Postproduction Manager:Morgane Hnnt
Editing:Paume, Tuan Hô, Bissane Kim, Damien Leclercq
Animation:Paume, Tuan Hô, Bissane Kim, Damien Leclercq
Color Grading:Florian Martiny
Composition:Po Gtr, Jim Calamel, Bon Enfant Production
Mix & Sound Design:Po Gtr, Jim Calamel, Bon Enfant Production

Simplified Fluid Typography

Post pobrano z: 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.

Testing React Hooks With Enzyme and React Testing Library

Post pobrano z: 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.

ABInbev Budweiser: Beer Against Climate Change

Post pobrano z: ABInbev Budweiser: Beer Against Climate Change
Outdoor, Print
ABInbev

To face climate change the planet needs less hashtags and more actions. Therefore, Budweiser is committed to brewing its beer with 100% renewable energy. Below, you will find a series of prints that prove it.

Advertising Agency:DraftLine Maz, Colombia
Creative Director:Valentin Villamil, Iván Rivera, Camilo Ruano
Copywriter:Sebastián Pelaez, Sergio Colmenares
Art Director:Cristian Jimenez
Postproduction:La Salvación Studio

ABInbev Budweiser: Beer Against Climate Change

Post pobrano z: ABInbev Budweiser: Beer Against Climate Change
Outdoor, Print
ABInbev

To face climate change the planet needs less hashtags and more actions. Therefore, Budweiser is committed to brewing its beer with 100% renewable energy. Below, you will find a series of prints that prove it.

Advertising Agency:DraftLine Maz, Colombia
Creative Director:Valentin Villamil, Iván Rivera, Camilo Ruano
Copywriter:Sebastián Pelaez, Sergio Colmenares
Art Director:Cristian Jimenez
Postproduction:La Salvación Studio

How to Create a Wrapped Ribbon Text Effect in Adobe Illustrator

Post pobrano z: How to Create a Wrapped Ribbon Text Effect in Adobe Illustrator

Final product image
What You’ll Be Creating

In the following steps, you will learn how to create a wrapped ribbon text effect in Adobe Illustrator.

For starters, you will learn how to set up a simple grid and how to easily add and center a piece of text. Next, using basic tools and effects along with some basic vector shape-building techniques, you will learn how to create your pack of ribbon shapes. 

Moving on, you will learn how to easily save and edit actions in Adobe Illustrator and how to make your work easier by using them. Finally, you will learn how to add a subtle texture and some shading using basic blending techniques and some effects.

1. Create a New Document and Set Up a Grid

Hit Control-N to create a new document. Select Pixels from the Units drop-down menu, enter 800 in the width box and 400 in the height box, and then click on the Advanced button. Select RGB, Screen (72 ppi) and make sure that the Align New Objects to Pixel Grid box is unchecked before you click OK.

Enable the Grid (View > Show Grid) and the Snap to Grid (View > Snap to Grid). You will need a grid every 1 px, so simply go to Edit > Preferences > Guides > Grid, and enter 1 in the Gridline every box and 1 in the Subdivisions box. Try not to get discouraged by all that grid—it will make your work easier, and keep in mind that you can easily enable or disable it using the Control-„ keyboard shortcut.

You should also open the Info panel (Window > Info) for a live preview with the size and position of your shapes. Don’t forget to set the unit of measurement to pixels from Edit > Preferences > Units > General. All these options will significantly increase your work speed.

setup grid

2. Create Striped Text

Step 1

Pick the Type Tool (T) and focus on your Toolbar. Remove the color from the stroke, and then select the fill and set its color to R=241 G=90 B=35. Move to your artboard, click on it, and add the „RIBBON” piece of text. Use the Berlin Sans FB Demi Bold font with the size set to 140 px.

Once you’re done, you need to center this text, so open the Align panel (Window > Align). First, set the aligning to artboard (open the fly-out menu and go to Show Options if you can’t see the Align To section as shown in the following image), and then simply click the Horizontal Align Center and Vertical Align Center buttons. In the end, your text should be placed in the center of the artboard, as shown in the following image.

Making sure that it’s still selected, go to Type > Create Outlines (Shift-Control-O). Ungroup the resulting group using the Shift-Control-G keyboard shortcut and then turn your letter shapes into a simple compound path using the Control-8 keyboard shortcut.

add text

Step 2

Using the Rectangle Tool (M), create a 511 x 5 px shape, place it as shown in the first image, and pick a random blue for the fill color. Make sure that this new shape stays selected and go to Effect > Distort & Transform > Transform. Drag the Move Vertical slider to 8 px, enter 11 in that Copies box, and then hit the OK button.

blue rectangle

Step 3

Make sure that your blue shape is still selected, go to Object > Expand Appearance, and then turn the resulting group of shapes into a simple compound path using that same Control-8 keyboard shortcut.

Select both compound paths made so far, open the Pathfinder panel (Window > Pathfinder), and click the Intersect button. Ungroup (Shift-Control-G) the resulting group and turn all those blue shapes into a new compound path (Control-8).

intersect

Step 4

Now, you need to adjust some of the blue shapes that make up your text. Using the Rectangle Tool (M), add a bunch of simple rectangles in the areas highlighted in the following image. Once you’re done, select all these new shapes and turn them into a compound path using that same Control-8 keyboard shortcut.

adjust

Step 5

Disable the Grid (Control-’) and the Snap to Grid (Shift-Control-’). Reselect both compound paths made so far and click the Unite button from the Pathfinder panel. In the end, your set of blue shapes should look like the second image.

unite

3. Adjust Your Striped Text

Step 1

For this step, you need to take a closer look at your blue shapes and get rid of the unnecessary anchor points.

Pick the Delete Anchor Point Tool (-) and simply click on the anchor points that you wish to remove. Take a good look at each shape, and when possible make sure that your shape doesn’t have more than four anchor points.

delete anchor point

Step 2

Take a look at your Layers panel (Window > Layers) and make sure that your blue shapes are grouped. Select this group and go to Effect > Distort & Transform > Roughen. Enter the attributes shown in the following image and then click the OK button.

roughen

Step 3

Make sure that your group of blue shapes is still selected and go to Object > Expand Appearance. Select the resulting group and Ungroup it using the Shift-Control-G keyboard shortcut. You’ll have to hit this command twice so that you can get rid of all those groups and subgroups.

Once you’re done, select all your blue shapes and go to Effect > Warp > Arch. Enter the properties shown below, click the OK button, and then go Effect > Warp > Flag. Enter the attributes shown in the following image, hit the OK button, and then go to Object > Expand Appearance.

warp

Step 4

Duplicate some of your blue shapes and spread the copies roughly, as shown in the following images.

spread

4. Save the First Action

Step 1

Open the Actions (Window > Actions) panel and simply hit the Create New Set button to create a new set of actions. Name it „Ribbon Actions„.

actions new set

Step 2

Now, let’s create the first actions. You’ll have to be a bit more careful in these steps and follow the instructions exactly to make sure that you’re recording the correct commands. 

Focus on one of your blue shapes, select it, focus on the Actions panel, and hit the Create New Action button. Name it „Black 1„, select F2 for the keyboard shortcut, pick red for the color, and then hit the Record button. 

Keep focusing on your selected shape and make a copy in front (Control-C > Control-F). Make sure that only the newly made copy is selected and replace the existing fill color with R=255 G=123 B=172.

new action

Step 3

Be sure that your pink shape is still selected and go to Object > Transform > Move. Enter the properties shown in the following image, click the Copy button, and make sure that the resulting shape stays selected. Next, go to Select > Same > Fill Color to select both pick shapes, and then click the Minus Front button from the Pathfinder panel.

new action

Step 4

Make sure that the thin shape made in the previous step is still selected, and focus on the Appearance panel (Window > Appearance). Replace the existing fill color with black, lower the Opacity to 50%, and change the Blending Mode to Soft Light

Finally, return to the Actions panel. Simply hit that Stop Recording button and you have your first action.

new action

Step 5

Select one of your other blue shapes, simply hit the F2 button from your keyboard and your „Black 1” action will do the work for you. Select the rest of the shapes one by one and repeat this technique.

play action

5. Save Four More Actions

Step 1

Reselect one of your blue shapes, return to the Actions panel and hit again that Create New Action button to create a second action. Name it „Black 2„, select F3 for the keyboard shortcut, pick red for the color, and then hit the Record button.

Keep focusing on your selected shape and make a copy in front (Control-C > Control-F). Select the copy and replace the existing fill color with R=255 G=0 B=255, and then go to Object > Transform > Move. Enter the properties shown in the following image and then click the Copy button.

Make sure that the newly made shape is selected and go to Select > Same > Fill Color to select both magenta shapes, and then click the Minus Front button from the Pathfinder panel. Be sure that the resulting shape stays selected and focus on the Appearance panel. Replace the existing fill color with black, lower the Opacity to 10%, and change the Blending Mode to Soft Light.

Finally, return to the Actions panel, hit that Stop Recording button, and you have your second action.

second action

Step 2

Select the rest of your blue shapes one by one and use the F3 keyboard shortcut to easily play the „Black 2” action.

play action

Step 3

Reselect one of your short blue shapes, return to the Actions panel, and hit that Create New Action button again to create a third action. Name it „White 1 S„, select F4 for the keyboard shortcut, pick green for the color, and then hit the Record button.

Keep focusing on your selected shape and make a copy in front (Control-C > Control-F). Select the copy and replace the existing fill color with R=34 G=181 B=115, and then go to Object > Transform > Rotate. Set the Angle to -25 degrees and then click the Copy button.

Make sure that the newly made shape is selected and go to Select > Same > Fill Color to select both green shapes, and then click the Intersect button from the Pathfinder panel. Be sure that the resulting shape stays selected and focus on the Appearance panel. Replace the existing fill color with white, lower the Opacity to 25%, and change the Blending Mode to Soft Light.

Finally, return to the Actions panel, hit that Stop Recording button, and you have your third action.

third action

Step 4

Make sure that your „White 1 S” action is selected, open the fly-out menu of the Actions panel, and go to Duplicate. Rename the newly added action „White 1 L” and expand it so that you can see all the registered commands.

Be sure that one of your blue shapes is selected and simply double-click that Rotate command that lies inside your „White 1 L” action to edit it. Set the Angle to -15 degrees and then click the Copy button. Return to your artboard and get rid of that newly made shape.

edit action

Step 5

Keep focusing on the Actions panel, double-click on your „White 1 L” action and set the keyboard shortcut to F5. Select one of your long blue shapes and use this keyboard shortcut to easily add a highlight like the one shown in the second image.

play action

Step 6

Select the rest of your blue shapes one by one, and use the „White 1 S” action (F4) for the short ones and the „White 1 L” action (F5) for the long ones.

play action

Step 7

Reselect one of your short blue shapes, return to the Actions panel, and hit that Create New Action button again to save one more action. Name it „White 2„, select F6 for the keyboard shortcut, pick blue for the color, and then hit the Record button.

Keep focusing on your selected shape and make a copy in front (Control-C > Control-F). Select the copy and replace the existing fill color with R=140 G=98 B=57 and then go to Object > Transform > Move. Enter the properties shown in the following image and then click the Copy button.

Making sure that your newly made shape is selected, go to Select > Same > Fill Color to select both brown shapes, and then click the Minus Front button from the Pathfinder panel. Be sure that the resulting shape stays selected and focus on the Appearance panel. Replace the existing fill color with white, lower the Opacity to 40%, and change the Blending Mode to Overlay.

Finally, return to the Actions panel, hit that Stop Recording button, and you have your final action.

fourth action

Step 8

Select the rest of your blue shapes one by one and use the F6 keyboard shortcut to easily play that „White 2” action.

Open the fly-out menu of the Actions panel and go to Button Mode if you wish to understand the use of the colors that you selected for your saved actions.

play action

6. Create the Back Pieces

Step 1

Enable the Smart Guides (Control-U) and then focus on the Layers panel (Window > Layers).

Double-click on the existing layer and name it „FRONT„. Add a second layer using the Add New Layer button and name it „BACK„. Make sure that it stays selected and drag it below your „FRONT” layer.

Focus on the blue shapes in the top right that make up the „N” letter. Using the Pen Tool (P), draw a simple, oblique shape that connects the top blue shape with the one that lies below. The Smart Guides feature will make things a lot easier for you. Once you have this shape, set its fill color to R=7 G=80 B=120.

smart guides

Step 2

Repeat the technique mentioned in the previous step and add a bunch of new shapes that connect your blue shapes roughly as shown in the following images.

back shapes

Step 3

Using the same tool and color, add a set of wavy shapes that connect the scattered blue shapes with the main ones, roughly as shown in the following image.

back wavy shapes

7. Add a Subtle Texture and a Simple Shadow

Step 1

Focus on the Layers panel, duplicate your „BACK” layer, select the copy, rename it „TEXTURE„, and drag it to the top of the panel. Duplicate all the blue shapes that lie inside your „FRONT” layer, select the copies, and drag them inside the „TEXTURE” layer.

new layer

Step 2

Reselect all the shapes that lie inside your „TEXTURE” layer and click the Unite button from the Pathfinder panel. Turn the resulting group of shapes into a compound path (Control-8), make sure that it stays selected, focus on the Appearance panel, and replace the existing fill color with black.

black compound path

Step 3

Make sure that your black compound path stays selected and focus on the Appearance panel. Lower the Opacity to 10%, change the Blending Mode to Soft Light, and then go to Effect > Distort > Glass. Enter the attributes shown in the following image and click the OK button.

film grain

Step 4

Focus on the Layers panel, duplicate your „TEXTURE” layer, select the copy, rename it „SHADOW„, and drag it to the bottom of the panel.

Select the compound path that lies inside your „SHADOW” layer and focus on the Appearance panel.

Get rid of that Film Grain effect, restore the Blending Mode to Normal, and increase the Opacity back to 100%. Replace the existing fill color with R=7 G=80 B=120 and then go to Effect > Stylize > Drop Shadow. Enter the attributes shown in the left window (in the following image), click the OK button, and then go again to Effect > Stylize > Drop Shadow. Enter the properties shown in the right window and then hit the OK button.

shadow

Congratulations! You’re Done!

Here is how it should look. I hope you’ve enjoyed this tutorial and can apply these techniques in your future projects.

final product

Agregator najlepszych postów o designie, webdesignie, cssie i Internecie