Categories
Uncategorized

How To Test Your React Apps With The React Testing Library

How To Test Your React Apps With The React Testing Library

How To Test Your React Apps With The React Testing Library

Chidi Orji

2020-07-03T12:30:00+00:00
2020-07-05T00:11:03+00:00

Today, we’ll briefly discuss why it’s important to write automated tests for any software project, and shed light on some of the common types of automated testing. We’ll build a to-do list app by following the Test-Driven Development (TDD) approach. I’ll show you how to write both unit and functional tests, and in the process, explain what code mocks are by mocking a few libraries. I’ll be using a combination of RTL and Jest — both of which come pre-installed in any new project created with Create-React-App (CRA).

To follow along, you need to know how to set up and navigate a new React project and how to work with the yarn package manager (or npm). Familiarities with Axios and React-Router are also required.

Best Practices With React

React is a fantastic JavaScript library for building rich user interfaces. It provides a great component abstraction for organizing your interfaces into well-functioning code, and there’s just about anything you can use it for. Read more articles on React →

Why You Should Test Your Code

Before shipping your software to end-users, you first have to confirm that it is working as expected. In other words, the app should satisfy its project specifications.

Just as it is important to test our project as a whole before shipping it to end-users, it’s also essential to keep testing our code during the lifetime of a project. This is necessary for a number of reasons. We may make updates to our application or refactor some parts of our code. A third-party library may undergo a breaking change. Even the browser that is running our web application may undergo breaking changes. In some cases, something stops working for no apparent reason — things could go wrong unexpectedly. Thus, it is necessary to test our code regularly for the lifetime of a project.

Broadly speaking, there are manual and automated software tests. In a manual test, a real user performs some action on our application to verify that they work correctly. This kind of test is less reliable when repeated several times because it’s easy for the tester to miss some details between test runs.

In an automated test, however, a test script is executed by a machine. With a test script, we can be sure that whatever details we set in the script will remain unchanged on every test run.

This kind of test gives us the benefits of being predictable and fast, such that we can quickly find and fix bugs in our code.

Having seen the necessity of testing our code, the next logical question is, what sort of automated tests should we write for our code? Let’s quickly go over a few of them.

Types Of Automated Testing

There are many different types of automated software testing. Some of the most common ones are unit tests, integration tests, functional tests, end-to-end tests, acceptance tests, performance tests, and smoke tests.

  1. Unit test
    In this kind of test, the goal is to verify that each unit of our application, considered in isolation, is working correctly. An example would be testing that a particular function returns an expected value, give some known inputs. We’ll see several examples in this article.
  2. Smoke test
    This kind of test is done to check that the system is up and running. For example, in a React app, we could just render our main app component and call it a day. If it renders correctly we can be fairly certain that our app would render on the browser.
  3. Integration test
    This sort of test is carried out to verify that two or more modules can work well together. For example, you might run a test to verify that your server and database are actually communicating correctly.
  4. Functional test
    A functional test exists to verify that the system meets its functional specification. We’ll see an example later.
  5. End-to-end test
    This kind of test involves testing the application the same way it would be used in the real world. You can use a tool like cypress for E2E tests.
  6. Acceptance test
    This is usually done by the business owner to verify that the system meets specifications.
  7. Performance test
    This sort of testing is carried out to see how the system performs under significant load. In frontend development, this is usually about how fast the app loads on the browser.

There’s more here if you’re interested.

Why Use React Testing Library?

When it comes to testing React applications, there are a few testing options available, of which the most common ones I know of are Enzyme and React Testing Library (RTL).

RTL is a subset of the @testing-library family of packages. Its philosophy is very simple. Your users don’t care whether you use redux or context for state management. They care less about the simplicity of hooks nor the distinction between class and functional components. They just want your app to work in a certain way. It is, therefore, no surprise that the testing library’s primary guiding principle is

“The more your tests resemble the way your software is used, the more confidence they can give you.”

So, whatever you do, have the end-user in mind and test your app just as they would use it.

Choosing RTL gives you a number of advantages. First, it’s much easier to get started with it. Every new React project bootstrapped with CRA comes with RTL and Jest configured. The React docs also recommend it as the testing library of choice. Lastly, the guiding principle makes a lot of sense — functionality over implementation details.

With that out of the way, let’s get started with building a to-do list app, following the TDD approach.

Project Setup

Open a terminal and copy and run the below command.

# start new react project and start the server
npx create-react-app start-rtl && cd start-rtl && yarn start

This should create a new React project and start the server on http://localhost:3000. With the project running, open a separate terminal, run yarn test and then press a. This runs all tests in the project in watch mode. Running the test in watch mode means that the test will automatically re-run when it detects a change in either the test file or the file that is being tested. On the test terminal, you should see something like the picture below:

Initial test passing

Initial test passing. (Large preview)

You should see a lot of greens, which indicates that the test we’re running passed in flying colors.

As I mentioned earlier, CRA sets up RTL and Jest for every new React project. It also includes a sample test. This sample test is what we just executed.

When you run the yarn test command, react-scripts calls upon Jest to execute the test. Jest is a JavaScript testing framework that’s used in running tests. You won’t find it listed in package.json but you can do a search inside yarn.lock to find it. You can also see it in node_modules/.

Jest is incredible in the range of functionality that it provides. It provides tools for assertions, mocking, spying, etc. I strongly encourage you to take at least a quick tour of the documentation. There’s a lot to learn there that I cannot scratch in this short piece. We’ll be using Jest a lot in the coming sections.

Open package.json let’s see what we have there. The section of interest is dependencies.

  "dependencies": {
    "@testing-library/jest-dom": "^4.2.4",
    "@testing-library/react": "^9.3.2",
    "@testing-library/user-event": "^7.1.2",
    ...
  },

We have the following packages installed specifically for testing purpose:

  1. @testing-library/jest-dom: provides custom DOM element matchers for Jest.
  2. @testing-library/react: provides the APIs for testing React apps.
  3. @testing-library/user-event: provides advanced simulation of browser interactions.

Open up App.test.js let’s take a look at its content.

import React from 'react';
import { render } from '@testing-library/react';
import App from './App';

test('renders learn react link', () => {
  const { getByText } = render();
  const linkElement = getByText(/learn react/i);
  expect(linkElement).toBeInTheDocument();
});

The render method of RTL renders the <App /> component and returns an object which is de-structured for the getByText query. This query finds elements in the DOM by their display text. Queries are the tools for finding elements in the DOM. The complete list of queries can be found here. All of the queries from the testing library are exported by RTL, in addition to the render, cleanup, and act methods. You can read more about these in the API section.

The text is matched with the regular expression /learn react/i. The i flag makes the regular expression case-insensitive. We expect to find the text Learn React in the document.

All of this mimics the behavior a user would experience in the browser when interacting with our app.

Let’s start making the changes required by our app. Open App.js and replace the content with the below code.

import React from "react";
import "./App.css";
function App() {
  return (
    <div className="App">
      <header className="App-header">
        <h2>Getting started with React testing library</h2>
      </header>
    </div>
  );
}
export default App;

If you still have the test running, you should see the test fail. Perhaps you can guess why that is the case, but we’ll return to it a bit later. Right now I want to refactor the test block.

Replace the test block in src/App.test.js with the code below:

# use describe, it pattern
describe("<App />", () => {
  it("Renders <App /> component correctly", () => {
    const { getByText } = render(<App />);
    expect(getByText(/Getting started with React testing library/i)).toBeInTheDocument();
  });
});

This refactor makes no material difference to how our test will run. I prefer the describe and it pattern as it allows me structure my test file into logical blocks of related tests. The test should re-run and this time it will pass. In case you haven’t guessed it, the fix for the failing test was to replace the learn react text with Getting started with React testing library.

In case you don’t have time to write your own styles you can just copy the one below into App.css.

.App {
  min-height: 100vh;
  text-align: center;
}
.App-header {
  height: 10vh;
  display: flex;
  background-color: #282c34;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  font-size: calc(10px + 2vmin);
  color: white;
}
.App-body {
  width: 60%;
  margin: 20px auto;
}
ul {
  padding: 0;
  display: flex;
  list-style-type: decimal;
  flex-direction: column;
}
li {
  font-size: large;
  text-align: left;
  padding: 0.5rem 0;
}
li a {
  text-transform: capitalize;
  text-decoration: none;
}
.todo-title {
  text-transform: capitalize;
}
.completed {
  color: green;
}
.not-completed {
  color: red;
}

You should already see the page title move up after adding this CSS.

I consider this a good point for me to commit my changes and push to Github. The corresponding branch is 01-setup.

Let’s continue with our project setup. We know we’re going to need some navigation in our app so we need React-Router. We’ll also be making API calls with Axios. Let’s install both.

# install react-router-dom and axios
yarn add react-router-dom axios

Most React apps you’ll build will have to maintain state. There’s a lot of libraries available for managing state. But for this tutorial, I’ll be using React’s context API and the useContext hook. So let’s set up our app’s context.

Create a new file src/AppContext.js and enter the below content.

import React from "react";
export const AppContext = React.createContext({});

export const AppProvider = ({ children }) => {
  const reducer = (state, action) => {
    switch (action.type) {
      case "LOAD_TODOLIST":
        return { ...state, todoList: action.todoList };
      case "LOAD_SINGLE_TODO":
        return { ...state, activeToDoItem: action.todo };
      default:
        return state;
    }
  };
  const [appData, appDispatch] = React.useReducer(reducer, {
    todoList: [],
    activeToDoItem: { id: 0 },
  });
  return (
    <AppContext.Provider value={{ appData, appDispatch }}>
      {children}
    </AppContext.Provider>
  );
};

Here we create a new context with React.createContext({}), for which the initial value is an empty object. We then define an AppProvider component that accepts children component. It then wraps those children in AppContext.Provider, thus making the { appData, appDispatch } object available to all children anywhere in the render tree.

Our reducer function defines two action types.

  1. LOAD_TODOLIST which is used to update the todoList array.
  2. LOAD_SINGLE_TODO which is used to update activeToDoItem.

appData and appDispatch are both returned from the useReducer hook. appData gives us access to the values in the state while appDispatch gives us a function which we can use to update the app’s state.

Now open index.js, import the AppProvider component and wrap the <App /> component with <AppProvider />. Your final code should look like what I have below.

import { AppProvider } from "./AppContext";

ReactDOM.render(
  <React.StrictMode>
    <AppProvider>
      <App />
    </AppProvider>
  </React.StrictMode>,
  document.getElementById("root")
);

Wrapping <App /> inside <AppProvider /> makes AppContext available to every child component in our app.

Remember that with RTL, the aim is to test our app the same way a real user would interact with it. This implies that we also want our tests to interact with our app state. For that reason, we also need to make our <AppProvider /> available to our components during tests. Let’s see how to make that happen.

The render method provided by RTL is sufficient for simple components that don’t need to maintain state or use navigation. But most apps require at least one of both. For this reason, it provides a wrapper option. With this wrapper, we can wrap the UI rendered by the test renderer with any component we like, thus creating a custom render. Let’s create one for our tests.

Create a new file src/custom-render.js and paste the following code.

import React from "react";
import { render } from "@testing-library/react";
import { MemoryRouter } from "react-router-dom";

import { AppProvider } from "./AppContext";

const Wrapper = ({ children }) => {
  return (
    <AppProvider>
      <MemoryRouter>{children}</MemoryRouter>
    </AppProvider>
  );
};

const customRender = (ui, options) =>
  render(ui, { wrapper: Wrapper, ...options });

// re-export everything
export * from "@testing-library/react";

// override render method
export { customRender as render };

Here we define a <Wrapper /> component that accepts some children component. It then wraps those children inside <AppProvider /> and <MemoryRouter />. MemoryRouter is

A <Router> that keeps the history of your “URL” in memory (does not read or write to the address bar). Useful in tests and non-browser environments like React Native.

We then create our render function, providing it the Wrapper we just defined through its wrapper option. The effect of this is that any component we pass to the render function is rendered inside <Wrapper />, thus having access to navigation and our app’s state.

The next step is to export everything from @testing-library/react. Lastly, we export our custom render function as render, thus overriding the default render.

Note that even if you were using Redux for state management the same pattern still applies.

Let’s now make sure our new render function works. Import it into src/App.test.js and use it to render the <App /> component.

Open App.test.js and replace the import line. This

import { render } from '@testing-library/react';

should become

import { render } from './custom-render';

Does the test still pass? Good job.

There’s one small change I want to make before wrapping up this section. It gets tiring very quickly to have to write const { getByText } and other queries every time. So, I’m going to be using the screen object from the DOM testing library henceforth.

Import the screen object from our custom render file and replace the describe block with the code below.

import { render, screen } from "./custom-render";

describe("<App />", () => {
  it("Renders <App /> component correctly", () => {
    render(<App />);
    expect(
      screen.getByText(/Getting started with React testing library/i)
    ).toBeInTheDocument();
  });
});

We’re now accessing the getByText query from the screen object. Does your test still pass? I’m sure it does. Let’s continue.

If your tests don’t pass you may want to compare your code with mine. The corresponding branch at this point is 02-setup-store-and-render.

Testing And Building The To-Do List Index Page

In this section, we’ll pull to-do items from http://jsonplaceholder.typicode.com/. Our component specification is very simple. When a user visits our app homepage,

  1. show a loading indicator that says Fetching todos while waiting for the response from the API;
  2. display the title of 15 to-do items on the screen once the API call returns (the API call returns 200). Also, each item title should be a link that will lead to the to-do details page.

Following a test-driven approach, we’ll write our test before implementing the component logic. Before doing that we’ll need to have the component in question. So go ahead and create a file src/TodoList.js and enter the following content:

import React from "react";
import "./App.css";
export const TodoList = () => {
  return (
    <div>
    </div>
  );
};

Since we know the component specification we can test it in isolation before incorporating it into our main app. I believe it’s up to the developer at this point to decide how they want to handle this. One reason you might want to test a component in isolation is so that you don’t accidentally break any existing test and then having to fight fires in two locations. With that out of the way let’s now write the test.

Create a new file src/TodoList.test.js and enter the below code:

import React from "react";
import axios from "axios";
import { render, screen, waitForElementToBeRemoved } from "./custom-render";
import { TodoList } from "./TodoList";
import { todos } from "./makeTodos";

describe("<App />", () => {
  it("Renders <TodoList /> component", async () => {
    render(<TodoList />);
    await waitForElementToBeRemoved(() => screen.getByText(/Fetching todos/i));

    expect(axios.get).toHaveBeenCalledTimes(1);
    todos.slice(0, 15).forEach((td) => {
      expect(screen.getByText(td.title)).toBeInTheDocument();
    });
  });
});

Inside our test block, we render the <TodoList /> component and use the waitForElementToBeRemoved function to wait for the Fetching todos text to disappear from the screen. Once this happens we know that our API call has returned. We also check that an Axios get call was fired once. Finally, we check that each to-do title is displayed on the screen. Note that the it block receives an async function. This is necessary for us to be able to use await inside the function.

Each to-do item returned by the API has the following structure.

{
  id: 0,
  userId: 0,
  title: 'Some title',
  completed: true,
}

We want to return an array of these when we

import { todos } from "./makeTodos"

The only condition is that each id should be unique.

Create a new file src/makeTodos.js and enter the below content. This is the source of todos we’ll use in our tests.

const makeTodos = (n) => {
  // returns n number of todo items
  // default is 15
  const num = n || 15;
  const todos = [];
  for (let i = 0; i < num; i++) {
    todos.push({
      id: i,
      userId: i,
      title: `Todo item ${i}`,
      completed: [true, false][Math.floor(Math.random() * 2)],
    });
  }
  return todos;
};

export const todos = makeTodos(200);

This function simply generates a list of n to-do items. The completed line is set by randomly choosing between true and false.

Unit tests are supposed to be fast. They should run within a few seconds. Fail fast! This is one of the reasons why letting our tests make actual API calls is impractical. To avoid this we mock such unpredictable API calls. Mocking simply means replacing a function with a fake version, thus allowing us to customize the behavior. In our case, we want to mock the get method of Axios to return whatever we want it to. Jest already provides mocking functionality out of the box.

Let’s now mock Axios so it returns this list of to-dos when we make the API call in our test. Create a file src/__mocks__/axios.js and enter the below content:

import { todos } from "../makeTodos";

export default {
  get: jest.fn().mockImplementation((url) => {
    switch (url) {
      case "https://jsonplaceholder.typicode.com/todos":
        return Promise.resolve({ data: todos });
      default:
        throw new Error(`UNMATCHED URL: ${url}`);
    }
  }),
};

When the test starts, Jest automatically finds this mocks folder and instead of using the actual Axios from node_modules/ in our tests, it uses this one. At this point, we’re only mocking the get method using Jest’s mockImplementation method. Similarly, we can mock other Axios methods like post, patch, interceptors, defaults etc. Right now they’re all undefined and any attempt to access, axios.post for example, would result in an error.

Note that we can customize what to return based on the URL the Axios call receives. Also, Axios calls return a promise which resolves to the actual data we want, so we return a promise with the data we want.

At this point, we have one passing test and one failing test. Let’s implement the component logic.

Open src/TodoList.js let’s build out the implementation piece by piece. Start by replacing the code inside with this one below.

import React from "react";
import axios from "axios";
import { Link } from "react-router-dom";
import "./App.css";
import { AppContext } from "./AppContext";

export const TodoList = () => {
  const [loading, setLoading] = React.useState(true);
  const { appData, appDispatch } = React.useContext(AppContext);

  React.useEffect(() => {
    axios.get("https://jsonplaceholder.typicode.com/todos").then((resp) => {
      const { data } = resp;
      appDispatch({ type: "LOAD_TODOLIST", todoList: data });
      setLoading(false);
    });
  }, [appDispatch, setLoading]);

  return (
    <div>
      // next code block goes here
    </div>
  );
};

We import AppContext and de-structure appData and appDispatch from the return value of React.useContext. We then make the API call inside a useEffect block. Once the API call returns, we set the to-do list in state by firing the LOAD_TODOLIST action. Finally, we set the loading state to false to reveal our to-dos.

Now enter the final piece of code.

{loading ? (
  <p>Fetching todos</p>
) : (
  <ul>
    {appData.todoList.slice(0, 15).map((item) => {
      const { id, title } = item;
      return (
        <li key={id}>
          <Link to={`/item/${id}`} data-testid={id}>
            {title}
          </Link>
        </li>
      );
    })}
  </ul>
)}

We slice appData.todoList to get the first 15 items. We then map over those and render each one in a <Link /> tag so we can click on it and see the details. Note the data-testid attribute on each Link. This should be a unique ID that will aid us in finding individual DOM elements. In a case where we have similar text on the screen, we should never have the same ID for any two elements. We’ll see how to use this a bit later.

My tests now pass. Does yours pass? Great.

Let’s now incorporate this component into our render tree. Open up App.js let’s do that.

First things. Add some imports.

import { BrowserRouter, Route } from "react-router-dom";
import { TodoList } from "./TodoList";

We need BrowserRouter for navigation and Route for rendering each component in each navigation location.

Now add the below code after the <header /> element.

<div className="App-body">
  <BrowserRouter>
    <Route exact path="/" component={TodoList} />
  </BrowserRouter>
</div>

This is simply telling the browser to render the <TodoList /> component when we’re on the root location, /. Once this is done, our tests still pass but you should see some error messages on your console telling you about some act something. You should also see that the <TodoList /> component seems to be the culprit here.

Terminal showing act warnings

Terminal showing act warnings. (Large preview)

Since we’re sure that our TodoList component by itself is okay, we have to look at the App component, inside of which is rendered the <TodoList /> component.

This warning may seem complex at first but it is telling us that something is happening in our component that we’re not accounting for in our test. The fix is to wait for the loading indicator to be removed from the screen before we proceed.

Open up App.test.js and update the code to look like so:

import React from "react";
import { render, screen, waitForElementToBeRemoved } from "./custom-render";
import App from "./App";
describe("<App />", () => {
  it("Renders <App /> component correctly", async () => {
    render(<App />);
    expect(
      screen.getByText(/Getting started with React testing library/i)
    ).toBeInTheDocument();
    await waitForElementToBeRemoved(() => screen.getByText(/Fetching todos/i));
  });
});

We’ve made two changes. First, we changed the function in the it block to an async function. This is a necessary step to allow us to use await in the function body. Secondly, we wait for the Fetching todos text to be removed from the screen. And voila!. The warning is gone. Phew! I strongly advise that you bookmark this post by Kent Dodds for more on this act warning. You’re gonna need it.

Now open the page in your browser and you should see the list of to-dos. You can click on an item if you like, but it won’t show you anything because our router doesn’t yet recognize that URL.

For comparison, the branch of my repo at this point is 03-todolist.

Let’s now add the to-do details page.

Testing And Building The Single To-Do Page

To display a single to-do item we’ll follow a similar approach. The component specification is simple. When a user navigates to a to-do page:

  1. display a loading indicator that says Fetching todo item id where id represents the to-do’s id, while the API call to https://jsonplaceholder.typicode.com/todos/item_id runs.
  2. When the API call returns, show the following information:
    • Todo item title
    • Added by: userId
    • This item has been completed if the to-do has been completed or
    • This item is yet to be completed if the to-do has not been completed.

Let’s start with the component. Create a file src/TodoItem.js and add the following content.

import React from "react";
import { useParams } from "react-router-dom";

import "./App.css";

export const TodoItem = () => {
  const { id } = useParams()
  return (
    <div className="single-todo-item">
    </div>
  );
};

The only thing new to us in this file is the const { id } = useParams() line. This is a hook from react-router-dom that lets us read URL parameters. This id is going to be used in fetching a to-do item from the API.

This situation is a bit different because we’re going to be reading the id from the location URL. We know that when a user clicks a to-do link, the id will show up in the URL which we can then grab using the useParams() hook. But here we’re testing the component in isolation which means that there’s nothing to click, even if we wanted to. To get around this we’ll have to mock react-router-dom, but only some parts of it. Yes. It’s possible to mock only what we need to. Let’s see how it’s done.

Create a new mock file src/__mocks__ /react-router-dom.js. Now paste in the following code:

module.exports = {
  ...jest.requireActual("react-router-dom"),
  useParams: jest.fn(),
};

By now you should have noticed that when mocking a module we have to use the exact module name as the mock file name.

Here, we use the module.exports syntax because react-router-dom has mostly named exports. (I haven’t come across any default export since I’ve been working with it. If there are any, kindly share with me in the comments). This is unlike Axios where everything is bundled as methods in one default export.

We first spread the actual react-router-dom, then replace the useParams hook with a Jest function. Since this function is a Jest function, we can modify it anytime we want. Keep in mind that we’re only mocking the part we need to because if we mock everything, we’ll lose the implementation of MemoryHistory which is used in our render function.

Let’s start testing!

Now create src/TodoItem.test.js and enter the below content:

import React from "react";
import axios from "axios";
import { render, screen, waitForElementToBeRemoved } from "./custom-render";
import { useParams, MemoryRouter } from "react-router-dom";
import { TodoItem } from "./TodoItem";

describe("<TodoItem />", () => {
  it("can tell mocked from unmocked functions", () => {
    expect(jest.isMockFunction(useParams)).toBe(true);
    expect(jest.isMockFunction(MemoryRouter)).toBe(false);
  });
});

Just like before, we have all our imports. The describe block then follows. Our first case is only there as a demonstration that we’re only mocking what we need to. Jest’s isMockFunction can tell whether a function is mocked or not. Both expectations pass, confirming the fact that we have a mock where we want it.

Add the below test case for when a to-do item has been completed.

  it("Renders <TodoItem /> correctly for a completed item", async () => {
    useParams.mockReturnValue({ id: 1 });
    render(<TodoItem />);

    await waitForElementToBeRemoved(() =>
      screen.getByText(/Fetching todo item 1/i)
    );

    expect(axios.get).toHaveBeenCalledTimes(1);
    expect(screen.getByText(/todo item 1/)).toBeInTheDocument();
    expect(screen.getByText(/Added by: 1/)).toBeInTheDocument();
    expect(
      screen.getByText(/This item has been completed/)
    ).toBeInTheDocument();
  });

The very first thing we do is to mock the return value of useParams. We want it to return an object with an id property, having a value of 1. When this is parsed in the component, we end up with the following URL https://jsonplaceholder.typicode.com/todos/1. Keep in mind that we have to add a case for this URL in our Axios mock or it will throw an error. We will do that in just a moment.

We now know for sure that calling useParams() will return the object { id: 1 } which makes this test case predictable.

As with previous tests, we wait for the loading indicator, Fetching todo item 1 to be removed from the screen before making our expectations. We expect to see the to-do title, the id of the user who added it, and a message indicating the status.

Open src/__mocks__/axios.js and add the following case to the switch block.

      case "https://jsonplaceholder.typicode.com/todos/1":
        return Promise.resolve({
          data: { id: 1, title: "todo item 1", userId: 1, completed: true },
        });

When this URL is matched, a promise with a completed to-do is returned. Of course, this test case fails since we’re yet to implement the component logic. Go ahead and add a test case for when the to-do item has not been completed.

  it("Renders <TodoItem /> correctly for an uncompleted item", async () => {
    useParams.mockReturnValue({ id: 2 });
    render(<TodoItem />);
    await waitForElementToBeRemoved(() =>
      screen.getByText(/Fetching todo item 2/i)
    );
    expect(axios.get).toHaveBeenCalledTimes(2);
    expect(screen.getByText(/todo item 2/)).toBeInTheDocument();
    expect(screen.getByText(/Added by: 2/)).toBeInTheDocument();
    expect(
      screen.getByText(/This item is yet to be completed/)
    ).toBeInTheDocument();
  });

This is the same as the previous case. The only difference is the ID of the to-do, the userId, and the completion status. When we enter the component, we’ll need to make an API call to the URL https://jsonplaceholder.typicode.com/todos/2. Go ahead and add a matching case statement to the switch block of our Axios mock.

case "https://jsonplaceholder.typicode.com/todos/2":
  return Promise.resolve({
    data: { id: 2, title: "todo item 2", userId: 2, completed: false },
  });

When the URL is matched, a promise with an uncompleted to-do is returned.

Both test cases are failing. Now let’s add the component implementation to make them pass.

Open src/TodoItem.js and update the code to the following:

import React from "react";
import axios from "axios";
import { useParams } from "react-router-dom";
import "./App.css";
import { AppContext } from "./AppContext";

export const TodoItem = () => {
  const { id } = useParams();
  const [loading, setLoading] = React.useState(true);
  const {
    appData: { activeToDoItem },
    appDispatch,
  } = React.useContext(AppContext);

  const { title, completed, userId } = activeToDoItem;
  React.useEffect(() => {
    axios
      .get(`https://jsonplaceholder.typicode.com/todos/${id}`)
      .then((resp) => {
        const { data } = resp;
        appDispatch({ type: "LOAD_SINGLE_TODO", todo: data });
        setLoading(false);
      });
  }, [id, appDispatch]);
  return (
    <div className="single-todo-item">
      // next code block goes here.
    </div>
  );
};

As with the <TodoList /> component, we import AppContext. We read activeTodoItem from it, then we read the to-do title, userId, and completion status. After that we make the API call inside a useEffect block. When the API call returns we set the to-do in state by firing the LOAD_SINGLE_TODO action. Finally, we set our loading state to false to reveal the to-do details.

Let’s add the final piece of code inside the return div:

{loading ? (
  <p>Fetching todo item {id}</p>
) : (
  <div>
    <h2 className="todo-title">{title}</h2>
    <h4>Added by: {userId}</h4>
    {completed ? (
      <p className="completed">This item has been completed</p>
    ) : (
      <p className="not-completed">This item is yet to be completed</p>
    )}
  </div>
)}

Once this is done all tests should now pass. Yay! We have another winner.

Our component tests now pass. But we still haven’t added it to our main app. Let’s do that.

Open src/App.js and add the import line:

import { TodoItem } from './TodoItem'

Add the TodoItem route above the TodoList route. Be sure to preserve the order shown below.

# preserve this order
<Route path="/item/:id" component={TodoItem} />
<Route exact path="/" component={TodoList} />

Open your project in your browser and click on a to-do. Does it take you to the to-do page? Of course, it does. Good job.

In case you’re having any problem, you can check out my code at this point from the 04-test-todo branch.

Phew! This has been a marathon. But bear with me. There’s one last point I’d like us to touch. Let’s quickly have a test case for when a user visits our app, and then proceed to click on a to-do link. This is a functional test to mimic how our app should work. In practice, this is all the testing we need to be done for this app. It ticks every box in our app specification.

Open App.test.js and add a new test case. The code is a bit long so we’ll add it in two steps.

import userEvent from "@testing-library/user-event";
import { todos } from "./makeTodos";

jest.mock("react-router-dom", () => ({
  ...jest.requireActual("react-router-dom"),
}));

