Collective #492

Inspirational Website of the Week: Florian Monfrini Smooth animations on scroll and modern typographic elements made us chose Florian Monfrini’s website as inspiration for this week. Get inspired Our Sponsor Meet The #1 WordPress Popup Plugin Build Popups in the same place you build Read more

iOS Performance Tricks To Make Your App Feel More Performant

dreamt up by webguru in Uncategorized | Comments Off on iOS Performance Tricks To Make Your App Feel More Performant

iOS Performance Tricks To Make Your App Feel More Performant

iOS Performance Tricks To Make Your App Feel More Performant

Axel Kee



Although modern iOS hardware is powerful enough to handle many intensive and complex tasks, the device could still feel unresponsive if you are not careful about how your app performs. In this article, we will look into five optimization tricks that will make your app feel more responsive.

1. Dequeue Reusable Cell

You’ve probably used tableView.dequeueReusableCell(withIdentifier:for:) inside tableView(_:cellForRowAt:) before. Ever wondered why you have to follow this awkward API, instead of just passing an array of cell in? Let’s go through the reasoning of this.

Say you have a table view with a thousand rows. Without using reusable cells, we would have to create a new cell for each row, like this:

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
   // Create a new cell whenever cellForRowAt is called.
   let cell = UITableViewCell()
   cell.textLabel?.text = "Cell (indexPath.row)"
   return cell
}

As you might have thought, this will add a thousand cells to the device’s memory as you scroll to the bottom. Imagine what would happen if each cell contained a UIImageView and a lot of text: Loading them all at once could cause the app to run out of memory! Apart from that, every single cell would require new memory to be allocated during scrolling. If you scroll a table view quickly, a lot of small chunks of memory will be allocated on the fly, and this process will make the UI janky!

To resolve this, Apple has provided us with the dequeueReusableCell(withIdentifier:for:) method. Cell reuse works by placing the cell that is no longer visible on the screen into a queue, and when a new cell is about to be visible on the screen (say, the subsequent cell below as the user scrolls down), the table view will retrieve a cell from this queue and modify it in the cellForRowAt indexPath: method.


Cell reuse queue mechanism
How cell reuse queues work in iOS (Large preview)

By using a queue to store cells, the table view doesn’t need to create a thousand cells. Instead, it needs just enough cells to cover the area of the table view.

By using dequeueReusableCell, we can reduce the memory used by the app and make it less prone to running out of memory!

2. Using A Launch Screen That Looks Like The Initial Screen

As mentioned in Apple’s Human Interface Guidelines (HIG), launch screens can be used to enhance the perception of an app’s responsiveness:

“It’s solely intended to enhance the perception of your app as quick to launch and immediately ready for use. Every app must supply a launch screen.”

It’s a common mistake to use a launch screen as a splash screen to show branding or to add a loading animation. Design the launch screen to be identical to the first screen of your app, as mentioned by Apple:

“Design a launch screen that’s nearly identical to the first screen of your app. If you include elements that look different when the app finishes launching, people can experience an unpleasant flash between the launch screen and the first screen of the app.

“The launch screen isn’t a branding opportunity. Don’t design an entry experience that looks like a splash screen or an “About” window. Don’t include logos or other branding elements unless they’re a static part of your app’s first screen.”

Using a launch screen for loading or branding purposes could slow down the time of first use and make the user feel that the app is sluggish.

When you start a new iOS project, a blank LaunchScreen.storyboard will be created. This screen will be shown to the user while the app loads the view controllers and layout.

To make your app feel faster, you can design the launch screen to be similar to the first screen (view controller) that will be shown to the user.

For example, the Safari app’s launch screen is similar to its first view :


Launch screen and first view look similar
A comparison of launch screen and first view of Safari app (Large preview)

The launch screen storyboard is like any other storyboard file, except that you can only use the standard UIKit classes, like UIViewController, UITabBarController, and UINavigationController. If you attempt to use any other custom subclasses (such as UserViewController), Xcode will notify you that using custom class names is prohibited.


Xcode shows error when a custom class is used
Launch screen storyboard cannot contain non-UIKit standard class. (Large preview)

Another thing to note is that UIActivityIndicatorView doesn’t animate when placed on the launch screen, because iOS will generate a static image from the launch screen storyboard and displays it to the user. (This is mentioned briefly in the WWDC 2014 presentation “Platforms State of the Union”, around 01:21:56.)

Apple’s HIG also advises us not to include text on our launch screen, because the launch screen is static, and you can’t localize text to cater to different languages.

Recommended reading: Mobile App With Facial Recognition Feature: How To Make It Real

3. State Restoration For View Controllers

State preservation and restoration allow the user to return to the exact same UI state from just before they left the app. Sometimes, due to insufficient memory, the operating system might need to remove your app from memory while the app is in the background, and the app might lose track of its last UI state if it is not preserved, possibly causing users to lose their work in progress!

In the multitasking screen, we can see a list of apps that have been put in the background. We might assume that these apps are still running in the background; in reality, some of these apps might get killed and restarted by the system due to the demands of memory. The app snapshots we see in the multitasking view are actually screenshots taken by the system from right when we exited the app (i.e. to go to the home or multitasking screen).


iOS fabricates the illusion of apps running in the background by taking a screenshot of the most recent view
Screenshots of apps taken by iOS when user exits the app (Large preview)

iOS uses these screenshots to give the illusion that the app is still running or is still displaying this particular view, whereas the app might have been already terminated or restarted in the background while still displaying the same screenshot.

Have you ever experienced, upon resuming an app from the multitasking screen, that the app shows a user interface different from the snapshot shown in the multitasking view? This is because the app hasn’t implemented the state-restoration mechanism, and the displayed data was lost when the app was killed in the background. This can lead to a bad experience because the user expects your app to be in the same state as when they left it.

From Apple’s article:

“They expect your app to be in the same state as when they left it. State preservation and restoration ensures that your app returns to its previous state when it launches again.”

UIKit does a lot of work to simplify state preservation and restoration for us: It handles the saving and loading of an app’s state automatically at appropriate times. All we need to do is add some configuration to tell the app to support state preservation and restoration and to tell the app what data needs to be preserved.

To enable state saving and restoring, we can implement these two methods in AppDelegate.swift:

func application(_ application: UIApplication, shouldSaveApplicationState coder: NSCoder) -> Bool {
   return true
}
func application(_ application: UIApplication, shouldRestoreApplicationState coder: NSCoder) -> Bool {
   return true
}

This will tell the app to save and restore the application’s state automatically.

Next, we’ll tell the app which view controllers need to be preserved. We do this by specifying the “Restoration ID” in the storyboard :


Setting restoration ID in storyboard
Setting restoration ID in storyboard (Large preview)

You can also check “Use Storyboard ID” to use the storyboard ID as the restoration ID.

To set the restoration ID in the code, we can use the restorationIdentifier property of the view controller.

// ViewController.swift
self.restorationIdentifier = "MainVC"

During state preservation, any view controller or view that has been assigned a restoration identifier will have its state saved to disk.

Restoration identifiers can be grouped together to form a restoration path. The identifiers are grouped using the view hierarchy, from the root view controller to the current active view controller. Suppose a MyViewController is embedded in a navigation controller, which is embedded in another tab bar controller. Assuming they are using their own class names as restoration identifiers, the restoration path will look like this:

TabBarController/NavigationController/MyViewController

When the user leaves the app with the MyViewController being the active view controller, this path will be saved by the app; then the app will remember the previous view hierarchy shown (Tab Bar ControllerNavigation ControllerMy View Controller).

After assigning the restoration identifier, we will need to implement the encodeRestorableState(with coder:) and decodeRestorableState(with coder:) methods for each of the preserved view controllers. These two methods let us specify what data need to be saved or loaded and how to encode or decode them.

Let’s see the view controller:

// MyViewController.swift
​
// MARK: State restoration
// UIViewController already conforms to UIStateRestoring protocol by default
extension MyViewController {

   // will be called during state preservation
   override func encodeRestorableState(with coder: NSCoder) {
       // encode the data you want to save during state preservation
       coder.encode(self.username, forKey: "username")
       super.encodeRestorableState(with: coder)
   }
   
   // will be called during state restoration
   override func decodeRestorableState(with coder: NSCoder) {
     // decode the data saved and load it during state restoration
     if let restoredUsername = coder.decodeObject(forKey: "username") as? String {
       self.username = restoredUsername
     }
     super.decodeRestorableState(with: coder)
   }
} 

Remember to call the superclass implementation at the bottom of your own method. This ensures that the parent class has a chance to save and restore state.

Once the objects have finished decoding, applicationFinishedRestoringState() will be called to tell the view controller that the state has been restored. We can update the UI for the view controller in this method.

// MyViewController.swift
​
// MARK: State restoration
// UIViewController already conforms to UIStateRestoring protocol by default
extension MyViewController {
   ...
 
   override func applicationFinishedRestoringState() {
     // update the UI here
     self.usernameLabel.text = self.username
   }
}

There you have it! These are the essential methods to implement state preservation and restoration for your app. Keep in mind that the operating system will remove the saved state when the app is being force-closed by the user, in order to avoid getting stuck in a broken state in case something goes wrong in the state preservation and restoration.

Also, don’t store any model data (i.e. data that should have been saved to UserDefaults or Core Data) to the state, even though it might seem convenient to do so. State data will be removed when the user force quits your app, and you certainly don’t want to lose model data this way.

To test whether state preservation and restoration are working well, follow the steps below:

  1. Build and launch an app using Xcode.
  2. Navigate to the screen with state preservation and restoration that you want to test.
  3. Return to the home screen (by swiping up or double-clicking home button, or pressing Shift ⇧ + Cmd ⌘ + H in the simulator) to send the app to the background.
  4. Stop the app in Xcode by pressing the ⏹ button.
  5. Launch the app again and check whether the state has been restored successfully.

Because this section only covers the basics of state preservation and restoration, I recommend the following articles by Apple Inc. for more in-depth knowledge of state restoration:

  1. Preserving And Restoring State
  2. UI Preservation Process
  3. UI Restoration Process

4. Reduce Usage Of Non-Opaque Views As Much As Possible

An opaque view is a view that has no transparency, meaning that any UI element placed behind it is not visible at all. We can set a view to be opaque in the Interface Builder:


This will inform the drawing system to skip drawing whatever is behind this view
Set UIView to opaque in storyboard (Large preview)

Or we can do it programmatically with the isOpaque property of UIView:

view.isOpaque = true

Setting a view to opaque will make the drawing system optimize some drawing performance while rendering the screen.

If a view has transparency (i.e. alpha is below 1.0), then iOS will have to do extra work to calculate what should be displayed by blending different layers of views in the view hierarchy. On the other hand, if a view is set to opaque, then the drawing system will just put this view in front and avoid the extra work of blending the multiple view layers behind it.

You can check which layers are being blended (non-opaque) in the iOS Simulator by checking DebugColor Blended Layers.


Green is non-color blended, red is blended layer
Show color blended layers in Simulator

After checking the Color Blended Layers option, you can see that some views are red and some are green. Red indicates that the view is not opaque and that its output display is a result of layers blended behind it. Green indicates that the view is opaque and no blending has been done.


With an opaque color background, the layer doesn’t need to blend with another layer
Assign non-transparent background color to UILabel whenever possible to reduce color blended layers. (Large preview)

The labels shown above (“View Friends”, etc.) are highlighted in red because when a label is dragged to the storyboard, its background color is set to transparent by default. When the drawing system is compositing the display near the label area, it will ask for the layer behind the label and do some calculation.

One way you can optimize app performance is to reduce how many views are highlighted with red as much as possible.

By changing label.backgroundColor = UIColor.clear to label.backgroundColor = UIColor.white, we can reduce layer blending between the label and the view layer behind it.


Using a transparent background color will cause layer blending
Many labels are highlighted in red because their background color is transparent, causing iOS to calculate the background color by blending the view behind it. (Large preview)

You might have noticed that, even if you have set a UIImageView to opaque and assigned a background color to it, the simulator will still show red in the image view. This is probably because the image you used for the image view has an alpha channel.

To remove the alpha channel for an image, you can use the Preview app to make a duplicate of the image (Shift ⇧ + Cmd ⌘ + S), and uncheck the “Alpha” checkbox when saving.


Uncheck the ‘Alpha’ checkbox when saving an image to discard the alpha channel.
Uncheck the ‘Alpha’ checkbox when saving an image to discard the alpha channel. (Large preview)

5. Pass Heavy Processing Functions To Background Threads (GCD)

Because UIKit only works on the main thread, performing heavy processing on the main thread will slow down the UI. The main thread is used by UIKit not only to handle and respond to user input, and also to draw the screen.

The key to making an app responsive is to move as many heavy processing tasks to background threads as possible. Avoid doing complex calculation, networking, and heavy IO operation (e.g. reading and writing to disk) on the main thread.

You might have once used an app that suddenly became unresponsive to your touch input, and it feels like the app has hung. This is most probably caused by the app running heavy computation tasks on the main thread.

The main thread usually alternates between UIKit tasks (such as handling user input) and some light tasks in small intervals. If a heavy task is running on main thread, then UIKit will need to wait until the heavy task has finished before being able to handle touch input.


Avoid running performance-intensive or time-consuming task on the main thread
Here is how the main thread handles UI tasks and why it causes the UI to hang when heavy tasks are performed. (Large preview)

By default, the code inside view controller lifecycle methods (such as viewDidLoad) and IBOutlet functions are executed on the main thread. To move heavy processing tasks to a background thread, we can use the Grand Central Dispatch queues provided by Apple.

Here’s the template for switching queues :

// Switch to background thread to perform heavy task.
DispatchQueue.global(qos: .default).async {
   // Perform heavy task here.
 
   // Switch back to main thread to perform UI-related task.
   DispatchQueue.main.async {
       // Update UI.
   }
}

The qos stands for “quality of service”. Different quality-of-service values indicate different priorities for the specified tasks. The operating system will allocate more CPU time and CPU power I/O throughput for tasks allocated in queues with higher QoS values, meaning that a task will finish faster in a queue with higher QoS values. A higher QoS value will also consume more energy due to it using more resources.

Here is the list of QoS values from highest to lowest priority:


Quality-of-service values of queue sorted by performance and energy efficiency
Quality-of-service values of queue sorted by performance and energy efficiency (Large preview)

Apple has provided a handy table with examples of which QoS values to use for different tasks.

One thing to keep in mind is that all UIKit code should always be executed on the main thread. Modifying UIKit objects (such as UILabel and UIImageView) on the background thread could have an unintended consequence, like the UI not actually updating, a crash occurring, and so on.

From Apple’s article:

“Updating UI on a thread other than the main thread is a common mistake that can result in missed UI updates, visual defects, data corruptions, and crashes.”

I recommend watching Apple’s WWDC 2012 video on UI concurrency to better understand how to build a responsive app.

Notes

The trade-off of performance optimization is that you have to write more code or configure additional settings on top of the app’s functionality. This might make your app delivered later than expected, and you will have more code to maintain in the future, and more code means potentially more bugs.

Before spending time on optimizing your app, ask yourself whether the app is already smooth or whether it has some unresponsive part that really needs to be optimized. Spending a lot of time optimizing an already smooth app to shave off 0.01 seconds might not be worth it, as the time could be better spent developing better features or other priorities.

Further Resources

Smashing Editorial
(jd, ra, il)

Source: Smashing Magazine, iOS Performance Tricks To Make Your App Feel More Performant

A Guide To CSS Support In Browsers

dreamt up by webguru in Uncategorized | Comments Off on A Guide To CSS Support In Browsers

A Guide To CSS Support In Browsers

A Guide To CSS Support In Browsers

Rachel Andrew



We will never live in a world where everyone viewing our sites has an identical browser and browser version, just as we will never live in a world where everyone has the same size screen and resolution. This means that dealing with old browsers — or browsers which do not support something that we want to use — is part of the job of a web developer. That said, things are far better now than in the past, and in this article, I’m going to have a look at the different types of browser support issues we might run into. I’m going to show you some ways to deal with them, and also look at things which might be coming soon which can help.

Why Do We Have These Differences?

Even in a world where the majority of browsers are Chromium-based, those browsers are not all running the same version of Chromium as Google Chrome. This means that a Chromium-based browser such as Vivaldi, might be a few versions behind Google Chrome.

And, of course, users do not always quickly update their browsers, although that situation has improved in recent years with most browsers silently upgrading themselves.

There is also the manner in which new features get into browsers in the first place. It is not the case that new features for CSS are designed by the CSS Working Group, and a complete spec handed down to browser vendors with an instruction to implement it. Quite often it is only when an experimental implementation happens, that all the finer details of the specification can be worked out. Therefore, feature development is an iterative process and requires that browsers implement these specifications in development. While implementation happens these days most often behind a flag in the browser or available only in a Nightly or preview version, once a browser has a complete feature, it is likely to switch it on for everyone even if no other browser yet has support.

All this means that — as much as we might like it — we will never exist in a world where features are magically available on every desktop and phone simultaneously. If you are a professional web developer then your job is to deal with that fact.

Bugs vs. Lack Of Support

There are three issues that we face with regard to browser support:

  1. No Support Of A Feature
    The first issue (and easiest to deal with) is when a browser does not support the feature at all.
  2. Dealing With Browser “Bugs”
    The second is when the browser claims to support the feature, but does so in a way that is different to the way that other browsers support the feature. Such an issue is what we tend to refer to as a “browser bug” because the end result is inconsistent behavior.
  3. Partial Support Of CSS Properties
    This one is becoming more common; a situation in which a browser supports a feature — but only in one context.

It’s helpful to understand what you are dealing with when you see a difference between browsers, so let’s have a look at each of these issues in turn.

1. No Support Of A Feature

If you use a CSS property or value that a browser does not understand, the browser will ignore it. This is the same whether you use a feature that is unsupported, or make up a feature and try to use it. If the browser does not understand that line of CSS, it just skips it and gets on with the next thing it does understand.

This design principle of CSS means that you can cheerfully use new features, in the knowledge that nothing bad will happen to a browser that doesn’t have support. For some CSS, used purely as an enhancement, that is all you need to do. Use the feature, make sure that when that feature is not available the experience is still good, and that’s it. This approach is the basic idea behind progressive enhancement, using this feature of the platform which enables the safe use of new things in browsers which don’t understand them.

If you want to check whether a feature you are using is supported by browsers then you can look at the Can I Use website. Another good place to look for fine-grained support information is the page for each CSS property on MDN. The browser support data there tends to be very detailed.

New CSS Understands Old CSS

As new CSS features are developed, care is taken in terms of how they interact with existing CSS. For example, in the Grid and Flexbox specification, it is detailed in terms of how display: grid and display: flex deal with scenarios such as when a floated item becomes a grid item, or a multicol container is turned into a grid. This means that certain behaviors are ignored, helping you to simply overwrite the CSS for the nonsupporting browser. These overrides are detailed in the page for Progressive enhancement and Grid Layout on MDN.

Detecting Support With Feature Queries

The above method only works if the CSS you need to use does not need other properties to go along with it. You might need to add additional properties to your CSS for older browsers which would then also be interpreted by the browsers which support the feature too.

A good example of this can be found when using Grid Layout. While a floated item which becomes a grid item loses all float behavior, it is likely that if you are trying to create a fallback for a grid layout with float, you will have added percentage widths and possibly margins to the items.

.grid > .item {
    width: 23%;
    margin: 0 1%;
}

A four column layout
Using floats we can create a four column layout, widths and margins need to be set in %. (Large preview)

These widths and margins will then still apply when the floated item is a grid item. The width becomes a percentage of the grid track rather than the full width of the container; any margin will then be applied as well as a gap you may have specified.

.grid > .item {
    width: 23%;
    margin: 0 1%;
}

.grid {
    display: grid;
    grid-template-columns: 1fr 1fr 1fr 1fr;
    column-gap: 1%;
}

A four column layout with squished columns
The width is now a percentage of the grid track — not the container. (Large preview)

Thankfully, there is a feature built into CSS and implemented into modern browsers which helps us deal with this situation. Feature Queries allow us to directly ask the browser what they support and then act on the response. Just like a Media Query — which tests for some properties of the device or screen — Feature Queries test for support of a CSS property and value.

Test For Support

Testing for support is the simplest case, we use @supports and then test for a CSS property and value. The content inside the Feature Query will only run if the browser responds with true, i.e. it does support the feature.

Test For No Support

You can ask the browser if it does not support a feature. In this case, the code inside the Feature Query will only run if the browser indicates it has no support.

@supports not (display: grid) {
    .item {
        /* CSS from browsers which do not support grid layout */
    }
}
Test For Multiple Things

If you need more than one property to be supported, use and.

@supports (display: grid) and (shape-outside: circle()){
    .item {
        /* CSS from browsers which support grid and CSS shapes */
    }
}

If you need support of one property or another, use or.

@supports (display: grid) or (display: flex){
    .item {
        /* CSS from browsers which support grid or flexbox */
    }
}
Picking A Property And Value To Test For

You don’t need to test for every property you want to use — just something which would indicate support for the features you are planning to use. Therefore, if you want to use Grid Layout, you might test for display: grid. In the future (and once subgrid support lands in browsers), you might need to be more specific and test for subgrid functionality. In that case, you would test for grid-template-columns: subgrid to get a true response from only those browsers which had implemented subgrid support.

If we now return to our floated fallback example, we can see how feature queries will sort it out for us. What we need to do is to query the browser to find out if it supports grid layout. If it does, we can set the width on the item back to auto and the margin to 0.

.grid > .item {
    width: 23%;
    margin: 0 1%;
}

@supports(display: grid) {
    .grid {
        display: grid;
        grid-template-columns: 1fr 1fr 1fr 1fr;
        column-gap: 1%;
    }

    .grid > .item {
        width: auto;
        margin: 0;
    }
}

Note that while I have included all of the grid code inside my feature query, I don’t need to. If a browser didn’t understand the grid properties it would ignore them so they could safely be outside of the feature query. The things that must be inside a feature query in this example are the margin and width properties, as these are needed for the old browser code but would also be applied by supporting browsers.

Embrace The Cascade

A very simple way to offer fallbacks is to utilize the fact that browsers ignore CSS that they don’t understand, and the fact that where everything else has equal specificity, source order is taken into account in terms of which CSS is applied to an element.

You first write your CSS for browsers which do not support the feature. Then test for support of property you want to use, if the browser confirms it has support overwrite the fallback code with your new code.

This is pretty much the same procedure that you might use when using media queries for responsive design, following a mobile-first approach. In that approach, you start with your layout for smaller screens, then add or overwrite things for larger ones as you move up through your breakpoints.

Can I Use CSS Feature Queries? Data on support for CSS Feature Queries across the major browsers from caniuse.com.

The above way of working means that you do not need to worry about browsers which do not support Feature Queries. As you can see from Can I Use, Feature Queries have really great support. The standout browsers that do not support them being any version of Internet Explorer.

It is likely, however, that the new feature you want to use is also not supported in IE. So, at the present time you will almost always start by writing CSS for browsers without support, then you test with a Feature Query. This Feature Query should test for support.

  1. Browsers which support Feature Queries will return true if they have support and so the code inside the query will be used, overwriting the code for older browsers.
  2. If the browser supports Feature Queries but not the feature being tested, it will return false. The code inside the feature query will be ignored.
  3. If the browser does not support Feature Queries then everything inside the Feature Query block will be ignored, which means that a browser such as IE11 will use your old browser code, which is very likely exactly what you want!

2. Dealing With Browser “Bugs”

The second browser support issue is thankfully becoming less common. If you read “What We Wished For” (published at the end of last year), you can get a little tour into some of the more baffling browser bugs of the past. That said, any software is liable to have bugs, browsers are no exception. And, if we add to that the fact that due to the circular nature of specification implementation, sometimes a browser implemented something and then the spec changed so they now need to issue an update. Until that update ships, we might be in a situation where browsers do something different to each other.

Feature Queries can’t help us if the browser reports support of something supports it badly. There is no mode by which the browser can say, “Yes, but you probably won’t like it.” When an actual interoperability bug shows up, it is in these situations where you might need to be a little more creative.

If you think you are seeing a bug then the first thing to do is confirm that. Sometimes when we think we see buggy behavior, and browsers doing different things, the fault lies with us. Perhaps we have used some invalid syntax, or are trying to style malformed HTML. In those cases, the browser will try to do something; however, because you aren’t using the languages as they were designed, each browser might cope in a different way. A quick check that your HTML and CSS is valid is an excellent first step.

At that point, I’d probably do a quick search and see if my issue was already widely understood. There are some repos of known issues, e.g. Flexbugs and Gridbugs. However, even just a well-chosen few keywords can turn up Stack Overflow posts or articles that cover the subject and may hand you a workaround.

But let’s say you don’t really know what is causing the bug, which makes it pretty hard to search for a solution. So, the next step is to create a reduced test case of your issue, i.e. stripping out anything irrelevant to help you identify exactly what triggers the bug. If you think you have a CSS bug, can you remove any JavaScript, or recreate the same styling outside of a framework? I often use CodePen to pop together a reduced test case of something I am seeing; this has the added advantage of giving me the code in a way I can easily share with someone else if I need to ask about it.

Most of the time, once you have isolated the issue, it is possible to think up an alternate way of achieving your desired result. You will find that someone else has come up with a cunning workaround, or you can post somewhere to ask for suggestions.

With that said, if you think you have a browser bug and can’t find anyone else talking about the same issue, it is quite possible you have found something new that should be reported. With all of the new CSS that has landed recently, issues can sometimes show up as people start to use things in combination with other parts of CSS.

Check out this post from Lea Verou about reporting such issues, “Help The Community! Report Browser Bugs!”. The article also has great tips for creating a reduced test case.

3. Partial Support Of CSS Properties

