Building an Image Gallery Blog with Symfony Flex: Data Testing

In the previous article, we demonstrated how to set up a Symfony project from scratch with Flex, and how to create a simple set of fixtures and get the project up and running.

The next step on our journey is to populate the database with a somewhat realistic amount of data to test application performance.

Note: if you did the “Getting started with the app” step in the previous post, you’ve already followed the steps outlined in this post. If that’s the case, use this post as an explainer on how it was done.

As a bonus, we’ll demonstrate how to set up a simple PHPUnit test suite with basic smoke tests.

More Fake Data

Once your entities are polished, and you’ve had your “That’s it! I’m done!” moment, it’s a perfect time to create a more significant dataset that can be used for further testing and preparing the app for production.

Simple fixtures like the ones we created in the previous article are great for the development phase, where loading ~30 entities is done quickly, and it can often be repeated while changing the DB schema.

Testing app performance, simulating real-world traffic and detecting bottlenecks requires bigger datasets (i.e. a larger amount of database entries and image files for this project). Generating thousands of entries takes some time (and computer resources), so we want to do it only once.

We could try increasing the COUNT constant in our fixture classes and seeing what will happen:

// src/DataFixtures/ORM/LoadUsersData.php
class LoadUsersData extends AbstractFixture implements ContainerAwareInterface, OrderedFixtureInterface
{
    const COUNT = 500;
    ...
}

// src/DataFixtures/ORM/LoadGalleriesData.php
class LoadGalleriesData extends AbstractFixture implements ContainerAwareInterface, OrderedFixtureInterface
{
    const COUNT = 1000;
    ...
}

Now, if we run bin/refreshDb.sh, after some time we’ll probably get a not-so-nice message like PHP Fatal error: Allowed memory size of N bytes exhausted.

Apart from slow execution, every error would result in an empty database because EntityManager is flushed only at the very end of the fixture class. Additionally, Faker is downloading a random image for every gallery entry. For 1,000 galleries with 5 to 10 images per gallery that would be 5,000 – 10,000 downloads, which is really slow.

There are excellent resources on optimizing Doctrine and Symfony for batch processing, and we’re going to use some of these tips to optimize fixtures loading.

First, we’ll define a batch size of 100 galleries. After every batch, we’ll flush and clear the EntityManager (i.e., detach persisted entities) and tell the garbage collector to do its job.

To track progress, let’s print out some meta information (batch identifier and memory usage).

Note: After calling $manager->clear(), all persisted entities are now unmanaged. The entity manager doesn’t know about them anymore, and you’ll probably get an “entity-not-persisted” error.

The key is to merge the entity back to the manager $entity = $manager->merge($entity);

Without the optimization, memory usage is increasing while running a LoadGalleriesData fixture class:

> loading [200] AppDataFixturesORMLoadGalleriesData
100 Memory usage (currently) 24MB / (max) 24MB
200 Memory usage (currently) 26MB / (max) 26MB
300 Memory usage (currently) 28MB / (max) 28MB
400 Memory usage (currently) 30MB / (max) 30MB
500 Memory usage (currently) 32MB / (max) 32MB
600 Memory usage (currently) 34MB / (max) 34MB
700 Memory usage (currently) 36MB / (max) 36MB
800 Memory usage (currently) 38MB / (max) 38MB
900 Memory usage (currently) 40MB / (max) 40MB
1000 Memory usage (currently) 42MB / (max) 42MB

Memory usage starts at 24 MB and increases for 2 MB for every batch (100 galleries). If we tried to load 100,000 galleries, we’d need 24 MB + 999 (999 batches of 100 galleries, 99,900 galleries) * 2 MB = ~2 GB of memory.

After adding $manager->flush() and gc_collect_cycles() for every batch, removing SQL logging with $manager->getConnection()->getConfiguration()->setSQLLogger(null) and removing entity references by commenting out $this->addReference('gallery' . $i, $gallery);, memory usage becomes somewhat constant for every batch.

// Define batch size outside of the for loop
$batchSize = 100;

...

for ($i = 1; $i <= self::COUNT; $i++) {
    ...

    // Save the batch at the end of the for loop
    if (($i % $batchSize) == 0 || $i == self::COUNT) {
        $currentMemoryUsage = round(memory_get_usage(true) / 1024);
        $maxMemoryUsage = round(memory_get_peak_usage(true) / 1024);
        echo sprintf("%s Memory usage (currently) %dKB/ (max) %dKB n", $i, $currentMemoryUsage, $maxMemoryUsage);

        $manager->flush();
        $manager->clear();

        // here you should merge entities you're re-using with the $manager
        // because they aren't managed anymore after calling $manager->clear();
        // e.g. if you've already loaded category or tag entities
        // $category = $manager->merge($category);

        gc_collect_cycles();
    }
}

As expected, memory usage is now stable:

> loading [200] AppDataFixturesORMLoadGalleriesData
100 Memory usage (currently) 24MB / (max) 24MB
200 Memory usage (currently) 26MB / (max) 28MB
300 Memory usage (currently) 26MB / (max) 28MB
400 Memory usage (currently) 26MB / (max) 28MB
500 Memory usage (currently) 26MB / (max) 28MB
600 Memory usage (currently) 26MB / (max) 28MB
700 Memory usage (currently) 26MB / (max) 28MB
800 Memory usage (currently) 26MB / (max) 28MB
900 Memory usage (currently) 26MB / (max) 28MB
1000 Memory usage (currently) 26MB / (max) 28MB

Instead of downloading random images every time, we can prepare 15 random images and update the fixture script to randomly choose one of them instead of using Faker’s $faker->image() method.

Let’s take 15 images from Unsplash and save them in var/demo-data/sample-images.

Then, update the LoadGalleriesData::generateRandomImage method:

private function generateRandomImage($imageName)
    {
        $images = [
            'image1.jpeg',
            'image10.jpeg',
            'image11.jpeg',
            'image12.jpg',
            'image13.jpeg',
            'image14.jpeg',
            'image15.jpeg',
            'image2.jpeg',
            'image3.jpeg',
            'image4.jpeg',
            'image5.jpeg',
            'image6.jpeg',
            'image7.jpeg',
            'image8.jpeg',
            'image9.jpeg',
        ];

        $sourceDirectory = $this->container->getParameter('kernel.project_dir') . '/var/demo-data/sample-images/';
        $targetDirectory = $this->container->getParameter('kernel.project_dir') . '/var/uploads/';

        $randomImage = $images[rand(0, count($images) - 1)];
        $randomImageSourceFilePath = $sourceDirectory . $randomImage;
        $randomImageExtension = explode('.', $randomImage)[1];
        $targetImageFilename = sha1(microtime() . rand()) . '.' . $randomImageExtension;
        copy($randomImageSourceFilePath, $targetDirectory . $targetImageFilename);

        $image = new Image(
            Uuid::getFactory()->uuid4(),
            $randomImage,
            $targetImageFilename
        );

        return $image;
    }