describe("<App />"
  ...
  // previous test case
  ...

  it("Renders todos, and I can click to view a todo item", async () => {
    render(<App />);
    await waitForElementToBeRemoved(() => screen.getByText(/Fetching todos/i));
    todos.slice(0, 15).forEach((td) => {
      expect(screen.getByText(td.title)).toBeInTheDocument();
    });
    // click on a todo item and test the result
    const { id, title, completed, userId } = todos[0];
    axios.get.mockImplementationOnce(() =>
      Promise.resolve({
        data: { id, title, userId, completed },
      })
    );
    userEvent.click(screen.getByTestId(String(id)));
    await waitForElementToBeRemoved(() =>
      screen.getByText(`Fetching todo item ${String(id)}`)
    );

    // next code block goes here
  });
});

We have two imports of which userEvent is new. According to the docs,

user-event is a companion library for the React Testing Library that provides a more advanced simulation of browser interactions than the built-in fireEvent method.”

Yes. There is a fireEvent method for simulating user events. But userEvent is what you want to be using henceforth.

Before we start the testing process, we need to restore the original useParams hooks. This is necessary since we want to test actual behavior, so we should mock as little as possible. Jest provides us with requireActual method which returns the original react-router-dom module.

Note that we must do this before we enter the describe block, otherwise, Jest would ignore it. It states in the documentation that requireActual:

“…returns the actual module instead of a mock, bypassing all checks on whether the module should receive a mock implementation or not.”

Once this is done, Jest bypasses every other check and ignores the mocked version of the react-router-dom.

As usual, we render the <App /> component and wait for the Fetching todos loading indicator to disappear from the screen. We then check for the presence of the first 15 to-do items on the page.

Once we’re satisfied with that, we grab the first item in our to-do list. To prevent any chance of a URL collision with our global Axios mock, we override the global mock with Jest’s mockImplementationOnce. This mocked value is valid for one call to the Axios get method. We then grab a link by its data-testid attribute and fire a user click event on that link. Then we wait for the loading indicator for the single to-do page to disappear from the screen.

Now finish the test by adding the below expectations in the position indicated.

expect(screen.getByText(title)).toBeInTheDocument();
expect(screen.getByText(`Added by: ${userId}`)).toBeInTheDocument();
switch (completed) {
  case true:
    expect(
      screen.getByText(/This item has been completed/)
    ).toBeInTheDocument();
    break;
  case false:
    expect(
      screen.getByText(/This item is yet to be completed/)
    ).toBeInTheDocument();
    break;
  default:
    throw new Error("No match");
    }
  

We expect to see the to-do title and the user who added it. Finally, since we can’t be sure about the to-do status, we create a switch block to handle both cases. If a match is not found we throw an error.

You should have 6 passing tests and a functional app at this point. In case you’re having trouble, the corresponding branch in my repo is 05-test-user-action.

Conclusion

Phew! That was some marathon. If you made it to this point, congratulations. You now have almost all you need to write tests for your React apps. I strongly advise that you read CRA’s testing docs and RTL’s documentation. Overall both are relatively short and direct.

I strongly encourage you to start writing tests for your React apps, no matter how small. Even if it’s just smoke tests to make sure your components render. You can incrementally add more test cases over time.

Smashing Editorial
(ks, ra, yk, il)

Source: smash mag

Categories
Uncategorized

Collective #612



60 Days of Animation

The Undead Institute offers books on HTML, CSS, Responsive Design and more that marry humor, excellent teaching, and brain-lodging practice for an experience that’ll outlive the apocalypse.

Check it out




Stryve

A new community for tech enthusiasts to share, learn, and build their careers.

Check it out


The design systems between us

Ethan Marcotte shares his thoughts on why he believes that design systems haven’t brought rich, cross-functional collaboration to most organizations.

Read it









Profiled

With Profiled you can create a developer portfolio from your GitHub account.

Check it out


Is WebP really better than JPEG?

According to Google, WebP is 25 – 34% smaller than JPEG at equivalent quality. But how much of it is really true? Find out in this article by Johannes Siipola.

Read it









The post Collective #612 appeared first on Codrops.


Source: codrops

Categories
Uncategorized

Differences Between Static Generated Sites And Server-Side Rendered Apps

Differences Between Static Generated Sites And Server-Side Rendered Apps

Differences Between Static Generated Sites And Server-Side Rendered Apps

Timi Omoyeni

2020-07-02T12:00:00+00:00
2020-07-05T00:11:03+00:00

JavaScript currently has three types of applications that you can build with: Single Page Applications (SPAs), pre-rendering/static generated sites and server-side rendered applications. SPAs come with many challenges, one of which is Search Engine Optimization (SEO). Possible solutions are to make use of Static Site Generators or Server-Side Rendering (SSR).

In this article, I’m going to explain them alongside listing their pros and cons so you have a balanced view. We’re going to look at what static generated/pre-rendering is as well as frameworks such as Gatsby and VuePress that help in creating statically generated sites. We’re also going to look at what server-side rendered (SSR) applications are as well as frameworks like Nextjs and Nuxtjs that can help you create SSR applications. Finally, we’re going to cover the differences between these two methods and which of them you should use when building your next application.

Note: You can find all the code snippets in this article on GitHub.

What Is A Static Site Generator?

A Static Site Generator (SSG) is a software application that creates HTML pages from templates or components and a given content source. You give it some text files and content, and the generator will give you back a complete website, and this completed website is referred to as a static generated site. What this means is that your site pages are generated at build time and your site content does not change unless you add new contents or components and “rebuild” or you have to rebuild your site if you want it to be updated with new content.

Diagram explaining how static site generation works

How static site generation works (Large preview)

This approach is good for building applications that the content does not change too often — sites that the content does not have to change depending on the user, and sites that do not have a lot of user-generated content. An example of such a site is a blog or a personal website. Let’s look at some advantages of using static generated sites.

PROS

  • Fast website: Since all of your site’s pages and content have been generated at build time, you do not have to worry about API calls to the server for content and this makes your site very fast.
  • Easy to deploy: After your static site has been generated, you would be left with static files, and hence, it can be easily deployed to platforms like Netlify.
  • Security: Static generated site are solely composed of static files, the risk of being vulnerable to cyber attacks is minimal. This is because static generated sites have no database, attackers cannot inject malicious code or exploit your database.
  • You can use version control software (e.g git) to manage and track changes to your content. This can come in handy when you want to roll back changes you made to the content on your site.

CONS

  • Content can become stale if it changes too quickly.
  • To update its content, you have to rebuild the site.
  • Build time would increase depending on the size of the application.

Examples of static site generators are GatsbyJS and VuePress. Let us take a look at how to create static sites using these two generators.

Gatsby

According to their official website,

“Gatsby is a free and open-source framework based on React that helps developers build blazing-fast websites and apps.”

This means developers familiar with React would find it easy to get started with Gatsby.

To use this generator, you first have to install it using NPM:

npm install -g gatsby-cli

This will install Gatsby globally on your machine, you only have to run this command once on your machine. After this installation is complete, you can create your first static site generator using the following command.

gatsby new demo-gatsby

This command will create a new Gatsby project that I have named demo-gatsby. When this is done, you can start up your app server by running the following command:

cd demo-gatsby
gatsby develop

Your Gatsby application should be running on localhost:8000.

Gatsby default landing page

Gatsby default starter page (Large preview)

The folder structure for this app looks like this;

--| gatsby-browser.js  
--| LICENSE        
--| README.md
--| gatsby-config.js
--| node_modules/  
--| src/
----| components
----| pages
----| images
--| gatsby-node.js     
--| package.json   
--| yarn.lock
--| gatsby-ssr.js      
--| public/
----| icons
----| page-data
----| static

For this tutorial, we’re only going to look at the src/pages folder. This folder contains files that would be generated into routes on your site.

To test this, let us add a new file (newPage.js) to this folder:

import React from "react"
import { Link } from "gatsby"
import Layout from "../components/layout"
import SEO from "../components/seo"
const NewPage = () => (
  <Layout>
    <SEO title="My New Page" />
    <h1>Hello Gatsby</h1>
    <p>This is my first Gatsby Page</p>
    <button>
      <Link to='/'>Home</Link>
    </button>
  </Layout>
)
export default NewPage

Here, we import React from the react package so when your code is transpiled to pure JavaScript, references to React will appear there. We also import a Link component from gatsby and this is one of React’s route tag that is used in place of the native anchor tag ( <a href='#'>Link</a>). It accepts a to prop that takes a route as a value.

We import a Layout component that was added to your app by default. This component handles the layout of pages nested inside it. We also import the SEO component into this new file. This component accepts a title prop and configures this value as part of your page’s metadata. Finally, we export the function NewPage that returns a JSX containing your new page’s content.

And in your index.js file, add a link to this new page we just created:

import React from "react"
import { Link } from "gatsby"
import Layout from "../components/layout"
import Image from "../components/image"
import SEO from "../components/seo"
const IndexPage = () => (
  <Layout>
    <SEO title="Home" />
    <h1>Hi people</h1>
    <p>Welcome to your new Gatsby site.</p>
    <p>Now go build something great.</p>
    <div style={{ maxWidth: `300px`, marginBottom: `1.45rem` }}>
      <Image />
    </div>
    <Link to="/page-2/">Go to page 2</Link>
    {/* new link */}
    <button>
      <Link to="/newPage/">Go to New Page</Link>
    </button>
  </Layout>
)
export default IndexPage

Here, we import the same components that were used in newPage.js file and they perform the same function in this file. We also import an Image component from our components folder. This component is added by default to your Gatsby application and it helps in lazy loading images and serving reduced file size. Finally, we export a function IndexPage that returns JSX containing our new link and some default content.

Now, if we open our browser, we should see our new link at the bottom of the page.

Gatsby default landing page with link to a new page

Gatsby landing page with new link (Large preview)

And if you click on Go To New Page, it should take you to your newly added page.

New page containing some texts

New gatsby page (Large preview)

VuePress

VuePress is a static site generator that is powered by Vue, Vue Router and Webpack. It requires little to no configuration for you to get started with it. While there are a number of tools that are static site generators, VuePress stands out from amongst the pack for a single reason: its primary directive is to make it easier for developers to create and maintain great documentation for their projects.

To use VuePress, you first have to install it:

//globally
yarn global add vuepress # OR npm install -g vuepress

//in an existing project
yarn add -D vuepress # OR npm install -D vuepress

Once the installation process is done, you can run the following command in your terminal:

# create the project folder
mkdir demo-vuepress && cd demo-vuepress

# create a markdown file
echo '# Hello VuePress' > README.md

# start writing
vuepress dev

Here, we create a folder for our VuePress application, add a README.md file with # Hello VuePress as the only content inside this file, and finally, start up our server.

When this is done, our application should be running on localhost:8080 and we should see this in our browser:

A VuePress webpage with a text saying ‘Hello VuePress’

VuePress landing page (Large preview)

VuePress supports VueJS syntax and markup inside this file. Update your README.md file with the following:

# Hello VuePress
_VuePress Rocks_
> **Yes!**
_It supports JavaScript interpolation code_
> **{{new Date()}}**
<p v-for="i of ['v','u', 'e', 'p', 'r', 'e', 's', 's']">{{i}}</p>

If you go back to your browser, your page should look like this:

Updated VuePress page

Updated Vuepress page (Large preview)

To add a new page to your VuePress site, you add a new markdown file to the root directory and name it whatever you want the route to be. In this case, I’ve gone ahead to name it Page-2.md and added the following to the file:

# hello World
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
consequat.

And now, if you navigate to /page-2 in your browser, we should see this:

A VuePress webpage containing hello world

A “Hello World” page in VuePress (Large preview)

What Is Server-Side Rendering? (SSR)

Server-Side Rendering (SSR), is the process of displaying web-pages on the server and passing it to the browser/client-side instead of rendering it in the browser. Server-side sends a fully rendered page to the client; the client’s JavaScript bundle takes over and allows the SPA framework to operate.

This means if you have an application that is server-side rendered, your content is fetched on the server side and passed to your browser to display to your user. With client-side rendering it is different, you would have to navigate to that page first before it fetches data from your server meaning your user would have to wait for some seconds before they’re served with the content on that page. Applications that have SSR enabled are called Server-side rendered applications.

A diagram explaining how server-side rendering works

How SSR works (Large preview)

This approach is good for building complex applications that require user interaction, rely on a database, or where the content changes very often. This is because content on these sites changes very often and the users need to see the updated content as soon as they’re updated. It is also good for applications that have tailored content depending on who is viewing it and applications where you need to store user-specific data like email and user preference while also catering for SEO. An example of this is a large e-commerce platform or a social media site. Let us look at some of the advantages of server-side rendering your applications.

Pros

  • Content is up to date because it fetches content on the go;
  • Your site loads fast because it fetches its content on the server-side before rendering it to the user;
  • Since in SSR JavaScript is rendered server-side, your users’ devices have little relevance to the load time of your page and this leads to better performance.

CONS

  • More API calls to the server since they’re made per request;
  • Cannot deploy to a static CDN.

Further examples of frameworks that offer SSR are Next.js and Nuxt.js.

Next.js

Next.js is a React.js framework that helps in building static sites, server-side rendered applications, and so on. Since it was built on React, knowledge of React is required to use this framework.

To create a Next.js app, you need to run the following:

npm init next-app
# or
yarn create next-app

You would be prompted to choose a name your application, I have named my application demo-next. The next option would be to select a template and I’ve selected the Default starter app after which it begins to set up your app. When this is done, we can now start our application

cd demo-next
yarn dev 
# or npm run dev

Your application should be running on localhost:3000 and you should see this in your browser;

Default Nextjs landing page

Next.js landing page (Large preview)

The page that is being rendered can be found in pages/index.js so if you open this file and modify the JSX inside the Home function, it would reflect in your browser. Replace the JSX with this:

import Head from 'next/head'
export default function Home() {
  return (
    <div className="container">
      <Head>
        <title>Hello Next.js</title>
        <link rel="icon" href="/favicon.ico" />
      </Head>
      <main>
        <h1 className="title">
          Welcome to <a href="https://nextjs.org">Next.js!</a>
        </h1>
        <p className='description'>Nextjs Rocks!</p>
      </main>
      <style jsx>{`
        main {
          padding: 5rem 0;
          flex: 1;
          display: flex;
          flex-direction: column;
          justify-content: center;
          align-items: center;
        }
        .title a {
          color: #0070f3;
          text-decoration: none;
        }
        .title a:hover,
        .title a:focus,
        .title a:active {
          text-decoration: underline;
        }
        .title {
          margin: 0;
          line-height: 1.15;
          font-size: 4rem;
        }
        .title,
        .description {
          text-align: center;
        }
        .description {
          line-height: 1.5;
          font-size: 1.5rem;
        }
      `}</style>
      <style jsx global>{`
        html,
        body {
          padding: 0;
          margin: 0;
          font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto,
            Oxygen, Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue,
            sans-serif;
        }
        * {
          box-sizing: border-box;
        }
      `}</style>
    </div>
  )
}

In this file, we make use of Next.js Head component to set our page’s metadata title and favicon for this page. We also export a Home function that returns a JSX containing our page’s content. This JSX contains our Head component together with our main page’s content. It also contains two style tags, one for styling this page and the other for the global styling of the app.

Now, you should see that the content on your app has changed to this:

Nextjs landing page containing ‘welcome to Nextjs’ text

Updated landing page (Large preview)

Now if we want to add a new page to our app, we have to add a new file inside the /pages folder. Routes are automatically created based on the /pages folder structure, this means that if you have a folder structure that looks like this:

--| pages
----| index.js ==> '/'
----| about.js ==> '/about'
----| projects
------| next.js ==> '/projects/next'

So in your pages folder, add a new file and name it hello.js then add the following to it:

import Head from 'next/head'
export default function Hello() {
  return (
    <div>
       <Head>
        <title>Hello World</title>
        <link rel="icon" href="/favicon.ico" />
      </Head>
      <main className='container'>
        <h1 className='title'>
         Hello <a href="https://en.wikipedia.org/wiki/Hello_World_(film)">World</a>
        </h1>
        <p className='subtitle'>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Voluptatem provident soluta, sit explicabo impedit nobis accusantium? Nihil beatae, accusamus modi assumenda, optio omnis aliquid nobis magnam facilis ipsam eum saepe!</p>
      </main>
      <style jsx> {`
      
      .container {
        margin: 0 auto;
        min-height: 100vh;
        max-width: 800px;
        text-align: center;
      }
      .title {
        font-family: "Quicksand", "Source Sans Pro", -apple-system, BlinkMacSystemFont,
          "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
        display: block;
        font-weight: 300;
        font-size: 100px;
        color: #35495e;
        letter-spacing: 1px;
      }
      .subtitle {
        font-weight: 300;
        font-size: 22px;
        color: #526488;
        word-spacing: 5px;
        padding-bottom: 15px;
      }
      `} </style>
    </div>
  )
}

This page is identical to the landing page we already have, we only changed the content and added new styling to the JSX. Now if we visit localhost:3000/hello, we should see our new page:

A Nextjs webpage containing ‘Hello world’

A “Hello World ” page in Next.js (Large preview)

Finally, we need to add a link to this new page on our index.js page, and to do this, we make use of Next’s Link component. To do that, we have to import it first.

# index.js
import Link from 'next/link'

#Add this to your JSX
<Link href='/hello'>
<Link href='/hello'>
  <a>Next</a>
</Link>

This link component is how we add links to pages created in Next in our application.

Now if we go back to our homepage and click on this link, it would take us to our /hello page.

Nuxt.js

According to their official documentation:

“Nuxt is a progressive framework based on Vue.js to create modern web applications. It is based on Vue.js official libraries (vue, vue-router and vuex) and powerful development tools (webpack, Babel and PostCSS). Nuxt’s goal is to make web development powerful and performant with a great developer experience in mind.”

It is based on Vue.js so that means Vue.js developers would find it easy getting started with it and knowledge of Vue.js is required to use this framework.

To create a Nuxt.js app, you need to run the following command in your terminal:

yarn create nuxt-app <project-name>
# or npx
npx create-nuxt-app <project-name>

This would prompt you to select a name along with some other options. I named mine demo-nuxt and selected default options for the other options. When this is done, you can open your app folder and open pages/index.vue. Every file in this folder file is turned into a route and so our landing page is controlled by index.vue file. So if you update it with the following:

<template>
  <div class="container">
    <div>
      <logo />
      <h1 class="title">
        Hello Nuxt
      </h1>
      <h2 class="subtitle">
        Nuxt.js ROcks!
      </h2>
      <div class="links">
        <a
          href="https://nuxtjs.org/"
          target="_blank"
          class="button--green"
        >
          Documentation
        </a>
        <a
          href="https://github.com/nuxt/nuxt.js"
          target="_blank"
          class="button--grey"
        >
          GitHub
        </a>
      </div>
    </div>
  </div>
</template>
<script>
import Logo from '~/components/Logo.vue'
export default {
  components: {
    Logo
  }
}
</script>
<style>
.container {
  margin: 0 auto;
  min-height: 100vh;
  display: flex;
  justify-content: center;
  align-items: center;
  text-align: center;
}
.title {
  font-family: 'Quicksand', 'Source Sans Pro', -apple-system, BlinkMacSystemFont,
    'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
  display: block;
  font-weight: 300;
  font-size: 100px;
  color: #35495e;
  letter-spacing: 1px;
}
.subtitle {
  font-weight: 300;
  font-size: 42px;
  color: #526488;
  word-spacing: 5px;
  padding-bottom: 15px;
}
.links {
  padding-top: 15px;
}
</style>

And run your application:

cd demo-nuxt
# start your applicatio
yarn dev # or npm run dev

Your application should be running on localhost:3000 and you should see this:

Default Nuxtjs landing page

Nuxt.js landing page (Large preview)

We can see that this page displays the content we added in to index.vue. The router structure works the same way Next.js router works; it renders every file inside /pages folder into a page. So let us add a new page (hello.vue) to our application.

<template>
  <div>
    <h1>Hello World!</h1>
    <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Id ipsa vitae tempora perferendis, voluptate a accusantium itaque vel ex, provident autem quod rem saepe ullam hic explicabo voluptas, libero distinctio?</p>
  </div>
</template>
<script>
export default {};
</script>
<style>
</style>

So if you open localhost:3000/hello, you should see your new page in your browser.

A Nuxtjs webpage containing ‘Hello World’

“Hello World” page in Nuxtjs (Large preview)

Taking A Closer Look At The Differences

Now that we have looked at both static-site generators and server-side rendering and how to get started with them by using some popular tools, let us look at the differences between them.

Static Sites Generators Server-Side Rendering
Can easily be deployed to a static CDN Cannot be deployed to a static CDN
Content and pages are generated at build time Content and pages are generated per request
Content can become stale quickly Content is always up to date
Fewer API calls since it only makes it at build time Makes API calls each time a new page is visited

Conclusion

We can see why it is so easy to think both static generated sites and server-side rendered applications are the same. Now that we know the differences between them are, I would advise that we try to learn more on how to build both static generated sites and server-side rendered applications in order to fully understand the differences between them.

Further Resources

Here are some useful links that are bound to help you get started in no time:

Smashing Editorial
(ks, ra, il)

Source: smash mag

Categories
Uncategorized

Information And Information Architecture: The BIG Picture

Information And Information Architecture: The BIG Picture

Information And Information Architecture: The BIG Picture

Carrie Webster

2020-07-01T12:30:00+00:00
2020-07-05T00:11:03+00:00

We are living in a world exploding with information, but how do we find what is relevant to us at the time that we need it? I believe that good information architecture is key to helping us navigate through the mountains of data and information we have created for ourselves. 

In this article, we will first describe what information architecture is, why it’s important, and approaches to effective implementation. Then we explore ideas around the broader view of the information age, how we use information, and how it impacts our world and our lives. These insights are designed to help you to understand the bigger picture, which enables us to grasp the value that good information architecture delivers to help our information-overloaded lives.

What Is Information Architecture And Why Is It Important?

“Information architecture is the practice of deciding how to arrange the parts of something to be understandable.”

The Information Architecture Institute

From a user experience perspective, this really means understanding how your users think, what problems they are trying to solve, and then presenting information in a logical way that makes sense from within this context. 

Whether it is a website, a software application or a smartphone app, it’s about first designing the structure of how your information is organized, and then translating this into a logical navigation hierarchy that makes sense to the users who will be accessing it. In this world where we can sometimes feel as though we are drowning in data, information architecture provides us with a logical way of organizing this data to make it easier to locate. 

Here are some other reasons why good information architecture is important:

For The User

  • It reduces cognitive load.
    Too much information on a screen with no clear pathway can make it difficult for a user to focus. Too many options can lead to choice deferral where a user chooses not to make a decision at all.
  • It speeds up the process of finding the right information.
    This is the opposite of choice deferral, where the user is able to easily locate what they are looking for with clear navigation choices.
  • It can keep the user focussed on the task they are trying to achieve.
    If the task a user is engaging in is easy to follow without additional non-contextual navigation elements, it’s less likely they will be distracted.
  • It makes it easier to analyze and understand information by the addition of context.
    Providing a visual navigation path of exactly where the user is within a website can provide more context for the content they are viewing. For example, during an online bank account application, displaying the total number of steps in the process and visually indicating exactly which step you are at, and what the next steps may involve gives context to the flow.
  • Reduces frustration and contacting support.
    If it is clear to the user where they can find what they need, there is no need to request help. For example, if a customer has received a purchased item that is faulty, without obvious instruction on how to rectify the situation, they may call the customer support center. 

Below are a couple of examples helping to illustrate the points about the user.

Wizard example

(Image source: Shaun Utter) (Large preview)

The example above demonstrates:

  • The use of a “wizard” style application form and illustrates many of the points above. 
  • Clear navigation steps across the top of the page providing context as to where the user is in the process.
  • Simple choices to guide the user. 
  • Contextual information links in the form of FAQs relating to the step the user is at. 
  • Navigation button at the bottom of the page giving specific instructions for the next step.

(Large preview)

The website example above, Punk Avenue shows another example of clear main navigation, with a brief summary of what you will find on each page. Below that is a series of tabs that keep you on the same page and visually indicate what information you are viewing. 

For A Business

  • Keeps customers on their website for longer.
    Research shows that visitors to a website will often leave within 10-20 seconds, but with a clear purpose, you can engage your visitors for a longer period. Although good design and messaging help to present the site’s value proposition, a well-designed navigation display can also contribute to demonstrate what kind of information supports this value proposition.

  • Increases the chance of customer conversion.
    If your site visitor can find what they want via the navigation, and there are simple and minimal steps provided on how to acquire it, the chances of conversion are far higher than a site design that is unable to direct the user to the right information.

  • Reduces risk of customers going to a competitor.
    If a visitor to your site can easily find what they are looking for through effective navigation and good design, chances are they’ll stay there rather than move onto the next Google search result.

  • Reduces duplication of information (by design).
    Good information architecture can ensure that the same or similar content is not replicated. Understanding and documenting the content structure, particularly on information-heavy sites, can prevent these potential issues.

  • Better ROI through efficient use of the platform.
    The investment spent on ensuring that the information architecture on your site is effective and makes sense to your users is a compelling way to increase your customer conversions and the income derived from those sales.

  • Reduces cost of support when a user can’t find something.
    As described earlier, creating an unnecessary load on the customer support team is an additional cost that can be avoided by a site that functions well and provides assistance for customers when they need it. 

The example below helps to illustrate some of the points above about business.

(Image source: Optimizely Blog) (Large preview)

The example above demonstrates how poor navigation displays can impact customer conversion. This case study shows an increase in customer revenue by 53.8%. The additional information in-between the search bar and the products was removed which also served to move the product display closer to the top of the page. The vertical information that was removed created the effect of what may have been perceived as a superfluous navigation bar, or maybe just information that was not considered relevant for a user in their product search. 

When thinking about designing the information architecture for your website or app, efficient site navigation is crucial.


As a designer, ask yourself “Does the language resonate with the user, does the hierarchy make sense to the user flow, can they easily find their way back to where they were?”

If your website is content-heavy, you may also consider the use of site search. Let’s explore some research around site search vs navigation.

Search vs Navigation

In 1997, Jakob Neilson conducted a study that showed over 50% of website users would use the search function over site navigation. In 2012, econsultancy.com reported that 30% of website visitors to e-commerce sites will use the site search, while a Kiss metrics study found that 40% of users preferred using search. In 2010, Gerry Mcgovern’s study demonstrated 30% of users preferring search.

(Image source: Neil Patel) (Large preview)

Although the relationship between these findings may seem elusive, one thing is clear; and that is that users will use both site search and site navigation to find information, in varying proportions.

In order to provide the best user experience for your customers, you may need to consider integrating a site search, in conjunction with an effective and well-designed site navigation if your website has a complex structure and large amounts of information.

Here is a practical example of where a site search would be useful for site visitors. Let’s say you visit a website that sells cleaning and health products, and you were looking to buy some antibacterial hand wash. There are two categories you can see, “Body Washing Products” and “Skin Cleansers”. Which one do you choose? 

Body washing products

Body washing products (Image source: Good Housekeeping) (Large preview)

Skin cleansers

Skin cleansers (Image source: Skincare Hero) (Large preview)

And if you were to browse these categories that may have products listed alphabetically, there may be a large list to scan through. Below are some similar phrases that could be used, depending on what any individual’s idea of antibacterial hand wash could also be called:

  • hand sanitizer
  • sanitizing soap
  • hand disinfectant
  • disinfectant hand wash
  • hand sterilizer
  • hygienic soap
  • antiseptic handwash

If you are looking for “hygienic soap”, it may take you a while to scan the list to find the “antibacterial hand wash”. As it is difficult to cater to all possible synonym variations in the navigation structure of a site, a well-designed site search can allow users to search for these variations, by adding what we call metatags to each piece of content. For example, the “antibacterial hand wash” product could have additional hidden information or tags that include all the terms listed above, allowing users to search for any of these and return search results that match.

The Politico website below uses both navigation and a search function. It demonstrates an example of a content-heavy site that groups the information into categories making it easier to find topics. The site utilizes a “megamenu” which is accessed from the top left corner of the page. This is a common way to provide a menu of options with categories and subcategories that can be used for those visitors that want to browse content, and the search function can be used to locate a specific piece of information.

(Large preview)

According to research from measuringu.com, about 14% of users will start with a search and the rest will start by browsing through the navigation options.

Good And Bad Information Architecture Examples

Let’s review some website examples demonstrating good and bad uses of information architecture. Great navigation is a reflection of well-designed information architecture that considers the target audience’s needs.

Useful Navigation

This Sears website makes good use of mega drop-down menus. These help to provide navigation options to sub-categories that are clearly grouped. It also uses images to provide much faster cognition for the user.

(Large preview)

Pinterest demonstrates a useful way to present visual user-generated content based on search terms. The search is the navigation. This works well based on the sheer amount of content available on the site, which would make it difficult to provide a simple navigation system based on categories.

Pinterest website