The third type of issue has become more common due to the way that modern CSS specifications are designed. If we think about Grid Layout and Flexbox, these specs both use the properties and values in Box Alignment Level 3, to do alignment. Therefore, properties such as align-items, justify-content, and column-gap are specified to be used in both Grid and Flexbox as well as other layout methods.

At the time of writing, however, the gap properties work in Grid Layout in all grid-supporting browsers, and column-gap works in Multicol; however, only Firefox has implemented these properties for Flexbox.

If I were to use margins to create a fallback for Flexbox, then test for column-gap and remove the margins, my boxes will have no space between them in browsers which support column-gap in Grid or multicol, so my fallback spacing will be removed.

@supports(column-gap: 20px) {
    .flex {
        margin: 0; /* almost everything supports column-gap so this will always remove the margins, even if we do not have gap support in flexbox. */
    }
}

This is a current limitation of Feature Queries. We don’t have a way to test for support of a feature in another feature. In the above situation, what I want to ask the browser is, “Do you have support for column-gap in Flexbox?” This way, I can get a negative response so I can use my fallback.

There is a similar issue with the CSS fragmentation properties break-before, break-after, and break-inside. As these have better support when the page is printed, browsers will often claim support. However, if you are testing for support in multicol, you get what appear to be false positives. I’ve raised an issue over at the CSS Working Group for this issue, however, it isn’t a straightforward problem to solve. If you have thoughts, please do add them there.

Testing For Selector Support

Currently, Feature Queries can only test for CSS Properties and Values. Another thing we might like to test for is the support of newer selectors, such as those in Level 4 of the Selectors specification. There is an explainer note and also an implementation behind a flag in Firefox Nightly of a new feature for Feature Queries which will achieve this.

If you visit about:config in Firefox and enable the flag layout.css.supports-selector.enabled then you can test to see if various selectors are supported. The syntax is currently very straightforward, for example to test for the :has selector:

@supports selector(:has){
  .item {
      /* CSS for support of :has */
  }
}

This is a specification in development, however, you can see how features to help us manage the ever-present issues of browser support are being added as we speak.

Further Reading

It can seem frustrating when you want to use a feature and discover that it isn’t supported by one major browser, or if things seem to be behaving in different ways. I’ve rounded up some practical further reading that might help.

https://cdn.jsdelivr.net/gh/ireade/caniuse-embed/caniuse-embed.min.js

Smashing Editorial
(il)

Source: Smashing Magazine, A Guide To CSS Support In Browsers

Using Vue.js To Create An Interactive Weather Dashboard With APIs

dreamt up by webguru in Uncategorized | Comments Off on Using Vue.js To Create An Interactive Weather Dashboard With APIs

Using Vue.js To Create An Interactive Weather Dashboard With APIs

Using Vue.js To Create An Interactive Weather Dashboard With APIs

Souvik Sarkar



(This is a sponsored article.) In this tutorial, you will build a simple weather dashboard from scratch. It will be a client-end application that is neither a “Hello World” example, nor too intimidating in its size and complexity.

The entire project will be developed using tools from the Node.js + npm ecosystem. In particular, we will be heavily relying on the Dark Sky API for the data, Vue.js for all the heavy lifting, and FusionCharts for data visualization.

Prerequisites

We expect that you are familiar with the following:

  • HTML5 and CSS3 (we will also be using the basic features provided by Bootstrap;
  • JavaScript (especially ES6 way of using the language);
  • Node.js and npm (the basics of the environment and package management is just fine).

Apart from the ones mentioned above, it would be great if you have familiarity with Vue.js, or any other similar JavaScript framework. We don’t expect you to know about FusionCharts — it’s so easy to use that you will learn it on the fly!

Expected Learnings

Your key learnings from this project will be:

  1. How to plan about implementing a good dashboard
  2. How to develop applications with Vue.js
  3. How to create data-driven applications
  4. How to visualize data using FusionCharts

In particular, each of the sections take you a step closer to the learning goals:

  1. An Introduction To The Weather Dashboard
    This chapter gives you an overview of different aspects of the undertaking.
  2. Create The Project
    In this section, you learn about creating a project from scratch using the Vue command-line tool.
  3. Customize The Default Project Structure
    The default project scaffolding that you get in the previous section is not enough; here you learn the additional stuff needed for the project from a structural point of view.
  4. Data Acquisition And Processing
    This section is the meat of the project; all the critical code for acquiring and processing data from the API is showcased here. Expect to spend maximum time on this section.
  5. Data Visualization With FusionCharts
    Once we have all the data and other moving parts of the project stabilized, this section is dedicated towards visualizing the data using FusionCharts and a bit of CSS.

1. The Dashboard Workflow

Before we dive into the implementation, it is important to be clear about our plan. We break our plan into four distinct aspects:

Requirements

What are our requirements for this project? In other words, what are the things that we want to showcase through our Weather Dashboard? Keeping in mind that our intended audience are probably mere mortals with simple tastes, we would like to show them the following:

  • Details of the location for which they want to see the weather, along with some primary information about the weather. Since there are no stringent requirements, we will figure out the boring details later. However, at this stage, it is important to note that we will have to provide the audience a search box, so that they can provide input for the location of their interest.
  • Graphical information about the weather of their location of interest, such as:
    • Temperature variation for the day of query
    • Highlights of today’s weather:
      • Wind Speed and Direction
      • Visibility
      • UV Index

Note: The data obtained from the API provides information regarding many other aspects of the weather. We choose not to use all of them for the sake of keeping the code to a minimum.

Structure

Based on the requirements, we can structure our dashboard as shown below:


Dashboard structure
(Large preview)

Data

Our dashboard is as good as the data we get, because there will be no pretty visualizations without proper data. There are plenty of public APIs that provide weather data — some of them are free, and some are not. For our project, we will collect data from the Dark Sky API. However, we will not be able to poll the API endpoint from the client end directly. Don’t worry, we have a workaround that will be revealed just at the right time! Once we get the data for the searched location, we will do some data processing and formatting — you know, the type of technicalities that helps us pay the bills.

Visualization

Once we get clean and formatted data, we plug it in to FusionCharts. There are very few JavaScript libraries in the world as capable as FusionCharts. Out of the vast number of offerings from FusionCharts, we will use only a few — all written in JavaScript, but works seamlessly when integrated with the Vue wrapper for FusionCharts.

Armed with the bigger picture, let’s get our hands dirty — it’s time to make things concrete! In the next section, you will create the basic Vue project, on top of which we will build further.

2. Creating The Project

To create the project, execute the following steps:

  1. Install Node.js + npm
    (If you have Node.js installed on your computer, skip this step.)
    Node.js comes with npm bundled with it, so you don’t need to install npm separately. Depending on the operating system, download and install Node.js according to the instructions given here.

    Once installed, it’s probably a good idea to verify if the software is working correctly, and what are their versions. To test that, open the command-line/terminal and execute the following commands:

    node --version
    npm --version
    
  2. Install packages with npm
    Once you have npm up and running, execute the following command to install the basic packages necessary for our project.
    npm install -g vue@2 vue-cli@2
    
  3. Initialize project scaffolding with vue-cli
    Assuming that the previous step has gone all well, the next step is to use the vue-cli — a command-line tool from Vue.js, to initialize the project. To do that, execute the following:
    • Initialize the scaffolding with webpack-simple template.
      vue init webpack-simple vue_weather_dashboard
              

      You will be asked a bunch of questions — accepting the defaults for all but the last question will be good enough for this project; answer N for the last one.


      A screenshot of the command-line/terminal
      (Large preview)

      Keep in mind that although webpack-simple is excellent for quick prototyping and light application like ours, it is not particularly suited for serious applications or production deployment. If you want to use any other template (although we would advise against it if you are a newbie), or would like to name your project something else, the syntax is:

      vue init [template-name] [project-name]
              
    • Navigate to the directory created by vue-cli for the project.
      cd vue_weather_dashboard
              
    • Install all the packages mentioned in the package.json, which has been created by the vue-cli tool for the webpack-simple template.
      npm install
              
    • Start the development server and see your default Vue project working in the browser!
      npm run dev
              

If you are new to Vue.js, take a moment to savor your latest achievement — you have created a small Vue application and its running at localhost:8080!


A screenshot of the Vue.js website
(Large preview)

Brief Explanation Of The Default Project Structure

It’s time to take a look at the structure inside the directory vue_weather_dashboard, so that you have an understanding of the basics before we start modifying it.

The structure looks something like this:

vue_weather_dashboard
|--- README.md
|--- node_modules/
|     |--- ...
|     |--- ...
|     |--- [many npm packages we installed]
|     |--- ...
|     |--- ...
|--- package.json
|--- package-lock.json
|--- webpack.config.js
|--- index.html
|--- src
|     |--- App.vue
|     |--- assets
|     |     |--- logo.png
|     |--- main.js 

Although it might be tempting to skip getting familiar with the default files and directories, if you are new to Vue, we strongly recommend at least taking a look at the contents of the files. It can be a good educational session and trigger questions that you should pursue on your own, especially the following files:

  • package.json, and just a glance at its cousin package-lock.json
  • webpack.config.js
  • index.html
  • src/main.js
  • src/App.vue

A brief explanation of each of the files and directories shown in the tree diagram are given below:

  • README.md
    No prize for guessing — it is primarily for humans to read and understand the steps necessary for creating the project scaffolding.
  • node_modules/
    This is the directory where npm downloads the packages necessary for kickstarting the project. The information about the packages necessary are available in the package.json file.
  • package.json
    This file is created by the vue-cli tool based on the requirements of the webpack-simple template, and contains information about the npm packages (including with their versions and other details) that must be installed. Take a hard look at the content of this file — this is where you should visit and perhaps edit to add/delete packages necessary for the project, and then run npm install. Read more about package.json here.
  • package-lock.json
    This file is created by npm itself, and is primarily meant for keeping a log of things that npm downloaded and installed.
  • webpack.config.js
    This a JavaScript file that contains the configuration of webpack — a tool that bundles different aspects of our project together (code, static assets, configuration, environments, mode of use, etc.), and minifies before serving it to the user. The benefit is that all things are tied together automatically, and the user experience enhances greatly because of the improvement in the application’s performance (pages are served quickly and loads faster on the browser). As you might encounter later, this is the file that needs to be inspected when something in the build system does not works the way it is intended to be. Also, when you want to deploy the application, this is one of the key files that needs to be edited (read more here).
  • index.html
    This HTML file serves as the matrix (or you can say, template) where data and code is to be embedded dynamically (that’s what Vue primarily does), and then served to the user.
  • src/main.js
    This JavaScript file contains code that primarily manages top/project level dependencies, and defines the topmost level Vue component. In short, it orchestrates the JavaScript for the entire project, and serves as the entry point of the application. Edit this file when you need to declare project-wide dependencies on certain node modules, or you want something to be changed about the topmost Vue component in the project.
  • src/App.vue
    In the previous point, when we were talking about the “topmost Vue component”, we were essentially talking about this file. Each .vue file in the project is a component, and components are hierarchically related. At the start, we have only one .vue file, i.e. App.vue, as our only component. But shortly we will add more components to our project (primarily following the structure of the dashboard), and link them in accordance to our desired hierarchy, with App.vue being the ancestor of all. These .vue files will contain code in a format that Vue wants us to write. Don’t worry, they are JavaScript code written maintaining a structure that can keep us sane and organized. You have been warned — by the end of this project, if you are new to Vue, you may get addicted to the template — script — style way of organizing code!

Now that we have created the foundation, it’s time to:

  • Modify the templates and tweak the configuration files a bit, so that the project behaves just the way we want.
  • Create new .vue files, and implement the dashboard structure with Vue code.

We will learn them in the next section, which is going to be a bit long and demands some attention. If you need caffeine or water, or want to discharge — now is the time!


3. Customizing The Default Project Structure

It’s time to tinker with the foundation that the scaffolded project has given us. Before you start, ensure that the development server provided by webpack is running. The advantage of running this server continuously is that any changes you make in the source code — one you save it and refresh the web page — it gets immediately reflected on the browser.

If you want to start the development server, just execute the following command from the terminal (assuming your current directory is the project directory):

npm run dev

In the following sections, we will modify some of the existing files, and add some new files.
It will be followed by brief explanations of the content of those files, so that you have an idea of what those changes are meant to do.

Modify Existing Files

index.html

Our application is literally a single page application, because there is just one webpage that gets displayed on the browser. We will talk about this later, but first let’s just make our first change — altering the text within the <title> tag.

With this small revision, the HTML file looks like the following:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <!-- Modify the text of the title tag below -->
    <title>Vue Weather Dashboard</title>
  </head>
  <body>
    
/dist/build.js </body> </html>

Take a moment to refresh the webpage at localhost:8080, and see the change reflected on the title bar of the tab on the browser — it should say “Vue Weather Dashboard”. However, this was just to demonstrate you the process of making changes and verifying if it’s working. We have more things to do!

This simple HTML page lacks many things that we want in our project, especially the following:

  • Some meta information
  • CDN links to Bootstrap (CSS framework)
  • link to custom stylesheet (yet to be added in the project)
  • Pointers to the Google Maps Geolocation API from tag

After adding those things, the final index.html has the following content:



 
   
   
   
   
   
   Weather Dashboard
   
 </head>
 <body>
   
/dist/build.js </body> </html>

Save the file, and refresh the webpage. You might have noticed a slight bump while the page was getting loaded — it is primarily due to the fact that the page style is now being controlled by Bootstrap, and the style elements like fonts, spacing, etc. are different from the default we had earlier (if you are not sure, roll back to the default and see the difference).


A screenshot when you refresh the webpage with localhost:8080
(Large preview)

Note: One important thing before we move on — the URL for the Google Maps API contains a key which is a property of FusionCharts. For now, you can use this key to build the project, as we don’t want you to get bogged down by these type of minute details (which can be distractions while you are new). However, we strongly urge you to generate and use your own Google Maps API key once you have made some progress and feel comfortable to pay attention to these tiny details.

package.json

At the time of writing this, we used certain versions of the npm packages for our project, and we know for sure that those things work together. However, by the time you are executing the project, it is very much possible that the latest stable versions of the packages that npm downloads for you are not the same as we used, and this might break the code (or do things that are beyond our control). Thus, it is very important to have the exact same package.json file that was used to build this project, so that our code/explanations and the results you get are consistent.

The content of the package.json file should be:

{
 "name": "vue_weather_dashboard",
 "description": "A Vue.js project",
 "version": "1.0.0",
 "author": "FusionCharts",
 "license": "MIT",
 "private": true,
 "scripts": {
   "dev": "cross-env NODE_ENV=development webpack-dev-server --open --hot",
   "build": "cross-env NODE_ENV=production webpack --progress --hide-modules"
 },
 "dependencies": {
   "axios": "^0.18.0",
   "babel": "^6.23.0",
   "babel-cli": "^6.26.0",
   "babel-polyfill": "^6.26.0",
   "fusioncharts": "^3.13.3",
   "moment": "^2.22.2",
   "moment-timezone": "^0.5.21",
   "vue": "^2.5.11",
   "vue-fusioncharts": "^2.0.4"
 },
 "browserslist": [
   "> 1%",
   "last 2 versions",
   "not ie <= 8"
 ],
 "devDependencies": {
   "babel-core": "^6.26.0",
   "babel-loader": "^7.1.2",
   "babel-preset-env": "^1.6.0",
   "babel-preset-stage-3": "^6.24.1",
   "cross-env": "^5.0.5",
   "css-loader": "^0.28.7",
   "file-loader": "^1.1.4",
   "vue-loader": "^13.0.5",
   "vue-template-compiler": "^2.4.4",
   "webpack": "^3.6.0",
   "webpack-dev-server": "^2.9.1"
 }
}

We encourage you to go through the new package.json, and figure out what are functions of different objects in the json. You may prefer changing the value of the “author” key to your name. Also, the packages mentioned in the dependencies will reveal themselves at the right time in the code. For the time being, it’s sufficient to know that:

  • babel-related packages are for properly handling the ES6 style code by the browser;
  • axios deals with Promise-based HTTP requests;
  • moment and moment-timezone are for date/time manipulation;
  • fusioncharts and vue-fusioncharts are responsible for rendering charts:
  • vue, for obvious reasons.
webpack.config.js

As with package.json, we suggest you to maintain a webpack.config.js file that is consistent with the one we used for building the project. However, before making any changes, we recommend you to carefully compare the default code in the webpack.config.js, and the code we have provided below. You will notice quite a few differences — google them and have a basic idea of what they mean. Since explaining webpack configurations in depth is out of the scope of this article, you are on your own in this regard.

The customized webpack.config.js file is as follows:

var path = require('path')
var webpack = require('webpack')

module.exports = {
 entry: ['babel-polyfill', './src/main.js'],
 output: {
   path: path.resolve(__dirname, './dist'),
   publicPath: '/dist/',
   filename: 'build.js'
 },
 module: {
   rules: [
     {
       test: /.css$/,
       use: [
         'vue-style-loader',
         'css-loader'
       ],
     },      {
       test: /.vue$/,
       loader: 'vue-loader',
       options: {
         loaders: {
         }
         // other vue-loader options go here
       }
     },
     {
       test: /.js$/,
       loader: 'babel-loader',
       exclude: /node_modules/
     },
     {
       test: /.(png|jpg|gif|svg)$/,
       loader: 'file-loader',
       options: {
         name: '[name].[ext]?[hash]'
       }
     }
   ]
 },
 resolve: {
   alias: {
     'vue$': 'vue/dist/vue.esm.js'
   },
   extensions: ['*', '.js', '.vue', '.json']
 },
 devServer: {
   historyApiFallback: true,
   noInfo: true,
   overlay: true,
   host: '0.0.0.0',
   port: 8080
 },
 performance: {
   hints: false
 },
 devtool: '#eval-source-map'
}

if (process.env.NODE_ENV === 'production') {
 module.exports.devtool = '#source-map'
 // http://vue-loader.vuejs.org/en/workflow/production.html
 module.exports.plugins = (module.exports.plugins || []).concat([
   new webpack.DefinePlugin({
     'process.env': {
       NODE_ENV: '"production"'
     }
   }),
   new webpack.optimize.UglifyJsPlugin({
     sourceMap: true,
     compress: {
       warnings: false
     }
   }),
   new webpack.LoaderOptionsPlugin({
     minimize: true
   })
 ])
}

With changes made to the project’s webpack.config.js, it’s imperative that you stop the development server which is running (Ctrl + C), and restart it with the following command executed from the project’s directory after installing all the packages mentioned in the package.json file:

npm install

npm run dev

With this, the ordeal of tweaking the configurations and ensuring that the right packages are in place ends. However, this also marks the journey of modifying and writing code, which is a bit long but also very rewarding!

src/main.js

This file is the key to top-level orchestration of the project — it is here that we define:

  • What the top level dependencies are (where to get the most important npm packages necessary);
  • How to resolve the dependencies, along with instructions to Vue on using plugins/wrappers, if any;
  • A Vue instance that manages the topmost component in the project: src/App.vue (the nodal .vue file).

In line with our goals for the src/main.js file, the code should be:

// Import the dependencies and necessary modules
import Vue from 'vue';
import App from './App.vue';
import FusionCharts from 'fusioncharts';
import Charts from 'fusioncharts/fusioncharts.charts';
import Widgets from 'fusioncharts/fusioncharts.widgets';
import PowerCharts from 'fusioncharts/fusioncharts.powercharts';
import FusionTheme from 'fusioncharts/themes/fusioncharts.theme.fusion';
import VueFusionCharts from 'vue-fusioncharts';

// Resolve the dependencies
Charts(FusionCharts);
PowerCharts(FusionCharts);
Widgets(FusionCharts);
FusionTheme(FusionCharts);

// Globally register the components for project-wide use
Vue.use(VueFusionCharts, FusionCharts);

// Instantiate the Vue instance that controls the application
new Vue({
 el: '#app',
 render: h => h(App)
})
src/App.vue

This is one of the most important files in the entire project, and represents the topmost component in the hierarchy — the entire application itself, as a whole. For our project, this component will do all the heavy lifting, which we will explore later. For now, we want to get rid of the default boilerplate, and put something of our own.

If you are new to Vue’s way of organizing code, it would be better to get an idea of the general structure within the .vue files. The .vue files comprises of three sections:

  • Template
    This is where the HTML template for the page is defined. Apart from the static HTML, this section also contains Vue’s way of embedding dynamic content, using the double curly braces {{ }}.
  • Script
    JavaScript rules this section, and is responsible for generating dynamic content that goes and sits within the HTML template at appropriate places. This section is primarily an object that is exported, and consists of:

    • Data
      This is a function itself, and usually it returns some desired data encapsulated within a nice data structure.
    • Methods
      An object that consists of one or more functions/methods, each of which usually manipulates data in some way or the other, and also controls the dynamic content of the HTML template.
    • Computed
      Much like the method object discussed above with one important distinction — while all the functions within the method object are executed whenever any one of them is called, the functions within the computed object behaves much more sensibly, and executes if and only if it has been called.
  • Style
    This section is for CSS styling that applies to the HTML of the page (written within template) — put the good old CSS here to make your pages beautiful!

Keeping the above paradigm in mind, let’s minimally customize the code in App.vue:

<template>
  

This component’s code is in {{ filename }}

</template> export default { data() { return { filename: 'App.vue' } }, methods: { }, computed: { }, } <style> </style>

Remember that the above code snippet is simply for testing out that App.vue is working with our own code in it. It will later go on through a lot of changes, but first save the file and refresh the page on the browser.


A screenshot of the browser with the message “This component’s code is in App.vue”
(Large preview)

At this point, it’s probably a good idea to get some help in tooling. Check out the Vue devtools for Chrome, and if you don’t have much problems in using Google Chrome as your default browser for development, install the tool and play around with it a bit. It will come in extremely handy for further development and debugging, when things becomes more complicated.

Additional Directories And Files

The next step would be to add additional files, so that the structure of our project becomes complete. We would add the following directories and files:

Note: Save the hyperlinked .svg files in your project.

Create the directories and files mentioned above. The final project structure should like look (remember to delete folders and files from the default structure that are now unnecessary):

vue_weather_dashboard/
|--- README.md
|--- node_modules/
|     |--- ...
|     |--- ...
|     |--- [many npm packages we installed]
|     |--- ...
|     |--- ...
|--- package.json
|--- package-lock.json
|--- webpack.config.js
|--- index.html
|--- src/
|     |--- App.vue
|     |--- css/
|     |     |--- style.css 
|     |--- assets/
|     |     |--- calendar.svg
|     |     |--- location.svg
|     |     |--- location.svg
|     |     |--- winddirection.svg
|     |     |--- windspeed.svg
|     |--- main.js
|     |--- components/
|     |     |--- Content.vue
|     |     |--- Highlights.vue
|     |     |--- TempVarChart.vue
|     |     |--- UVIndex.vue
|     |     |--- Visibility.vue
|     |     |--- WindStatus.vue

There might be some other files, like .babelrc, .gitignore, .editorconfig, etc. in the project’s root folder. You may ignore them safely for now.

In the following section, we will add minimal content to the newly added files, and test whether they are properly working.

src/css/style.css

Although it will not be of much use immediately, copy the following code to the file:

@import url("https://fonts.googleapis.com/css?family=Roboto:300,400,500");

:root {
   font-size: 62.5%;
}

body {
   font-family: Roboto;
   font-weight: 400;
   width: 100%;
   margin: 0;
   font-size: 1.6rem;
}

#sidebar {
   position: relative;
   display: flex;
   flex-direction: column;
   background-image: linear-gradient(-180deg, #80b6db 0%, #7da7e2 100%);
}

#search {
   text-align: center;
   height: 20vh;
   position: relative;
}

#location-input {
   height: 42px;
   width: 100%;
   opacity: 1;
   border: 0;
   border-radius: 2px;
   background-color: rgba(255, 255, 255, 0.2);
   margin-top: 16px;
   padding-left: 16px;
   color: #ffffff;
   font-size: 1.8rem;
   line-height: 21px;
}

#location-input:focus {
   outline: none;
}

::placeholder {
   color: #FFFFFF;
   opacity: 0.6;
}

#current-weather {
   color: #ffffff;
   font-size: 8rem;
   line-height: 106px;
   position: relative;
}

#current-weather>span {
   color: #ffffff;
   font-size: 3.6rem;
   line-height: 42px;
   vertical-align: super;
   opacity: 0.8;
   top: 15px;
   position: absolute;
}

#weather-desc {
   font-size: 2.0rem;
   color: #ffffff;
   font-weight: 500;
   line-height: 24px;
}

#possibility {
   color: #ffffff;
   font-size: 16px;
   font-weight: 500;
   line-height: 19px;
}

#max-detail,
#min-detail {
   color: #ffffff;
   font-size: 2.0rem;
   font-weight: 500;
   line-height: 24px;
}

#max-detail>i,
#min-detail>i {
   font-style: normal;
   height: 13.27px;
   width: 16.5px;
   opacity: 0.4;
}

#max-detail>span,
#min-detail>span {
   color: #ffffff;
   font-family: Roboto;
   font-size: 1.2rem;
   line-height: 10px;
   vertical-align: super;
}

#max-summary,
#min-summary {
   opacity: 0.9;
   color: #ffffff;
   font-size: 1.4rem;
   line-height: 16px;
   margin-top: 2px;
   opacity: 0.7;
}

#search-btn {
   position: absolute;
   right: 0;
   top: 16px;
   padding: 2px;
   z-index: 999;
   height: 42px;
   width: 45px;
   background-color: rgba(255, 255, 255, 0.2);
   border: none;
}

#dashboard-content {
   text-align: center;
   height: 100vh;
}