It’s a good idea to remove old files in var/uploads when reloading fixtures, so I’m adding rm var/uploads/* command to bin/refreshDb.sh script, immediately after dropping the DB schema.

Loading 500 users and 1000 galleries now takes ~7 minutes and ~28 MB of memory (peak usage).

Dropping database schema...
Database schema dropped successfully!
ATTENTION: This operation should not be executed in a production environment.

Creating database schema...
Database schema created successfully!
  > purging database
  > loading [100] AppDataFixturesORMLoadUsersData
300 Memory usage (currently) 10MB / (max) 10MB
500 Memory usage (currently) 12MB / (max) 12MB
  > loading [200] AppDataFixturesORMLoadGalleriesData
100 Memory usage (currently) 24MB / (max) 26MB
200 Memory usage (currently) 26MB / (max) 28MB
300 Memory usage (currently) 26MB / (max) 28MB
400 Memory usage (currently) 26MB / (max) 28MB
500 Memory usage (currently) 26MB / (max) 28MB
600 Memory usage (currently) 26MB / (max) 28MB
700 Memory usage (currently) 26MB / (max) 28MB
800 Memory usage (currently) 26MB / (max) 28MB
900 Memory usage (currently) 26MB / (max) 28MB
1000 Memory usage (currently) 26MB / (max) 28MB

Take a look at the fixture classes source: LoadUsersData.php and LoadGalleriesData.php.

The post Building an Image Gallery Blog with Symfony Flex: Data Testing appeared first on SitePoint.


Source: Sitepoint

How to Create a Mall Map with Real-time Data Using WRLD

As a web developer, you sometimes find yourself in a position where you are required to implement a map. Your first choice is to use Google Maps, right?

google-maps

This looks okay. However, you may be required to overlay additional information over the map with the help of markers. You can use this method, or you can find a better solution that allows you to create markers inside an indoor 3D map! How cool is that? With indoor markers, you can provide unique experiences for users where they will be able to access information and interact with UIs right inside the map.

completed-mall-map

In this tutorial, we’ll create two demos illustrating the power of WRLD maps. You’ll learn how to create custom apps that can overlay real-time information over a 3D map. In the first demo, we’ll add interactive markers to an existing indoor map of a mall. In the second demo, we’ll place colored polygons over parking areas, indicating capacity.

You can find the completed project for both demos in this GitHub repository.

Prerequisites

For this article, you only need to have a fundamental understanding of the following topics:

I’ll assume this is your first time using WRLD maps. However, I do recommend you at least have a quick read of the article:

You’ll also need a recent version of Node.js and npm installed on your system (at the time of writing, 8.10 LTS is the latest stable version). For Windows users, I highly recommend you use Git Bash or any other terminal capable of handling basic Linux commands.

This tutorial will use yarn for package installation. If you prefer to use npm, please refer to this guide if you are unfamiliar with yarn commands.

Acquire an API Key

Before you get started, you’ll need to create a free account on WRLD. Once you’ve logged in and verified your email address, you’ll need to acquire an API key. For detailed instructions on how to acquire one, please check out the Getting Started section on Building Dynamic 3D Maps where it’s well documented.

Approach to Building the Map

The creation of WRLD maps is a major technological achievement with great potential benefits for many industries. There are two main ways of expanding the platform’s capabilities:

  • Using built-in tools, e.g. Map Designer and Places Designer
  • Building a custom app

Let me break down how each method can be used to achieve the desired results.

1. Using Map Designer and Places Designer

For our first demo, we can use Places Designer to create Store Cards. This will require us to create a Collection Set where all Point of Interest markers will be held. This set can be accessed both within the WRLD ecosystem, and externally via the API key. We can pass this data to a custom map created using the Map Designer. With this tool, we can share the map with others using its generated link. If you would like to learn more about the process, please watch the video tutorials on this YouTube playlist.

map-tools

The beauty of this method is that no coding is required. However, in our case, it does have limitations:

  • Restrictive UI design – we can only use the UI that comes with Places Designer
  • Restrictive data set – we can’t display additional information beyond what is provided

In order to overcome these limitations, we need to approach our mall map challenge using the second method.

2. Building a Custom App

Building custom apps is the most flexible option. Although it takes some coding effort, it does allow us to comprehensively tap into the wealth of potential provided by the WRLD platform. By building a custom app, we can create our own UI, add more fields and access external databases in real-time. This is the method that we’ll use for this tutorial.

Building the App

Let’s first create a basic map, to which we’ll add more functionality later. Head over to your workspace directory and create a new folder for your project. Let’s call it mall-map.

Open the mall-map folder in your code editor. If you have VSCode, access the terminal using Ctrl + ` and execute the following commands inside the project directory:

# Initialize package.json
npm init -f

# Create project directories
mkdir src
mkdir src/js src/css

# Create project files
touch src/index.html
touch src/js/app.js
touch src/css/app.css
touch env.js

This is how your project structure should look:

 project-structure

Now that we have our project structure in place, we can begin writing code. We’ll start with index.html. Insert this code:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <link rel="stylesheet" href="./css/app.css" />
  <title>Shopping Mall</title>
</head>
<body>
  <div id="map"></div>
  <script src="js/app.js"></script>
</body>
</html>

Next, let’s work on css/app.css. I’m providing the complete styling for the entire project so that we don’t have to revisit this file again. In due time you’ll understand the contents as you progress with the tutorial.

@import "https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.0.1/leaflet.css";
@import "https://cdn-webgl.wrld3d.com/wrldjs/addons/resources/latest/css/wrld.css";
@import "https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.3.0/semantic.min.css";

html,
body {
  margin: 0;
  padding: 0;
  width: 100%;
  height: 100%;
}

#map {
  width: 100%;
  height: 100%;
  background-color: #000000;
}

/* -------- POPUP CONTENT -------- */
.main-wrapper > .segment {
  padding: 0px;
  width: 300px;
}

.contacts > span {
  display: block;
  padding-top: 5px;
}

Now we need to start writing code for app.js. However, we need a couple of node dependencies:

yarn add wrld.js axios

As mentioned earlier, we’ll be taking advantage of modern JavaScript syntax to write our code. Hence, we need to use babel to compile our modern code to a format compatible with most browsers. This requires installing babel dependencies and configuring them via a .babelrc file. Make sure to install them as dev-dependencies.

yarn add babel-core babel-plugin-transform-runtime babel-runtime --dev
touch .babelrc

Copy this code to the .babelrc file:

{
  "plugins": [
    [
      "transform-runtime",
      {
        "polyfill": false,
        "regenerator": true
      }
    ]
  ]
}

We’ll also need the following packages to run our project:

  • Parcel bundler – it’s like a simplified version of webpack with almost zero configuration
  • JSON Server – for creating a dummy API server

Install the packages globally like this:

yarn global add parcel-bundler json-server

# Alternative command for npm users
npm install -g parcel-bundler json-server

That’s all the node dependencies we need for our project. Let’s now write some JavaScript code. First, supply your WRLD API key in env.js:

module.exports = {
  WRLD_KEY: '<put api key here>',
 };

Then open js/app.js and copy this code:

const Wrld = require('wrld.js');
const env = require('../../env');

const keys = {
  wrld: env.WRLD_KEY,
};

window.addEventListener('load', async () => {
  const map = await Wrld.map('map', keys.wrld, {
    center: [56.459733, -2.973371],
    zoom: 17,
    indoorsEnabled: true,
  });
});

The first three statements are pretty obvious. We’ve put all our code inside the window.addEventListener function. This is to ensure our code is executed after the JavaScript dependencies, that we’ll specify later in index.html, have loaded. Inside this function, we’ve initialized the map by passing several parameters:

  • map – the ID of the div container we specified in index.html
  • keys.wrld – API key
  • center – latitude and longitude of the Overgate Mall located in Dundee, Scotland
  • zoom – elevation
  • indoorsEnabled – allow users to access indoor maps

Let’s fire up our project. Go to your terminal and execute:

parcel src/index.html

Wait for a few seconds for the project to finish bundling. When it’s done, open your browser and access localhost:1234. Depending on your Internet speed, the map shouldn’t take too long to load.

building-map

Beautiful, isn’t it? Feel free to click the blue icon. It will take you indoors. Navigate around to see the different stores. However, you’ll soon realize that you can’t access other floors. There’s also no button for exiting the indoor map. Let’s fix that in the next chapter.

Create Indoor Controls

To allow users to switch between different floors, we’ll provide them with a control widget that will allow them to do this. Simply add the following scripts to the head section of the public/index.html file:

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js"></script>
<script src="https://cdn-webgl.wrld3d.com/wrldjs/addons/indoor_control/latest/indoor_control.js"></script>

Still within the html file, add this div in the body section, right before the #map div:

<div id="widget-container" class="wrld-widget-container"></div>

Now let’s update js/app.js to initialize the widget. Place this code right after the map initialization section:

const indoorControl = new WrldIndoorControl('widget-container', map);

Now refresh the page, and click the ‘Enter Indoors’ icon. You should have a control widget that will allow you to switch between floors. Just drag the control up and down to fluidly move between floors.

indoor-controls

Amazing, isn’t it? Now let’s see how we can make our map a little bit more convenient for our users.

Enter Indoors Automatically

Don’t you find it a bit annoying that every time we need to test our map, we need to click the ‘Indoors’ icon? Users may start navigating to other locations which is not the intention of this app. To fix this, we need to navigate indoors automatically when the app starts without any user interaction. First, we require the indoor map id to implement this feature. We can find this information from the indoormapenter event. You can find all Indoor related methods here.

Add the following code in the js/app.js file.

...
// Place this code right after the Wrld.map() statement
map.indoors.on('indoormapenter', async (event) => {
  console.log(event.indoorMap.getIndoorMapId());
});
...

Refresh the page then check out your console. You should get this ID printed out: EIM-e16a94b1-f64f-41ed-a3c6-8397d9cfe607. Let’s now write the code that will perform the actual navigation:

const indoorMapId = 'EIM-e16a94b1-f64f-41ed-a3c6-8397d9cfe607';

map.on('initialstreamingcomplete', () => {
  map.indoors.enter(indoorMapId);
});

After saving the file, refresh the page and see what happens.

The indoor mall map should navigate automatically. Next, we’ll look at how we can create cards for each store. But first, we need to determine where to source our data.

Mall Map Planning

To create store cards for our map, we need several items:

  • Exact Longitude/Latitude coordinates of a store
  • Store contact information and opening hours
  • Design template for the store card

Store Card Coordinates

To acquire Longitude/Latitude coordinates, we need to access maps.wrld3d.com. Wait for the map to finish loading then enter the address 56.459733, -2.973371 in the search box. Press enter and the map will quickly navigate to Overgate Mall. Click the blue indoor icon for Overgate Mall and you should be taken to the mall’s indoor map. Once it’s loaded, locate the ‘Next’ store and right-click to open the context menu. Click the ‘What is this place? option. The coordinate popup should appear.

place-coordinates

Click the ‘Copy Coordinate’ button. This will give you the exact longitude/latitude coordinates of the store. Save this location address somewhere temporarily.

Store Card Information

You’ll also need to gather contact information from each store which includes:

  • image
  • description
  • phone
  • email
  • web
  • Twitter
  • opening times

You can source most of this information from Google. Luckily, I’ve already collected the data for you. For this tutorial, we’ll only deal with four stores on the ground floor. To access the information, just create a folder at the root of the project and call it data. Next save this file from GitHub in the data folder. Make sure to save it as db.json. Here is a sample of the data we’ll be using:

{
  "id":1,
  "title": "JD Sports",
  "lat": 56.4593425,
  "long": -2.9741433,
  "floor_id": 0,
  "image_url": "https://cdn-03.belfasttelegraph.co.uk/business/news/...image.jpg",
  "description":"Retail chain specialising in training shoes, sportswear & accessories.",
  "phone": "+44 138 221 4545",
  "email": "customercare@jdsports.co.uk",
  "web": "https://www.jdsports.co.uk/",
  "twitter": "@jdhelpteam",
  "tags": "sports shopping",
  "open_time":[
    { "day": "Mon",
      "time": "9:30am - 6:00pm"
    },]
}

The data is stored in an array labeled ‘pois’. POI stands for Places of Interest. Now that we have the data available, we can easily make it accessible via an API REST point by running the JSON server. Just open a new terminal and execute the command:

json-server --watch data/db.json

It should take a few seconds for the API to start. Once it’s fully loaded, you can test it with your browser at localhost:3000/pois. You can also fetch a single POI using this syntax:

- localhost:3000/pois/{id}

For example, localhost:3000/pois/3 should return a poi record with ID 3 in JSON format.

Store Card Design

We’ll use a clean elegant theme to neatly display contact information and opening times using a couple of tabs. We’ll create markers that will display a popup when clicked. This popup will have the following UI.

store-card-template

The code for this HTML design is a bit long to put here. You can view and download the file from this link. The design only has three dependencies:

  • Semantic UI CSS
  • jQuery
  • Semantic UI JS

Now that we have the data required and the design, we should be ready to start working on our indoor map.

Implementing Store Cards in Indoor Map

First let’s create a service that will allow us to access data from the JSON REST APIs. This data will be used for populating the Store Cards with the necessary information. Create the file js/api-service.js and copy this code:

const axios = require('axios');

const client = axios.create({
  baseURL: 'http://127.0.0.1:3000',
  timeout: 1000,
});

module.exports = {
  getPOIs: async () => {
    try {
      const response = await client.get('/pois');
      return response.data;
    } catch (error) {
      console.error(error);
    }
    return [];
  },
  getPOI: async (id) => {
    try {
      const response = await client.get(`/pois/${id}`);
      return response.data;
    } catch (error) {
      console.error(error);
    }
    return {};
  },
}

Here we are making use of the library axios to request data from the JSON server.

Next, we’ll convert our static HTML design for the Store Card to a format that will allow us to render data. We’ll be using JsRender for this. We’ll break down our static design into three templates:

  • Base Template – has containers for menu, info and time tabs.
  • Info Template – tab for store contact information.
  • Time Template – tab for store opening hours.

First, open index.html and add these scripts to the head section, right after the jQuery and indoor control scripts:

<head>
  ...
  <script src="https://cdnjs.cloudflare.com/ajax/libs/jsrender/0.9.90/jsrender.min.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.3.0/semantic.min.js"></script>
  ...
</head>

Next, copy this section of code right before the widget-container div:

  ...
  <!-- Menu Tabs UI -->
 <script id="baseTemplate" type="text/x-jsrender">
    <div class="main-wrapper">
      <div class="ui compact basic segment">
        <div class="ui menu tabular"> </div>
        <div id="infoTab" class="ui tab active" data-tab="Info"></div>
        <div id="timeTab" class="ui tab" data-tab="Time"></div>
      </div>
    </div>
  </script>

  <!-- Info Data Tab -->
  <script id="infoTemplate" type="text/x-jsrender">
    <div class="ui card">
      <div class="image">
        <img src={{:image_url}}>
      </div>
      <div class="content">
        <div class="header">{{:title}}</div>
        <div class="description">
          {{:description}}
        </div>
      </div>
      <div class="extra content contacts">
        <span>
          <i class="globe icon"></i>
          <a href="{{:web}}" target="_blank">{{:web}}</a>
        </span>
        <span>
          <i class="mail icon"></i>
          {{:email}}
        </span>
        <span>
          <i class="phone icon"></i>
          {{:phone}}
        </span>
      </div>
    </div>
  </script>

  <!-- Opening Times Data Tab -->
  <script id="timeTemplate" type="text/x-jsrender">
    <table class="ui celled table">
      <thead>
        <tr>
          <th>Day</th>
          <th>Time</th>
        </tr>
      </thead>
      <tbody>
        {{for open_time}}
        <tr>
          <td>{{:day}}</td>
          <td>{{:time}}</td>
        </tr>
        {{/for}}
      </tbody>
    </table>
  </script>
  ...

This is how the full code for index.html should look.

Next, let’s create another service that will manage the creation of Popups. Create the file js/popup-service.js and copy this code:

const Wrld = require('wrld.js');
const { getPOI } = require('./api-service');

const baseTemplate = $.templates('#baseTemplate');
const infoTemplate = $.templates('#infoTemplate');
const timeTemplate = $.templates('#timeTemplate');

const popupOptions = {
  indoorMapId: 'EIM-e16a94b1-f64f-41ed-a3c6-8397d9cfe607',
  indoorMapFloorIndex: 0,
  autoClose: true,
  closeOnClick: true,
  elevation: 5,
};

Let me explain each block step by step:

  • Block 1: WRLD is required for creating the Popup, getPOI function is required for fetching data
  • Block 2: The templates that we discussed earlier are loaded using jsrender
  • Block 3: Parameters that will be passed during Popup instantiation. Here is the reference documentation.

Next, let’s add tab menus that will be used for switching tabs. Simply add this code to js/popup-service.js:

const createMenuLink = (linkName, iconClass) => {
  const link = document.createElement('a');
  link.className = 'item';
  const icon = document.createElement('i');
  icon.className = `${iconClass} icon`;
  link.appendChild(icon);
  link.appendChild(document.createTextNode(` ${linkName}`));
  link.setAttribute('data-tab', linkName);
  link.addEventListener('click', () => {
    $.tab('change tab', linkName);
    $('.item').toggleClass('active');
  });
  return link;
};

const createMenu = (menuParent) => {
  const infoLink = createMenuLink('Info', 'info circle');
  infoLink.className += ' active';
  menuParent.appendChild(infoLink);
  const timeLink = createMenuLink('Time', 'clock');
  menuParent.appendChild(timeLink);
};

You might be wondering why we are using a complicated method of creating menu links. Ideally, we should be able to create them using HTML, then add a small JavaScript script to activate the tabs. Unfortunately, this doesn’t work within the context of a Popup. Instead, we need to create clickable elements using DOM manipulation methods.

The post How to Create a Mall Map with Real-time Data Using WRLD appeared first on SitePoint.


Source: Sitepoint

How To Speed Up The Wireframing Process With Photoshop And Adobe XD

How To Speed Up The Wireframing Process With Photoshop And Adobe XD

How To Speed Up The Wireframing Process With Photoshop And Adobe XD

Manuela Langella

2018-06-19T15:45:38+02:00
2018-06-19T14:32:28+00:00

Before starting any design project, there is one word that is sure follow you from the very beginning: wireframing. Today, we will learn how to create a wireframe in Adobe XD and how to implement some graphics from Photoshop just by using libraries.

But first, what exactly is a wireframe?

A wireframe is a visual representation of your project’s structure. It defines the bones, the elements that will work in your layout, and the placement of the content for your prototype.

The great thing about wireframing is that it’s a combination of simple elements that make you concentrate on your project’s functionality. In this stage, you can draw without thinking too much about the style and design.

You just have to figure out what your project targets are and how to develop them through wireframing by using simple elements. As you move further through wireframing, you develop the best solutions as team component make comments and suggestions about your sketch.

The first step is to create a project and name it “sections”, then make a list of “elements” you need to complete the different steps, up to the creation of the final “architecture.”

Creating a wireframe “by hand” first makes a lot of sense. It helps you develop the entire idea on paper (without digital limits), and also lets your concepts flow easily. For those of you who work in a team, working with paper doesn’t seem the best approach if you want to share your notions with everyone involved in the project — especially if you work with your team online.

In this tutorial, we will cover the following steps:

  1. Create a wireframe, select, and insert PS assets through libraries;
  2. Update PS files and see the results in Adobe XD.

We will create a set of objects to use in our wireframe. We will put them aside in our assets as we had an extra panel from where we can take our tools.

Once you’ve done with it, you can save it and re-use for future projects, by using the same elements again and adding some more objects as well.

You will need these Photoshop elements I prepared to use in our wireframe.

Here’s what we will create:


wireframe
Wireframe (Large preview

complete layout
Complete layout (Large preview

1 . Create A Wireframe And Select And Insert PS Assets Through Libraries

The best place to begin developing a wireframe from scratch is to draw it by hand first.

In this project, I want to create a landing page for an online course site. I know I need to communicate essential information in it. It doesn’t have to be perfect the first time out, but in the end, its effectiveness is very much dependent on how I organized the wireframe and how closely it aligns with the initial purpose.

First Step: Here are my own hand-drawn wireframes.


wireframe
Large preview

wireframe
Large preview

As you can see, there is not much information on them. The first intention is just to show how the layout will be composed and which elements are to be considered. Clean and simple.

Second Step: Re-submit the wireframe in a smaller size and with some margin notes which I use to explain the elements and their use:


explaining elements on wireframe
Large preview

Third Step: Let’s begin to create our digital wireframe with Adobe XD.

Open Adobe XD and choose “Web 1920” from the open window.


chose web 1920 in adobe xd
Large preview

Save your project as “Wireframe” by selecting File → Save as.

Once your file is saved, create another artboard for iPhone 67 Plus as well.

Click on the A (Artboard) button on the left side, and choose “iPhone 6/7/8” in the right sidebar.


creating artboard for iPhone formats
Large preview

creating artboard for iPhone formats
Large preview

And here are our two artboards: one for desktop and one for mobile.


creating two artboards, one for desktop and one for mobile
Large preview

Now we can begin to create our wireframe objects. Following our hand-drawn sketches, we will now create the same objects in XD.

Hero Image
Select the Rectangle Tool (R) and draw a shape where your hero image should be. Then grab the Line Tool (L) and draw two lines joining the vertices. This kind of shape represents our image placeholder.

Group the shape and lines and call the group “Hero”:


grouping shapes and lines
Large preview

Now let’s continue with the “Icons” section. I put some text before my icons, and I’m going to represent that visually with some lines. Grab the Line Tool (L) again and draw a single horizontal line. Click on Repeat Grid Tool ( + R on Mac or CTRL + R on Windows), and drag your line until you have three of them.

creatings icons

We need three symbols for our icons, so click on Ellipse Tool /E) and draw a circle. Click again on Repeat Grid Tool ( + R on Mac or CTRL + R on Windows) and create three circles. Then select the space between the circles and drag to make it wider.

creating circles

Feature Section
Create a light grey background (#F8F8F8) by using the Rectangle Tool (R). Repeat the steps from the previous Hero Image section above to create an image placeholder, then repeat the steps from the Icons Section (also above) to create a line for text. Finally, create a simple button with the help of the Rectangle Tool (R) tool.

This is the final result:


Final result
Large preview

For the Testimonial Section, repeat the same steps as before in order to create an image placeholder and some text lines. As you can see from the complete wireframe image, there’s a quotation-mark symbol that we have to insert.

We’re going to do it using Photoshop.

Open the Photoshop file I provided by clicking on this link.

I want to insert this image as a symbol by using Libraries CC.

In Photoshop, be sure to see Libraries panel by going on Window → Libraries. Create a new library by clicking on the little icon top right (see image):


creating a new library
Large preview

I named my library “Wireframe”. Feel free to give your library the name you desire.

Now click and drag on the symbol you want to have in your library:

clicking and dragging symbols in library

Switch back to XD, and go to File → Open CC Libraries and you will see the last symbol you just uploaded through Photoshop and the library you created.


symbols created in photoshop and moved to adobe xd
Large preview

Drag the quotation symbol into your wireframe in XD and position it wherever you need it to be.

positioning symbols into your wireframe

For the “Prices, Subscribe and Footer” sections, we will represent them by using additional boxes and lines like the ones you see in the image below.

Note: You can find the email icon in the Photoshop file which I’ve provided here.)

Follow the steps described in the Feature section to insert the symbol in the library through Photoshop, open it in XD, and drag it into your wireframe artboard.

This is the result:


result
Large preview

One last thing we need to do before going ahead is to order our layers. Be sure your layers are activated by clicking on the Layer Icon ( + Y for Mac or CTRL + Y for Windows).


ordering layers in adobe xd
Large preview

Group all of the section elements into folders (I assigned them the same name of the section they represent). This way, you will have all of your elements placed in order and won’t have any difficulty in finding them quickly (see image).


grouping section elements into folders
Large preview

grouping section elements into folders
Large preview

We are now done with our wireframe!

In the next step, we will build our design by using our wireframe and discovering how to modify Libraries’ elements instantly.

2. Adding A Layer Of Fidelity To Your Wireframe

We have just finished our wireframe and, at this point, we can double-check it to see if we have missed something. Once we are sure that we have all of the necessary information included in the wireframe, we can then share it with the project’s team.

We are ready to move on and update our wireframe to make it “live” with images, color, and placeholder copy.

Go ahead and create your design. Duplicate your wireframe by saving it with another name (e.g. “Wireframe-Layout”).

First, we’ll need an image for our Hero section (I went ahead and used this one by Priscilla Du Preez from Unsplash. .)

Open the image in Photoshop, and reduce the image size by clicking on Image → Image size and set the width at 3000px:


reducing image size
Large preview

Save your image and then drag it into your Libraries.

In XD, drag the image from Libraries to your Artboard. Let it fit with the shape we just created as the image placeholder.


dragging the image from Libraries to your Artboard
Large preview

I’m going to add a logo and some text to this image; I need the image to be a little darker so that the information is easy to read. Go back into Photoshop Libraries and double click on the image in the panel. Once the image is open, go on the Layer panel, select the image layer and click on Add a layer style at the bottom of the panel. Set a Color Overlay with the settings as shown below:


adding a logo and some text to the image
Large preview

Save it, and it will be automatically be saved in all of your libraries. Switch back to XD and you will see the image in your artboard updated (no need to drag it back from the library again).

Note: Depending on the image size, it could take a little more time for the libraries to update themselves.


updating images in libraries
Large preview

Now let’s insert our logo. Open the Photoshop file and drag the “Learn!” logo into the Libraries. This is the font I used.


inserting the logo in photoshop
Large preview

Since our background is dark, we will need a white logo. Switch back to Photoshop and double click on the logo from Libraries.

Grab the Type Tool, highlight the logo text, and make it white. Save it, and it will automatically be saved in your XD artboard as well.


creating a while logo on a dark background
Large preview

creating a while logo on a dark background
Large preview

Insert some text and a button to complete the Hero section.


inserting some text and a button to complete the Hero section
Large preview

Next, I’m going fill out the next section by adding text and icons. The ones I used are from a free pack I created for Smashing Magazine that you can find here.

As previously done, open up the icons and add them to your libraries in Photoshop, then switch back to XD to place them in your wireframe. Here is the result:


adding text and icons, result
Large preview

Now we’ll move on to the Feature section. As before, we’ll drag an image onto the image placeholder (I used this image by Sonnie Hiles found on Unsplash). Add in some text and a button as I have shown you in the previous steps above.


feature section
Large preview

Open the Photoshop file I provided and add the check symbol into your Libraries. Open Libraries in XD and put the icon near the text. Use the Repeat Grid to make three copies of it:

making copies of an icon and playing them next to the text

Now let’s change the color of the check symbol. Go back to Photoshop, open it from the Libraries and give it a Color Overlay as shown below:


changing the color of the check symbol
Large preview

Save it, and see your icons in XD directly updated.


changing the color of the check symbol
Large preview

Now let’s finish our layout.

For the Testimonial section, add in text and an image for the testimonial (I took mine from UI Faces).


adding in text and an image for the testimonial
Large preview

Finally, we’ll add information for the Price section, the Subscribe section, and the footer. You can find Price tables in the Photoshop file I provided. Drag them into your Libraries in Photoshop, then open Libraries in XD and drag them into your artboard. Feel free to modify them as you want.

And… we’re done!

Conclusion

In this tutorial, we have learned how to work with Photoshop and Adobe XD to create a wireframe, and then how to quickly add fidelity to it by modifying Libraries elements. For your reference, I’ve created a mobile wireframe which you can use to practice and follow along to this tutorial. Follow the steps as we did for the desktop version to add text and images.

Let me see your result in the comments!

This article is part of the UX design series sponsored by Adobe. Adobe XD tool is made for a fast and fluid UX design process, as it lets you go from idea to prototype faster. Design, prototype and share — all in one app. You can check out more inspiring projects created with Adobe XD on Behance, and also sign up for the Adobe experience design newsletter to stay updated and informed on the latest trends and insights for UX/UI design.

Smashing Editorial
(il)


Source: Smashing Magazine

Optimizing CSS: ID Selectors and Other Myths

In today’s typical scenario, where the average website ships 500KB of gzipped JavaScript and 1.5MB of images, running on a midrange Android device via 3G with a 400ms round trip time, CSS selector performance is the least of our problems.

Still, there’s something to be said about the topic, especially to weed out some of the myths and legends surrounding them. So let’s dive right in.

The Basics of CSS Parsing

First, to get on the same page — this article isn’t about the performance of CSS properties and values. What we’re covering here is the performance cost of the selectors themselves. I’ll be focusing on the Blink rendering engine, specifically Chrome 62.

The selectors can be split into a few groups and (roughly) sorted from the least to most expensive:

ranktypeexample
1.ID#classID
2.Class.class
3.Tagdiv
4.General and adjacent siblingdiv ~ a, div + a
5.Child and descendantdiv > a, div a
6.Universal*
7.Attribute[type="text"]
8.Pseudo-classes and elementsa:first-of-type, a:hover

Does this mean that you should only use IDs and classes? Well … not really. It depends. First, let’s cover how browsers interpret CSS selectors.

Browsers read CSS from right to left. The rightmost selector in a compound selector is known as the key selector. So, for instance, in #id .class > ul a, the key selector is a. The browser first matches all key selectors. In this case, it finds all elements on the page that match the a selector. It then finds all ul elements on the page and filters the as down to just those elements that are descendants of uls — and so on until it reaches the leftmost selector.

Therefore, the shorter the selector, the better. If possible, make sure that the key selector is a class or an ID to keep it fast and specific.

Measuring the Performance

Ben Frain created a series of tests to measure selector performance back in 2014. The test consisted of an enormous DOM comprising 1000 identical elements, and measuring the speed it took to parse various selectors, ranging from IDs to some seriously complicated and long compound selectors. What he found was that the delta between the slowest and fastest selector was ~15ms.

However, that was back in 2014. Things have changed a lot since then, and memorizing rules is all but useless in the ever-changing browser landscape. Always remember to do your own tests, especially when performance is concerned.

I went to do my own tests, and for that I used Paul Lewis’ test mentioned in Paul Irish’s comment expressing concern over the useful, yet convoluted “quantity selectors“:

These selectors are among the slowest possible. ~500 slower than something wild like “div.box:not(:empty):last-of-type .title”. Test page http://jsbin.com/gozula/1/quiet

The test was bumped up a bit, to 50000 elements, and you can test it out yourself. I did an average of 10 runs on my 2014 MacBook Pro, and what I got was the following:

SelectorQuery Time (ms)
div4.8740
.box3.625
.box > .title4.4587
.box .title4.5161
.box ~ .box4.7082
.box + .box4.6611
.box:last-of-type3.944
.box:nth-of-type(2n - 1)16.8491
.box:not(:last-of-type)5.8947
.box:not(:empty):last-of-type .title8.0202
.box:nth-last-child(n+6) ~ div20.8710

The results will of course vary depending on whether you use querySelector or querySelectorAll, and the number of matching nodes on the page, but querySelectorAll comes closer to the real use case of CSS, which is targeting all matching elements.

Even in such an extreme case, with 50000 elements to match, and using some really insane selectors like the last one, we find that the slowest one is ~20ms, while the fastest is the simple class at ~3.5ms. Not really that much of a difference. In a realistic, more “tame” DOM, with around 1000–5000 nodes, you can expect those results to drop by a factor of 10, bringing them to sub-millisecond parsing speeds.

What we can see from this test is that it’s not really worth it to worry over CSS selector performance. Just don’t overdo it with pseudo selectors and really long selectors. We can also see how Blink improved in the last two years. Instead of the stated ~500x slowdown for a “quantity selector” (.box:nth-last-child(n+6) ~ div) compared to an “insanity selector” (.box:not(:empty):last-of-type .title), we only see a ~1.5x slowdown. That’s an amazing improvement, and we can only expect browsers to get better, making CSS selector performance even less impactful.

You should, however, stick to using classes whenever possible, and adopt some sort of namespacing convention like BEM, SMACSS or OOCSS, since it will not only help your website’s performance but vastly help with code maintainability. Overqualified compound selectors, especially when used with tag and universal selectors — such as .header nav ul > li a > .inner — are extremely brittle and a source of many unforeseen errors. They are also a nightmare to maintain, especially if you inherit the code from someone else.

Quality over Quantity

A bigger problem of simply having expensive selectors is having a lot of them. This is know as “style bloat”, and you’ve probably seen the problem a lot. Typical examples are sites which import entire CSS frameworks like Bootstrap or Foundation, while using less than 10% of the transferred CSS. Another example is seen in old, never-refactored projects whose CSS has devolved into, as I like to call them, “Chronological Style Sheets” — CSS with a ton of appended classes to the end of the file as the project has changed and grown, now looking more like an overgrown garden full of weeds.

Not only does a large CSS file take longer to transfer, (and network is the biggest bottleneck in website performance), they also take longer to parse. As well as constructing the DOM from your HTML, the browser needs to construct a CSSOM (CSS Object Model) to compare it with the DOM and match the selectors.

So, keep your styles lean and DRY, don’t include everything and the kitchen sink, load what you need and when you need it, and use UNCSS if you need to.

If you want to dig more into how the browsers parse CSS, check out Nicole Sullivan’s post on Webkit, Ilya Grigorik’s article on how Blink does it, or Lin Clark’s article on Mozilla’s new Stylo CSS engine.

The post Optimizing CSS: ID Selectors and Other Myths appeared first on SitePoint.


Source: Sitepoint

Building an Image Gallery Blog with Symfony Flex: the Setup

This post begins our journey into Performance Month’s zero-to-hero project. In this part, we’ll set our project up so we can fine tune it throughout the next few posts, and bring it to a speedy perfection.


Now and then you have to create a new project repository, run that git init command locally and kick off a new awesome project. I have to admit I like the feeling of starting something new; it’s like going on an adventure!

Lao Tzu said:

The journey of a thousand miles begins with one step

We can think about the project setup as the very first step of our thousand miles (users!) journey. We aren’t sure where exactly we are going to end up, but it will be fun!

We also should keep in mind the advice from prof. Donald Knuth:

Premature optimization is the root of all evil (or at least most of it) in programming.

Our journey towards a stable, robust, high-performance web app will start with the simple but functional application — the so-called minimum viable product (MVP). We’ll populate the database with random content, do some benchmarks and improve performance incrementally. Every article in this series will be a checkpoint on our journey!

This article will cover the basics of setting up the project and organizing files for our Symfony Flex project. I’ll also show you some tips, tricks and helper scripts I’m using for speeding up the development.

What Are We Building?

Before starting any project, you should have a clear vision of the final destination. Where are you headed? Who will be using your app and how? What are the main features you’re building? Once you have that knowledge, you can prepare your environment, third-party libraries, and dive into developing the next big thing.

In this series of articles, we’ll be building a simple image gallery blog where users can register or log in, upload images, and create simple public image galleries with descriptions written in Markdown format.

We’ll be using the new Symfony Flex and Homestead (make sure you’ve read tutorials on them, as we’re not going to cover them here). We picked Flex because Symfony 4 is just about to come out (if it hasn’t already, by the time you’re reading this), because it’s infinitely lighter than the older version and lends itself perfectly to step-by-step optimization, and it’s also the natural step in the evolution of the most popular enterprise PHP framework out there.

All the code referenced in this article is available at the GitHub repo.

We’re going to use the Twig templating engine, Symfony forms, and Doctrine ORM with UUIDs as primary keys.

Entities and routes will use annotations; we’ll have simple email/password based authentication, and we’ll prepare data fixtures to populate the database.

Getting Started with the app

To try out the example we’ve prepared, do the following:

  • Set up an empty database called “blog”.
  • Clone the project repository from GitHub.
  • Run composer install.
  • If you now open the app in your browser, you should see an exception regarding missing database tables. That’s fine, since we haven’t created any tables so far.
  • Update the .env file in your project root directory with valid database connection string (i.e., update credentials).
  • Run the database init script ./bin/refreshDb.sh and wait until it generates some nice image galleries.
  • Open the app in your browser and enjoy!

After executing bin/refreshDb.sh you should be able to see the home page of our site:

Project homepage

You can log in to the app with credentials user1@mailinator.com and password 123456. See LoadUserData fixture class for more details regarding generated users.

Starting from scratch

In this section, we’ll describe how to set up a new project from scratch. Feel free to take a look at the sample app codebase and see the details.

After creating a new project based on symfony/skeleton by executing the command

composer create-project "symfony/skeleton:^3.3" multi-user-gallery-blog

… we can first set minimum stability to “dev” because of some cutting edge packages:

composer config minimum-stability dev

… and then require additional packages (some of them are referenced by their aliases, the new feature brought by Flex):

composer req annotations security orm template asset validator ramsey/uuid-doctrine

Dependencies used only in the dev environment are required with the --dev flag:

 composer req --dev fzaninotto/faker doctrine/Doctrine-Fixtures-Bundle

Flex is doing some serious work for us behind the scenes, and most of the libraries (or bundles) are already registered and configured with good-enough defaults! Check the config directory. You can check all the dependencies used in this project in the composer.json file.

Routes are defined by annotations, so the following will be automatically added into config/routes.yaml:

controllers:
  resource: ../src/Controller/
  type: annotation

Database, Scripts and Fixtures

Configure the DATABASE_URL environment variable (for example, by editing the .env file) to set up a working DB connection. If you’re using our own Homestead Improved (recommended), you’ve got a database set up called homestead with the user / pass homestead / secret. A DB schema can be generated from existing entities by executing:

./bin/console doctrine:schema:create

If this doesn’t run, try executing the console by invoking the PHP binary, like so:

php bin/console doctrine:schema:create

If this step executed fine in the “Getting Started with the app” section above, you should be able to see newly created tables in the database (for Gallery, Image and User entities).

If you want to drop the database schema, you can run:

./bin/console doctrine:schema:drop --full-database --force

Fake it ’til you make it!

I can’t imagine developing an app today without having data fixtures (i.e., scripts for seeding the DB). With a few simple scripts, you can populate your database with realistic content, which is useful when it comes to rapid app development and testing, but it’s also a requirement for a healthy CI pipeline.

I find the Doctrine Fixtures Bundle to be an excellent tool for handling data fixtures as it supports ordered fixtures (i.e., you can control the order of execution), sharing objects (via references) between scripts, and accessing the service container.

Default Symfony services configuration doesn’t allow public access to services, as best practice is to inject all dependencies. We’ll need some services in our fixtures, so I’m going to make all services in AppServices publicly available by adding the following to config/services.yaml:

AppService:
  resource: '../src/Service/*'
  public: true

I’m also using Faker to get random but realistic data (names, sentences, texts, images, addresses, …).

Take a look at the script for seeding galleries with random images to get a feeling of how cool this combination is.

Usually, I combine commands for dropping the existing DB schema, creating the new DB schema, loading data fixtures, and other repetitive tasks into a single shell script — for example, bin/refreshDb.sh — so I can easily regenerate the schema and load dummy data:

# Drop schema
./bin/console doctrine:schema:drop --full-database --force

# Create schema
./bin/console doctrine:schema:create

# Load fixtures
./bin/console doctrine:fixtures:load -n --fixtures src/DataFixtures/ORM

# Install assets
./bin/console assets:install --symlink

# Clear cache
./bin/console cache:clear

Make sure you restrict execution of this script on production, or you’re going to have some serious fun at one point.

One can argue that randomly generated data can’t reproduce different edge cases, so your CI can sometimes fail or pass depending on the data generation. It’s true, and you should make sure all edge cases are covered with your fixtures.

Every time you find an edge case causing a bug, make sure you add it to data fixtures. This will help you build a more robust system and prevent similar errors in the future.

The post Building an Image Gallery Blog with Symfony Flex: the Setup appeared first on SitePoint.


Source: Sitepoint

BEM For Beginners: Why You Need BEM

BEM For Beginners: Why You Need BEM

BEM For Beginners: Why You Need BEM

Inna Belaya

2018-06-18T14:00:51+02:00
2018-06-18T14:08:56+00:00

BEM makes your code scalable and reusable, thus increasing productivity and facilitating teamwork. Even if you are the only member of the team, BEM can be useful for you. Nevertheless, many developers believe that such a system approach like BEM puts additional boundaries on their project and makes your project overloaded, cumbersome, and slow.

We’ll be collecting all of the main aspects of BEM in a condensed form. This article helps you understand the basic ideas of BEM in just 20 minutes, and to reject prejudices that the system approach is detrimental to your project.

The Big BEM consists of Methodology, Technologies, Libraries, and Tools. In this article, we’ll talk more about the methodology itself because it is the concentrated experience of a huge number of developers and it brings a systematic approach to any project.

In order to show you some practical cases of BEM, we’ll touch on the BEM technologies and completely skip the libraries and tools.

From theory to practice:

So, is BEM a hero or a villain? It’s up to you! But first, read the article.


BEM as a Batman logo
BEMBatman

The Main Reasons Why We Do Not Use Any Selectors Except Classes

One of the basic rules of the BEM methodology is to use only class selectors. In this section, we’ll explain why.

  • Why don’t we use IDs?
  • Why don’t we use tag selectors?
  • Why don’t we use a universal selector?
  • Why don’t we use CSS reset?
  • Why don’t we use nested selectors?
  • Why don’t we combine a tag and a class in a selector?
  • Why don’t we use combined selectors-
  • Why don’t we use attribute selectors?

We Don’t Use IDs (ID Selectors)

The ID provides a unique name for an HTML element. If the name is unique, you can’t reuse it in the interface. This prevents you from reusing the code.

Common Misconceptions
  1. IDs are required for using JavaScript.
    Modern browsers can work with either IDs or classes. Any type of selector is processed at the same rate in the browser.
  2. IDs are used with the <label> tag.
    If you place <label> inside a control, it doesn’t need an ID. Instead of <input id="ID"><label for="ID">Text</label>, simply use <label><input type="...">Text</label>.

We Don’t Use Tag Selectors

HTML page markup is unstable: A new design can change the nesting of the sections, heading levels (for example, from <h1> to <h3>) or turn the <p> paragraph into the <div> tag. Any of these changes will break styles that are written for tags. Even if the design doesn’t change, the set of tags is limited. To use an existing layout in another project, you have to solve conflicts between styles written for the same tags.

An extended set of semantic tags can’t meet all layout needs, either.

An example is when the page header contains a logo. A click on the logo opens the main page of the site (index). You can mark it up with tags by using the <img> tag for the image and the <a> tag for the link.

<header>
  <a href="/">
    <img src="img.logo.png" alt="Logo">
  </a>
</header>

To distinguish between the logo link and an ordinary link in the text, you need extra styles. Now remove underlining and the blue color from the logo link:

header a {
  ...
}

The logo link doesn’t need to be shown on the main page, so change the index page markup:

<header>
  <!-- the <a> tag is replaced with <span> -->
  <span>
    <img src="img.logo.png" alt="Logo">
  </span>
</header>

You don’t need to remove the underlining and the blue color for the <span> tag. So let’s make general rules for the logo link from different pages:

header a,
header span
{
  ...
}

At first glance, this code seems all right, but imagine if the designer removes the logo from the layout. The selector names don’t help you understand which styles should be removed from the project with the logo. The “header a” selector doesn’t show the connection between the link and the logo. This selector could belong to the link in the header menu or, for example, to the link to the author’s profile. The “header span” selector could belong to any part of the header.

To avoid confusion, just use the logo class selector to write the logo styles:

.logo {
  ...
}

We Don’t Use CSS Reset

CSS reset is a set of global CSS rules created for the whole page. These styles affect all layout nodes, violate the independence of components, and make it harder to reuse them.

In BEM, “reset” and “normalize” aren’t even used for a single block. Resetting and normalization cancel existing styles and replace them with other styles, which you will have to change and update later in any case. As a result, the developer has to write styles that override the ones that were just reset.

We Don’t Use The Universal Selector (*)

The universal selector indicates that the project features a style that affects all nodes in the layout. This limits reuse of the layout in other projects:

  • You have to additionally transfer the styles with an asterisk to the project. But in this case, the universal selector might affect the styles in the new project.
  • The styles with an asterisk must be added to the layout you are transferring.

In addition, a universal selector can make the project code unpredictable. For example, it can affect the styles of the universal library components.

Common styles don’t save you time. Often developers start by resetting all margins for components (* { margin: 0; padding: 0; }), but then they still set them the same as in the layout (for example, margin: 12px; padding: 30px;).

We Don’t Use Nested Selectors

Nested selectors increase code coupling and make it difficult to reuse the code.

The BEM methodology doesn’t prohibit nested selectors, but it recommends not to use them too much. For example, nesting is appropriate if you need to change styles of the elements depending on the block’s state or its assigned theme.

.button_hovered .button__text
{
  text-decoration: underline;
}
.button_theme_islands .button__text
{
  line-height: 1.5;
}

We Don’t Use Combined Selectors

Combined selectors are more specific than single selectors, which makes it more difficult to redefine blocks.

Consider the following code:

<button class="button button_theme_islands">...</button>

Let’s say you set CSS rules in the .button.button_theme_islands selector to do less writing. Then you add the “active” modifier to the block:

<button class="button button_theme_islands button_active">...</button>

The .button_active selector doesn’t redefine the block properties written as .button.button_theme_islands because .button.button_theme_islands is more specific than .button_active. To redefine it, combine the block modifier selector with the .button selector and declare it below the .button.button_theme_islands because both selectors are equally specific:

.button.button_theme_islands {}
.button.button_active {}

If you use simple class selectors, you won’t have problems redefining the styles:

.button_theme_islands {}
.button_active {}
.button {}

We Don’t Combine A Tag And A Class In A Selector

Combining a tag and a class in the same selector (for example, button.button) makes CSS rules more specific, so it is more difficult to redefine them.

Consider the following code:

<button class="button">...</button>

Let’s say you set CSS rules in the button.button selector.
Then you add the active modifier to the block:

<button class="button button_active">...</button>

The .button_active selector doesn’t redefine the block properties written as button.button because button.button is more specific than .button_active. To make it more specific, you should combine the block modifier selector with the button.button_active tag.

As the project develops, you might end up with blocks with input.button, span.button or a.button selectors. In this case, all modifiers of the button block and all its nested elements will require four different declarations for each instance.

Possible Exceptions

In rare cases, the methodology allows combining tag and class selectors. For example, this can be used for setting the comments style in CMS systems that can’t generate the correct layout.

You can use the comment to write a text, insert images, or add markup. To make them match the site design, the developer can pre-define styles for all tags available to the user and cascade them down to the nested blocks:

<div class="content">
  ... <!-- the user’s text -->
</div>
CSS rules:
.content a {
  ...
}
.content p {
  font-family: Arial, sans-serif;
  text-align: center;
}

We Don’t Use Attribute Selectors

Attribute selectors are less informative than class selectors. As proof, consider an example with a search form in the header:

<header>
  <form action="/">
    <input name="s">
    <input type="submit">
  </form>
</header>

Try using selector attributes to write the form styles:

header input[type=submit],
header input[type=checkbox] {
  width: auto;
  margin-right: 20px;
}
header input[type=checkbox] {
  margin: 0;
}

In this example, you can’t tell for sure from the selector name that the styles belong to the search form. Using classes makes it clearer. Classes don’t have restrictions that prevent you from writing clearly. For example, you can write it like this:

.form .search {
  ...
}

Now the code is less ambiguous, and it’s clear that the styles belong to the search form.

But the nested selectors still make the CSS rules more specific and prevent you from transferring the layout between projects. To get rid of nesting, use BEM principles.

Summary: class is the only selector that allows you to isolate the styles of each component in the project; increase the readability of the code and do not limit the re-use of the layout.

CSS styles isolation is the most frequent start point of the BEM journey. But this is the least that BEM can give you. To understand how isolated independent components are arranged in BEM, you need to learn the basic concepts, i.e. Block, Element, Modifier, and Mix. Let’s do this in the next section.

The Basics Of BEM

Block And Elements

The BEM methodology is a set of universal rules that can be applied regardless of the technologies used, such as CSS, Sass, HTML, JavaScript or React.

BEM helps to solve the following tasks:

  • Reuse the layout;
  • Move layout fragments around within a project safely;
  • Move the finished layout between projects;
  • Create stable, predictable and clear code;
  • Reduce the project debugging time.

In a BEM project, the interface consists of blocks that can include elements. Blocks are independent components of the page. An element can’t exist outside the block, so keep in mind that each element can belong to one block only.


Source: Smashing Magazine

7 Performance Tips for Jank-free JavaScript Animations

The role of web animation has evolved from mere decorative fluff to serving concrete purposes in the context of user experience — such as providing visual feedback as users interact with your app, directing users’ attention to fulfill your app’s goals, offering visual cues that help users make sense of your app’s interface, and so on.

To ensure web animation is up to such crucial tasks, it’s important that motion takes place at the right time in a fluid and smooth fashion, so that users perceive it as aiding them, rather than as getting in the way of whatever action they’re trying to pursue on your app.

One dreaded effect of ill-conceived animation is jank, which is explained on jankfree.org like this:

Modern browsers try to refresh the content on screen in sync with a device’s refresh rate. For most devices today, the screen will refresh 60 times a second, or 60Hz. If there is some motion on screen (such as scrolling, transitions, or animations) a browser should create 60 frames per second to match the refresh rate. Jank is any stuttering, juddering or just plain halting that users see when a site or app isn’t keeping up with the refresh rate.

If animations are janky, users will eventually interact less and less with your app, thereby negatively impacting on its success. Obviously, nobody wants that.

In this article, I’ve gathered a few performance tips to help you solve issues with JavaScript animations and make it easier to meet the 60fps (frame per second) target for achieving smooth motion on the web.

#1 Avoid Animating Expensive CSS Properties

Whether you plan on animating CSS properties using CSS Transitions/CSS keyframes or JavaScript, it’s important to know which properties bring about a change in the geometry of the page (layout) — meaning that the position of other elements on the page will have to be recalculated, or that painting operations will be involved. Both layout and painting tasks are very expensive for browsers to process, especially if you have several elements on your page. As a consequence, you’ll see animation performance improve significantly if you avoid animating CSS properties that trigger layout or paint operations and stick to properties like transforms and opacity, because modern browsers do an excellent job of optimizing them.

On CSS Triggers you’ll find an up-to-date list of CSS properties with information about the work they trigger in each modern browser, both on the first change and on subsequent changes.

CSS Triggers Website

Changing CSS properties that only trigger composite operations is both an easy and effective step you can take to optimize your web animations for performance.

#2 Promote Elements You Want to Animate to Their Own Layer (with Caution)

If the element you want to animate is on its own compositor layer, some modern browsers leverage hardware acceleration by offloading the work to the GPU. If used judiciously, this move can have a positive effect on the performance of your animations.

To have the element on its own layer, you need to promote it. One way you can do so is by using the CSS will-change property. This property allows developers to warn the browser about some changes they want to make on an element, so that the browser can make the required optimizations ahead of time.

However, it’s not advised that you promote too many elements on their own layer or that you do so with exaggeration. In fact, every layer the browser creates requires memory and management, which can be expensive.

You can learn the details of how to use will-change, its benefits and downsides, in An Introduction to the CSS will-change Property by Nick Salloum.

#3 Replace setTimeOut/setInterval with requestAnimationFrame

JavaScript animations have commonly been coded using either setInterval() or setTimeout().

The code would look something like this:

var timer;
function animateElement() {
  timer = setInterval( function() {
    // animation code goes here
  } , 2000 );
}

// To stop the animation, use clearInterval
function stopAnimation() {
  clearInterval(timer);
}

Although this works, the risk of jank is high, because the callback function runs at some point in the frame, perhaps at the very end, which can result in one or more frames being missed. Today, you can use a native JavaScript method which is tailored for smooth web animation (DOM animation, canvas, etc.), called requestAnimationFrame().

requestAnimationFrame() executes your animation code at the most appropriate time for the browser, usually at the beginning of the frame.

Your code could look something like this:

function makeChange( time ) {
  // Animation logic here

  // Call requestAnimationFrame recursively inside the callback function
  requestAnimationFrame( makeChange ):
}

// Call requestAnimationFrame again outside the callback function
requestAnimationFrame( makeChange );

Performance with requestAnimationFrame by Tim Evko here on SitePoint offers a great video introduction to coding with requestAnimationFrame().

The post 7 Performance Tips for Jank-free JavaScript Animations appeared first on SitePoint.


Source: Sitepoint

Monthly Web Development Update 6/2018: Complexity, DNS Over HTTPS, And Push Notifications

Monthly Web Development Update 6/2018: Complexity, DNS Over HTTPS, And Push Notifications

Monthly Web Development Update 6/2018: Complexity, DNS Over HTTPS, And Push Notifications

Anselm Hannemann

2018-06-15T12:32:58+02:00
2018-06-15T13:49:35+00:00

We see complexity in every corner of a web project these days. We’ve read quite a bunch of articles about how complex a specific technology has become, and we discuss this over and over again. Coming from a time where we uploaded websites via FTP and had no git or anything comparable, now living in a time where we have a build system, transpilers, frameworks, tests, and a CI even for the smallest projects, this is easy to understand. But on the other hand, web development has grown up so much in the past 15 years that we can’t really compare today to the past anymore. And while it might seem that some things were easier in the past, we neglect the advantages and countless possibilities we have today. When we didn’t write tests back then, well, we simply had no test — meaning no reliable way to test for success. When we had no deployment process, it was easy to upload a new version but just as easy to break something — and it happened a lot more than today when a Continuous Integration system is in place.

Jeffrey Zeldman wrote an interesting article on the matter: “The Cult of Complex” outlines how we lose ourselves in unnecessary details and often try to overthink problems. I like the challenge of building systems that are not too complex but show a decent amount of responsibility (when it comes to ethics, privacy, security, a great user experience, and performance) and are working reliably (tests, deployments, availability, and performance again). I guess the problem of finding the right balance won’t go away anytime soon. Complexity is everywhere — we just need to decide if it’s useful complexity or if it was added simply because it was easier or because we were over-engineering the original problem.

News

  • The upcoming Safari version 12 was unveiled at Apple’s WWDC. Here’s what’s new: icons in tabs, strong passwords, as well as a password generator control via HTML attributes including two-factor authentication control, a 3D and AR model viewer, the Fullscreen API on iPads, font-display, and, very important, Intelligent Tracking Prevention 2.0 which is more restrictive than ever and might have a significant impact on the functionality of existing websites.
  • The headless Chrome automation library Puppeteer is now out in version 1.5. It brings along Browser contexts to isolate cookies and other data usually shared between pages, and Workers can now be used to interact with Web Workers, too.
  • Google released Lighthouse 3.0, the third major version of their performance analyzation tool which features a new report interface, some scoring changes, a CSV export, and First Contentful Paint measurement.
  • Chrome 67 is here, bringing Progressive Web Apps to the Desktop, as well as support for the Generic Sensor API, and extending the Credential Management API to support U2F authenticators via USB.
  • We’ve seen quite some changes in the browsers’ security interfaces over the past months. First, they emphasized sites that offer a secured connection (HTTPS). Then they decided to indicate insecure sites, and now Chrome announced new changes coming in fall that will make HTTPS the default by marking HTTP pages as “not secure”.
Desktop PWA in Chrome 67
Desktop Progressive Web Apps are now supported in Chrome OS 67, and the Chrome team already started working on support for Mac and Windows, too. (Image credit)

General

  • In “The Cult of the Complex”, Jeffrey Zeldman writes about how we often seem to forget that simplicity is the key and goal of everything we do, the overall goal for projects and life. He explains why it’s so hard to achieve and why it’s so much easier — and tempting — to cultivate complex systems. A very good read and definitely a piece I’ll add to my ‘evergreen’ list.
  • Heydon Pickering shared a new, very interesting article that teaches us to build a web component properly: This time he explains how to build an inclusive and responsive “Card” module.

UI/UX

  • Cool Backgrounds is a cool side project by Moe Amaya. It’s an online generator for polygonal backgrounds with gradients that can generate a lot of variants and shapes. Simply beautiful.

Tooling

Security

  • As security attacks via DNS gain popularity, DNS over HTTPS gets more and more important. Lin Clark explains the technology with a cartoon to make it easier to understand.
  • Windows Edge is now previewing support for same-site cookies. The attribute to lock down cookies even more is already available in Firefox and Chrome, so Safari is the only major browser that still needs to implement it, but I guess it’ll land in their Tech Preview builds very soon as well.
DNS Over HTTPS
Lin Clark created a cartoon to explain how you can better protect your users’ privacy with DNS over HTTPS. (Image credit)

Privacy

Web Performance

Accessibility

  • It’s relatively easy to build a loading spinner — for a Single Page Application during load, for example —, but we rarely think about making them accessible. Stuart Nelson now explains how to do it.
  • Paul Stanton shares which accessibility tools we should use to get the best results.

JavaScript

  • JavaScript has lately been bullied by people who favor Elm, Rust, TypeScript, Babel or Dart. But JavaScript is definitely not worse, as Andrea Giammarchi explains with great examples. This article is also a great read for everyone who uses one of these other languages as it shows a couple of pitfalls that we should be aware of.
  • For a lot of projects, we want to use analytics or other scripts that collect personal information. With GDPR in effect, this got a lot harder. Yett is a nice JavaScript tool that lets you block the execution of such resources until a user agrees to it.
  • Ryan Miller created a new publication called “The Frontendian”, and it features one of the best explanations and guides to CORS I’ve come across so far.
  • The folks at Microsoft created a nice interactive demo page to show what Web Push Notifications can and should look like. If you haven’t gotten to grips with the technology yet, it’s a great primer to how it all works and how to build an interface that doesn’t disturb users.
  • Filepond is a JavaScript library for uploading files. It looks great and comes with a lot of adapters for React, Vue, Angular, and jQuery.
  • React 16.4 is out and brings quite a feature to the library: Pointer Events. They’ll make it easier to deal with user interactions and have been requested for a long time already.
The Frontendian
Inspired by the parallels between basic astrological ideas and push notification architecture, the team at Microsoft explains how to send push notifications to a user without needing the browser or app to be opened. (Image credit)

CSS

Work & Life

  • Anton Sten wrote about the moral implications for our apps. A meaningful explanation why the times of “move fast and break things” are definitely over as we’re dealing with Artificial Intelligence, social networks that affect peoples’ lives, and privacy matters enforced by GDPR.
  • Basecamp now has a new chart type to display a project’s status: the so-called “hill chart” adds a better context than a simple progress bar could ever do it.
  • Ben Werdmüller shares his thoughts about resumes and how they always fail to reflect who you are, what you do, and why you should be hired.

I hope you enjoyed this monthly update. The next one is scheduled for July 13th, so stay tuned. In the meantime, if you like what I do, please consider helping me fund the Web Development Reading List financially.

Have a great day!

— Anselm

Smashing Editorial
(cm)


Source: Smashing Magazine

JSON Schema Validation & Expressive Query Syntax in MongoDB 3.6

This article was originally published on MongoDB. Thank you for supporting the partners who make SitePoint possible.

One of MongoDB’s key strengths has always been developer empowerment: by relying on a flexible schema architecture, MongoDB makes it easier and faster for applications to move through the development stages from proof-of-concept to production and iterate over update cycles as requirements evolve.

However, as applications mature and scale, they tend to reach a stable stage where frequent schema changes are no longer critical or must be rolled out in a more controlled fashion, to prevent undesirable data from being inserted into the database. These controls are especially important when multiple applications write into the same database, or when analytics processes rely on predefined data structures to be accurate and useful.

MongoDB 3.2 was the first release to introduce Document Validation, one of the features that developers and DBAs who are accustomed to relational databases kept demanding. As MongoDB’s CTO, Eliot Horowitz, highlighted in Document Validation and What Dynamic Schemas Means:

Along with the rest of the 3.2 “schema when you need it” features, document validation gives MongoDB a new, powerful way to keep data clean. These are definitely not the final set of tools we will provide, but is rather an important step in how MongoDB handles schema.

Announcing JSON Schema Validation Support

Building upon MongoDB 3.2’s Document Validation functionality, MongoDB 3.6 introduces a more powerful way of enforcing schemas in the database, with its support of JSON Schema Validation, a specification which is part of IETF’s emerging JSON Schema standard.

JSON Schema Validation extends Document Validation in many different ways, including the ability to enforce schemas inside arrays and prevent unapproved attributes from being added. These are the new features we will focus on in this blog post, as well as the ability to build business validation rules.

Starting with MongoDB 3.6, JSON Schema is the recommended way of enforcing Schema Validation. The next section highlights the features and benefits of using JSON Schema Validation.

Switching from Document Validation to JSON Schema Validation

We will start by creating an orders collection (based on an example we published in the Document Validation tutorial blog post):

db.createCollection("orders", {
  validator: {
    item: { $type: "string" },
    price: { $type: "decimal" }
  }
});

With this document validation configuration, we not only make sure that both the item and price attributes are present in any order document, but also that item is a string and price a decimal (which is the recommended type for all currency and percentage values). Therefore, the following element cannot be inserted (because of the “rogue” price attribute):

db.orders.insert({
    "_id": 6666, 
    "item": "jkl", 
    "price": "rogue",
    "quantity": 1 });

However, the following document could be inserted (notice the misspelled “pryce” attribute):

db.orders.insert({
    "_id": 6667, 
    "item": "jkl", 
    "price": NumberDecimal("15.5"),
    "pryce": "rogue" });

Prior to MongoDB 3.6, you could not prevent the addition of misspelled or unauthorized attributes. Let’s see how JSON Schema Validation can prevent this behavior. To do so, we will use a new operator, $jsonSchema:

db.runCommand({
  collMod: "orders",
  validator: {
    $jsonSchema: {
      bsonType: "object",
      required: ["item", "price"],
      properties: {

       item: {
            bsonType: "string"
       },
       price: {
          bsonType: "decimal"
        }
      }
    }
  }
});

The JSON Schema above is the exact equivalent of the document validation rule we previously set above on the orders collection. Let’s check that our schema has indeed been updated to use the new $jsonSchema operator by using the db.getCollectionInfos() method in the Mongo shell:

db.getCollectionInfos({name:"orders"})

This command prints out a wealth of information about the orders collection. For the sake of readability, below is the section that includes the JSON Schema:

...
"options" : {
    "validator" : {
        "$jsonSchema" : {
            "bsonType" : "object",
            "required" : [
                "item",
                "price"
            ],
            "properties" : {
                "item" : {
                    "bsonType" : "string"
                },
                "price" : {
                    "bsonType" : "decimal"
                }
            }
        }
    },
    "validationLevel" : "strict",
    "validationAction" : "error"
}
...

Now, let’s enrich our JSON schema a bit to make better use of its powerful features:

db.runCommand({
  collMod: "orders",
  validator: {
    $jsonSchema: {
      bsonType: "object",
      <strong>additionalProperties: false</strong>,
      required: ["item", "price"],
      properties: {
       <strong>_id: {}</strong>,
       item: {
            bsonType: "string",
            description: "'item' must be a string and is required"
        },
        price: {
          bsonType: "decimal",
          description: "'price' must be a decimal and is required"
        },
        quantity: {
          <strong>bsonType: ["int", "long"]</strong>,
          minimum: 1,
          maximum: 100,
          exclusiveMaximum: true,
          description:
            "'quantity' must be short or long integer between 1 and 99"
        }
      }
    }
  }
});

Let’s go through the additions we made to our schema:

  • First, note the use of the additionalProperties:false attribute: it prevents us from adding any attribute other than those mentioned in the properties section. For example, it will no longer be possible to insert data containing a misspelled pryce attribute. As a result, the use of additionalProperties:false at the root level of the document also makes the declaration of the _id property mandatory: whether our insert code explicitly sets it or not, it is a field MongoDB requires and would automatically create, if not present. Thus, we must include it explicitly in the properties section of our schema.
  • Second, we have chosen to declare the quantity attribute as either a short or long integer between 1 and 99 (using the minimum, maximum and exclusiveMaximum attributes). Of course, because our schema only allows integers lower than 100, we could simply have set the bsonType property to int. But adding long as a valid type makes application code more flexible, especially if there might be plans to lift the maximum restriction.
  • Finally, note that the description attribute (present in the item, price, and quantity attribute declarations) is entirely optional and has no effect on the schema aside from documenting the schema for the reader.

With the schema above, the following documents can be inserted into our orders collection:

db.orders.insert({ 
    "item": "jkl", 
    "price": NumberDecimal(15.50),
    "quantity": NumberInt(99)
  });

  db.orders.insert({ 
    "item": "jklm", 
    "price": NumberDecimal(15.50),
    "quantity": NumberLong(99)
  });

However, the following documents are no longer considered valid:

db.orders.insert({ 
    "item": "jkl", 
    "price": NumberDecimal(15.50),
    <strong>"quantity": NumberInt(100)</strong>
  });
  db.orders.insert({ 
    "item": "jkl", 
    "price": NumberDecimal(15.50),
    <strong>"quantity": "98"</strong>
  });
  db.orders.insert({ 
    "item": "jkl", 
    <strong>"pryce": NumberDecimal(15.50),</strong>
    "quantity": NumberInt(99)
  });

You probably noticed that our orders above are seemingly odd: they only contain one single item. More realistically, an order consists of multiple items and a possible JSON structure might be as follows:

{
    _id: 10000,
    total: NumberDecimal(141),
    VAT: 0.20,
    totalWithVAT: NumberDecimal(169),
    lineitems: [
        {
            sku: "MDBTS001",
            name: "MongoDB Stitch T-shirt",
            quantity: NumberInt(10),
            unit_price:NumberDecimal(9)
        },
        {
            sku: "MDBTS002",
            quantity: NumberInt(5),
            unit_price: NumberDecimal(10)
        }
    ]
}

With MongoDB 3.6, we can now control the structure of the lineitems array, for instance with the following JSON Schema:

The post JSON Schema Validation & Expressive Query Syntax in MongoDB 3.6 appeared first on SitePoint.


Source: Sitepoint