Pinterest (Large preview)

Overwhelming Navigation

This website example is complete information overload with bad use of white space and way too many choices. It doesn’t help that the design of the website is cramped making it hard to identify all the options available.  

Frys.com

(Large preview)

How Do You Get It Right?

Here is a brief list of considerations and processes to use when you are designing the information architecture for a product or service.

  • First understand your user’s needs and what tasks they are trying to achieve.
    You can conduct user interviews to really understand what problems your product or service is solving. From here, think about how they might interact with your website and what pathways they could take to achieve their objectives.
  • Try to create a hierarchy with minimal sub-levels.
    If you can achieve this, then the user can access any information on your site with a maximum of two clicks.

Sitemap example

Map out your site navigation to see if you can organise into a minimal number of sub-levels or categories. (Large preview)
  • Don’t use jargon in the navigation language.
    Understand the language of your audience. Test with your users to ensure they understand the correct meaning of the language used.
  • Don’t rely on images or icons alone as a navigation tool.
    There are very few universally understood icons, such as Help, Error, and Print, and these may differ culturally. 

(Large preview)

iphone icons with labels

Note that on smartphones, icons are always accompanied by a text label to help you navigate. (Large preview)
  • Always indicate to the user exactly where they are within the site so they can easily navigate back to a previous page. Breadcrumb navigation is one example of how to do this effectively as shown in the example below. It can sit below the main navigation showing you each page you have clicked on with the current location displaying as the last on the right.

Breadcrumb examples

Breadcrumb navigation example (Large preview)
  • Use design to create distinct visual differences between the hierarchy levels.
    For example, a top-level hierarchy heading may be displayed with a larger font size. These visual differences can guide the user’s eye to more important information first. It can also be the job of the visual designer to help differentiate these areas.

Methods To Test Your Navigation

Card Sorting

Write out the name of each information section on paper, and have participants sort cards containing all your navigation sections into groups that make sense to them. Try doing this same sort with at least five participants so you can start to identify patterns and preferences for the categories and subcategories that are created. This is called an open card sort. A closed card sort can be used if you decide to have predetermined top-level categories that the participants place the cards under based on what makes sense to them.

Card sorting

Card sorting (Image source: UX Indonesia on Unsplash) (Large preview)

Recommended reading: Card Sorting Beginner’s Guide: Improving Your Information Architecture

Scenario Testing

By using a wireframe or prototype, ask participants to complete a specific task by navigating through the site. You can use a clickable wireframe to test this by observing how clear it is for a user to carry out the activity. An example task (refer to the wireframe below) might be to register on the website and then make a booking for a single event and publish it.

Wireframe example

Scenario testing (Large preview)

Tools

Treejack is a tool that allows you to validate your navigation structure. It asks the participants to indicate where they would look to find specific information and provides you with insightful actions.

Treejack tool

Navigation testing tool (Large preview)

Free Keyword Tools

You can use free tools to help to identify commonly used search terms that can help with language choice in your navigation. For example, answerthepublic.com is a free site that allows you to enter a search term to see what other related search terms are being used.

Answer the public keyword search tool

Keyword search tool (Large preview)

We’ve covered the basics of information architecture, and now it’s time to move onto the bigger picture, the Information Age. Understanding context around the massive amounts of data and information we are surrounded by can help to shape your outlook as a UX designer, as it has helped inform the direction and approach to my own design practice.

The Information Age

We live in a time where our access to information is unprecedented. It is instantaneous, it is global, it is everywhere, it is the Internet. News stories are broadcast as they unfold, communication with friends and family in other parts of the world has never been easier, and Google has become our personal library of virtually limitless topics. Information is king and queen. 

Key Facts About Information

  • 90% of the world’s data has been created in the past 2 years.
  • The amount of data in the world doubles every two years.
  • If all the data in our world was stored on 128G iPad tablets, they would create a stack going from the Earth to the Moon 6.6 times! 
  • Only 37% of all data is considered “useful”. And of that 37%, a much smaller percentage is actually analyzed.
  • 33 percent of managers feel that information overload was impacting their health.
  • 66 percent of managers reported increased conflict with teammates as well as reduced job satisfaction.

And finally, let’s examine how information can be used and abused in this age of information.


“We live in a time where our access to information is unprecedented. It is instantaneous, it is global, it is everywhere, it is the Internet.”

The Power Of Information

“With power comes great responsibility.”

This famous quote is often attributed to Uncle Ben from Spiderman. We can think of this in reference to how powerful information can be, but when in the wrong hands, there is an opportunity to abuse this power. Below is my perspective on how the power of information can manifest in our world, and why it is both a precious and dangerous commodity. 

“Information Is Power”

Internet activist, Aaron Swartz, took his life in 2013 at the age of 26. Aaron was the original creator of Reddit, and among many achievements, his untimely death occurred when he was fighting felony charges for illegally accessing and downloading academic information. He wrote a manifesto that called for activists to “liberate” information secured by corporations, and campaigned against Internet censorship. 

We recognize that information alone is useless if no one can find it. And then once it is made available, it needs to be acted upon. On a large scale, information can be shared to protect public health and safety, to help governments to create better policies and to empower individuals to live better lives. It can also be used for propaganda purposes for political gain, to create fear for the purpose of control, and to instill beliefs for the sole purpose of financial profit. 

Information Can Change World Events In An Instant

How quickly have governments pivoted and changed their approach to the COVID-19 pandemic based on new information? Not to mention the release of conflicting information from alternate sources that has also created mass confusion.

An example of this pivot was seen in Australia, when our Prime Minister announced non-elective surgery would be suspended from March 26, but just hours later, it was moved to April 1st after the health minister met with the private hospital sector that afternoon. This was due to the updated information received that would see the stand-down of medical staff, even as hospitals prepared for a surge in COVID-19 cases. 

Dangers Of Misinformation

In current times, examples: “Fake news” claims, presidential tweets, and allegations of misinformation coming from China around the COVID19 pandemic. Donald Trump who is attributed with the reference to “Fake News”, now more generally attributes incorrect news reporting to journalists and media outlets such as CNN.

Unfounded “conspiracy theories” are another example of ways to link seemingly related information points that have no solid relationship evidence. For example:

Information Security

In 2018, it was revealed Facebook was exposed to a massive security breach after hackers exploited a vulnerability to access user’s personal data. The impact of the access to this kind of personal information could have ramifications for those individuals impacted for years to come.

In July 2017, shortly after I left employment at Equifax (no connection whatsoever!), a data breach impacting over 147 million people occurred in the US. The data exposed included Social Security numbers, birth dates, and some credit card details. After spending $1.4 billion on security upgrades, it is still resolving ongoing class actions from consumers that were impacted.

The importance of protecting privacy and personal data has become increasingly important throughout the world. 132 of 194 countries currently have legislation in place to protect the sharing of personal information without consent, and the data and privacy of individuals. In 2017-18 there was a 10% rise in the number of countries enacting data privacy laws.

Based on the examples above, it is clear that information in itself doesn’t discriminate for good or for evil. That’s why it is so important to validate data sources and analyze information before taking it on board.

Conclusion

We have reviewed how we use information, the power it yields, the sheer volume of data we have created, the impacts of information overload, and how information architecture can be used to organize and structure this information for those seeking it. There is no denying that in this age of Information why it is so important to focus on information architecture as a solid foundation for delivering the right information to your customers to make their lives easier.

Further Reading on SmashingMag:

Smashing Editorial
(ah, ra, il)

Source: smash mag

Categories
Uncategorized

Finding Inspiration In The Simple Things (July 2020 Wallpapers Edition)

Finding Inspiration In The Simple Things (July 2020 Wallpapers Edition)

Finding Inspiration In The Simple Things (July 2020 Wallpapers Edition)

Cosima Mielke

2020-06-30T09:00:00+00:00
2020-07-05T00:11:03+00:00

The smell of rain after a hot day, watching the moon rise on a summer night’s sky, going for a swim — often it’s the simple experiences that inspire us and that we treasure most. No matter what July will have in store for you this year, our new batch of wallpapers is bound to cater for some colorful inspiration along the way.

More than nine years ago, we started out on this wallpapers adventure to bring you beautiful and inspiring wallpapers every month. It’s a community effort, made possible by artists and designers from all across the globe who challenge their creative skills and contribute their artworks to it. Just like this month.

In this post you’ll find their wallpapers for July 2020. All of them come in versions with and without a calendar and can be downloaded for free. As a little bonus goodie, we also compiled some favorites from past July editions at the end of this post. Maybe you’ll discover one of your almost-forgotten favorites in there, too? A huge thank-you to everyone who shared their artworks with us this month — we sincerely appreciate it! Happy July!

  • All images can be clicked on and lead to the preview of the wallpaper,
  • We respect and carefully consider the ideas and motivation behind each and every artist’s work. This is why we give all artists the full freedom to explore their creativity and express emotions and experience through their works. This is also why the themes of the wallpapers weren’t anyhow influenced by us but rather designed from scratch by the artists themselves.

Submit your wallpaper

Did you know that you could get featured in one of our upcoming wallpapers posts, too? We are always looking for creative talent, so if you have an idea for a wallpaper for August, please don’t hesitate to submit it. We’d love to see what you’ll come up with. Join in! →

Birdie July

Designed by Lívi Lénárt from Hungary.

Birdie July

Strive For Progress, Not Perfection

“I created this wallpaper as a daily reminder that it’s better to take one small step towards my goal every day than to do nothing at all out of fear that it won’t be perfect. I hope you enjoy it and it helps you keep motivated every day!” — Designed by Andrew from the United States.

Strive For Progress, Not Perfection

Plastic Whale

“The growing amount of plastic pollution in our rivers, seas, and oceans can have a devastating effect on marine wildlife both large and small. Plastic bags are known to stay in the oceans for about 450 years before beginning to degrade. I was inspired by illustrator Kerby Rosanes’ ‘geometric beasts’ series and created my own version to convey this message.” — Designed by Aysha Samrin from Vancouver.

Plastic Whale

It’s Getting Hot

Designed by Ricardo Gimenes from Sweden.

It’s Getting Hot

Happy Birthday, Harrison Ford!

“This month, we’re paying tribute to the legendary actor Harrison Ford, who turns 78 on July 13th! A woodworker and an aspiring actor, during the 70s Ford did carpentry works in the homes of some of the leading film directors and producers, thus crossing paths with Star Wars director George Lucas. It proved to be a defining moment that would shape not only Ford’s life but cinematography in general. Ford portrayed Han Solo in several of the franchise’s installments, leaving an indelible mark through his friendship with Chewbacca and love with princess Leia. After more than five decades of a successful and immaculate career, award-winning and acclaimed roles, and a life away from the public eye, Harrison Ford remains one of the best, most versatile, and prolific actors of our era, relishing his two favorite hobbies – flying and carpentry – as well as impressing us with each new movie to come.” — Designed by PopArt Studio from Serbia.

Happy Birthday, Harrison Ford!

Majestic Moon

“A few weeks ago, I drove the street at night and saw the moon just dipping the horizon. It appeared to be bigger than usual and it was so majestic. It was a really amazing moment that inspired me to create this wallpaper. Keep your mind and eyes open. Enjoy nature whenever you can!” — Designed by Kai Böker from Germany.

Majestic Moon

Happy International Kissing Day

“Celebrating kissing day in a Corona period requires attention. #staysafe” — Designed by Nathalie Lansbergen from the Netherlands.

Happy International Kissing Day

Bloom Where You Are Planted

“I was thrilled to catch this perfect moment of my blooming sunflower.” — Designed by Monica Barron from New Jersey.

Bloom Where You Are Planted

Global Day Of Parents

“This is the day in which people all over the world get the chance to say thank you to their parents (and parental figures) for the often thankless tasks they undertake for their kids. It’s a day to say ‘Thank you, I know I put you through a lot but I do appreciate it!’.” — Designed by Ever Increasing Circles from the United Kingdom.

Global Day Of Parents

Oldies But Goodies

Our wallpapers archives are full of timeless treasures that are just too good to be forgotten. So here’s a small selection of favorites from past July editions. Enjoy! (Please note that these designs don’t come with a calendar.)

Hello, Strawberry Sundae!

“Is there anything more refreshing (and more delicious!) than a strawberry sundae on a hot summer day? Well, we don’t think so. And did you know that strawberry celebration is on its way in the U.S. Oh, yes! July 7th is the National Strawberry Sundae Day, and we predict that it’s going to be sweet and yummy. So, make your favorite dessert and start preparing yourself for the festive July days.” — Designed by PopArt Studio from Serbia.

Hello, Strawberry Sundae!

Floral Thing

“The wallpaper which I created consists of my personal sketches of Polish herbs and flowers. I wanted it to be light and simple with a hint of romantic feeling. I hope you’ll enjoy it!” — Designed by Beata Kurek from Poland.

Smashing Desktop Wallpapers - July 2012

Keep Moving Forward

“Snails can be inspiring! If you keep heading towards your goal, even if it is just tiny steps, enjoy the journey and hopefully it will be worth the effort.” — Designed by Glynnis Owen from Australia.

Keep Moving Forward

Alentejo Plain

“Based in the Alentejo region, in the south of Portugal, where there are large plains used for growing wheat. It thus represents the extensions of the fields of cultivation and their simplicity. Contrast of the plain with the few trees in the fields. Storks that at this time of year predominate in this region, being part of the Alentejo landscape and mentioned in the singing of Alentejo.” — Designed by José Guerra from Portugal.

Alentejo Plain

July Rocks!

Designed by Joana Moreira from Portugal.

July Rocks!

Riding In The Drizzle

“Rain has come, showering the existence with new seeds of life. Everywhere life is blooming, as if they were asleep and the falling music of raindrops have awakened them. Feel the drops of rain. Feel this beautiful mystery of life. Listen to its music, melt into it.” — Designed by DMS Software from India.

Riding In The Drizzle

Save The Tigers

“Global Tiger Day, often called International Tiger Day, is an annual celebration to raise awareness for tiger conservation, held annually on July 29. It was created in 2010 at the Saint Petersburg Tiger Summit. The goal of the day is to promote a global system for protecting the natural habitats of tigers and to raise public awareness and support for tiger conservation issues.” — Designed by Athulya from Calicut.

Save The Tigers

Cactus Hug

Designed by Ilaria Bagnasco from Italy.

Cactus Hug

Heated Mountains

“Warm summer weather inspired the color palette.” — Designed by Marijana Pivac from Croatia.

Heated Mountains

Summer Cannonball

“Summer is coming in the northern hemisphere and what better way to enjoy it than with watermelons and cannonballs.” — Designed by Maria Keller from Mexico.

Summer Cannonball

Tropical Lilies

“I enjoy creating tropical designs, they fuel my wanderlust and passion for the exotic. Instantaneously transporting me to a tropical destination.” — Designed by Tamsin Raslan from the United States.

Tropical Lilies

Eternal Summer

“And once you let your imagination go, you find yourself surrounded by eternal summer, unexplored worlds and all-pervading warmth, where there are no rules of physics and colors tint the sky under your feet.” — Designed by Ana Masnikosa from Belgrade, Serbia.

Eternal Summer

A Flamboyance Of Flamingos

“July in South Africa is dreary and wintery so we give all the southern hemisphere dwellers a bit of colour for those grey days. And for the northern hemisphere dwellers a bit of pop for their summer!” — Designed by Wonderland Collective from South Africa.

A Flamboyance Of Flamingos

Summermoon

Designed by Erik Neumann from Germany.

Summermoon

July Flavor

Designed by Natalia Szendzielorz from Poland.

July Flavour

Summer Heat

Designed by Xenia Latii from Berlin, Germany.

Summer Heat

Source: smash mag

Categories
Uncategorized

Smashing Podcast Episode 19 With Andy Bell: What Is CUBE CSS?

Smashing Podcast Episode 19 With Andy Bell: What Is CUBE CSS?

Smashing Podcast Episode 19 With Andy Bell: What Is CUBE CSS?

Drew McLellan

2020-06-30T05:00:00+00:00
2020-07-05T00:11:03+00:00

Photo of Andy BellToday, we’re talking about CUBE CSS. What is it, and how does it differ from approaches such as BEM, SMACSS, and OOCSS? I spoke to its creator, Andy Bell, to find out.

Show Notes

Note: Listeners of the Smashing Podcast can save a whopping 40% on Andy’s Learn Eleventy From Scratch course using the code SMASHINGPOD.

Weekly Update

Transcript

Drew McLellan: He is an educator and freelance web designer based in the U.K. and has worked in the designer web industries for well over a decade. In that time he’s worked with some of the largest organizations in the world, like Harley-Davidson, BSkyB, Unilever, Oracle, and the NHS. Alongside Heydon Pickering, he’s the co-author of Every Layout, as well as running Front-End Challenges Club which is focused on teaching front-end development best practice via short, fun challenges.

Drew: His latest venture is Piccalilli, a website newsletter with tutorials and courses to help you level up as a front-end developer and designer. So we know he’s an experienced developer and educator, but did you know he was the first person allowed to compete at Crufts with a panda?

Drew: My Smashing friends, please welcome, Andy Bell. Hi, Andy, how are you?

Andy Bell: I’m smashing, thanks. How are you?

Drew: I’m very good, thank you very much. Now I wanted to talk to you today about something that you posted on your site, Piccalilli, about a CSS methodology that you’ve developed for yourself over recent years. First of all, I guess we should probably explore what we mean by a CSS methodology because that could mean different things to different people. So when you think of the CSS methodology, what is it to you?

Andy: That’s a good, hard question to start with, Drew. Appreciate that, thank you!

Drew: Welcome!

Andy: It’s a tricky one. So, for context, I’ve used BEM for a long time, and that is Block Element Modifier. That’s been around for a long time. The way that I look at a CSS methodology is, it’s not a framework, it’s an organization structure. That’s how I like to see it. It’s like a process almost. It’s like you’ve got a problem that you need to solve with CSS and you use the methodology to solve it for you and keep things predictable and organized. BEM’s just legendary for that because it’s been wildly successful.

Andy: Then you could almost qualify things like the style components and that sort of thing. You can almost say that they’re methodology orientated even though they’re a bit more framework entwined, but still, it’s a methodology of breaking things into tiny molecules. So essentially that’s what I’m trying to do with CUBE CSS as well. A thinking structure, I think I described it as.

Drew: So it’s an application of process for how you architect and you write CSS, and it’s not so much anything that’s based on tools or any other sort of technology, it’s just a sort of work flow. So there’s lots of different approaches out there. You mentioned BEM. There’s SMACSS, OOCSS, Atomic CSS. And then you’ve got these sort of unusual lovechild approaches like ABEM. Have you seen that one?

Andy: Yeah.

Drew: Why publish your own?

Andy: Yeah, yeah. Why make your own? That’s a very good question. I think those who know me well know I like to sail against the tide a lot. It’s mainly because I tend to do lots of varied projects as well, in varied teams. So it’s very hard, I’ve found, to work with BEM with a traditional developer because they’re used to using CSS for what CSS is all about: the cascade, et cetera, and that’s why I sort of stole that from the language.

Andy: On the other flip side is that less structured methodologies, it’s harder to work with a programmer, JS sort of person because they like structure and organization and small components, which is understandable working with the language that they work in.

Andy: So I found myself in these positions where I was working with different types of people, different types of projects where one methodology wasn’t quite working. Over the years, I’ve just been playing around with this idea of how things go, and then there’s the stuff me and Heydon did, Every Layout, which sort of enforced the big part of it, which is the C, the composition part, and then I’ve just sort of evolved it very rapidly over the last six months.

Andy: The only reason I wrote an article about it was because I was just doing this course and I thought I’d better write some supplementary material to go with it so people understand it, and it’s absolutely blown up. So maybe we’re not over methodologies quite yet, Drew.

Drew: So the course material that you’ve been putting together actually uses CUBE CSS as its methodology, does it?

Andy: Yeah. So a good 50% of the course is actually front-end, even though it’s a course unlimited. It’s so, so deeply entwined in the way that we build the front-end that I couldn’t just say, “Oh, by the way, this is how I write a nice CSS,” and then leave it. I know people like to get into the detail, so I was like, what I’ll do is, I’ll write this post that’s really long and really detailed and then if someone wants to skill up and really understand what we’re doing, then they can do, and the rest is from there. And here we are today, Drew, sitting and chatting about it.

Drew: So if somebody already understands BEM and is maybe already using BEM, as an example, because that’s probably one of the widest adopted methodologies, isn’t it? So if they’ve already got an understanding of BEM and they’re coming to CUBE, is that something that they would find easy to adopt? Are there many similarities or is it completely different?

Andy: Yeah. I’d say going from BEM to CUBE is probably a smooth transition, especially the way I like to still write the CSS for CUBE. So the majority of stuff’s happening at a higher level. So it’s happening at the cascade level and it’s happening global CSS, using the utilities to do a lot of the stuff. But when you come into the nuts and bolts of it, it’s very BEM-like components, blocks and elements. The only thing that’s sort of different from BEM is, instead of having modifiers, we use this thing called exceptions instead which is, instead of using CSS classes, it turns to data attributes, which I think gives a nice bit of separation and a real exception, which is what a modifier should be.

Andy: Part of the reason why I’ve sort of sailed away from BEM was because I found the way I was working with it, especially in design systems, was modifiers were dominated and it became a problem because it was like, what is the role of my block at this point? Because if I’m modifying it to the point where it’s unrecognizable regularly, then is this methodology still working how it’s supposed to work?

Andy: Then there’s the whole design token stuff, the stuff that Jina did with the Lightning Design System which we’ve all started adopting now. The utility class stuff really started to make sense with that approach. So I just sort of smushed all the things I like about other people’s work and slotted into my own instead.

Drew: You talk about with BEM, the sort of modifier approach kind of getting out of control. Was that one of the main pain points with BEM that CUBE tries to address?

Andy: Yeah, absolutely. I do like the modifier approach with BEM, it does make sense. What I like about BEM is something that I still do, is the double underscore for an element, and then there’s the double dash for a modifier. I like that way of organizing things. It was just a case of okay, well, a lot of the modifiers I can actually account for with utility classes and then the other bits…

Andy: So the example I use in the article is, imagine you’ve got a card and then the card is flipped, so the content appears before the image. So then that makes sense, to see display: flex and then you reverse it, reverse the order. That makes sense, to have an exception rule for that because it’s an exception of the card’s default state, and that’s how I like to see it. It’s like an affected state on that component, is what an exception is, and that makes sense.

Andy: With a lot of the stuff that I’ve done more recently, the modern front-end stack with reactive JavaScript, there’s a lot of state changing and it’s makes sense to handle it appropriately between CSS and JavaScript because they are becoming more and more entwined with each other, whether you like it or not. It’s a common language for them. As you can see by my face, very much not, but there you go. You’re probably thinking, “Actually, I’ve been working with react quite a lot recently, so I’m the other way round.” So I can see that as well.

Drew: So let’s get into CUBE then. So CUBE stands for Composition Utility Block Exception. Is that right?

Andy: Yeah. That’s the one.

Drew: What on Earth does that mean?

Andy: Oh, mate, you should have heard it before! I was doing a talk last year. I did a talk, and it was called Keeping it Simple with CSS that scales, and in there I sort of introduced an earlier version of it called CBEUT, which was Cascade Block Element Utility Token. It was rubbish. I hated the name of it. I did it a couple of times, this talk last year, and I really didn’t like the name. Then when I came to doing this stuff this year, I thought, “I really need to think about what it actually is and what it’s called.” I think CUBE is a little less rubbish. I think that’s the best way I can describe it.

Andy: But then, names are always rubbish for these things, aren’t they? I mean, like BEM, what a rubbish name that is! But we all do it. Look at Jamstack: that’s a terrible name as well, isn’t it?

Drew: My lips are sealed!

Andy: Your boss is going, “What?” It’s true. It’s just the way it is, isn’t it, in our industry.

Drew: It seems that a lot of the CSS methodologies try and work around some of the features of CSS, things like the cascade. My understanding from what I read is, CUBE tries to make use of those sort of features and properties of CSS.

Andy: Yeah. A good analogy for it is SCSS, like Sass, is an extension of the natural CSS language, isn’t it? You’re pretty much right in CSS still. So CUBE CSS is like that. So it’s an extension of CSS. So we should still write CSS, as CSS should… well, it’s supposed to be written. Let’s be honest, that how it’s supposed to be written, is the name gives it away: Cascading Style Sheets. So it’s embracing that again because what I’ve found is that I’ve gone all the way down to the micro-optimization level. I’ve been down the path that I see a lot of people going down recently where… and I’ve mentioned this in the article as well, where I can see… there’s some evidence of it recently. I’ve spotted people have been creating spacer components and stuff like that, and I understand that problem, I’ve been in that situation.

Andy: The way I fixed it was, instead of drilling down and trying to micro-optimize, I actually started thinking about things on a compositional level instead because it doesn’t matter how small your components are, at some point they’re going to be pages, they’re going to be views. You cannot avoid that, that’s how it’s going to be. So instead of trying to say, “Right, I need these tiny little helpers to do this layout,” you say, “Right, I’ve got a contact page view, or a product page view, and that’s a skeletal layout composition. Then inside of that I can slot whatever components I want in there.” We’ve got things like Grid and Flexbox now which make that much more achievable, and you can essentially put whatever you want inside of the skeletal layout, it doesn’t matter. Then the components should, at that point, behave how you want them to behave, with or without container queries.

Drew: This is the composition part of CUBE where you’re looking at things at more of a macro level, looking at how components can be composed into a view to create the sort of pages that you need to create for a site or an app or what have you.

Andy: So it’s creating rules, essentially. It’s like guidance. It’s trying to get guidelines for something. It’s not like a strict rules, like, you should do this, you should do that. That’s essentially what you’re doing with the browser, with this methodology, is you’re saying… you’re not forcing it to do anything. You’re saying, “Look, ideally, if you could lay it out like this, that would be great, but I understand that that might not be the case so here’s some bounds and some upper and lower levels that we can work with. Do what you can, and cheers.” Then you just chuck some components at it and let it just do what it does. You add enough control in there for it to not look rubbish.

Andy: So a good example would see… we do a layout in Every Layout called the switcher, which essentially will in-line items until a certain point where the calculation that works out how wide it should will just stack them on top of each other. But because we add margin to the in-line and the block, it works, regardless of what the state of it is, it still looks fine. That’s the idea, is that we’re not telling the browser to say, “You must layer this three column grid out.” We’re saying, “If you can layer a three column grid out, do it. Otherwise, just stack and space instead.” So it’s that sort of methodology, of letting the browser do its job really.

Drew: Many of the different approaches that have come along for CSS over the last few years have very much focused on the component level of dealing with everything like it’s a component. CUBE doesn’t downplay that component aspect so much, it just gives this extra concept over the top of taking those components and composing them into bigger layouts, rather than just saying the layout’s just another component.

Andy: Yeah, that’s a good point, yeah. I think the thing to say about components is they’re important, especially in modern front-end stuff. We do a lot of component stuff, system stuff. But the way I see a component is, it’s a collection of rules that extend what’s already been done.

Andy: The point I make in the article is, by the time you get down to the block level, most of your styling has actually been done, and really what your component is doing is dotting the Is and crossing the Ts and it’s saying, “Right, in this context…” So for a card, for example, make the image have a minimum height of X, and add a bit of padding here. Do this, that and the other. Put a button here. It’s just sort of additional rules on top of what’s already been inherited from the rest of the CSS. I think that’s probably the best way to describe it.

Andy: Whereas in BEM, the component is the source of truth. Until you put that class on the element, nothing has been applied at that point, and that method works. I just found I wrote more CSS by doing that, and more repetitive CSS, which I don’t like doing.

Drew: Would you consider the typography and the colors and the vertical rhythms, spacing, and all of that, is all that part of the idea of composition in this model?

Andy: Yeah. In a global CSS, yeah, absolutely. The vertical rhythm especially, and the flow. We did an article on that at 24 ways, didn’t we, a couple of years ago, the flow and rhythm component. That was a sort of abstract from this approach as well, where you set a base component which essentially uses the lobotomized owl selector. I don’t know how I’m going to describe that on the radio, but we will. We’ll just put, I think, in the show notes about the Heydon article or something. But essentially that, it selects child elements… sorry, sibling elements.

Andy: So it says, “Right, every element that follows element X have margin top of CSS costs and property value,” and then the beauty of that is then you can set that CSS custom property value on a compositional context as well. So you can say, “Right, in this component, if there’s some flow on the go, we’ll set flow space to actually be two rem because we want it to be nice and beefy, the wide space.” Then in another one you might say, “Actually, I want flow space to be half a rem because I want it to be tight.” This is all happening, all the control is coming from a higher level and then what you’re doing is, you’re adding contextual overrides rather than reinventing it each time, reinventing the same thing over and over again.

Drew: So that’s the C, Composition. Next we’ve got U, which is Utility. So what do we mean by utility?