#date-desc,
#location-desc {
   color: #ffffff;
   font-size: 1.6rem;
   font-weight: 500;
   line-height: 19px;
   margin-bottom: 15px;
}

#date-desc>img {
   top: -3px;
   position: relative;
   margin-right: 10px;
}

#location-desc>img {
   top: -3px;
   position: relative;
   margin-left: 5px;
   margin-right: 15px;
}

#location-detail {
   opacity: 0.7;
   color: #ffffff;
   font-size: 1.4rem;
   line-height: 20px;
   margin-left: 35px;
}

.centered {
   position: fixed;
   top: 45%;
   left: 50%;
   transform: translate(-50%, -50%);
}

.max-desc {
   width: 80px;
   float: left;
   margin-right: 28px;
}

.temp-max-min {
   margin-top: 40px
}

#dashboard-content {
   background-color: #F7F7F7;
}

.custom-card {
   background-color: #FFFFFF !important;
   border: 0 !important;
   margin-top: 16px !important;
   margin-bottom: 20px !important;
}

.custom-content-card {
   background-color: #FFFFFF !important;
   border: 0 !important;
   margin-top: 16px !important;
   margin-bottom: 0px !important;
}

.header-card {
   height: 50vh;
}

.content-card {
   height: 43vh;
}

.card-divider {
   margin-top: 0;
}

.content-header {
   color: #8786A4;
   font-size: 1.4rem;
   line-height: 16px;
   font-weight: 500;
   padding: 15px 10px 5px 15px;
}

.highlights-item {
   min-height: 37vh;
   max-height: 38vh;
   background-color: #FFFFFF;
}


.card-heading {
   color: rgb(33, 34, 68);
   font-size: 1.8rem;
   font-weight: 500;
   line-height: 21px;
   text-align: center;
}

.card-sub-heading {
   color: #73748C;
   font-size: 1.6rem;
   line-height: 19px;
}

.card-value {
   color: #000000;
   font-size: 1.8rem;
   line-height: 21px;
}

span text {
   font-weight: 500 !important;
}

hr {
   padding-top: 1.5px;
   padding-bottom: 1px;
   margin-bottom: 0;
   margin-top: 0;
   line-height: 0.5px;
}

@media only screen and (min-width: 768px) {
   #sidebar {
       height: 100vh;
   }

   #info {
       position: fixed;
       bottom: 50px;
       width: 100%;
       padding-left: 15px;
   }

   .wrapper-right {
       margin-top: 80px;
   }
}

@media only screen and (min-width:1440px) {
   #sidebar {
       width: 350px;
       max-width: 350px;
       flex: auto;
   }

   #dashboard-content {
       width: calc(100% — 350px);
       max-width: calc(100% — 350px);
       flex: auto;
   }
}
src/assets/

In this directory, download and save the .svg files mentioned below:

src/components/Content.vue

This is what we call a dumb component — a placeholder, that is there just to maintain the hierarchy, and essentially passes on data to its child components.

Remember that there is no technical bar for writing all our code in the App.vue file, but we take the approach of splitting up the code by nesting the components for two reasons:

  • To write clean code, which aids readability and maintainability;
  • To replicate the same structure that we will see on screen, i.e., the hierarchy.

Before we nest the component defined in Content.vue within the root component App.vue, let’s write some toy (but educational) code for Content.vue:

<template>
  

This child components of Content.vue are:

  • {{ child }}
</template> export default { data () { return { childComponents: ['TempVarChart.vue', 'Highlights.vue'] } }, methods: { }, computed: { }, } <style> </style>