Andy: So it’s a class that does one job, and it does it really well. That could be an implementation of a design token which… it’s an abstract of properties. Usually it’s colors or typography styles, sizing, and rules like that. The idea is you generate utility classes that apply those. So you’ve got a utility that will apply background primary, which is like the primary color, and then color primary, which is the text color. Then you might generate some spacing tokens for marginal padding, and all those sorts of things. They just do one job. They just add that one spacing rule, that one color rule, and that’s it.

Andy: But then you’ve got other utilities as well. So a good example is a wrapper utility. What that will do is, it will put a maximum width on an element and then it will put left and right auto margin to sit it in the middle of the thing. So it’s just got one job, and it’s just doing it efficiently and well.

Andy: So you’ve got your global styles, you’ve done a lot of your typography settings and a lot of your flooring space. Your composition’s then giving context and layout. Then utilities are applying tokens directly to elements to give them those styling that you need. So like a heading, for example, you’re saying, “I want this to be this size and I want it to have this lead in, and I want it to have this measure.” Then at that point… this is what I was saying about the blocks… then you go further down the stack, and you’ve already done most of the work at the point.

Andy: So they give you this really efficient way of working, and because HTML sort of streams down the pipe as well, it’s really nice to abstract the workload onto HTML rather than CSS as well, I’ve found. I used to really get into utility classes, like in this sort of old curmudgeon style of, “Oh, separation of concerns,” but I actually think it’s a really decent way of working. I mention in the article that I actually like Tailwind CSS. I think it does work, and I really like using it for product typing because I can really put something out really quick. But I think it just goes a little bit too far, does Tailwind, whereas I like to rain it in when it goes beyond just applying a single rule on a class. So that’s it, I think. Do you?

Drew: So, yeah, you talk in the article a lot about design tokens, which is something that we’ve talked about on the Smashing Podcast with Jina Anne back in episode three, I think it was. So it sounds like design tokens are a really fundamental aspect.

Andy: Yeah. Oh, God, yeah. I remember so vividly when Jina was doing the Lightning Design System stuff because I was building a design system at the time, or something that resembled a design system, and we were struggling to get executive approval of it. When the Lightning Design System came out, I literally just sent them link after link and I said, “This is what we’re doing. We’re building a design system. This is what Salesforce are currently using it for.” I remember her work at the time actually helped me to get stuff through the door.

Andy: Then the design token stuff has always stuck with me as being a really good way of applying these rules. Because I’m a designer by trade, so I can just sort of see that organization and the ability to organize and create a single source of truth being really useful because it’s something we’ve not really had in digital design, especially in the Adobe era of working with Photoshop and stuff, we just didn’t have that luxury. We had it in print with the Pantone Book but we didn’t have it in digital with random hex codes all over the shop.

Andy: So it’s just great. I love that level of control. Actually, I think it aids in creativity because you’re not thinking about unimportant stuff anymore, you’re just thinking about what you’re doing with it.

Drew: Does the implementation of those design tokens matter particularly with the approach? Is it always CSS custom properties?

Andy: I think that’s a really important point with CUBE. Some of the responses I’ve had, people have struggled with this a little bit. There’s no mention of technology in it whatsoever. The only technology that’s consistent is CSS. You can do it however you want. You could do all this with whatever CSS and JS things people are doing now, or you could it with just Vanilla CSS. You could do it with Sass. I do it with Sass. Less, if that’s what you’re still doing. All these available technologies, post CSS, all these things. You can do however you want to do it, it doesn’t matter.

Andy: The idea is that if you follow those sort of constructs, you’ll be fine. That’s the idea behind it. It’s a very loose and not strict as some of the methodologies are. I’ve seen it with BEM especially, people get really ingrained in the principles of BEM to the point where it’s like you’re not even solving the problem anymore. I think that you’ve got to be flexible. I said it in this talk last year. I was like, “If you stick to your guns too tightly, you can actually cause problems for yourself in the long run because you try and follow a certain path, and you know it’s not working anymore.” You should always be flexible and work with a system rather than working to the letter to it.

Drew: So the B, the B is Block. You’ve talked about this idea, that by the time you get down to the block level, most of everything should be in place, and then the block level styling is only really concerned with the actual very detail of a particular component. Generally, is the concept of a block similar to what people will be familiar with?

Andy: Oh, absolutely, yeah. So imagine your BEM component and take all the visual stuff out of it, and that’s what you’re left with, essentially, the block. This is what I struggled to articulate when I first started thinking of this methodology. A block is actually really a C, it’s a composition, but that makes it really difficult because you’re into recursive territory there and I think people’s brains would explode. But really that’s what a block is, it’s actually another compositional layer but more of a sort of strict context, so like your card, your button, your carousel, if that’s what you like doing still, and all that sort of stuff.

Andy: It’s like a specific thing, a component, and then inside of there you’re setting specific rules for it to follow, really ignoring the rest so you’re not… You might apply tokens in the blocks, and I do do that still, but really it’s more layout orientated, is a block, as far as I work with them, or at least taking the token and applying it in a specific way, like a button hover status, stuff like that. So really your block should be tiny by the time you get down to them, they shouldn’t be really doing much at all.

Drew: So it could be as small as a hyperlink.

Andy: Yeah.

Drew: But it could also be a compound collection of other blocks?

Andy: Yeah. Like a module sort of thing. You could definitely do that. Because, again, that goes back to the sort of compositional aspect of it, is that whatever goes in it shouldn’t matter. The good example of that is like the card. So the content of a card is, say, like a heading, a summary and a button. You shouldn’t really be specifically targeting those three elements. You should be saying, “Look, anything that happens to find itself in content, have some flow rules in there and have some sort of compositional layout rules,” and then it doesn’t matter what you put in there. You might decide that you want to put an image in that content thing and it should just work, it should just look fine.

Andy: That’s the whole point of working with CSS. It’s a very forgiving way of working with CSS. You’re making your life a lot easier by being less rigid because when stuff accidentally finds itself in something, which it will, it doesn’t look horrific as it could do if you were being more specific about things, is what I’ve found.

Drew: I definitely need a lot of forgiveness around my CSS!

Andy: I know you do!

Drew: Cheers! So that’s the B. The last thing is E: E is Exception. Now we’re not talking about error messages, are we?

Andy: No, no. It’s a sort of-

Drew: We’re not talking about JavaScript exceptions.

Andy: Yeah, yeah. There should be none of that at this point. I should hope not anyway, otherwise you really are in the woods at that point: I don’t think I’m going to be able to help you! The whole idea of this is… so you’ve created the context with your block, and an exception is exactly that, it’s like an exception to the rule: so a flipped card, or it might be a ghost button. So you know those buttons that have just got a border and a transparent background? That would be an exception because a button has probably got a solid background color and then the label color. So it’s creating a sort of distinct state of variation.

Andy: The reason why I do this with data attributes instead of classes, and the reason why that is is because a) I think it’s nice to have a distinction. So when you’re scanning through lots of HTML, you can see data, hyphen something, you’re like, “Right, okay, something has definitely changed on this element.” The other thing is that it’s very nice to give JavaScript access to that state, and vice versa as well. So I really like applying state with data attributes in JavaScript. I think that is essentially what they’re for, a sort of communication layer. The harmony between them seems to work really well.

Andy: So a good example is, say you’ve got a status message and then JavaScript will add data state is either success, error or information, or something. You can then hook into that with your exception styles in CSS. So you know that’s an exception of the status component and it’s going against its default state. So it’s just a really handy way of working with things. It’s predictable on both ends: it’s predictable on the CSS end, and it’s predictable on the JavaScript end as well.

Drew: I guess it’s quite nice that something that class names don’t give you is a property and value. So if you want to have something like that which is the state, and it can either be success or failure or warning or what have you, you can specifically address that state property and flip its value. Whereas with a big long list of class names, if you’re manipulating that in JavaScript, for example, you’re going to have to look at each one of them and add that business logic in there that says, “I can only set one of these,” and what happens if two of those classes are applied to the same element? You can’t get that with a data attribute, it only has one value.

Andy: Yeah. That’s a good way of saying that, yeah. It is very helpful, I’ve found, to work like that.

Drew: That’s quite interesting. I don’t think I’ve seen any other methodologies that take that approach. Is that completely unique to CUBE, doing that?

Andy: It might be. I don’t really pay much attention to other stuff, which I should do. Someone else is probably doing that. I’ll tell you now, it’s been the most controversial aspect of it. Some people really did not like the idea of using data attributes. The thing is as well, and how I respond, is, do what you want. We’re not telling you to do things in a certain way, it’s just suggestions. If you want to do exceptions to CSS classes, like modifiers, then knock yourself out. The CUBE police aren’t going to come knocking at your door. It’s absolutely fine.

Andy: The CUBE thing is a thinking thing, it’s a structure. You apply that structure however you want to apply it, with what tooling you want, or whatever technology you want. As long as you keep things consistent, that’s the important thing.

Drew: So there’s no such thing as pure CUBE?

Andy: The way I write it is pure CUBE, Drew. Everyone else is just a fake, it’s just a weak immitation.

Drew: Apart from to you, no-one can say, “That isn’t textbook CUBE.”

Andy: No, that’s it. No-one can dispute that really, can they? So, yeah, I’ll go with that. Gives you a bit of clout or something, I think, something like that.

Drew: Can you mix and match a CUBE approach with other methodologies? Can you use bits of BEM?

Andy: Yeah, I reckon so. I’ve been thinking about it a little bit because I’m going to do some more stuff on it soon because it’s become quite popular, so people will want more work. One thing I’m going to look at is how to approach using the CUBE methodology with something existing.

Andy: So there’s two opposite ends of the scale. A good question that people have asked is: “How does this work with every layout, the other stuff?” I’m like, basically, every layout is the C. That’s what every layout is, it’s the compositional layer. Then someone else asked, “Well, how would this work with something like Atomic Web Design, like their stuff that Brad Frost did? It’s like, well, you could break those pieces up and apply them at each level. Atomic Design goes all the way down into the micro detail. It’s abstracting that into using, right, okay, well I can apply this with utilities, so the molecules, I think. I can apply that with the utilities, and it’s translating what you know already into this slightly different structure of working.

Andy: Really, it’s a renaming for a lot of things. I’ve not invented anything here, I’ve just sort of, like I say, I’ve just stolen things that I like. I love the way that some of the Atomic Design stuff is thought about. That’s really some smart work. And BEM. The stuff Harry did, the Inverted Triangle CSS, I thought that was really cool. So I’ve just sort of nicked bits that I like from each one of them and sort of stitched them all together into this other hybrid thing, approach. More to come, I think.

Drew: Can the CUBE approach be applied to existing projects that already have CSS in place or is it something you really need to start on a fresh project with?

Andy: That very much depends. So if you’ve got like a bootstrap job and it’s just got thousands of lines of custom CSS, that I’ve definitely been involved in before, then I think you might be trying to put a fire out with a bottle of water at that point. But if you… say, for instance, if you’ve got a rough BEM setup and it’s gone a bit layer-y, you could use CUBE to refactor and actually pull it back into shape again.

Andy: It depends, the answer to that one. But it’s doable, as with everything. If you really want it to work, Drew, you can do it if you want, can’t you? The world is our oyster!

Drew: Especially if your BEM site’s gone layer-y.

Andy: Yeah. Nothing worse than a layer-y BEM site!

Drew: I’ve noticed in the examples that you’ve given… and I’ve got an eagle eye, I’ve seen you’ve been doing this for a while… a lot of your class values in the HTML attribute are wrapped in square brackets.

Andy: Oh, God, yeah. Tell you what, Drew-

Drew: What is that about? What is that about?

Andy: I’ll tell you what, if there’s ever one thing that I’ve done in my whole career that’s just been absolutely outrageously controversial… and you follow me on Twitter, you’ve seen what comes out of my mouth… it’s those bloody brackets! My, God! People either love them or hate them. They’re Marmite, they are.

Andy: The reason I do them is a grouping mechanism. So if you look at the way that they’re structured, the way I do it is, block at the start and then I’ll do a utilities after that. Then what I might do is, in between a block group and a utility group, there might be another block class. So a good example of that would be… we’ll go back to the card again. But then say that there’s a specific block called a CTA, like a call to action. You might have that applied to the card as well, and then your utilities are enforcing the design attributes, so the colors and all that business. So then you’ve got three groups of stuff.

Andy: When you come to it, if you’ve got that order in your head each time, you know, okay, right, this first group’s blocks. Oh, that’s looks like another block. I’ve got that one. Then it’s like, right, they’re definitely utility classes. Then what I might even do is, if there’s a lot of design token implementation, have that in a separate group. So it’s just very clear what each group is doing, and there’s a separation inside of the classes there as well. I’ve found it really helpful. Some people find it incredibly offensive. It’s definitely a do it if you want to do it. Definitely you don’t have to do it.

Andy: It’s quite funny, when I published that article, so many people finished halfway through to ask me, “What is it with these brackets?” I was like, “Did you finish the article? Because there’s a big section at the end where it explains exactly what they’re doing,” to the point where I actually had to write a bit in the middle of the article of, “If the brackets are essentially doing your head in, click here and I’ll skip you all the way down to that explanation bit so you can just read about them.” It can be quite controversial.

Andy: When I’ve worked on really, really complex front-ends… and we did a little bit of stuff together, didn’t we, last year?

Drew: Yeah.

Andy: You’ve seen the sort of design implementation on that project that we were on. It requires that sort of grouping because there’s just so much going on at the time, there’s so much different stuff happening. I’ve just found it really, really useful over the years, and also get lots of questions about it, to the point where I was almost going to write just one page on my website that I could just fire people to to answer the question for them.

Drew: Slash, what’s with the brackets?

Andy: Yeah. Slash, brackets. Have you seen that new Hey Email thing that’s just come out? They’ve bought a domain of itsnotatypo.com, just to answer the whole Imbox, like im with an M rather than an in. Basically, I was like, “I think I need to do that,” like, whatswiththebrackets.com, and just do a one-pager about it.

Drew: It strikes me that the approach with brackets actually could be something that might be useful when using things like Tailwind or something that has a lot of classes because that can-

Andy: Yeah. Oh, God, yes.

Drew: You have classes that are addressing your break points and what have you, and then you’ll have things that are for layout, things that are for color or type, or what have you. So it might also be a useful way of dealing in situations like that.

Andy: I’d definitely agree with that. A good analogy… not analogy. A good bit of info about Tailwind is that I actually quite like Tailwind. I’ve used that on very big projects. The one thing that really opened my eyes to Tailwind though was when I saw a junior developer try to work out what was going on, and it was really, really eye-opening because they just didn’t have a clue what was happening.

Andy: I think that’s one problem I’ve found with these sort of over-engineered approaches, which I think it’s fair to say Tailwind is, is that there’s a certain skill level that is required to work with it. I know the industry tends to have an obsession with seniority now, but there’s still people that are just getting into the game that we need to accommodate, and I think having stuff that’s closer to the language itself is more helpful in those situations because they’re probably learning material that is the language as it is. So I think it’s just a bit more helpful. Especially having a diverse team of people as well. Just food for thought for everyone.

Drew: People might look at all the different methodologies that are out there and say, “This is evidence that CSS is terrible and broken, that we need… all these problems have to be solved by hacking stuff on top. We need tools to fix bits of CSS. We need strict procedures for how we implement it, just to get it to work.” Should the platform be adapting itself? Do we need new bits of CSS to try and solve these problems or are we all right just hacking around and making up funny acronyms?

Andy: I think the power of CSS, I think, is its flexibility. So if you’re going to program CSS, a lot of the knowledge is less of the syntax and more of the workings of a browser and how it works. I think that might be a suggestion, that the problem is that people might not have learnt CSS quite as thoroughly as they thought they might have learnt it, who created these problems. I’ve seen that in evidence myself. I spotted a spacing mechanism that had been invested, which was very complicated, and I thought, “This person has not learnt what padding is because padding would actually fix this problem for them, understanding how padding works and the box model.” That’s not to be snidey about it.

Andy: We work in an industry now that moves at an even faster pace than it has done previously and I think there’s a lot less time for people to learn things in detail. But, on that front, I think CSS still does have work to do in terms of the working group, who I think do a bloody good job. A great, shining example of their work was the Grid spec which was just phenomenal. The way that rolled out in pretty much every browser on day one, I thought that was so good.

Andy: But we’ve got more work to do, I think, and I think maybe the pace might need to increase a little, especially with stuff like container queries, we all love talking about them. Stuff like that I think would help to put CSS in a different light with people, I think. But I think CSS is brilliant, I love it. I’ve never had a problem with it in lots of years really. I do find some of the solutions a bit odd, but there you go.

Drew: What’s the response been like to CUBE since you published the article?

Andy: Mind-blowing. I honestly published it as just supporting material, and that’s all I expected it to be, and it’s just blown up all over the place. A lot of people have been reading it, asking about it, been really interested about it. There’s definitely more to come on it.

Andy: I did say in the article, I said, “Look, if people are actually quite interested in this, I’ll expand on this post and actually make some documentation.” I’ve got bits of documentation dotted around all over the place, but to sort of centralize that, and then I was thinking of doing some workshops and stuff. So there’s stuff to go. It’s how Every Layout started as well. We both had these scattered ideas about layout and then we sort of merged them together. So something like that, I suppose, will come in the future.

Drew: Are there any downsides that you’re aware of to using CUBE? Are there problems that it doesn’t attempt to solve?

Andy: Yeah. This accent, Drew, it just won’t go way, no matter what I do! In all seriousness, I think CUBE’s got as close as I can get to being happy with the front-end, which is saying a lot, I think. You never know, things might change again. This has evolved over more recent years. Give it another five years, I’ll probably be struggling with this and trying something else. I think that’s the key point, is to just keep working on yourself and working on what you know and how you approach things.

Andy: This definitely won’t work for everyone as well, I know that for a fact. I know that from my comments. I don’t expect it to work for everyone. I don’t expect anything to work for everyone. It’s the same with JavaScript stuff: some people like the reactive stuff and some people don’t. It is what it is. We’re all people at the end of the day, we all have different tastes. It’s all about communicating with your teammates at the end of the day, that’s the important thing.

Drew: I know you as a very talented designer and developer and you, like many of us, you’re just working on real projects all day, every day. But you’ve recently started publishing on this site, Piccalilli, which is where the CUBE CSS introduction article was. So Piccalilli is kind of a new venture for you, isn’t it? What’s it all about?

Andy: Very kind of you to say, Drew. You’ve actually worked with me, so that’s high praise. But the Piccalilli thing is an evolution. So I’m a freelancer. I do client work, but I think this has become apparent with the pandemic, that that is not the most sustainable thing in the world in some industries. I think freelancing can be very, very tough, as a developer and designer. It’s something that I’ve been doing it for so long now, 10 years… well, 12 years now actually.

Andy: I fancied doing something a bit different and applying the knowledge that I’ve got and actually sharing it with people. I’ve always been very open and sharing, and I wanted to formalize that. So I created Piccalilli to write tutorials, but mainly for courses that I’m producing: that’s the main meat and potatoes. And then there’s the newsletter which is… people are really enjoying the newsletter because I share cool things I’ve found on the internet every week. That’s the backbone of it. It’s just going really well. That’s essentially where I want to see myself doing more and more full-time, as the years go on, I think.

Drew: So what’s coming next for Piccalilli? Have you got anything that you’ve got coming out?

Andy: Thanks for the door open there, Drew! By the time this recording goes out, the first course will be live: Learn Eleventy From Scratch, and that’s where we learn how to build a Gatsby website! No, you learn how to build an Eleventy site. So you start off with a completely empty directory, there’s nothing in it, it’s empty, and then at the end of it you’ll finish up with this really nice-looking agency site. We learn all sorts in it. You learn how to really go to town with Eleventy. We pull remote data in from places. We use CUBE CSS to build a really nice front-end for it.

Andy: If you want to get into the Jamstack and you want to get into static site generators, or just how to build a nice website, it’s just a really handy course, I hope, for that. It’s currently being edited within an inch of its life as we’re talking. It’s going to be cool, I hope, and useful. But that’s an accumulation of a lot of stuff I’ve been doing over the last couple of years. So it should be fun.

Andy: So buy it, and I’ll do a discount code, do like smashingpod for 40% off, and you can get it when it comes out.

Drew: Amazing. We’ll link that up. Have you figured out how to spell Piccalilli reliably yet?

Andy: I was on with Chris and Dave with the ShopTalk Show and I said on there, “If there’s ever one thing you want to hire me for it’s to write Piccalilli by hand first time without even thinking about it,” because I’ve written that word so many times that I just know exactly how to spell it off by heart. So the answer to your question is yes.

Drew: Well, I’m still struggling, I’ll tell you that much!

Andy: It is hard. Oh, God. I totally empathize. It took me a long time to learn how to spell it but it’s one of those words in our vocabulary. This year I’m trying to spell necessary without making a spelling mistake!

Drew: So I’ve been learning all about CUBE CSS. What have you been learning about lately, Andy?

Andy: Do you know what? This is going to surprise you, Drew. MySQL is what I’ve been learning about recently. So, basically, Piccalilli is totally self-published. It’s an Eleventy site but it’s got an API behind it, and that’s got a MySQL database behind it. The stuff that gives people content that they’ve purchased requires some pretty hefty querying. So I’ve just actually properly invested in some MySQL… especially the difference between joins, which I didn’t actually realize there was a difference between each type of join. So that’s been really useful and it’s resulted in some pretty speedy interactions with the database.

Andy: I used to run this thing called Front-End Challenges Club and when I first launched it it just collapsed and died on itself because MySQL was shoddy to say the least. So I’ve really been learning how to do that because I’m not a backend person at all, I’m a pixel-pusher. So it’s definitely not in my remit. That’s more your neck of the woods, isn’t it? I find it really cool, MySQL. I actually really like writing it. It’s a really nice, instructional language, isn’t it?

Drew: It is, it’s great. I learnt SQL at school.

Andy: Wow!

Drew: We’re talking like 20 years ago now.

Andy: Did they have computers in those days?

Drew: They did, yeah. We had to wind-

Andy: Did you have to write it by hand?

Drew: We had to wind them up! We did. But, I tell you, for a developer, it’s akin to learning your times tables: one of those things that seems like a bit of a chore but once you’re fluent, it just becomes useful time and time again.

Andy: Yeah. For sure. There’s really tangible differences as well. You really see the difference in speed. I really like working with Node because that’s really fast but Node and MySQL is just… not a very common choice, but I think it’s a pretty good choice. I think it works really, really well. So I’m happy with that. As you know, I don’t like writing PHP. So that’s never going to be an option.

Drew: If you, dear listener, would like to hear more from Andy, you can follow him on Twitter where he’s at hankchizljaw. You can find Piccalilli at piccalil.li, where you’ll also find the article describing CUBE CSS, and we’ll also add links to all of those in the show notes, of course.

Drew: Thanks for joining us today, Andy. Did you have any parting words?

Andy: Stay safe, and wear your mask.

Smashing Editorial
(il)

Source: smash mag

Categories
Uncategorized

Firebase Push Notifications In React

Firebase Push Notifications In React

Firebase Push Notifications In React

Chidi Orji

2020-06-29T13:30:00+00:00
2020-07-05T00:11:03+00:00

Notifications have become a stable part of the web nowadays. It’s not uncommon to come across sites asking for permission to send notifications to your browser. Most modern web browsers implement the push API and are able to handle push notifications. A quick check on caniuse shows that the API enjoys wide support among modern chrome-based browsers and Firefox browser.

There are various services for implementing notifications on the web. Notable ones are Pusher and Firebase. In this article, we’ll implement push notifications with the Firebase Cloud Messaging (FCM) service, which is “a cross-platform messaging solution that lets you reliably send messages at no cost”.

I assume that the reader has some familiarity with writing a back-end application in Express.js and/or some familiarity with React. If you’re comfortable with either of these technologies, then, you could work with either the frontend or backend. We will implement the backend first, then move on to the frontend. In that way, you can use whichever section appeals more to you.

So let’s get started.

Types Of Firebase Messages

The Firebase documentation specifies that an FCM implementation requires two components.

  1. A trusted environment such as Cloud Functions for Firebase or an app server on which to build, target, and send messages.
  2. An iOS, Android, or web (JavaScript) client app that receives messages via the corresponding platform-specific transport service.

We will take care of item 1 in our express back-end app, and item 2 in our react front-end app.

The docs also state that FCM lets us send two types of messages.

  1. Notification messages (sometimes thought of as “display messages”) are handled by the FCM SDK automatically.
  2. Data messages are handled by the client app.

Notification messages are automatically handled by the browser on the web. They can also take an optional data payload, which must be handled by the client app. In this tutorial, we’ll be sending and receiving data messages, which must be handled by the client app. This affords us more freedom in deciding how to handle the received message.

Setting Up A Firebase Project

The very first thing we need to do is to set up a Firebase project. FCM is a service and as such, we’ll be needing some API keys. This step requires that you have a Google account. Create one if you don’t already have one. You can click here to get started.

After setting up your Google account, head on to the Firebase console.

Click on add project. Enter a name for your project and click on continue. On the next screen, you may choose to turn off analytics. You can always turn it on later from the Analytics menu of your project page. Click continue and wait a few minutes for the project to be created. It’s usually under a minute. Then click on continue to open your project page.

Once we’ve successfully set up a project, the next step is to get the necessary keys to work with our project. When working with Firebase, we need to complete a configuration step for the frontend and backend separately. Let’s see how we can obtain the credentials needed to work with both.

Frontend

On the project page, click on the icon to add Firebase to your web app.

Add Firebase to a web project

Add Firebase to a web project. (Large preview)

Give your app a nickname. No need to set up Firebase hosting. Click on Register app and give it a few seconds to complete the setup. On the next screen, copy out the app credentials and store them somewhere. You could just leave this window open and come back to it later.

Firebase web app credentials

Firebase web app credentials. (Large preview)

We’ll be needing the configuration object later. Click on continue to console to return to your console.

Backend

We need a service account credential to connect with our Firebase project from the backend. On your project page, click on the gear icon next to Project Overview to create a service account for use with our Express backend. Refer to the below screenshot. Follow steps 1 to 4 to download a JSON file with your account credentials. Be sure to keep your service account file in a safe place.

Steps for creating a service account credential

Steps for creating a service account credential. (Large preview)

I’ll advise you not to download it until you’re ready to use it. Just remember to come back to these sections if you need a refresher.

So now we’ve successfully set up a Firebase project and added a web app to it. We’ve also seen how to get the credentials we need to work with both the frontend and backend. Let’s now work on sending push notifications from our express backend.

Getting Started

To make it easier to work through this tutorial, I’ve set up a project on Github with both a server and a client. Usually, you’ll have a separate repo for your backend and frontend respectively. But I’ve put them together here to make it easier to work through this tutorial.

Create a fork of the repo, clone it to your computer, and let’s get our front-end and back-end servers started.

  1. Fork the repo and check out the 01-get-started branch.
  2. Open the project in your code editor of choice and observe the contents.
  3. In the project root, we have two folders, client/ and server/. There’s also a .editorconfig file, a .gitignore, and a README.md.
  4. The client folder contains a React app. This is where we will listen for notifications.
  5. The server folder contains an express app. This is where we’ll send notifications from. The app is from the project we built in my other article How To Set Up An Express API Backend Project With PostgreSQL.
  6. Open a terminal and navigate to the client/ folder. Run the yarn install command to install the project dependencies. Then run yarn start to start the project. Visit http://localhost:3000 to see the live app.
  7. Create a .env file inside the server/ folder and add the CONNECTION_STRING environment variable. This variable is a database connection URL pointing to a PostgreSQL database. If you need help with this, check out the Connecting The PostgreSQL Database And Writing A Model section of my linked article. You should also provide the PORT environment variable since React already runs on port 3000. I set PORT=3001 in my .env file.
  8. Open a separate terminal and navigate to the server/ folder. Run the yarn install command to install the project dependencies. Run yarn runQuery to create the project database. Run yarn startdev to start the project. Visit http://localhost:3001/v1/messages and you should see some messages in a JSON format.

Frontend and backend servers running

Frontend and backend servers running. (Large preview)

React frontend app running

React frontend app running. (Large preview)

Express backend app running

Express backend app running. (Large preview)

Now that we have our front-end and back-end apps running, let’s implement notifications in the backend.

Setting Up Firebase Admin Messaging On The Backend

Sending out push notifications with FCM on the backend requires either the Firebase admin SDK or the FCM server protocols. We’ll be making use of the admin SDK in this tutorial. There’s also the notifications composer, which is good for “testing and sending marketing and engagement messages with powerful built-in targeting and analytics”.

In your terminal, navigate to the server/ folder and install the Admin SDK.

# install firebase admin SDK
yarn add firebase-admin

Open your .env file and add the following environment variable.

GOOGLE_APPLICATION_CREDENTIALS="path-to-your-service-account-json-file"

The value of this variable is the path to your downloaded service account credentials. At this point, you probably want to go back to the section where we created the service account for our project. You should copy the admin initialization code from there and also download your service account key file. Place this file in your server/ folder and add it to your .gitignore.

Remember, in an actual project, you should store this file in a very secure location on your server. Don’t let it get into the wrong hands.

Open server/src/settings.js and export the application credentials file path.

# export the service account key file path
export const googleApplicationCredentials = process.env.GOOGLE_APPLICATION_CREDENTIALS;

Create a file server/src/firebaseInit.js and add the below code.

import admin from 'firebase-admin';

import { googleApplicationCredentials } from './settings'

const serviceAccount = require(googleApplicationCredentials);

admin.initializeApp({
  credential: admin.credential.cert(serviceAccount),
  databaseURL: 'your-database-url-here'
});

export const messaging = admin.messaging();

We import the admin module from firebase-admin. We then initialize the admin app with our service account file. Finally, we create and export the messaging feature.

Note that I could have passed the path to my service account key file directly, but it is the less secure option. Always use environment variables when dealing with sensitive information.

To check that you completed the initialization successfully, open up server/src/app.js and include the following lines.

import { messaging } from './firebaseInit'
console.log(messaging)

We import the messaging instance and log it in the console. You should see something like the picture below. You should remove these once you verify that your admin is set up correctly.

Console log of messaging feature

Console log of messaging feature. (Large preview)

If you run into any problems, you can check out the 02-connect-firebase-admin branch of my repo for comparison.

Now that we’ve successfully setup admin messaging, let’s now write the code to send the notifications.

Sending Push Notifications From The Backend

FCM data message configuration is very simple. All you have to do is supply one or more target(s) and a JSON of the message you wish to send to the client(s). There are no required keys in the JSON. You alone decide what key-value pairs you want to include in the data. The data messages form works across all platforms, so our notification could also be processed by mobile devices.

There are additional configurations for other platforms. For example, there’s an android settings that only work with android devices and apns settings that work on only iOS devices. You can find the configuration guide here.

Create a file server/src/notify.js and enter the below code.

import { messaging } from './firebaseInit';

export const sendNotificationToClient = (tokens, data) => {
  // Send a message to the devices corresponding to the provided
  // registration tokens.
  messaging
    .sendMulticast({ tokens, data })
    .then(response => {
      // Response is an object of the form { responses: [] }
      const successes = response.responses.filter(r => r.success === true)
        .length;
      const failures = response.responses.filter(r => r.success === false)
        .length;
      console.log(
        'Notifications sent:',
        `${successes} successful, ${failures} failed`
      );
    })
    .catch(error => {
      console.log('Error sending message:', error);
    });
};

We created a function that accepts an array of token strings and a data object. Each token string represents a device that has accepted to receive notifications from our back-end application. The notification will be sent to each client in the tokens array. We’ll see how to generate the token in the front-end section of the tutorial.

The messaging instance’s sendMulticast method returns a promise. On success, we get an array from which we count the number of successes as well as failed notifications. You could certainly handle this response anyhow you want.

Let’s use this function to send out a notification each time a new message is added to the database.

Open server/src/controllers/message.js and update the addMessage function.

import { sendNotificationToClient } from '../notify';

export const addMessage = async (req, res) => {
  const { name, message } = req.body;
  const columns = 'name, message';
  const values = `'${name}', '${message}'`;
  try {
    const data = await messagesModel.insertWithReturn(columns, values);
    const tokens = [];
    const notificationData = {
      title: 'New message',
      body: message,
    };
    sendNotificationToClient(tokens, notificationData);
    res.status(200).json({ messages: data.rows });
  } catch (err) {
    res.status(200).json({ messages: err.stack });
  }
};

This function handles a post request to the /messages endpoint. Once a message is successfully created, a notification is sent out by the sendNotificationToClient function followed by the response to the client. The only missing piece in this code is the tokens to send the notifications to.

When we connect the client app, we’ll copy the generated token and paste it in this file. In a production app, you’ll store the tokens somewhere in your database.

With this last piece of code, we’ve completed the back-end implementation. Let’s now switch over to the frontend.

The corresponding branch in my repo at this point is 03-send-notification.

Setting Up Firebase Messaging Notifications On The Client

Let’s take a look at the main components of our front-end React app.

Open up client/src/App.js and inspect the content. I’ll leave out most of the import statements and just look at the program logic.

# library imports

import { Messaging } from './Messaging';

axios.defaults.baseURL = 'http://localhost:3001/v1';

const App = () => {
  return (
    <Fragment>
      <ToastContainer autoClose={2000} position="top-center" />
      <Navbar bg="primary" variant="dark">
        <Navbar.Brand href="#home">Firebase notifictations with React and Express</Navbar.Brand>
      </Navbar>
      <Container className="center-column">
        <Row>
          <Col>
            <Messaging />
          </Col>
        </Row>
      </Container>
    </Fragment>
  );
};
export default App;

This is a regular react component styled with react-bootstrap. There’s a toast component right at the top of our app, which we shall use to display notifications. Note that we also set the baseURL for the axios library. Everything of note happens inside the <Messaging /> component. Let’s now take a look at its content.

Open up client/src/Messaging.js and inspect the content.

export const Messaging = () => {
  const [messages, setMessages] = React.useState([]);
  const [requesting, setRequesting] = React.useState(false);

  React.useEffect(() => {
    setRequesting(true);
    axios.get("/messages").then((resp) => {
      setMessages(resp.data.messages);
      setRequesting(false);
    });
  }, []);

  return (
    <Container>
      {/* form goes here */}
      <div className="message-list">
        <h3>Messages</h3>
        {requesting ? (
          <Spinner animation="border" role="status">
            <span className="sr-only">Loading...</span>
          </Spinner>
        ) : (
          <>
            {messages.map((m, index) => {
              const { name, message } = m;
              return (
                <div key={index}>
                  {name}: {message}
                </div>
              );
            })}
          </>
        )}
      </div>
    </Container>
  );
};

We have two state variables, messages and requesting. messages represent the list of messages from our database and requesting is for toggling our loader state. We have a React.useEffect block where we make our API call to the /messages endpoint and set the returned data in our messages state.

In the return statement, we map over the messages and display the name and message fields. On the same page, we include a form for creating new messages.

<Formik
  initialValues={{
    name: "",
    message: "",
  }}
  onSubmit={(values, actions) => {
    setTimeout(() => {
      alert(JSON.stringify(values, null, 2));
      actions.setSubmitting(false);
      toast.success("Submitted succesfully");
    }, 1000);
  }}
>
  {(prop) => {
    const { handleSubmit, handleChange, isSubmitting } = prop;
    return (
      <>
        <InputGroup className="mb-3">
          <InputGroup.Prepend>
            <InputGroup.Text id="basic-addon1">Name</InputGroup.Text>
          </InputGroup.Prepend>
          <FormControl
            placeholder="Enter your name"
            onChange={handleChange("name")}
          />
        </InputGroup>
        <InputGroup className="mb-3">
          <InputGroup.Prepend>
            <InputGroup.Text id="basic-addon1">Message</InputGroup.Text>
          </InputGroup.Prepend>
          <FormControl
            onChange={handleChange("message")}
            placeholder="Enter a message"
          />
        </InputGroup>
        {isSubmitting ? (
          <Button variant="primary" disabled>
            <Spinner
              as="span"
              size="sm"
              role="status"
              animation="grow"
              aria-hidden="true"
            />
            Loading...
          </Button>
        ) : (
          <Button variant="primary" onClick={() => handleSubmit()}>
            Submit
          </Button>
        )}
      </>
    );
  }}
</Formik>

We’re using the Formik library to manage our form. We pass the <Formik /> component an initialvalues props, an onSubmit prop and the form component we want to render. In return, we get back some handy functions such as handleChange which we can use to manipulate our form inputs, and handleSubmit which we use to submit the form. isSubmitting is a boolean that we use to toggle the submit button state.

I encourage you to give formik a try. It really simplifies working with forms. We will replace the code in the onSubmit method later.

Let’s now implement the method that will request a browser’s permission and assign it a token.

To start using Firebase in the frontend, we have to install the Firebase JavaScript client library. Note that this is a different package from the firebase-admin SDK.

# install firebase client library
yarn add firebase

Create a file client/src/firebaseInit.js and add the following content.

import firebase from 'firebase/app';
import 'firebase/messaging';

const config = {
  apiKey: "API-KEY",
  authDomain: "AUTH-DOMAIN",
  databaseURL: "DATABASE-URL",
  projectId: "PROJECT-ID",
  storageBucket: "STORAGE-BUCKET",
  messagingSenderId: "MESSAGING-SENDER-ID",
  appId: "APP-ID"
};

firebase.initializeApp(config);
const messaging = firebase.messaging();

// next block of code goes here

The Firebase docs state that:

“The full Firebase JavaScript client includes support for Firebase Authentication, the Firebase Realtime Database, Firebase Storage, and Firebase Cloud Messaging.”

So here, we import only the messaging feature. At this point, you could refer to the section on creating a Firebase project to get the config object. We then initialize Firebase and export the messaging feature. Let’s add in the final block of code.

export const requestFirebaseNotificationPermission = () =>
  new Promise((resolve, reject) => {
    messaging
      .requestPermission()
      .then(() => messaging.getToken())
      .then((firebaseToken) => {
        resolve(firebaseToken);
      })
      .catch((err) => {
        reject(err);
      });
  });

export const onMessageListener = () =>
  new Promise((resolve) => {
    messaging.onMessage((payload) => {
      resolve(payload);
    });
  });

The requestFirebaseNotificationPermission function requests the browser’s permission to send notifications and resolves with a token if the request is granted. This is the token that FCM uses to send a notification to the browser. It is what triggers the prompt you see on browsers asking for permission to send a notification.

The onMessageListener function is only invoked when the browser is in the foreground. Later, we will write a separate function to handle the notification when the browser is in the background.

Open up client/src/App.js and import the requestFirebaseNotificationPermission function.

import { requestFirebaseNotificationPermission } from './firebaseInit'

Then inside the App function, add the below code before the return statement.

requestFirebaseNotificationPermission()
  .then((firebaseToken) => {
    // eslint-disable-next-line no-console
    console.log(firebaseToken);
  })
  .catch((err) => {
    return err;
  });

Once the app loads this function runs and requests the browser’s permission to show notifications. If the permission is granted, we log the token. In a production app, you should save the token somewhere that your backend can access. But for this tutorial, we’re just going to copy and paste the token into the back-end app.

Now run your app and you should see the notification request message. Click allow and wait for the token to be logged to the console. Since you’ve granted the browser permission, if we refresh the page you won’t see the banner anymore, but the token will still be logged to the console.

App request to show notifications

App request to show notifications. (Large preview)

You should know that Firefox browser (v75) doesn’t ask for notification permission by default. The permission request has to be triggered by a user-generated action like a click.

This is a good point for me to commit my changes. The corresponding branch is 04-request-permission.

Let’s now complete the code for saving a message to our database.

Open up client/src/Messaging.js and replace the onSubmit function of our form with the below code.

onSubmit={(values, actions) => {
  axios
    .post("/messages", values)
    .then((resp) => {
      setMessages(resp.data.messages.concat(messages));
      actions.setSubmitting(false);
      toast.success("Submitted succesfully");
    })
    .catch((err) => {
      console.log(err);
      toast.error("There was an error saving the message");
    });
}}

We make a post request to the /messages endpoint to create a new message. If the request succeeds we take the returned data and put it at the top of the messages list. We also display a success toast.

Let’s try it out to see if it works. Start the front-end and back-end servers. Before trying out the post request, open server/src/controllers/messages.js and comment out the line where we’re sending the notification.

# this line will throw an error if tokens is an empty array comment it out temporarily
// sendNotificationToClient(tokens, notificationData);

Try adding some messages to the database. Works? That’s great. Now uncomment that line before continuing.

Copy the notification token from the developer console and paste it into the tokens array. The token is a very long string, as shown below.


    const tokens = [
      'eEa1Yr4Hknqzjxu3P1G3Ox:APA91bF_DF5aSneGdvxXeyL6BIQy8wd1f600oKE100lzqYq2zROn50wuRe9nB-wWryyJeBmiPVutYogKDV2m36PoEbKK9MOpJPyI-UXqMdYiWLEae8MiuXB4mVz9bXD0IwP7bappnLqg',
    ];

Open client/src/Messaging.js, import the onMessageListener and invoke it just under the useEffect block. Any position within the function is fine as long it’s before the return statement.

import { onMessageListener } from './firebaseInit';

  React.useEffect(() => {
    ...
  }, []);

  onMessageListener()
    .then((payload) => {
      const { title, body } = payload.data;
      toast.info(`${title}; ${body}`);
    })
    .catch((err) => {
      toast.error(JSON.stringify(err));
    });

The listener returns a promise which resolves to the notification payload on success. We then display the title and body in a toast. Note that we could have taken any other action once we receive this notification but I’m keeping things simple here. With both servers running, try it out and see if it’s working.

Works? That’s great.

In case you run into problems, you could always compare with my repo. The corresponding branch at this point is 05-listen-to-notification.

There’s just one bit we need to take care of. Right now we can only see notifications when the browser is in the foreground. The point about notifications is that it should pop up whether the browser is in the foreground or not.

If we were to be sending a display message i.e. we included a notification object in our notification payload, the browser will take care of that on its own. But since we’re sending a data message, we have to tell the browser how to behave in response to a notification when our browser is in the background.

To handle the background notification we need to register a service worker with our front-end client.

Create a file client/public/firebase-messaging-sw.js and enter the following content:

importScripts('https://www.gstatic.com/firebasejs/7.14.2/firebase-app.js');
importScripts('https://www.gstatic.com/firebasejs/7.14.2/firebase-messaging.js');

const config = {
  apiKey: "API-KEY",
  authDomain: "AUTH-DOMAIN",
  databaseURL: "DATABASE-URL",
  projectId: "PROJECT-ID",
  storageBucket: "STORAGE-BUCKET",
  messagingSenderId: "MESSAGING-SENDER-ID",
  appId: "APP-ID"
};

firebase.initializeApp(config);
const messaging = firebase.messaging();

messaging.setBackgroundMessageHandler(function(payload) {
  console.log('[firebase-messaging-sw.js] Received background message ', payload);
  const notificationTitle = payload.data.title;
  const notificationOptions = {
    body: payload.data.body,
    icon: '/firebase-logo.png'
  };
  return self.registration.showNotification(notificationTitle,
    notificationOptions);
});

self.addEventListener('notificationclick', event => {
  console.log(event)
  return event;
});

At the top of the file, we’re importing the firebase-app and the firebase-messaging libraries since we only need the messaging feature. Don’t worry if the import syntax is new. It’s a syntax for importing external scripts into service worker files. Make sure that the version being imported is the same as the one in your package.json. I’ve run into issues that I solved by harmonizing the versions.

As usual, we initialize Firebase, then we invoke the setBackgroundMessageHandler, passing it a callback, which receives the notification message payload. The remaining part of the code specifies how the browser should display the notification. Notice that we can also include an icon to display as well.

We can also control what happens when we click on the notification with the notificationclick event handler.

Create a file client/src/serviceWorker.js and enter the below content.

export const registerServiceWorker = () => {
  if ('serviceWorker' in navigator) {
    navigator.serviceWorker
      .register('firebase-messaging-sw.js')
      .then(function (registration) {
        // eslint-disable-next-line no-console
        console.log('[SW]: SCOPE: ', registration.scope);
        return registration.scope;
      })
      .catch(function (err) {
        return err;
      });
  }
};

This function registers our service worker files. Note that we have replaced the more detailed version generated by React. We first check if the serviceWorker is present in the navigator object. This is simple browser support. If the browser supports service workers, we register the service worker file we created earlier.

Now open client/src/index.js, import this function, and invoke it.

# other imports

import { registerServiceWorker } from './serviceWorker'

ReactDOM.render(
  ...
);

registerServiceWorker()

If all goes well, you should see the service worker’s scope logged to your console.

Open http://localhost:3000/messaging in a second browser and create a message. You should see the notification from the other browser come into view.

Background and foreground notifications

Background and foreground notifications. (Large preview)

With that, we’ve come to the end of this tutorial. The corresponding branch in my repo is 06-handle-background-notification.

Conclusion

In this article, we learned about the different types of notification messages we can send with the Firebase Cloud Messaging (FCM). API. We then implemented the “data message” type on the backend. Finally, we generated a token on the client app which we used to receive notification messages triggered by the back-end app. Finally, we learned how to listen for and display the notification messages when the browser is in either the background or foreground.

I encourage you to take a look at the FCM docs to learn more.

Smashing Editorial
(ks, ra, yk, il)

Source: smash mag

Categories
Uncategorized

Inspired Design Decisions With Giovanni Pintori: Publicity Becomes An Art Form

Inspired Design Decisions With Giovanni Pintori: Publicity Becomes An Art Form

Inspired Design Decisions With Giovanni Pintori: Publicity Becomes An Art Form

Andrew Clarke

2020-06-26T14:00:00+00:00
2020-07-05T00:11:03+00:00

With one or two occasional exceptions, I’ve spent the past twenty-two years designing for countless clients. A few of these projects lasted a year, some several months, but the majority for no more than a few weeks.

Being completely absorbed for a few weeks or months in designing a product interface or a website can be a thrill. It often starts with the gratification which comes from winning the work. Gaining a new client’s confidence and trust can be addictive. During the seductive “getting to know you” phase, you learn about the client and what they expect from you and your work. Addictive personalities like mine crave the intensity of these feelings, but — just like some relationships — the initial excitement soon fades into the realities of working together.

This creative promiscuity has suited my often short attention span and restless curiosity very well. But, there were times I wished I could stay with an organization for longer, get to know them better, and be a positive influence on what they do and make.

I know many designers who work in-house. While I never envy their commute or the money they spend on living close to work, there’s a part of me that envies their ability to stay and shape the long-term creative direction of a company in the way which Giovanni Pintori helped Olivetti.

“In our day and age, publicity has become an art form, and increasingly needs to live up to this name. Publicity is a form of discourse that should eschew vagueness in favor of brevity, clarity, and persuasiveness. Those who engage in publicity (writers, painters, architects) need logic and imagination in equal measure.”

— Giovanni Pintori

Italian designer Giovanni Pintori worked for business products manufacturer Olivetti for over 31 years. During this time, his style developed into the company’s unique design vocabulary. The appeal of working with one company for longer than a few months has become stronger as I’ve got older. For the past 18 months, I’ve devoted most of my time to working with a Swiss cybersecurity company, based not far from Milan and where Giovanni Pintori called home.

Like Olivetti, this company values design in every form. While my top priority is the design of the company’s products, I’ve also had the opportunity to influence their brand, marketing, and overall creative direction.

I still spend time on other people’s projects when the work attracts me, but I’ve learned how rewarding a long-term client relationship can be. I’m happy and more creatively satisfied than I have been in years. Plus, as old age catches up with me, I don’t have the energy to chase every attractive project like I used to.

Read More From The Series

Inspired By Giovanni Pintori

Born in Sardinia in 1912, Giovanni Pintori became one of the most influential European graphic designers of the 20th century. He became known for the distinctive style he crafted into Olivetti’s design language for over 30 years.

Pintori studied design at Italy’s influential L’Istituto Superiore per le Industrie Artistiche (Higher Institute for Artistic Industries) where he was surrounded by the creative arts. ISIA was a progressive school where students studied ceramics, painting, metalwork, and woodwork.

While studying at ISIA, Pintori met Renato Zveteremich the advertising director and publicist who headed Olivetti’s advertising department during the 1930s. After graduating from HIAI, Pintori joined Olivetti to work under Zveteremich and became the company’s art director in 1950.

poster by Giovanni Pintori and etching by Joan Miró

Left: Tir à l’arc (1972) etching and aquatint by Joan Miró. Right: Olivetti Lettera 22 poster (1954) designed by Giovanni Pintori. (Large preview)

Olivetti manufactured business machines, most famously its range of typewriters. When Pintori joined Olivetti, the company was already known for its original product designs. Its products were immediately recognizable, and under the guidance of industrial designer Marcello Nizzoli, every detail of their designs—from the shape of a spacebar to the color of their outer casings was carefully considered.

“If artists are called upon to interpret, express, and defend the functional purity of a machine, it is truly a sign that the machine has entered the human spirit and that the problem of forms and relationships is still of an intuitive nature.”

— Renato Zveteremich

But Olivetti’s preoccupation with design didn’t end with its products. Creativity was an essential part of the company’s culture which was evident from the architecture of its factories and offices to its advertising and graphic design used to promote its products.

Over his 30 year career at Olivetti, Pintori designed the company’s advertising, brochures, and even their annual calendars. Pintori’s aesthetic style was bold and confident. He used bright colors from minimal color palettes and combined them with shapes to fill his designs with energy.

But Pintori’s work wasn’t just playful, it was thoughtful. His choice of shapes wasn’t abstract. Shapes suggested the benefits of using a product rather than describe its features literally. Pintori didn’t just illustrate products, he brought them to life through his designs by suggesting how they might be used and what they could do to enhance people’s lives and work.

“I do not attempt to speak on behalf of the machines. Instead, I have tried to make them speak for themselves, through the graphic presentation of their elements, their operations and their use.”

Pintori defined Olivetti’s image far beyond his time at the company, and he continued to work on projects with them after leaving in 1967. He established his own studio in Milan, where he worked as a freelance designer, before retiring and dedicating himself to painting.

painting by Jasper Johns and poster by Giovanni Pintori

Left: Numbers in Color (1958–59) painting by Jasper Johns. Right: Olivetti numbers poster (1949) designed by Giovanni Pintori. (Large preview)

Giovanni Pintori died in Milan in 1999, and there’s a book, Pintori by Marta Sironi and published by Moleskine which catalogs his astonishing career.

Pintori’s work inspires not only because of the boldness of his colorful shapes, but because of what they represent. Pintori understood that promoting a product required more than listing its features. Publicity should tell a story that resonates with customers, and that’s a lesson we should all be inspired by.

brochures designed by Giovanni Pintori

Left: Olivetti Lettera 22 brochure designed by Giovanni Pintori, 1954. Right: Olivetti Lettera 22 brochure designed by Giovanni Pintori, 1965. (Large preview)

posters designed by Giovanni Pintori

Left: Olivetti Graphika poster designed by Giovanni Pintori, 1956. Right: Olivetti Tetractys poster designed by Giovanni Pintori, 1956. (Large preview)

Creating Color Palettes

The colors we choose should tell a story about a company, product, or service as eloquently as our layout or typography. Our color choices can attract someone’s attention, influence their perception of what we do, and even stimulate emotions. Color plays an essential role in making a product or website easy and intuitive to use. As well as brand colors, color palettes for the web help people to navigate, let them know what they can press, and where they’ve been.

I like to keep my colors simple, and my palettes rarely contain more than three hues; a dominant color, secondary or supporting color, and an accent. To these, I add a small selection of neutral colors for use as backgrounds, borders, and text.

To add depth to my designs — and to give me greater flexibility — I also introduce shades and tints of each of my hues. I use darker shades for borders — for example — around buttons — and lighter tints to add highlights.

Left: My color palette. Right: Demonstrating percentage use.

Left: My color palette. Right: Demonstrating percentage use. (Large preview)

Since operating system dark modes have become more prevalent, I also subtly alter the lightness and saturation of colors in my palettes, so they appear more vibrant against dark backgrounds.

color palette against a dark background

Left & right: Presenting a color palette against a dark background. (Large preview)

Using Primary Colors

Pintori-inspired design

The primary colors used in this Pintori-inspired design emphasise the clarity of its message and the simplicity of its layout. (Large preview)

The HTML needed to implement my first Pintori-inspired design is meaningful and simple as the design itself. I need just four structural elements; a header which contains two SVGs of the iconic Morris Traveller’s profile, the main element for my running text, an SVG of the Traveller’s front, and finally a footer which contains the Morris Motors company logo:

<header>  
  <svg>…</svg>
  <svg>…</svg>
</header>

<main>
  <h1>…</h1>
  <p>…</p>
</main>

<figure>
  <svg>…</svg>
</figure>

<footer>
  <svg>…</svg>
</footer>

While external SVG files will be cached and ready to render, I now embed SVG in my HTML whenever possible. Fewer external files mean fewer HTTP requests, but the benefits of embedding go far beyond performance.

Subtle changes in color saturation and lightness between light themes and dark modes are often necessary to maintain the punchiness of design elements against contrasting background colors. When an SVG is embedded in HTML, its fills and strokes can be subtlety altered using CSS.

I start by applying color and typography foundation styles for the distinguished dark version of my design. These include Moderna Sans, a versatile sans-serif typeface designed by Luciano Vergara and Alfonso García which I chose to evoke the style of Pintori’s work for Olivetti:

body {
padding: 2rem;
background-color: #262626;
font-family: "moderna_sans-light";
color: #fff; }

h1 {
font-family: "moderna_sans-bold-cnd-it";
font-size: 2.8rem;
font-weight: normal;
line-height: 1; }

Flexbox transforms my header into a horizontally scrolling panel, one of the most effective ways to maintain visual hierarchy in a small screen design:

header {
display: flex;
flex-wrap: nowrap;
overflow-x: scroll; }

The flex-grow property with its value of 1 ensures all images expand to fill any available space, while flex-basis makes sure these flex items start from a minimum of 640px;

header svg {
flex-grow: 1;
flex-basis: 640px; }
header svg:not(:last-of-type) {
margin-right: 2rem; }

Finally, I add large amounts of horizontal padding and align the Morris logo to the centre of my footer:

footer {
padding-right: 8rem;
padding-left: 8rem;
text-align: center; }

My horizontal scrolling panel adds interest to a small screen, but the extra space available on medium-size screens allows me to show more of my quintessentially English Travellers.

CSS Grid offers the precise placement and stacking of elements which Flexbox lacks and is the perfect choice for this header on medium to large screens. I change the display property’s value from flex to grid, then add three symmetrical columns and rows.

While the width of the two outer columns is fixed at 270px, the inner column expands to fill all remaining space. I use a similar technique for the three rows, fixing the outer two at a height of 100px. This offsets the position of both images and adds depth to this design:

header {
display: grid;
grid-template-columns: 270px 1fr 270px;
grid-template-rows: 100px 1fr 100px; }

Using pseudo-class selectors and line numbers, I place the first SVG, then reduce it in size to add perspective:

header svg:first-of-type {
grid-column: 2 / 4;
grid-row: 1 / 2;
transform: scale(.85); }

Then, I place the second of my two graphics. I raise it within the stacking order by adding a higher z-index value which brings it visually closer to the viewer:

header svg:last-of-type {
grid-column: 1 / 3;
grid-row: 2 / 4;
z-index: 2; }

Even a seemingly mundane even-ratio grid can result in an original layout when a design includes plenty of whitespace to help lead the eye. For this medium-size design, I apply a symmetrical six-column grid with column and rows gap values which are proportional to the width and height of a screen:

@media (min-width: 48em) {
body {
display: grid;
grid-template-columns: repeat(6, 1fr);
column-gap: 2vw;
row-gap: 2vh; }
}

My header element fills the entire width of my grid. Then, I place the main, figure, and footer elements, adding proportionally more white space to narrow the width of my figure and footer:

header {
grid-column: 1 / -1; }

main {
grid-column: 2 / 6; }

figure {
grid-column: 3 / 5; }

footer {
grid-column: 3 / 5;
padding-right: 4rem;
padding-left: 4rem; }

This design becomes more distinguished with the space available on large screens.

For them, I apply grid values to the body element to create the eight columns of a 6+4 compound grid:

@media (min-width: 64em) {

body {
grid-template-columns: 2fr 1fr 1fr 2fr 2fr 1fr 1fr 2fr; }
}

Basing my medium-size design on six columns, then including the same grid in my large screen compound, helps to maintain proportions throughout all sizes of my design. Then, I reposition the four structural elements onto my new grid:

header {
grid-column: 1 / 8; }

main {
grid-column: 2 / 5;
text-align: right; }

figure {
grid-column: 5 / 7; }

footer {
grid-column: 4;
padding: 0; }

Finally, to create a solid block of content in the centre of my design, I bind the main content to its now adjacent figure by realigning its text to the right:

main {
text-align: right; }

Primary colors

Left: Primary colors against an off-white background. Right: Primary colors stand out against this dark background. (Large preview)

The monochromatic color palettes used in my next design. (Large preview)

Monochromatic Palettes

Even after over twenty years in the business, I still find working with color the most challenging aspect of design. Perhaps that’s why I so often gravitate towards monochromatic color schemes because they make achieving a visually cohesive look quite simple.

Monochromatic color palettes contain variations in shade, tints, and tones, by adding varying percentages of black, grey, or white to a chosen base color.

  • Shades: Darken color using black
  • Tints: Lighten color using white
  • Tones: Desaturate color using grey

When they’re used for backgrounds, borders, and details, shades and tints can make a design feel harmonious.

Using shades, tints, and tones can help to tone down vibrant colors which might draw unwanted attention to aspects of a design. They are particularly useful when developing a more varied color palette from a set of existing brand colors.

I often choose either a purely monochromatic or partially-monochromatic palette which includes an accent color. This added color acts as a counterpoint to the base color and gives a design greater depth.

tints and tones

Left: Shades: 100%–50% Center: Tints: 100%–50% Right: Tones: 100%–50%. (Large preview)

Limiting The Palette

full-color version of a Pintori-inspired design

This full-color version of my Pintori-inspired design includes several monochromatic elements placed onto a modular grid. (Large preview)

Thanks to CSS Grid, background image gradients, and pseudo elements, this next Pintori-inspired design achieves enormous value from a very small set of HTML elements. I need only a headline, a single paragraph, plus seven empty divisions. I give each division its own identity. This allows me to give them their own distinctive style:

<h1>…</h1>
<p>…</p>
<div id="panel-a"></div>
<div id="panel-b"></div>
<div id="panel-c"></div>
<div id="panel-d"></div>
<div id="panel-e"></div>
<div id="panel-f"></div>
<div id="panel-g"></div>

This HTML places the headline and paragraph before the seven panels, but look closely at the finished small screen design, and you’ll see this content has been reordered to place the Morris logo, then a picture of the Traveller’s front-end at the top.

Whereas I often introduce grid properties to medium and large screens, CSS Grid is also useful for reordering content on smaller screens. For this design, I change the body element’s display value to grid, then introduce a viewport height-based gap between the intrinsic, unspecified rows:

body {
display: grid;
row-gap: 2vh; }

Then, I reorder the panels which contain my Morris Motors logo and image, plus the headline, using row line numbers:

#panel-d { grid-row: 1; }
#panel-e { grid-row: 2; }
h1 { grid-row: 3; }

Because my panel divisions have no other elements, their height will collapse to zero, leaving only their borders. To ensure there’s space to display their generated backgrounds and content, I specify a minimum height for all panels:

[id*="panel"] {
min-height: 380px; }

The panel which appears first in my small screen design shows the Morris Motors logo, which I insert using a CSS generated content data URI. If you’re unfamiliar with this handy content type, a data URI is a file which has been encoded into a string. You can use a data URI anywhere in your CSS or HTML:

<img src="data:image/png…">
<img src="data:image/svg+xml…">
div { 
background-image: url("data:image/svg+xml…"); }

When a browser finds a data URI, it decodes the content and reconstructs the original file. Data URIs aren’t limited to encoded images but are frequently used to encode PNG format images and SVGs. You will find several tools for converting images to data URIs online.

First, I change the minimum height of this panel to match the height of my logo, then I insert the logo:

#panel-d {
min-height: 90px;
text-align: center; }