In the code, carefully observe and understand the following:

  • Within the tag (where we obviously write some JavaScript code), we define an object that is exported (made available to other files) by default. This object contains a function data(), that returns an array object called childComponents, with its elements being names of the component files that should be nested further.
  • Within the tag (where we write some HTML template), the thing of interest is the
      .

      • Within the unordered list, each list item should be names of the intended child components, as defined in the array object childComponents. Moreover, the list should automatically extend till the last element of the array. Seems like we should write a for-loop, isn’t it? We do that by using the v-for directive provided by Vue.js. The v-for directive:
        • Acts as an attribute of the
        • tag, iterates through the array, renders the names of the child components where the iterator is mentioned within the {{ }} brackets (where we write the text for the list items).

    The code and the explanation above forms the basis of your subsequent understanding of how the script and the template are interrelated, and how we can use the directives provided by Vue.js.

    We have learnt quite a lot, but even after all these, we have one thing left to learn about seamlessly connecting components in hierarchy — passing data down from the parent component to its children. For now, we need to learn how to pass some data from src/App.vue to src/components/Content.vue, so that we can use the same techniques for the rest of the component nesting in this project.

    Data trickling down from the parent to the child components might sound simple, but the devil is in the details! As briefly explained below, there are multiple steps involved in making it work:

    • Defining and the data
      For now, we want some static data to play with — an object containing hard-coded values about different aspects of weather will just be fine! We create an object called weather_data and return it from the data() function of App.vue. The weather_data object is given in the snippet below:
    weather_data: {
            location: "California",
            temperature: {
              current: "35 C",
            },
            highlights: {
              uvindex: "3",
              windstatus: {
                speed: "20 km/h",
                direction: "N-E",
              },
              visibility: "12 km",
            },
          },
    
    • Passing the data from the parent
      To pass the data, we need a destination where we want to send the data! In this case, the destination is the Content.vue component, and the way to implement it is to:

      • Assign the weather_data object to a custom attribute of the tag
      • Bind the attribute with the data using the v-bind: directive provided by Vue.js, which makes the attribute value dynamic (responsive to changes made in the original data).
        
        

    Defining and passing the data is handled at the source side of the handshake, which in our case is the App.vue file.

    The code for the App.vue file, at its current status, is given below:

    
      

    This component’s code is in {{ filename }}

    import Content from './components/Content.vue' export default { name: 'app', components: { 'Content': Content }, data () { return { filename: 'App.vue', weather_data: { location: "California", temperature: { current: "35 C", }, highlights: { uvindex: "3", windstatus: { speed: "20 km/h", direction: "N-E", }, visibility: "12 km", }, }, } }, methods: { }, computed: { }, } <style> </style>

    A screenshot of the browser with the message “This component’s code is in App.vue. This child components of Content.vue are: TempVarChart.vue, Highlights.vue”
    (Large preview)

    With the data defined and passed from the source (parent component), it is now the child’s responsibility to receive the data and render it appropriately, as explained in the next two steps.

    • Receiving the data by the child
      The child component, in this case Content.vue, must receive the weather_data object send to it by the parent component App.vue. Vue.js provides a mechanism to do so — all you need is an array object called props, defined in the default object exported by Content.vue. Each element of the array props is a name of the data objects it wants to receive from its parent. For now, the only data object that it is supposed to receive is weather_data from App.vue. Thus, the props array looks like:
    <template>
      // HTML template code here
    </template>
    
    
    export default {
      props: ["weather_data"],
      data () {
        return {
          // data here
        }
      },
    }
    
    
    <style>
      // component specific CSS here
    </style>
    
    • Rendering the data in the page
      Now that we have ensured receiving the data, the last task we need to complete is to render the data. For this example, we will directly dump the received data on the web page, just to illustrate the technique. However, in real applications (like the one we are about to build), data normally goes through lots of processing, and only the relevant parts of it are displayed in ways that suits the purpose. For example, in this project we will eventually get raw data from the weather API, clean and format it, feed the data to the data structures necessary for the charts, and then visualize it. Anyway, to display the raw data dump, we will just use the {{ }} brackets that Vue understands, as shown in the snippet below:
    <template>
      
    // other template code here {{ weather_data }}
    </template>

    It’s now time to assimilate all the bits and pieces. The code for Content.vue — at its current status — is given below:

    <template>
      

    This child components of Content.vue are:

    • {{ child }}
    {{ weather_data }}
    </template> export default { props: ["weather_data"], data () { return { childComponents: ['TempVarChart.vue', 'Highlights.vue'] } }, methods: { }, computed: { }, } <style> #pagecontent { border: 1px solid black; padding: 2px; } </style>

    A screenshot of the browser with the result of the code provided
    (Large preview)

    After making the changes discussed above, refresh the webpage on the browser and see how it looks. Take a moment to appreciate the complexity that Vue handles — if you modify the weather_data object in App.vue, it gets silently conveyed to Content.vue, and eventually to the browser displaying the webpage! Try by changing the value for the key location.

    Although we have learned about props and data binding using static data, we will be using dynamic data collected using web APIs in the application, and will change the code accordingly.

    Summary

    Before we move on to the rest of the .vue files, let’s summarize what we have learnt while we wrote the code for App.vue and components/Content.vue:

    • The App.vue file is what we call the root component — the one that sits at the top of the component hierarchy. The rest of the .vue files represents components that are its direct child, grandchild, and so on.
    • The Content.vue file is a dummy component — its responsibility is to pass on the data to levels below and maintain the structural hierarchy, so that our code remains consistent with the philosophy “*what we see is what we implement*”.
    • The parent-child relationship of component does not happen out of thin air — you must register a component (either globally or locally, depending on the intended usage of the component), and then nest it using custom HTML tags (whose spellings are the exact same as that of the names with which the components has been registered).
    • Once registered and nested, data is passed on from parent to child components, and the flow is never reverse (bad things will happen if the project architecture allows backflow). The parent component is the relative source of the data, and it passes down relevant data to its children using the v-bind directive for the attributes of the custom HTML elements. The child receives the data intended for it using props, and then decides on its own what to do with the data.

    For the rest of the components, we will not indulge in detailed explanation — we will just write the code based on the learnings from the above summary. The code will be self-evident, and if you get confused about the hierarchy, refer to the diagram below:


    A diagram explaining the hierarchy of the code
    (Large preview)

    The diagram says that TempVarChart.vue and Highlights.vue are the direct child of Content.vue. Thus, it might be a good idea to prepare Content.vue for sending data to those components, which we do using the code below:

    <template>
      

    This child components of Content.vue are:

    • {{ child }}
    {{ weather_data }}
    </template> import TempVarChart from './TempVarChart.vue' import Highlights from './Highlights.vue' export default { props: ["weather_data"], components: { 'temp-var-chart': TempVarChart, 'today-highlights': Highlights }, data () { return { childComponents: ['TempVarChart.vue', 'Highlights.vue'], tempVar: this.weather_data.temperature, highlights: this.weather_data.highlights, } }, methods: { }, computed: { }, } <style> </style>

    Once you save this code, you will get errors — don’t worry, it is expected. It will be fixed once you have the rest of the component files ready. If it bothers you not to be able to see the output, comment out the lines containing the custom element tags <temp-var-chart> and <today-highlights>.

    For this section, this is the final code of Content.vue. For the rest of this section, we will reference to this code, and not the previous ones that we wrote for learning.

    src/components/TempVarChart.vue

    With its parent component Content.vue passing on the data, TempVarChart.vue must be set up to receive and render the data, as shown in the code below:

    <template>
      

    Temperature Information:

    {{ tempVar }}
    </template> export default { props: ["tempVar"], data () { return { } }, methods: { }, computed: { }, } <style> </style>
    src/components/Highlights.vue

    This component will also receive data from App.vue — its parent component. After that, it should be linked with its child components, and relevant data should be passed on to them.

    Let’s first see the code for receiving data from the parent:

    <template>
      

    Weather Highlights:

    {{ highlights }}
    </template> export default { props: ["highlights"], data () { return { } }, methods: { }, computed: { }, } <style> </style>

    At this point, the web page looks like the image below:


    Result of the code displayed in the browser
    (Large preview)

    Now we need to modify the code of Highlights.vue to register and nest its child components, followed by passing the data to children. The code for it is as follows:

    <template>
      

    Weather Highlights:

    {{ highlights }}
    </template> import UVIndex from './UVIndex.vue'; import Visibility from './Visibility.vue'; import WindStatus from './WindStatus.vue'; export default { props: ["highlights"], components: { 'uv-index': UVIndex, 'visibility': Visibility, 'wind-status': WindStatus, }, data () { return { } }, methods: { }, computed: { }, } <style> </style>

    Once you save the code and see the web page, you are expected to see errors in the Developer Console tool provided by the browser; they appear because although Highlights.vue is sending data, nobody is receiving them. We are yet to write the code for the children of Highlights.vue.

    Observe that we have not done much of the data processing, i.e, we have not extracted the individual factors of weather data that goes under the Highlights section of the dashboard. We could have done that in the data() function, but we preferred to keep Highlights.vue a dumb component that just passes on the entire data dump it receives to each of the children, who then own their own extracts what is necessary for them. However, we encourage you to try out extracting data in the Highlights.vue, and send relevant data down to each child component — it’s a good practice exercise nonetheless!

    src/components/UVIndex.vue

    The code for this component receives the data dump of highlights from Highlights.vue, extracts the data for UV Index, and renders it on the page.

    <template>
      

    UV Index: {{ uvindex }}

    </template> export default { props: ["highlights"], data () { return { uvindex: this.highlights.uvindex } }, methods: { }, computed: { }, } <style> </style>
    src/components/Visibility.vue

    The code for this component receives the data dump of highlights from Highlights.vue, extracts the data for Visibility, and renders it on the page.

    <template>
      

    Visibility: {{ visibility }}

    </template> export default { props: ["highlights"], data () { return { visibility: this.highlights.visibility, } }, methods: { }, computed: { }, } <style> </style>
    src/components/WindStatus.vue

    The code for this component receives the data dump of highlights from Highlights.vue, extracts the data for Wind Status (speed and direction), and renders it on the page.

    <template>
      

    Wind Status:

    Speed — {{ speed }}; Direction — {{ direction }}

    </template> export default { props: ["highlights"], data () { return { speed: this.highlights.windstatus.speed, direction: this.highlights.windstatus.direction } }, methods: { }, computed: { }, } <style> </style>

    After adding the code for all the components, take a look at the web page on the browser.


    Result of the code displayed in the browser
    (Large preview)

    Not to dishearten, but all these toiling was just to link the components in hierarchy, and test out whether data flow is happening between them or not! In the next section, we will throw away most of the code we have written so far, and add a lot more pertaining to the actual project. However, we will certainly retain the structure and nesting of the components; the learnings from this section will allow us to build a decent dashboard with Vue.js.

    4. Data Acquisition And Processing

    Remember the weather_data object in App.vue? It had some hard-coded data that we used to test whether all the components are working correctly, and also to help you learn some basic aspects of Vue application without getting bogged down in the details of real-world data. However, it’s now time that we shed our shell, and step out into the real world, where data from the API will dominate most of our code.

    Preparing Child Components To Receive And Process Real Data

    In this section, you will get code dump for all the components except App.vue. The code will handle receiving real data from App.vue (unlike the code we wrote in the previous section to receive and render dummy data).

    We strongly encourage to read the code of each component carefully, so that you form an idea of what data each of those components are expecting, and will eventually use in visualization.

    Some of the code, and the overall structure, will be similar to the ones you have seen in the previous structure — so you will not face something drastically different. However, the devil is in the details! So examine the code carefully, and when you have understood them reasonably well, copy the code to the respective component files in your project.

    Note: All the components in this section are in the src/components/ directory. So each time, the path will not be mentioned — only the .vue file name will be mentioned to identify the component.

    Content.vue
    <template>
     
    </template> import TempVarChart from './TempVarChart.vue'; import Highlights from './Highlights.vue'; export default { props: ['highlights', 'tempVar'], components: { 'temp-var-chart': TempVarChart, 'today-highlights': Highlights }, }

    The following changes have been made from the previous code:

    • In the <template>, text and data within {{ }} has been removed, since we are now just receiving data and passing down to the children, with no rendering specific this component.
    • In the export default {}:
      • The props have been changed to match the data objects that will be send by the parent: App.vue. The reason for changing the props is that App.vue itself will display some of the data it acquires from the weather API and other online resources, based on the search query of the user, and pass on the rest of the data. In the dummy code we wrote earlier, App.vue was passing on the entire dummy data dump, without any discrimination, and the props of Content.vue was set up accordingly.
      • The data() function now returns nothing, as we are not doing any data manipulation in this component.
    TempVarChart.vue

    This component is supposed to receive detailed temperature projections for the rest of the current day, and eventually display them using FusionCharts. But for the time being, we will display them only as text on the webpage.

    <template>
      
    {{ tempVar.tempToday }}
    </template> export default { props: ["tempVar"], components: {}, data() { return { }; }, methods: { }, }; <style> </style>
    Highlights.vue
    <template>
      
    </template> import UVIndex from './UVIndex.vue'; import Visibility from './Visibility.vue'; import WindStatus from './WindStatus.vue'; export default { props: ["highlights"], components: { 'uv-index': UVIndex, 'visibility': Visibility, 'wind-status': WindStatus, }, data () { return { } }, methods: { }, computed: { }, } <style> </style>

    The changes made from the previous code are:

    • In the <template>, the text and the data within {{ }} has been removed, because this is a dumb component, just like Content.vue, whose only job is to pass on the data to children while maintaining the structural hierarchy. Remember that dumb components like Highlights.vue and Content.vue exists to maintain the parity between the visual structure of the dashboard, and the code we write.
    UVIndex.vue

    The changes made to the previous code are as follows:

    • In the <template> and <style>, the div id has been changed to uvIndex, which is more readable.
    • In the export default {}, the data() function now returns a string object uvIndex, whose value is extracted from the highlights object received by the component using props. This uvIndex is now temporarily used to display the value as text within the <template>. Later on, we will plug in this value to the data structure suitable for rendering a chart.
    Visibility.vue
    <template>
      

    Visibility: {{ visibility }}

    </template> export default { props: ["highlights"], data () { return { visibility: this.highlights.visibility.toString() } }, methods: { }, computed: { }, } <style> </style>

    The only change made in this file (with respect to its previous code) is that the definition of the visibility object returned by the data() function now contains toString() at its end, since the value received from the parent will be a floating point number, which needs to be converted into string.

    WindStatus.vue
    <template>
      

    Wind Speed — {{ windSpeed }}

    Wind Direction — {{ derivedWindDirection }}, or {{ windDirection }} degree clockwise with respect to true N as 0 degree.

    </template> export default { props: ["highlights"], data () { return { windSpeed: this.highlights.windStatus.windSpeed, derivedWindDirection: this.highlights.windStatus.derivedWindDirection, windDirection: this.highlights.windStatus.windDirection } }, methods: { }, computed: { }, } <style> </style>

    The changes made to the previous code are as follows:

    • Throughout the file, windstatus has been renamed as windStatus, to promote readability and also to be in sync with the the highlights object that App.vue provides with actual data.
    • Similar naming changes have been made for the speed and direction — the new ones are windSpeed and windDirection.
    • A new object derivedWindDirection has come into play (also provided by App.vue in the highlights bundle).

    For now, the received data is rendered as text; later, it will be plugged in to the data structure necessary for visualization.

    Testing With Dummy Data

    Resorting to dummy data repeatedly might be a bit frustrating for you, but there are some good reasons behind it:

    • We have made a lot of changes to the code of each component, and it’s a good idea to test whether those changes are breaking the code. In other words, we should check that whether the data flow is intact, now that we are about to move to more complex parts of the project.
    • The real data from the online weather API will need lot of massaging, and it might be overwhelming for you to juggle between the code for data acquisition and processing, and the code for smooth data flow down the components. The idea is to keep the quantum of complexity under control, so that we have a better understanding of the errors we might face.

    In this section, what we do is essentially hardcode some json data in the App.vue, which will obviously be replaced with live data in the near future. There are a lot of similarity between the dummy json structure, and the json structure we will use for the actual data. So it also provides you a rough idea of what to expect from the real data, once we encounter it.

    However, we admit that this is far from the ideal approach one might adopt while building such a project from scratch. In the real world, you will often start with the real data source, play around with it a bit to understand what can and should be done to tame it, and then think about the appropriate json data structure to capture the relevant information. We intentionally shielded you from all those dirty work, since it takes you farther from the objective — learning how to use Vue.js and FusionCharts to build a dashboard.

    Let’s now jump into the new code for App.vue:

    <template>
      
    </template> import Content from './components/Content.vue' export default { name: 'app', components: { 'dashboard-content': Content }, data () { return { tempVar: { tempToday: [ {hour: '11.00 AM', temp: '35'}, {hour: '12.00 PM', temp: '36'}, {hour: '1.00 PM', temp: '37'}, {hour: '2.00 PM', temp: '38'}, {hour: '3.00 PM', temp: '36'}, {hour: '4.00 PM', temp: '35'}, ], }, highlights: { uvIndex: 4, visibility: 10, windStatus: { windSpeed: '30 km/h', windDirection: '30', derivedWindDirection: 'NNE', }, }, } }, methods: { }, computed: { }, } <style> </style>

    The changes made to the code with respect to its previous version are as follows:

    • The name of the child component has been changed to dashboard-content, and accordingly the custom HTML element in the <template> has been revised. Note that now we have two attributes — highlights and tempVar — instead of a single attribute that we used earlier with the custom element. Accordingly, the data associated with those attributes have also changed. What’s interesting here is that we can use the v-bind: directive, or its shorthand : (as we have done here), with multiple attributes of a custom HTML element!
    • The data() function now returns the filename object (that existed earlier), along with two new objects (instead of the old weather_data): tempVar and highlights. The structure of the json is appropriate for the code we have written in the child components, so that they can extract the data pieces they need from the dumps. The structures are quite self-explanatory, and you can expect them to be quite similar when we deal with live data. However, the significant change that you will encounter is the absence of hardcoding (obvious, isn’t it) — we will leave the values blank as the default state, and write code to dynamically update them based on the values we will receive from the weather API.

    You have written a lot of code in this section, without seeing the actual output. Before you proceed further, take a look at the browser (restart the server with npm run dev, if necessary), and bask in the glory of your achievement. The web page that you should see at this point looks like the image below:


    Result of the code displayed in the browser
    (Large preview)

    Code For Data Acquisition And Processing

    This section is going to be the meat of the project, with all the code to be written in App.vue for the following:

    • Location input from the user — an input box and a call-to-action button is sufficient;
    • Utility functions for various tasks; these functions will be called later in various parts of the component code;
    • Getting detailed geolocation data from Google Maps API for JavaScript;
    • Getting detailed weather data from the Dark Sky API;
    • Formatting and processing the geolocation and weather data, which will be passed on to the child components.

    The subsections that follows illustrates how we can implement the tasks laid out for us in the above points. With some exceptions, most of them will follow the sequence.

    Input From The User

    It’s quite obvious that the action starts when the user provides the name of the place for which the weather data needs to be displayed. For this to happen, we need to implement the following:

    • An input box for entering the location;
    • A submit button that tells our application that the user has entered the location and it’s time to do the rest. We will also implement the behavior when processing starts upon hitting Enter.

    The code we show below will be restricted to the HTML template part of App.vue. We will just mention the name of the method associated with the click events, and define them later in the methods object of the in App.vue.

    
    

    Placing the above snippet in the right place is trivial — we leave it to you. However, the interesting parts of the snippet are:

    • @keyup.enter="organizeAllDetails"
    • @click="organizeAllDetails"

    As you know from the earlier sections, @ is Vue’s shorthand for the directive v-on:, which is associated with some event. The new thing is “organizeAllDetails” — it’s nothing but the method that will fire once the events (pressing Enter or clicking the button) happens. We are yet to define method, and the puzzle will be complete by the end of this section.

    Text Information Display Controlled By App.vue

    Once the user input triggers the action and lots of data is acquired from the APIs, we encounter the inevitable question — “What to do with all these data?”. Obviously some data massaging is required, but that does not answer our question fully! We need to decide what’s the end use of the data, or more directly, which are the entities that receives different chunks of the acquired and processed data?

    The child components of App.vue, based on their hierarchy and purpose, are the frontline contenders for the bulk of the data. However, we will also have some data that does not belong to any of those child components, yet are quite informative and makes the dashboard complete. We can make good use of them if we display them as text information directly controlled by App.vue, while the rest of the data are passed on to the child for getting displayed as pretty charts ultimately.

    With this context in mind, let’s focus on the code for setting the stage of using text data. It’s simple HTML template at this point, on which the data will eventually come and sit.

    {{ currentWeather.temp }} °C
    {{ currentWeather.summary }}
    {{ currentWeather.todayHighLow.todayTempHigh }} °C
    at {{ currentWeather.todayHighLow.todayTempHighTime }}
    {{ currentWeather.todayHighLow.todayTempLow }} °C
    at {{ currentWeather.todayHighLow.todayTempLowTime }}
    {{ currentWeather.time }}
    {{ currentWeather.full_location }}
    Lat: {{ currentWeather.formatted_lat }}
    Long: {{ currentWeather.formatted_long }}

    In the above snippet, you should understand the following:

    • The stuff inside {{ }} — they are Vue’s way of inserting dynamic data in the HTML template, before it renders in the browser. You have encountered them before, and there is nothing new or surprising. Just keep in mind that these data objects stems from the data() method in the export default() object of App.vue. They have default values that we will set according to our requirements, and then write certain methods to populate the objects with real API data.

    Don’t worry for not seeing the changes on the browser — the data is not defined yet, and it’s natural for Vue to not render things that it does not know. However, once the data is set (and for now, you can even check by hard-coding the data), the text data will be controlled by App.vue.

    The data() Method

    The data() method is a special construct in the .vue files — it contains and returns data objects that are so crucial for the application. Recollect the generic structure of the part in any .vue file — it roughly contains the following:

    
    // import statements here
    
    export default {
        // name, components, props, etc.
    
        data() {
            return {
                // the data that is so crucial for the application is defined here.
                // the data objects will have certain default values chosen by us.
                // The methods that we define below will manipulate the data.
                // Since the data is bounded to various attributes and directives, they
                // will update as and when the values of the data objects change.
            }
        },
    
        methods: {
            // methods (objects whose values are functions) here.
            // bulk of dynamic stuff (the black magic part) is controlled from here.
        },
    
        computed: {
            // computed properties here
        },
    
        // other objects, as necessary
    
    }
    
    

    So far, you have encountered the names of some of the data objects, but are a lot more. Most of them are relevant for the child components, each of which handles a different aspect of the weather information dump. Given below is the entire data() method that we will need for this project — you will have a fair idea about what data we are expecting from the APIs, and how we are disseminating the data, based on the nomenclature of the objects.

    data() {
       return {
         weatherDetails: false,
         location: '', // raw location from input
         lat: '', // raw latitude from google maps api response
         long: '', // raw longitude from google maps api response
         completeWeatherApi: '', // weather api string with lat and long
         rawWeatherData: '', // raw response from weather api
         currentWeather: {
           full_location: '', // for full address
           formatted_lat: '', // for N/S
           formatted_long: '', // for E/W
           time: '',
           temp: '',
           todayHighLow: {
             todayTempHigh: '',
             todayTempHighTime: '',
             todayTempLow: '',
             todayTempLowTime: ''
           },
           summary: '',
           possibility: ''
         },
         tempVar: {
           tempToday: [
             // gets added dynamically by this.getSetHourlyTempInfoToday()
           ],
         },
         highlights: {
           uvIndex: '',
           visibility: '',
           windStatus: {
             windSpeed: '',
             windDirection: '',
             derivedWindDirection: ''
           },
         }
       };
     },
    

    As you can see, in most cases the default value is empty, because that will suffice at this point. Methods will be written for manipulating the data and filling it up with appropriate values, before it is rendered or passed on to the child components.

    Methods in App.vue

    For .vue files, the methods are generally written as values of keys nested in the methods { } object. Their primary role is to manipulate the data objects of the component. We will write the methods in App.vue keeping the same philosophy in mind. However, based on their purpose, we can categorize the methods of App.vue into the following:

    • Utility methods
    • Action/Event oriented methods
    • Data acquisition methods
    • Data processing methods
    • High level glue methods

    It’s important that you understand this — we are presenting the methods to you on a platter because we have already figured out how the APIs work, what data they give, and how we should use the data in our project. It’s not that we pulled the methods out of thin air, and wrote some arcane code to deal with the data. For the purpose of learning, it’s a good exercise to diligently read and understand the code for the methods and data. However, when faced with a new project that you have to build from scratch, you must do all the dirty work yourself, and that means experimenting a lot with the APIs — their programmatic access and their data structure, before glueing them seamlessly with the data structure that your project demands. You will not have any hand holding, and there will be frustrating moments, but that’s all part of maturing as a developer.

    In the following subsections, we will explain each of the method types, and also show the implementation of the methods belonging to that category. The method names are quite self-explanatory about their purpose, and so is their implementation, which we believe you will find to be easy enough to follow. However, before that, recollect the general scheme of writing methods in .vue files:

    
    // import statements here
    
    export default {
        // name, components, props, etc.
    
        data() {
            return {
                // the data that is so crucial for the application is defined here.
            }
        },
    
        methods: {
            // methods (objects whose values are functions) here.
            // bulk of dynamic stuff (the black magic part) is controlled from here.
            method_1: function(arg_1) {
            
            },
            method_2: function(arg_1, arg_2) {
            
            },
            method_3: function(arg_1) {
            
            },
            …….
        },
    
        computed: {
            // computed properties here
        },
    
        // other objects, as necessary
    
    }
    
    

    Utility Methods

    The utility methods, as the name suggests, are methods written primarily for the purpose of modularizing repetitive code used for fringe tasks. They are called by other methods when necessary. Given below are the utility methods for App.vue:

    convertToTitleCase: function(str) {
         str = str.toLowerCase().split(' ');
         for (var i = 0; i < str.length; i++) {
           str[i] = str[i].charAt(0).toUpperCase() + str[i].slice(1);
         }
         return str.join(' ');
       },
    
    // To format the “possibility” (of weather) string obtained from the weather API
    formatPossibility: function(str) {
         str = str.toLowerCase().split('-');
         for (var i = 0; i < str.length; i++) {
           str[i] = str[i].charAt(0).toUpperCase() + str[i].slice(1);
         }
         return str.join(' ');
       },
    
    // To convert Unix timestamps according to our convenience
    unixToHuman: function(timezone, timestamp) {
         /* READ THIS BEFORE JUDGING & DEBUGGING
         For any location beyond the arctic circle and the
         antarctic circle, the goddamn weather api does not return certain
         keys/values in each of this.rawWeatherData.daily.data[some_array_index].
         Due to this, console throws up an error.
         The code is correct, the problem is with the API.
         May be later on I will add some padding to tackle missing values.
         */
         var moment = require('moment-timezone'); // for handling date & time
         var decipher = new Date(timestamp * 1000);
         var human = moment(decipher)
           .tz(timezone)
           .format('llll');
         var timeArray = human.split(' ');
         var timeNumeral = timeArray[4];
         var timeSuffix = timeArray[5];
         var justTime = timeNumeral + ' ' + timeSuffix;
         var monthDateArray = human.split(',');
         var monthDate = monthDateArray[1].trim();
         return {
           fullTime: human,
           onlyTime: justTime,
           onlyMonthDate: monthDate
         };
       },
    
    // To convert temperature from fahrenheit to celcius
    fahToCel: function(tempInFahrenheit) {
         var tempInCelcius = Math.round((5 / 9) * (tempInFahrenheit — 32));
         return tempInCelcius;
       },
    
    // To convert the air pressure reading from millibar to kilopascal
    milibarToKiloPascal: function(pressureInMilibar) {
         var pressureInKPA = pressureInMilibar * 0.1;
         return Math.round(pressureInKPA);
       },
    
    // To convert distance readings from miles to kilometers
    mileToKilometer: function(miles) {
         var kilometer = miles * 1.60934;
         return Math.round(kilometer);
       },
    
    // To format the wind direction based on the angle
    deriveWindDir: function(windDir) {
         var wind_directions_array = [
           { minVal: 0, maxVal: 30, direction: 'N' },
           { minVal: 31, maxVal: 45, direction: 'NNE' },
           { minVal: 46, maxVal: 75, direction: 'NE' },
           { minVal: 76, maxVal: 90, direction: 'ENE' },
           { minVal: 91, maxVal: 120, direction: 'E' },
           { minVal: 121, maxVal: 135, direction: 'ESE' },
           { minVal: 136, maxVal: 165, direction: 'SE' },
           { minVal: 166, maxVal: 180, direction: 'SSE' },
           { minVal: 181, maxVal: 210, direction: 'S' },
           { minVal: 211, maxVal: 225, direction: 'SSW' },
           { minVal: 226, maxVal: 255, direction: 'SW' },
           { minVal: 256, maxVal: 270, direction: 'WSW' },
           { minVal: 271, maxVal: 300, direction: 'W' },
           { minVal: 301, maxVal: 315, direction: 'WNW' },
           { minVal: 316, maxVal: 345, direction: 'NW' },
           { minVal: 346, maxVal: 360, direction: 'NNW' }
         ];
         var wind_direction = '';
         for (var i = 0; i = wind_directions_array[i].minVal &&
             windDir <= wind_directions_array[i].maxVal
           ) {
             wind_direction = wind_directions_array[i].direction;
           }
         }
         return wind_direction;
       },
    

    Although we haven’t implemented it, you can take out the utility methods from the .vue file, and put it in a separate JavaScript file. All you need to do is import the .js file at the start of the script part in the .vue file, and you should be good to go. Such approach works really well and keeps the code clean, especially in big applications where you might use lots of methods that are better grouped together based on their purpose. You can apply this approach to all of the method groups listed in this article, and see the effect itself. However, we suggest you do that exercise once you have followed the course presented here, so that you have the big picture understanding of all the parts working in complete sync, and also have a working piece of software which you can refer to, once something breaks while experimenting.

    Action/Event Oriented Methods

    These methods are generally executed when we need to take an action corresponding to an event. Depending on the case, the event might be triggered from an user interaction, or programmatically. In the App.vue file, these methods sit below the utility methods.

    makeInputEmpty: function() {
         this.$refs.input.value = '';
       },
    
    makeTempVarTodayEmpty: function() {
         this.tempVar.tempToday = [];
       },
    
    detectEnterKeyPress: function() {
         var input = this.$refs.input;
         input.addEventListener('keyup', function(event) {
           event.preventDefault();
           var enterKeyCode = 13;
           if (event.keyCode === enterKeyCode) {
             this.setHitEnterKeyTrue();
           }
         });
       },
    
    locationEntered: function() {
         var input = this.$refs.input;
         if (input.value === '') {
           this.location = "New York";
         } else {
           this.location = this.convertToTitleCase(input.value);
         }
         this.makeInputEmpty();
         this.makeTempVarTodayEmpty();
       },
    

    One interesting thing in some of the above code snippets is the use of $ref. In simple terms, it’s Vue’s way of associating the code statement containing it, to the HTML construct it is supposed to affect (for more information, read the official guide). For example, the methods makeInputEmpty() and detectEnterKeyPress() affects the input box, because in the HTML of the input box we have mentioned the value of the attribute ref as input.

    Data Acquisition Methods

    We are using the following two APIs in our project:

    • Google Maps Geocoder API
      This API is for getting the coordinates of the location that the user searches. You will need an API key for yourself, which you can get by following the documentation in the given link. For now, you can use the API key used by FusionCharts, but we request you not to abuse it and get a key of your own. We refer to the JavaScript API from the index.html of this project, and we shall use the constructors provided by it for our code in the App.vue file.
    • The Dark Sky Weather API
      This API is for getting the weather data corresponding to the coordinates. However, we won’t be using it directly; we will wrap it within an URL that redirects through one of the FusionCharts’s server. The reason is that if you send a GET request to the API from an entirely client-end application such as ours, it results in the frustrating CORS error (more information here and here).

    Important Note: Since we have used Google Maps and Dark Sky APIs, Both these APIs have their own API keys which we have shared with you in this article. This will help you focus on client-side developments rather than the headache of backend implementation. However, we recommend you to create your own keys, because our APIs keys will come with limits and if these limits exceed you won’t be able to try the application by yourself.

    For Google Maps, go to this article to get your API key. For Dark Sky API, visit https://darksky.net/dev to create your API key and respective endpoints.

    With the context in mind, let’s see the implementation of the data acquisition methods for our project.

    getCoordinates: function() {
         this.locationEntered();
         var loc = this.location;
         var coords;
         var geocoder = new google.maps.Geocoder();
         return new Promise(function(resolve, reject) {
           geocoder.geocode({ address: loc }, function(results, status) {
             if (status == google.maps.GeocoderStatus.OK) {
               this.lat = results[0].geometry.location.lat();
               this.long = results[0].geometry.location.lng();
               this.full_location = results[0].formatted_address;
               coords = {
                 lat: this.lat,
                 long: this.long,
                 full_location: this.full_location
               };
               resolve(coords);
             } else {
               alert("Oops! Couldn't get data for the location");
             }
           });
         });
       },
    
    /*
    The coordinates that Google Maps Geocoder API returns are way too accurate
    for our requirements. We need to bring it into shape before passing the coordinates on 
    to the weather API. Although this is a data processing method in its own right, we can’t 
    help mentioning it right now, because the data acquisition method for the weather API has dependency on the output of this method. 
    */
    setFormatCoordinates: async function() {
         var coordinates = await this.getCoordinates();
         this.lat = coordinates.lat;
         this.long = coordinates.long;
         this.currentWeather.full_location = coordinates.full_location;
         // Remember to beautify lat for N/S
         if (coordinates.lat > 0) {
           this.currentWeather.formatted_lat =
             (Math.round(coordinates.lat * 10000) / 10000).toString() + '°N';
         } else if (coordinates.lat  0) {
           this.currentWeather.formatted_long =
             (Math.round(coordinates.long * 10000) / 10000).toString() + '°E';
         } else if (coordinates.long < 0) {
           this.currentWeather.formatted_long =
             (-1 * (Math.round(coordinates.long * 10000) / 10000)).toString() +
             '°W';
         } else {
           this.currentWeather.formatted_long = (
             Math.round(coordinates.long * 10000) / 10000
           ).toString();
         }
       },
    
    /*
    This method dynamically creates the the correct weather API query URL, based on the 
    formatted latitude and longitude. The complete URL is then fed to the method querying for 
    weather data.
    Notice that the base URL used in this method (without the coordinates) points towards a 
    FusionCharts server — we must redirect our GET request to the weather API through a server to avoid the CORS error.
    */
    fixWeatherApi: async function() {
         await this.setFormatCoordinates();
         var weatherApi =
           'https://csm.fusioncharts.com/files/assets/wb/wb-data.php?src=darksky&lat=' +
           this.lat +
           '&long=' +
           this.long;
         this.completeWeatherApi = weatherApi;
       },
    
    fetchWeatherData: async function() {
         await this.fixWeatherApi();
         var axios = require('axios'); // for handling weather api promise
         var weatherApiResponse = await axios.get(this.completeWeatherApi);
         if (weatherApiResponse.status === 200) {
           this.rawWeatherData = weatherApiResponse.data;
         } else {
           alert('Hmm... Seems like our weather experts are busy!');
         }
       },
    

    Through these methods, we have introduced the concept of async-await in our code. If you have been a JavaScript developer for some time now, you must be familiar with the callback hell, which is a direct consequence of the asynchronous way JavaScript is written. ES6 allows us to bypass the cumbersome nested callbacks, and our code becomes much cleaner if we write JavaScript in a synchronous way, using the async-await technique. However, there is a downside. It takes away the speed that asynchronous code gives us, especially for the portions of the code that deals with data being exchanged over the internet. Since this is not a mission-critical application with low latency requirements, and our primary aim is to learn stuff, the clean code is much more preferable over the slightly fast code.

    Data Processing Methods

    Now that we have the methods that will bring the data to us, we need to prepare the ground for properly receiving and processing the data. Safety nets must be cast, and there should be no spills — data is the new gold (OK, that might be an exaggeration in our context)! Enough with the fuss, let’s get to the point.

    Technically, the methods we implement in this section are aimed at getting the data out of the acquisition methods and the data objects in App.vue, and sometimes setting the data objects to certain values that suits the purpose.

    getTimezone: function() {
         return this.rawWeatherData.timezone;
       },
    
    getSetCurrentTime: function() {
         var currentTime = this.rawWeatherData.currently.time;
         var timezone = this.getTimezone();
         this.currentWeather.time = this.unixToHuman(
           timezone,
           currentTime
         ).fullTime;
       },
    
    getSetSummary: function() {
         var currentSummary = this.convertToTitleCase(
           this.rawWeatherData.currently.summary
         );
         if (currentSummary.includes(' And')) {
           currentSummary = currentSummary.replace(' And', ',');
         }
         this.currentWeather.summary = currentSummary;
       },
    
    getSetPossibility: function() {
         var possible = this.formatPossibility(this.rawWeatherData.daily.icon);
         if (possible.includes(' And')) {
           possible = possible.replace(' And', ',');
         }
         this.currentWeather.possibility = possible;
       },
    
    getSetCurrentTemp: function() {
         var currentTemp = this.rawWeatherData.currently.temperature;
         this.currentWeather.temp = this.fahToCel(currentTemp);
       },
    
    getTodayDetails: function() {
         return this.rawWeatherData.daily.data[0];
       },
    
    getSetTodayTempHighLowWithTime: function() {
         var timezone = this.getTimezone();
         var todayDetails = this.getTodayDetails();
         this.currentWeather.todayHighLow.todayTempHigh = this.fahToCel(
           todayDetails.temperatureMax
         );
         this.currentWeather.todayHighLow.todayTempHighTime = this.unixToHuman(
           timezone,
           todayDetails.temperatureMaxTime
         ).onlyTime;
         this.currentWeather.todayHighLow.todayTempLow = this.fahToCel(
           todayDetails.temperatureMin
         );
         this.currentWeather.todayHighLow.todayTempLowTime = this.unixToHuman(
           timezone,
           todayDetails.temperatureMinTime
         ).onlyTime;
       },
    
    getHourlyInfoToday: function() {
         return this.rawWeatherData.hourly.data;
       },
    
    getSetHourlyTempInfoToday: function() {
         var unixTime = this.rawWeatherData.currently.time;
         var timezone = this.getTimezone();
         var todayMonthDate = this.unixToHuman(timezone, unixTime).onlyMonthDate;
         var hourlyData = this.getHourlyInfoToday();
         for (var i = 0; i < hourlyData.length; i++) {
           var hourlyTimeAllTypes = this.unixToHuman(timezone, hourlyData[i].time);
           var hourlyOnlyTime = hourlyTimeAllTypes.onlyTime;
           var hourlyMonthDate = hourlyTimeAllTypes.onlyMonthDate;
           if (todayMonthDate === hourlyMonthDate) {
             var hourlyObject = { hour: '', temp: '' };
             hourlyObject.hour = hourlyOnlyTime;
             hourlyObject.temp = this.fahToCel(hourlyData[i].temperature).toString();
             this.tempVar.tempToday.push(hourlyObject);
             /*
             Since we are using array.push(), we are just adding elements
             at the end of the array. Thus, the array is not getting emptied
             first when a new location is entered.
             to solve this problem, a method this.makeTempVarTodayEmpty()
             has been created, and called from this.locationEntered().
             */
           }
         }
         /*
         To cover the edge case where the local time is between 10 — 12 PM,
         and therefore there are only two elements in the array
         this.tempVar.tempToday. We need to add the points for minimum temperature
         and maximum temperature so that the chart gets generated with atleast four points.
         */
         if (this.tempVar.tempToday.length <= 2) {
           var minTempObject = {
             hour: this.currentWeather.todayHighLow.todayTempHighTime,
             temp: this.currentWeather.todayHighLow.todayTempHigh
           };
           var maxTempObject = {
             hour: this.currentWeather.todayHighLow.todayTempLowTime,
             temp: this.currentWeather.todayHighLow.todayTempLow
           };
           /*
           Typically, lowest temp are at dawn,
           highest temp is around mid day.
           Thus we can safely arrange like min, max, temp after 10 PM.
           */
           // array.unshift() adds stuff at the beginning of the array.
           // the order will be: min, max, 10 PM, 11 PM.
           this.tempVar.tempToday.unshift(maxTempObject, minTempObject);
         }
       },
    
    getSetUVIndex: function() {
         var uvIndex = this.rawWeatherData.currently.uvIndex;
         this.highlights.uvIndex = uvIndex;
       },
    
    getSetVisibility: function() {
         var visibilityInMiles = this.rawWeatherData.currently.visibility;
         this.highlights.visibility = this.mileToKilometer(visibilityInMiles);
       },
    
    getSetWindStatus: function() {
         var windSpeedInMiles = this.rawWeatherData.currently.windSpeed;
         this.highlights.windStatus.windSpeed = this.mileToKilometer(
           windSpeedInMiles
         );
         var absoluteWindDir = this.rawWeatherData.currently.windBearing;
         this.highlights.windStatus.windDirection = absoluteWindDir;
         this.highlights.windStatus.derivedWindDirection = this.deriveWindDir(
           absoluteWindDir
         );
       },
    

    High Level Glue Methods

    With the utility, acquisition, and processing methods out of our way, we are now left with the task of orchestrating the entire thing. We do that by creating high level glue methods, that essentially calls the methods written above in a particular sequence, so that the entire operation is executed seamlessly.

    // Top level for info section
    // Data in this.currentWeather
    organizeCurrentWeatherInfo: function() {
         // data in this.currentWeather
         /*
         Coordinates and location is covered (get & set) in:
         — this.getCoordinates()
         — this.setFormatCoordinates()
         There are lots of async-await involved there.
         So it's better to keep them there.
         */
         this.getSetCurrentTime();
         this.getSetCurrentTemp();
         this.getSetTodayTempHighLowWithTime();
         this.getSetSummary();
         this.getSetPossibility();
       },
    
    // Top level for highlights
    organizeTodayHighlights: function() {
         // top level for highlights
         this.getSetUVIndex();
         this.getSetVisibility();
         this.getSetWindStatus();
       },
    
    // Top level organization and rendering
    organizeAllDetails: async function() {
         // top level organization
         await this.fetchWeatherData();
         this.organizeCurrentWeatherInfo();
         this.organizeTodayHighlights();
         this.getSetHourlyTempInfoToday();
       },
    
    mounted

    Vue provides instance lifecycle hooks — properties that are essentially methods, and gets triggered when the instance lifecycle reaches that stage. For example, created, mounted, beforeUpdate, etc., are all very useful lifecycle hooks that allows the programmer to control the instance at a much more granular level than that would have been possible otherwise.

    In the code of a Vue component, these lifecycle hooks are implemented just like you would for any other prop. For example:

    <template>
    </template>
    
    
    // import statements
    export default {
      data() {
        return {
        // data objects here
        }
      },
      methods: {
      // methods here
      },
      mounted: function(){
      // function body here
      },
    }
    
    
    <style>
    </style>
    

    Armed with this new understanding, take a look at the code below for the mounted prop of App.vue:

    mounted: async function() {
       this.location = "New York";
       await this.organizeAllDetails();    
     }
    

    Complete Code For App.vue

    We have covered a lot of ground in this section, and the last few sections have given you things in bits and pieces. However, it’s important that you have the complete, assembled code for App.vue (subject to further modifications in subsequent sections). Here it goes:

    <template>
     

    And finally, after so much of patience and hard work, you can see the data flow with its raw power! Visit the application on the browser, refresh the page, search for a location in the application’s search box, and hit Enter!


    The application as shown in the browser
    (Large preview)

    Now that we are done with all the heavy lifting, take a break. The subsequent sections focus on using the data to create charts that are beautiful and informative, followed by giving our ugly looking application a much deserved grooming session using CSS.

    5. Data Visualization With FusionCharts

    Fundamental Considerations For Charts

    For the end user, the essence of a dashboard is essentially this: a collection of the filtered and carefully curated information on a particular topic, conveyed through visual/graphic instruments for quick ingestion. They don’t care about the subtleties of your data pipeline engineering, or how aesthetic your code is — all they want is a high-level view in 3 seconds. Therefore, our crude application displaying text data means nothing to them, and it’s high time we implement mechanisms to wrap the data with charts.

    However, before we dive deep into the implementation of charts, let’s consider some pertinent questions and the possible answers from our perspective:

    • What type of charts are appropriate for the type of data we are dealing with?
      Well, the answer has two aspects — the context, and the purpose. By context, we mean the type of data, and it’s overall fit in the scheme of bigger things, bounded by the scope and audience of the project. And by purpose, we essentially mean “what we want to emphasize on?”. For example, we can represent today’s temperature at different times of the day by using a Column chart (vertical columns of equal width, with height proportional to the value the column represents). However, we are rarely interested in the individual values, but rather the overall variation and trend throughout the data. To suit the purpose, it is in our best interest to use a Line chart, and we will do that shortly.
    • What should be kept in mind before selecting a charting library?
      Since we are doing a project predominantly using JavaScript based technologies, it’s a no-brainer that any charting library that we choose for our project should be a native of the JavaScript world. With that basic premise in mind, we should consider the following before zeroing down on any particular library:

      • Support for the frameworks of our choice, which in this case, is Vue.js. A project can be developed in other popular JavaScript frameworks like React, or Angular — check the support of the charting library for your favorite framework. Also, support for other popular programming languages like Python, Java, C++, .Net (AS and VB), especially when the project involves some serious backend stuff, must be considered.
      • Availability of charts types and features, since it is almost impossible to know beforehand what will be final shape and purpose of the data in the project (especially if the requirements are regulated by your clients in a professional setting). In this case, you should cast your net wide, and choose a charting library that has the widest collection of charts. More importantly, to differentiate your project from others, the library should have have enough features in the form of configurable chart attributes, so that you can fine-tune and customize most aspects of the charts and the right level of granularity. Also, the default chart configurations should be sensible, and the library documentation has to be top notch, for reasons that’s obvious to professional developers.
      • Learning curve, support community, and balance must also be taken into consideration, especially when you are new to data visualization. On one end of the spectrum, you have proprietary tools like Tableau and Qlickview that costs a bomb, has smooth learning curve, but also comes with so many limitations in terms of customizability, integration, and deployment. On the other end there is d3.js — vast, free (open source), and customizable to its core, but you have to pay the price of a very steep learning curve to be able to do anything productive with the library.

    What you need is the sweet spot — the right balance between productivity, coverage, customizability, learning curve, and off course, cost. We nudge you to take a look at FusionCharts — the world’s most comprehensive and enterprise-ready JavaScript charting library for the web and mobile, that we will be using in this project for creating charts.

    Introduction To FusionCharts

    FusionCharts is used worldwide as the go-to JavaScript charting library by millions of developers spread across hundreds of countries around the globe. Technically, it’s as loaded and configurable as it can be, with support for integrating it with almost any popular tech stack used for web based projects. Using FusionCharts commercially requires a license, and you have to pay for the license depending on your use case (please contact sales if you are curious). However, we are using FusionCharts in this projects just to try out a few things, and therefore the non-licensed version (comes with a small watermark in your charts, and a few other restrictions). Using the non-licensed version is perfectly fine when you are trying out the charts and using it in your non-commercial or personal projects. If you have plans to deploy the application commercially, please ensure that you have a license from FusionCharts.

    Since this is a project involving Vue.js, we will need two modules that needs to be installed, if not done earlier:

    • The fusioncharts module, because it contains everything you will need for creating the charts
    • The vue-fusioncharts module, which is essentially a wrapper for fusioncharts, so that it can be used in a Vue.js project

    If you have not installed them earlier (as instructed in the third section), install them by executing the following command from the project’s root directory:

    npm install fusioncharts vue-fusioncharts --save
    

    Next, ensure that the src/main.js file of the project has the following code (also mentioned in section 3):

    import Vue from 'vue';
    import App from './App.vue';
    import FusionCharts from 'fusioncharts';
    import Charts from 'fusioncharts/fusioncharts.charts';
    import Widgets from 'fusioncharts/fusioncharts.widgets';
    import PowerCharts from 'fusioncharts/fusioncharts.powercharts';
    import FusionTheme from 'fusioncharts/themes/fusioncharts.theme.fusion';
    import VueFusionCharts from 'vue-fusioncharts';
    
    Charts(FusionCharts);
    PowerCharts(FusionCharts);
    Widgets(FusionCharts);
    FusionTheme(FusionCharts);
    
    Vue.use(VueFusionCharts, FusionCharts);
    
    new Vue({
     el: '#app',
     render: h => h(App)
    })
    

    Perhaps the most critical line in the above snippet is the following:

    Vue.use(VueFusionCharts, FusionCharts)
    

    It instructs Vue to use the vue-fusioncharts module for making sense of many things in the project that are apparently not explicitly defined by us, but is defined in the module itself. Also, this type of statement implies global declaration, by which we mean that anywhere Vue encounters anything strange in the code of our project (things that we have not explicitly defined about using FusionCharts), it will at least look once in the vue-fusioncharts and fusioncharts node modules for their definitions, before throwing up errors. If we would have used FusionCharts in an isolated part of our project (not using it in almost all of the component files), then perhaps local declaration would have made more sense.

    With that, you are all set to use FusionCharts in the project. We will be using quite a few variety of charts, the choice being dependent on the aspect of the weather data that we want to visualize. Also, we will get to see the interplay of data binding, custom components, and watchers in action.

    General Scheme For Using Fusioncharts In .vue Files

    In this section, we will explain the general idea of using FusionCharts for creating various charts in the .vue files. But first, let’s see the pseudocode that schematically illustrates the core idea.

    <template>
      
    </template> export default { props: ["data_prop_received_by_the_component"], components: {}, data() { return { data_object_1: "value_1", data_object_2: "value_2", … … }; }, methods: {}, computed: {}, watch: { data_prop_received_by_the_component: { handler: function() { // some code/logic, mainly data manipulation based }, deep: true } } }; <style> // component specific special CSS code here </style>

    Let’s understand different parts of the above pseudocode:

    • In the <template>, within the top level
      (that’s pretty much mandatory for the template HTML code of every component), we have the custom component . We have the definition of the component contained in the vue-fusioncharts Node module that we have installed for this project. Internally, vue-fusioncharts relies on the fusioncharts module, which have also been installed. We imported the necessary modules and resolved their dependencies, instructed Vue to use the wrapper globally (throughout the project) in the src/main.js file, and therefore there is no lack of definition for the custom component that we have used here. Also, the custom component has custom attributes, and each of the custom attribute is bound to a data object (and in turn, their values), by the v-bind directive, for which the shorthand is the colon (:) symbol. We will learn about the attributes and their associated data objects in a greater detail, when we discuss some of the specific charts used in this project.
    • In the , first you declare the props that the component is supposed to receive, and then go on defining the data objects that are bounded to the attributes of . The values assigned to the data objects are the values that the attributes of pulls in, and the charts are created on the basis of those pulled in values. Apart from these, the most interesting part of the code is the watch { } object. This is a very special object in Vue’s scheme of things — it essentially instructs Vue to watch over any changes happening to certain data, and then take actions based on how the handler function for that data has been defined. For example, we want Vue to keep a watch on the prop received, i.e., data_prop_received_by_the_component in the pseudocode. The prop becomes a key in the watch { } object, and the value of the key is another object — a handler method that describes what needs to be done whenever the prop changes. With such elegant mechanisms to handle the changes, the app maintains its reactivity. The deep: true represents a boolean flag that you can associate with watchers, so that the object being watched is watched rather deeply, i.e., even the changes made in the nested levels of the object are tracked.
      (For more information on watchers, consult the official documentation).

    Now that you are equipped with an understanding of the general scheme of things, let’s dive into the specific implementations of the charts in the .vue component files. The code will be pretty self-explanatory, and you should try to understand how the specifics fit in the general scheme of things described above.

    Implementation Of Charts In .vue Files

    While the very specifics of the implementation varies from one chart to another, the following explanation is applicable for all of them:


    • As explained previously, the custom component has several attributes, each of them being bound to corresponding data object defined in the data() function by using the v-bind: directive. The attribute names are quite self-explanatory for what they mean, and figuring out the corresponding data objects is also trivial.

    • In the data() function, the data objects and their values are what makes the charts work, because of the binding done by the v-bind (:) directives used on the attributes of . Before we dive deep into the individual data objects, it’s worth mentioning some general characteristics:

      • The data objects whose values are either 0 or 1 are boolean in nature, where 0 represents something not available/turned off, and 1 represents availability/turned on state. However, be cautious that non-boolean data objects can also have 0 or 1 as their values, besides other possible values — it depends on the context. For example, containerbackgroundopacity with its default value as 0 is boolean, whereas lowerLimit with its default value as 0 simply means the number zero is its literal value.
      • Some data objects deals with CSS properties like margin, padding, font-size, etc. — the value has an implied unit of “px” or pixel. Similarly, other data objects can have implicit units associated with their values. For detailed information, please refer to the respective chart attributes page of FusionCharts Dev Center.
    • In the data() function, perhaps the most interesting and non-obvious object is the dataSource. This object has three main objects nested within it:
      • chart: This object encapsulates lots of chart attributes related to the configuration and cosmetics of the chart. It is almost a compulsory construct that you will find in all the charts you will create for this project.
      • colorrange: This object is somewhat specific to the chart under consideration, and is mainly present in charts that deals with multiple colors/shades to demarcate different sub-ranges of the scale used in chart.
      • value: This object, again, is present in charts that has a specific value that needs to be highlighted in the range of the scale.
    • The watch { } object is perhaps the most crucial thing that makes this chart, and the other charts used in this project, spring to life. The reactivity of the charts, i.e., the charts updating themselves based on the new values resulting from a new user query is controlled by the watchers defined in this object. For example, we have defined a watcher for the prop highlights received by the component, and then defined a handler function to instruct Vue about the necessary actions that it should take, when anything changes about the object being watched in the entire project. This means that whenever App.vue yields a new value for any of the object within highlights, the information trickles down all the way down to this component, and the new value is updated in the data objects of this component. The chart being bound to the values, also gets updated as a result of this mechanism.

    The above explanations are quite broad strokes to help us develop an intuitive understanding of the bigger picture. Once you understand the concepts intuitively, you can always consult the documentation of Vue.js and FusionCharts, when something is not clear to you from the code itself. We leave the exercise to you, and from the next subsection onward, we will not explain stuff that we covered in this subsection.

    src/components/TempVarChart.vue

    A diagram showing Hourly Temperature
    (Large preview)
    
     
    </div> </template> export default { props: ["tempVar"], components: {}, data() { return { tempChartData: { chart: { caption: "Hourly Temperature", captionFontBold: "0", captionFontColor: "#000000", captionPadding: "30", baseFont: "Roboto", chartTopMargin: "30", showHoverEffect: "1", theme: "fusion", showaxislines: "1", numberSuffix: "°C", anchorBgColor: "#6297d9", paletteColors: "#6297d9", drawCrossLine: "1", plotToolText: "$label

    $dataValue", showAxisLines: "0", showYAxisValues: "0", anchorRadius: "4", divLineAlpha: "0", labelFontSize: "13", labelAlpha: "65", labelFontBold: "0", rotateLabels: "1", slantLabels: "1", canvasPadding: "20" }, data: [], }, }; }, methods: { setChartData: function() { var data = []; for (var i = 0; i <style> </style>
    src/components/UVIndex.vue

    This component contains an extremely useful chart — the Angular Gauge.


    UV Index
    (Large preview)

    The code for the component is given below. For detailed information on the chart attributes of Angular Gauge, refer to FusionCharts Dev Center page for Angular Gauge.

    <template>
     
    </div> </template> export default { props: ["highlights"], components: {}, data() { return { type: "angulargauge", width: "100%", height: "100%", containerbackgroundopacity: 0, dataformat: "json", datasource: { chart: { caption: "UV Index", captionFontBold: "0", captionFontColor: "#000000", captionPadding: "30", lowerLimit: "0", upperLimit: "15", lowerLimitDisplay: "1", upperLimitDisplay: "1", showValue: "0", theme: "fusion", baseFont: "Roboto", bgAlpha: "0", canvasbgAlpha: "0", gaugeInnerRadius: "75", gaugeOuterRadius: "110", pivotRadius: "0", pivotFillAlpha: "0", valueFontSize: "20", valueFontColor: "#000000", valueFontBold: "1", tickValueDistance: "3", autoAlignTickValues: "1", majorTMAlpha: "20", chartTopMargin: "30", chartBottomMargin: "40" }, colorrange: { color: [ { minvalue: "0", maxvalue: this.highlights.uvIndex.toString(), code: "#7DA9E0" }, { minvalue: this.highlights.uvIndex.toString(), maxvalue: "15", code: "#D8EDFF" } ] }, annotations: { groups: [ { items: [ { id: "val-label", type: "text", text: this.highlights.uvIndex.toString(), fontSize: "20", font: "Source Sans Pro", fontBold: "1", fillcolor: "#212529", x: "$gaugeCenterX", y: "$gaugeCenterY" } ] } ] }, dials: { dial: [ { value: this.highlights.uvIndex.toString(), baseWidth: "0", radius: "0", borderThickness: "0", baseRadius: "0" } ] } } }; }, methods: {}, computed: {}, watch: { highlights: { handler: function() { this.datasource.colorrange.color[0].maxvalue = this.highlights.uvIndex.toString(); this.datasource.colorrange.color[1].minvalue = this.highlights.uvIndex.toString(); this.datasource.annotations.groups[0].items[0].text = this.highlights.uvIndex.toString(); }, deep: true } } };
    src/components/Visibility.vue

    In this component, we use a Horizontal Linear Gauge to represent the visibility, as shown in the image below:


    A screenshot of the Horizontal Linear Gauge representing Air Visibility (16km)
    (Large preview)

    The code for the component is given below. For an in depth understanding of the different attributes of this chart type, please refer to FusionCharts Dev Center page for Horizontal Linear Gauge.

    <template>
     
    </div> </template> export default { props: ["highlights"], components: {}, methods: {}, computed: {}, data() { return { type: "hlineargauge", width: "100%", height: "100%", containerbackgroundopacity: 0, dataformat: "json", creditLabel: false, datasource: { chart: { caption: "Air Visibility", captionFontBold: "0", captionFontColor: "#000000", baseFont: "Roboto", numberSuffix: " km", lowerLimit: "0", upperLimit: "40", showPointerShadow: "1", animation: "1", transposeAnimation: "1", theme: "fusion", bgAlpha: "0", canvasBgAlpha: "0", valueFontSize: "20", valueFontColor: "#000000", valueFontBold: "1", pointerBorderAlpha: "0", chartBottomMargin: "40", captionPadding: "30", chartTopMargin: "30" }, colorRange: { color: [ { minValue: "0", maxValue: "4", label: "Fog", code: "#6297d9" }, { minValue: "4", maxValue: "10", label: "Haze", code: "#7DA9E0" }, { minValue: "10", maxValue: "40", label: "Clear", code: "#D8EDFF" } ] }, pointers: { pointer: [ { value: this.highlights.visibility.toString() } ] } } }; }, watch: { highlights: { handler: function() { this.datasource.pointers.pointer[0].value = this.highlights.visibility.toString(); }, deep: true } } };
    src/components/WindStatus.vue

    This component displays the wind speed and direction (wind velocity, if you are physics savvy), and it is very difficult to represent a vector using a chart. For such cases, we suggest representing them with the aid of some nice images and text values. Since the representation we have thought about is entirely dependent on CSS, we will implement it in the next section that deals with the CSS. However, take a look at what we are aiming to create:


    Wind Status: Wind Direction (left) and Wind Speed (right)
    (Large preview)
    <template>
     
    Wind Status

    Wind Direction

    {{ highlights.windStatus.derivedWindDirection }}

    Wind Speed

    {{ highlights.windStatus.windSpeed }} km/h

    </div> </div> </div> </template> export default { props: ["highlights"], components: {}, data() { return {}; }, methods: {}, computed: {} };

    Wrapping Up With Highlights.vue

    Recall that we have already implemented code with CSS for all the components — except Content.vue and Highlights.vue. Since Content.vue is a dumb component that just relays data, the minimal styling it needs has already been covered. Also, we have already written appropriate code for styling the sidebar and the cards containing the charts. Therefore, all we are left to do is add some stylistic bits to Highlights.vue, which primarily involves using the CSS classes:

    <template>
      
    Highlights
    </div> </div> </template> import UVIndex from "./UVIndex.vue"; import Visibility from "./Visibility.vue"; import WindStatus from "./WindStatus.vue"; export default { props: ["highlights"], components: { "uv-index": UVIndex, "visibility": Visibility, "wind-status": WindStatus, }, };

    Deployment And Source Code

    With the charts and style in order, we are done! Take a moment to appreciate the beauty of your creation.


    The result
    (Large preview)

    It’s now time for you to deploy your application, and share it with your peers. If you don’t have much idea about deployment and expect us to help you, look here about our take on deployment ideas. The linked article also contains suggestions on how to remove the FusionCharts watermark at the left bottom of every chart.

    If you mess up somewhere and want a reference point, the source code is available on Github.

    Smashing Editorial
    (ms, ra, il)

Source: Smashing Magazine, Using Vue.js To Create An Interactive Weather Dashboard With APIs

Collective #488

dreamt up by webguru in Uncategorized | Comments Off on Collective #488





















Collective #488 was written by Pedro Botelho and published on Codrops.


Source: Codrops, Collective #488

Spread The Love! Inspiring Wallpapers For February 2019

dreamt up by webguru in Uncategorized | Comments Off on Spread The Love! Inspiring Wallpapers For February 2019

Spread The Love! Inspiring Wallpapers For February 2019

Spread The Love! Inspiring Wallpapers For February 2019

Cosima Mielke



As designers we usually turn to different sources of inspiration, and, well, sometimes the best inspiration lies right in front of us. With that in mind, we embarked on our wallpapers creativity mission more than nine years ago. The idea: to provide you with unique and inspiring desktop wallpapers each month anew. Wallpapers created by the community for the community.

We are very thankful to all artists and designers who have contributed and are still diligently contributing to this challenge, who tickle their creativity to keep the steady stream of wallpapers flowing. This post features their artworks for February 2019. All wallpapers come in two versions — with and without a calendar — and can be downloaded for free. At the end of this post, we also collected some wallpaper favorites from past February editions for you. After all, some things are just too good to be forgotten, right?

Please note that:

  • All images can be clicked on and lead to the preview of the wallpaper,
  • You can feature your work in our magazine by taking part in our Desktop Wallpaper Calendar series. We are regularly looking for creative designers and artists to be featured on Smashing Magazine. Are you one of them?

Further Reading on SmashingMag:

Savannah Stroll

“February is a month focused on romance and as a southerner, I can’t think of a more romantic way to spend a day than strolling the historic moss-draped streets of Savannah, GA. Spending time sitting on a bench on one of the many beautiful squares, holding hands and people watching as you sip a cappuccino.” — Designed by Heather Ozee Designs from the United States.

Savannah Stroll

Love Is Worth Fighting For

Designed by Maria Keller from Mexico.

Love Is Worth Fighting For

Dark Temptation

“A dark romantic feel, walking through the city on a dark and rainy night.” — Designed by Matthew Talebi from the United States.

Dark Temptation

Feel The Love!

“We’re celebrating Valentine’s Day with our February wallpaper. Whatever Valentine’s Day means to you we’re on board, love, relationships or the best of friends. It’s something to be celebrated!” — Designed by Focus from the United Kingdom.

Feel The Love!

Cold And Frost

“Frosts in Russia are very severe. Walking through the Park, I found these branches at the top and decided to capture the moment.” — Designed by Nikolay Belikov from Russia.

Cold And Frost

What Is Love

“We all feel it, even if we cannot define it. But, who needs a definition anyway? When you sense it in your gut and feel like standing on the top of the world. Happy. From the bottom of your heart. ‘Where there is love, there is life,’ said Mahatma Gandhi. And we couldn’t agree more. May the love be with you. Always.” — Designed by PopArt Studio from Serbia.

What Is Love

Polar Cold

“February is the month of the bear. I would like to be in the Arctic and play with the bears.” — Designed by Verónica Valenzuela from Spain.

Polar Cold

Lovely Day

Designed by Ricardo Gimenes from Sweden.

Lovely Day

Let Love Speak

“As it’s February, we just thought of celebrating Valentine month. Let the love between nature and human being prosper. These days we are forgetting to take care of our nature. So it’s a kind of gentle reminder for all of us.” — Designed by Sweans Technologies from London.

Let Love Speak

Umbrella Day

“Always good to have an umbrella, on rainy or sunny days! On the 10th of February we celebrate the umbrellas.” — Designed by Melissa Bogemans from Belgium.

Umbrella Day

Sunset

“I want to emphasize February 14th because it’s Valentine’s Day. I thought it would be fun to use two cats that are in love watching the sunset in the park. I have tried to use as many warm and loving colors as possible, such as red, rose, orange and purple.” — Designed by Vince Teckmans from Belgium.

Sunset

World Radio Day

“Music is an important element in our everyday lives. It connects people all over the world, regardless of culture, religion etc.” — Designed by Ilke Cauwenbergh from Belgium.

World Radio Day

Oldies But Goodies

No matter if it’s a brave icebreaker that confronts even the most adverse weather conditions, a piece of design wisdom, or a pun — a lot of things have inspired our artists to design a February wallpaper in the past few years. Below you’ll find a little best-of. Please note that these wallpapers don’t come with a calendar.

Dog Year Ahead

Designed by PopArt Studio from Serbia.

Dog Year Ahead

Balloons

Designed by Xenia Latii from Germany.

Balloons

Febpurrary

“I was doodling pictures of my cat one day and decided I could turn it into a fun wallpaper – because a cold, winter night in February is the perfect time for staying in and cuddling with your cat, your significant other, or both!” — Designed by Angelia DiAntonio from Ohio, USA.

Febpurrary

Minimalistic Love

“This minimalistic love logo is designed from geometric shapes, where the heart represents the letter ‘O’ and love. The anchor represents the letter ‘V’ very delicately and stylish and it symbolizes your wanderlust. The anchor is a symbol of adventure and travels.” — Designed by Antun Hirsman from Croatia.

Minimalistic Love

“Greben” Icebreaker

“Danube is Europe’s second largest river, connecting 10 different countries. In these cold days, when ice paralyzes rivers and closes waterways, a small but brave icebreaker called Greben (Serbian word for ‘reef’) seems stronger than winter. It cuts through the ice on Đerdap gorge (Iron Gate) – the longest and biggest gorge in Europe – thus helping the production of electricity in the power plant. This is our way to give thanks to Greben!” — Designed by PopArt Studio from Serbia.

“Greben” Icebreaker

Winter Wonderland

“In February, nature shows its creativity. Our artwork occurs when it is being drawn.” — Designed by Ana Masnikosa from Belgrade, Serbia.

Winter Wonderland

Love Angel Vader

“Valentine’s Day is coming? Noooooooooooo!” — Designed by Ricardo Gimenes from Sweden.

Love Angel Vader

Febrewery

“I live in Madison, WI USA, which is famous for its breweries. Wisconsin even named their baseball team “The Brewers.” If you like beer, brats, and lots of cheese, it’s the place for you!” — Designed by Danny Gugger from the United States.

Febrewery

Farewell, Winter…

“Although I love winter (mostly because of the fun winter sports), there are other great activities ahead. February, the last winter month, this year is even one day longer. But I don’t mind. Thanks, winter, and see you next year!” — Designed by Igor Izhik from Canada.

Farewell, Winter...

Principles Of Good Design

“The simplicity seen in the work of Dieter Rams which has ensured his designs from the 50s and 60s still hold a strong appeal.” — Designed by Vinu Chaitanya from India.

Principles of Good Design- Dieter Rams

I Believe I Can Fly

Designed by Elise Vanoorbeek from Belgium.

Smashing Wallpaper - february 13

Out There, There’s Someone Like You

“I am a true believer that out there in this world there is another person who is just like us, the problem is to find her/him.” — Designed by Maria Keller from Mexico.

Out There, There’s Someone Like You

The Great Beyond

“My inspiration came mostly from ‘The Greay from’. It’s about a dog and an astronaut exploring a strange new world.” — Designed by Lars Pauwels from Belgium.

The Great Beyond

Join In Next Month!

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.

Thank you to all designers for their participation. Join in next month!

Source: Smashing Magazine, Spread The Love! Inspiring Wallpapers For February 2019

Spread The Love! Inspiring Wallpapers For February 2019

dreamt up by webguru in Uncategorized | Comments Off on Spread The Love! Inspiring Wallpapers For February 2019

Spread The Love! Inspiring Wallpapers For February 2019

Spread The Love! Inspiring Wallpapers For February 2019

Cosima Mielke



As designers we usually turn to different sources of inspiration, and, well, sometimes the best inspiration lies right in front of us. With that in mind, we embarked on our wallpapers creativity mission more than nine years ago. The idea: to provide you with unique and inspiring desktop wallpapers each month anew. Wallpapers created by the community for the community.

We are very thankful to all artists and designers who have contributed and are still diligently contributing to this challenge, who tickle their creativity to keep the steady stream of wallpapers flowing. This post features their artworks for February 2019. All wallpapers come in two versions — with and without a calendar — and can be downloaded for free. At the end of this post, we also collected some wallpaper favorites from past February editions for you. After all, some things are just too good to be forgotten, right?

Please note that:

  • All images can be clicked on and lead to the preview of the wallpaper,
  • You can feature your work in our magazine by taking part in our Desktop Wallpaper Calendar series. We are regularly looking for creative designers and artists to be featured on Smashing Magazine. Are you one of them?

Further Reading on SmashingMag:

Savannah Stroll

“February is a month focused on romance and as a southerner, I can’t think of a more romantic way to spend a day than strolling the historic moss-draped streets of Savannah, GA. Spending time sitting on a bench on one of the many beautiful squares, holding hands and people watching as you sip a cappuccino.” — Designed by Heather Ozee Designs from the United States.

Savannah Stroll

Love Is Worth Fighting For

Designed by Maria Keller from Mexico.

Love Is Worth Fighting For

Dark Temptation

“A dark romantic feel, walking through the city on a dark and rainy night.” — Designed by Matthew Talebi from the United States.

Dark Temptation

Feel The Love!

“We’re celebrating Valentine’s Day with our February wallpaper. Whatever Valentine’s Day means to you we’re on board, love, relationships or the best of friends. It’s something to be celebrated!” — Designed by Focus from the United Kingdom.

Feel The Love!

Cold And Frost

“Frosts in Russia are very severe. Walking through the Park, I found these branches at the top and decided to capture the moment.” — Designed by Nikolay Belikov from Russia.

Cold And Frost

What Is Love

“We all feel it, even if we cannot define it. But, who needs a definition anyway? When you sense it in your gut and feel like standing on the top of the world. Happy. From the bottom of your heart. ‘Where there is love, there is life,’ said Mahatma Gandhi. And we couldn’t agree more. May the love be with you. Always.” — Designed by PopArt Studio from Serbia.

What Is Love

Polar Cold

“February is the month of the bear. I would like to be in the Arctic and play with the bears.” — Designed by Verónica Valenzuela from Spain.

Polar Cold

Lovely Day

Designed by Ricardo Gimenes from Sweden.

Lovely Day

Let Love Speak

“As it’s February, we just thought of celebrating Valentine month. Let the love between nature and human being prosper. These days we are forgetting to take care of our nature. So it’s a kind of gentle reminder for all of us.” — Designed by Sweans Technologies from London.

Let Love Speak

Umbrella Day

“Always good to have an umbrella, on rainy or sunny days! On the 10th of February we celebrate the umbrellas.” — Designed by Melissa Bogemans from Belgium.

Umbrella Day

Sunset

“I want to emphasize February 14th because it’s Valentine’s Day. I thought it would be fun to use two cats that are in love watching the sunset in the park. I have tried to use as many warm and loving colors as possible, such as red, rose, orange and purple.” — Designed by Vince Teckmans from Belgium.

Sunset

World Radio Day

“Music is an important element in our everyday lives. It connects people all over the world, regardless of culture, religion etc.” — Designed by Ilke Cauwenbergh from Belgium.

World Radio Day

Oldies But Goodies

No matter if it’s a brave icebreaker that confronts even the most adverse weather conditions, a piece of design wisdom, or a pun — a lot of things have inspired our artists to design a February wallpaper in the past few years. Below you’ll find a little best-of. Please note that these wallpapers don’t come with a calendar.

Dog Year Ahead

Designed by PopArt Studio from Serbia.

Dog Year Ahead

Balloons

Designed by Xenia Latii from Germany.

Balloons

Febpurrary

“I was doodling pictures of my cat one day and decided I could turn it into a fun wallpaper – because a cold, winter night in February is the perfect time for staying in and cuddling with your cat, your significant other, or both!” — Designed by Angelia DiAntonio from Ohio, USA.

Febpurrary

Minimalistic Love

“This minimalistic love logo is designed from geometric shapes, where the heart represents the letter ‘O’ and love. The anchor represents the letter ‘V’ very delicately and stylish and it symbolizes your wanderlust. The anchor is a symbol of adventure and travels.” — Designed by Antun Hirsman from Croatia.

Minimalistic Love

“Greben” Icebreaker

“Danube is Europe’s second largest river, connecting 10 different countries. In these cold days, when ice paralyzes rivers and closes waterways, a small but brave icebreaker called Greben (Serbian word for ‘reef’) seems stronger than winter. It cuts through the ice on Đerdap gorge (Iron Gate) – the longest and biggest gorge in Europe – thus helping the production of electricity in the power plant. This is our way to give thanks to Greben!” — Designed by PopArt Studio from Serbia.

“Greben” Icebreaker

Winter Wonderland

“In February, nature shows its creativity. Our artwork occurs when it is being drawn.” — Designed by Ana Masnikosa from Belgrade, Serbia.

Winter Wonderland

Love Angel Vader

“Valentine’s Day is coming? Noooooooooooo!” — Designed by Ricardo Gimenes from Sweden.

Love Angel Vader

Febrewery

“I live in Madison, WI USA, which is famous for its breweries. Wisconsin even named their baseball team “The Brewers.” If you like beer, brats, and lots of cheese, it’s the place for you!” — Designed by Danny Gugger from the United States.

Febrewery

Farewell, Winter…

“Although I love winter (mostly because of the fun winter sports), there are other great activities ahead. February, the last winter month, this year is even one day longer. But I don’t mind. Thanks, winter, and see you next year!” — Designed by Igor Izhik from Canada.

Farewell, Winter...

Principles Of Good Design

“The simplicity seen in the work of Dieter Rams which has ensured his designs from the 50s and 60s still hold a strong appeal.” — Designed by Vinu Chaitanya from India.

Principles of Good Design- Dieter Rams

I Believe I Can Fly

Designed by Elise Vanoorbeek from Belgium.

Smashing Wallpaper - february 13

Out There, There’s Someone Like You

“I am a true believer that out there in this world there is another person who is just like us, the problem is to find her/him.” — Designed by Maria Keller from Mexico.

Out There, There’s Someone Like You

The Great Beyond

“My inspiration came mostly from ‘The Greay from’. It’s about a dog and an astronaut exploring a strange new world.” — Designed by Lars Pauwels from Belgium.

The Great Beyond

Join In Next Month!

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.

Thank you to all designers for their participation. Join in next month!

Source: Smashing Magazine, Spread The Love! Inspiring Wallpapers For February 2019

Adobe Experience Manager vs. WordPress: The Authoring Experience Compared

dreamt up by webguru in Uncategorized | Comments Off on Adobe Experience Manager vs. WordPress: The Authoring Experience Compared

Adobe Experience Manager vs. WordPress: The Authoring Experience Compared

Adobe Experience Manager vs. WordPress: The Authoring Experience Compared

Kevin Weber



Thanks, WordPress and Gutenberg, for making block-based editing the standard for authoring web pages. In this article, I’m going to compare the new authoring experience in WordPress with the experience from Adobe Experience Manager (AEM), an enterprise content management system that also embraces block-based editing. I’ve implemented both WordPress and AEM for multiple companies (such as Informatica and Twitter) and had to realize that despite the authoring experience is critical for non-technical authors, it is often neglected by developers.

Note: With the term “authoring experience” I’m referring to the user experience for those people whose goal it is to create and publish content on a website. I’m not referring to the people that are going to consume the published content. If you haven’t thought about the authoring experience before, here’s a primer by Eileen Webb who was also featured in Smashing Book 5.

Adobe Experience Manager is, compared to WordPress, a complex system with a steep learning curve, especially for developers. At the same time, AEM is easier to use than more established and more expensive content management solutions, placing AEM somewhere in between free and very costly solutions.

From a technical perspective, AEM is a conglomerate of open source technologies with several touches from Adobe, placing AEM somewhere in between open-source and proprietary software. It is those touches from Adobe that make the system shiny and usable. For example, a visual drag and drop page builder has been the standard way for creating pages in AEM — long before WordPress Gutenberg was born.

Let’s take a look at some of the features that elevate the authoring experience above average.

Components (Blocks)

One of the most significant ideas for websites is the concept of a component (or block in WordPress lingo). A component represents a piece of content that follows specific rules instead of being a blob of anything. For example, you can have a video component where the author can only paste in a Youtube link and control Youtube-specific settings. Or you can have a quote component where the author adds a quote to one text field and the name of the person being quoted to another text field. You can even have a layout component that contains other components and displays them below each other on a mobile device, whereas on a large screen, those components get spread across three columns.


Title component in AEM with an overlay that includes an edit button
Authors can update components right in the visual editor. Available editing options get displayed based on the selected component. (Image source) (Large preview)

An author knows exactly what to expect from a specific component and can easily fill it with the appropriate content. Equally important are the long-term benefits and new opportunities that wouldn’t be feasible for the old school “one text field fits all content” approach that has been prevalent for the past decades:

  • If a component requires a date input, the component authoring dialog can display a date picker instead of a plain text field, making it easier for the author to pick a date with the right format.
  • If a designer wants the name of a quoted person to be displayed above the quote instead of below the quote, the developer can easily rearrange the code because the quote and the name are stored separately. If the quote and name would be stored the old-fashioned way, the developer would have to manually extract the name from the text blob and move it in front of the quote.
  • If a quote needs to be translated from English to German, the quote can be submitted to a translation service. If the translation service has translated this quote before already, it can return the saved translation. If the quote would be part of a longer paragraph instead of being standalone, the translation would be much harder and probably require a human translator.
  • If a video lacks a transcript and therefore prevents deaf users from consuming it, the component can be complemented with a summary text that makes the video more accessible to deaf users.

Component-based editing has been embraced by users of AEM for a while already, and because of the arrival of Gutenberg in WordPress 5.0, component-based editors are now the de facto standard for authoring web pages.

Note: Leonardo Losoviz dives deeper into the implications of blocks in the context of WordPress.

Fragments

Content fragments and experience fragments are new terms that have been dominating the AEM scene for the past year. I’ll summarize those two concepts simply as fragments. In essence, fragments allow authors to create neutral content that can be used across web, mobile, social media and other channels.

Fragments are created outside of a page editor and are, compared to a component, less opinionated about how their data is going to be used. Let’s imagine a fragment called “Quote of the day” that authors update once a day with a new quote. Now the quoted text of this fragment can be used in a variety of places:

  • A footer widget displays the quote of the day at the bottom of every page. As soon as an author updates the fragment, the footer updates as well. The fragment determines what is going to be displayed whereas the footer widget determines how the quote is going to be displayed.
  • A quote component allows authors to import a quote from past “Quotes of the day” and add it to the blog post.
  • A plugin adds a “Share quote of the day” button to the homepage. Whenever someone clicks on that button, the plugin takes the quote of the day and formats it to meet best practices for sharing via Facebook, Twitter, and email.

Fragment editor for populating a component with airport data
Using an editor for fragments, authors focus on related data without having to know how exactly it is going to be displayed on websites and in apps. (Image source) (Large preview)

In WordPress, widgets and menus resemble fragments: Authors create menu items in a neutral interface, then developers display those items as part of the theme in a way that makes sense for the theme. If the theme gets replaced with a new theme, those menu items persist and can be displayed in the new theme as well, even though the new theme might look very different from the previous one.

I expect fragments to become more widely used, even though the concept has different names in different systems. Indeed, Matt Mullenweg already announced that his team is currently focusing on “expanding the block interface to other aspects of content management [including the creation of] a navigation menu block [and] porting all widgets over to blocks.”

Page Templates

Pages templates can be described as higher-level components because they include several other components. In AEM, authors can create templates that lock components such as a header component into a fixed position while also defining flexible areas where components can be added on a per page basis.


Page template editor
The template on this screen provides a heading, image and text component by default. It prevents authors from removing the “Text Locked” text and allows authors to add more components below that text. (Image source) (Large preview)

One important aspect of this is that such a flexible area can limit which components can go into it. That way you can create page templates for different purposes:

  • Template #1: Article page template
    Header, title, content area and footer are fixed. The author can update the title component but can’t remove it. The author can drop text, image and video components into the content area.
  • Template #2: Landing page template
    Only a logo and a title component at the top of the page are fixed. The author can choose among a set of landing page-specific components that are optimized for converting visitors into customers.

Permissions And Workflows

It’s unlikely that every author in a large team should be able to modify critical templates such as the article page template. In order to prevent people from being able to accidentally and irrevocably break the site, it is important to define who can modify which part of the site. Welcome to the concept of permissions and workflows. This concept is neither new nor special, but it’s important for large teams.


Screen where admins handle detailed permissions.
Yes, AEM’s interface for handling permissions could need a facelift. Anyhow, it works. (Image source) (Large preview)

A typical AEM site includes the actual production website and at least one production-like site, aka staging. Authors can publish content to a private staging site before publishing it to the public production site. The process of publishing content to staging followed by publishing content to production can be called a workflow. Another common type of workflow is that the content must go through an approval process before it can be published to the production site, and only certain people are able to hit the “publish to production” button.


Page editor with workflow message
This page indicates that a workflow has been initiated and the author can either complete or delegate the “Request for Activation”. (Image source) (Large preview)

Permissions and workflows are features that are negligible on small teams. However, as a team grows, those features become critical for the productivity and success of the team. Despite AEM comes with the basics for creating workflows and developers can make AEM work for any specific need, it requires quite some code changes and isn’t implemented with the snap of a finger. This is even more true for WordPress. It would be nice to have an authoring-friendly tool to create custom workflows.


Visual workflow editor in Git
Imagine how a user-friendly tool could simplify the creation of workflows. Here is how the creation of workflows in Github will look like once Github Actions are out of beta. Unfortunately, AEM doesn’t offer such a tool. (Large preview)

Editing Modes

In AEM, authors can quickly edit and view each page in different modes. The author switches between modes based on what job needs to get done:

  • To arrange components and edit their content, choose Edit mode.
  • To change how components should be arranged on an iPad, choose Layout mode.
  • To look at the content as if you were a visitor, choose Preview mode.

Page editor emulating an iPad-sized screen
On this page responsive layout mode is active. The author can emulate different device sizes and adjust the position of components within a responsive grid. (Image source) (Large preview)

There are a few more modes that show up based on how the site is set up. One ideal scenario is that A/B testing and personalization is set up by integrating AEM with Adobe Target. Using Targeting mode, authors can define when to show certain components based on a visitor’s location, age, referral page, the time of the day, etc.

Integrations in AEM are comparable to plugins in WordPress but with the difference that AEM integrations are more complex and usually custom-tailored. Especially integrating AEM Target can be more painful than salespeople make it sound. 😉


Dropdown with layout modes
Switch the mode through a dropdown. (Image source) (Large preview)

Editor where author defines a target audience
In targeting mode, content can be personalized and tested right from the page editor. (Image source) (Large preview)

Putting development complexity and money aside, the consequence of such an effort can result in a superb authoring experience. The concept of editing modes demonstrates how a simple dropdown creates an opportunity for authors to get a range of jobs done while staying on a single page.

Visual Single-Page Editor

Looking at the screenshots in this article, you must have realized that AEM’s page editor is not just component-based but also visual: If a component gets updated, the change becomes visible immediately and the author doesn’t have to open a preview in a new window. Quite a feature. Even though page builders are omnipresent in the WordPress ecosystem, the team behind WordPress has yet to define a best practice for visual editing. Let me take this one step further and ask: What happens if you marry visual editors with single-page applications (SPAs)?

SPAs are websites where navigating from one page to another feels seamless because the browser doesn’t have to reload the whole page. Some popular websites such as Gmail and Facebook are SPAs but most of the sites on the internet are not. One reason for rather low adoption is that creating SPAs is hard, and maintaining SPAs with thousands of pages is even harder. There are currently two major ways for managing content in SPAs:

  • The content of a site gets updated by updating code. That’s obviously not authoring-friendly.
  • The content is managed in a CMS that is decoupled from the visitor-facing part of the website. The content from the CMS is consumed via an API, for example by a React app. The authoring interface looks different from the assembled site that the visitor will see.

Implementing a visual editor and an SPA each by itself is already a tough technical challenge. Having a visual editor that works with an SPA is nearly unheard. Adobe’s team is working on supporting SPAs in AEM while trying not to compromise any benefits of their existing system. Even though promising first versions have been released to the AEM community in 2018, there’s still a lot of work to be done.


Weather component with overlay
This page shows a React app. Note how AEM has added a layer on top of the weather component so authors can edit its properties. (Image source) (Large preview)

Summary

Adobe Experience Manager comes with several useful features that have already made or will make their way into popular open-source projects. AEM didn’t necessarily invent the concepts highlighted in this article, but it certainly commercializes well as one of the most authoring-friendly systems on the market.

The concept of components became mainstream with the introduction of blocks in WordPress. The concept of fragments, page templates, permissions and workflows are at least partially implemented in WordPress and are important for teams with many authors that serve content to multiple channels.

The authoring experience can be improved even more using a visual editor with edit modes and support for single-page applications. Such an editor is difficult to implement but as the efforts by Adobe indicate, the improved experience might be worth the effort and eventually make it into WordPress as well.

Further Reading

Smashing Editorial
(dm, ra, il)

Source: Smashing Magazine, Adobe Experience Manager vs. WordPress: The Authoring Experience Compared

Understanding API-Based Platforms: A Guide For Product Managers

dreamt up by webguru in Uncategorized | Comments Off on Understanding API-Based Platforms: A Guide For Product Managers

Understanding API-Based Platforms: A Guide For Product Managers

Understanding API-Based Platforms: A Guide For Product Managers

Michał Sędzielewski



To build a digital product today is to integrate the myriad of various back-office systems with customer touchpoints and devices. The cost of engaging a software team to connect them in a single working solution might sky-rocket.

This is why modern product managers, when choosing vendors, often put integration capabilities in the first place which may come down to choosing systems exposing API. What’s the API and how to test it without engaging your tech team? Read on.

Embrace The Data: Why We Need APIs At All

Customer data change how the business operates. If properly collected and shifted around, they can help companies shoot up customer acquisition and retention rates, leading eventually to a burst in income.

But data crunching is a tedious job. That’s why business tapped into computer science. In the 1990s, the databases which automated the most time-consuming data tasks became massively popular across marketing departments. This led to a massive shift in how marketing strategies were conceived — that shift was called the data-driven approach.

Databases had a major con, though. To make them something of value, a company needed to hire software engineers. They were the heroes who knew how to turn huge piles of data into working insights. They were also the guards protecting data integrity and thus making sure the system was future-proof.

But software engineers cost a lot, and their communication interface required effort.

When the number of data collection channels spanned over several departments and even external companies, databases and their operators became a bottleneck. Businesses needed to find an automated way of accessing data stores.

This is how the idea of API-first systems originated.

What The API Actually Is Without The Tech Lingo

API-first systems, today commonly shortened as API (Application Programmable Interface), are the applications which ensure that other systems can access their data in a unified and secure way.

Without a computer science grade, Application Programmable Interface doesn’t really ring a bell. Let’s have a look at a more tangible explanation.

One of the best analogies I’ve found in the web so far has been written by Taija:

“If you go to a restaurant as a customer, you are not allowed to enter the kitchen. You need to know what is available. For that, you have the menu. After looking at the menu, you make an order to a waiter, who passes it to the kitchen and who will then deliver what you have asked for. The waiter can only deliver what the kitchen can provide.

https://platform.twitter.com/widgets.js

How does that relate to an API? The waiter is the API. You are someone who is asking for service. In other words, you are an API customer or consumer. The menu is the documentation which explains what you can ask for from the API. The kitchen is, for example, a server; a database that holds only a certain type of data  —  whatever the buyer has bought for the restaurant as ingredients and what the chef has decided they will offer and what the cooks know how to prepare.”

So again:

  • Kitchen
    The database, no customers allowed to protect data integrity.
  • Waiter
    The API, a middleman that knows how to serve data from the database without disrupting its functioning.
  • Customer
    An external system that wants to get their data
  • Menu
    The data format reference the external systems have to use to perform their operation.
  • Order
    An actual single API call.

With the current state of technology, it still takes a software developer to “make an order.” But it’s way faster (read: cheaper) because the menu, like McDonald’s, is more or less standardized across the world.

So now, we’re going to wear a software developer’s shoes and try to call an exemplary API. Don’t worry; we’re not going to go beyond school computer science classes.

How Your Weather App Gets The Data: API Basics

We’re going to find out how your weather app knows the current temperature. In this way, we’ll get the basics of how to communicate with systems over the internet.

What we need:

  • A weather database
  • A browser
  • A dash of willpower

That’s it! Today’s technology makes it easy to test the API without the need for big developer tools.

Of course, that’s different when you want to create a full-blown integration. When push comes to shove, you need to know more advanced tools and programming languages, but for testing/proof of concepts, this setup is enough.

So, let’s try to get the current temperature index for your city — or, in the parlance of coders — let’s invoke the first API call. After all, it boils down to sending some text to a server and receiving a message in exchange.

The Anatomy Of An API Request

In this article, we’ll be using the https://openweathermap.org API. Visit the site and try checking weather conditions in several locations. Hopefully, you’re feeling better than me in Katowice today:


Open Weather Map API widget
Open Weather Map API widget (Large preview)

As you might have guessed, the website is calling the API to get the data. Developers implemented it in a way that every time you press search, behind the scenes the application knocks the API’s door and says “give me <city> temperature.”

Let’s put on a hacker hat and see the API calls this website is calling with your browser. You can use Developer Tools in your Browser to see what’s happening behind the scenes:

  1. In Chrome, go to Menu → More tools → Developer Tools;
  2. Switch to Network tab;
  3. Try checking temperature in different cities in the widget above;
  4. In the list on the bottom, you’ll notice links which have been called:

    Requests monitor in Chrome Developer Tools
    Requests monitor in Chrome Developer Tools (Large preview)

    If you copy the link, you can see it includes the location name and a couple of other parameters.

    https://openweathermap.org/data/2.5/find?callback=jQuery19103887954878001505_1542285819413&q=Katowice&type=like&sort=population&cnt=30&appid=b6907d289e10d714a6e88b30761fae22&_=1542285819418
      
  5. When you paste the link to the browser’s address bar, you should see the API replies with:
    jQuery19103887954878001505_1542285819413({"message":"accurate","cod":"200","count":1,"list":[{"id":3096472,"name":"Katowice","coord":{"lat":50.2599,"lon":19.0216},"main":{"temp":281.69,"pressure":1031,"humidity":61,"temp_min":281.15,"temp_max":282.15},"dt":1542285000,"wind":{"speed":3.6,"deg":50},"sys":{"country":"PL"},"rain":null,"snow":null,"clouds":{"all":90},"weather":[{"id":804,"main":"Clouds","description":"overcast clouds","icon":"04d"}]}]})
    
  6. It’s a bit chaotic, but if you take out the content of parentheses and run it with a data formatter, you’ll see a structure that makes sense:
    {  
       "message":"accurate",
       "cod":"200",
       "count":1,
       "list":[  
          {  
             "id":3096472,
             "name":"Katowice",
             "coord":{  
                "lat":50.2599,
                "lon":19.0216
             },
             "main":{  
                "temp":281.69,
                "pressure":1031,
                "humidity":61,
                "temp_min":281.15,
                "temp_max":282.15
             },
             "dt":1542285000,
             "wind":{  
                "speed":3.6,
                "deg":50
             },
             "sys":{  
                "country":"PL"
             },
             "rain":null,
             "snow":null,
             "clouds":{  
                "all":90
             },
    
  7. The reply from the API is a data structure with information about the current weather conditions — you should easily decrypt most of the parameters. This format of data is called JSON. This is an important notation because most of modern APIs use it. This pile of idents and brackets serves one purpose — it’s easier for an application to parse a well-structured message than a randomly placed text.

A word of explanation of what we’ve just done here.

The web application behind the Open Weather Map website takes the data from the API and displays it on the website.

Every time you type the city name and press search, the website connects to a server with a specific link which includes the name of the city as a parameter.

The same sentence in the tech jargon: the application behind the website sends a request to an API endpoint providing the name of the city as an argument.

Then, the API replies (sends an API response) with a text message formatted as JSON.


Webapp - Database diagram
Webapp <—> Database diagram (Large preview)

To create an API request you need to put together its address. Yeah, the address is a good analogy. To ship something you need to provide the courier with:

  • City,
  • Street and number,
  • Sometimes some extra information on how to get to your office.

And, to connect to the API, by analogy, you need:

  • https://openweathermap.org/ (link)
    The city or root-endpoint — a starting point, an internet address of a server you want to connect to, in our case.
  • data/2.5/find (link)
    The street number or the path — determines the resource you want to get from an API.
  • ?callback=jQuery19103887954878001505_1542285819413&q=Katowice&type=like&sort=population&cnt=30&appid=b6907d289e10d714a6e88b30761fae22&_=1542285819418 (link)
    The extra info or the query parameters — let the API server know what we want to get in particular and what structure and order the data should have.

This is how APIs are designed. The root-endpoint usually stays the same for a single vendor, then you need to figure out what path and query parameters are available and what information the API development team put behind them.

Now let’s put the hacker hat a bit tighter. In our case, not all query parameters are necessary to get the weather data. Try removing different parameters after the question mark (?) and check how the Weather API replies.

For example, you can start by removing callback from the request link:

https://openweathermap.org/data/2.5/find?callback=jQuery19103887954878001505_1542285819413&q=Katowice&type=like&sort=population&cnt=30&appid=b6907d289e10d714a6e88b30761fae22&_=1542285819418

The result:

https://openweathermap.org/data/2.5/find?q=Katowice&type=like&sort=population&cnt=30&appid=b6907d289e10d714a6e88b30761fae22&_=1542285819418

If you play around with the other ones, you can see that some of them are optional too. Actually only q and appid are mandatory:

https://openweathermap.org/data/2.5/find?q=Katowice&appid=b6907d289e10d714a6e88b30761fae22

How do you know what’s mandatory and what’s optional? How do you know where to get the root-endpoint and path in the first place?

API Documentation: A Must-Read Before You Start

You always need to check the API documentation first to learn how to construct your request the right way.

In our case, the documentation https://openweathermap.org/current shows the available endpoints. It also explains all response data fields — so you can find what information the API will reply even before you even send a request.

A good API documentation offers quick start tutorials on how to create simple requests and moves on to more advanced stuff. Fortunately, the Open Weather API has one and we’re going to use it now.

Creating An API Call From Scratch

Let’s sum up our findings. We’ve already sent a request to the API. We’ve figured out the correct link by sniffing what OpenWeatherMap does behind the scenes. This approach is called reverse-engineering and it’s often hard or not possible at all.

Moreover, most of the times, API providers ban users from over-using this option. That’s why we should learn how to “call” the API by the rules (meaning — documentation).

One way to do this is to code it. But as we’re not coders (yet!), we’re going to use tools that make this easier. So much easier that even software developers have it under their toolbelt.

As promised, we won’t leave the browser. But we need to install an extension (Chrome only) — Postman. This simple plugin turns your browser into an API connector.


Postman main view
Postman main view (Large preview)

OK, now that we have a tool, let’s take a look into the documentation to see how we can get current weather conditions for a specific city name https://openweathermap.org/current#name.

The docs say we should use the following endpoint: api.openweathermap.org/data/2.5/weather?q={city name}

When we break it down we get the following elements:

  • Root-endpoint: api.openweathermap.org
  • Path: data/2.5/weather
  • Query parameter: q={city name} (this notion means that we should replace the braces with a specific city name)

Let’s put it into Postman. The process boils down to three easy steps:

  1. Click on ‘Request’ in the top menu.

    Postman new request view
    Postman new request view (Large preview)
  2. Name your request and provide the catalog name in the section at the bottom as well.

    Postman request name view
    Postman request name view (Large preview)
  3. Paste the API endpoint you want to call, click Send, and you should see the API response in the Response section:

    Sending the first request with Postman
    Sending the first request with Postman (Large preview)

Congrats! You’ve just successfully called your fir… wait a second! Let’s pay attention to the API response:


Invalid response example
Invalid response example (Large preview)

It’s not a JSON filled with weather information we’ve seen before. What do the 401 and Invalid API key mean at all? Is our documentation wrong?

Authentication

You wouldn’t let anybody access your cocktail cabinet without your permission, would you? By the same token, API providers also want to control the users of their product to protect it from malicious activity. What’s malicious activity? For example, sending many API requests at the same time, which will “overheat” the server and cause downtime for other users.

How can you control the access? The same way as you guard your drinks! By using keys — API keys.

If you visit the How to start guide from Weather API documentation, you’ll notice how you can get your key. Sign up now and check your inbox.

So now the question is how to use the key? It’s easy, according to the docs, just copy and paste the key at the end of your endpoint URL (without braces).

api.openweathermap.org/data/2.5/weather?q=Katowice&appid={your API key}

And click send again. Here you go, we can now see the API response! 🙌


Successful response from Open Weather Map API
Successful response from Open Weather Map API (Large preview)

But there’s much more you can get from the API using Postman. Ready to become a true API hacker?

API parameters: Getting Tailored Responses

Usually, API endpoints have some utility features you can use to adjust the API response, e.g. if you need a better data format or you want to get the data in a particular order. These options are often hidden behind some parameters you can find in the documentation.

Query parameters are just a structured text you add at the endpoint address with the following pattern:

  1. A question mark (?) after the path,
  2. Name of a parameter,
  3. Equals (=) symbol,
  4. Value of the parameter,
  5. Ampersand (&) and others follow with points 2-4 (in this way you can add as many parameters as you want).

Take our first request as an example:

https://openweathermap.org/data/2.5/find?q=Katowice&appid=b6907d289e10d714a6e88b30761fae22

Important note: The order of query params doesn’t matter.

?q=Katowice&appid=b6907d289e10d714a6e88b30761fae22

The above is the same as the following:

?appid=b6907d289e10d714a6e88b30761fae22&q=Katowice

As mentioned, query params are described in API docs. The following excerpt from the weather API documentation shows you how to get the temperature in different units (imperial or metric):


OpenWeatherMap API documentation excerpt
OpenWeatherMap API documentation excerpt (Large preview)

Try sending these two options with Postman to see the difference in results. Remember to add your API key at the end of the endpoint address.

Note: Always take some time to study the documentation and find parameters which can save you or your development team some serious time.

API Request Options: How To Send Data To The API

So far, we’ve been getting information from the API. What if we want to add or modify information in the database behind the API? Request methods are the answer.

Let’s take a look at Postman once again. You might have noticed an upper-case GET label next to the API endpoint address. This represents one of four request methods. GET means we want to get something from the API (thanks captain) and it’s a default option. What are the other options?

Method Name What it does with the API
GET The API looks for the data you’ve requested and sends it back to you.
POST The API creates a new entry in the database and tells you whether the creation is successful.
PUT The API updates an entry in the database and tells you whether the update is successful.
DELETE The API deletes an entry in the database and tells you whether the deletion is successful.

Still confusing? Let’s move to examples.

API POST: How To Create A Record In The API

We can’t create or update anything with Weather API (because it’s meant to be read-only), so we need to find a different one for testing purposes.

Let’s come up with some more business-oriented example. We’re going to simulate the following scenario:

If it’s rainy, create a “cheer up” discount coupon for your customers.

We’re going to use Voucherify which provides an API for creating and tracking promotions for any e-commerce system.

Disclaimer: I’m a co-founder of Voucherify. I’m happy to answer your questions about designing and implementing digital promotions, and about our API of course. 🖐

We already know how to get them from the previous example, so let’s focus on creating a voucher:

  1. As we’ve said, we should always start with the documentation.
  2. The quick start guide tells us to get our API key.
    Note: Instead of creating an account, you can use the test keys from the quick start guide — we’ll show you how in a minute.
  3. Now, let’s find how to create a discount coupon. In Voucherify, this kind of promotion is represented as “voucher”.
  4. From the docs, you’ll learn that to create voucher, you need to call a POST method to /vouchers endpoint.
  5. Create a new Request in Postman.
  6. Change method to POST.

    Postman - API methods selection
    Postman – API methods selection (Large preview)
  7. Paste the Voucherify endpoint https://api.voucherify.io/v1/vouchers/ and click Send.

    Missing credentials
    Missing credentials (Large preview)
  8. Oh snap, we’re not authorized to call this endpoint. As you might have guessed, we need to provide API keys.

    Voucherify has a slightly different way of doing so. Instead of putting them as query params, you should put them to Headers. This is a common approach because it’s easier to implement and maintain keys this way rather than append them as query params.

    Add the keys as in the picture and click Send. Notice that Voucherify requires two keys. Here the ones you can use for the purpose of this tutorial:
    X-App-Id: 8a824b12-0530-4ef4-9479-d9e9b9930176
    X-App-Token: 9e322bac-8297-49f7-94c8-07946724bcbc


    Providing API keys in Postman
    Providing API keys in Postman (Large preview)
  9. We get another error message, this time it says the payload cannot be empty.

    Voucherify API returns error code 400
    Voucherify API returns error code 400 (Large preview)

    What the heck is a payload? As in the case of GET we want to retrieve some information, with POST we need to send something and the message we send is called the payload and it’s usually a JSON file.

    Now Voucherify API is complaining that we didn’t provide one, which means that it cannot create a voucher because we didn’t tell what kind of voucher it should create. So what now? Back to the docs!

  10. Let’s find what kind of information this request needs to succeed. We can see a lot of options on the list.

    Voucherify API documentation excerpt
    Voucherify API documentation excerpt (Large preview)

    One parameter (type) is required and another optional. Let’s say it’s going to be a 20% off discount, available for the first 100 customers, expiring today. Now we need to find parameters responsible for this discount features and put them together into a format understable to Voucherify API. As you can see in the examples above, the JSON notation you should use looks like this:

    {
        "type":"DISCOUNT_VOUCHER",
        "discount":{
            "percent_off":20.0,
            "type":"PERCENT"
        },
        "expiration_date":"2018-12-03T23:59:59Z",
        "redemption":{
            "quantity":100
        }
    
  11. To set up the payload in Postman, paste the JSON message into Body tab. Select “raw” type and JSON from the list of available payload formats and confirm with Send.

    POST method in Postman
    POST method in Postman (Large preview)
  12. Voila! Voucherify has created our 20% off discount coupon successfully (as we’re working with a test account, all generated codes start with the “voucherify.io-” prefix). The marketing team can now share the code with customers and Voucherify will automatically validate it whenever they come to your shop to redeem it.

    Voucherify returns 200 OK
    Voucherify returns 200 OK (Large preview)

    But how do we know it is a successful request? First of all, we can see that Voucherify has sent us a message which, according to their docs, looks like a correct API response. Secondly, Postman displays the status 200 OK — which means our request is successful. Why 200 and what’s the status?

API Status Codes And Error Messages

Most of the APIs you’ll ever interact with will be HTTP-based. The HTTP is a protocol which standardizes communication between various client applications and servers on the Internet.

One of the key elements of HTTP is status codes. By understanding the status code you (or actually systems you implement) can immediately tell what’s happened to your request. Chances are you faced one of the most popular status codes when you typed the wrong link — 404


Voucherify 404 error code
Voucherify 404 error code (Large preview)

But there are many more and end-users usually don’t see them. They range from 100+ to 500+. In general, the numbers follow the following rules:

  • 200+ means the request has succeeded;
  • 300+ means the request is redirected to another URL;
  • 400+ means an error that originates from the client application has occurred;
  • 500+ means an error that originates from the server has occurred.

If you could go through the steps once again, you would see that Voucherify replied with 401 Unauthorized when we didn’t provide API keys. Or 400 Bad Request when there was no payload which is required for Create Voucher request. Finally, we received 200 as a token of a successful API call.

If you’re curious about HTTP status codes meaning, there’s no better place than HTTP Cats (or this article maybe). 🙃


HTTP Cats 401 Unauthorized
HTTP Cats 401 (Large preview)

Summary

The growing amount of data and the need for speed in building products pushed APIs to become the lingua franca of digital teams. To design systems based on API-first systems, make sure you understand the vendors’ offerings. This hands-on testing guide is a good starting point in doing so. It will help you explore the API capabilities even before you throw it to your teaching team, saving their energy — and yours as well.

Further Reading

Smashing Editorial
(rb, ra, il)

Source: Smashing Magazine, Understanding API-Based Platforms: A Guide For Product Managers

How To Sound Like A Cloud Expert

dreamt up by webguru in Uncategorized | Comments Off on How To Sound Like A Cloud Expert

How To Sound Like A Cloud Expert

How To Sound Like A Cloud Expert

Zack Grossbart & Eduardo Abe



Your code is written and the design looks great. The new project is almost ready to go when the client asks, “Should this run in the cloud?”

You break out in a cold sweat. The question is massive. Regions and zones, high availability, load balancing — the cloud has its own language.

Don’t worry; you’ve got this. This article will teach you how to make smart decisions about the cloud, and answer your client’s cloud questions.

Four Big Questions

Before you and your client can know what kind of cloud you want, you need to discuss four questions:

  • How complex is the software you need to run?
  • How much does it need to scale?
  • How important is it that it never goes down?
  • How fast does it need to run for users around the world?

This article gives you the background and information you need to answer these questions and sound like a cloud expert.

Let’s begin.

  1. What Is A Cloud?
  2. Why Your Client Cares About The Cloud
  3. How Is A Cloud Different From A Hosting Service?
  4. Why Virtual Machines Matter So Much
  5. Let’s Talk A Little About Networking
  6. The Different Types Of Clouds
  7. The Basic Pieces Of A Cloud
  8. Questions Your Clients Are Likely To Ask

What Is A Cloud?

When we talk about cloud computing, we really mean the ability to rent a piece of a computer from someone else. That’s all there is to it.

Companies like Amazon and Google have a lot of computers and they’re willing to rent parts of them to you. Renting computers from them is cost-effective because you don’t have to build your own data centers or hire your own staff of experts to run them.

When you rent a part of a computer, you need it to look like a whole computer so you can run any software you want. That’s why providers give you a virtual machine (VM) — software that makes it look like you’re running on your own separate computer.

Why Your Client Cares About The Cloud

Before you learn more about the cloud, it’s important to understand why your client cares. Let’s not dismiss the allure of buzzwords; the cloud is really trendy right now. Your client may just be asking because all the cool kids are doing it, but there are reasons the cool kids are doing it.

Let’s start with the basics. Hosting your own data center would be a pain in the rear. You’d have to worry about power consumption, keeping your hardware up to date, hiring a team of experts to run it, and a thousand other problems that have nothing to do with your business. What would happen if the power went out, there was a flood, or the roof caved in? These are all reasons not to host your website on a server running in your living room.

Not only can you pass on all the headaches to someone else, but having them run the data center gives you three big advantages:

  1. Clouds are global.
    They exist in data centers around the world, including one near your client. That means speed. You don’t want customers in China waiting for data to load from the United States. When I go to Google.com, I get a different data center in Boston than I would in Chicago or L.A., and that’s just in the U.S. That’s a large part of what makes Google’s speed possible.
  2. Clouds grow and shrink.
    If I buy a server, I have one server; even if my app doesn’t need the whole computer, I still need to pay for that server. When my app gets really popular, I need to buy more servers fast. The cloud doesn’t work like that. Renting shares of servers means I can change how much I’m renting: I can scale up the order when I’m busy, and scale it down when I don’t need as much.
  3. Clouds never go down.
    Never say never…but almost never. Cloud providers talk about “five nines” — that means being up 99.999% of the time (with just 5.26 minutes of downtime a year). You can make that even smaller with services like load balancing and failover.

Those are all reasons clouds can be cool, but you can get some of them from a simple hosting service. If your client is asking about the cloud, you need to know the difference.

How Is A Cloud Different From A Hosting Service?

I have a personal website on a hosting service named Media Temple. My site runs WordPress, so it needs a few things:

  • A directory to put my files in
  • An HTTP server
  • A database
  • PHP

My directory runs on Linux, my HTTP server is Apache, my database is MySQL, and it all runs on PHP; that’s why they call it a LAMP stack (Linux-Apache-MySQL-PHP). That may sound like a lot of pieces, but they’re limited. For example, I can’t install new software. If I want to run my database on PostgreSQL, I’m out of luck. I can’t run other languages like Python or Go; I can’t write my own separate programs. I only get a very limited, pre-configured set of things I’m allowed to do.

My website also only runs on one server in one place. Where is that server? I have no idea. I think it’s somewhere in the United States, but other than that I don’t know, and I don’t really care. The hosting provider gives me a single server, I type in a URL, and my site comes up (most of the time).

Hosting providers keep it simple. Some of them host other stacks and some allow a little more configuration, but it’s always a set package.

The fundamental difference between a hosting service and a cloud is the virtual machine. A hosting service just gives me part of an existing operating system. A virtual machine gives me an entire operating system all to myself.

Why Virtual Machines Matter So Much

A virtual machine acts just like a real machine. It can run Linux or Windows and it can do anything a normal computer can do. Apple doesn’t allow you to run OS X on a virtual machine (although a few people have made it work, building a “Hackintosh”).

When you have a virtual machine you have total control. You can run anything you want there — databases, email servers, encryption, even searches for extraterrestrials. The virtual machine allows you to do anything you want.

Having an entire operating system all to yourself is really powerful, but before you can do anything useful you need to access the VM.

Let’s Talk A Little About Networking

Virtual machines are useless if you can’t get to them. You need networking, although networking can get a little complex.


Image of cloud network cables
Networking isn’t this difficult. (Large preview)

But these basics will give you what you need to get started. Let’s start with an example you’ll be familiar with. It’s probably running in your house right now.


Diagram of home network
My home network (Large preview)

I have Comcast at home. Comcast gives me a cable modem with an IP address like 10.0.0.89. While it only gives me one IP, I have two laptops, an iPad, and a phone; my wife and daughter have even more devices. To make that work, I have a wireless router that connects to my cable modem. My wireless router gives every device I have an IP address like 192.168.0.100, 192.168.0.101, and so on, but those addresses are private to my network.

The technical term for those private addresses is non-routable. There are a few addresses that are set up for private use; most start with 10. or 192.168, as a sign to Internet routers that these addresses aren’t allowed out in the wild. I’m using these special addresses because they’re reusable.

Every IP address must be unique in a given network; otherwise, the router wouldn’t know which computer I wanted to connect to. There are 4,294,967,296 possible IP addresses. Although network designers thought that was a lot when all of this started in the 1970s, we’re now running out. There are some other protocols, like IPv6, that may solve this problem in the future, but today we solve this problem with Network Address Translation (NAT). Let me show you how it works.

The devices in my house have addresses that are unique in my house, but not in the entire world. When I want to get out to the Internet, I need the wireless router to translate them for me. Every time I click a link, my laptop talks to my wireless router to make the request for me; in turn the wireless router talks to the world on my behalf, but using its own address. Then my cable modem does the same thing when it talks to Comcast, and Comcast does the same thing again on a much larger scale when it sends my request out to the general Internet. Each router is translating the IP address from the one before it. All of this means that many computers can reuse an IP address like 192.168.0.101, and it all works out without conflicts.

So what’s my real IP address on the real Internet? Right now my real IP address is 66.30.118.150 and my private IP address is 192.168.0.103.

Where did the address 66.30.118.150 come from? It’s an IP address that Comcast owns. It makes it clear that I’m in Cambridge, Massachusetts, in the northeastern United States. I don’t need the details; all I need to know is that it’s a real public address that Comcast manages for me. Comcast probably uses the same address for hundreds or thousands of other people.

All of this address translation does something else very important. It gives me a single point of access that controls everything that goes into my home network. Nobody outside can access 192.168.0.103; to them, it’s a different address. I get a lot of security by controlling what traffic can come in and out. My wireless router is a pinch point where I can control what happens.

Most clouds work just like my home network. The computers are virtual, networks are called subnets, and the wireless router is called a gateway, but it’s all the same thing. They have virtual machines with private addresses and gateways that translate them to public addresses. They also have a space of addresses that they can use just like my wireless router at home. Like this:


A diagram of a cloud network
My cloud network (Large preview)

It might sound complicated, but it’s not too hard. A cloud is a way for me to rent computers from someone else and set them up to look like my home network.

The most important things to remember are:

  • Private IPs are only available on your private network;
  • Public IPs are available on the Internet;
  • NAT allows private IPs to look like public IPs.

That’s not everything that happens with cloud networking, but it’s more than enough to get up and running and access your cloud. Now you need to decide what kind of cloud you want.

The Different Types Of Clouds

Cloud is an amorphous term — people use it to mean a lot of different things. There are really three different categories of clouds.

Infrastructure Clouds

The virtual machine and network are the building blocks for an infrastructure cloud, also known as Infrastructure as a Service (IaaS). They provide the virtualized infrastructure, which you have full control over. You decide on the operating system and everything else that runs on top of it.

You get flexibility and control, but you’re responsible for managing and supporting everything you install.

Infrastructure clouds are either static or elastic. A static cloud works like my home network: I have a set of virtual machines running whatever I need them to. They live on a private network, with a public gateway that grants them access to the Internet. Static clouds are great for processing data, having some extra computing power, or hosting a more complex site than a hosting provider can handle. You can also replicate your static cloud to other data centers around the world.

Elastic clouds work like static clouds, but they’re dynamic. Instead of a fixed set of virtual servers, you have a set that can grow or shrink depending on your needs. Your cloud expands when you have high demand on your site or service, and shrinks back to normal size when you don’t. All the expanding and shrinking saves you money. You give back the computing power you don’t need when you don’t need it.

Netflix uses IaaS. Amazon Web Services provides the infrastructure, and Netflix implements its entire system on top; it wrote its own software to provide highly customized support for streaming high-definition content. The Weather Company is another example — it runs on top of the IBM Cloud.

Platform Clouds

A platform cloud, also known as Platform as a Service (PaaS), is a specialized cloud that provides software building blocks for your application while the cloud provider manages the infrastructure and software stack for you.

For instance, if you needed a web application, PaaS could provide you with vanilla WordPress or Drupal for you to use. If you needed a database, you could pick MySQL or PostgreSQL. If you need development tools, you might choose from Node, Java, or PHP. You don’t need to worry what operating system is running, or whether a MySQL security patch needs to be applied — the cloud provider takes care of that for you.

Heroku is a PaaS cloud. It provides the software underneath, and you just write what you want on top of that. It gives you some flexibility, but does a lot more management than an IaaS cloud.

Software Clouds

A software cloud, also known as Software as a Service (SaaS), is a very specialized type of cloud that provides you with a well-defined online service. Hosting providers are a special type of SaaS cloud under the covers; they work in a very limited way.

Wix is another example of SaaS. It provides you a complete web application hosting service with a great editor, support for user login and payment, and a wide variety of templates to choose from. Wix focuses on just one job. It’s very limited in functionality, but also easier to use.

The Basic Pieces Of A Cloud

We’ve talked about the different types of clouds and why you need them. Before you can really sound like a cloud expert, you need to know the different components that make up a cloud.

Virtual Machines

A virtual machine is a software that runs like hardware, acting like a real machine without ever needing a separate server anywhere. You can do whatever you want with the VM, and it will pass the duck test — it walks and quacks like a real server. Your software will never know the difference.

This flexibility has a price. You’re responsible for maintaining your virtual machine. The cloud provider will make sure the hardware is in top condition, but you’ll have to select your operating system and everything else that runs on top of it — security patches, software updates, configuration. It’s all on you.

And if you’re not on an elastic cloud, you’d better remember to release any virtual machines you don’t need anymore. The provider will charge you whether the VM is doing work or not.

Subnets

We covered cloud networking already. Subnets are networks that run in the cloud.

Private IPs And Public IPs

The cloud network will assign a non-routable IP address to virtual machines in the subnet. Those are known as private IPs because they’re private to my network. When a virtual machine sends requests to the internet, the public gateway will translate those requests. That’s called egress traffic by the cloud providers.

Just as I can’t access my home printer from a coffee shop, in the cloud I need a public IP to access a virtual machine.

Public IPs can be assigned to virtual machines to allow ingress traffic (traffic coming from the Internet routed to virtual machines). That’s important because cloud providers will charge you differently for ingress and egress traffic.

SSH Keys

An SSH key is a piece of private information that allows you to access your cloud. It’s made up of two files. There’s a public key that looks like this:

ssh-rsa
AAAAB3NzaC1yc2EAAAADAQABAAABAQC5b8xmtjUd1taP4svy9FM/WZc/n5gkqKVkhIsqW27hw2WuhfTVNLA6IBBOs9+br+HlqGYwgYB3DSh0Zm/3Bok1uQhinH77FmKsrPGDpvtJv16weIvGiTMVp+Mct8DVKl48KZxvQKa0Hp6MxEc7cQ9WPvzWn9BPLHERSkSNwXSUobqpFBgIPy9UBWr5DsI2Li5HeMgMgTcbuVVdO/8I/rhKoIyTqkhY4CZcyssmWhMvPmk6+9IcOr0O4SyW9TL+CZgDH1mW2dUypT+1j6HgFjr9H8NfJ4EKnWnFkQXo8HZ4oh6lSTaIfDQfnbrjVUO14N7FW9ZgXbL9cJVx5FLw3ny9 you@them.com

And there’s a private key that looks like this:

-----BEGIN RSA PRIVATE KEY-----
MIIEogIBAAKCAQEAuW/MZrY1HdbWj+LL8vRTP1mXP5+YJKilZISLKltu4cNlroX0
1TSwOiAQTrPfm6/h5ahmMIGAdw0odGZv9waJNbkIYpx++xZirKzxg6b7Sb9esHiL
xokzFafjHLfA1SpePCmcb0CmtB6ejMRHO3EPVj781p/QTyxxEUpEjcF0lKG6qRQY
CD8vVAVq+Q7CNi4uR3jIDIE3G7lVXTv/CP64SqCMk6pIWOAmXMrLJloTLz5pOvvS
HDq9DuEslvUy/gmYAx9ZltnVMqU/tY+h4BY6/R/DXyeBCp1pxZEF6PB2eKIepUk2
iHw0H52641VDteDexVvWYF2y/XCVceRS8N58vQIDAQABAoIBAHU7UKW+m2X55Dui
zf0SqW5rXUtDwhOq6qTZhoGIvFjOBwKGfXosjRyyGJ0o6jyqvM1L4Q7ZUDXzg5fT
CwXIhAYKrFprRXvHcypnS2hHsKW27k3yZ6tkIX+XW+VT5fzdhCXUyKks3jcRBHtJ
ux7BI0kLGR02e6MSHYkowp47p1Auukx1saRkFTwvy+znABgqVETvtHBxAiElXndL
JfQntaQacgWWDjl2qUj+06IB/Qzd9/Mo1Vtdb8SUZxv/Qc2raSi3LL0N4aSJGLGU
pq395ggv9NdhUQf+DN9uGaOC4hYeGdO8gm27yysZ4rTT5iln5wOaCAcMTMrGL4+k
GoU/nKECgYEA7AP/Mh9sUi9AX/17a3A/zxNAO1ZrvM+Caj/X/t/pt3HEOhqLz7o5
z3g8/Z+H0CJLZNiP9XbMak2wvOiqRj0y+FihX/ESll6XgIEPTBUcFSirWMe4f9og
FltrnelUjHG9MTDW0P4jmmp1E5V8RgnCCv2VjN40ulP5zHPXXdU2FP8CgYEAySNs
/qlFL7DTB/A851y6cUzQC5kiKlr/T8aUtOHeBo626jlnHDy/VY9vIJ0ttsYyHCdM
OSdqZh5wRwvshr94tpOBQNnDTI4Xv7t2couHl7q2xTOYeWViwGyZaatNYlWWFh/u
YSCTd2jn6cvBZOZP3BAiWoF9nzLcsjfpNLdzAkMCgYAx8TaTOKsHSRBqP41aUspt
2zkAVW0+6vpB2Xivalpegyhu0yc6scGB8YOWd6eZl2g00s7DtnvTEtWPY/yEGHcs
rjSXxL+WKjYM70J5aw4iPBTmGH0mMNYRZQ8Ev1cw0PCj9B3A48ZM6rITjtJZT79L
7BU1Vd/6fcKiTPEJ3hAvqQKBgBKOQBnmR8m0iGNtGFFHzrNxIKhRQkOiDXewnDtr
su3r8Jf/H7INMKGWD+x0U6lO84SBY5jKOBifqkADq5hqxZoiVYREEq5XVX2Mr8q1
cJbg1MewkNpyLgAOhMCo2wS9XJFB9N3lAXW8qdh5waerT6a/nku3Mn2jVZTjb5I7
clK9AoGAZLuvLAJpFOf/mweajULV+oFMGzIArvbk1c+cGySeI5uZwfQ9lv2MOb0N
DuFTXZt6QpKV9Nix/8KgBIP2Vac6gSAeF6kIXk2+nV6gXm5tojYrf6gG1jY8ceRD
IFSeGlnBhYVrFcQ79fYwJtSQgGde4PtNF1yq9ipluAyLuy1cLUc=
-----END RSA PRIVATE KEY-----

You use these two files together instead of a password.

SSH public-key authentication is a robust way to log in to a remote system, more secure than the standard username and password. (SSH stands for “secure shell.”) You’ve probably seen SSH keys before in the form of your Github key.

SSH keys rely on public-key cryptography and challenge-response authentication to prevent brute-force attacks and other threats.

These keys ensure that only you have access to your virtual machines, and they work great with scripts and other automated tasks.

Data Centers

A data center is a big building full of computers. Cloud providers rent parts of them to you.


An image of a cloud data center taken at IBM
A cloud data center from IBM (Large preview)

The computers are powerful servers that can each host many virtual machines at the same time. These servers consume a lot of power and generate a lot of heat, and they need to be physically close together so the networking is fast. So a data center is the facility in which a cloud provider houses all the physical hardware that runs a piece of its cloud. The data center requires proper cooling, redundant power supply, massive network bandwidth, controlled access and skilled staff to keep all the machines running.

A data center can handle a lot of load, but it’s in just one place. A global cloud provider needs to spread data centers across the world to ensure that clients everywhere will have acceptable network-access latency to their servers.

Regions And Zones

A cloud provider needs to organize its data centers to ensure it can keep fulfilling requests during power outages, floods, hurricanes and other disasters. Providers call this Quality of Service, and it’s all part of making sure the cloud never goes down.

A region is a geographic area with a specific round-trip network latency (from where to where?). Regions have names like Dallas, Tokyo, or Frankfurt. You’re guaranteed to get the network latency you’re paying for inside that area.

A region is made up of one or more zones. Zones are an isolated data center in a region with independent electrical, mechanical and network infrastructure designed to guarantee no shared single point of failure between other zones. That enables you to build highly available fault tolerant applications by deploying to multiple zones in a region. The region can keep going if a zone crashes, but it’s really bad when all the zones go down.

A data center hosts a zone. Multiple data centers are clustered together to create a multi-zone region. Zones have names like us-south-1, us-south-2, and us-south-3. A small region can be served by a single robust data center, while a populous region might require multiple data centers to cope with the network and computing demand.

Disaster Recovery, High Availability, And Fault Tolerance

These concepts make even the most accomplished IT architects sweat. They stay awake at night wondering how they’ll make sure the cloud never goes down.

But you’ve got this! The cloud (almost) never goes down, and it’s easy to make sure you’re covered if it does. Let’s review these concepts one by one:

Disaster recovery (DR) is a set of policies and procedures detailing what to do during and after a major incident. Design your system so that it fails gracefully (meaning that your users will understand that something is wrong but you’ve got it covered), and after an incident, you know how to bring it back as quickly as possible. You need DR when a whole region goes down, your service is under cyber attack, or it’s vulnerable and you need to take it down.

High availability (HA) is but a goal — the “five nines” we talked about earlier. You want your application to be resilient to failure. This is nearly impossible if you run a server in your living room, but it’s doable with the cloud. You achieve the goal of high availability by relying on the cloud’s fault-tolerant infrastructure — everything’s taken care of for you.

A fault-tolerant infrastructure is a design that makes sure a backup takes over if something goes down. The cloud is a fault-tolerant system. If a zone within a region fails, the other zones will ensure continuity of service. You need to take advantage of that fault tolerance by using components like load balancers.

Load Balancers

High availability comes from running multiple instances of your application at the same time. A load balancer is a piece of equipment that will route traffic from your users to one of your instances that is live and well; it takes requests and sends them to the next healthy server.

The load balancer monitors the health of your virtual machines, and it can take different parameters into account. It can detect if your virtual machine or app crashes, but it can also check for network latency, specific data in the request headers and so much more.

Questions Your Clients Are Likely To Ask

So your application is running great, but your client is thinking about embracing the cloud. Are you ready for it? Here are common questions you should be able to answer to sound like a cloud expert.

When Do I Want More Than A Hosting Provider?

Your client might be engaged full speed ahead on migrating all services to the cloud. With greater flexibility, resiliency and a geographically distributed presence, the cloud makes it easy to get excited.

But even though the cloud is faster and more flexible and dependable, it’s also costlier; it might even require additional IT support to keep your client’s system running. The cloud is not for everyone.

If your client’s system is currently running on a hosting provider, should you move to the cloud?

Consider present and future needs: If your client is looking for high availability to comply with regulations, then move to the cloud. If your client is targeting a global audience, move to the cloud. If drastic surges in demand throughout the year are expected, move to the cloud.

If the system will be mostly accessed by local users within a specific geography, or if it’s not mission-critical to your client, then don’t move to the cloud.

The important takeaway: If your client’s system runs well on a hosting provider, you should consider existing SaaS offerings that would provide the resilience and performance of the cloud, while isolating your client from unnecessary IT expenses. There are traditional hosting providers like Bluehost offering cloud-based services, and there are cloud providers offering hosting services.

What Type Of Cloud Should I Use?

Finding the right type of cloud can be overwhelming, with each provider offering a wide range of services and options. The first things to consider are the complexity of your client’s software stack and the hardware it’s currently running on.

If the whole stack is highly customized with recompiled open-source libraries, customized Linux kernels or special storage optimizations, you should look at IaaS first. Some providers will allow your client to mix and match, using IaaS for highly customized components while picking PaaS for other parts.

However, if your client’s needs are mostly built on top of off-the-shelf libraries, PaaS is most likely a better starting point; the cloud provider will make sure your client’s code will always be running on top of up-to-date dependencies.

Once the right cloud model is identified, the requirements and evaluation criteria will be unique to your client. But it’s always worth paying attention to non-functional requirements: Does your client have to comply with domain-specific regulations, certifications or standards? Do they have any dependency on or partnership with specific vendors?

Finally, think about the cloud provider’s migration support, and vendor lock-in — how hard it is for your customer to migrate to a competitor once the system is running. Consider an exit plan even if your client is not planning to move out.

How Do I Make Sure My Site Never Goes Down?

Let’s say your client is working in the U.S. healthcare industry. HIPAA requires that every organization has some sort of disaster recovery plan, and that includes your website. Your client’s business will fail if the site goes down, so you need the resiliency of the cloud.

The cloud gives you the tools you need to ensure that your site never goes down. You’ll need multiple instances of your application running at the same time, and a way to control traffic so your users will never access a bad instance.

If your client is deploying your site on an IaaS cloud, you’ll need multiple instances of your application and a load balancer to control traffic. If your client has a PaaS cloud, just ensure that you have multiple instances running, as the cloud will provide the routing part automatically.

If your site uses a database, make sure that your client’s cloud is configured to support session affinity (also called sticky sessions), a way to ensure that all user traffic is routed to the same virtual machine.

How Do I Make My Site Fast For Everyone In The World?

If your client is going global and requires your application to provide fast service to end users around the world, then you need the flexibility and geographic reach of the cloud.

Even though the cloud is global, you need instances of your application to be running close to where your users are.

Work with your client to identify which cloud regions would best serve your user base, and where your client is hosting the APIs that support your site — your instances should be close to both. You might also discuss setting up a global proxy that routes traffic to different geographical areas based on the user’s location.

Conclusion

Providing scalable and resilient service to a growing customer base across the Internet is very complex; we’ve barely scratched the surface here. That’s why we want to leave the details to Amazon, Google, and the other cloud providers. Your job is to make good decisions about how to use their services.

Remember, when your client asks you if something should run in the cloud, you need to ask four questions:

  • How complex is the software you need to run?
  • How much does it need to scale?
  • How important is it that it never goes down?
  • How fast does it need to run for users around the world?

Start with those four simple questions, and you’ll sound like a cloud expert. Work with your clients to understand what the answers to these questions mean for them, considering the different types of cloud, and you’ll be a big part of solving their needs.

Smashing Editorial
(dm, ra, il)

Source: Smashing Magazine, How To Sound Like A Cloud Expert

How Web Designers Can Contribute To Mobile-First Marketing

dreamt up by webguru in Uncategorized | Comments Off on How Web Designers Can Contribute To Mobile-First Marketing

How Web Designers Can Contribute To Mobile-First Marketing

How Web Designers Can Contribute To Mobile-First Marketing

Suzanne Scacca



I recently wrote about why we should be working on retiring unnecessary design elements instead of adding more stuff to websites in this mobile-first world. And it got me thinking:

What about marketing? Is there even such a thing as mobile-first marketing?

In short, I believe so.

I’m going to examine the key areas of marketing that stem from the websites we build. Then, zero in on the ways in which designers should adjust these marketing strategies for mobile-friendly and mobile-first audiences.

How Web Designers Can Contribute To Mobile-First Marketing

There are typically three kinds of marketing strategies businesses utilize:

  1. Content marketing
  2. Email marketing
  3. Social media marketing

Similar to how we view web design through a mobile-first lens, a similar process must occur when designing marketing strategies going forward. That means not being afraid to toss out design or even text elements for the mobile experience. It also means taking tried-and-true marketing techniques and compressing them down so they’re more easily digestible for audiences on mobile.

Here are some things to consider as a web designer:

Content Marketing

Traditionally, content marketing has been synonymous with blogging. But, in recent years, this kind of marketing encompasses a much wider range of content creation as consumers demand information more quickly and conveniently than ever before. Many of these blog alternatives (like vlogs and podcasts) do well on mobile. They are easier to get ahold of on these devices and can be consumed at the user’s convenience wherever and whenever they please.

Regardless of the type of content you produce, there are certain ways it should be designed for the mobile-first audience.

Remove Sidebar Elements

To start, you need to ditch those cumbersome sidebar elements (something I talked about in my previous post). I like Airbnb’s example of this:


Airbnb mobile blog without sidebar
The Airbnb mobile blog greatly simplifies the sidebar. (Image source: Airbnb) (Large preview)

As you can see, the bottom of the post isn’t bogged down in a bunch of unnecessary sidebar widgets for mobile users to scroll through. There’s a link to related articles and that’s it.

This enables mobile users to really focus on the content before them and to absorb the message without a bunch of other distractions around.

Use Captivating Visuals For Longer Posts

If you have to write longer pages or posts (which isn’t necessarily a bad thing), make sure you include unique and captivating visuals throughout. Also, make sure they don’t overwhelm the screen.

This is a good example from The Guardian:


The Guardian featured image
The Guardian uses a unique illustration for its featured image. (Image source: The Guardian) (Large preview)

The featured image for the blog post is a custom-drawn illustration. If that didn’t impress readers enough, the matching illustrations placed at regular intervals throughout the post should:


The Guardian custom illustrations
The Guardian uses custom illustrations throughout its article. (Image source: The Guardian) (Large preview)

When your mobile site is as content-driven as The Guardian’s, you need to rely heavily on the visual component to support the story on the page. If videos or podcasts can’t completely replace a lengthy post, make sure images can aptly tell a story on their own as well as provide real support to the narrative told on the page.

“Design” The Text

For text-based content marketing, the structure and formatting of it are going to be a big deal.

Case in point: Powell’s Books:


Powell’s Books justified text
Powell’s Books uses justified text on the mobile site. (Image source: Powell’s Books) (Large preview)

What’s so surprising about this example is that it comes from a very well-known bookstore in the United States. You would think that a business that deals in the written word would take greater care in designing the words on its web pages.

There are a number of problems with the design. For starters, a supporting image has been included next to each of the books included in the roundup, but they’re small and don’t fit well beside the text. Then, you have the text itself which uses justified alignment. This introduces a bunch of awkward white space which makes the text difficult to read.

The big takeaway here is to take time to design the text on the page as much as you do the visuals. If visitors can’t read the content easily, your marketing will be ineffective.

Break Up Lengthy Text

It’s also important to be mindful of the length of the content on your pages. Take a look at this post from the Pitchfork website:


Pitchfork blog post on mobile
A Pitchfork blog post has no paragraph breaks. (Image source: Pitchfork) (Large preview)

Most of the paragraphs in this write-up look like this. Overly long sentences. Run-on paragraphs. You can’t even see an end to the paragraph in many cases.

Even if you’re not the writer of the content, find ways to break it up for the mobile user. Realistically, desktop websites should have a maximum limit of 3 or 4 lines for paragraphs. On mobile, let’s set that limit to 5 or 6. This ensures readers won’t become overwhelmed with content that seemingly has no end in sight.

I would suggest using the Smashing Magazine website for inspiration when designing the structure and length of marketing content:


Easy reading on Smashing Magazine
Smashing Magazine includes notes about posts and callouts to ease the mobile reading experience. (Image source: Smashing Magazine) (Large preview)

The top of each article includes a number of callouts that quickly summarize what you’re about to read. This sets expectations properly.

The rest of the article is just as well-designed:


Smashing Magazine post example
An example of a Smashing Magazine post design. (Image source: Smashing Magazine) (Large preview)

This example demonstrates how Smashing Magazine has taken the time to properly design the text on the page. A header tag is present. Hyperlinked text stands out well. Text is properly sized for mobile. It also flows naturally from left-to-right with a ragged edge which makes for easier reading. And it’s intercepted by an image/poll.

It might not seem like a big deal on desktop to lay out five or six paragraphs one after the other, but do take a minute to hop over to mobile and see how that translates to the smaller screen. You may be surprised at how much readability differs between the on-site experiences.

Keep Mobile Users Engaged

Getting people to convert on mobile isn’t easy.

But it’s not impossible to get around those traditionally low conversion rates. First, you need to acknowledge that mobile users have shorter attention spans. Check out this blog post from The Home Depot:


Home Depot blog CTAs
The Home Depot includes ever-present CTAs on the blog. (Image source: Home Depot) (Large preview)

This post serves as an inspirational tutorial for readers, providing them with ideas on how to remodel their own bathroom at home.

But notice the “Shop This Project” bar that sits along the bottom of the post. This is a fantastic idea. Rather than hope that mobile readers will be willing to read the entire post and click the links on their own, the Home Depot has made this nice and easy. This says:

“Hey, this is a great idea if you’re looking to remodel your home… but we get it if you’re short on time. Here are all the materials you need to do it on your own!”

It plays into the idea that the mobile user wants information to be succinct and convenient and also doesn’t have the time to ingest long pages of content — no matter how helpful they may be.

The Home Depot has another great example of how to keep mobile users with limited attention spans engaged:


Home Depot image Pins
The Home Depot makes images Pinnable. (Image source: Home Depot) (Large preview)

In the above example, you can see the Home Depot has made its images Pinnable. Why is this great? It’s because it embraces the multichannel experience.

Remember; we’re designing websites to be mobile-first, not mobile-all-encompassing.

Chances are good that the majority of your website visitors will start on mobile. It’s your job to ensure that one of two things happens:

If the journey ends on mobile, it ends with a conversion.

or

If the user doesn’t convert on mobile, you’ve provided them with a clear pathway to convert at a later time and from another device.

That’s what the Home Depot has done here by including “Pin It” buttons. Mobile users can save those images (and the links to the posts) to Pinterest for later. Then, when they’re ready to work on that home renovation project, they can open their laptop or tablet and pull up that cool-looking photo and supporting article to guide them through it.

Email Marketing

Without a doubt, email marketing is one of the most effective ways to get in front of a mobile audience and compel them to convert. A report from just a few years ago showed that 80% of respondents believed email marketing to be the key driver in terms of consumer acquisition and retention.

Although email marketing is highly effective in attracting quality customers, you have to be careful with it on mobile. Mobile users don’t think or act the same way as desktop users, as evidenced by the research around micro-moments.

That’s why, when it comes to designing email campaigns and newsletters for clients, you must keep the following tips in mind:

Keep It Simple

Unless your email subscriber list is expecting lengthy messages on a regular basis (and there’s a good reason for it), it’s best to keep email communications simple. That’s why we build websites – so that marketing messages can remain short and to the point and then link out to provide more information.

Here is an example from Chase of a message that went a little overboard:


Chase email marketing
An email marketing message from Chase. (Image source: Chase) (Large preview)

First of all, the message is a little misleading. It talks about the top tools you’re about to discover to manage your Chase account. However, the points broken out below it are nothing more than features included in the mobile app.

Another problem we have is the unevenly distributed text. When you design anything for the web, there should be symmetry. But it’s clear here that the copywriter and designer did not work together to craft the message.

Finally, there’s the linking issue. The goal of this message is to motivate Chase customers to download and use the mobile app. And, yet, there are two links to the Chase website that compete with the two app store links at the bottom.

Overall, there’s just too much going on in the design and message of this email. Chase would’ve been better off simplifying it with an eye-catching graphic and pushing traffic to the website to learn more and download the app from there.

Use Images

While I understand why some marketers think it’s a good idea to write email copy to look like messages people receive from others they know, I don’t think it’s a great idea on mobile.

Here’s an example of a message I received from Small Business Trends:


Small Biz Trends email
Small Business Trends sends text-only email marketing communications. (Image source: Small Business Trends) (Large preview)

When I receive messages that look like this (i.e. plain text, no images, free-standing links), my gut reaction is:

This is spam.

It’s not that this email is difficult to read. And, in all fairness, I should have been expecting it since I had signed up for a webinar. While it’s great that the message mostly fits within one screenful of my mobile device, I’m left seriously wanting.

If you intend to impress me with a webinar, why have you made the email such a lackluster experience?

This is a great argument for why images are helpful in convincing mobile email users to stop and read your marketing communication.

Even if images don’t make sense for the message, you should design a professional, branded template that clients can use to skin all messages. This helps guide users to the content framed within it. It also gives marketing messages a consistent and instantly recognizable look.

Make Sure They Can Read the Text

Earlier last year, I explored the role typography played in the mobile user experience. What’s not surprising at all is the fact that the size of the font can seriously hinder the reading experience. Let me show you a couple of examples that demonstrate this point in the context of email:

This is a marketing communication I received from Freelancers Union. I enjoy reading their blog, but I’ve never been a fan of the blog roundup newsletters:


Freelancers Union email marketing
Text is too small to read in Freelancers Union email. (Image source: Freelancers Union) (Large preview)

Some of the text is readable, for sure. “Today on Freelancers Union” as well as the title of the excerpt are both legible from where I’m sitting. The rest of the message, however, is quite problematic.

By my count, there are five different font sizes used in this message, not to mention varying styles and colors of hyperlinks included. When I see these messages in my inbox, I know now not to bother with trying to read them. I just go straight to the titles under “Excerpts”. I suspect I’m not the only one who does this.

Then, you have this example from Zendesk:


Zendesk email marketing
A Zendesk email falls short on typography. (Image source: Zendesk) (Large preview)

There’s a lot that’s wrong here.

For starters, the headline creates confusion. I thought the message was from Zendesk and, yet, the headline is all about Gartner and a superfluous report name I don’t recognize (at least I think it’s a report?). If there’s a Zendesk connection, it should be in the header. Even if there isn’t, the header should clearly explain why I should keep reading.

Then, there’s the issue of reading. Why the heck is the headline banner so big and the rest of the message in a font face that’s way too small for mobile? It’s almost as if they didn’t want anyone to read the rest.

Also, if we were to look at the hierarchy of the page, why didn’t the report button get more real estate? In terms of taking action on the page, that should be the primary target, but the design would not have you believe that’s the case.

Bottom line: size matters on mobile. If you’re not yet testing your mobile email marketing messages before sending them, do it now.

Tell the Story with an Image

Now that I’ve spent all that time sharing examples of poorly designed mobile newsletters, I want to show you one that’s done quite well.

This one comes to us courtesy of Sweet Frog:


Sweet Frog email marketing
Sweet Frog uses little text and a captivating image in email. (Image source: Sweet Frog) (Large preview)

I think the only gripe I have with this message is that the image didn’t come before the text. Otherwise, I think it’s well done.

The image looks great on mobile. It’s well-sized, well-designed, colorful and provides the message in as concise a manner as possible.

The designer has done a nice job with the text as well. The paragraph isn’t too long. Plus, all-caps is a good way to draw mobile users’ attention to the key part of the message. In other words, CONVERT HERE.

Send Relevant Emails

When we looked at the key micro-moments that take place on mobile, we focused on how users are looking to take quick and precise actions. When you craft an email marketing message for those users, you better make sure to keep those in mind as well.

One example I’m particularly fond of is this one from Hotels.com:


Hotels.com email marketing
Hotels.com sends a well-crafted CTA email. (Image source: Hotels.com) (Large preview)

Although there are a few distractors in the email template I’d like to see gone, I really do appreciate how pointed the message is. Hotels.com spells out in the subject line exactly what I’m going to get: 10% off.

Then, using a strong color and impossible-to-miss font, it quickly reiterates that all they want from me at this moment is to share a 10% off coupon. And with a blue button, just a couple paces below it, I can quickly grab my coupon code and explore their offers.

This is absolutely perfect. If you’re paying close attention to what users search for and end up doing when they’re on mobile, you should be able to craft messages like these with ease.

Look for ways to serve up a quick action like:

  • Use this coupon.
  • Watch this quick video.
  • Join us this weekend.
  • Etc.

The more you know about your audience and the things you can do to make them take immediate action, the more effective your mobile email marketing will be.

Social Media Marketing

Social media marketing has always been a kind of bite-sized form of marketing. Because of the limited amounts of space available within posts and the emphasis on communicating through videos and images, social media has always provided us with a more succinct and quick way to reach others.

That said, some people still haven’t figured out how to master it.

This is why I’m encouraging you as a web designer to step in and help. Here’s why:

Choose Images Wisely

If you think about the space available on a mobile screen, images and videos on social media are going to comprise a good majority of it. That’s why it’s critical to choose your visual content well.

I’m going to show you two examples from Lyft:


Lyft social media video
Lyft shares promotional, branded videos on Twitter. (Image source: Lyft) (Large preview)

The video in the top post is a great example of social media visuals done well. The video is beautifully shot and matches the branding of the company sharing it. It’s clear that great care was put into this piece of video marketing.

Then, you have this example also from the Lyft Twitter page:


Lyft social media photo
Lyft’s co-founder shares a photo on Twitter. (Image source: Lyft) (Large preview)

User-generated content can be a wonderful thing for a brand, especially if your company is young and your capacity to create content is somewhat limited. But Lyft is not a new company.

While I appreciate the co-founder’s efforts to show himself driving people around, his photos stand out like a sore thumb in the feed. In the case of this image, the lighting is poor, and many of the passengers get lost in the background. In other photos he’s shared and that Lyft has retweeted, everyone is out-of-focus.

Now, we could just chalk this up to: “Well, he’s not a professional photographer. We shouldn’t expected that much from him.” There’s just one problem with that:


Walt Disney World social media
An example of UGC from Walt Disney World. (Image source: Walt Disney World) (Large preview)

Walt Disney World’s Instagram feed is mostly comprised of photos and videos taken by visitors of the amusement park. The above example is one of them. And, yet, WDW takes good care in sharing photos that shed the best light on its brand.

As a general rule, whether you’re uploading your own photos, sharing them from others or designing them by hand, make sure you’re okay with them representing your brand. If they’re more appropriate for sharing on a personal account, leave them to the founder of the company to stick them on his or her feed. Keep your professional mobile feeds full of impressive content.

Consider The Space

Although social media typically limits the amount of space one can use to share a post, there are some channels where these limits have been stretched. When considering the “space” your post takes, be sure to go beyond just the physical dimensions of the image or text.

Consider this example from New York Magazine:


New York Magazine Instagram
New York Magazine Instagram post example. (Image source: New York Magazine) (Large preview)

This is a post from New York Magazine’s Instagram feed. What I like about this is that they’ve designed the image to include the title, byline and featured image similar to how it would appear on the website’s news feed. That attention to things like consistency from the website to social media is fantastic.

But now let’s scroll down:


New York Magazine Instagram caption
New York Magazine writes lengthy captions for Instagram posts. (Image source: New York Magazine) (Large preview)

This is the caption you’ll find beneath the image.

Here’s the thing: we already know that this post is promoting a new article on the website. The title is incredibly clear what the post is about and the featured image drives the point home.

There’s no reason to make Instagram users stop scrolling through their news feed to read this lengthy description. It’s just a waste of space and energy at this point. The post could’ve just as easily said:

“Do you have an unhealthy diet? Click the link in our bio to find out about this dangerous trend.”

Another reason why you need to be conscious of space is this:


A description of what the image shows for alt text
A caption to be shown below the image (Image source: New York Magazine) (Large preview)

Instagram now has this feature for businesses where they can aggregate a bunch of their post links in one place. This is great considering it was difficult to share content in the past years since links weren’t clickable.

That said, this is yet another step New York Magazine readers have to take to get to that unhealthy eating post. Pay attention to how much work you’re asking mobile readers to do and determine whether or not the payoff is really worth it.

Use Hashtags (and Wisely)

There are certain social media channels where hashtags aren’t really needed and could potentially add friction where you don’t want any. I would say that Facebook and LinkedIn are two such examples of channels where you don’t need them.

That said, as a brand using social media for the purposes of marketing, you’re going to want to use hashtags (#) and handles (@) when crafting posts on places like Instagram, Pinterest, and Twitter. Content can be difficult to find on these channels without them, which is why you can’t afford to go without as Zaftig’s has done here:


Zaftig’s social media
Zaftig’s social media post without hashtags. (Image source: Zaftig’s) (Large preview)

Zaftig’s is one of my favorite restaurants in Boston. It also happens to be a lot of other people’s favorite restaurant as there’s almost always a wait whenever you go. So, I’m surprised to see such an underwhelming post on Instagram.

The photo is great. It reminds me of the many delicious meals I had here. But why no hashtags? A photo like this and a restaurant with such a great reputation in Boston should be able to leverage hashtags to bolster their reputation and extend their reach.

As someone who follows food inspiration Instagram pages, I can tell you I find new places to eat all the time with hashtags as simple as #breakfast. This is a huge missed opportunity for them.

On the other hand, it is possible to go overboard with hashtags on social media.

This is a Twitter post from The Yum Yum Foodie:


Yum Yum Foodie on Twitter
A Twitter post from The Yum Yum Foodie. (Image source: The Yum Yum Foodie) (Large preview)

In this example, you can see that there are too many hashtags throughout. This is problematic for a couple reasons:

  • Hashtags are like links in that they stand out from the surrounding text. When they’re included in the middle of a post, it disrupts the reading flow.
  • These particular hashtags aren’t really click-worthy. I would also suggest they don’t add any value to the post. Again, they’re more a distraction than anything.

As a designer, I’d ask you to look at hashtags and handles similar to how you would a CTA on a website. You know they’re going to stand out from the rest of the text, so make them worth the social media users’ time to look at. If their reason for being isn’t to click, at least infuse them with some of the brand personality.

If there’s no value in hashtagging a word or string of words in a post, don’t do it.

Format Your Posts

Blog posts and emails aren’t the only things where attention to formatting and balance matters. Although an empty message box on Twitter or Facebook might make you want to just type and type and type away, there’s more you can do to enhance the readability of your posts. I’d argue that proper formatting also makes your posts more attractive to the eye.

If you want more mobile users to stop and take a look at what you’ve posted, take a page out of Dr. Bronner’s book:


Dr. Bronner on Twitter
Dr. Bronners’ well-formatted Twitter post. (Image source: Dr. Bronner) (Large preview)

Okay, so the image itself is attention-grabbing, so this company gets major kudos for that.

But have a look at how the post itself is structured.

There’s a one-line sentence that introduces the promotion, topped off with a cute emoji. Emojis are to social media what icons are to websites. Don’t be afraid to use them, especially if your target audience is millennial or Gen-Z.

Then, you have a numbered list! Twitter and other social channels might not make this as easy to write out as a word processor, but so what? Look at how nice that looks? When compared to most posts that usually follow the tired paragraph format, this is sure to stand out and get mobile users to take a look.

Wrapping Up

If you’ve been entrusted to make critical design choices for your clients’ websites, then it’s time to leverage that trust and move into marketing with them.

While the design piece might not be as time- or labor-intensive as what you’d do on websites, online marketing is an ongoing thing. This could open up a new, steady income stream if you can demonstrate your knowledge and expertise in designing for marketing just as well as you did for the web.

Smashing Editorial
(ra, il)

Source: Smashing Magazine, How Web Designers Can Contribute To Mobile-First Marketing