#panel-d:before {
content: url("data:image/svg+xml…");
display: block;
width: 135px;
height: 90px;
margin: 0 auto; }

I use a similar technique to place a background image behind my paragraph. I add repeat, position, and size properties which make the background flexible and place it always at the horizontal and vertical centre of my paragraph:

p {
background-image: url("data:image/svg+xml…"); }

p {
background-repeat: no-repeat;
background-position: 50% 50%;
background-size: 50% 50%; }

panels with distinctive graphic design

Each one of the panels has its own distinctive graphic design. (Large preview)

Each one of my panels has its own distinctive graphic design. I could’ve placed images into these seven panels, but this would’ve required at least seven additional HTTP requests. So instead, I use various combinations of multiple background images using data URIs and CSS gradients to achieve the results I need.

The first panel contains a graphic of the Morris’s hub cap over a striped blue, white, and black background. The hub cap background image comes from a data URI:

#panel-a {
background-image: url("data:image/svg+xml…"); }

Then, I add the second, striped background image using a linear-gradient:

#panel-a {
background-image: url("data:image/svg+xml…"),
linear-gradient(
to bottom, 
#34749F,
#34749F 65px,
#fff 65px,
#fff 80px,
#262626 80px); }

I specify two sets of comma separated repeat, position, and size values, remembering to keep them in the same order as my background images:

#panel-a {
background-repeat: no-repeat, repeat-x;
background-position: 50% 100%, 0 0;
background-size: 75% 75%, auto auto; }

This next panel includes two SVG images, followed by more complex black, yellow, and white stripes. By placing color stops with different colors at the same position in my gradient, I create a striped background with hard lines between my colors:

#panel-b {
background-image: 
url("data:image/svg+xml…"),
url("data:image/svg+xml…"),
linear-gradient(
to bottom, 
#B5964D,
#B5964D 125px,
#262626 125px,
#262626 140px,
#fff 140px,
#fff 155px,
#262626 155px); }

#panel-b {
background-repeat: no-repeat, no-repeat, repeat-x;
background-position: 50% 45px, 50% 190px, 0 0;
background-size: 90%, 90%, auto; }

I developed each of my panels using different combinations of these same techniques, making them fast loading and flexible. It’s rare to find designs online which are based on a modular grid, but it is the perfect choice for this Pintori-inspired, large screen design. This modular grid is comprised of three columns and rows.

I add grid properties to the body element, then specify my column widths to fill all available space. To make sure there’s always enough height to show the content of each panel, I use Grid’s minmax value, setting the minimum height at 300px and the maximum at 1fr:

@media (min-width: 64em) {

body {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
grid-template-rows: repeat(3, minmax(300px, 1fr));
gap: 1rem;
min-height: 100vh; }
}

Elements in this design don’t overlap, so I use grid-template-areas for their simplicity. This design has nine grid areas, and I give each one a single letter name, a–h. As the letter d is used for two adjacent areas, the item placed using that letter will occupy both:

body {
grid-template-areas: 
"a b c"
"d d e"
"f g h"; }

In this large screen implementation, the CSS Grid minmax value controls the height of my rows, making the min-height I applied earlier redundant:

[id*="panel"] {
min-height: none; }

I place my panels using area names which allows me to change where they appear in my layout without altering their position in my HTML:

#panel-a { grid-area: a; }
#panel-b { grid-area: b; }
#panel-c { grid-area: c; }
#panel-d { grid-area: d; }
#panel-e { grid-area: e; }
#panel-f { grid-area: f; }
#panel-g { grid-area: g; }
p { grid-area: h; }

While the design of my panels remains consistent across screen sizes, there’s one panel where the content and backgrounds change for larger screens. This panel contains the familiar Morris logo and what appears to be the main headline, “Style… in a BIG way.”

To develop this panel, I first add a deep solid border at the top, followed by a data URI background image:

#panel-d {
border-top: 15px solid #262626;
background-image: url("data:image/svg+xml…"); }

Then, I add a second gradient background image which creates the black panel and two vertical yellow stripes:

#panel-d {
background-image: url("data:image/svg+xml…"),
linear-gradient(
to right, 
#fff,
#fff 280px,
#B5964D 280px,
#B5964D 320px,
#fff 320px,
#fff 335px,
#262626 335px,
#262626 calc(100% - 40px),
#F2C867 calc(100% - 40px),
#F2C867 100%); }

Earlier in my process, I used a :before pseudo-element to add the Morris logo to this design. For large screens, I reposition that logo to the bottom-left of my panel:

#panel-d
position: relative; }
#panel-d:before {
position: absolute;
bottom: 0;
left: 0;
margin: 0; }

My large headline is immediately descended from the HTML body and is not part of this panel, making it tricky to position across flexible screen sizes. To reproduce my design precisely, without compromising accessibility, I first use an accessible method to hide this headline visually for people who use screen readers:

h1 { 
position: absolute !important;
height: 1px; 
width: 1px;
overflow: hidden;
clip: rect(1px, 1px, 1px, 1px);
white-space: nowrap; }

Then, I reinstate the headline’s text using generated content and an :after pseudo-element. I position it to the bottom-right of my panel and replicate its bold, condensed, italic style:

#panel-d:after {
content: "Style… in a BIG way";
position: absolute;
bottom: 0;
right: 0;
font-family: "moderna_sans-bold-cnd-it";
font-size: 2.8rem;
line-height: 1;
text-align: right; }

Pintori-inspired design

Left: Monochrome version. Right: Full-color version of this Pintori-inspired design. (Large preview)

Four complementary palettes include two colors on opposite sides of a color wheel

Four complementary palettes include two colors on opposite sides of a color wheel. (Large preview)

Complementary Palettes

Colors that complement each other sit on opposite sides of a color wheel. But, although it’s easy to understand their mathematical relationship, working with complementary colors can be challenging.

Adjacent complementary color combinations can look harsh, and rather than complement each other, can feel unharmonious. To prevent them from clashing, use shades, tints, or tones of complementary colors which will also help to expand your palette of usable colors.

Alternatively, use split complementary colors where instead of opposing colors, the palette includes two colors on either side of the complementary.

Split complementary palette includes two colors on either side of the complementary.

Split complementary palette includes two colors on either side of the complementary. (Large preview)

Complementing Colors

Complementary pairings add personality to this Pintori-inspired design.

Complementary pairings add personality to this Pintori-inspired design. (Large preview)

Several colorfully complementary Morris Traveller blueprints overlap in my next Pintori-inspired design. The HTML needed to develop this design is as minimal as the depictions of this car. A banner division includes an SVG of the Morris logo, and the main element contains the headline and running text.

But, the flexibility of this design across several screen sizes comes from using two picture elements, each containing three images. I include one picture element in the header, then another in my footer:

<div class="banner">
  <svg>…</svg>
</div>

<header>
  <picture>
    <source media="(min-width: 72em)">
    <source media="(min-width: 48em)">
    <img>
  </picture>
</header>

<main>
  <h1><b>…</b></h1>
  <p>…</p>
</main>

<footer>
  <picture>
    <source media="(min-width: 72em)">
    <source media="(min-width: 48em)">
    <img>
  </picture>
</footer>

Every one of my development projects starts by adding the now familiar foundation styles, this time adding an off-white background color and almost black sans-serif text:

body {
background-color: #f3f2f2;
font-family: "moderna_sans-light";
color: #262626; }

I align the content of my banner division to the centre, then set the logo’s maximum width to a diminutive 150px:

.banner {
text-align: center; }

.banner svg {
max-width: 150px; }

The main headline in this design is set in the bold, condensed, italic style of Moderna Sans:

h1 {
font-family: "moderna_sans-bold-cnd-it";
font-size: 2.027rem;
font-weight: normal;
line-height: 1.2; }

Part of this headline is enclosed within a span element which enables me to change its color to match other aspects of this design, including the bull emblem at the center of the Morris Motors logo:

h1 span {
color: #df4561; }

#logo .emblem {
fill: #df4561; }

On small screens, both the header and footer contain a single Traveller image. When there’s space to place two Travellers side-by-side, a browser changes the images in the two picture elements.

For medium-size screens, I make use of the extra space available space and introduce a four-column symmetrical grid:

@media (min-width: 48em) {

body {
display: grid;
grid-template-columns: repeat(4, 1fr); }
}

I place the banner division in the two centre columns, centre my logo, then shift it vertically to fit between the bumpers of my two Travellers:

.banner {
grid-column: 2 / 4; 
text-align: center;
transform: translateY(2vh); }

Both my header and footer span the grid from edge to edge, while placing the main content into the two centre columns creates a comfortable measure:

header,
footer {
grid-column: 1 / -1; }
main {
grid-column: 2 / 4; }

The most significant changes to the layout of this design can be seen at larger screen sizes. Despite their names, you needn’t place a header or footer element at the top and bottom of a layout. They can be placed anywhere within a design, including on the left or right.

For more precise control over my layout, I increase the number of columns in my grid from four to eight, then introduce two rows. The first row has a fixed height of 160px, while the height of the second will be dictated by the content:

@media (min-width: 72em) {

body {
grid-template-columns: repeat(8, 1fr);
grid-template-rows: 160px auto;
column-gap: 2vw; }
}

I reposition my banner division across three columns and set the main element below to match:

.banner,
main {
grid-column: 3 / 5; }

Then, I place the footer into the first three columns, and the header into the final four to create an asymmetrical layout from the symmetrical grid:

header {
grid-column: 5 / -1; }

footer {
grid-column: 1 / 4; }

Both header and footer fill the height of my grid from top to bottom:

header,
footer {
grid-row: 1 / 3; }

While the banner division occupies the first row:

.banner {
grid-row: 1;  }

And the main element fits neatly underneath it:

main {
grid-row: 2 / 3;
z-index: 2; }

Implementing light themes and dark designs has become part of everyday product and website design since Apple introduced a dark mode to iOS and macOS. Developing dark/light modes is easy, and there’s now a widely supported media query for this user preference. There are three values to choose from:

  • no-preference: Someone hasn’t expressed a preference
  • light: Someone has selected a light theme
  • dark: Someone has chosen a dark theme

Introducing a dark mode version of this design involves little more than adding changes to certain color values within that media query. For example, by reversing the background and foreground text colors, and changing the path fill colors in my SVG logo:

@media (prefers-color-scheme: dark) {

body {
background-color: #262626;
color: #fff; }
#logo .metal,
#logo .emblem {
fill: #fff; }
}

Deciding on dark mode colors sometimes involves more than simply inverting them, making white backgrounds black, and the black text white. Pure white text on full black backgrounds makes reading long passages of text tiring for the eye, so consider softening this contrast by using an off-white:

body {
color: #f3f2f2; }

Sometimes, even vibrant complementary colors can appear different when they’re placed against a dark background. Thankfully, CSS filters can increase a color’s brightness, saturation, or both, with no need to export a different version of a file for darker backgrounds:

header img,
footer img {
filter: saturate(1.5) brightness(1.1); }

Vivid complementary colors against a dark background.

Vivid complementary colors against a dark background. (Large preview)

Brightening Colors

Increased lightness and saturation

Increasing lightness and saturation makes colors appear more vibrant against dark backgrounds. (Large preview)

In my final Pintori-inspired design, colorful rectangles float above the dark grey background. This design needs just three structural elements; a header which again includes the Morris Motors logo, a figure element which contains not one, not two, but three outline images of the Morris Traveller, and the main element containing my running text:

<header>
  <svg>…</svg>
</header>

<figure>
  <img>
  <img>
  <img>
</figure>

<main>
  <h1>…</h1>
  <p>…</p>
  <p>…</p>
</main>

To this minimal HTML, I add four purely presentational SVG images. As I don’t want these to be announced by assistive technologies, I add an ARIA hidden attribute to each of them:

<svg id="bg-1" aria-hidden="true">…</svg>
<svg id="bg-2" aria-hidden="true">…</svg>
<svg id="bg-3" aria-hidden="true">…</svg>
<svg id="bg-4" aria-hidden="true">…</svg>

First, I specify foundation styles for background and foreground colors, then apply those same presentational SVG images to the background using data URIs:

body {
background-color: #262626; }
background-image: 
url("data:image/svg+xml…"),
url("data:image/svg+xml…"),
url("data:image/svg+xml…"),
url("data:image/svg+xml…");
color: #f3f2f2; }

Then, I specify background repeat and position values, placing each SVG in the centre, and stacking them vertically on the page. Finally, I set their sizes:

body {
background-repeat: no-repeat;
background-position:
50% 20px,
50% 240px,
50% 460px,
50% 680px;
background-size: 
200px 200px,
300px 200px,
200px 200px,
100px 100px; }

So the logo in my header matches the size of the SVG background behind it, I restrict its maximum width, then centre it using horizontal margins:

header {
max-width: 200px;
margin: 0 auto; }

Again, a horizontal scrolling panel is a useful way to present my three outlined Traveller images, so I set their figure’s display value to flex and prevent any horizontal overflow by setting its value to scroll:

figure {
display: flex;
flex-wrap: nowrap;
margin: 0;
padding: 0;
max-width: 100vw;
overflow-x: scroll; }

Then, I specify a flex-basis value and an height to match:

figure img {
flex-grow: 1;
flex-basis: 320px;
height: 320px; }

I applied my four colorful SVGs as background images, so I don’t want them to appear on small screens. Using attribute selectors to precisely match a style’s property and value is an ideal way to target elements without resorting to additional class attributes:

[aria-hidden="true"] {
display: none; }

This design needs only one media query breakpoint to apply layout styles for medium and large screens. I apply eight equal-width columns and eight rows, then remove the background images I applied for small screens:

@media (min-width: 48em) {

body {
display: grid;
grid-template-columns: repeat(8, 1fr);
grid-template-rows: repeat(8, auto);
background-image: none; }

Then, I place the header and main elements between line numbers in my grid:

header {
grid-column: 1;
grid-row: 1; }
main {
grid-column: 5 / 8;
grid-row: 5 / 7; }

I need to place the figure’s images and division onto my grid, not the figure itself, so I change its display property to contents, which effectively removes it from the DOM for styling purposes:

figure {
display: contents; }

Then, I place each Traveller image into a different set of grid columns and rows, which alters their sizes along with their positions:

figure img:nth-of-type(1) {
grid-column: 3 / 6;
grid-row: 2 / 4; }

figure img:nth-of-type(2) {
grid-column: 5 / 8;
grid-row: 2 / 5; }

figure img:nth-of-type(3) {
grid-column: 3 / 4;
grid-row: 5 / 6; }

CSS transforms are ideal tools for fine-tuning the size and position of elements within grids’ constraints. They’re also useful for adding unusual touches to a design. I use rotate, scale, and translate to finely tune these images:

figure img:nth-of-type(1) {
transform: rotate(-20deg) translateX(-12rem); }
figure img:nth-of-type(2) { transform: scale(1.1); }
figure img:nth-of-type(3) { transform: scale(1.25); }

I now reveal the colorful, presentational rectangles and push them behind my content by setting a low z-index value. Where these images overlap, a mix-blend-mode adds even more color to this design:

[aria-hidden="true"] { 
display: block;
z-index: 0; 
mix-blend-mode: multiply; }

In this final step, I place these shapes onto my grid, using rotations to add even more personality to this already colorful design:

.bg-1 {
grid-column: 2 / 4;
grid-row: 2 / 4;
transform: rotate(-30deg);
transform-origin: 75% 50%; }

.bg-2 {
grid-column: 4 / 8;
grid-row: 2 / 5; }

.bg-3 {
grid-column: 3 / 5;
grid-row: 4 / 6; }

.bg-4 {
grid-column: 4 / 5;
grid-row: 6 / 7;
transform: rotate(5deg);
transform-origin: 0 0; }

Left: The original colors for my design. Right: Increasing lightness and saturation by 10% increases their vibrancy.

Left: The original colors for my design. Right: Increasing lightness and saturation by 10% increases their vibrancy. (Large preview)

NB: Smashing members have access to a beautifully designed PDF of Andy’s Inspired Design Decisions magazine and full code examples from this article. You can also buy the PDF and examples from this, and every issue from Andy’s website.

Read More From The Series

Smashing Editorial
(ra, yk, il)

Source: smash mag

Categories
Uncategorized

Collective #611




Our Sponsor

Create WordPress sites with ease

You’ve never built a WordPress website like this before. Divi is more than just a WordPress theme, it’s a completely new website building platform that replaces the standard WordPress post editor with a vastly superior visual editor.

Check it out







Reflex

A collection of themes and plugins to help you build Gatsby sites faster.

Check it out





Accessible to some

An experiment that tries to help you understand how bad accessibility affects users and their daily experiences on the web. By Manuel Matuzovi?.

Check it out


RSS Box

RSS Box lets you subscribe to RSS feeds for websites that do not support RSS themselves, by using the respective website’s API and then translating that data to RSS feeds.

Check it out









The post Collective #611 appeared first on Codrops.


Source: codrops

Categories
Uncategorized

Django Highlights: Wrangling Static Assets And Media Files (Part 4)

Django Highlights: Wrangling Static Assets And Media Files (Part 4)

Django Highlights: Wrangling Static Assets And Media Files (Part 4)

Philip Kiely

2020-06-25T12:30:00+00:00
2020-07-05T00:11:03+00:00

Django websites involve a lot of files. It’s not just source code for the configuration, models, views, and templates, but also static assets: CSS and JavaScript, images, icons. As if that wasn’t enough already, sometimes users come along and want to upload their own files to your website. It’s enough to make any developer incredulous. Files everywhere!

Here’s where I wish I could say (without caveats): “Don’t worry, Django has your back!” But unfortunately, when dealing with static assets and media files, there are a lot of caveats to deal with.

Today, we’ll address storing and serving files for both single-server and scalable deployments while considering factors like compression, caching, and availability. We’ll also discuss the costs and benefits of CDNs and dedicated file storage solutions.

Note: This is not a tutorial on how to deploy a Django site to any specific platform. Instead, like the other articles in the Django Highlights series (see below), it’s intended as a guide for front-end developers and designers to understand other parts of the process of creating a web application. Today, we’re focusing on what happens after the style hotfix or beautiful graphic you just finished is pushed to master. Together, we’ll develop an intuition for the strategies available to Django developers for serving these files to users worldwide in a secure, performant, and cost-effective manner.

Previous Parts In The Series:

  • Part 1: User Models And Authentication
  • Part 2: Templating Saves Lines
  • Part 3: Models, Admin, And Harnessing The Relational Database

Definitions

Most of these terms are pretty straightforward, but it’s worth taking a moment to establish a shared vocabulary for this discussion.

The three types of files in a live Django application are:

  1. Source Code
    The Python and HTML files that are created with the Django framework. These files are the core of the application. Source code files are generally pretty small, measured in kilobytes.
  2. Static Files
    Also called “static assets,” these files include CSS and JavaScript, both written by the application developer and third-party libraries, as well as PDFs, software installers, images, music, videos, and icons. These files are only used client-side. Static files range from a few kilobytes of CSS to gigabytes of video.
  3. Media Files
    Any file uploaded by a user, from profile pictures to personal documents, is called a media file. These files need to be securely and reliably stored and retrieved for the user. Media files can be of any size, the user might upload a couple of kilobytes of plaintext to a few gigabytes of video. If you’re on the latter end of this scale, you probably need more specialized advice than this article is prepared to give.

The two types of Django deployments are:

  1. Single-Server
    A single-server Django deployment is exactly what it sounds like: everything lives on a single server. This strategy is very simple and closely resembles the development environment, but cannot handle large or inconsistent amounts of traffic effectively. The single-server approach is only applicable for learning or demonstration projects, not real-word applications that require reliable uptime.
  2. Scalable
    There are lots of different ways to deploy a Django project that allows it to scale to meet user demand. These strategies often involve spinning up and down numerous servers and using tools like load balancers and managed databases. Fortunately, we can effectively lump everything more complex than a single-server deployment into this category for the purposes of this article.

Option 1: Default Django

Small projects benefit from simple architecture. Django’s default handling of static assets and media files is just that: simple. For each, you have a root folder that stores the files and lives right next to the source code on the server. Simple. These root folders are generated and managed mostly through the yourproject/settings.py configuration.

Static Assets

The most important thing to understand when working with static files in Django is the python manage.py collectstatic command. This command rifles through the static folder of each app in the Django project and copies all static assets to the root folder. Running this command is an important part of deploying a Django project. Consider the following directory structure:

- project
  - project
    - settings.py
    - urls.py
    - ...
  - app1
    - static/
      - app1
        - style.css
        - script.js
        - img.jpg
    - templates/
    - views.py
    - ...
  - app2
    - static/
      - app2
        - style.css
        - image.png
    - templates/
    - views.py
    - ...

Also assume the following settings in project/settings.py:

STATIC_URL = "/static/"
STATIC_ROOT = "/path/on/server/to/djangoproject/static"

Running the python manage.py collectstatic command will create the following folder on the server:

- /path/on/server/to/djangoproject/static
  - app1
    - style.css
    - script.js
    - img.jpg
  - app2
    - style.css
    - image.png

Notice that within each static folder, there’s another folder with the app’s name. This is to prevent namespacing conflicts after the static files are collected; as you can see in the above file structure this keeps app1/style.css and app2/style.css distinct. From here, the application will look for static files in this structure at the STATIC_ROOT during production. As such, reference static files as follows in a template in app1/templates/:

{% load static %}
<link rel="stylesheet" type="text/css" href="{% static "app1/style.css" %}">

Django automatically figures out where to get the static file from in development to model this behavior, you do not need to run collectstatic during development.

For more details, see the Django documentation.

Media Files

Imagine a professional networking site with a database of users. Each of those users would have an associated profile, which might contain, among other things, an avatar image and a resume document. Here’s a short example model of that information:

from django.db import models
from django.contrib.auth.models import User

def avatar_path(instance, filename):
    return "avatar_{}_{}".format(instance.user.id, filename)

class Profile(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE)
    resume = models.FileField(upload_to="path/string")
    avatar = models.ImageField(upload_to=avatar_path)

For this to work, you need the following options in project/settings.py, like with static assets:

MEDIA_URL = "/media/"
MEDIA_ROOT = "/path/on/server/to/media"

An ImageField inherits from FileField, so it shares the same parameters and capabilities. Both fields have an optional upload_to argument, which takes a string that is a path and appends it to the MEDIA_ROOT to store the file, which is then accessible by the same path on top of MEDIA_URL. The upload_to argument can also take a function that returns a string, as demonstrated with the avatar_path function.

Make sure to omit the media files directory and its contents from version control. Its contents may conflict when two developers test the same application on different machines, and it is, unlike static assets, not a part of the deployable Django application.

Option 2: Django With Services

My guiding philosophy is to use tools for what they’re best at. Django is an amazing framework, and it provides great tooling out of the box for user authentication, server-side rendering, working with models and forms, administrative functions, and dozens of other essential aspects of building web applications. However, its tooling for handling static assets and media files is not, in my opinion, well-suited for production on scalable sites. The Django core developers recognize that many people choose alternate approaches to handling these files in production; the framework is very good at getting out of your way when you do. Most Django sites intended for general use will want to incorporate static assets and handle media files using these non-Django-specific approaches.

Static Assets On A CDN

While small-to-medium projects can get away without one, a CDN (content delivery network) is easy to use and improves the performance of applications of any size. A CDN is a network of servers, generally worldwide, that distributes and serves web content, mostly static assets. Popular CDNs include Cloudflare CDN, Amazon CloudFront, and Fastly. To use a CDN, you upload your static files, then in your application reference them as follows:

<link rel="stylesheet" type="text/css" href="https://cdn.example.com/path/to/your/files/app1/style.css">

This process is easy to integrate with your Django deployment scripts. After running the python manage.py collectstatic command, copy the generated directory to your CDN (a process that varies substantially based on the service you’re using), then remove the static assets from the Django deployment package.

In development, you’ll want to access different copies of your static assets than in production. This way, you can make changes locally without affecting the production site. You can either use local assets or run a second instance of the CDN to deliver the files. Configure yourproject/settings.py with some custom variable, like CDN_URL, and use that value in your templates to ensure you’re using the correct version of assets in development and production.

One final note is that many libraries for CSS and JavaScript have free CDNs that most websites can use. If you’re loading, say, Bootstrap 4 or underscore.js, you can skip the hassle of using your own copy in development and the expense of serving your own copies in production by using these public CDNs.

Media Files with a Dedicated Filestore

No production Django site should store user files in a simple /media/ folder somewhere on the server that runs the site. Here are three of the many reasons why not to:

  1. If you need to scale up the site by adding multiple servers, you need some way of copying and syncing the uploaded files across those servers.
  2. If a server crashes, the source code is backed up in your version control system, but media files aren’t backed up by default, unless you configured your server to do so, but for that effort you’d be better off using a dedicated filestore.
  3. In case of malicious activity, it’s somewhat better to keep user-uploaded files on a separate server from the one running the application, although this in no way removes the requirement to validate user-uploaded files.

Integrating a third party to store your user-uploaded files is really easy. You don’t need to change anything in your code, except maybe removing or modifying the upload_to value of FileFields in your models, and configuring a few settings. For example, if you were planning to store your files in AWS S3, you’d want to do the following, which is very similar to the process of storing files with Google Cloud, Azure, Backblaze, or similar competing services.

First, you’ll need to install the libraries boto3 and django-storages. Then, you need to set up a bucket and IAM role on AWS, which is outside the scope of this article, but you can see instructions for here. Once all of that is configured, you need to add three variables to your project/settings.py:

DEFAULT_FILE_STORAGE = "storages.backends.s3boto3.S3Boto3Storage"
AWS_STORAGE_BUCKET_NAME = "BUCKET_NAME"
AWS_S3_REGION_NAME = "us-east-2"

Additionally, you will need to set up credential access to your AWS bucket. Some tutorials will demonstrate adding an ID and secret key to your settings file or as environment variables, but these are insecure practices. Instead, use django-storages with the AWS CLI to configure the keys, as described here. You may also be interested in the django-storages documentation.

You don’t want development or testing media files to get mixed up with uploads from actual users. Avoiding this is pretty simple: set up multiple buckets, one for development (or one for each developer), one for testing, and one for production. Then, all you need to change is the AWS_STORAGE_BUCKET_NAME setting per environment and you’re good to go.

Performance And Availability

There are numerous factors that affect the performance and reliability of your website. Here are some important ones when considering static and media files that matter regardless of which approach you take to managing them.

Cost

Serving files to a user costs money for two reasons: storage and bandwidth. You have to pay the hosting provider to store the files for you, but you also have to pay them to serve the files. Bandwidth is substantially more expensive than storage (for example, AWS S3 charges 2.3 cents per gigabyte for storage versus 9 cents per gigabyte of data transfer out to the Internet at the time of writing). The economics of a file store like S3 or a CDN are different than the economics of a generalized host like a Digital Ocean droplet. Take advantage of specialization and economies of scale by moving expensive files to services designed for them. Furthermore, many file stores and CDNs offer free plans so sites that might be small enough to get away without using them can instead do so and reap the benefits without any additional infrastructure costs.

Compression and Transcoding

Most of the problems caused by static assets like photos and videos are because they are big files. Naturally, developers address this by trying to make these files smaller. There are a number of ways to do this using a mix of compression and transcoding in two general categories: lossless and lossy. Lossless compression retains the original quality of the assets but provides relatively modest decreases in file size. Lossy compression, or transcoding into a lossy format, allows for much smaller file sizes at the expense of losing some of the quality of the original artifact. An example of this is transcoding video to a lower bitrate. For details, check out this article about optimizing video delivery. When serving large files over the web, bandwidth speeds often demand that you serve highly compressed artifacts, requiring lossy compression.

Unless you’re YouTube, compression and transcoding doesn’t happen on the fly. Static assets should be appropriately formatted prior to deployment, and you can enforce basic file type and file size restrictions on user uploads to ensure sufficient compression and appropriate formatting in your users’ media files.

Minification

While files of JavaScript and CSS aren’t usually as large as images, they can often be compressed to squeeze into fewer bytes. This process is called minification. Minification does not change the encoding of the files, they’re still text, and a minified file still needs to be valid code for its original language. Minified files retain their original extensions.

The main thing removed in a minified file is unnecessary whitespace, and from the computer’s perspective almost all whitespace in CSS and JavaScript is unnecessary. Minification schemes also shorten variable names and remove comments.

Minification by default obfuscates code; as a developer, you should work exclusively with non-minified files. Some automatic step during the deployment process should minify the files before they are stored and served. If you’re using a library provided by a third-party CDN, make sure you’re using the minified version of that library if available. HTML files can be minified, but as Django uses server-side rendering, the processing cost of doing so on the fly would most likely outweigh the small decrease in page size.

Global Availability

Just like it takes less time to send a letter to your neighbor than it does to send it across the country, so to does it take less time to transmit data nearby than across the world. One of the ways that a CDN improves page performance is by copying assets onto servers across the world. Then, when a client makes a request, they receive the static assets from the nearest server (often called an edge node), decreasing load times. One of the advantages to using a CDN with a Django site is decoupling the global distribution of your static assets from the global distribution of your code.

Client-Side Caching

What’s better than having a static file on a server near your user? Having the static file already stored on your user’s device! Caching is the process of storing the results of a computation or request so that they can be accessed repeatedly more quickly. Just like a CSS stylesheet can be cached around the world in a CDN, it can be cached in the client’s browser the first time they load a page from your site. Then, the stylesheet is available on the device itself in subsequent requests, so the client is making fewer requests, improving page load time, and decreasing bandwidth use.

Browsers perform their own caching operations, but if your site enjoys substantial traffic, you can optimize your client-side caching behavior using Django’s cache framework.

In Conclusion

Again, my guiding philosophy is to use tools for what they’re best at. Single-server projects and small scalable deployments with only lightweight static assets can use Django’s built-in static asset management, but most applications should separate out assets to be served over a CDN.

If your project is intended for any kind of real-word use, do not store media files with Django’s default method, instead use a service. With enough traffic, where “enough traffic” is a relatively small number on the scale of the Internet, the additional complications to architecture, the development process, and deployment are more than worth it for the performance, reliability, and cost savings of using a separate CDN and file storage solution for static and media files, respectively.

Recommended Reading

  • Part 1: User Models And Authentication
  • Part 2: Templating Saves Lines
  • Part 3: Models, Admin, And Harnessing The Relational Database
Smashing Editorial
(dm, ra, yk, il)

Source: smash mag

Categories
Uncategorized

A Practical Guide To Testing React Applications With Jest

A Practical Guide To Testing React Applications With Jest

A Practical Guide To Testing React Applications With Jest

Adeneye David Abiodun

2020-06-24T12:00:00+00:00
2020-07-05T00:11:03+00:00

In this article, I’m going to introduce you to a React testing tool named Jest, along with the popular library Enzyme, which is designed to test React components. I’ll introduce you to Jest testing techniques, including: running tests, testing React components, snapshot testing, and mocking.
If you are new to testing and wondering how to get started, you will find this tutorial helpful because we will start with an introduction to testing. By the end, you’ll be up and running, testing React applications using Jest and Enzyme. You should be familiar with React in order to follow this tutorial.

A Brief Introduction To Testing

Testing is a line-by-line review of how your code will execute. A suite of tests for an application comprises various bit of code to verify whether an application is executing successfully and without error. Testing also comes in handy when updates are made to code. After updating a piece of code, you can run a test to ensure that the update does not break functionality already in the application.

Why Test?

It’s good to understand why we doing something before doing it. So, why test, and what is its purpose?

  1. The first purpose of testing is to prevent regression. Regression is the reappearance of a bug that had previously been fixed. It makes a feature stop functioning as intended after a certain event occurs.
  2. Testing ensures the functionality of complex components and modular applications.
  3. Testing is required for the effective performance of a software application or product.

Testing makes an app more robust and less prone to error. It’s a way to verify that your code does what you want it to do and that your app works as intended for your users.

Let’s go over the types of testing and what they do.

Unit Test

In this type of test, individual units or components of the software are tested. A unit might be an individual function, method, procedure, module, or object. A unit test isolates a section of code and verifies its correctness, in order to validate that each unit of the software’s code performs as expected.

In unit testing, individual procedures or functions are tested to guarantee that they are operating properly, and all components are tested individually. For instance, testing a function or whether a statement or loop in a program is functioning properly would fall under the scope of unit testing.

Component Test

Component testing verifies the functionality of an individual part of an application. Tests are performed on each component in isolation from other components. Generally, React applications are made up of several components, so component testing deals with testing these components individually.

For example, consider a website that has different web pages with many components. Every component will have its own subcomponents. Testing each module without considering integration with other components is referred to as component testing.

Testing like this in React requires more sophisticated tools. So, we would need Jest and sometimes more sophisticated tools, like Enzyme, which we will discuss briefly later.

Snapshot Test

A snapshot test makes sure that the user interface (UI) of a web application does not change unexpectedly. It captures the code of a component at a moment in time, so that we can compare the component in one state with any other possible state it might take.

We will learn about snapshot testing in a later section.

Advantages and Disadvantages of Testing

Testing is great and should be done, but it has advantages and disadvantages.

Advantages

  1. It prevents unexpected regression.
  2. It allows the developer to focus on the current task, rather than worrying about the past.
  3. It allows modular construction of an application that would otherwise be too complex to build.
  4. It reduces the need for manual verification.

Disadvantages

  1. You need to write more code, as well as debug and maintain.
  2. Non-critical test failures might cause the app to be rejected in terms of continuous integration.

Introduction to Jest

Jest is a delightful JavaScript testing framework with a focus on simplicity. It can be installed with npm or Yarn. Jest fits into a broader category of utilities known as test runners. It works great for React applications, but it also works great outside of React applications.

Enzyme is a library that is used to test React applications. It’s designed to test components, and it makes it possible to write assertions that simulate actions that confirm whether the UI is working correctly.

Jest and Enzyme complement each other so well, so in this article we will be using both.

Process Of Running A Test With Jest

In this section, we will be installing Jest and writing tests. If you are new to React, then I recommend using Create React App, because it is ready for use and ships with Jest.

npm init react-app my-app

We need to install Enzyme ****and enzyme-adapter-react-16 with react-test-renderer (the number should be based on the version of React you’re using).

npm install --save-dev enzyme enzyme-adapter-react-16 react-test-renderer

Now that we have created our project with both Jest and Enzyme, we need to create a setupTest.js file in the src folder of the project. The file should look like this:

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

This imports Enzyme and sets up the adapter to run our tests.

Before continuing, let’s learn some basics. Some key things are used a lot in this article, and you’ll need to understand them.

  • it or test
    You would pass a function to this method, and the test runner would execute that function as a block of tests.
  • describe
    This optional method is for grouping any number of it or test statements.
  • expect
    This is the condition that the test needs to pass. It compares the received parameter to the matcher. It also gives you access to a number of matchers that let you validate different things. You can read more about it in the documentation.
  • mount
    This method renders the full DOM, including the child components of the parent component, in which we are running the tests.
  • shallow
    This renders only the individual components that we are testing. It does not render child components. This enables us to test components in isolation.

Creating A Test File

How does Jest know what’s a test file and what isn’t? The first rule is that any files found in any directory with the name __test__ are considered a test. If you put a JavaScript file in one of these folders, Jest will try to run it when you call Jest, for better or for worse. The second rule is that Jest will recognize any file with the suffix .spec.js or .test.js. It will search the names of all folders and all files in your entire repository.

Let’s create our first test, for a React mini-application created for this tutorial. You can clone it on GitHub. Run npm install to install all of the packages, and then npm start to launch the app. Check the README.md file for more information.

Let’s open App.test.js to write our first test. First, check whether our app component renders correctly and whether we have specified an output:

it("renders without crashing", () => {
  shallow(<App />);
});

it("renders Account header", () => {
  const wrapper = shallow(<App />);
  const welcome = <h1>Display Active Users Account Details</h1>;
  expect(wrapper.contains(welcome)).toEqual(true);
});

In the test above, the first test, with shallow, checks to see whether our app component renders correctly without crashing. Remember that the shallow method renders only a single component, without child components.

The second test checks whether we have specified an h1 tag output of “Display Active User Account” in our app component, with a Jest matcher of toEqual.

Now, run the test:

npm run test 
/* OR */
npm test

The output in your terminal should like this:

  PASS  src/App.test.js
  √ renders without crashing (34ms)
  √ renders Account header (13ms)

Test Suites: 1 passed, 1 total
Tests:       2 passed, 2 total
Snapshots:   0 total
Time:        11.239s, estimated 16s
Ran all test suites related to changed files.

Watch Usage: Press w to show more.

As you can see, our test passed. It shows we have one test suite named App.test.js, with two successful tests when Jest ran. We’ll talk about snapshot testing later, and you will also get to see an example of a failed test.

Skipping Or Isolating A Test

Skipping or isolating a test means that when Jest runs, a specific marked test is not run.

it.skip("renders without crashing", () => {
  shallow(<App />);
});

it("renders Account header", () => {
  const wrapper = shallow(<App />);
  const header = <h1>Display Active Users Account Details</h1>;
  expect(wrapper.contains(header)).toEqual(true);
});

Our first test will be skipped because we’ve used the skip method to isolate the test. So, it will not run or make any changes to our test when Jest runs. Only the second one will run. You can also use it.only().

It’s a bit frustrating to make changes to a test file and then have to manually run npm test again. Jest has a nice feature called watch mode, which watches for file changes and runs tests accordingly. To run Jest in watch mode, you can run npm test -- --watch or jest --watch. I would also recommend leaving Jest running in the terminal window for the rest of this tutorial.

Mocking Function

A mock is a convincing duplicate of an object or module without any real inner workings. It might have a tiny bit of functionality, but compared to the real thing, it’s a mock. It can be created automatically by Jest or manually.

Why should we mock? Mocking reduces the number of dependencies — that is, the number of related files that have to be loaded and parsed when a test is run. So, using a lot of mocks makes tests execute more quickly.

Mock functions are also known as “spies”, because they let you spy on the behavior of a function that is called directly by some other code, rather than only testing the output.

There are two ways to mock a function: either by creating a mock function to use it in test code, or by writing a manual mock to override a module dependency.

Manual mocks ****are used to stub out functionality with mock data. For example, instead of accessing a remote resource, like a website or a database, you might want to create a manual mock that allows you to use fake data.

We will use a mock function in the next section.

Testing React Components

The section will combine all of the knowledge we have gained so far in understanding how to test React components. Testing involves making sure the output of a component hasn’t unexpectedly changed to something else. Constructing components in the right way is by far the most effective way to ensure successful testing.

One thing we can do is to test components props — specifically, testing whether props from one component are being passed to another. Jest and the Enzyme API allow us to create a mock function to simulate whether props are being passed between components.

We have to pass the user-account props from the main App component to the Account component. We need to give user-account details to Account in order to render the active account of users. This is where mocking comes in handy, enabling us to test our components with fake data.

Let’s create a mock for the user props:

const user = {
  name: "Adeneye David",
  email: "david@gmail.com",
  username: "Dave",
};

We have created a manual mock function in our test file and wrapped it around the components. Let’s say we are testing a large database of users. Accessing the database directly from our test file is not advisable. Instead, we create a mock function, which enables us to use fake data to test our component.

describe("", () => {
  it("accepts user account props", () => {
    const wrapper = mount(<Account user={user} />);
    expect(wrapper.props().user).toEqual(user);
  });
  it("contains users account email", () => {
    const wrapper = mount(<Account user={user} />);
    const value = wrapper.find("p").text();
    expect(value).toEqual("david@gmail.com");
  });
});

We have two tests above, and we use a describe layer, which takes the component being tested. By specifying the props and values that we expect to be passed by the test, we are able to proceed.

In the first test, we check whether the props that we passed to the mounted component equal the mock props that we created above.

For the second test, we pass the user props to the mounted Account component. Then, we check whether we can find the <p> element that corresponds to what we have in the Account component. When we run the test suite, you’ll see that the test runs successfully.

We can also test the state of our component. Let’s check whether the state of the error message is equal to null:

it("renders correctly with no error message", () => {
  const wrapper = mount();
  expect(wrapper.state("error")).toEqual(null);
});

In this test, we check whether the state of our component error is equal to null, using a toEqual() matcher. If there is an error message in our app, the test will fail when run.

In the next section, we will go through how to test React components with snapshot testing, another amazing technique.

Snapshot Testing

Snapshot testing captures the code of a component at a moment in time, in order to compare it to a reference snapshot file stored alongside the test. It is used to keep track of changes in an app’s UI.

The actual code representation of a snapshot is a JSON file, and this JSON contains a record of what the component looked like when the snapshot was made. During a test, Jest compares the contents of this JSON file to the output of the component during the test. If they match, the test passes; if they don’t, the test fails.

To convert an Enzyme wrapper to a format that is compatible with Jest snapshot testing, we have to install enzyme-to-json:

npm install --save-dev enzyme-to-json

Let’s create our snapshot test. When we run it the first time, the snapshot of that component’s code will be composed and saved in a new __snapshots__ folder in the src directory.

it("renders correctly", () => {
  const tree = shallow(<App />);
  expect(toJson(tree)).toMatchSnapshot();
});

When the test above runs successfully, the current UI component will be compared to the existing one.

Now, let’s run the test:

npm run test

When the test suite runs, a new snapshot will be generated and saved to the __snapshots__ folder. When we run a test subsequently, Jest will check whether the components match the snapshot.

As explained in the previous section, that shallow method from the Enzyme package is used to render a single component and nothing else. It doesn’t render child components. Rather, it gives us a nice way to isolate code and get better information when debugging. Another method, named mount, is used to render the full DOM, including the child components of the parent component, in which we are running the tests.

We can also update our snapshot, Let’s make some changes to our component in order to make our test fail, which will happen because the component no longer corresponds to what we have in the snapshot file. To do this, let’s change the <h3> tag in our component from <h3> Loading...</h3> to <h3>Fetching Users...</h3>. When the test runs, this what we will get in the terminal:

 FAIL  src/App.test.js (30.696s)
  × renders correctly (44ms)

  ● renders correctly

    expect(received).toMatchSnapshot()
    Snapshot name: `renders correctly
1

    - Snapshot
    + Received

      
        

Display Active Users Account Details

- Loading... + Fetching Users...

7 | it("renders correctly", () => { 8 | const wrapper = shallow(); > 9 | expect(toJson(wrapper)).toMatchSnapshot(); | ^ 10 | }); 11 | 12 | /* it("renders without crashing", () => { at Object. (src/App.test.js:9:27) › 1 snapshot failed. Snapshot Summary › 1 snapshot failed from 1 test suite. Inspect your code changes or press `u` to update them. Test Suites: 1 failed, 1 total Tests: 1 failed, 1 total Snapshots: 1 failed, 1 total Time: 92.274s Ran all test suites related to changed files. Watch Usage: Press w to show more.

If we want our test to pass, we would either change the test to its previous state or update the snapshot file. In the command line, Jest provides instruction on how to update the snapshot. First, press w in the command line to show more, and then press u to update the snapshot.

› Press u to update failing snapshots.

When we press u to update the snapshot, the test will pass.

Conclusion

I hope you’ve enjoyed working through this tutorial. We’ve learned some Jest testing techniques using the Enzyme testing library. I’ve also introduced you to the process of running a test, testing React components, mocking, and snapshot testing. If you have any questions, you can leave them in the comments section below, and I’ll be happy to answer every one and work through any issues with you.

Resources And Further Reading

Smashing Editorial
(ks, ra, il, al)

Source: smash mag

Categories
Uncategorized

How Web Designers Can Help Restaurants Move Into Digital Experiences

How Web Designers Can Help Restaurants Move Into Digital Experiences

How Web Designers Can Help Restaurants Move Into Digital Experiences

Suzanne Scacca

2020-06-23T12:00:00+00:00
2020-07-05T00:11:03+00:00

As much as I’ve always loved the experience of going out to eat and ordering in takeout, it’s very rare that I enjoy visiting a restaurant’s website. But I get it. The restaurant industry tends to run on very slim profit margins, so it’s hard to justify spending money on a professionally designed website when all they want it to do is list their hours of operation and menu.

However, I envision all that changing in 2020 (and beyond) as restaurants are forced to expand into digital in order to survive. Unlike a website that a novice might hack together with a cheap site builder, establishing a competitive digital presence isn’t something they’re going to be able to do on their own.

That’s why web designers should seriously start thinking about expanding into this niche.

How Web Designers Can Help Restaurants Move into Digital

Usually, when something serious shakes up the restaurant industry, those that want to survive will adopt newer and better technologies to adapt. So, it’s not like restaurants are strangers to digital transformation. Until now, though, the focus has mainly been on investing in technology that improves how they work in-house.

With everything that’s happened in 2020, though, restaurants are going to need web designers’ help in doing three things that ensure their survival in an increasingly digital world:

1. Modernize The Restaurant Website

Whenever I write one of these posts, I spend time reviewing a few dozen websites to find the best examples to make my point. I’m not going to lie, this one was tough. While I knew I could turn to national chain restaurants to find modern-looking websites, I had a really hard time with others.

While it’s not impossible to find an independent restaurant operator or local chain that has a great-looking website in 2020, I’d say that at least half of them are way behind the times, if they even have a website at all.

Remember when websites were designed like this?

A blurred-out website to demonstrate how independent restaurants still have unresponsive designs

An outdated restaurant website in 2020, blurred out to protect its identity. (Source: Anonymous) (Large preview)

I’ve blurred out the restaurant’s name and details to protect its identity, but you can still get a sense of how bad this design is for 2020.

Restaurant websites can’t afford to be crappy, non-responsive placeholders anymore. They need to become impressive digital presences that set the stage for what customers will experience when interacting with restaurants as diners.

Let’s take a look at how In-N-Out Burger has nailed modern web design. The first thing you’ll notice is it’s a responsive design. On desktop, the website fits the full width of the screen, so there’s no wasted space around the border. It looks good on a mobile device, too:

In-N-Out Burger website on mobile

The In-N-Out Burger mobile website is responsive and easy to read. (Source: In-N-Out Burger) (Large preview)

Also, take notice of the images. This is a burger joint, so you should expect the website to be full of burger photos, which it is. However, there’s something interesting to note about the burgers you find on the site.

In-N-Out Burger website photos and transitions

The In-N-Out Burger website uses perfectly framed images and well-chosen transitions. (Source: In-N-Out Burger) (Large preview)

When someone enters a page where there’s a burger photo, the food slides into the frame as if someone were sliding it over to a customer in the restaurant. It’s a neat little transition and many visitors to the site might not even realize what’s happening, but it makes the experience feel more lifelike and interactive.

Transitions aren’t the only things you can do to create this sort of experience. Background videos taken within the establishment work just as well as it gives customers the opportunity to walk through the establishment instead of relying on static images that only paint part of the picture.

Another thing restaurant websites need to improve is how they’re organized.

When people are ready to go out to eat or to dine in, don’t waste their time trying to force the restaurant’s history down their throats (which many of these sites surprisingly do). The navigation as well as the order in which CTAs appear on the home page should reflect the actions customers want to take.

The thought process most likely goes like this:

  1. “I’m not sure what to order. Where’s the menu?” (Menu)
  2. “Do I need to make a reservation or can we just go whenever?” (Reservations)
  3. “Where is this place again?” (Locations or Contact)

Or, these days, #2 looks more like this:

  • “Do they do takeout? I wonder if they’ll deliver it.” (Order Online)

There are other things customers might want to do on the website. Like buy gift cards or merchandise, sign up for rewards or apply for a job.

So, while the above tasks should be a priority in terms of what visitors see first, make sure to look at the site’s data to see what else they’re interested in doing. Then, make sure those popular actions take center stage in the navigation and site design.

2. Empower Them to Diversify Their Income

Under normal circumstances, profitability is a problem for many restaurants. Add a crisis to the mix and it’s going to become downright impossible to generate any profit — that is if they rely solely on dine-in business.

Long before COVID-19, consumers were already showing a growing preference for digital dining solutions.

According to Peapod, 77% of U.S. consumers said they preferred eating at home than going out. But that doesn’t necessarily translate to ordering in from a restaurant.

  • 27% preferred to order groceries online and pick them up from the store.
  • 26% planned to use grocery delivery.
  • 20% were interested in meal kits.

Then, you have information from Technomic and the National Restaurant Association that found that about 60% of all restaurant sales in the U.S. come from off-premise dining.

For restaurants that haven’t yet made the leap to digital dining options, they’re going to have to ASAP. This isn’t just a temporary thing either.


Restaurants that fail to digitize going forward won’t survive.

So, web designers are going to be needed to help them build out the following:

  • An online ordering system for their website or a link to an external service,
  • A reservation system (for when in-house dining is available).

That’s just the bare minimum though. For instance, this is what Snooze Eatery has done:

Snooze Eatery online delivery or pickup

Snooze Eatery advertises delivery or pickup on its website. (Source: Snooze Eatery) (Large preview)

The first thing visitors see on the website is the online ordering option. When they click “Place Your Order”, they’re taken to the restaurant’s proprietary ordering portal:

Snooze Eatery online ordering portal

Snooze Eatery’s online ordering portal. (Source: Snooze Eatery) (Large preview)

This in and of itself is a great solution for restaurants to have available through their websites as it allows them to control the ordering process and capture more of the profits (but that’s up to your clients to decide, of course). That said, many restaurants are getting creative and going beyond traditional online ordering options.

Below the fold on the Snooze Eatery site, visitors will find this banner:

Snooze Eatery neighborhood provisions: family-style meal kits and food essentials

Snooze Eatery now offers neighborhood provisions. (Source: Snooze Eatery) (Large preview)

As I mentioned earlier, there’s a good number of people who want to be able to order food online but then prepare it for themselves at home. While that would previously have left restaurants high and dry, that’s not the case anymore as many restaurants are expanding their offering to include family-style meal kits and groceries like Snooze.

This alone means that web designers are going to become increasingly more important for restaurants. And don’t expect the work to end there. Restaurants will also need your help building other monetized offerings into their websites. For instance:

  • Gift cards;
  • Merchandise;
  • Subscription services for meal kits, alcohol deliveries and more;
  • Online memberships for cooking classes, premium recipes, etc.

If they don’t have one yet, they’ll also probably need help creating a rewards and account management system as well.

3. Fix Their Brand Images on Third-party Sites

Although the website should be the engine that powers everything for the business online, restaurants need other sites to help with visibility, too. For example:

  • Facebook to share photos, advertise location information and collect customer reviews;
  • Instagram to share photos, restaurant updates and customer-generated content;
  • Yelp and TripAdvisor to collect customer reviews and feedback;
  • Google My Business to create a local presence in Google search and Maps as well as to collect reviews;
  • Delivery services like DoorDash to outsource delivery to;
  • Reservation sites like OpenTable to outsource reservation bookings to.

If customers are looking for restaurants online, they need to be willing and able to meet them where they are… before eventually bringing them to the website.

Although it’s ultimately the restaurant’s responsibility to create these pages, you should provide assistance when it comes to the visual branding piece. For one, it ensures that there’s some consistency between all their platforms. Also, it enables you to fill in missing pieces that restaurateurs might not think about.

Let’s take a look at Rhode Island staple, IGGY’S:

IGGY’S website with picture of clamcakes and 3 options for online ordering

IGGY’S website visitors are introduced to the restaurant with an image of its iconic clamcakes. (Source: IGGY’S) (Large preview)

The waterfront eatery immediately gets down to business and provides visitors with 3 options for ordering online (based on which location they want to go to).

Here’s what the online ordering portal looks like:

IGGY’S online ordering portal

IGGY’S restaurant’s online ordering portal. (Source: IGGY’S) (Large preview)

Notice how good this looks. It takes what would otherwise be a text-only menu and turns it into something much more attractive and, arguably, more effective in driving up sales.

Now, contrast that with IGGY’S online ordering through DoorDash:

DoorDash online ordering for IGGY’s

DoorDash customers can order online from IGGY’s restaurant. (Source: DoorDash) (Large preview)

The items on this page rarely come with descriptions or images.

Now, IGGY’S is a well-known restaurant around Rhode Island, so this might not be a dealbreaker for online customers. However, new customers might approach the menu with more trepidation than the one available through the IGGY’S website since it’s devoid of details.

This is where your visual-centric approach comes in handy. By making sure each item comes with a high-resolution and mouth-watering photo (the same as the one used on the site), you can optimize this sales opportunity for them.

It’s also important to ensure the brand elements are consistently presented. That way, if an existing customer runs across their favorite restaurant on DoorDash, they won’t hesitate to order because they’ll instantly know it’s their favorite restaurant.

For example, the logo on DoorDash is nothing like the one on the website in terms of quality or looks:

DoorDash logo and location information for IGGY’S RI

The DoorDash logo for IGGY’S doesn’t match the one on the website. (Source: DoorDash) (Large preview)

Be it the logo or another branded element, you want to make sure that 1) it matches the website and 2) looks good. This goes for online ordering sites like DoorDash as well as all the other ones I mentioned earlier.

Wrapping Up

We’re at a point now where restaurants can no longer be reluctant or stingy about improving their digital presence. And, as a web designer, this should get you excited.

There’s a lot you can do to help businesses in this space beyond designing basic websites. Because so much of their digital transformation involves making sales online, you’ll get to design experiences that are intuitive, modern, and mouth-watering while also creating new monetized pathways for them.

Smashing Editorial
(ra, yk, il)

Source: smash mag

Categories
Uncategorized

An Introduction To SWR: React Hooks For Remote Data Fetching

An Introduction To SWR: React Hooks For Remote Data Fetching

An Introduction To SWR: React Hooks For Remote Data Fetching

Ibrahima Ndaw

2020-06-22T12:00:00+00:00
2020-07-05T00:11:03+00:00

SWR is a lightweight library created by Vercel (formerly ZEIT) that allows fetching, caching, or refetching data in realtime using React Hooks. It’s built with React Suspense which lets your components “wait” for something before they can render, including data. SWR ships also with great features such as dependent fetching, focus on revalidation, scroll position recovery, and so on. It’s also a very powerful tool since it’s backend agnostic and has good support for TypeScript. It’s a package that has a bright future.

Why should you care? You should care if you’ve been looking for a library that does not only fetch data from APIs but also make it possible to do things like caching and dependent fetching. What will be covered in this tutorial will come in handy when building React applications with a lot of moving parts. It’s expected that you should have made use of Axios and the Fetch API, even though we’ll compare how they differ from SWR, we won’t be going into details on how they’ll be implemented.

In this guide, I will introduce you to React Hooks for Remote Data Fetching by building a Pokedex app that requests data from the Pokemon API. We will also dive into other features that come with SWR as well, and highlight its differences compared to popular solutions such as the Fetch API and the Axios library and give you the reasons why using this library and why you should keep an eye on SWR.

So, let’s start by answering a fundamental question: What is SWR?

What Is SWR?

SWR is an initialism of stale-while-revalidate. It’s a React Hooks library for remote data fetching. SWR works with three main steps: first, it returns the data from the cache (the stale part), then sends the fetch request (the revalidate part), and finally comes with the up-to-date data. But no worries, SWR handles all these steps for us. The only thing we have to do is give the useSWR hook the needed parameters to make the request.

SWR has also some nice features such as:

  • Back-end agnostic
  • Fast page navigation
  • Revalidation on focus
  • Interval polling
  • Request deduplication
  • Local mutation
  • Pagination
  • TypeScript ready
  • SSR support
  • Suspense mode
  • React Native support
  • Lightweight.

Sounds magical? Well, SWR simplifies things and increases for sure the user experience of your React app. And once we start implementing it in our project, you will see why this hook is handy.

It’s important to know that the name of the package is swr or SWR and the hook used to get SWR features is named useSWR.

In theory, the SWR is maybe what you need to enhance your data fetching. However, we already have two great ways of making HTTP requests in our app: the Fetch API and the Axios library.

So, why using a new library to retrieve data? let’s try answering this legit question in the next section.

Comparison With Fetch And Axios

We already have many ways to make HTTP requests in our React Apps, and two of the most popular is the Fetch API and the Axios library. They are both great and allows us to fetch or send data easily. However, once the operation is done, they will not help us to cache or paginate data, you have to do it on our own.

Axios or Fetch will just handle the request and return the expected response, nothing more.

And compared to SWR, it’s a bit different because the SWR under the hood uses the Fetch API to request data from the server — it’s kind of a layer built on top of it. However, it has some nice features such as caching, pagination, scroll position recovery, dependent fetching, etc, and to be precise a certain level of reactivity out of the box that Axios or Fetch do not have. It’s a big advantage because having such features help to make our React Apps fast and user-friendly and reduce markedly the size of our code.

And to conclude, just keep in mind that SWR is not the same as Axios or Fetch even if it helps to deal with HTTP requests. SWR is more advanced than them, it provides some enhancements to keep our app synchronized with the back-end and hence increases the performance of our app.

Now we know what’s differences SWR has compared to the Axios library or the Fetch API, it’s time to dive into why using such a tool.

Recommended reading: Consuming REST APIs In React With Fetch And Axios

Why Using SWR For Data Fetching?

As I said earlier SWR ships with some handy features that help to increase the usability of your app easily. With SWR, you can paginate your data in no-time using useSWRPages, you can also fetch data that depends on another request or recover a scroll position when you get back to a given page, and so much more.

Usually, we show to the user a loading message or a spinner while fetching data from the server. And with SWR, you can make it better by showing to the user the cached or stale data while retrieving new data from the API. And once that operation is done, it will revalidate the data to show the new version. And you don’t need to do anything, SWR will cache the data the first time you fetch it and retrieve it automatically when a new request is made.

So far, we already see why using SWR over Axios or Fetch is better depending obviously on what you are aiming to build. But for many cases, I will recommend using SWR because it has great features that go beyond just fetching and returning data.

That said, we can now start building our React app and use the SWR library to fetch remote data.

So, let’s start by setting up a new project.

Setting Up

As I said earlier in the introduction, we will build an app that fetches data from the Pokemon API. You can use a different API if you want too, I will stick with it for now.

And to create a new app, we need to run the following command on the terminal:

npx create-react-app react-swr

Next, we need to install the SWR library by first navigating to the folder that holds the React app.

cd react-swr

And run on the terminal the following command to install the SWR package.

yarn add swr

Or if you’re using npm:

npm install swr

Now we have all set up done, let’s structure the project as follow to start using SWR:

src
├── components
|  └── Pokemon.js
├── App.js
├── App.test.js
├── index.js
├── serviceWorker.js
├── setupTests.js
├── package.json
├── README.md
├── yarn-error.log
└── yarn.lock

As you can see, the folder structure is simple. The only thing to notice is the components folder that holds the Pokemon.js file. It will be used later as a presentational component to show a single Pokemon once we get data from the API.

Great! With that in place, we can now start fetching data from the API using useSWR.

Fetching Remote Data

The SWR package has some handy features as we have seen above. However, there are two ways of configuring this library: either locally or globally.

A local setup means that every time we create a new file, we have to setup SWR again to be able to fetch remote data. And a global setup allows us to reuse a part of our configuration within different files because a fetcher function can be declared once and used everywhere.

And no worries, we will see both in this article, but for now, let’s get hands dirty and add some meaningful code in the App.js file.

Displaying The Data

import React from 'react'
import useSWR from 'swr'
import { Pokemon } from './components/Pokemon'

const url = 'https://pokeapi.co/api/v2/pokemon'

const fetcher = (...args) => fetch(...args).then((res) => res.json())

function App() {
    const { data: result, error } = useSWR(url, fetcher)

    if (error) return <h1>Something went wrong!</h1>
    if (!result) return <h1>Loading...</h1>

    return (
        <main className='App'>
            <h1>Pokedex</h1>
            <div>
                {result.results.map((pokemon) => (
                    <Pokemon key={pokemon.name} pokemon={pokemon} />
                ))}
            </div>
        </main>
    )
}
export default App

As you can see, we start by importing useSWR from the SWR library. This declares the URL of the API you want to get data from, and a function to fetch these data.

The function fetcher is used here to transform the data into JSON. It receives the data fetched as an argument and returns something.

Notice that here, I use the Rest operator ((...args)) since I’m not sure of the type and length of data received as a parameter, therefore, I copy everything before passing it again as an argument to the fetch method provided by useSWR which transforms the data into JSON and returns it.

That said, the fetcher and the url of the API can be now passed as parameters to the useSWR hook. With that, it can now make the request and it returns two states: the data fetched and an error state. And data: result is the same as data.result, we use object destructuring to pull result from data.

With the returned values, we can now check if the data is successfully fetched and then loop through it. And for each user, use the Pokemon component to display it.

Now we have the data and pass it down to the Pokemon Component, it’s time to update Pokemon.js to be able to receive and display the data.

Creating The Pokemon Component

import React from 'react'
import useSWR from 'swr'

const fetcher = (...args) => fetch(...args).then((res) => res.json())

export const Pokemon = ({ pokemon }) => {
    const { name } = pokemon
    const url = 'https://pokeapi.co/api/v2/pokemon/' + name

    const { data, error } = useSWR(url, fetcher)

    if (error) return <h1>Something went wrong!</h1>
    if (!data) return <h1>Loading...</h1>

    return (
        <div className='Card'>
            <span className='Card--id'>#{data.id}</span>
            <img
                className='Card--image'
                src={data.sprites.front_default}
                alt={name}
            />
            <h1 className='Card--name'>{name}</h1>
            <span className='Card--details'>
                {data.types.map((poke) => poke.type.name).join(', ')}
            </span>
        </div>
    )
}

Here, we have a component that receives a single Pokemon data from the API and displays it. However, the data received does not contain all fields needed, hence we have to make another request to the API to get the complete Pokemon object.

And as you can see, we use the same process to retrieve the data even if this time we append the name of the Pokemon to the URL.

By the way, if you are not familiar with destructuring, ({ pokemon }) is the same as receiving props and accessing to the pokemon object with props.pokemon. It’s just a shorthand to pull out values from objects or arrays.

With that in place, if you navigate to the root folder of the project and run on the terminal the following command:

yarn start

Or if you’re using npm:

npm start

You should see that the data are successfully fetched from the Pokemon API and displayed as expected.

fetching

Fetching illustration. (Large preview)

Great! We are now able to fetch remote data with SWR. However, this setup is a local one and can be a bit redundant because you can already see that App.js and Pokemon.js use the same fetcher function to do the same thing.

But luckily, the package comes with a handy provider named SWRConfig that helps to configure SWR globally. It’s a wrapper component that allows child components to use the global configuration and therefore the fetcher function.

To setup SWR globally, we need to update the index.js file because it’s where the App component is rendered using React DOM. If you want, you can use SWRConfig directly in the App.js file.

Configuring SWR Globally

import React from 'react'
import ReactDOM from 'react-dom'
import { SWRConfig } from 'swr'
import App from './App'
import './index.css'

const fetcher = (...args) => fetch(...args).then((res) => res.json())

ReactDOM.render(
    <React.StrictMode>
        <SWRConfig value={{ fetcher }}>
            <App />
        </SWRConfig>
    </React.StrictMode>,
    document.getElementById('root')
)

As you can see, we start by importing SWRConfig which is a provider that needs to wrap the higher component or just part of your React app that needs to use SWR features. It takes as props a value that expects an object of config. You can pass more than one property to the config object, here I just need the function to fetch data.

Now, instead of declaring the fetcher function in every file, we create it here and pass it as value to SWRConfig. With that, we can now retrieve data at any level in our app without creating another function and hence avoid redundancy.

Besides that, fetcher is equal to fetcher: fetcher, it’s just syntactic sugar proposed by ES6. With that change, we need now to update our components to use the global config.

Using The Global SWR Config

import React from 'react'
import useSWR from 'swr'
import { Pokemon } from './components/Pokemon'

const url = 'https://pokeapi.co/api/v2/pokemon'

function App() {
    const { data: result, error } = useSWR(url)

    if (error) return <h1>Something went wrong!</h1>
    if (!result) return <h1>Loading...</h1>

    return (
        <main className='App'>
            <h1>Pokedex</h1>
            <div>
                {result.results.map((pokemon) => (
                    <Pokemon key={pokemon.name} pokemon={pokemon} />
                ))}
            </div>
        </main>
    )
}
export default App

Now we only need to pass the url to useSWR, instead of passing the url and fetcher method. Let’s also tweak the Pokemon component a bit.

import React from 'react'
import useSWR from 'swr'

export const Pokemon = ({ pokemon }) => {
    const { name } = pokemon
    const url = 'https://pokeapi.co/api/v2/pokemon/' + name

    const { data, error } = useSWR(url)

    if (error) return <h1>Something went wrong!</h1>
    if (!data) return <h1>Loading...</h1>

    return (
        <div className='Card'>
            <span className='Card--id'>#{data.id}</span>
            <img
                className='Card--image'
                src={data.sprites.front_default}
                alt={name}
            />
            <h1 className='Card--name'>{name}</h1>
            <span className='Card--details'>
                {data.types.map((poke) => poke.type.name).join(', ')}
            </span>
        </div>
    )
}

You can already see that we have no fetcher function anymore, thanks to the global configuration which passes the function to useSWR under the hood.

Now, you can use the global fetcher function everywhere in your app. The only thing that the useSWR hook needs to fetch remote data is the URL.

However, we can still enhance the setup furthermore by creating a custom hook to avoid declaring the URL again and again, and instead, just pass as parameter the path.

Advanced Setup By Creating A Custom Hook

To do so, you have to create a new file in the root of the project named useRequest.js (you can name it whatever you want) and add this code block below to it.

import useSwr from 'swr'

const baseUrl = 'https://pokeapi.co/api/v2'

export const useRequest = (path, name) => {
    if (!path) {
        throw new Error('Path is required')
    }

    const url = name ? baseUrl + path + '/' + name : baseUrl + path
    const { data, error } = useSwr(url)

    return { data, error }
}

Here, we have a function that receives a path and optionally a name and appends it to the base URL to build the complete URL. Next, it checks if a name parameter is received or not and handle it consequently.

Then, that URL is passed as a parameter to the useSWR hook to be able to fetch the remote data and return it. And if no path is passed, it throws an error.

Great! we need now to tweak the components a bit to use our custom hook.

import React from 'react'
import { useRequest } from './useRequest'
import './styles.css'
import { Pokemon } from './components/Pokemon'

function App() {
    const { data: result, error } = useRequest('/pokemon')

    if (error) return <h1>Something went wrong!</h1>
    if (!result) return <h1>Loading...</h1>

    return (
        <main className='App'>
            <h1>Pokedex</h1>
            <div>
                {result.results.map((pokemon) => (
                    <Pokemon key={pokemon.name} pokemon={pokemon} />
                ))}
            </div>
        </main>
    )
}
export default App

Now, instead of using the SWR hook, we use the custom hook built on top of it and then pass as expected the path as an argument. With that in place, everything will work like before but with a much cleaner and flexible configuration.

Let’s also update the Pokemon component.

import React from 'react'
import { useRequest } from '../useRequest'

export const Pokemon = ({ pokemon }) => {
    const { name } = pokemon
    const { data, error } = useRequest('/pokemon', name)

    if (error) return <h1>Something went wrong!</h1>
    if (!data) return <h1>Loading...</h1>

    return (
        <div className='Card'>
            <span className='Card--id'>#{data.id}</span>
            <img
                className='Card--image'
                src={data.sprites.front_default}
                alt={name}
            />
            <h1 className='Card--name'>{name}</h1>
            <span className='Card--details'>
                {data.types.map((poke) => poke.type.name).join(', ')}
            </span>
        </div>
    )
}

You can already see how our custom hook makes things easier and more flexible. Here, we just need to pass additionally the name of the Pokemon to fetch to useRequest and it handles everything for us.

I hope you start enjoying this cool library — However, we still have things to discover because SWR offers so many features, and one of them is useSWRPages which is a hook to paginate data easily. So, let’s use that hook in the project.

Paginate Our Data With useSWRPages

SWR allows us to paginate data easily and request only a part of it, and when needed refetch data to show for the next page.

Now, let’s create a new file in the root of the project usePagination.js and use it as a custom hook for pagination.

import React from 'react'
import useSWR, { useSWRPages } from 'swr'
import { Pokemon } from './components/Pokemon'

export const usePagination = (path) => {
    const { pages, isLoadingMore, loadMore, isReachingEnd } = useSWRPages(
        'pokemon-page',
        ({ offset, withSWR }) => {
            const url = offset || `https://pokeapi.co/api/v2${path}`
            const { data: result, error } = withSWR(useSWR(url))

            if (error) return <h1>Something went wrong!</h1>
            if (!result) return <h1>Loading...</h1>

            return result.results.map((pokemon) => (
                <Pokemon key={pokemon.name} pokemon={pokemon} />
            ))
        },
        (SWR) => SWR.data.next,
        []
    )

    return { pages, isLoadingMore, loadMore, isReachingEnd }
}

As you can see, here we start by importing useSWRPages which is the helper that allows paginating data easily. It receives 4 arguments: the key of the request pokemon-page which is also used for caching, a function to fetch the data that returns a component if the data are successfully retrieved, and another function that takes the SWR object and request data from the next page, and an array of dependencies.

And once the data fetched, the function useSWRPages returns several values, but here we need 4 of them: the pages that is the component returned with the data, the function isLoadingMore which checks if the data are currently fetched, the function loadMore that helps fetching more data, and the method isReachingEnd which determines whether there is still data to retrieve or not.

Now we have the custom hook that returns the needed values to paginate data, we can now move to the App.js file and tweak it a bit.

import React from 'react'
import { usePagination } from './usePagination'
import './styles.css'

export default function App() {
    const { pages, isLoadingMore, loadMore, isReachingEnd } = usePagination(
        '/pokemon'
    )

    return (
        <main className='App'>
            <h1>Pokedex</h1>
            <div>{pages}</div>
            <button
                onClick={loadMore}
                disabled={isLoadingMore || isReachingEnd}
            >
                Load more...
            </button>
        </main>
    )
}

Once the usePagination hook imported, we can now pass the path as a parameter and get back the returned values. And since pages is a component, we don’t need to loop through the data or anything like that.

Next, we use the function loadMore on the button to fetch more data and disable it if the retrieving operation is not finished or if there is no data to fetch.

Great! with that change, we can now browse on the root of the project and start the server with this command to preview our app.

yarn start

Or if you’re using npm:

npm start

You should see that the data are successfully fetched and if you click on the button, new data will be retrieved by SWR.

Pagination
Pagination. (Large preview)

So far, we have seen in practice the SWR library, and I hope you are finding value on it. However, it still has some features to offer. Let’s dive into these functionalities in the next section.

Other Features Of SWR

The SWR library has a bunch of handy things that simplifies the way we build React apps.

Focus Revalidation

It’s a feature that allows updating or revalidating to be precise the data when you re-focus a page or switch between tabs. And by default, this functionality is enabled, but you can disable it anyway if it does not fit your need. It can be useful especially if you have data with high-level-frequency updates.

Refetch on Interval

The SWR library allows refetching data after a certain amount of time. It can be handy when your data changes at high speed or you need to make a new request to get a piece of new information from your database.

Local Mutation

With SWR, you can set a temporary local state that will update automatically when new data are fetched(revalidation). This feature comes in play particularly when you deal with an Offline-first approach, it helps to update data easily.

Scroll Position Recovery

This feature is very handy, especially when it comes to dealing with huge lists. It allows you to recover the scroll position after getting back to the page. And in any case, it increases the usability of your app.

Dependent Fetching

SWR allows you to fetch data that depends on other data. That means it can fetch data A, and once that operation is done, it uses it to fetch data B while avoiding waterfalls. And this feature helps when you have relational data.

That said, SWR helps to increase the user experience in any matter. It has more features than that, and for many cases it’s better to use it over the Fetch API or the Axios library.

Conclusion

Throughout this article, we have seen why SWR is an awesome library. It allows remote data fetching using React Hooks and helps to simplify some advanced features out of the box such as pagination, caching data, refetching on interval, scroll position recovery, and so on. SWR is also backend agnostic which means it can fetch data from any kind of APIs or databases. In definitive, SWR increases a lot the user experience of your React apps, it has a bright future and you should keep an eye on it or better use it in your next React app.

You can preview the finished project live here.

Thanks for reading!

Next Steps

You can go on to check the following links which will give you a better understanding beyond the scope of this tutorial.

Further Reading on SmashingMag:

Smashing Editorial
(ks, ra, yk, il)

Source: smash mag

Categories
Uncategorized

A Monthly Update On All Things Smashing

A Monthly Update On All Things Smashing

A Monthly Update On All Things Smashing

Iris Lješnjanin

2020-06-19T18:00:00+00:00
2020-07-05T00:11:03+00:00

The entire Smashing team has been doing its very best to bring you live sessions with real experts — people with practical experience who love to share what they have learned throughout their careers. We organized Smashing Meets on three days — events that were open to everyone in the web community. With sessions on performance, CSS, GraphQL, and creative courage, we made sure to leave plenty of time for Q&A, networking, competitions and prizes.

We’re overwhelmed with the feedback we’ve received since then; many of you seemed to mostly enjoy spending time with speakers, and asking questions directly. A huge thank you again to everyone who joined in — it was such an incredible experience to have shared with you all!

A still image of the Smashing Meets event that took place on June 9 2020 including Henri Helvetica, Yiying Lu and Vitaly Friedman

Henri Helvetica, Yiying Lu and Vitaly Friedman live on screen at the recent Smashing Meets event. (Image credit: Yiying Lu)

Smashing Meets Schedule Overview (May & June)

May 18 Yiying Lu Creativity in Cross-Cultural Communication Talk slides Watch video →
May 18 Phil Hawksworth Building With JAMStack: Keeping UIs And APIs Aligned Talk slides Watch video →
May 18 Mark Boulton Accessible Typography Talk slides Watch video →
May 19 Mandy Michael Fun With Browser and Sensor APIs Talk slides Watch video →
May 19 Rachel Andrew Hello, Subgrid! Talk slides Watch video →
May 19 Mark Boulton Accessible Typography Talk slides Watch video →
June 9 Henri Helvetica Moving Pictures: A Snap Shot of The Future Of Media Talk Slides

Watch video →
June 9 Rachel Andrew The Evolution of Responsive Design Talk Slides

Watch video →
June 9 Christian Nwamba GraphQL APIs for Frontend Developers Talk Slides

Watch video →
June 9 Yiying Lu Creative Courage Talk Slides

Watch video →

Smashing Online Workshops: Coming Up Next

Mark your calendars! We’ll be organizing even more online events in the next weeks. Whether the spotlight shines on CSS, accessibility, performance or UX, we want to help you boost your skills and learn practical, actionable insights from experts in the industry.

The previous workshops have been incredibly popular with many becoming sold out, so do check the details and save your spot as soon as you can.

Date Name Topic
July 2–17 Vitaly Friedman Buy! The eCommerce UX Workshop See details →
July 7–21 Brad Frost Design Systems See details →
August 17–31 Susan and Guthrie Weinschenk Behavioral Design See details →
August 20–Sept 4 Yiying Lu Designing for a Global Audience See details →
Sept 17–Oct 2 Natalia Tepluhina Vue.js: The Practical Guide See details →
Staying up to date can be so difficult. It’s a jungle out there, but we have you covered!
See online events →

One last thing. In case you find yourself thinking twice about joining in a Smashing workshops just because you think your manager could need just a little bit more persuasion, then we’ve got your back with a neat lil’ template: Convince Your Boss. Good luck!

Our Latest Addition To The Smashing Books: Shipping Now

We’re so proud to have officially released Paul Boag’s book, “Click! How To Encourage Clicks Without Shady Tricks”. It is a detailed guide on how to increase conversion and boost business KPIs without alienating customers along the way, and we’re sure you’ll find it tremendously useful.

A photograph of the hard copy of the latest Click! book written by Paul Boag

Quality hardcover. Free worldwide shipping. 100 days money-back-guarantee. Tell me more →

Smashing Podcast: Tune In!

Smashing Podcast18 episodes in, the Smashing Podcast has been better than we had ever expected! Every two weeks, Drew McLellan speaks to folks from different backgrounds, and there’s always so much to learn and share! You’re always welcome to tune in and share your questions and thoughts with us anytime.

We publish a new article every day on various topics that are current in the web industry. Here are some that our readers seemed to enjoy the most and have recommended further:

Best Picks From Our Newsletter

We’ll be honest: Every second week, we struggle with keeping the Smashing Newsletter issues at a moderate length — there are just so many talented folks out there working on brilliant projects! Kudos to everyone involved!

Interested in sponsoring? Feel free to check out our partnership options and get in touch with the team anytime — they’ll be sure to get back to you right away.

P.S. A huge thank you to Cosima Mielke for writing and preparing these posts!

Modern CSS Solutions For Old CSS Problems

We all know that CSS can sometimes be tricky to master. Just think of the classic question of how to center a div. In “Modern CSS Solutions for Old CSS Problems”, Stephanie Eckles explores solutions to those big and small CSS problems she has been solving in the last 13 years of being a front-end developer.

Modern CSS Solutions

Apart from the old centering issue, the series explores challenges like creating elements of equal height, making dropdown menus accessible, styling buttons, and much more. Be sure to check back regularly as Stephanie keeps adding new topics. Brilliant!

Illustrations For Everyone

Illustrations are a great way to add a personal touch to a design. However, not everyone is a born illustrator and not every project has the budget to hire someone who masters the craft. During the lockdown, Pablo Stanley and a few of his friends decided to change that and created a tool that makes art more accessible and gives everyone the ability to use illustrations in their creations. Meet Blush.

Blush

Blush is a collection of 13 mix-and-match illustration libraries created by artists from around the world. Whether it’s characters, cityscapes, plants, food, or a piece of abstract art, you can pick your favorite illustration from one of the packs and customize every little detail until you have the combination you need to tell your story. As Pablo puts it, it’s “like playing legos made of vectors”. The illustrations can be downloaded for free as high-quality PNGs. If you are an illustrator yourself and would like to make your work available to other makers, too, you can apply to get featured in Blush. A fantastic example of sharing and caring.

Learn Flexbox With Code Tidbits

You’ve always wanted to learn Flexbox but the whole undertaking seemed a bit, well, daunting? It doesn’t have to be. In fact, it might only take 30 code tidbits to get you on the path to mastering some Flexbox magic. Samantha Ming has got your back.

Flexbox30

In her free course Flexbox30, Samantha takes you through 30 short and crisp Flexbox lessons. After learning the core concepts of Flexbox, you will explore the ins and outs of parent and child properties. Each lesson comes with a cheat sheet that you can download which makes the course also a great refresher if you already know your way around Flexbox but struggle with some of the properties.

Global Website Speed Profiler

Performance benchmarking tools usually measure performance form a single location. But how does your site perform for real-world users who aren’t based in that one location? To get more precise results for their own market, the folks behind the WordPress security plugin Wordfence built Fast or Slow, a tool that measures real-world performance from different locations around the world.

Fast or Slow

Fast or Slow gets its data from a network of 13 servers in 13 cities around the world. Each server was calibrated to have the same performance so that the speed measurements give a true indication of what the site feels like to a real user. The performance test provides you with an overall score of a site’s performance and breaks the results further down by geographic location, while audits on CSS minification, image optimization, caching, and more help reveal specific performance bottlenecks. Fast Or Slow is free to use.

Sleek Browser Frames For Your Screenshots

With screenshots, there are usually two options: You take a screenshot of the entire browser window with browser extensions and maybe even bookmarks visible or, if you want something less distracting, you decide to only take a screenshot of the site without any border at all. If you’re looking for a more sophisticated solution, Browserframe might be for you.

BrowserFrame

Just drag and drop your screenshot into the tool, and it wraps it into a neat browser frame. There are multiple browsers, operating systems, and themes to choose from, and you can adjust the background color, shadow, padding, and some other details before you download the image. Perfect for blog posts, social media, slides, or wherever else you might want to use a screenshot.

SVG Path Data Syntax Explored

Do you know what the SVG path data syntax actually means? If not, you’re not alone. Mathieu Dutour has been working with SVGs for quite some time but always struggled to understand the path data structure. That’s why he built a visualizer for it.

SVG Path Visualizer

The SVG Path Visualizer visualizes the SVG path data you enter and lets you discover all its different commands. There are also several examples you can explore to get more familiar with the syntax. An insightful look into the skeleton of an SVG.

The Future Is Here

Imagine you are working on a project and want to add a picture of a book to it. You have the book right beside you on your desk, so you take out your phone, take a picture of the book, send the picture to your computer, open it in Photoshop, remove the background, and, finally, include the picture in your file. Well, now what if you could copy and paste the book from reality onto your screen, just where you want it to be? What might sound like science fiction, is already possible today.

AR Copy Paste

The research prototype AR Copy Paste allows you to copy elements from your surroundings and paste them into an image editing software. Three independent modules make it happen: a mobile app, a local server that acts as the interface between the app and Photoshop and finds the correct position on your screen, and, last but not least, an objection detection/background removal service to cut the image into shape. An impressive peek at the future of AR.

Black Illustrations

Black people and people of color are often underrepresented in illustrations and visuals. To spark change in the digital landscape, the team at 5four created Black Illustrations, illustration packs that paint a more diverse picture and show black people and people of color in a myriad of tasks.

Black Illustrations

There are six illustration packs that can be used on websites, pitch decks, infographics, or anywhere you like. Two of the decks are free to download: “The Office Hustle” shows people in an office environment, working from home, and having conversations, as well as in a healthcare setting, while “The Movement Pack” calls attention to #BlackLivesMatter and the fight against racial inequality. Other packs include education- and lifestyle-themed images. Beautiful designs with a powerful message.

See Easing Curves In Action

Movement in the real world is something fluid, no harsh or instant starts or stops, no constant speed. Easing curves help us bring that natural feel to motion on the web. However, in practice, they can be quite abstract to grasp, too.

Easings

With Easings, Paul Macgregor built a useful tool to visualize the effect common easing curves have on a range of interfaces — from a gallery carousel to a side menu, scroll jack, and modal. Just choose an easing curve from the collection to see it in action or enter a custom one. One for the bookmarks.

Insights Into How Developers Work Today

Since almost a decade, the yearly Developer Survey conducted by Stack Overflow is the largest survey of people who code. This year, they made it more representative of the diversity of programmers, asking 65,000 developers from around the world how they learn, which tools they use, and what they want. The survey was conducted in February and the results offer a comprehensive look inside the community.

2020 Developer Survey

The survey covers the developers’ professional and demographical background, looks at tools and their popularity, what the respondents’ dev environments look like and how they learn and solve problems, just like at their career values, job priorities, and working conditions. Interesting insights guaranteed.

CSS Art In A Single Div

What can you do in a single div? Lynn Fisher has a lot of creative answers to this question, as her CSS drawing project A Single Div shows.

A Single Div

Every drawing is made up of CSS living inside a single div element and uses a combination of Pug for templating and Stylus for CSS preprocessing. From lettering and geometrical patterns to random objects and little characters, each colorful tile is a wonderful example of what can be accomplished with CSS.

A Magic Notebook For Exploring Data

Open source has transformed software development, making sharing, collaboration, and transparency the norm. Inspired by this, Observable aims at reimagining data science for a connected world. To make it more approachable, accessible, and social.

Observable

Described as a “magic notebook for exploring data and thinking with code”, Observable lets you sketch with live data. You can prototype visualizations, connect to Web APIs, and see your “notebook” update instantly when changes are made. Learning from one another, sharing and reusing components with a community of fellow authors is a key part of the concept, teams are even able to edit a notebook together in realtime. Now what to make with it? Reports, explanatory visualizations, UI prototypes, documentation, art projects, visual designs — only your imagination is the limit.

Smashing Editorial
(cm, vf, ra)

Source: smash mag