Dyslextiskt Kebabstuk

Smashingmagazine

Prenumerera på Nyhetsflöde Smashingmagazine
Recent content in Articles on Smashing Magazine — For Web Designers And Developers
Uppdaterad: 1 månad 3 veckor sedan

iOS Performance Tricks To Make Your App Feel More Performant

tis, 02/05/2019 - 13:00
iOS Performance Tricks To Make Your App Feel More Performant iOS Performance Tricks To Make Your App Feel More Performant Axel Kee 2019-02-05T13:00:00+01:00 2019-02-21T15:03:59+00:00

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.

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!

Getting workflow just right ain’t an easy task. So are proper estimates. Or alignment among different departments. That’s why we’ve set up “this-is-how-I-work”-sessions — with smart cookies sharing what works well for them. A part of the Smashing Membership, of course.

Explore Smashing Membership ↬ 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 :

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.

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).

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 (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:

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.

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.

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.

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. (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.

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 (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 (jd, ra, il)
Kategorier: Amerikanska

A Guide To CSS Support In Browsers

mån, 02/04/2019 - 12:00
A Guide To CSS Support In Browsers A Guide To CSS Support In Browsers Rachel Andrew 2019-02-04T12:00:00+01:00 2019-02-21T15:03:59+00:00

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.

Front-end is messy and complicated these days. That's why we publish articles, printed books and webinars with useful techniques to improve your work. Even better: Smashing Membership with a growing selection of front-end & UX goodies. So you get your work done, better and faster.

Explore Smashing Membership ↬ 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%; } 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%; } 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; } }

See the Pen Feature Queries and Grid by (Rachel Andrew) on CodePen.

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.

(il)
Kategorier: Amerikanska

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

fre, 02/01/2019 - 13:00
Using Vue.js To Create An Interactive Weather Dashboard With APIs Using Vue.js To Create An Interactive Weather Dashboard With APIs Souvik Sarkar 2019-02-01T13:00:18+01:00 2019-02-21T15:03:59+00:00

(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:

(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. (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!

(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> <div id="app"></div> <script src="/dist/build.js"></script> </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 <script> tag

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

<!DOCTYPE html> <html lang="en"> <head> <meta http-equiv="Content-Type" content="text/html;charset=utf-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css"> <link rel="stylesheet" type="text/css" href="src/css/style.css"> <title>Weather Dashboard</title> <script src="https://maps.googleapis.com/maps/api/js?key=AIzaSyC-lCjpg1xbw-nsCc11Si8Ldg2LKYizqI4&libraries=places"></script> </head> <body> <div id="app"></div> <script src="/dist/build.js"></script> </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).

(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> <div id="app"> <p>This component’s code is in {{ filename }}</p> </div> </template> <script> export default { data() { return { filename: 'App.vue' } }, methods: { }, computed: { }, } </script> <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.

(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> <div> <p>This child components of Content.vue are:</p> <ul> <li v-for="child in childComponents">{{ child }}</li> </ul> </div> </template> <script> export default { data () { return { childComponents: ['TempVarChart.vue', 'Highlights.vue'] } }, methods: { }, computed: { }, } </script> <style> </style>

In the code, carefully observe and understand the following:

  • Within the <script> 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 <template> tag (where we write some HTML template), the thing of interest is the <ul>.
    • 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 <li> 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 <Content> 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). <Content v-bind:weather_data=“weather_data”></Content>

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:

<template> <div id="app"> <p>This component’s code is in {{ filename }}</p> <Content v-bind:weather_data="weather_data"></Content> </div> </template> <script> 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: { }, } </script> <style> </style> (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> <script> export default { props: ["weather_data"], data () { return { // data here } }, } </script> <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> <div id="pagecontent"> // other template code here {{ weather_data }} </div> </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> <div id="pagecontent"> <p>This child components of Content.vue are:</p> <ul> <li v-for="child in childComponents">{{ child }}</li> </ul> {{ weather_data }} </div> </template> <script> export default { props: ["weather_data"], data () { return { childComponents: ['TempVarChart.vue', 'Highlights.vue'] } }, methods: { }, computed: { }, } </script> <style> #pagecontent { border: 1px solid black; padding: 2px; } </style> (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:

(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> <div id="pagecontent"> <p>This child components of Content.vue are:</p> <ul> <li v-for="child in childComponents">{{ child }}</li> </ul> {{ weather_data }} <temp-var-chart :tempVar="tempVar"></temp-var-chart> <today-highlights :highlights="highlights"></today-highlights> </div> </template> <script> 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: { }, } </script> <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> <div id="tempvarchart"> <p>Temperature Information:</p> {{ tempVar }} </div> </template> <script> export default { props: ["tempVar"], data () { return { } }, methods: { }, computed: { }, } </script> <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> <div id="highlights"> <p>Weather Highlights:</p> {{ highlights }} </div> </template> <script> export default { props: ["highlights"], data () { return { } }, methods: { }, computed: { }, } </script> <style> </style>

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

(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> <div id="highlights"> <p>Weather Highlights:</p> {{ highlights }} <uv-index :highlights="highlights"></uv-index> <visibility :highlights="highlights"></visibility> <wind-status :highlights="highlights"></wind-status> </div> </template> <script> 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: { }, } </script> <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> <div id="uvindex"> <p>UV Index: {{ uvindex }}</p> </div> </template> <script> export default { props: ["highlights"], data () { return { uvindex: this.highlights.uvindex } }, methods: { }, computed: { }, } </script> <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> <div id="visibility"> <p>Visibility: {{ visibility }}</p> </div> </template> <script> export default { props: ["highlights"], data () { return { visibility: this.highlights.visibility, } }, methods: { }, computed: { }, } </script> <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> <div id="windstatus"> <p>Wind Status:</p> <p>Speed — {{ speed }}; Direction — {{ direction }}</p> </div> </template> <script> export default { props: ["highlights"], data () { return { speed: this.highlights.windstatus.speed, direction: this.highlights.windstatus.direction } }, methods: { }, computed: { }, } </script> <style> </style>

After adding the code for all the components, take a look at the web page on 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> <div style="position: relative;"> <temp-var-chart :tempVar="tempVar"></temp-var-chart> <today-highlights :highlights="highlights"></today-highlights> </div> </template> <script> import TempVarChart from './TempVarChart.vue'; import Highlights from './Highlights.vue'; export default { props: ['highlights', 'tempVar'], components: { 'temp-var-chart': TempVarChart, 'today-highlights': Highlights }, } </script>

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> <div> {{ tempVar.tempToday }} </div> </template> <script> export default { props: ["tempVar"], components: {}, data() { return { }; }, methods: { }, }; </script> <style> </style> Highlights.vue <template> <div> <uv-index :highlights="highlights"></uv-index> <visibility :highlights="highlights"></visibility> <wind-status :highlights="highlights"></wind-status> </div> </template> <script> 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: { }, } </script> <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> <div> <p>Visibility: {{ visibility }}</p> </div> </template> <script> export default { props: ["highlights"], data () { return { visibility: this.highlights.visibility.toString() } }, methods: { }, computed: { }, } </script> <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> <div> <p>Wind Speed — {{ windSpeed }}</p> <p>Wind Direction — {{ derivedWindDirection }}, or {{ windDirection }} degree clockwise with respect to true N as 0 degree.</p> </div> </template> <script> export default { props: ["highlights"], data () { return { windSpeed: this.highlights.windStatus.windSpeed, derivedWindDirection: this.highlights.windStatus.derivedWindDirection, windDirection: this.highlights.windStatus.windDirection } }, methods: { }, computed: { }, } </script> <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> <div id="app"> <dashboard-content :highlights="highlights" :tempVar="tempVar"></dashboard-content> </div> </template> <script> 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: { }, } </script> <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:

(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 <script> in App.vue.

<div id="search"> <input id="location-input" type="text" ref="input" placeholder="Location?" @keyup.enter="organizeAllDetails" > <button id="search-btn" @click="organizeAllDetails"> <img src="./assets/Search.svg" width="24" height="24"> </button> </div>

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.

<div id="info"> <div class="wrapper-left"> <div id="current-weather"> {{ currentWeather.temp }} <span>°C</span> </div> <div id="weather-desc">{{ currentWeather.summary }}</div> <div class="temp-max-min"> <div class="max-desc"> <div id="max-detail"> <i>▲</i> {{ currentWeather.todayHighLow.todayTempHigh }} <span>°C</span> </div> <div id="max-summary">at {{ currentWeather.todayHighLow.todayTempHighTime }}</div> </div> <div class="min-desc"> <div id="min-detail"> <i>▼</i> {{ currentWeather.todayHighLow.todayTempLow }} <span>°C</span> </div> <div id="min-summary">at {{ currentWeather.todayHighLow.todayTempLowTime }}</div> </div> </div> </div> <div class="wrapper-right"> <div class="date-time-info"> <div id="date-desc"> <img src="./assets/calendar.svg" width="20" height="20"> {{ currentWeather.time }} </div> </div> <div class="location-info"> <div id="location-desc"> <img src="./assets/location.svg" width="10.83" height="15.83" style="opacity: 0.9;" > {{ currentWeather.full_location }} <div id="location-detail" class="mt-1"> Lat: {{ currentWeather.formatted_lat }} <br> Long: {{ currentWeather.formatted_long }} </div> </div> </div> </div> </div>

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 <script> part in any .vue file — it roughly contains the following:

<script> // 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 } </script>

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:

<script> // 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 } </script> 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.length; i++) { if ( windDir >= 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_lat = (-1 * (Math.round(coordinates.lat * 10000) / 10000)).toString() + '°S'; } else { this.currentWeather.formatted_lat = ( Math.round(coordinates.lat * 10000) / 10000 ).toString(); } // Remember to beautify long for N/S if (coordinates.long > 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> <script> // import statements export default { data() { return { // data objects here } }, methods: { // methods here }, mounted: function(){ // function body here }, } </script> <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> <div id="ancestor"> <div class="container-fluid" id="app"> <div class="row"> <div id="sidebar" class="col-md-3 col-sm-4 col-xs-12 sidebar"> <div id="search"> <input id="location-input" type="text" ref="input" placeholder="Location?" @keyup.enter="organizeAllDetails" > <button id="search-btn" @click="organizeAllDetails"> <img src="./assets/Search.svg" width="24" height="24"> </button> </div> <div id="info"> <div class="wrapper-left"> <div id="current-weather"> {{ currentWeather.temp }} <span>°C</span> </div> <div id="weather-desc">{{ currentWeather.summary }}</div> <div class="temp-max-min"> <div class="max-desc"> <div id="max-detail"> <i>▲</i> {{ currentWeather.todayHighLow.todayTempHigh }} <span>°C</span> </div> <div id="max-summary">at {{ currentWeather.todayHighLow.todayTempHighTime }}</div> </div> <div class="min-desc"> <div id="min-detail"> <i>▼</i> {{ currentWeather.todayHighLow.todayTempLow }} <span>°C</span> </div> <div id="min-summary">at {{ currentWeather.todayHighLow.todayTempLowTime }}</div> </div> </div> </div> <div class="wrapper-right"> <div class="date-time-info"> <div id="date-desc"> <img src="./assets/calendar.svg" width="20" height="20"> {{ currentWeather.time }} </div> </div> <div class="location-info"> <div id="location-desc"> <img src="./assets/location.svg" width="10.83" height="15.83" style="opacity: 0.9;" > {{ currentWeather.full_location }} <div id="location-detail" class="mt-1"> Lat: {{ currentWeather.formatted_lat }} <br> Long: {{ currentWeather.formatted_long }} </div> </div> </div> </div> </div> </div> <dashboard-content class="col-md-9 col-sm-8 col-xs-12 content" id="dashboard-content" :highlights="highlights" :tempVar="tempVar" ></dashboard-content> </div> </div> </div> </template> <script> import Content from './components/Content.vue'; export default { name: 'app', props: [], components: { 'dashboard-content': Content }, 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: '' }, } }; }, methods: { // Some utility functions 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(' '); }, 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(' '); }, 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 }; }, fahToCel: function(tempInFahrenheit) { var tempInCelcius = Math.round((5 / 9) * (tempInFahrenheit — 32)); return tempInCelcius; }, milibarToKiloPascal: function(pressureInMilibar) { var pressureInKPA = pressureInMilibar * 0.1; return Math.round(pressureInKPA); }, mileToKilometer: function(miles) { var kilometer = miles * 1.60934; return Math.round(kilometer); }, 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.length; i++) { if ( windDir >= wind_directions_array[i].minVal && windDir <= wind_directions_array[i].maxVal ) { wind_direction = wind_directions_array[i].direction; } } return wind_direction; }, // Some basic action oriented functions 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(); }, 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"); } }); }); }, // Some basic asynchronous functions 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_lat = (-1 * (Math.round(coordinates.lat * 10000) / 10000)).toString() + '°S'; } else { this.currentWeather.formatted_lat = ( Math.round(coordinates.lat * 10000) / 10000 ).toString(); } // Remember to beautify long for N/S if (coordinates.long > 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(); } }, 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!'); } }, // Get and set functions; often combined, because they are short // For basic info — left panel/sidebar 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); } }, // For Today Highlights 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 ); }, // top level for info section 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(); }, organizeTodayHighlights: function() { // top level for highlights this.getSetUVIndex(); this.getSetVisibility(); this.getSetWindStatus(); }, // topmost level orchestration organizeAllDetails: async function() { // top level organization await this.fetchWeatherData(); this.organizeCurrentWeatherInfo(); this.organizeTodayHighlights(); this.getSetHourlyTempInfoToday(); }, }, mounted: async function() { this.location = "New York"; await this.organizeAllDetails(); } }; </script>

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!

(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> <div> <fusioncharts :attribute_1="data_object_1" :attribute_2="data_object_2" … … ... > </fusioncharts> </div> </template> <script> 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 } } }; </script> <style> // component specific special CSS code here </style>

Let’s understand different parts of the above pseudocode:

  • In the <template>, within the top level <div> (that’s pretty much mandatory for the template HTML code of every component), we have the custom component <fusioncharts>. 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 <fusioncharts> 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 <script>, 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 <fusioncharts>. The values assigned to the data objects are the values that the attributes of <fusioncharts> 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:

  • <template>
    As explained previously, the <fusioncharts> 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.
  • <script>
    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 <fusioncharts>. 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 (Large preview) <template> <div class="custom-card header-card card"> <div class="card-body pt-0"> <fusioncharts type="spline" width="100%" height="100%" dataformat="json" dataEmptyMessage="i-https://i.postimg.cc/R0QCk9vV/Rolling-0-9s-99px.gif" dataEmptyMessageImageScale=39 :datasource="tempChartData" > </fusioncharts> </div> </div> </template> <script> 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<br><hr><b>$dataValue</b>", 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 < this.tempVar.tempToday.length; i++) { var dataObject = { label: this.tempVar.tempToday[i].hour, value: this.tempVar.tempToday[i].temp }; data.push(dataObject); } this.tempChartData.data = data; }, }, mounted: function() { this.setChartData(); }, watch: { tempVar: { handler: function() { this.setChartData(); }, deep: true }, }, }; </script> <style> </style> src/components/UVIndex.vue

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

(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 class="highlights-item col-md-4 col-sm-6 col-xs-12 border-top"> <div> <fusioncharts :type="type" :width="width" :height="height" :containerbackgroundopacity="containerbackgroundopacity" :dataformat="dataformat" :datasource="datasource" ></fusioncharts> </div> </div> </template> <script> 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 } } }; </script> src/components/Visibility.vue

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

(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 class="highlights-item col-md-4 col-sm-6 col-xs-12 border-left border-right border-top"> <div> <fusioncharts :type="type" :width="width" :height="height" :containerbackgroundopacity="containerbackgroundopacity" :dataformat="dataformat" :datasource="datasource" > </fusioncharts> </div> </div> </template> <script> 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 } } }; </script> 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:

(Large preview) <template> <div class="highlights-item col-md-4 col-sm-6 col-xs-12 border-top"> <div> <div class="card-heading pt-5">Wind Status</div> <div class="row pt-4 mt-4"> <div class="col-sm-6 col-md-6 mt-2 text-center align-middle"> <p class="card-sub-heading mt-3">Wind Direction</p> <p class="mt-4"><img src="../assets/winddirection.svg" height="40" width="40"></p> <p class="card-value mt-4">{{ highlights.windStatus.derivedWindDirection }}</p> </div> <div class="col-sm-6 col-md-6 mt-2"> <p class="card-sub-heading mt-3">Wind Speed</p> <p class="mt-4"><img src="../assets/windspeed.svg" height="40" width="40"></p> <p class="card-value mt-4">{{ highlights.windStatus.windSpeed }} km/h</p> </div> </div> </div> </div> </template> <script> export default { props: ["highlights"], components: {}, data() { return {}; }, methods: {}, computed: {} }; </script> 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> <div class="custom-content-card content-card card"> <div class="card-body pb-0"> <div class="content-header h4 text-center pt-2 pb-3">Highlights</div> <div class="row"> <uv-index :highlights="highlights"></uv-index> <visibility :highlights="highlights"></visibility> <wind-status :highlights="highlights"></wind-status> </div> </div> </div> </template> <script> 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, }, }; </script> Deployment And Source Code

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

(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.

(ms, ra, il)
Kategorier: Amerikanska

How To Sound Like A Cloud Expert

mån, 01/28/2019 - 12:00
How To Sound Like A Cloud Expert How To Sound Like A Cloud Expert Zack Grossbart & Eduardo Abe 2019-01-28T12:00:47+01:00 2019-02-15T11:33:02+00:00

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

Front-end is messy and complicated these days. That's why we publish articles, printed books and webinars with useful techniques to improve your work. Even better: Smashing Membership with a growing selection of front-end & UX goodies. So you get your work done, better and faster.

Explore Smashing Membership ↬ 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.

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.

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:

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.

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.

(dm, ra, il)
Kategorier: Amerikanska

Table Design Patterns On The Web

tors, 01/24/2019 - 13:30
Table Design Patterns On The Web Table Design Patterns On The Web Huijing Chen 2019-01-24T13:30:57+01:00 2019-02-12T19:05:42+00:00

Tables are a design pattern for displaying large amounts of data in rows and columns, making them efficient for doing comparative analysis on categorical objects. Tables have been used for this purpose as early as the 2nd century and when the world started to go digital, tables came along with us.

It was inevitable that the web would support the display of data in a tabular format. HTML tables present tabular data in a semantic and structurally appropriate manner. However, the default styles on HTML tables are not exactly the most aesthetically pleasing thing you’ve ever seen. Depending on the desired visual design, some effort was required on the CSS front to prettify those tables. A decade ago, an article with the “Top 10 CSS Table Designs” was published on Smashing Magazine, and it still continues to get a lot of traffic to this day.

The web has evolved a lot over the past decade, and it’s more convenient than ever to make your site or application adapt to the viewport it is viewed in. That being said, we have to continue to make considered design choices that do not compromise on accessibility. Since tables don’t seem to be falling out of favor anytime soon, let’s see how tables can be created on the web in 2019.

Front-end is messy and complicated these days. That's why we publish articles, printed books and webinars with useful techniques to improve your work. Even better: Smashing Membership with a growing selection of front-end & UX goodies. So you get your work done, better and faster.

Explore Smashing Membership ↬ CSS-Only Options

If your dataset isn’t that large, and features like pagination and sorting are not necessary, then consider a JavaScript-free option. You can get some pretty nice results that work well on a whole gamut of screen sizes without the added weight of a large library.

Unfortunately, without the help of JavaScript for some DOM manipulation on the accessibility front, we only have a handful of CSS-only options. But for small data sets, they are often sufficient.

Option 1: Do Nothing

We’re going to start off with a low-effort scenario. If your data fits in a table with only a few columns and lots of rows, then such a table is pretty much mobile-ready to begin with.

A table with a few columns and many rows displayed on narrow and wide screens (Large preview)

The issue you’d have is probably having too much room on wider screens, so it might be advisable to “cap” the maximum width of the table with a max-width while letting it shrink as necessary on a narrow screen.

See the Pen Table #1: Few columns, many rows by (Chen Hui Jing) on CodePen.

This sort of a pattern works best if your data itself isn’t lines and lines of text. If they are numeric, or short phrases, you can probably get away with not doing much.

Option 2: Style The Scroll

David Bushell wrote up his technique for responsive tables back in 2012, which involved invoking overflow and allowing users to scroll to see more data. One could argue that this isn’t exactly a responsive solution, but technically, the container is responding to the width of the viewport.

When styling tables, allow users to scroll to see more data. (Large preview)

Let’s look at the “basic” overflow first. Imagine me using air-quotes around basic, because the styling for the scrolling shadows is anything but. Still, in this instance, “basic” refers to the fact that the table does not transform in any way.

See the Pen Table #3: Style the scroll (basic) by Chen Hui Jing on CodePen.

This technique for doing scrolling shadows comes from Roma Komarov and Lea Verou riffing off each other’s ideas to create magic. It hinges on using multiple gradients (linear and radial) as background images on the containing element, and using background-attachment: local to position the background relative to its contents.

What’s nice about this technique is that for browsers that don’t support scrolling shadows, you can still scroll the table as per normal. It doesn’t break the layout at all.

Another scrolling option would be to flip the table headers from a row configuration to a column configuration, while applying a horizontal scroll onto the <tbody> element’s contents. This technique leverages Flexbox behavior to transform the table’s rows into columns.

See the Pen Table #3: Style the scroll (flipped headers) by Chen Hui Jing on CodePen.

By applying display: flex to the table, it makes the <thead> and <tbody> both flex children, which are by default laid out next to each other on the same flex line.

We also make the <tbody> element a flex container, thus making all the <tr> elements within it flex children laid out in a single flex line as well. Lastly, every table cell has to have their display set to block instead of the default table-cell.

The advantage of this technique is that the headers are always in view, like a fixed header effect, so users don’t lose context as they scroll through the columns of data. One thing to take note of is that this technique results in a discrepancy of the visual and source order, and this makes things slightly unintuitive.

Sprinkle On Some JavaScript

As mentioned earlier, layout options that involving morphing the table by modifying display values sometimes have negative implications for accessibility, which require some minor DOM manipulation to rectify.

In addition, there are a number of user experience tips when it comes to designing data tables from Andrew Coyle, including features like pagination, sorting, filtering, and so on (features that do require some JavaScript to enable).

If you’re working with a relatively simpler dataset, perhaps you would like to write your own functions for some of these features.

Rows To Blocks, With Accessibility Fix

As far as I know of, this responsive data table technique came about from the CSS-Tricks article “Responsive Data Tables” by Chris Coyier back in 2011. It has since been adapted and expanded upon by many others.

The gist of this technique is to make use of a media query to switch the display property of the table element and its children to block on a narrow viewport.

Collapsing rows into blocks (Large preview)

On a narrow screen, the table headers are visually hidden, but still exist in the accessibility tree. By applying data attributes to the table cells, we can then display labels for the data via CSS, while keeping the content of the label in the HTML. Please refer to the CodePen below for how the mark-up and styles look like:

See the Pen Table #2: Rows to blocks by Chen Hui Jing on CodePen.

The original method applies a width on the pseudo-element displaying the label text, but that means you’d need to know the amount of space your label needed to begin with. The above example uses a slightly different approach, whereby the label and data are each on opposite sides of their containing block.

We can achieve such an effect via auto-margins in a flex formatting context. If we set the display property for each <td> element to flex, because pseudo-elements generate boxes as if they were immediate children of their originating element, they become flex children of the <td>.

After that, it’s a matter of setting margin-right: auto on the pseudo-element to push the cell’s content to the far end edge of the cell.

Another approach doing the narrow viewport layout is using a combination of Grid and display: contents. Please note that display: contents in supporting browsers has issues with accessibility at the moment, and this method shouldn’t be used in production until those bugs are fixed.

But maybe you’re reading this after those bugs have been resolved, in that case, here’s an alternative layout option.

See the Pen Table #2: Rows to blocks (alt) by Chen Hui Jing on CodePen.

Each <tr> element is set to display: grid, and each <td> element is set to display: contents. Only the immediate children of a grid container participate in a grid formatting context; in this case, it’s the <td> element.

When display: contents is applied to the <td>, it gets “replaced” by its contents, in this case, the pseudo-element and the <span> within the <td> become the grid children instead.

What I like about this approach is the ability to use max-content to size the column of pseudo-elements, ensuring that the column will always be the width of the longest label, without us having to manually assign a width value for it.

In future, when the sizing values of min-content, max-content and fit-content (covered by the CSS Intrinsic & Extrinsic Sizing Module Level 3) are supported as general width and height values, we’ll have even more options for laying things out.

The downside to this approach is you do need that additional <span> or <p> around the content in your table cell if it didn’t have one already, otherwise, there’d be no way to apply styles to it.

Simple Pagination

This example shows a basic pagination implementation that was modified off this CodePen by Gjore Milevski to paginate on table rows instead of divs. It is an extension of the “style the scroll” example discussed in the previous section.

See the Pen Table #4: Simple pagination by Chen Hui Jing on CodePen.

From a layout perspective, Flexbox comes in very handy for positioning the pagination elements across various viewport sizes. Different project designs will have different requirements, but the versatility of Flexbox should allow you to cater for these differences accordingly.

In this case, the pagination is centred on the page and above the table. The controls for navigating backward and forward are placed on either side of the page indicators on wider screens, but all four appear above the page indicators on narrow screens.

We can do this by levaraging the order property. Visual reordering of content should always be done with consideration because this property does not change the source order — only how it appears on screens.

Simple Sorting

This example shows a basic sorting implementation modified off this code snippet by Peter Noble to cater for both text and numerals:

See the Pen #Table 5: Simple sorting by Chen Hui Jing on CodePen.

It would be useful to have some sort of indicator of which column is currently being sorted and in what order. We can do that with the addition of CSS classes which can then be styled however you want. In this case, the indicator symbols are pseudo-elements that are toggled when the target header is clicked.

Simple Search

This example is a basic filtering functionality that iterates through all the textual content of each table cell and applies a CSS class to hide all rows that do not match the search input field.

See the Pen Table #6: Simple filtering by Chen Hui Jing on CodePen.

Such an implementation is relatively naive, and if your use case calls for it, it might make sense to search per column instead. In that scenario, it might be a good idea to have each input field as part of the table in their respective columns.

Let A Library Handle It

The above JavaScript snippets serve to demonstrate how tables with larger amounts of data can be enhanced to make life easier for users of those tables. But with really large datasets, it might probably make sense to use an existing library to manage your large tables.

The column toggle pattern is one whereby non-essential columns are hidden on smaller screens. Normally, I’m not a fan of hiding content simply because the viewport is narrow, but this approach by Maggie Costello Wachs of Filament Group resolves that qualm of mine by providing a drop-down menu which allows users to toggle the hidden columns back into view.

The above article was published back in 2011, but Filament Group has since developed a full suite of responsive table plugins known as Tablesaw, which includes features like sorting, row selection, internationalization and so on.

The column toggle feature in TableSaw also no longer depends on jQuery, unlike the examples from the original article. Tablesaw is one of the only libraries I could find that does not have a dependency on jQuery at the moment.

Wrapping Up

There is a myriad of table design patterns out there, and which approach you pick depends heavily on the type of data you have and the target audience for that data. At the end of the day, tables are a method for the organization and presentation of data. It is important to figure out which information matters most to your users and decide on an approach that best serves their needs.

Further Reading (ra, il)
Kategorier: Amerikanska

Designing Emotional Interfaces Of The Future

ons, 01/23/2019 - 13:00
Designing Emotional Interfaces Of The Future Designing Emotional Interfaces Of The Future Gleb Kuznetsov 2019-01-23T13:00:12+01:00 2019-02-12T19:05:42+00:00

Emotions play a vital role in our decision-making process. One second of emotion can change the whole reality for people engaging with a product.

Humans are an emotionally driven species; we choose certain products not because of what makes sense, but because of how we think they will make us feel. The interfaces of the future will use the concept of emotions within the foundation of product design. The experiences that people use will be based both on intellectual quotient (IQ) and emotional quotient (EQ).

This article is my attempt to look into the future and see what interfaces we will design in the next ten years. We’ll be taking a closer look at the three mediums for interaction:

  1. Voice
  2. Augmented Reality (AR)
  3. Virtual Reality (VR)
Developing For Virtual Reality

It’s not that difficult to create content for VR nowadays. Still, if you’re looking for a way to get a better understanding of VR development, working a demo project can help. Learn more →

Our new book, in which Alla Kholmatova explores how to create effective and maintainable design systems to design great digital products. Meet Design Systems, with common traps, gotchas and the lessons Alla has learned over the years.

Table of Contents → Practical Examples Of Future Emotional Interfaces

How will interfaces look like in the future? Even though we do not have an answer to this question just yet, we can discuss what characteristics interfaces might have. In my opinion, I’m sure that we will eventually move away from interfaces full of menus, panels, buttons, and move towards more ‘natural interfaces’, i.e. interfaces that extend our bodies. The interfaces of the future will not be locked in a physical screen, but instead they will use the power of all five senses. Because of that, they will require a less learning curve — ideally, no learning curve at all.

The Importance Of EQ Emotional Intelligence In Business

Apart from making the experience more natural and reducing the learning curve, designing for emotion has another benefit for product creators: it improves user adoption of the product. It’s possible to use humans’ ability to act on emotions to create better user engagement.

Voice Interfaces That Feel Real

Products that use voice as the primary interface are becoming more and more popular. Many of us use Amazon Echo and Apple Siri for daily routine activities such as setting an alarm clock or making an appointment. But a majority of voice interaction systems available on the market today still have a natural limitation: they do not take user emotions into account. As a result, when users interact with products like Google Now, they have a strong sense of communicating with a machine — not a real human being. The system responds predictably, and their responses are scripted. It’s impossible to have a meaningful dialogue with such a system.

But there are some completely different systems available on the market today. One of them is Xiaoice, a social chatbot application. This app has an emotional computing framework at its core; the app is built on the idea that it’s essential to establish an emotional connection with the user first. Xiaoice can dynamically recognize emotion and engage the user throughout long conversations with relevant responses. As a result, when users interact with Xiaoice they feel like they’re having a conversation with a real human being.

The limitation of Xiaoice is that it’s a text-based chat app. It’s evident that you can achieve a more powerful effect by making voice-based interactions (the human voice has different characteristics such as a tone that can convey a powerful spectrum of emotions).

Many of us have seen the power of voice-based interactions in the film “Her” (2013). Theodore (the main character played by Joaquin Phoenix) fell in love with Samantha (a sophisticated OS). This also makes us believe that one of the primary purposes of voice-based systems of the future will be a virtual companion to users. The most interesting thing about this film is that Theodore did not have a visual image of the Samantha — he only had her voice. To build that kind of intimacy, it’s essential to generate responses that reflect a consistent personality. This will make the system both predictable and trustworthy.

Technology is still a long away from a system like Samantha, but I believe that voice-first multimodal interfaces will be the next chapter in the evolution of voice-enabled interfaces. Such interfaces will use voice as a primary way of interaction and provide additional information in a context that creates and builds a sense of connection.

An example of a voice interface designed for Brain.ai (Image credit: Gleb Kuznetsov) The Evolution Of AR Experience

Augmented Reality (AR) is defined as a digital overlay on top of the real world and transforms the objects around us into interactive digital experiences. Our environment becomes more ‘intelligent’ and users have an illusion of ‘tangible’ objects on the tips of their fingers, which establishes a deeper connection between a user and a product (or content).

Reimagine Existing Concepts Using AR

The unique aspect of AR is that it gives us an extraordinary ability to physically interact with digital content. It allows us to see things that we could not see before and this helps us learn more about the environment around us. This AR property helps designers to create new level experiences using familiar concepts.

For example, by using mobile AR, it’s possible to create a new level of in-flight experience that allows a passenger to see detailed information about her class or current flight progress:

AR in flight experience for Airbus A380. (Image credit: Gleb Kuznetsov)

AR helps us find our way through spaces and get the required information at a glance. For example, AR can be used to create rich contextual hints for your current location. The technology known as SLAM (Simultaneous Localization And Mapping) is perfect for this. SLAM allows real-time mapping of an environment and also makes it possible to place multimedia content into the environment.

There are massive opportunities for providing value to users. For example, users can point their devices at a building and learn more about it right there on their screens. It significantly reduces the effort and allows for an emotional experience of ease by allowing navigation and access.

Providing additional information in context (Image credit: Gleb Kuznetsov)

The environment around us (such as walls or floors) can become a scene for interactivity in ways that used to be limited to our smartphones and computers.

The concept that you see below does just that; it uses a physical object (white wall) as a canvas for the content usually delivered using a digital device:

The concept of interactive walls — a digital overlay on top of the real world. (Image credit: Gleb Kuznetsov) Avoiding Information Overload

Many of us saw the video called “HYPER-REALITY”. In this video, the physical and digital worlds have merged, and the user is overwhelmed with a vast amount of information.

Technology allows us to display several different objects at the same time. When it’s misused, it can easily cause overload.

Information overload is a serious issue that has a negative impact on user experience and avoiding it will be one of the goals of designing for AR. Well-designed apps will filter out elements that are irrelevant to users using the power of AI.

Advanced Personalization

Personalization in digital experience happens when the system curates the content or functionality to users’ needs and expectations in real time. Many modern mobile apps and websites use the concept of personalization to provide relevant content. For example, when you visit Netflix, the list of movies you see is personalized based on your interests.

AR glasses allow creating a new level of personalization, i.e. an ‘advanced’ level of personalization. Since the system ‘sees’ what the user sees, it’s possible to utilize this information to make a relevant recommendation or provide additional information in context. Just imagine that you’ll soon be wearing AR glasses, and the information that is transferred to your retina will be tailored to your needs.

Here’s a foretaste of what’s in store for us:

Moving From Augmented Reality Towards Virtual Reality To Create An Immersive Experience

AR experience has a natural limitation. As users, we have a clear line between us and content; this line separates one world (AR) with another (real world). This line causes a sense that the AR world is clearly not real.

You probably know how to solve this limitation, i.e. with virtual reality (VR), of course. VR is not exactly a new medium, but it’s only been in the last few years that technology has reached a point where it allowed designers to create immersive experiences.

Immersive VR experiences remove the barrier between the real world and digital. When you put on a VR headset, it’s difficult for your brain to process whether the information that you are receiving is real. The idea of how VR experiences can look in the nearest future is well explained in the movie “Ready Player One”:

Here is what designers need to remember when creating immersive virtual environments:

  1. Write a story
    Meaningful VR has a strong story at its core. That’s why before you even start designing for a VR environment, you need to write a narrative for the user journey. A powerful tool known as a ‘storyboard’ can help you with that. Using a storyboard, it’s possible to create a story and examine all the possible outcomes. When you examine your story, you will see when and how to use both visual and audio cues to create an immersive experience.
  2. Create a deeper connection with a character
    In order to make users believe that all things around them in VR are real, we need to create a connection with the characters played by the users. One of the most obvious solutions is to include a representation of users’ hands in the virtual scene. This representation should be of actual hands — not just a rigged replica. It’s vital to consider different factors (such as gender or skin color) because it’ll make interactions more realistic.
    A user can look at his or her hands and see themselves appear as a character. (Source: leapmotion)
    It’s also possible to bring some objects from real life to a VR environment in order to create this connection. For instance, a mirror. When the user looks at a mirror and sees their character in the reflection, it enables more realistic interactions between the user and virtual characters.
    A virtual reality user looks into a virtual mirror and sees himself as a character in a VR environment. Credits: businesswire. (Large preview)
  3. Use gestures instead of menus
    When designing immersive VR experiences, we can’t rely on traditional menus and buttons. Why? Because it is relatively easy to break a sense of immersion by showing a menu. Users will know that everything around them is not real. Instead of using traditional menus, designers need to rely on gestures. The design community is still in the process of defining a universal language for using gestures, and taking part in this activity is fun and exciting exercise. The tricky part is to make gestures familiar and predictable for users.
    Hovercast VR menu is an attempt to reuse existing concepts of interaction for VR experience. Unfortunately, this concept can break the sense of immersion. New medium requires a new model of interaction.
  4. Interact with elements in the VR environment
    To create an environment that feels real, we need to give the user the ability to interact with objects in that reality. Ideally, all objects in the environment can be designed in a way that allows users to touch and inspect them. Such objects will act as stimuli and will help you create a more immersive experience. Touch is extremely important for exploring the environment; the most important information that babies get in the first days is received through touch.
  5. Share emotion in VR
    VR has a real opportunity to become a new level of social experience. But to make it happen, we need to solve one significant problem, i.e. bring the non-verbal cues into the interaction.

    When we interact with other people, a significant part on information that we get comes from body language. Surprise, disgust, anger — all these emotions are in our facial expressions, and during face-to-face interactions, we infer information from the eye region. It’s important to provide this information when people interact in a VR environment to create more realistic interactions.

    The good news is that the head-mounted devices (HMDs) will soon cover emotion recognition. Almost any area of human-to-human interaction will benefit from facial expressions in VR.
    Sharing emotions in VR space (Source: Rachel Metz of MITReview)
  6. Design sound and music suitabke for a VR environment
    Audio is a huge component of the immersive experience. It’s impossible to create a genuinely immersive experience without designing sound for the environment. The sound can both be used as a background element (i.e., ambient sound of wind) or directional. In the latter case, the sound can be used as a cue — by playing with directionality (where the sound comes from) and distance (it’s possible to focus user attention on particular elements).

    When it comes to designing audio for VR, it’s essential to make the sound 3D. 2D sound doesn’t work for VR very well because it makes everything too flat. The 3D sound is the sound that you can hear in every direction around you — front, behind, above and beyond — all over the place. You don’t need specialized headphones to experience 3D sound; it’s possible to create it using standard stereo speakers of HMD.

    Head tracking is another critical aspect of a good sound design. It’s vital to make sounds behave in a realistic manner. That’s why when a user moves his head, the sound should change according to the head movement.
  7. Prevent motion sickness
    Motion sickness is one of the primary pain-points in VR. It’s a condition in which a disagreement exists between visually perceived movement and the vestibular system’s sense of movement. It’s vital to keep users comfortable while they experience VR.

    There are two popular theories what causes motion sickness in VR:
    • ‘Sensory Conflict’ Theory
      According to this theory, motion sickness occurs as a result of a sensory disagreement between expected motion and motion that is actually experienced.
    • ‘Eye Movement’ Theory
      In the book “The VR Book: Human-Centered Design For Virtual Reality”, Jason Jerald mentions that motion sickness occurs because of the unnatural eye motion that is required to keep the scene’s image stable on the retina.
    Here are a few tips that will help you prevent users from reaching for the sickbag:
    • Physical body movement should match with visual movement. Sometimes even a small visual jitter can have an enormously negative impact on the experience.
    • Let users rest between moving scenes (this is especially important when the VR experience is really dynamic).
    • Reduce virtual rotations.
Conclusion

When we think about the modern state of product design, it becomes evident that we are only at the tip of the iceberg because we’re pretty limited to flat screens.

We’re witnessing a fundamental shift in Human-Computer Interaction (HCI) — rethinking the whole concept of digital experience. In the next decade, designers will break the glass (the era of mobile devices as we know them today) and move to the interfaces of the future — sophisticated voice interfaces, advanced ARs, and truly immersive VRs. And when it comes to creating a new experience, it’s essential to understand that the only boundary we have are our brains telling us it’s got to be how it’s always been.

(cc, ra, yk, il)
Kategorier: Amerikanska

Monthly Web Development Update 1/2019: Rethinking Habits And Finding Custom Solutions

fre, 01/18/2019 - 12:40
Monthly Web Development Update 1/2019: Rethinking Habits And Finding Custom Solutions Monthly Web Development Update 1/2019: Rethinking Habits And Finding Custom Solutions Anselm Hannemann 2019-01-18T12:40:24+01:00 2019-02-05T13:07:49+00:00

What could be better than starting the new year with some new experiments? Today I figured it was time to rethink JavaScript tooling in one of my projects. And since we wrote everything in plain ECMAScript modules already, I thought it would be easy to serve them natively now and remove all the build and transpilation steps. Until I realized that — although we wrote most code ourselves — we have a couple of third-party dependencies in there and, of course, not all of them are ECMAScript modules. So for now, I have to give up my plans to remove all the build steps and continue to bundle and transpile things, but I’ll try to figure out a better solution to modernize and simplify our tooling setup while providing a smaller bundle to our users.

Another experiment: Just a few weeks ago I had to build a simple “go to the top of the page” button for a website. I used requestAnimationFrame and similar stuff to optimize event handling, but today I found a way nicer and more efficient solution that uses IntersectionObserver to toggle the button on the viewport. You will find that article in the JavaScript section below. The reason I wanted to share these little stories is because I believe that the most important thing is that we review our habits and current solutions and see whether there are better, newer, simpler ideas that could improve a product. Keep playing, keep researching, and be sure to rethink existing systems from time to time.

News UI/UX Web Performance JavaScript Passwordless authentication? The WebAuthn API makes it possible. (Image credit) CSS Una Kravet’s “super underline” example uses randomly generated underlines for each element. Made possible with Houdini and the Paint API. (Image credit) Work & Life
  • “Feeling a sense of accomplishment is an important part of our sense of self-worth. Beating up on yourself because you think you could have accomplished more can dent your confidence and self-esteem and leave you feeling depleted at the end of the day.” Lisa Evans shares what we can do to avoid falling into that trap.
  • Itamar Turner-Trauring shares his thoughts on how to get a job with a good work-life balance when you’re competing against people who are willing to work long hours.
  • Is it a good idea to provide healthcare and treatment based on digital products like apps? And if so, what are the requirements, the standards for this? How can we ensure this is done ethically correct? How do we set the limits, the privacy boundaries, how far do we allow companies to go with experiments here? Would personalized content be fine? Is it okay to share data collected from our devices with healthcare providers or insurances? These are questions we will have to ask ourselves and find an individual answer for.
  • This article about how Millenials became the burnout generation hit me hard this week. I see myself in this group of people described as “Millenials” (I do think it affects way more people than just the 20-year-olds) and I could relate to so many of the struggles mentioned in there that I now think that these problems are bigger than I ever imagined. They will affect society, politics, each individual on our planet. Given that fact, it’s crazy to hear that most people today will answer that they don’t have a friend they could talk to about their fears and anything else that disturbs them while two decades ago the average answer was still around five. Let’s assure our friends that we’re there for them and that they can talk to us about tough things. 2019 should be a year in which we — in our circle of influence — make it great to live in a human community where we can think with excitement and happiness about our friends, neighbors, and people we work with or talk to on the Internet.
  • We all try to accommodate so many things at the same time: being successful and productive at work, at home, with our children, in our relationships, doing sports, mastering our finances, and some hobbies. But we blindly ignore that it’s impossible to manage all that on the same level at the same time. We feel regret when we don’t get everything done in a specific timeframe such as at the end of a calendar year. Shawn Blanc argues that we should celebrate what we did do instead of feeling guilty for what we didn’t do.
Going Beyond…
  • There are words, and then there are words. Many of us know how harmful “just” can be as a word, how prescriptive, how passively aggressive it is. Tobias Tom challenges whether “should” is a useful word by examining the implicit and the result of using it in our daily language. Why “should” can be harmful to you and to what you want to achieve.
  • “We all know what we stand for. The trick is to state our values clearly — and to stand by them,” says Ben Werdmuller and points out how important it is to think about your very own red line that you don’t want to cross regardless of external pressure you might face or money you might get for it.
  • Exciting news for climate improvement this week: A team of arborists has successfully cloned and grown saplings from the stumps of some of the world’s oldest and largest coast redwoods, some of which were 3,000 years old and measured 35 feet in diameter when they were cut down in the 19th and 20th centuries. Earlier this month, 75 of the cloned saplings were planted at the Presidio National Park in San Francisco. What makes this so special is the fact that these ancient trees can sequester 250 tons of carbon dioxide from the atmosphere over their lives, compared to 1 ton for an average tree.
  • The ongoing technological development and strive to build new services that automate more and more things make it even more critical to emphasize human connection. Companies that show no effort in improving things for their clients, their employees, or the environment will begin to struggle soon, Ryan Paugh says.
  • We usually don’t expect much nice news about technology inventions from the car industry and their willingness to share it with others. But Toyota now has decided to share their automated safety system ‘Guardian’ with competitors. It uses self-driving technology to keep cars from crashing. “We will not keep it proprietary to ourselves only. But we will offer it in some way to others, whether that’s through licensing or actual whole systems,” says Gill Pratt from the company.

Thank you for reading! I’m happy to be back with this new edition of my Web Development Update in 2019 and grateful for all your ongoing support. It makes me happy to hear that so many people find this resource helpful. So if you enjoyed it, please feel free to share it with people you know, give me feedback, or support it with a small amount of money. —Anselm

(cm)
Kategorier: Amerikanska

Grabbing Visual Attention With The Visual Cortex

tors, 01/17/2019 - 13:15
Grabbing Visual Attention With The Visual Cortex Grabbing Visual Attention With The Visual Cortex Susan Weinschenk 2019-01-17T13:15:03+01:00 2019-02-05T13:07:49+00:00

(This is a sponsored post.) You are designing a landing page. The goal of the page is to get people to notice, and hopefully click on a button on the screen to subscribe to a monthly newsletter. “Make sure the button captures people’s attention” is the goal you’ve been given.

So how, exactly, do you do that?

Research on the visual cortex in the brain can give you some ideas. The visual cortex is the part of the brain that processes visual information. Each of the senses has an area of the brain where the signals for that sensory perception are usually sent and processed. The visual cortex is the largest of the sensory cortices because we are very visual animals.

Recommended reading: What Is The Role Of Creativity In UX Design?

The Pre-Attention Areas Of The Visual Cortex

There are special areas of the visual cortex that process visual information very quickly. These are called the “pre-attention” areas because they process information faster than someone may realize they’ve even noticed something visually.

Within the visual cortex are four areas called V1, V2, V3 and V4. These are the “pre-attention” areas of the visual cortex, and they are dedicated to very small and specific visual elements.

Let’s take a look at each one:

Orientation

If one item is oriented differently than others, then it is noticed right away:

(Large preview) Size And Shape

If one item is either a different size or shape than others then it is noticed right away:

(Large preview) Color

If one item is a different color than others around it then it is noticed right away:

(Large preview) Movement

If one item moves in quickly, especially if it zooms in from starting at a small size and then becoming larger quickly (think tiger running quickly towards you), that grabs attention.

But Only One At A Time

The interesting, not immediately obvious factor here is that if you use these factors together at the same time then nothing really attracts attention.

(Large preview)

If you want to capture attention then, pick one of the methods and use it only.

Take a look at the two designs presented below. Which one draws your attention to the idea that you should enroll?

(Large preview) (Large preview)

Obviously, the image that has just one color area draws your attention more, rather than the one area that is color.

The Fusiform Facial Area

The pre-attention areas of the visual cortex are not the only visual/brain connection to use. Another area of the brain you can tap to grab attention on a page could be the Fusiform Facial Area (or FFA).

The FFA is a special part of the brain that is sensitive to human faces. The FFA is located in the mid/social part of the brain near the amygdala which processes emotions. Faces grab attention because of the FFA.

(Large preview)

The FFA identifies:

  • Is this a face?
  • Someone I know?
  • Someone I know personally?
  • What are they feeling?

What stimulates the FFA?

  • Faces that look straight out stimulate the FFA.
  • Faces that are in profile may eventually stimulate the FFA, but not as quickly. In the example below the face is in profile and obscured by hair. It may not stimulate the FFA at all.
  • (Large preview)
  • Even inanimate objects like the picture of the car below may stimulate the FFA area if they have things that look like facial parts such as eyes and a mouth.
(Large preview) Looking Where The Face Looks?

You may have seen the heat maps that show that if you show a face and the face is looking at an object (for example, a button or a product) on the screen then the person looking at the page will also look at the same object. Here’s an example:

(Large preview)

The red areas show where people looked most. When the model looks at the shampoo bottle then people tend to look there too.

But be careful about drawing too many conclusions from this. Although the research shows that people’s eye gaze will follow the eye gaze of the photo, that doesn’t necessarily mean that people will take action. Highly emotional facial expressions lead to more action taking than just eye gaze.

Recommended reading: The Importance Of Macro And Micro-Moment Design

Takeaways

If you want to grab someone’s visual attention:

  • Use the pre-attention areas of the visual cortex: make everything on the page plain except for one element.

or

  • Show a large face, facing forward;
  • If you want to spur action have the face show a strong emotion;
  • Resist the urge to use many methods at once, such as a face, and color, and size, and shape, and orientation.

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

(cm, ms, yk, il)
Kategorier: Amerikanska

HTML5 Input Types: Where Are They Now?

ons, 01/16/2019 - 14:30
HTML5 Input Types: Where Are They Now? HTML5 Input Types: Where Are They Now? Drew McLellan 2019-01-16T14:30:11+01:00 2019-02-05T13:07:49+00:00

One of the stand-out headline features of HTML5 for many designers and developers was the addition of a number of new types of form input that could be used. For years, we’d been confined to using single-line text inputs (type="text") and laying on JavaScript and user instructions to try an accurately capture valid data of different types through that one unsophisticated field.

HTML5 brought with it new values of the type attribute that enabled us to be much more specific about the types of data we needed to capture through the field, with the promise being that the browser would then provide the interface and validation required to coerce the user into completing the field accurately.

From URLs to emails, and from search fields to dates, the hope was that instead of needing to write cumbersome JavaScript to try and validate those fields, we could just leave it to the browser to do that hard work for us. What’s more, by adding what it knows about the user’s context (type of device, type of interaction, timezones, and so on) the browser would be able to do a much better job of tailoring the interface to meet the user’s needs that we ever could as page authors.

Recommended reading: UX And HTML5: Let’s Help Users Fill In Your Mobile Form

Having new items in a spec is one thing, but it doesn’t really mean too much unless the browsers our audience are using support those features. These new values of the type attribute had the big advantage of falling back to type="text" if the browser had no support, but this may have also come at the cost of removing the browsers makers’ imperative when it came to implementing those new types in their products.

It’s the start of 2019, and HTML5 has been the current version of HTML now for more than four years. Which of those new types have been implemented, which can we use, and are there any we should be avoiding?

  1. Search Fields
  2. Telephone Number Fields
  3. URL Fields
  4. Email Fields
  5. Number Fields
  6. Range Fields
  7. Color Fields
  8. Date Fields

Web forms are such an important part of the web, but we design them poorly all the time. The brand-new “Form Design Patterns” book is our new practical guide for people who design, prototype and build all sorts of forms for digital services, products and websites. The eBook is free for Smashing Members.

Check the table of contents ↬ 1. Search Fields

The type="search" input is intended to be used for search fields. Functionally, these are very the same as basic text fields, but having a dedicated type enables the browser to apply different styling. This is particularly useful if the user’s operating system has a set style for search fields, as this enables the browser to style the search fields on web pages to match.

The specification states that the difference between search and text is purely stylistic, so it may be best to avoid this if you intend to restyle the field with CSS anyway. There appears to be no semantic advantage to its use.

Can I Use input-search? Data on support for the input-search feature across the major browsers from caniuse.com.

Recommendation

Use type="search" if you intend to leave the styling of the search field up to the browser.

2. Telephone Number Fields

The type="tel" input is used for entering telephone numbers. These are like the unique usernames used by Whatsapp. If you’re unsure, ask your grandparents.

Internationally, telephone numbers take on lots of different formats, for both technical and localization reasons. Due to this, the tel input doesn’t attempt to validate the format of a phone number. You can make use of the associated validation tools such as the pattern attribute on the tag, or the setCustomValidity() JavaScript method to enforce a format if required.

On desktop browsers, the use of telephone fields seems to have little impact. On devices with virtual keyboards, however, they can be really useful. For example, on iOS, focusing input on a telephone field brings up a numeric keypad ready for keying in a number. In addition, the device’s autocomplete mechanisms kick in and suggest phone numbers that can be autofilled with a single tap.

Can I Use input-email-tel-url? Data on support for the input-email-tel-url feature across the major browsers from caniuse.com.

Recommendation

Use type="tel" for any phone number fields. It’s very useful where implemented, and comes at no cost when it’s not.

3. URL Fields

The type="url" field can be used for capturing URLs. You might use this when asking a user to input their website address for a business directory, for example. The curious thing about the URL field is that it takes only full, absolute URLs. There’s no option to be able to capture just a domain name, or just a path, for example. This does restrict its usefulness in some respects, as I imagine CMS and web app developers would have found lots of uses for a field that accepts and validates relative paths.

While this would be a valid absolute URL:

https://twitter.com/drewm

Both of these would not pass the field’s validation:

smashingmagazine.com /2019/01/css-multiple-column-layout-multicol/

It feels like a missed opportunity that different parts of a URL cannot be specified, but that’s what we have. Browser support is across the board pretty great, with virtual keyboard devices offering some customization for URL entry. iOS customizes its keyboard with ., / and an autocomplete button for common TLDs such as .com and for my locale, .co.uk. This is a good example of the browser being able to offer more intelligent choices than we can as web developers.

Can I Use input-email-tel-url? Data on support for the input-email-tel-url feature across the major browsers from caniuse.com.

Recommendation

Use type="url" whenever you need to collect a full, absolute URL. Browser support is great, but remember that it’s no good for individual URL components.

4. Email Fields

Possibly one of the most commonly used of the newer options is type="email" for email addresses. Much like we’ve seen with telephone numbers and URLs, devices with virtual keyboards customize the keys (to include things like @ buttons) and enable autofill from their contacts database.

Desktop browsers make use of this too, with Safari on macOS also enabling autofill for email fields, based on data in the system Contacts app.

Email addresses often seem like they follow a very simple format, but the variations actually make them quite complex. A naive attempt to validate email addresses can result in a perfectly good address being marked as invalid, so it’s great to be able to lean on the browser’s more sophisticated and well-tested validation methods to check the format.

Usefully, the multiple attribute can be added to email fields to collect a list of email addresses. In this case, each email address in the list is individually validated.

<input type="email" multiple>

Can I Use input-email-tel-url? Data on support for the input-email-tel-url feature across the major browsers from caniuse.com.

Recommendation

Use type="email" for email address fields whenever possible.

Safari on iOS shows custom keyboards for telephone, email and number fields, amongst others. (Large preview) 5. Number Fields

The type="number" field is designed for numerical values, and has some very useful attributes along with it in the shape of min, max and step. A valid value for a number field must be a floating point number between any minimum and maximum value specified by the min and max attributes.

If step is set, then a valid value is divisible by the step value.

<input type="number" min="10" max="30" step="5">

Valid input for the above field would be 10, 15, 20, 25 and 30, with any other value being rejected.

Browser support is broad, again with virtual keyboards often defaulting to a numeric input mode for keying in values.

Some desktop browsers (including Chrome, Firefox and Safari, but not Edge) add toggle buttons for nudging the values up and down by the value of step, or if no step is specified, the default step appears to be 1 in each implementation.

Can I Use input-number? Data on support for the input-number feature across the major browsers from caniuse.com.

Recommendation

Use type="number" for any floating point numbers, as it’s widely supported and can help prevent accidental input.

6. Range Fields

Less obvious in use that some of the other types, type="range" can be thought of as being an alternative for type="number" where the user doesn’t care about the exact value.

Range fields take, and will often use, the same min, max and step attributes as number fields, and browsers almost universally display this as a graphical slider. The user doesn’t necessarily get to see the exact value they’re setting.

Range fields might be useful for those sorts of questions on forms like “How likely are you to recommend this to a friend?” with “Likely” at one end and “Unlikely” at the other. The user could slide the slider to wherever they think represents their opinion, and under the hood that gets submitted as a numerical value that you can store and process.

Browser support is good, although the appearance varies between implementations.

Can I Use input-range? Data on support for the input-range feature across the major browsers from caniuse.com.

Recommendation

The uses for type="range" might be a bit niche, but support is good and the slider provides a user-friendly input method where appropriate.

7. Color Fields

The type="color" field is design for capturing RGB colors in hexadecimal notation, such as #aabbcc. The HTML specification calls this a “color well control”, with the intention that the browser should provide a user-friendly color picker of some sort.

Some browsers do provide this, notably Chrome and Firefox both providing access to the system color picker through a small color swatch.

Neither IE nor Safari provide any support here, leaving the user to figure out that they’re supposed to enter a 7-digit hex number all by themselves.

Color fields might find use in theming for personalization and in CMS use, but unless the users are sufficiently technical to deal with hex color codes, it may be better not to rely on the browser providing a nice UI for these.

Can I Use input-color? Data on support for the input-color feature across the major browsers from caniuse.com.

Recommendation

Unless you know your users will be happy to fall back to entering hexadecimal color codes, it’s best not to rely on browsers supporting type="color".

Range, color and date fields as displayed by Edge, Firefox and Chrome. (Large preview) 8. Date Fields

HTML5 introduced a number of different type values for creating inputs for dates and times. These included date, time, datetime-local, month and week.

At first glance, these appear to be heaven-sent, as collecting dates in a form is a difficult experience for both developer and user, and they’re needed pretty frequently.

The promise here is that the new field types enable the browser to provide a standardized, accessible and consistent user interface to capture dates and times from the user with ease. This is really important, as date and time formats vary world-over based on both language and locale, and so a friendly browser interface that translates an easy-to-use date selection into an unambiguous technical date format really does sound like the ideal solution.

As such, valid input for type="date" field is an unambiguous year-month-day value such as 2019-01-16. Developers like these, as they map pretty much to the ISO 8601 date format, which is used in most technical contexts. Unfortunately, few regular human beings use this date format and aren’t likely to reach for it when asked to provide a date in a single empty text field.

And, of course, a single empty text field is what the user is presented with if their browser does not provide a user interface for picking dates. In those cases, it then becomes very difficult for a user to enter a valid date value unless they happen to be familiar with the format required or the input is annotated with clear instructions.

Many browsers do provide a good user interface for picking dates, however. Firefox has a really excellent date picker, and Chrome and Edge also have pretty good interfaces. However, there’s no support in poor old IE and none in Safari, which could be an issue.

Can I Use input-datetime? Data on support for the input-datetime feature across the major browsers from caniuse.com.

Recommendation

While convenient where it works, the failure mode of type="date" and its associated date and time types is very poor. This makes it a risky choice that could leave users struggling to meet validation criteria.

Conclusion

A lot has changed in the browser landscape in the four years since the HTML5 specification became a recommendation. Support for the newer types of input is fairly strong — particularly in mobile devices with virtual keyboards such as tablets and phones. In most cases, these inputs are safe to use and provide some extra utility to the user.

There are a couple of notable exceptions, the worst of which being the date and time fields, which not only lack utility, but also have more patchy browsers support. When support isn’t available, the fallback mode of these fields is poor. In these cases, it might be best to stick to JavaScript-based solutions for progressively enhancing the basic type="text" input fields.

If you’d like to read more, I’d thoroughly recommend the MDN web docs on these field types, and as ever, the W3C specification.

(ra, il)
Kategorier: Amerikanska

When And How To Use CSS Multi-Column Layout

fre, 01/11/2019 - 14:00
When And How To Use CSS Multi-Column Layout When And How To Use CSS Multi-Column Layout Rachel Andrew 2019-01-11T15:00:30+02:00 2019-01-30T17:20:05+00:00

In all of the excitement about CSS Grid Layout and Flexbox, another layout method is often overlooked. In this article I’m going to take a look at Multi-column Layout — often referred to as multicol or sometimes “CSS Columns”. You’ll find out which tasks it is suited for, and some of the things to watch out for when making columns.

What Is Multicol?

The basic idea of multicol, is that you can take a chunk of content and flow it into multiple columns, as in a newspaper. You do this by using one of two properties. The column-count property specifies the number of columns that you would like the content to break into. The column-width property specifies the ideal width, leaving the browser to figure out how many columns will fit.

It doesn’t matter which elements are inside the content that you turn into a multicol container, everything remains in normal flow, but broken into columns. This makes multicol unlike other layout methods that we have in browsers today. Flexbox and Grid for example, take the child elements of the container and those items then participate in a flex or grid layout. With multicol, you still have normal flow, except inside a column.

In the below example I am using column-width, to display columns of at least 14em. Multicol assigns as many 14em columns as will fit and then shares out the remaining space between the columns. Columns will be at least 14em, unless we can only display one column in which case it may be smaller. Multicol was the first time that we saw this kind of behavior in CSS, columns being created which were essentialy responsive by default. You do not need to add Media Queries and change the number of columns for various breakpoints, instead we specify an optimal width and the browser will work it out.

See the Pen Smashing Multicol: column-width by Rachel Andrew (@rachelandrew) on CodePen.

Front-end is messy and complicated these days. That's why we publish articles, printed books and webinars with useful techniques to improve your work. Even better: Smashing Membership with a growing selection of front-end & UX goodies. So you get your work done, better and faster.

Explore Smashing Membership ↬ Styling Columns

The column boxes created when you use one of the column properties can’t be targeted. You can’t address them with JavaScript, nor can you style an individual box to give it a background color or adjust the padding and margins. All of the column boxes will be the same size. The only thing you can do is add a rule between columns, using the column-rule property, which acts like border. You can also control the gap between columns using the column-gap property, which has a default value of 1em however you can change it to any valid length unit.

See the Pen Smashing Multicol: column styling by Rachel Andrew (@rachelandrew) on CodePen.

That is the basic functionality of multicol. You can take a chunk of content and split it into columns. Content will fill the columns in turn, creating columns in the inline direction. You can control the gaps between columns and add a rule, with the same possible values as border. So far so good, and all of the above is very well supported in browsers and has been for a long time, making this spec very safe to use in terms of backwards compatibility.

There are some further things you might want to consider with your columns, and some potential issues to be aware of when using columns on the web.

Spanning Columns

Sometimes you might like to break some content into columns, but then cause one element to span across the column boxes. Applying the column-span property to a descendent of the multicol container achieves this.

In the example below, I have caused a <blockquote> element to span across my columns. Note that when you do this, the content breaks into a set of boxes above the span, then starts a new set of column boxes below. The content doesn’t jump across the spanned element.

The column-span property is currently being implemented in Firefox and is behind a feature flag.

See the Pen Smashing Multicol: column-span by Rachel Andrew (@rachelandrew) on CodePen.

Be aware that in the current spec, the values for column-span are either all or none. You can’t span just some of the columns, but you can get the kind of layout you might see in a newspaper by combining multicol with other layout methods. In this next example, I have a grid container with two column tracks. The left-hand track is 2fr, the right-hand track 1fr. The article in the left-hand track I have turned into a multicol container with two tracks, it also has a spanning element.

On the right, we have an aside which goes into the second Grid column track. By playing around with the various layout methods available to us, we can figure out exactly which layout method suits the job at hand — don’t be afraid to mix and match!

See the Pen Smashing Multicol: mixing layout methods by Rachel Andrew (@rachelandrew) on CodePen.

Controlling Content Breaks

If you have content containing headings, then you probably want to avoid the situation where a heading ends up as the last thing in a column with the content going into the next column. If you have images with captions then the ideal situation would be for the image and caption to stay as one unit, not becoming split across columns. To deal with these problems CSS has properties to control where the content breaks.

When you split your content into columns, you perform what is known as fragmentation. The same is true if you split your content between pages, such as when you create a stylesheet for a print context. Therefore, multicol is closest to Paged Media than it is to other layout methods on the web. Because of this, for several years the way to control breaks in the content has been to use the page-break- properties which were part of CSS2.1.

  • page-break-before
  • page-break-after
  • page-break-inside

More recently the CSS Fragmentation specification has defined properties for fragmentation which are designed for any fragmented context, the spec includes details for Paged Media, multicol and the stalled Regions spec; Regions also fragments a continuous piece of content. By making these properties generic they can apply to any future fragmented context to, in the same way that the alignment properties from Flexbox were moved into the Box Alignment spec in order that they could be used in Grid and Block layout.

  • break-before
  • break-after
  • break-inside

As an example, I have used break-inside avoid on the <figure> element, to prevent the caption being detached from the image. A supporting browser should keep the figure together even if that causes the columns to look unbalanced.

See the Pen Smashing Multicol: break-inside by Rachel Andrew (@rachelandrew) on CodePen.

Unfortunately, support for these properties in multicol is pretty patchy. Even where supported they should be seen as a suggestion due to the fact that it would be possible to make so many requests while trying to control breaking, that essentially the browser can’t really break anywhere. The spec does define priorities in this case, however it is probably more useful for you to control only the most important cases.

The Problem Of Columns On the Web

One reason why we don’t see multicol used much on the web is the fact that it would be very easy to end up with a reading experience which made the reader scroll in the block dimension. That would mean scrolling up and down vertically for those of us using English or another vertical writing mode. This is not a good reading experience!

If you fix the height of the container, for example by using the viewport unit vh, and there is too much content, then overflow will happen in the inline direction and so you get a horizontal scrollbar instead.

See the Pen Smashing Multicol: overflow columns by Rachel Andrew (@rachelandrew) on CodePen.

Neither of these things are ideal, and making use of multicol on the web is something we need to think about very carefully in terms of the amount of content we might be aiming to flow into our columns.

Block Overflow Columns

For Level 2 of the specification, we are considering how to enable a method by which overflow columns, those which currently end up causing the horizontal scrollbar, could instead be created in the block direction. This would mean that you could have a multicol container with a height, and once the content had made columns which filled that container, a new set of columns would be created below. This would look a little like our spanning example above, however, instead of having a spanner causing the new column boxes to start, it would be the overflow caused by a container with a restriction in the block dimension.

This feature would make multicol far more useful for the web. While we are a little way off right now, you can keep an eye on the issue in the CSS Working Group repo. If you have additional use cases for this feature do post them, it can really help when designing the new feature.

What Is Multicol Useful For Today?

With the current specification, splitting all of your content into columns without consideration for the problems of scrolling isn’t advised. However, there are some cases where multicol is ideal on the web. There are enough uses cases to make it something you should consider when looking at design patterns.

Collapsing Small UI Or Text Elements

Multicol can be useful in any place where you have a small list of items that you want to take up less space. For example a simple listing of checkboxes, or a list of names. Often in these scenarios, the visitor is not reading down one column and then going back to the top of the next, but scanning the content for a checkbox to click or an item to select. Even if you do create a scrolled experience, it may not be an issue.

You can see an example of multicol used in this way by Sander de Jong on the DonarMuseum website.

On DonarMuseum, we can see multicol used to display lists of names. (Image source) (Large preview) Known Small Amounts Of Content

There are times when we design a site where we know that some piece of content is relatively small, and will fit on the majority of screens without causing unwanted scrolling. I’ve used multicol on Notist presentation pages, for the introduction to a talk.

Andy Clarke designed a lovely example for the Equfund website.

On the Equfund website, you can see how different HTML elements remain in normal flow while displayed inside columns. (Image source) (Large preview)

To avoid the possibility of very small screens causing scrolling, remember that you can use media queries to check for height as well as width (or in a logical world, block as well as inline). If you only enable columns at a breakpoint which has a min-height large enough for their content, this can save users of very small devices having a poor scrolling experience.

Masonry-Like Display Of Content

Another place where Multiple-column Layout works beautifully is if you want to create a Masonry type of display of content. Multicol is the only layout method that will currently create this kind of layout with unequal height items. Grid would either leave a gap, or stretch the items to make a strict two-dimensional grid.

Veerle Pieters has a beautiful example of using multicol in this way on her inspiration page.

In this design by Veerle Pieters, multicol is used to layout multiple boxes or cards as columns. (Large preview) Grid And Flexbox Fallbacks

The column- properties can also be used as a fallback for Grid and Flex layout. If you specify one of the properties on a container, then turn that container into a Flex or Grid layout by using display: flex or display: grid any column behavior will be removed. If you have, for example, a cards layout that uses Grid Layout, and the layout would be readable if it ran in columns rather than across the page, you could use multicol as a simple fallback. Browsers that do not support Grid will get the multicol display, those which support Grid will get the Grid Layout.

Don’t Forget Multicol!

Fairly frequently I answer Grid and Flexbox questions where the answer is to not use Grid or Flexbox, but instead to look at Multicol. You probably won’t use it on every site, but when you come across a use case it can be really helpful. Over at MDN there are useful resources for multicol and the related fragmentation properties.

If you have used multicol on a project, perhaps drop a note in the comments, to share other ways in which we can use the feature.

(il)
Kategorier: Amerikanska

Designing A Font Based On Old Handwriting

tors, 01/10/2019 - 13:00
Designing A Font Based On Old Handwriting Designing A Font Based On Old Handwriting Carolyn Porter 2019-01-10T13:00:02+01:00 2019-01-30T17:20:05+00:00

Designers create handwriting-based connected cursive fonts for a variety of reasons: to immortalize the loops and swirls of a loved one’s handwriting, to digitize the penmanship of a person or document of historic significance, or to transform charming handwriting into a creative asset that can be licensed.

Let’s say you found a beautiful old handwriting specimen you want to digitize. You might presume you can trace individual letters, then seamlessly convert those tracings into a font. I will confess that was my assumption before I began to work on my first font. I had not taken into account the myriad of thoughtful and intentional decisions required to transform the specimen into an artful and functional font.

Before you begin the process of digitizing your specimen, it would be worthwhile to ask yourself a few questions about your goals and intent. Think of it as writing a creative brief for your project. Begin by assessing the importance of historical accuracy. Then conduct a close examination of the specimen: look at the idiosyncrasies in the handwriting, the variation in shape and position of individual letters, the method for connecting letters, and the texture. Possessing a keen familiarity of your specimen will allow you to make informed decisions about aesthetics as you design your font.

Recommended reading: Hands On The Sigmund Freud Typeface: Making A Font For Your Shrink

Meet Smashing Book 6 — our brand new book focused on real challenges and real front-end solutions in the real world: from design systems and accessible single-page apps to CSS Custom Properties, CSS Grid, Service Workers, performance, AR/VR and responsive art direction. With Marcy Sutton, Yoav Weiss, Lyza D. Gardner, Laura Elizabeth and many others.

Table of Contents → How Important Is Historical Accuracy?

One of the biggest decisions you will need to make is whether you want to capture every nuance of your handwriting specimen, or if you want to design something inspired by that handwriting. It is like watching a movie “based on a true story” versus one “inspired by real events.” In the first scenario, you can expect the movie (or font) maintains a higher degree of factual integrity than the second option, where the director (or designer) may take wide-ranging creative liberties.

If you choose to replicate your specimen with utmost precision, be aware that rigorously honoring accuracy may mean compromising legibility. “Old scripts, in particular, include letterforms that are less legible — even virtually illegible, like the old-style long s — than in modern handwriting,” notes Brian Willson, who has designed more than two dozen fonts based on the handwriting of notable figures such as Abigail Adams, Frederick Douglass, and Sam Houston.

Compare the similarities in these three fonts based on (or inspired by) the handwriting of Abraham Lincoln. Although 1863 Gettysburg (the font shown on the far right) was inspired by documents written by President Lincoln, the designer’s stated goal was not to replicate Lincoln’s exact writing, but to create a font that represented the era. (From left to right: LD Abe Lincoln by Lettering Delights/Illustration Ink, a Lincoln by Steve Woolf, and 1863 Gettysburg designed by GLC.) (Large preview)

You may find you want to make thoughtful revisions to strike a balance between historical accuracy and optimized legibility. As I designed the P22 Marcel Script, which is a connected cursive font based on handwritten WWII love letters, I chose to make small revisions to improve legibility.

The font retains the essential character of the original writing, but it is not a precise replica. Many of the original j’s, for example, did not have a tittle (a dot), and the original lowercase p did not have a closed curve on the bottom of the bowl. It looked like a hybrid between a p and an n. Knowing some people struggle to read cursive writing, I chose to dot the j and revise the shape of the p.

Note the small revisions to improve legibility. (From left to right: The original handwriting specimen, a close-up of the greeting “Mes chères petites”, and a sample of the font P22 Marcel Script.) (Large preview) Does Your Source Document Include Enough Material To Work With?

John Hancock had a fantastic signature, but designing an entire font based on the eight letters in his name — J, o, h, n, H, a, c, and k — would be a challenge. Assess whether your specimen is complete enough to support an entire font. Does it include both upper and lowercase letters? How about numbers?

When designing the font based on the handwriting of Jane Austen, designer Pia Frauss discovered she did not have a handwritten letter X to reference. If, like Frauss, you have a specimen that is mostly complete, you should be able to extrapolate what a specific letter might have looked like. That skill will also be necessary when it comes time to design new glyphs — the term for a specific character in a font file — like the Euro, which has only existed since 1999.

If your specimen only has a limited set of characters, gauge whether you feel comfortable designing the missing letters. If not, consider finding a handwritten specimen that is similar in style, then pulling the missing letters from the supplementary specimen.

What Idiosyncrasies Make The Handwriting Special?

Are crossbars unusually high, low, or angled? Are ascenders or descenders abnormally long or short? Are letters strikingly narrow or wide? Do specific letters extend above or below the baseline? Do letters loop in unusual ways?

If your goal is to create a historically accurate font, you will want to take care to ensure those idiosyncrasies are not lost as you digitize individual glyphs. If you are comfortable taking creative liberties, you might exaggerate those points of differentiation.

In the font Marydale, Brian Willson included quirky glyphs such as the loopy, two-story serif g found in the original handwriting. Brian thought it added friendly charm. But a risk of embracing idiosyncrasies is that some users may not like those letterforms — and indeed some users complained to Brian that the g was too unusual. Nevertheless, Marydale is one of Brian’s best-selling fonts.

To help you decode shapes and understand the mechanics behind some of your specimen’s idiosyncrasies, it may be helpful to identify the type of writing utensil that was used. A ballpoint pen will create uniform-width strokes; a split-nib pen can create graceful thicks and thins; a dip pen may carry evidence of ink being reapplied; a brush can create dramatic variation in thickness. You may also consider how the writing utensil had been held or if the writer had been in a hurry, since hand position and speed can influence the shape and style of the handwriting, too.

Assess Variations In Axis, Letter Height, Alignment To Baseline, And Stroke Width

Within any single handwriting specimen, you will likely see variation in axis, letter height, alignment to baseline, and stroke width. These irregularities enhance the individuality of handwriting — but transferring those irregularities to a digital font can be a challenge. If your font includes too many variations in axis, letter height, alignment to baseline, or stroke width, it may not reflect the visual unity of the original specimen. If your font does not include enough variation, it may lack the charm of the original writing.

Take time to assess which elements in the original specimen are consistent and which vary, then plan how you could incorporate those variations into your font. If you employ a consistent axis, consider varying alignment to baseline or stroke width. If you standardize stroke widths, consider varying axes and letter heights.

Fonts with increasing variability in axes, letter heights, alignment to baseline, and stroke thicknesses. (From left to right: Texas Hero by Brian Willson/Three Islands Press, P22 Cezanne Pro by Michael Want and James Grieshaber, and Selima by Jroh.) (Large preview)

When I began working on the P22 Marcel Script, I sourced favorite individual letters from five separate handwritten pages. The first time I pieced glyphs into words, I could see that the axes, letter heights, and stroke thicknesses were too variable. Even though each glyph had been a careful re-creation of one man’s handwriting, the resulting look was haphazard. I decided on a standard for axis and stroke thickness, then adjusted every glyph to that standard. Varying letter heights and alignment to baseline prevented the font from looking too mechanical.

Where And How Do Individual Letters Connect?

Are the connecting lines that sweep from one letter to the next high or low? Are the connecting lines thick or thin? Are there some letters that do not connect?

The key to designing a successfully connected cursive script font is to create an overlap so individual letters appear to seamlessly flow from one into the next. The trick is to identify one location for that overlap, then to start and end every glyph in that precise position. Some designers place those overlaps along the left-hand edge of a glyph, other designers place the overlap in the space between the glyphs. Some designers place the overlap low, others place the overlap high. There is no right or wrong answer; choose the location and method that makes sense for you.

Fonts that overlap on the left-hand edge of each glyph. The magenta circle shows the overlap; the horizontal magenta line shows the consistent placement of the sweeping connecting line on the letters m and a. (From left to right: Douglass Pen by Brian Willson/Three Islands Press, Rough Love by Positype, and Bambusa Pro by FontForecast.) (Large preview) Fonts that overlap in the space between the glyphs. (From left to right: Dear Sarah Pro by Christian Robertson, Storyteller Script by My Creative Land, and Mila by Facetime.) (Large preview)

You may also discover that not all letters in your specimen connect. In that case, you will still need to identify one location and implement a consistent strategy for the overlaps, though you will only create the overlap on those glyphs that connect.* *

Fonts where some, but not all, letters connect. (From left to right: Blooms by DearType, JoeHand 2 by JOEBOB Graphics, and Brush Marker by Fenotype.) (Large preview) Do You Want Your Font To Include A Texture Effect?

Adding texture can enhance a feeling of antiquity or nostalgia, but this treatment adds time, complexity, and increases file size. And it may influence whether or not someone will want to license and use your font, since they may or may not be looking for that specific effect.

Examine your original specimen to determine where the texture came from. Was it caused by the paper surface? Was variation caused by the writing tool or writing speed? Are irregularities clustered on curves? Does one side of the letter include more texture than the other? Do brush strokes or splatters of ink extend into the space surrounding each letter?

The word “Smashing” typeset in three different fonts that have three different textured looks: a paper texture look, rough edges, and brush strokes. (From left to right: Paper texture; Thirsty Rough by Yellow Design Studio, Scratchy pen and ink texture, Azalea Rough by Laura Worthington; Brush strokes, Sveglia by Wacaksara Co.) (Large preview)

Once you’ve made high-level decisions on the importance of historical accuracy, identified what idiosyncrasies make the handwriting special, considered how to add variation in axis, letter height, alignment to baseline, or stroke width, assessed how to connect glyphs, and decided if you want to include texture, it’s time to forge ahead with the design of your font.

Pick Your Letters

Make a copy of your specimen, and go through it line by line, flagging the specific characters you want to incorporate into your font. When designer Brian Willson begins a new font, it is not unusual for him to spend hours poring over the source material to select the individual letters he plans to include.

Consider flagging two types of letters:

  1. Favorite “workhorse” letters
    Workhorse letters will make up your basic glyph set. They might not be the fanciest option, but workhorse characters will make for a reliable and legible glyph set.

  2. Favorite swash letters
    Swash letters might include extra loops or flourishes, more or less texture, greater variation in position above or below the baseline, or exaggerated features such as extra-long crossbars.

Swash letters may be less legible, or may only be appropriate for use in specific instances, but can add variety, beauty, and personality. (Swash glyphs will be added later in the process; focus first on designing the workhorse glyph set.)

Ideally, at the end of your review, you will have flagged a complete set of upper and lowercase workhorse letters, numbers, punctuation marks, and an array of swash characters.

Scan Individual Letters And Prepare To Vectorize

Create high-resolution bitmap images of all letters you plan to include in your font. I have always used a flatbed scanner to capture those images, but I have heard of people exporting sketches from Procreate or taking high-resolution photos on their phones. If you take a photo, be sure your phone is parallel to your specimen to avoid distortion.

Once you have assembled a collection of bitmap images, you will need to choose between vectorizing the letterforms using Adobe Illustrator’s Image Trace feature or importing the scans directly into font editing software then vectorizing them by hand.

Using Illustrator’s Image Trace feature may be preferred if your specimen includes lots of texture since Illustrator can capture that texture for you. To create a vector outline, import a bitmap image into Illustrator, then using advanced Image Trace menu options, test combinations of Paths, Corners, and Noise to get the tracing result you prefer. Expand to get your vector outline.

Importing scans directly into font editing software may be preferred if your font is not going to include texture, if you are comfortable generating Bezier lines, or if you intend to make significant revisions to letter shapes.

Recommended reading: How New Font Technologies Will Improve The Web

Establish A New Font File

Popular font editing software options include FontLab Studio VI (Mac or Windows OS, $459), Glyphs (Mac OS, €249.90), Robofont (Mac OS, $490), Font Creator (Windows, $79–199), and Font Forge (free). There is also an extension for Illustrator and Photoshop CC called FontSelf which allows you to convert lettering into a font ($49–$98).

Establish a new font file. If you created vector outlines using Illustrator, import each outline into the applicable glyph cell (that is, place the vector outline of the letter a in the a glyph cell so that an a appears when you hit the a key on your keyboard).

If you chose to vectorize the glyphs within the font editing software, import each bitmap scan as a background sketch, then trace.

Identify Archetype Letters

Some font designers begin by designing or refining the letters n, b, o, v, A, H and O since those letters contain clues to the shape of many other letters. The n, for example, holds clues to the shape of the i and h; the b holds clues to the shape of d, p, and q; the O holds clues to C and G. Other designers begin with the most frequently used letters of the alphabet: e, t, a, o, i, and n. In the fantastic book Designing Type, which is chock full of images that compare variations in letter shapes, Karen Cheng notes some designers begin with the letters a, e, g, n, and o.

You may begin with one of those glyph sets, but if you’ve identified a few letters that quintessentially represent the aesthetic of your font, consider starting by refining those glyphs. When I began to work on the P22 Marcel Script, I began by working on the capital M for no other reason than the swoop of the original handwritten M was exquisite, and it brought joy to see that letter come to life as a glyph. (After working on the M, I focused on refining archetype letters n and e.)

From left to right: Original scan, original tracing, glyph outline in FontLab (note the refinements to stroke width and axis), and final M in P22 Marcel Script. (Large preview)

No matter which glyphs you begin with, you will quickly want to establish standards for the axis, letter heights, alignment to baseline, and stroke widths. Ensure all glyphs meet those standards while simultaneously keeping in mind incorporating variability to achieve an organic look.

In order to introduce variability in an intentional way, it might be helpful to add guidelines to your workspace to define the lower and upper ranges of variability. Use these guidelines to introduce variation in ascender height, descender length, or in alignment to the baseline.

Do you want your font to have more variability? Increase the distance between the guidelines. Do you want your font to have less variability? Decrease the distance between the guidelines. Add variability to stroke widths in a similar way.

Lowercase glyph set of P22 Marcel Script. Notice variability in length of the descenders in the letters f, g, y, and z, variability in the height of letters i and j, and how I chose not to connect letters v and w with the following letter. (Large preview)

You will also want to establish a standard position for the overlap. Since there is not one correct place for these overlaps, experiment with a limited number of glyphs until the overlaps appear as natural as if the letters had been written with a pen without lifting the pen off the page. Then, test the position of the overlap on tricky letters such as r, o, s, f, v and w to confirm the overlap works. You’ll know if there are issues because glyphs won’t connect, or the connection won’t appear smooth. (If you see white where two black letters overlap, check for a Postscript path direction error.)

The good news is that once you have established the successful position for your overlaps you should be able to cut, copy, and paste the connecting line to replicate the position of the overlap on all remaining letters.

Overlaps in P22 Marcel Script (Large preview) Test As You Go

As soon as you have a collection of glyphs — even if it isn’t the entire alphabet — generate a test file and preview your font. If your goal was to replicate your specimen with accuracy, assess whether the font reflects the rhythm and character of the original handwriting. Evaluate whether the “color” — the overall darkness or lightness — matches the original. Refine glyphs as necessary to achieve the right rhythm, character, and color.

If you have chosen to take liberties with the shapes of letters or to introduce variability, your goal should still be to achieve an overall cohesive aesthetic. It is up to you as the designer to define precisely what that means.

As you test, it will be helpful to print blocks of sample text at a range of sizes. Reverse letters out of black. Look at the spaces between letters. Look at the spaces inside of letters. Look at strings of glyphs backwards, then upside down. Look at the font both when printed on paper and on a computer monitor. Testing under different conditions will help you notice glyphs that need additional refinement. I found that when I tested sample text set in a foreign language the unfamiliar letter combinations would help me see individual glyphs that were too heavy, too light, too narrow or too wide, along with individual curves that seemed too rounded or too flat.

For the first-time type designer, I recommend the book Inside Paragraphs: Typographic Fundamentals by Cyrus Highsmith (see list of references below). The book provides an invaluable primer on learning how to look at shapes and spaces in and around letters.

Continue testing and revising glyphs until your font includes a–z lowercase, A–Z uppercase, numbers, fractions, punctuation marks, and diacritics (marks such as the umlaut, acute, or tilde added to letters to indicate stress or change in pronunciation).

Add Swashes And Alternate Characters

Once your workhorse glyph set is complete, consider adding the swash characters you flagged when you picked your initial letters. A digital font can never offer the infinite variability found in handwriting, but by writing lines of OpenType code and incorporating swashes, ligatures (two or more letters that are combined into a single glyph), and alternate characters, you can begin to close the gap between the mechanical nature of a font and organic variation of handwriting. OpenType code allows you to do things like ensure two of the same glyphs never appear next to each other, or replace a workhorses glyph with a fancy swash or with a glyph that has more (or less) texture.

This work can be time-consuming, but you just might find it is addictive. You might discover every word you test could benefit from some custom flourish. The font Suomi, designed by Tomi Haaparanta, includes more than 700 ligatures. The font Hipster Script, designed by Ale Paul has 1,066. Syys Script by Julia Sysmäläinen for Art. Lebedev Studio has more than 2,000 glyphs. And between the Latin and Cyrillic versions, the font NIVEA Care Type by Juliasys has more than 4,000 glyphs.

Between swashes, ornaments, and alternate characters, the Pro version of the P22 Marcel Script includes more than 1,300 glyphs. Many of the alternate glyphs were inspired by flourishes in the original specimen; other glyphs were of my own invention, but were made in the style of the original writing. In my experience, incorporating swashes, ligatures, and alternate characters is the most exciting part about designing a connected cursive script font. In fact, it is what brings a connected cursive script font to life.

From left to right: P22 Marcel Script basic (workhorse) glyph set, P22 Marcel Script Pro incorporating alternate glyphs, and P22 Marcel Script Pro showing additional alternate glyphs, swashes, and an ornament. (Large preview) Final Steps

Once all your glyphs have been designed and the font has been thoroughly tested for technical performance and aesthetics, it is time to name the font and release it into the world.

Ensure no other font is already using the name you are considering. You can do a preliminary search on aggregator websites such as MyFonts, Fonts.com, FontShop, or Creative Market. Fonts are also distributed by individual font foundries and designers. Because there are so many distribution channels, the only way to guarantee availability and protect a name is to apply for a copyright with the U.S. Patent and Trademark Office (for U.S. designers). Consider hiring a lawyer to help with the filing process.

Finally, when it is time to release the font, if this is your first font it may be easiest to distribute your font through an established foundry or aggregator website. They should offer technical support, and will track licensing and sales tax. Consider working with one of the websites listed above; each website will have a different process to submit a font for consideration.

Further Reading (ah, ra, il)
Kategorier: Amerikanska

How Much Should You Get Paid To Build Websites In 2019?

tors, 01/03/2019 - 13:50
How Much Should You Get Paid To Build Websites In 2019? How Much Should You Get Paid To Build Websites In 2019? Suzanne Scacca 2019-01-03T13:50:00+01:00 2019-01-04T13:29:47+00:00

(This is a sponsored post.) When a business owner is in need of a new website, one of the first answers they’re going to go in search of is: “How much should I pay for a website?”

Most of the articles they’ll find tell business owners that there are a few contributing factors when it comes to pricing:

  • Type of website (e.g. personal blog, small business website, booming e-commerce shop)
  • Size of website
  • Complexity of website

And some say that pricing should differ based on who builds your website (i.e. web designer vs. design agency).

The problem with this answer is that it teaches business owners to think about a website in terms of hours and manpower put into it. As you already know, this leaves many website clients focused on:

“How much work are you gonna do for me?”

Instead of:

“What will be the outcome of this investment?”

As you go out into the world, trying to reverse this faulty logic and convince customers to pay a fair wage for your web design services, you should do some calculations of your own. I’m going to provide you with a number of ways to set your prices and get paid well to create a website in 2019.

Different Ways To Get Paid To Build Websites

There are a few different ways you can get paid to build websites:

  1. Charge an hourly rate
  2. Charge a flat rate
  3. Charge a monthly rate.

There are pros and cons to each option. Let’s review what they are before we take a look at how to come up with a numerical value.

1. Charge An Hourly Rate

In this scenario, you set a value to each hour of work you put in. Then, when the project is done, you bill the client for total hours worked. Sources like Upwork put average hourly freelance rates between $15 and $75 an hour.

Here’s what you need to know about setting an hourly rate that works in both your favor and the client’s:

Client-Friendly (Pro)

As I explained above, many clients expect this form of payment. You put X amount of hours into designing their website and, in exchange, they’ll pay you for every hour worked.

Just remember to employ a time tracker, so you can later provide evidence of how much time was spent on the project (in case they ask for it).

An example of the in-app time tracker from AND CO.

AND CO has a great in-app time tracker that you can also add as a browser extension. What’s especially nice about this is that it integrates with your contract and invoicing software, so you can manage most of your financial relationship with clients in the same place.

Easy to Calculate (Pro)

For many new web designers, charging an hourly rate is an attractive prospect. Since you might not know how long a project will take — especially if it’s a kind you’ve never designed before — you still have an idea of what sort of hourly rate you want to charge.

If you don’t, I’d suggest using this web design calculator from BeeWits:

BeeWits provides a simple design calculator tool to help you determine an hourly

Input the estimated hours for each part of the website you’re contracted to do. Then apply what you think your hourly rate should be.

Take a look at the total. If it looks like a worthy value, commit to it and provide that to your clients.

You can adjust your hourly rate as you take on more projects and get a better sense for how long it actually takes. This is what Nela Dunato did:

“I charged per-project rates and logged my working hours so I knew what my hourly rate was on each project. At the end of the project I’d compare my actual hourly rate with my desired hourly rate and if it ended up lower I knew that I’d need to charge more on the next project of similar scope.” Secure Your Income (Pro)

Web designers have the unfortunate luck of running into clients that want to squeeze as much free work out of them as possible. However, when you agree to an hourly rate with a client, it’s very difficult for them to work their way around it.

Lisa Webster told SkillCrush:

“I’ve worked with too many entrepreneurs who keep adding changes that affect the entire design. This can result in hours of extra work that wouldn’t have been anticipated if you charged them per project.”

So, in a sense, an hourly rate does seem like a smart choice if you have clients trying to get the biggest bang for their buck — all at your expense. (I’ll talk a bit more about this below.)

Micro-Managing Clients (Con)

Although there are clear benefits to charging an hourly rate, there is a tradeoff which I hinted at earlier. In other words:

When clients associate a website with the hourly work put into it, they fail to see the value of it.

In turn, this puts a lot of pressure on you in the wrong way.

For instance, you’ll find that clients become super cognizant of the time you’re spending on something. When they see that beautiful mockup you’ve created for them, one of the first things they’ll ask is:

“How much time did you spend on this?”

This detracts from having more meaningful conversations that they’d otherwise initiate if they weren’t so focused on what you’re billing them.

Then, there are the inevitable interruptions that arise when you have to stop working to respond to clients who want to know what their total bill is to date. Worse, they might go the route of doubting how truthful you’re being:

“My nephew built a website for his team and he said it only took 8 hours. Why am I paying you for 50 hours if he can do it in a fifth of the time?”

You also might encounter clients who decide to cherry-pick what’s included in their website. Rather than allow you to develop a website from start to finish, you’ll end up committing to fractions of the website work which will lead to a disjointed experience on the frontend when the design conflicts with the copy and the SEO was dropped from the scope completely. (You get the idea.)

Earning Potential Cap (Con)

You have to be very careful about the hourly rate you charge for a website. While you might be okay with profit margins that come from a $30/hour rate… is it sustainable?

Think about it like this: You’re trying to be mindful of your clients. You know they need websites. You know they’re probably strapped for cash. And you don’t want to scare anyone away with high rates. So you compromise. $30 an hour will still net you a good chunk of change.

That said, when your business model is contingent on how much work you put into a website, that limits your earning potential. Let’s say you’re willing to work 50 hours a week and are able to bill clients for about 40 of it (the rest of the time is spent on business management).

40 hours × $30/hour = $1,200/week

That’s it. That is all the money you will ever be able to make.

If you choose to automate your workflows with software to free up time to work on more projects simultaneously, that reduces the number of billable hours you can charge. Automation just doesn’t work with this business model.

That said, I do think this is a viable option for new web designers. Until you have made a name for yourself, have an impressive portfolio to show off as well as clients who’ve given you rave reviews, it’ll be difficult to charge clients any other way. Just be careful with how much you set that hourly rate for.

2. Charge A Flat Rate

In this scenario, you charge a single flat rate for web design. You can also create a tier of web design packages that allow you to charge varying flat fees based on website type.

WebsiteSetup estimates these rates to be:

  • Between $1,000 and $3,000 for solo freelancers.
  • Between $10,000 and $15,000 for full-scale design agencies.

Here’s what you need to know to find your perfect flat rate:

Focus On Value (Pro)

The clear differentiator between charging website clients per hour and charging a flat rate is the mindset — both for you and the client. Instead of obsessing over how many hours went into the building of a website, both of you remain focused on the ultimate value of the product.

Because the client has a clearer understanding that they’re paying for the outcome, you can charge more as well.

Study Web Development breaks this logic down nicely:

“[I]f the business sells an average of ten 3D printers at an average of $2,000 each per month… and after calculating that I could potentially increase sales by 30% month after month, it then equals an extra three sales per month (or $6,000).”

In other words, your rate must reflect the true value of the website to the client.

That’s not to say you would charge $6,000 (in this specific case) and call it a day. You should think about what sort of ROI they’re going to get. Decide on a flat rate that reflects that and commit to it.

Faster Sales Process (Pro)

You’re not just a web designer. You’re also in charge of finding prospects and convincing them to become clients.

When you charge clients an hourly rate, you could certainly publish it to your website. But it leaves the matter open-ended, right? You might be able to say your web design services go for $100/hour… but they’re still going to want an idea of the invoice they can expect at the end.

Drawing up custom quotes for prospective clients takes time — time away from makes sales and designing websites. Rather than get wrapped up in the quoting process where prospects take their time looking over the numbers and question why certain parts of it would take so long (and whether they’re needed), a flat rate simplifies all of this.

Here is an example from Connective Web Design:

Connective Web Design simplifies pricing with flat rates

As you can see here, website types are explained in the most basic of terms (the ones that matter most to clients) and then a clear value is assigned to them. Clients also have the option to add more to the website, if they feel it’s necessary.

For those of you who are nervous about publishing your rates online, that’s okay. You don’t have to.

You can still charge flat rates — and the same ones for certain website types — even if it’s not publicly available. Just create a pricing sheet that you can send to prospects that inquire about your services.

Efficiency (Pro)

Because you’re not tied to the expectation that you’ll put in a certain number of hours in exchange for X amount of dollars, you can utilize more efficient ways of designing websites.

To start, you can templatize your workflow. You can do this with everything from communications you send to clients (e.g. contracts and emails) to the baseline frameworks you build websites from (like sitemaps and wireframes).

Smashing Magazine is an example of a source that provides templates and design kits that ease the amount of work designers have to do from-scratch:

Smashing Magazine offers free design templates and sets for designers to use.

Basically, anything that doesn’t require creativity and is repetitive in nature can be turned into a template.

You should also look for ways to offload menial and ill-fitting tasks. Anything that would be better handled by software should be automated. Anything else that doesn’t belong on your plate (maybe copywriting, QA and so on) should be outsourced to a team member or a third-party provider.

There are a number of benefits to this:

  1. You’ll maximize your efforts and be able to boost profit margins as you spend less money but accomplish more.
  2. You’ll enjoy the work you do because you won’t be tied down to tasks that don’t belong on your plate. Your happiness will have a direct impact on the quality of work you produce.
  3. As you delegate tasks to others and automate with software, you can multiply your efforts and take on more website projects simultaneously. Which means more money for you!
It Takes Time (Con)

Charging a flat rate to build websites is the best way to run and scale a design business. That said, it will be difficult to convince clients to pay that much if you don’t have enough experience behind you and proof to back it up.

Unlike hourly rates that are great for newer designers, this approach is one you probably can’t jump into right away.

That said, if you’re bringing extensive experience with you from a well-known design agency or another business and can demonstrate that you have the skills to meet client expectations, go for it. Just be aware that you’re more likely to face pushback if your business isn’t ready for it.

Scope Creep (Con)

Inevitably, you’re going to encounter clients that want you to do more without them having to pay for it. Some of them will be more blatant than others, asking how much you’re willing to give away in order to earn or retain their business.

Then, there are others who try a subtler approach.

“I really love Version 3 [of the design], but was wondering if you could add a live chat button to the bottom corner real quick. I saw another website that had it and I thought it looked great. Our customers would love it!”

That request, in and of itself, is not a problem. What is a problem is how you handle it.

If you have no contract in place, seemingly small changes like these can add up and eat away at your profit margins. Without a contract that limits how many revision requests are allowed or what exactly you’re to build, clients can technically ask for whatever they want and you’d have no recourse for charging more.

Now, let’s say you do have a contract in place. The terms of that agreement lay out what you are obligated to do. However, it’s an issue when you agree to make “small” changes that exceed those terms that it becomes an issue.

Even though the client has made it seem easy enough to add a button to the design, that button actually has to function on the website… which means they’re asking for much more than a design tweak. You now have to find a live chat platform, pay for it, integrate it with the site and make sure the button works properly.

Give a client an inch and they’ll run a mile with it. So, always make sure you have a strict contract in place and be willing to enforce those terms when clients try to push the limits of it.

3. Charge A Monthly Rate

In this scenario, you charge a flat rate, but it’s for a recurring web design service; not just a one-off build. The pros and cons of this are essentially the same as charging a flat, one-time fee.

The key difference is that this allows you to retain clients over the long-term — which is fantastic for creating a steady stream of predictable revenue for your business.

In terms of why you would do this, consider the following:

There are DIY website solutions like Wix and Weebly that appear to make the process of building one’s own site easy and cheap.

Then, there are traditional content management systems like WordPress that are more and more going the way of the user-friendly page builder, hoping to appeal to a larger set of users.

WordPress has recently implemented a more user-friendly page builder.

It’s already hard enough trying to convince clients that they need to hire a professional designer and to pay them a fair wage to build a website. Now, page builder tools are telling them that they really don’t need you.

That said, if you become an end-to-end website provider, you can not only charge a flat rate for your design services, you can do so month after month. It would simply require a shift in mindset from you, an adjustment to your branding and advertising, as well as some additional services.

In terms of how you would do this, consider offering a total solution so they don’t have to go anywhere else. The added convenience of entrusting all website-related matters to one professional could be the tipping point between you and the other options.

Think about rounding out your offering with:

  • Domain and web hosting management
  • SSL installation
  • CDN implementation
  • Premium theme licensing
  • Premium plugins licensing
  • Third-party storage services
  • Website maintenance
  • Marketing and SEO

You would also want ongoing web design edits and annual design audits to be a part of your ongoing service

Just as you would do with flat-rate design services, you could outsource items above that aren’t in your wheelhouse to others. Then, add a markup to the price when you sell the website package to the client. You’re still making money without having to increase your workload.

As you might imagine, this option for getting paid is for the experienced designer who has everything else in order and is in a position to comfortably shift their offering to one of greater value.

How To Decide How Much To Charge For Websites

Finally, we come to the valuation of your web design services.

Ask yourself the following in order to determine your best rate:

What kinds of websites are you capable of building?

Put them into buckets:

  • Small blogs,
  • Small- to medium-sized company websites,
  • Small- to medium-sized digital e-commerce stores,
  • Large e-commerce stores with physical and digital products,
  • Large e-commerce stores with brick-and-mortar presences,
  • Membership sites,
  • And so on.

Choose no more than three types to build. Then, break down how many pages as well as the key features you will need to build. This should tell you how much work is involved in creating a website of this nature. You can determine basic timelines and “manpower” based on this.

Who is your target client?

This is a slightly different question as it forces you to ask yourself who exactly you want to work for. This might mean defining a business by its size or it might mean choosing a niche industry to work in.

Either way, figure out who you want to build sites for and make sure they can afford your rates. (Check the competition’s rates to get an idea for what price range they’re willing to pay in.) If they can’t afford what you want to charge, they might be better off with the DIY approach and you should look for a new pool to play in.

How will these websites convert?

This is where the value question comes into play. In other words, what will your clients expect these websites to do for them:

  • Subscribe readers?
  • Schedule demos with prospective customers?
  • Sell goods?
  • Gain members?
  • Make money through advertising?
  • Something else?

You might not know exactly how much of a boost in conversions a new website will lead to, but you can estimate how much each new conversion is worth to them.

These three questions help you determine how much you should get paid to build websites. But there’s one more thing to think about:

“What do you have to spend to make this happen?”

You’re providing a valuable service here, but you’re not doing it all on your own, from a 10-year-old computer and in a rent-free apartment.

You have bills to pay that enable you to run your business in a way that leads to the top results you bring to clients. As such, you have to factor in your costs when determining the cost of your services.

The Bottom Line

Ultimately, it’s up to you to decide what monetary value you want to sign to your web design rates. However, if you want to remain competitive within the space, make sure to charge within one of the estimated ranges mentioned earlier — at least, to start. Then, as your business grows, you can steadily raise your prices in line with the increasing value of the websites you build.

(ms, ra, il)
Kategorier: Amerikanska

How To Learn CSS

ons, 01/02/2019 - 13:00
How To Learn CSS How To Learn CSS Rachel Andrew 2019-01-02T14:00:30+02:00 2019-01-04T13:29:47+00:00

I get a lot of people asking me to recommend to them tutorials on various parts of CSS, or asking how to learn CSS. I also see a lot of people who are confused about bits of CSS, in part because of outdated ideas about the language. Given that CSS has changed quite substantially in the last few years, this is a really good time to refresh your knowledge. Even if CSS is a small part of what you do (because you work elsewhere in the stack), CSS is how things end up looking as you want them on screen, so it is worth being reasonably up to date.

Therefore, this article aims to outline the key fundamentals of CSS and resources for further reading on key areas of modern CSS development. Many of those are things right here on Smashing Magazine, but I’ve also selected some other resources and also people to follow in key areas of CSS. It’s not a complete beginner guide or intended to cover absolutely everything. My aim is to cover the breadth of modern CSS with a focus on a few key areas, that will help to unlock the rest of the language for you.

Language Fundamentals

For much of CSS, you don’t need to worry about learning properties and values by heart. You can look them up when you need them. However, there are some key underpinnings of the language, without which you will struggle to make sense of it. It really is worth dedicating some time to making sure you understand these things, as it will save you a lot of time and frustration in the long run.

Front-end is messy and complicated these days. That's why we publish articles, printed books and webinars with useful techniques to improve your work. Even better: Smashing Membership with a growing selection of front-end & UX goodies. So you get your work done, better and faster.

Explore Smashing Membership ↬ Selectors, More Than Just Class

A selector does what it says on the tin, it selects some part of your document in order that you can apply CSS rules to it. While most people are familiar with using a class, or styling an HTML element such as body directly, there are a large number of more advanced selectors which can select elements based on their location in the document, perhaps because they come directly after an element, or are the odd rows in a table.

The selectors that are part of the Level 3 specification (you may have heard them referred to as Level 3 selectors) have excellent browser support. For a detailed look at the various selectors you can use, see the MDN Reference.

Some selectors act as if you had applied a class to something in the document. For example p:first-child behaves as if you added a class to the first p element, these are known as pseudo-class selectors. The pseudo-element selectors act as if an element was dynamically inserted, for example ::first-line acts in a similar way to you wrapping a span around the first line of text. However it will reapply if the length of that line changes, which wouldn’t be the case if you inserted the element. You can get fairly complex with these selectors. In the below CodePen is an example of a pseudo-element chained with a pseudo-class. We target the first p element with a :first-child psuedo-class, then the ::first-line selector selects the first line of that element, acting as if a span was added around that first line in order to make it bold and change the color.

See the Pen first-line by Rachel Andrew (@rachelandrew) on CodePen.

Inheritance And The Cascade

The cascade defines which rule wins when a number of rules could apply to one element. If you have ever been in a situation where you can’t understand why some CSS doesn’t seem to be applying, it’s likely the cascade is tripping you up. The cascade is closely linked to inheritance, which defines which properties are inherited by the child elements of the element they are applied to. It is also linked to specificity, different selectors have different specificity which controls which wins when there are several selectors which could apply to one element.

Note: To get an understanding of all of these things, I would suggest reading The Cascade and Inheritance, in the MDN Introduction to CSS.

If you are struggling with getting some CSS to apply to an element then your browser DevTools are the best place to start, take a look at the example below in which I have an h1 element targeted by the element selector h1 and making the heading orange. I am also using a class, which sets the h1 to rebeccapurple. The class is more specific and so the h1 is purple. In DevTools, you can see that the element selector is crossed out as it does not apply. Once you can see that the browser is getting your CSS (but something else has overruled it), then you can start to work out why.

See the Pen specificity by Rachel Andrew (@rachelandrew) on CodePen.

DevTools can help you see why some CSS is not applying to an element (Large preview) The Box Model

CSS is all about boxes. Everything that is displayed on the screen has a box, and the Box Model describes how the size of that box is worked out — taking into account margins, padding, and borders. The standard CSS Box Model takes the width that you have given an element, then adds onto that width the padding and border — meaning that the space taken up by the element is larger than the width you gave it.

More recently, we have been able to choose to use an alternate box model which uses the given width on the element as the width of the visible element on screen. Any padding or border will inset the content of the box from the edges. This makes a lot more sense for many layouts.

In the demo below, I have two boxes. Both have a width of 200 pixels, with a border of 5 pixels and padding of 20 pixels. The first box uses the standard box model and so takes up a total width of 250 pixels, the second the alternate Box Model and so is actually 200 pixels wide.

See the Pen box models by Rachel Andrew (@rachelandrew) on CodePen.

Browser DevTools can once again help you with understanding the box model in use. In the below image, I use Firefox DevTools to inspect a box using the default content-box box model. The tools tell me this is the Box Model in use, and I can see the sizing and how the border and padding is added to the width I assigned.

DevTools help you see why a box is a certain size, and the box model in use (Large preview)

Note: Prior to IE6, Internet Explorer used the alternate Box Model, with padding and borders insetting content away from the given width. So for a while browsers used different Box Models! When frustrated by interoperability issues today, be glad that things have improved so we aren’t dealing with browsers calculating the width of things differently.

There is a good explanation of the Box Model and Box Sizing on CSS Tricks, plus an explanation of the best way to globally use the alternate box model in your site.

Normal Flow

If you have a document with some HTML marking up the content and view it in a browser, it will hopefully be readable. Headings and paragraphs will start on a new line, words display as a sentence with a single white space between them. Links for formatting such as em does not break up the sentence flow. This content is being displayed in Normal Flow, or Block Flow Layout. Each part of the content is described as “in flow”; it knows about the rest of the content and so doesn’t overlap.

If you work with rather than against this behavior, your life is made much easier. It is one of the reasons why starting with a correctly marked up HTML document makes a lot of sense, as due to normal flow and the inbuilt stylesheets that browsers have which respect it, your content starts from a readable place.

Formatting Contexts

Once you have a document with content in normal flow, you may want to change how some of that content looks. You do this by changing the formatting context of the element. As a very simple example, if you wanted all of your paragraphs to run together and not start on a new line, you could change the p element to display: inline changing in from a block to an inline formatting context.

Formatting contexts essentially define an outer and an inner type. The outer controls how the element behaves alongside other elements on the page, the inner controls how the children should look. So, for example, when you say display: flex you are setting the outer to be a block formatting context, and the children to have a flex formatting context.

Note : The latest version of the Display Specification changes the values of display to explicitly declare the inner and outer value. Therefore, in the future you might say display: block flex; (block being the outer and flex being the inner).

Read more about display over at MDN.

Being In Or Out Of Flow

Elements in CSS are described as being ‘in flow’ or ‘out of flow.’ Elements in flow are given space and that space is respected by the other elements in flow. If you take an element out of flow, by floating or positioning it, then the space for that element will no longer be respected by other in flow items.

This is most noticeable with absolutely positioned items. If you give an item position: absolute it is removed from flow, then you will need to ensure that you do not have a situation in which the out of flow element overlaps and makes unreadable some other part of your layout.

See the Pen Out of Flow: absolute positioning by Rachel Andrew (@rachelandrew) on CodePen.

However, floated items also are removed from flow, and while following content will wrap around the shortened line boxes of a floated element, you can see by placing a background color on the box of the following elements that they have risen up and are ignoring the space used by the floated item.

See the Pen Out of flow: float by Rachel Andrew (@rachelandrew) on CodePen.

You can read more about in flow and out of flow elements on MDN. The important thing to remember is that if you take an element out of flow, you need to manage overlap yourself as the regular rules of block flow layout no longer apply.

Layout

For over fifteen years we have been doing layout in CSS without a designed for the job layout system. This has changed. We now have a perfectly capable layout system which includes Grid and Flexbox, but also Multiple-column Layout and the older layout methods used for their real purpose. If CSS Layout is a mystery to you, head on over to the MDN Learn Layout tutorial, or read my article Getting Started With CSS Layout here on Smashing Magazine.

Do not imagine that methods such as grid and flexbox are somehow competing. In order to use Layout well, you will sometimes find a component is best as a flex component and sometimes as Grid. On occasion, you will want the column flowing behavior of multicol. All of these are valid choices. If you feel you are fighting against the way something behaves, that is, in general, a very good sign that it might be worth taking a step back and trying a different approach. We are so used to hacking at CSS to make it do what we want, and so we are likely to forget that we have a number of other options to try.

Layout is my main area of expertise and I’ve written a number of articles here on Smashing Magazine and elsewhere to try and help tame the new Layout landscape. In addition to the Layout article mentioned above, I have a whole series on Flexbox — start with What Happens When You Create a Flexbox Flex Container. On Grid By Example, I have a whole bunch of small examples of CSS Grid — plus a video screencast tutorial.

In addition — and especially for designers — check out Jen Simmons and her Layout Land video series.

Alignment

I’ve separated Alignment out from Layout in general because while most of us were introduced to Alignment as part of Flexbox, these properties apply to all layout methods, and it is worth understanding them in that context rather than thinking about “Flexbox Alignment” or “CSS Grid alignment.” We have a set of Alignment properties which work in a common way where possible; they then have some differences due to the way different layout methods behave.

On MDN, you can dig into Box Alignment and how it is implemented for Grid, Flexbox, Multicol, and Block Layout. Here on Smashing Magazine, I have an article specifically covering alignment in Flexbox: Everything You Need To Know About Alignment In Flexbox.

Sizing

I spent much of 2018 speaking about the Intrinsic and Extrinsic Sizing specification, and how it relates to Grid and Flexbox in particular. On the web, we are used to setting sizing in lengths or percentages, as this is how we have been able to make Grid-type layouts using floats. However, modern layout methods can do a lot of the space distribution for us — if we let them. Understanding how Flexbox assigns space (or the Grid fr unit works), is worth your time.

Here on Smashing Magazine, I’ve written about Sizing in Layout in general and also for Flexbox in How Big Is That Flexible Box?.

Responsive Design

Our new layout methods of Grid and Flexbox often mean that we can get away with fewer media queries than we needed with our older methods, due to the fact that they are flexible and respond to changes in viewport or component size without us needing to change the widths of elements. However, there will be places where you will want to add in some breakpoints to further enhance your designs.

Here are some simple guides to Responsive Design, and for media queries, in general, check out my article Using Media Queries for Responsive Design in 2018. I take a look at what Media Queries are useful for, and also show the new features coming to Media Queries in Level 4 of the spec.

Fonts And Typography

Along with Layout, the use of fonts on the web has undergone huge change in the last year. Variable Fonts, enabling a single font file to have unlimited variations, are here. To get an overview of what they are and how they work, watch this excellent short talk from Mandy Michael: Variable Fonts and the Future of Web Design. Also, I would recommend Dynamic Typography With Modern CSS and Variable Fonts by Jason Pamental.

To explore Variable Fonts and their capabilities, there is a fun demo from Microsoft plus a number of playgrounds to try out Variable Fonts — Axis Praxis being the most well known (I also like the Font Playground).

Once you start working with Variable Fonts, then this guide on MDN will prove incredibly useful. To learn how to implement a fallback solution for browsers which does not support Variable Fonts read Implementing a Variable Font With Fallback Web Fonts by Oliver Schöndorfer. The Firefox DevTools Font Editor also has support for working with Variable Fonts.

Transforms And Animation

CSS Transforms and Animation are definitely something I look upon a need-to-know basis. I don’t often need to use them, and the syntax seems to hop right out of my head between uses. Thankfully, the reference on MDN helps me out and I would suggest starting with the guides on Using CSS Transforms and Using CSS Animations. Zell Liew also has a nice article that provides a great explanation to CSS transitions.

To discover some of the things that are possible, take a look at the Animista site.

One of the things that can be confusing about animations, is which approach to take. In addition to what is supported in CSS, you might need to involve JavaScript, SVG, or the Web Animation API, and these things all tend to get lumped together. In her talk, Choose Your Animation Adventure recorded at An Event Apart, Val Head explains the options.

Use Cheatsheets As A Reminder, Not A Learning Tool

When I mention Grid or Flexbox resources, I often see replies saying that they can’t do Flexbox without a certain cheatsheet. I have no problem with cheatsheets as a memory helper to look up syntax, and I’ve published some of my own. The problem with entirely relying on those is that you can miss why things work as you copy syntax. Then, when you hit a case where that property seems to behave differently, that apparent inconsistency seems baffling, or a fault of the language.

If you find yourself in a situation where CSS seems to be doing something very strange, ask why. Create a reduced test case that highlights the issue, ask someone who is more familiar with the spec. Many of the CSS problems I am asked about are because that person believes a property works in a different way to the way it works in reality. It’s why I talk a lot about things like alignment and sizing, as these are places where this confusion often lives.

Yes, there are strange things in CSS. It is a language that has evolved over the years, and there are things about it that we can’t change — until we invent a timemachine. However, once you have some basics down, and understand why things behave as they do, you will have a much easier time with the trickier places.

(il)
Kategorier: Amerikanska

2018: A Smashing Year In Review

mån, 12/31/2018 - 14:00
2018: A Smashing Year In Review 2018: A Smashing Year In Review Rachel Andrew 2018-12-31T14:00:30+01:00 2018-12-31T13:41:33+00:00

As we come to the end of 2018, I spoke to some of the Smashing team, to get some thoughts on what the past year has been like for Smashing Magazine. We’re a small and fully remote team, communicating via Slack and Notion. Many of us only work part-time for Smashing, however, in many ways, I think that is one of our strengths.

We’re not just the publishers of an online magazine or conference organizers, we are people who work in the web industry. Among the team, products have been launched, books are being written, conferences have been spoken at, and websites launched that have nothing to do with Smashing Magazine itself. I love that. It stops us being insular, and I hope this helps us to constantly broaden our reach — bringing people together from all over the world to share ideas and inspiration as we all work together to build a better web.

As Editor in Chief of Smashing Magazine, I look after the content that goes out on the online magazine, and also our upcoming print magazine for members. This year, we have published almost every weekday — that represents over 290 articles! That’s a whole lot of content on subjects from privacy and accessibility to CSS and WordPress. While I read every article that goes out, I do not have the expertise to know everything about all of these subjects. I couldn’t do my job without the help of our talented editors who work with individual authors: Alma Hoffmann (Design), Chui Chui Tan (UX Design), Drew McLellan (Coding), Jim Dabell (Mobile), Marko Dugonjić (Typography), Michel Bozgounov (Graphics), and Rey Bango (Coding). Plus thanks to Iris Lješnjanin, Markus Seyfferth, Yana Kirilenko, Cosima Mielke, Andrew Lobo and Ricardo Gimenes for their hard work and efforts.

In Between Timezones

On a personal note, this year has once again involved a lot of travel, as I continue to tour around speaking about new CSS and CSS Layout. That has included talks and workshops for Smashing. In total (with speaking engagements, workshops and CSS Working Group meetings), I have traveled 272,865 kilometers while visiting 45 cities and 15 countries. That amounts to spending 146 days on the road.

Here’s a fun fact: My weekly standup post in our Smashing Slack usually starts with sharing the timezones I’m going to be in that week. Well, next year will involve more travel, and I’ll be bringing my new CSS Layout workshop to San Francisco, Toronto, and New York.

As for the magazine, I hope we can continue to publish great content from authors — those who are experienced writers but also folks writing for the first time. We are very happy to work with you to shape an article for publication. Personally, writing has helped boost my career more than anything else I have done. I love to help other people get started. So, if you have an idea, read this and then send over an outline.

Please don’t hesitate. Some of our most popular posts have been beginner guides to a technology, so don’t feel you need to have solved a big problem, or have some brand new technique in order to contribute. A nice technique, demonstrated and explained well, is worth a lot to someone who has just hit that same issue.

Anyway, enough from me! What were the highlights of the year for everyone else here at Smashing?

Vitaly Friedman: A Transitional Year

2018 was a quite busy and adventurous year for me, with a good number of ups and downs, challenges, surprises, and rewards. I was honored to have had the opportunity to run trainings, workshops and even offer consultancy to the European Parliament, EPAM, OTTO, Sipgate, Axel Springer and Wondrous, among others. I was happy to support dozens of local meet-ups and conferences around the world with the kind help of our Smashing Members.

Earlier this year, I explored how we can improve the level of education for front-end and design. While speaking at universities and schools, I was also teaching to get a sense of what’s required to set up a proper design school. In February, I taught at the New Digital School in Porto, Portugal, for a week, while exploring the state of front-end and responsive interface design in a class of 20 students. In June, I helped dear friends from the Projector Design School in Kiev, Ukraine, set up Berlin Design Campus, an initiative for Ukrainian students to explore how digital agencies and designers work and live in Berlin. In October, I participated in a week-long co-working co-living campus in Mokrin, Serbia.

Specifically, I was exploring the state of design and front-end in uncommon locations, mostly second- and third-largest cities: Porto and Braga in Portugal (thanks Tiago!), Yerevan in Armenia (thanks Sona and Sargis!), Gdansk in Poland (thanks Patrycja!), Salzburg in Austria (thanks Markus!), Moscow and Saint Petersburg in Russia (thanks Julia, Daria, Alex, Andrey, Vadim and Alexey!), Split and Labin in Croatia (thanks Toni, Antonio and Domagoj!), Belgrade and Mokrin in Serbia (thanks Tatjana and Marija!), Belfast in Northern Ireland (thanks Tash and Oliver!), Manila in Philippines (thanks Sophia!), Tallinn in Estonia (thanks Artur!). Much of the time in the second half of the year was spent with wonderful people at the European Parliament in Brussels, where Nicolas and Manuel were kind enough to invite me to work on refinements and improvements of UIs for election sites, media library, and a few smaller sites. That was quite a bit of traveling, with the absolute worst highlight of the last years being a massively delayed 47-hour trip to the Philippines due to a closed runway at the Manila airport (thanks for bearing with me through this, dear Sophia and the crew!)

Over the course of the year, I have spoken at 17 conferences, and was privileged to meet many — many! — remarkable people. It ended up with conversations I will remember for years to come. Some of these conversations changed me for the better as a person and professional, so I was happy to receive constructive criticism on MCing skills, writing, as well as code and design. I managed to wrap my head around the intricacies of CSS Grid Layout and Service Workers but also spent a lot of time learning about network protocols and the underlying layers of the Internet. I also attended 6 workshops to stay afloat of what’s happening in our industry these days and sharpened up my front-end/UX/communication skills. In September, I was honored to participate in the Mozilla Tech Speakers coaching, along with Ada Rose Edwards and Marc Thiele, mentoring and giving feedback to dozens of new speakers (here’s a review of the event by Havi Hoffman).

In terms of the Smashing Universe, we spent quite a bit of time revising our workflows and streamlining our processes for conferences, books, and the Smashing Membership. With fantastic event management skills of Mariona Ciller, Amanda Tamny Annandale and Charis Rooda, we’ve run 5 conferences this year: in London, San Francisco, Toronto, Freiburg and New York.

For the first time, we experimented with a ’no-slides’ format in Toronto (every speaker presented “live” on stage in front of a large screen shwoing how they build and design — with performance and accessibility audits to live designing/coding/sketching sessions on stage. In fact, that’s the format we are going to continue exploring in 2019.

Editor’s Note: If this format sounds interesting to you, you can watch all of the SmashingConf talks on Vimeo.

Nadieh Bremer presenting at SmashingConf Toronto (watch on Vimeo)

After many months of work, we finally published “Smashing Book 6” and “Form Design Patterns” by Adam Silver, but quite a bit of time was spent on the next upcoming books that will be published in the next years. For the Membership, we were able to secure Scott Whitehead and Bruce Lawson to help evolve the Membership program.

On a more personal level, I will vividly remember vacations to Morocco (Marrakesh, Fez and the Sahara desert trip) and Sardinia (Northern part) earlier this year. Also, on a sad note, I’ve moved out from Vilnius, Lithuania, where I’ve resided for the past 3 years.

Overall, I see 2018 as an important “transitional” year which took a lot of time, effort, and hard work. It feels like it’s been a transitional year between how things used to be and what’s coming up next. With this in mind, I couldn’t be more excited to see what 2019 will bring us! Expect a few new books coming up, Smashing Magazine Print edition, four Smashing Conferences (San Francisco, Toronto, Freiburg, New York) and many wonderful Smashing TV sessions!

Markus Seyfferth: Never Stand Still

Change happens everywhere and all the time — in all organizations, agencies, and businesses. If you don’t thrive change on your own, then there comes a time when change takes place on its own and things get out of control.

Looking back on the year 2018, we’ve undergone changes to even better fit the needs of our readers. We published the Smashing Book 6 named “New Frontiers in Web Design”, a book packed into the probably the most beautiful cover that we have had designed so far. It’s a book that sheds light on all kinds of upcoming challenges that await web designers and developers in the near future.

Smashing Book 6 (photo credit Marc Thiele)

We also published Form Design Patterns, a book that focusses on building all sorts of accessible and resilient web forms, and how to make them pretty (thanks to progressive enhancement) — a book that I personally learned a lot from. We’ve also started working on two new books that we’ll be publishing early next year: “Art Direction on the Web” by Andy Clarke, and “Inclusive Components” by Heydon Pickering. I am eagerly looking forward to holding both of them in my hands!

At the end of last year, we did something that we usually wouldn’t do because it would be too much of a risk. We launched a fully redesigned site: we migrated the entire magazine, the Job Board, and our Smashing Shop onto a new platform, and also launched our Membership initiative to reduce advertising on the site and to make Smashing more independent from ads in the long run. All of this took place at the same time. Was it worth it? A definite “Yes!” We’ve seen a noticeable uptrend in our analytics and many positive outcomes. At around mid-2018, we had already crossed the 1,000 members mark, and we look forward to breaking the next big mark in the next year (always with the long-term goal of getting fully independent within the next three years)!

That’s right; Smashing Membership continues to evolve. In the upcoming months, we’ll be introducing a new print magazine for our Members — something that is both visually appealing and also most useful to read. Rachel will be building the print magazine mostly with print.css, so I’m really looking forward to seeing how this will turn out and whether we can reuse some of it for our upcoming books!

And that’s not the only sort of change that is still ongoing at Smashing. We also tried a new live coding and design conference format at this year’s SmashingConf in Toronto; we thought that the old format had gotten a bit too much of the same, something that makes SmashingConf a bit too similar with what others already do. After all, we want to run conferences that contain content that we ourselves find most useful and interesting, and the new live format brings precisely that! It did take quite a bit of a risk though, and we’re thrilled that it turned out to be a tremendous success! So we are going to double down with this new format in the next year.

Last but not least, we also moved our smashingconf.com site to Netlify just recently, but that happened mostly in the background, so if no one really noticed the change, I guess that’s a good thing.

Yes, 2018 was a year full of transitions, but I guess you never can afford to stand still anyway? ;-)

Bruce Lawson: Joining The Team

Before the end of the first year of Smashing Membership, we reached a thousand members — thank you so much, everyone! Those extra-special people who were with us for the whole year received a little thank you in the post.

I joined Scott in October, which allowed us to increase the number of Smashing TV webinars (which are free to Members and Smashing Members, of course). We’ve had sessions on coding Machine Learning, Designing with Ethics, the State of the Web in South-East Asia, and statistical techniques for collecting user data without compromising privacy. (All are recorded and available to members, if you have FOMO!)

When we set up Membership, we promised that it would be an inclusive place where lesser-heard voices (in addition to big names) would be beamed straight to your living room/ home office/ sauna over Smashing TV. While we’ve been speaking at other non-Smashing events, we’ve watched other sessions from lesser-known talents in our industry. With only $5 to $9 a month), your Membership subscription allows us to bring you even greater content, pay all our contributors fairly, and reduce advertising on the site.

Next year, we’ll be increasing the number of webinars again. Lined up is a course on how to make Progressive Web Apps, Internationalization, Semantic HTML, Houdini, as well as a monthly show hosted by me and Vitaly with industry guests. We sincerely hope you’ll join us!

Amanda Annandale: A Year Of Firsts

2018 was a year of firsts, new cities, new attendees, new speakers, and even a couple new formats. We had more than a few challenges in store, but if you have any experience with the Smashing Team, you’ll know that we thrive on challenges.

We started the year in London (our first time in the capital city and the first time in England in a couple of years). The sold-out conference took place in LSO’s St. Luke’s Church, and bathed in sunlight. This performance-based conference brought in a new crowd of attendees and speakers — all discussing why Performance Matters, the common pitfalls, and the tips and tricks for improving the day to day user experience. With Una Kravets and Patrick Hamman as MCs, the experience was new and empowering.

In April, the Smashing team headed back to San Francisco. The weather was wonderful as we returned to the bay, with 14 speakers, 8 workshops, and nearly 500 attendees. Held at the Palace of Fine Arts, Mina Markham walked us through the process of redesigning Slack, while Joe Leech broke down the process of “Designing Powerful User Experiences With Psychology.” We toured the area, competed with each other at the arcade, and came together to find new ways to solve new processes and challenges.

Backstage at Smashing Conf Toronto (Photo credit: Marc Thiele)

A couple of months later, SmashingConf experimented with its boldest change: no slides! All of the presenters, from Aaron Draplin to Rachel Andrew, tossed out their ‘normal’ presentation format and showed the attendees how they work. The experience was enlightening, showing how similar we all are in our work processes in some ways, while approaching things from an entirely different angle in others. In fact, we loved it so much that we’ve decided 2019 should be the year of No Slides!

The end of the summer is when Smashing goes home. Set on the foothills of the Black Forest, at the infamous Markethall, SmashingConf came back to Freiburg, Germany with 14 speakers. Chui Chui Tan spoke about Designing for Global Audiences while Josh Clark talked about Design in the Era of the Algorithm. In addition, we had a new experience adding community lightning talks to our program. No matter what changes though, there’s nothing like hosting SmashingConf Freiburg and bringing people to our home.

And finally, we ended the year in the city that never sleeps — and lived up to the name! In New York City, we had 14 speakers, 8 workshops, a packed speaker panel, a couple of retro parties, and events that kept everyone busy for four days. Smashing challenged the sold-out audience with an engaging group of speakers from John Maeda to Debbie Millman and Sara Soueidan. No matter how many times we go back, the experiences always change us in a way.

But, then again, that’s how the Smashing team always feels at the end of a season: challenged, moved, and driven. We’ve learned from over 30 speakers, met over 1500 attendees, flown to 5 cities, eaten lots of incredible food, and had countless wonderful experiences. Now, the team is ready to create, improve, and progress to see what 2019 has in store for us!

Marc Thiele: Moving Closer Together

If you ask me, I think that this year went by really quickly. When I look back, I see five Smashing conferences, which took place in London, San Francisco, Toronto, Freiburg and New York, as well as many improvements which we’ve achieved in the background.

Editor’s note: Marc has taken photos of many of our conferences, you can find the albums on his Flickr account.

When I say background, I mean that maybe readers, attendees or folks who visit Smashing Magazine don’t even recognize the work we do behind the curtains. Of course, there are final products that are presented in the articles published on Smashing Magazine, the Smashing Books, or projects that have been brought to your attention via Smashing TV or while attending a Smashing conference or workshops, but there is a small team of people who work hard to continue improving workflows and experiences for our cherished customers. What you often don’t see is see the messy middle and the bumpy journey we are on — from talking about a new idea to the final product. There actually is a lot of work, a lot of failure, and many discussions and conversations involved.

From the end of April onwards, we had many meetings and conversations to see where we can improve the work that we do. Defining clear roles and tasks, checking how the many different parts of the Smashing Universe can grow closer together, and also looking for new, exciting ideas to bring to life. There are also many new faces on board of the Smashing boat — fresh energy to move forward — and I am very much looking forward to seeing the results of their passion and input. Expect the quality you know from the magazine and the events and an even better Smashing Membership, Smashing TV, and maybe the one or other new idea.

So, when I personally speak about 2018, I tend to say that this year was not too good and felt strange for a reason that I can’t really grab and describe. Perhaps it is the overall mood and spirit that comes from what you see when you turn on the news, read the newspaper in the morning or talk to your neighbor? All I know is that it is important to stay positive and have a positive look into the future. I ran three very successful beyond tellerrand shows in Munich, Dusseldorf and Berlin, and I’ve seen the success we had with all the Smashing conferences and the improvements we’ve accomplished for the overall Smashing experience.

My wish for the upcoming new year: let’s meet — even more. Let’s share ideas and what we’ve learned. Let’s not just meet on the web, but in real life. It is wonderful to teach and share and to see other people taking what they’ve learned from you and take it further to create, inspire and teach others with it. One more thing that also is important: Stay curious and ask questions. Never fear to ask a question as you “might look stupid asking.” If you have a question, then it’s stupid not to ask.

With this, I wish everyone a wonderful journey over to 2019 and I am looking forward to meeting you in 2019!

Alma Hoffmann: Reflections

Working at Smashing Magazine has been a very rewarding experience. Each time one of the articles I have edited gets published, I think I am happier than the authors.

One article, in particular, that was very meaningful to me was written by Trine Falbe and titled “Ethical Design The Practical Getting-Started Guide.” We all talk about ethical design, but not often we are provided with a way to get started. It is a good article to reflect upon as the current year ends and the new starts.

Thank you, Smashing, for keeping me around!

A Truly Smashing Year

Reading all of this certainly makes me proud to be part of the Smashing team. At the heart of everything Smashing, is an absolute focus on you, our readers, members, and conference attendees. We hope that the things we do demonstrate that, and we are always happy to listen and to learn when we get it wrong!

I’m excited for the things we will be sharing in 2019, and along with all of the team I am always happy to hear your feedback. What can we do better? What do you want to learn? How can we help? We will be opening up a survey for some more formal feedback early in 2019, but our door (or email inbox at least) is always open!

(il)
Kategorier: Amerikanska

New Year, New Beginnings (January 2019 Wallpapers Edition)

mån, 12/31/2018 - 13:30
New Year, New Beginnings (January 2019 Wallpapers Edition) New Year, New Beginnings (January 2019 Wallpapers Edition) Cosima Mielke 2018-12-31T13:30:00+01:00 2018-12-31T13:41:33+00:00

Maybe you’ve already started into the new year when you read this, maybe you’re still waiting for the big countdown to begin. No matter what: Let’s welcome 2019 with a fresh wallpaper!

To give you a little inspiration boost, artists and designers from across the globe once again tickled their creativity and designed unique wallpapers for you to indulge in. All of them come in versions with and without a calendar for January 2019 and can be downloaded for free — just like every month since more than nine years already. At the end of this post, we also compiled some January favorites from past years that are too good not to share. Have an exciting new year!

Further Reading on SmashingMag:

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?

Meet Smashing Book 6 — our brand new book focused on real challenges and real front-end solutions in the real world: from design systems and accessible single-page apps to CSS Custom Properties, CSS Grid, Service Workers, performance, AR/VR and responsive art direction. With Marcy Sutton, Yoav Weiss, Lyza D. Gardner, Laura Elizabeth and many others.

Table of Contents → Let The Magic Begin

“When the noisy holidays stay behind us, and everything calms down to a peaceful setting, it is a perfect moment for the magic to step in and start making our wishes, hopes, and decisions come true. Happy January, everyone!” — Designed by PopArt Web Design from Serbia.

New Year

“The dawn of January the 1st of 2019 is the beginning of a new year which gives dreams, hopes and a lot more to billions of people around the world.” — Designed by Sweans Technologies Ltd. from London.

A New Beginning

“I wanted to do a lettering-based wallpaper because I love lettering. I chose January because for a lot of people the new year is perceived as a new beginning and I wish to make them feel as positive about it as possible! The ideia is to make them feel like the new year is (just) the start of something really great.” — Designed by Carolina Sequeira from Portugal.

Intense Winter

“Winter brings both Christmas & New Year together. A season which provides time for comfort, food, warmth, and touch of a friendly hand and for a talk beside the fire.” — Designed by Call Taxi Software from India.

New Year, New Chances

“The climate is changing very fast. 2019 is a new chance to save the earth, the people and the animals who suffer from global warming.” — Designed by Melissa Bogemans from Belgium.

A Cold But Happy 2019

“January is a cold but cozy month so i tried to make my illustrations in cold and cozy colors. Blue and white as the cold colors and red darkblue as the cozy colors. I made some small details like reindeers, clouds snowflakes and trees to make it look better.” — Designed by Vince Teckmans from Belgium.

Cold... Penguins!

“The new year is here! We waited for it like penguins. We look at the snow and enjoy with it!” — Designed by Veronica Valenzuela from Spain.

Hidden Gem

“Kingfishers are called ‘ijsvogels’ (ice-birds) in Dutch. Not because they like winter cold, but because of the intense blue and teal colors...” — Designed by Franke Margrete from the Netherlands.

Snowman

Designed by Ricardo Gimenes from Sweden.

Freezy Hope For 2019

“I took this photo while walking around the city where I live. It's located in the Vosges, France. It was cold but without snow. White color is coming from freezy touchs of the fog remaining on everything. I found it amazing. White is a color for hope. Let's have a pleasant year 2019” — Designed by Philippe Brouard from France

Oldies But Goodies

The past New Year’s editions have brought forth some timeless wallpaper goodies that work equally well in 2019. Please note that they don’t come with a calendar. May we present…

Open The Doors Of The New Year

“January is the first month of the year and usually the coldest winter month in the Northern hemisphere. The name of the month of January comes from ‘ianua’, the Latin word for door, so this month denotes the door to the new year and a new beginning. Let’s open the doors of the new year together and hope it will be the best so far!” — Designed by PopArt Studio from Serbia.

Start Somewhere

“If we wait until we’re ready, we’ll be waiting for the rest of our lives. Start today - somewhere, anywhere.” — Designed by Shawna Armstrong from the United States.

Winter Leaves

Designed by Nathalie Ouederni from France.

Boom!

Designed by Elise Vanoorbeek from Belgium.

January Fish

“My fish tank at home inspired me to make a wallpaper with a fish :)” — Designed by Arno De Decker from Belgium.

Angel In Snow

Designed by Brainer from Ukraine.

Happy Hot Tea Month

“You wake me up to a beautiful day; lift my spirit when I’m feeling blue. When I’m home you relieve me of the long day’s stress. You help me have a good time with my loved ones; give me company when I’m all alone. You’re none other than my favourite cup of hot tea.” — Designed by Acodez IT Solutions from India.

Dare To Be You

“The new year brings new opportunities for each of us to become our true selves. I think that no matter what you are — like this little monster — you should dare to be the true you without caring what others may think. Happy New Year!” — Designed by Maria Keller from Mexico.

White Mountains

“In Central Europe, we’ve had a very warm beginning of winter. I hope there will be lots of powder for snowboarding soon. I’d love to see the French Alps again as white as in 2015 when I took this photo.” — Designed by Annika Oeser from Germany.

Winter Getaway

“What could be better, than a change of scene for a week? Even if you are too busy, just think about it.” — Designed by Igor Izhik from Canada.

Be Awesome Today

“A little daily motivation to keep your cool during the month of January.” — Designed by Amalia Van Bloom from the United States.

Japanese New Year

Designed by Evacomics from Singapore.

A New Start

“The new year brings hope, festivity, lots and lots of resolutions, and many more goals that need to be achieved. This wallpaper is based on the idea of ‘A New Start’.” — Designed by Damn Perfect from India.

The Way To Get Started

“January is all about renewing efforts and making plans, but sometimes we need little reminders that plans don’t matter if you don’t do anything about them. Initially, I was inspired by the idea of a fresh sheet of paper, which became the background for some great words from Walt Disney.” — Designed by Resa Barillas from the United States.

Three Wise Men Of The East

“In Belgium remember the Three Wise Men of the East is a tradition. It involves children going from door to door, dressed up as the three Wise Men. They sing a little song, in exchange for sweets and/or money. The design is very minimalistic and ‘flat’. I hope you like it :)” — Designed by Jeroen Bartels from Belgium.

Rest Up For The New Year

“I was browsing for themes when I found this “Festival of Sleep” that takes place on the 3rd, and I’m a big fan of sleep… Especially in these cold months after the holiday craziness, it’s nice to get cozy and take a nice nap.” — Designed by Dorothy Timmer from Central Florida, USA.

Hello Summer In Australia

Designed by Tazi Designs from Australia.

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!

(il)
Kategorier: Amerikanska

Will PWAs Replace Native Mobile Apps?

fre, 12/28/2018 - 12:30
Will PWAs Replace Native Mobile Apps? Will PWAs Replace Native Mobile Apps? Suzanne Scacca 2018-12-28T12:30:52+01:00 2019-01-04T13:29:47+00:00

A developer friend of mine has decided to build a progressive web app for his new company. When I asked why he opted for a PWA instead of a native app, he said:

“Because the PWA is the future of the web.”

I thought that was an interesting sentiment. Until he mentioned it, I was of a similar mindset as Aaron Gustafson when he discussed the battle between the native app and PWA. In other words, I thought it really just came down to choice; not whether one was better than the other.

Now that the idea has been planted, though, I can’t help but notice a bunch of people proclaiming their support for the PWA over the native app. Not only that, many of them have gone as far as to say that the PWA will replace the native app entirely.

I’d like to see if that argument holds any water.

An Extensive Guide To PWAs

Progressive Web Applications are more of a methodology that involves a combination of technologies to make powerful web applications. Tell me more about PWAs →

Will PWAs Replace Native Apps?

I’m going to go ahead and answer that question right now:

“Yes, but not for everyone.”

Here’s the way I see it:

The mobile web has definitely improved from where it was just a couple years ago. It’s very rare to run into a website that isn’t 100% responsive in design. That said, I don’t think many mobile websites are 100% mobile-first in design (which I recently hinted at when talking about ditching design elements instead of acquiring more in 2019).

I think for an experience to be truly mobile-first, it would need to be faster and have an app shell. Which is exactly what a PWA offers.

While native apps may provide a superior experience (mostly) to other mobile experiences, I just don’t see a valid reason to spend that amount of money and time to build and manage one… unless your app sits in the top 20 of your category in an app store.

Let me break down the logic I used to come to this decision.

Our new book, in which Alla Kholmatova explores how to create effective and maintainable design systems to design great digital products. Meet Design Systems, with common traps, gotchas and the lessons Alla has learned over the years.

Table of Contents → Reason #1: Mobile Web Is Lagging

comScore’s Global Digital Future in Focus report from 2018 makes this point painfully clear:

comScore 2018 report shows use of mobile web vs. mobile apps (Image source: comScore) (Large preview)

That said, I don’t believe native apps will make mobile websites disappear. I also don’t believe this point counteracts the argument I’m attempting to make today. If this data demonstrates anything, it’s that mobile users strongly prefer the experience of interacting with a digital property through an app interface.

Web developers recognize this preference as well, as this survey from JAXenter demonstrates:

JAXenter developer survey about PWAs (Image source: JAXenter) (Large preview)

So, although the mobile web browser has proven to be the less preferred interface through which someone views a website, I don’t think that’ll be the case for much longer as more businesses build PWAs.

The PWA takes all of the things users love about native apps — the app shell, offline access, telephony features, an always-present navigation bar and so on — and gives users a more convenient means for experiencing them.

Look at a brand like Crabtree & Evelyn:

Crabtree & Evelyn PWA example (Image source: Crabtree & Evelyn) (Large preview)

This major retailer has the funds to build a native app counterpart to its website, but it’s chosen not to go that route. Instead, the progressive web app experience gives mobile users the convenience of browsing the online store and making a purchase without having to leave the browser.

Or, if they’re frequent users, they can add this PWA to their home screen and treat it as they would any other app (but more on that later).

Now, let’s look at an example of a PWA that, again, has opted not to go the route of the native app. Instead, Infobae has created a PWA that beats the mobile web experience:

Infobae PWA example (Image source: Infobae) (Large preview)

According to Google data, the Infobae PWA has:

  • A 5% bounce rate. The mobile web was 51%.
  • Sessions that are 230% longer than they were on mobile web.
  • Over three times more pages viewed per session than the mobile web.

So, if you’re worried that the PWA won’t cut it as an alternative to the mobile web, you can stop right there. There are clear benefits to building a PWA.

Reason #2: Native App Stores Are Overflowing

Native apps have a lot of competition in the native app stores — many of which are heavy hitters that mobile users are all too familiar with. If your intention is to launch an app in an already congested space, is the app store really the best place for it?

comScore’s report breaks down the top 5 apps based on reach:

comScore data on the top 5 apps by reach in 2018 (Image source: comScore) (Large preview)

As you can see, the top 5 apps tend to be dominated by the same mobile apps, no matter what part of the world mobile users are located in.

What you might be thinking is, “But what if my app has a unique edge? Isn’t that enough to dominate our niche?”

I could see that, especially if your app is targeted to region-specific mobile users. Then again, you have to consider what sort of app types perform well with mobile app users.

comScore breaks down this point:

comScore data on share of total app minutes (Image source: comScore) (Large preview)

Roughly 70% to 80% of all time spent in mobile apps goes to four categories:

  • Entertainment (like YouTube);
  • Social media (like Facebook);
  • Instant messaging (like Whatsapp);
  • Games (like Fortnite).

If your app concept doesn’t fall into one of those categories, is it worth all that work to place your app in the app store? While I recognize that those aren’t the only kinds of apps that succeed, I just think it would be a risky and expensive gamble to make, especially if your client’s business is brand new. Even then, there are so many cases of well-known entities that have opted not to compete in app stores, despite having a large enough audience or customer base to do so.

West Elm is a great example of a retailer who’s done this:

The West Elm PWA (Image source: West Elm) (Large preview)

If you look in the app stores, you’ll find that West Elm has developed two native apps. One is for registries. This makes sense as a mobile app could be conducive to tagging and tracking registry items. It also has one for the West Elm card. If someone is a frequent enough shopper, this type of app might make sense as well.

That said, neither of these native apps is popular with users (at least not in terms of quantity of reviews). So, it was a smart and economical move by West Elm to keep its main shopping interface in the PWA.

Reason #3: PWAs Rank In Search

On a related note, progressive web apps come with the added benefit of ranking in search engines. There are a few reasons why you and your clients should be overjoyed by this:

  1. Your app’s rank in search is contingent upon the SEO work you put into it. If you’re already doing this with your website, this should be easy!
  2. You don’t have to worry about a brand new app getting buried in app store search. Or easily dismissed because of a lack of ratings.
  3. Because a PWA can live in mobile users’ browsers as well as from a button on the home screen, it needs to have a link. And links make for much easier sharing with friends/family/colleagues than telling them the name of an app, hoping they can find it in the store on their own.

Bottom line: if you can give users a tangible link to your app, you can drastically reduce the friction often caused by having one that only exists in the app store.

Plus, I think the searchability aspect is an important one to consider when you think about how people use your app. Take micro-moments, for example.

When a consumer is inspired to:

  • Research something of interest,
  • Go somewhere,
  • Make a purchase,
  • Or do something…

Instead of opening a data-hogging application on their device, they’ll open their search browser and type or speak their query. It’s what we’re all trained to do as consumers. Have a question? Need something? Want help choosing a restaurant? Go to Google.

If your website or app provides an answer to those kinds of questions, you don’t want it hidden away in app stores. You also don’t want to give them a mobile website that offers an option to “Download the App”. You’re only creating extra work for them.

A PWA enables you to place your app directly in search results and to get your users the instant answers they require.

I think this is why e-commerce businesses have especially gravitated towards PWAs, like HobbyCraft.

HobbyCraft PWA example (Image source: HobbyCraft) (Large preview)

As you can see here, HobbyCraft is a niche retailer that sells craft supplies out of the UK. It wouldn’t make much sense to put something like this in the app stores — especially when the PWA interface works well enough as it is.

Lancome is another e-tailer that’s made the conscious decision to forego the native app and keep the mobile shopping experience in a PWA format.

Lancome e-commerce PWA example (Image source: Lancome) (Large preview)

One important design element I would point you to in both these examples is the Stores icon located in the top navigation bar. For businesses with brick-and-mortar counterparts, there’s no reason to keep your app out of local search in Google.

If you design your PWA correctly, you can make it show up in relevant location-based queries. And if you present an interface that’s reminiscent of a native app — and just as secure as one (since PWAs require HTTPS) — you can compel more mobile users to make a purchase on the spot.

Reason #4: Native Apps Struggle With Retention

For app types that have a hook that compels users to spend time inside a native app and spend money to enjoy the experience further, that’s great. When you find that perfect fit, there’s good money to be made from having a native app. It’s simply a matter of having people willing to commit to the download.

However, as we recently saw, most native apps struggle to retain users.

It doesn’t matter how many initial downloads you get. If mobile users don’t return to the app to engage with your content, purchase subscriptions or upgrades or click on ads, consider it a wasted investment. Unfortunately, that’s the case with a lot of them.

PWAs, on the other hand, don’t require the lofty commitment of having to download an app to one’s device. Heck, users don’t even have to save the PWA to their home screens, if they don’t want to. It’s an overall more convenient experience.

Nevertheless, you may want to urge users to save it for instant access in the future, as The Weather Channel does:

The Weather Channel asks users to save PWA to device. (Image source: The Weather Channel) (Large preview)

Really, what it boils down to is the type of app you’ve built.

The Weather Channel, for instance, provides a service that mobile users will want to use on a daily basis. They could install a native app from the app store with up-to-date weather forecasting, but that app would likely chew through data and battery power a lot more quickly than the browser-based PWA will.

There are other business types that should consider using a PWA for this reason. Think about an online magazine like Forbes.

Users can add the Forbes PWA to their home screen just like a native app. (Image source: Forbes) (Large preview)

Highly specialized publications would do really well to develop PWAs for their daily readers.

Again, it provides a much lighter-weight experience for their phones. Plus, PWAs give users offline access, so they can get access to content no matter where they are or how limited their access may be to the Internet. And the home screen presence (if they choose to put the button there), provides a nice little shortcut around the mobile web browser.

Reason #5: PWAs Can Generate More Revenue

With the exception of in-app advertising, Apple and Google take a sizable cut from any sales you make through a native app. This includes paid downloads, in-app purchases or upgrades and subscription fees. At one point, these fees were as high as 30% per sale.

When you’re hoping to spend money on design tweaks, much-needed development updates and promotional advertising, that’s the last thing you want to hear. In other words, a significant portion of the money that starts to trickle in from your native app goes straight into the pockets of app store owners. That doesn’t seem right, especially if you have to pay for app store ads in order to gain visibility within them.

PWAs don’t come with fees to pay-to-play, which means all revenue generated from them go directly to you (or whoever the owner of the business is). This is especially nice if you have an app concept like a local newspaper (such as The Billings Gazette) that probably deals in smaller profit margins to begin with.

The Billings Gazette PWA monetization example (Image source: The Billings Gazette) (Large preview)

That’s not the only way you can make more money from PWAs than native apps either.

To start, they’re significantly easier to build than native apps. Plus, managing them after launch requires less of a time commitment and resources from you. Yes, it still needs to be updated and maintained — just like anything else on the web — but you don’t have to deal with the obstacles that come with apps in the app store.

For example, you only have to build one progressive web app. You don’t have to create separate ones to match the guidelines for different mobile devices.

Updates are easier, too, especially if your PWA is based off of a WordPress website. You push an update through the pipeline and it shows up immediately in the live PWA. There’s no need to push updates to the app store admins and wait for their approvals. Everything happens in real time, which means getting new features and money-making initiatives out to the public more quickly.

This is helpful in the case of PWAs like Twitter Lite.

The Twitter Lite PWA can stay on the cutting edge in real time (Image source: Twitter) (Large preview)

When going up against a plethora of social media giants that dominate the app stores, having the ability to keep your app updated in real time can serve as a strong competitive edge. This is in addition to all of the other benefits that come from developing your app in a progressive web format.

This is what happened when Twitter put out its PWA.

As this case study from Google shows, Twitter took an incremental approach to optimizing its PWA. As such, they’ve been able to introduce huge improvements to the user experience without much detection from the end user. Their only response to the updates, in fact, has been greater usage of the PWA.

The PWA Is The Future For (Most Of) The Web

Visibility and searchability are known problems with native mobile apps. User retainment is another. And they’re just not sustainable unless you have an idea that’s inherently meant for a native interface that’s sure to bring in money. Mobile games are one example of this. I’d argue that dating apps are another. I used to think social media fell into that category, but Twitter has since proven me wrong.

Based on what I’m seeing online and from what I’ve heard from developer friends and colleagues, I do believe the future is in the PWA.

I think app stores will slowly quiet down as developers realize there are many more benefits to putting a small- to medium-sized company’s app into a progressive web form. The major players will stay put, and companies that have outgrown the bounds of the PWA may eventually move over. But, otherwise, most apps will end up in the progressive web format.

As this trend towards the PWA continues to grow, consumers will become more accustomed to encountering it in search and know that this user-friendly interface is accessible right from their browser. In turn, they’ll only go to the app stores for the kinds of apps that belong there, i.e. messaging, games, entertainment, and some social media. This will create a clearer division between online search and app store search, and further help to improve the overall user experience online.

(ra, yk, il)
Kategorier: Amerikanska

Common CSS Issues For Front-End Projects

tors, 12/27/2018 - 13:30
Common CSS Issues For Front-End Projects Common CSS Issues For Front-End Projects Ahmad Shadeed 2018-12-27T13:30:11+01:00 2019-01-04T13:29:47+00:00

When implementing a user interface in a browser, it’s good to minimize those differences and issues wherever you can, so that the UI is predictable. Keeping track of all of those differences is hard, so I’ve put together a list of common issues, with their solutions, as a handy reference guide for when you’re working on a new project.

Let’s begin.

1. Reset The Backgrounds Of button And input Elements

When adding a button, reset its background, or else it will look different across browsers. In the example below, the same button is shown in Chrome and in Safari. The latter adds a default gray background.

(Large preview)

Resetting the background will solve this issue:

button { appearance: none; background: transparent; /* Other styles */ }

See the Pen Button and Inputs by Ahmad Shadeed (@shadeed) on CodePen.

Front-end is messy and complicated these days. That's why we publish articles, printed books and webinars with useful techniques to improve your work. Even better: Smashing Membership with a growing selection of front-end & UX goodies. So you get your work done, better and faster.

Explore Smashing Membership ↬ 2. Overflow: scroll vs. auto

To limit the height of an element and allow the user to scroll within it, add overflow: scroll-y. This will look good in Chrome on macOS. However, on Chrome Windows, the scroll bar is always there (even if the content is short). This is because scroll-y will show a scroll bar regardless of the content, whereas overflow: auto will show a scroll bar only when needed.

Left: Chrome on macOS. Right: Chrome on Windows. (Large preview) .element { height: 300px; overflow-y: auto; }

See the Pen overflow-y by Ahmad Shadeed (@shadeed) on CodePen.

3. Add flex-wrap

Make an element behave as a flex container simply by adding display: flex. However, when the screen size shrinks, the browser will display a horizontal scroll bar in case flex-wrap is not added.

<div class="wrapper"> <div class="item"></div> <div class="item"></div> <div class="item"></div> <div class="item"></div> <div class="item"></div> <div class="item"></div> </div> .wrapper { display: flex; } .item { flex: 0 0 120px; height: 100px; }

The example above will work great on big screens. On mobile, the browser will show a horizontal scroll bar.

Left: A horizontal scroll bar is shown, and the items aren’t wrapped. Right: The items are wrapped onto two rows. (Large preview)

The solution is quite easy. The wrapper should know that when space is not available, it should wrap the items.

.wrapper { display: flex; flex-wrap: wrap; }

See the Pen flex-wrap by Ahmad Shadeed (@shadeed) on CodePen.

4. Don’t Use justify-content: space-between When The Number Of Flex Items Is Dynamic

When justify-content: space-between is applied to a flex container, it will distribute the elements and leave an equal amount of space between them. Our example has eight card items, and they look good. What if, for some reason, the number of items was seven? The second row of elements would look different than the first one.

The wrapper with eight items. (Large preview) The wrapper with seven items. (Large preview)

See the Pen justify-content by Ahmad Shadeed (@shadeed) on CodePen.

In this case, using CSS grid would be more suitable.

5. Long Words And Links

When an article is being viewed on a mobile screen, a long word or inline link might cause a horizontal scroll bar to appear. Using CSS’ word-break will prevent that from happening.

Large preview .article-content p { word-break: break-all; } (Large preview)

Check out CSS-Tricks for the details.

6. Transparent Gradients

When adding gradient with a transparent start and end point, it will look black-ish in Safari. That's because Safari doesn’t recognize the keyword transparent. By substituting it with rgba(0, 0, 0, 0), it will work as expected. Note the below screenshot:

Top: Chrome 70. Bottom: Safari 12. (Large preview) .section-hero { background: linear-gradient(transparent, #d7e0ef), #527ee0; /*Other styles*/ }

This should instead be:

.section-hero { background: linear-gradient(rgba(0, 0, 0,0), #d7e0ef), #527ee0; /*Other styles*/ } 7. The Misconception About The Difference Between auto-fit And auto-fill In CSS Grid

In CSS grid, the repeat function can create a responsive column layout without requiring the use of media queries. To achieve that, use either auto-fill or auto-fit.

.wrapper { grid-template-columns: repeat(auto-fill, minmax(100px, 1fr)); } (Large preview)

In short, auto-fill will arrange the columns without expanding their widths, whereas auto-fit will collapse them to zero width but only if you have empty columns. Sara Soueidan has written an excellent article on the topic.

8. Fixing Elements To The Top Of The Screen When The Viewport Is Not Tall Enough

If you fix an element to the top of the screen, what happens if the viewport is not tall enough? Simple: It will take up screen space, and, as a result, the vertical area available for the user to browse the website will be small and uncomfortable, which will detract from the experience.

@media (min-height: 500px) { .site-header { position: sticky; top: 0; /*other styles*/ } }

In the snippet above, we’re telling the browser to fix the header to the top only if the viewport’s height is equal to or greater than 500 pixels.

Also important: When you use position: sticky, it won’t work unless you specify the top property.

Large preview

See the Pen Vertical media queries: Fixed Header by Ahmad Shadeed (@shadeed) on CodePen.

9. Setting max-width For Images

When adding an image, define max-width: 100%, so that the image resizes when the screen is small. Otherwise, the browser will show a horizontal scroll bar.

img { max-width: 100%; } 10. Using CSS Grid To Define main And aside Elements

CSS grid can be used to define the main and aside sections of a layout, which is a perfect use for grid. As a result, the aside section’s height will be equal to that of the main element, even if it’s empty.

To fix this, align the aside element to the start of its parent, so that its height doesn’t expand.

.wrapper { display: grid; grid-template-columns: repeat(12, minmax(0, 1fr)); grid-gap: 20px; } // align-self will tell the aside element to align itself with the start of its parent. aside { grid-column: 1 / 4; grid-row: 1; align-self: start; } main { grid-column: 4 / 13; } (Large preview)

See the Pen main and aside by Ahmad Shadeed (@shadeed) on CodePen.

11. Adding fill To An SVG

Sometimes, while working with SVGs, fill won’t work as expected if the fill attribute has been added inline in the SVG. To solve this, either to remove the fill attribute from the SVG itself or override fill: color.

Take this example:

.some-icon { fill: #137cbf; }

This won’t work if the SVG has an inline fill. It should be this instead:

.some-icon path { fill: #137cbf; } 12. Working With Pseudo-Elements

I love to use pseudo-elements whenever I can. They provide us with a way to create fake elements, mostly for decorative purposes, without adding them to the HTML.

When working with them, the author might forget to do one of the following:

  • add the content: "" property,
  • set the width and height without defining the display property for it.

In the example below, we have a title with a badge as a pseudo-element. The content: "" property should be added. Also, the element should have display: inline-block set in order for the width and height to work as expected.

Large preview 13. The Weird Space When Using display: inline-block

Setting two or more elements to display: inline-block or display: inline will create a tiny space between each one. The space is added because the browser is interpreting the elements as words, and so it’s adding a character space between each one.

In the example below, each item has a space of 8px on the right side, but the tiny space caused by using display: inline-block is making it 12px, which is not the desired result.

li:not(:last-child) { margin-right: 8px; } (Large preview)

A simple fix for this is to set font-size: 0 on the parent element.

ul { font-size: 0; } li { font-size: 16px; /*The font size should be reassigned here because it will inherit `font-size: 0` from its parent.*/ } (Large preview)

See the Pen Inline Block Spacing by Ahmad Shadeed (@shadeed) on CodePen.

14. Add for="ID" When Assigning A Label Element To An Input

When working with form elements, make sure that all label elements have an ID assigned to them. This will make them more accessible, and when they’re clicked, the associated input will get focus.

<label for="emailAddress">Email address:</label> <input type="email" id="emailAddress"> Large preview 15. Fonts Not Working With Interactive HTML Elements

When assigning fonts to the whole document, they won’t be applied to elements such as input, button, select and textarea. They don’t inherit by default because the browser applies the default system font to them.

To fix this, assign the font property manually:

input, button, select, textarea { font-family: your-awesome-font-name; } 16. Horizontal Scroll Bar

Some elements will cause a horizontal scroll bar to appear, due to the width of those elements.

The easiest way to find the cause of this issue is to use CSS outline. Addy Osmani has shared a very handy script that can be added to the browser console to outline every element on the page.

[].forEach.call($$("*"), function(a) { a.style.outline = "1px solid #" + (~~(Math.random() * (1 << 24))).toString(16); }); (Large preview) 17. Compressed Or Stretched Images

When you resize an image in CSS, it could be compressed or stretched if the aspect ratio is not consistent with the width and height of the image.

The solution is simple: Use CSS’ object-fit. Its functionality is similar to that of background-size: cover for background images.

img { object-fit: cover; } (Large preview)

Using object-fit won’t be the perfect solution in all cases. Some images need to appear without cropping or resizing, and some platforms force the user to upload or crop an image at a defined size. For example, Dribbble accepts thumbnails uploads at 800 by 600 pixels.

18. Add The Correct type For input.

Use the correct type for an input field. This will enhance the user experience in mobile browsers and make it more accessible to users.

Here is some HTML:

<form action=""> <p> <label for="name">Full name</label> <input type="text" id="name"> </p> <p> <label for="email">Email</label> <input type="email" id="email"> </p> <p> <label for="phone">Phone</label> <input type="tel" id="phone"> </p> </form>

This is how each input will look once it’s focused:

(Large preview) 19. Phone Numbers In RTL Layouts

When adding a phone number like + 972-123555777 in a right-to-left layout, the plus symbol will be positioned at the end of the number. To fix that, reassign the direction of the phone number.

p { direction: ltr; } (Large preview) Conclusion

All of the issues mentioned here are among the most common ones I’ve faced in my front-end development work. My goal is to keep a list to check regularly while working on a web project.

Do you have an issue that you always face in CSS? Let us know in the comments!

(dm, ra, al, yk, il)
Kategorier: Amerikanska

What We Wished For

mån, 12/24/2018 - 14:30
What We Wished For What We Wished For Mat Marquis 2018-12-24T14:30:46+01:00 2019-01-04T13:29:47+00:00

I think we’re headed for trouble, though I can’t say for sure. Trouble — trouble I know. The on-ramp to it, though; I’ve only heard about that. I’ve only been doing this for ten years. I missed all the lead-up the last time around. What I can say for sure — what I know from experience — is that I’ve never had a wish made in anger come true without regretting it.

Ten years (I don’t mind saying) is a pretty long time. Back when I first truth-stretched my way into a web design internship, good ol’ Internet Explorer was already a laughingstock.

“If you notice that a piece of your content appears and disappears, and sections of the page only get half-drawn, these are good indications that an element requires a layout. [...] A hasLayout fix involves nothing more than declaring a CSS property that causes an element to gain a layout, when it wouldn’t ordinarily have a layout by default.”

The Internet Explorer hasLayout Property

I hated IE. I feel like I can cop to that now. I tried not to; I really, sincerely did. I’d tell people it was fun to support, if you can believe it.

As all the other browsers got easier and easier to deal with, I attempted to convince myself that there was at least still a challenge to quirky old IE. That even became something of a point of pride: I had gotten so good at fixing obscure IE issues that I’d learned to dodge them during the course of my everyday development, leaving nothing (well, less) to dread come the big “open it up in IE and see what broke” phase.

Web forms are such an important part of the web, but we design them poorly all the time. The brand-new “Form Design Patterns” book is our new practical guide for people who design, prototype and build all sorts of forms for digital services, products and websites. The eBook is free for Smashing Members.

Check the table of contents ↬

It’s fun, in a way. Fun. That was the lie I told myself.

/* Fixes #2588: When Windows Phone 7.5 (Mango) tries to calculate a numeric opacity for a select (including “inherit”) without explicitly specifying an opacity on the parent to give it context, a bug appears where clicking elsewhere on the page after opening the select will open the select again. */

jQuery Mobile source

I hated it. I full-on, bad-jokes-in-a-conference-talk hated IE, in every one of its incarnations. I hated it every bit as much everybody else did.

“Internet Explorer 6 has a puzzling bug involving multiple floated elements; text characters from the last of the floated elements are sometimes duplicated below the last float. ... The direct cause is nothing more than ordinary HTML comments, such as, <!-- end left column -->, sandwiched between floats that come in sequence.”

Explorer 6 Duplicate Characters Bug

A waste of my goddamned time is what it was. All those hours I spent hunched over a janky virtual machine—reload, wait, throw a nonsense fix at a nonsense bug, reload, crash, open IE again, wait, double-check that caching wasn’t a factor, reload, wait, and repeat. I could have been doing so much more with my time — I could have learned so much more.

I was certain that it didn’t just hold back my work, and it didn’t just hold the web back, but it held me back, as a developer. On that second point, I guess I wasn’t entirely wrong — all the obscure IE 6-7 browser bug knowledge I accumulated is all useless now. All I have to show for it are an involuntary flinch at the word “filter,” an inscrutable preference for padding over margin, and a deep-seated but largely unfounded fear of z-index. ​ “…extra whitespace causes the wrong styles to be picked up if the actual class name is a substring (or superstring) of some other class name.”

IE5/Mac whitespace parsing bug

I wished it would go away. Uninstalled by a clever and widespread virus, banned by law, Microsoft finally deciding to cut their shoddy rendering engine’s losses and switching to Firefox’s rendering engine, Gecko — whatever — just make it go away. But… no. The web kept evolving and we developers beat on, boats against the current, borne back ceaselessly into the past.

Chrome came along, Firefox kept getting better, new features kept rolling out, the exciting and endless possibilities presented by the advent of responsive web design spread out before us, and also (quick aside) remember that you’ll only have a couple of days to make it all more-or-less work in old IE, so don’t get too carried away. ​ “IF you are using IE8, AND you are using the CSS ordered list numbering approach described above, AND the HTML that has the classes that use the counter-reset and counter-increment CSS attributes is HIDDEN when the page loads, THEN whenever that hidden HTML is displayed, ALL of the automatic numbers will be ZERO, BUT ONLY IF THE CSS :hover PSEUDO-CLASS IS USED ON THAT PAGE!”

The IE8 “hover” Bug: The Most Awesome IE Bug Ever?

It’s hard to imagine experiencing that kind of frustration nowadays, at least for us relatively-old-timers. Not to say that there isn’t an incredible amount of work involved in tuning things up cross-browser these days, too — I know all too well that there is. But it’s tough not to feel the occasional pang of, “back in my day, all we had were floats, and let me tell you about IE’s double margin bug,” when you hear about a little difference in how CSS Grid works from one browser to another.

I was wrong; I want to be clear on that point. Not wrong for being frustrated. I don’t think anyone should be blamed for being frustrated with those old browser bugs, same as I don’t think anyone should be blamed for their frustration with any aspect of web development now. No, I was wrong for the conclusion that anger brought me to: the desire to see Trident burned to the ground and the earth where it once stood salted.

I suspect that only one dramatically-ironic thing grows out of that salted earth: those same frustrations, born anew, for a new generation of web developers. When I started my career, scant few years after the browser wars, those seeds had already taken root. Because, for a time — a time before my own — we web developers cursed Netscape the same way. The weaker, buggier, inarguably worse browser. But Internet Explorer — developers loved that browser. And they wished those other browsers — the bad browsers — would just go away: uninstalled by a clever and widespread virus, banned by law, Netscape finally deciding to cut their shoddy rendering engine’s losses and switch to IE’s rendering engine, Trident — whatever — just make it go away. Those inscrutable Internet Explorer bugs didn’t happen by coincidence or negligence. They came about because Internet Explorer had won, and we loved it for winning.

See, our frustration and our anger lied to us, as they usually do. They told us that supporting those other, worse browsers didn’t just hold back our work, and didn’t just hold the web back, but it held us back, as developers. A waste of our goddamned time is what it was. So, we told ourselves that it wasn’t only for our own good, but for the good of the entire web.

We weighed IE just a little more heavily. We gave it just a little more say in our decisions. And so, holding so many chips, Microsoft played their cards accordingly — who could blame them? Everyone built websites for them first, and the others second. Their word wasn’t law, but it was certainly more than suggestion. Sure, they deviated from web standards here and there (just a little bit), but after all, wasn’t something implemented by The Biggest Browser a sort of de facto standard anyway? Besides, supporting the better, faster, easier browser was doing the web itself a service! Together with Microsoft, we were pushing the web forward! Everybody wins.

The rendering engine that powers Microsoft’s Edge browser today — EdgeHTML — is a fork of gnarly old Trident. It’s a stripped-down and vastly improved fork of Trident, for sure, but it isn’t, let’s say, universally judged on its own merit. The EdgeHTML team has always been working with a couple of disadvantages: the first was technical, in that it took a tremendous amount of time and effort to catch up with the likes of Safari, Firefox, and Chrome. The second was emotional. It was us — you and me — jaded from years of Internet Explorer, staring down a cheery blue lowercase “e” with cold disdain.

A few weeks ago, the Edge team announced that they’d soon be abandoning EdgeHTML in favor of Blink, the rendering engine that powers Chrome. With this change, the last few remaining embers of Trident will be snuffed out forever. The wish I’d shared with so many will finally be granted. Ironically timed — as it turns out — EdgeHTML was becoming a pretty solid rendering engine.

Blink is an open-source project led and governed by Google. It powers both Chrome and Opera, the latter of which similarly abandoned their home-grown rendering engine a few years ago.

By an overwhelming margin, Blink is (and will increasingly be) the way the web is experienced the world over. Blink is fast, stable, packed with modern features, and — by comparison to development for the still-evolving EdgeHTML — painless.

It may have happened too late to save us from those ancient IE bugs, but our work will be easier now that there’s one less rendering engine to support. You and I will lose a little more of our collective “but does it work cross-browser” burden. Our projects will go more smoothly, and the web will lose just a little more of what was once holding it back.

As stewards of the engine powering so very much of the web, well, Google’s word won’t be law, but certainly more than suggestion. And maybe over the course of the next few years, they’ll deviate from web standards here and there (whether intentionally or accidentally) in the tiniest of ways. But after all, isn’t something implemented by The Biggest Browser a sort of de facto standard itself? Besides, how could you argue? Favoring the better, faster, more powerful browser is doing the web itself a service, after all. Together with Google, we’ll be pushing the web forward. Everybody will win.

That is, as long as little standards deviations and tiny, nagging bugs don’t grow bigger over time — thanks to the twin forces of entropy and complacency. Unless the decisions we’ve made for the good of the web (hand-in-hand with a notoriously privacy-hostile advertising company) begin to feel a little darker, and a new bogeyman begins to take shape in our minds — unless we find that our old fears and frustrations have risen again (like a phoenix that renders a couple hundred pixels away from where it should and flickers in a weird way when you scroll).

It doesn’t take much imagination to see newer, more exciting rendering engines appearing over the next handful of years. It takes just as little imagination to see them failing due to lack of support, as we favor “the browser that everyone uses” — first by choice, and later perhaps in grudging service of “the bottom line.”

Again, though, I don’t know. I’ve never seen this happen with a rendering engine myself. I’ve just heard the whole story, and I only know first-hand how it ends. I know the ending from the ache of old psychic scars; from an involuntary flinch at some bits of code, and muscle-memory that compels me to avoid others. I know it from the jokes in conference talks that always felt a little tired, but still resonated just the same in a way I wouldn’t allow myself to admit and still spoke to a secret wish that I held deep in my heart. A bitter, hateful wish.

But hey, listen. Not anymore. Now, I mean — I would never. I really do love a good rendering engine bug, now. I do.

“CSS 3D transforms with perspective() are rendered inside out.”

bugs.chromium.org

I mean, that’s actually kind of a fun bug, right? Like, fun in a way. Y’know?

It’s fun.

It’ll be fun.

(dm, ra, il)
Kategorier: Amerikanska

Generic First CSS: New Thinking On Mobile First

fre, 12/21/2018 - 14:45
Generic First CSS: New Thinking On Mobile First Generic First CSS: New Thinking On Mobile First Alastair Hodgson 2018-12-21T14:45:16+01:00 2019-01-04T13:29:47+00:00

I think it’s safe to say that Ethan Marcotte’s Responsive Web Design was a welcome revelation for web developers the world over. It triggered a whole new wave of design thinking and wonderful new front-end techniques. The reign of the oft-despised m dot websites was also over. In the same era and almost as influential was Luke Wroblewski’s Mobile First methodology — a solid improvement that built upon Marcotte’s impressive foundations.

These techniques are at the bedrock of most web developers lives, and they’ve served us well, but alas, times change, and developers constantly iterate. As we increase the efficiency of our methods and the project requirements become more complex, new frustrations emerge.

The Journey To Generic First

I can’t pinpoint exactly what made me change the way I write my CSS because it was really a natural progression for me that happened almost subconsciously. Looking back, I think it was more of a by-product of the development environment I was working in. The team I worked with had a nice SCSS workflow going on with a nifty little mixin for easily adding breakpoints within our CSS declarations. You probably use a similar technique.

This wonderful little SCSS mixin suddenly made it easy to write super granular media queries. Take a hypothetical biography block that looks a little something like this:

.bio { display: block; width: 100%; background-color: #ece9e9; padding: 20px; margin: 20px 0; @include media('>=small') { max-width: 400px; background-color: white; margin: 20px auto; } @include media('>=medium') { max-width: 600px; padding: 30px; margin: 30px auto; } @include media('>=large') { max-width: 800px; padding: 40px; margin: 40px auto; } @include media('>=huge') { max-width: 1000px; padding: 50px; margin: 50px auto; } }

Fig.1. Typical mobile first with cascading media queries

This works nicely — I’ve written a lot of CSS like this in the past. However, one day it dawned upon me that overwriting CSS declarations as the device width increased just didn’t make sense. Why declare a CSS property for it only to be overwritten in the following declaration?

Front-end is messy and complicated these days. That's why we publish articles, printed books and webinars with useful techniques to improve your work. Even better: Smashing Membership with a growing selection of front-end & UX goodies. So you get your work done, better and faster.

Explore Smashing Membership ↬

This is what lead me to begin writing compartmentalized media queries as opposed to the more common approach of media queries that cascade upwards (or downwards) like the example in Fig.1.

Instead of writing media queries that cascade upwards with increases in screen size, I began creating targeted media queries that would encapsulate styles at desired screen widths. The media query mixin would really come into its own here. Now my SCSS media queries are starting to look like this:

.bio { display: block; width: 100%; padding: 20px; margin: 20px 0; @include media('>=small', '<medium') { max-width: 400px; margin: 20px auto; } @include media('>=medium', '<large') { max-width: 600px; padding: 30px; margin: 30px auto; } @include media('>=large', '<huge') { max-width: 800px; padding: 40px; margin: 40px auto; } @include media('>=huge') { max-width: 1000px; padding: 50px; margin: 50px auto; } }

Fig.2. An example of compartmentalized media queries

This new approach just felt more intuitive to me, it cut down on having to reset styles from the previous breakpoint, and it was making the CSS easier to read. More importantly, it was making the media queries self-documenting in a more significant way.

I still wasn’t 100% happy with the above though, It seemed like there was still a major issue to overcome.

The Problem With Mobile First

The issue with mobile first is that by definition you will most likely have to override mobile-first styles in subsequent media-queries. This feels like a bit of an anti-pattern.

So — to me — the answer was obvious: let’s take the idea of media query compartmentalization to its logical conclusion — we will also compartmentalize the mobile specific styles into their very own media queries. I know, I know, this goes against the common convention we’ve learned over the years. “Mobile First” is so ubiquitous that it’s usually one of the “skills” questions a hiring manager will ask. So surely any alternative must be wrong, shouldn’t it? This is usually the part where people shake their heads at me whilst uttering mobile first over and over.

Okay, so we’re going to break through the mobile first dogma and compartmentalize all our styles into the relevant media queries. What we’re now left with is pure generic styles declared on a CSS selector, with all other device specific styles encapsulated in media queries that only apply to the relevant screen dimensions. We now have Generic First CSS:

.bio { display: block; width: 100%; @include media('>=0', '<small') { padding: 20px; margin: 20px 0; } @include media('>=small', '<medium') { max-width: 400px; margin: 20px auto; } @include media('>=medium', '<large') { max-width: 600px; padding: 30px; margin: 30px auto; } @include media('>=large', '<huge') { max-width: 800px; padding: 40px; margin: 40px auto; } @include media('>=huge') { max-width: 1000px; padding: 50px; margin: 50px auto; } }

Fig.3. An example of Generic First CSS

Yes, there are slightly more media queries, however, I see this as a benefit, any developer can now looks at this CSS and see exactly what styles are applied at each and every screen size without the cognitive overhead of having to pick apart media-query specificity.

This can be great for people unfamiliar with the code base or even the future you!

When Not To Compartmentalize

There are still times when media query compartmentalization is a burden, and in some cases a good old >= media query is fine. Remember, all we’re trying to do is avoid property overwrites.

Dev Tool Bliss

One major unintended consequence of writing compartmentalized Generic First CSS is the experience you will get from your developer tools style panel. Without the media query cascade, you will now have a clearer overview of which styles are applied — You won’t have a style panel full of struck-out declarations from overwritten media query rules — The noise is gone! This — for me — is one of the biggest benefits of the Generic First CSS technique. It brings a little extra sanity to the CSS debugging experience, and this is worth its weight in gold. Thank me later.

Fig.4. How generic first, compartmentalized css can help bring joy and sanity to your dev console. (Large preview) Performance Implications

So all these Generic First CSS benefits are starting to sound pretty good, but I think there is one last key question that I think needs to be addressed. It’s on the subject of performance optimization. Now I don’t know for certain yet, but I have an inkling that fully compartmentalized media queries may have a slight performance benefit.

Browsers perform a rendering task called computed style calculation. It’s the browsers way of calculating which styles need to be applied to an element at any given moment. This task is always performed on initial page load, but it can also be performed if page content changes or if other browser actions take place. Any boost you can give to the speed of the process is going to be great for initial page load, and it could have a compound effect on the lifecycle of your websites pages.

So going back to generic first CSS: Are there any performance issues related to the browser having to work out the CSS specificity of a multitude of cascading media queries?

To answer that, I’ve devised a test case that can be used to measure any speed benefits or indeed drawbacks.

The Test Case

The test case is comprised of a basic HTML page that outputs a “bio” block 5000 times, the markup is the same for each block, but the classes are slightly different (numeric differentiator), the CSS for this block is also outputted 5000 times, with class names being the only thing to differ. The outputted CSS is piped through a tool called CSS MQPacker, this helps dramatically reduce file size of CSS that uses a lot of inline media queries by combining all the separate instances of a specific media query into one — It’s a great tool that will probably benefit most modern CSS codebases — I’ve used it as a standalone cli tool via a npm task in the test projects package.json, you can also use it as a postcss plugin, which is nice and convenient!

The first test case is a mobile-first cascading media queries example, the second test case is a generic first compartmentalized variant of the CSS. The CSS for these cases is a little verbose and could probably be written in much more concise terms, but it really just serves as a rough example to test the argument.

The test was run 20 times for each CSS variation in desktop Google Chrome v70, not a massive set of data, but enough to give me a rough idea of a performance gain/loss.

The test metrics I have chosen to use are:

  • Overall page load time
    A basic metric to check page load time using the Performance API markers in the the start of the <head> and very end of <body>
  • The Recalculate Style
    Time from within the dev tools performance pane.
  • The Overall Page Rendering
    Time from within the dev tools performance pane.
Fig.5. The key metric being measured is “Recalculate Style”. (Large preview)

Results Table (all times in milliseconds)

@media all and (max-width: 799px) { .break-out { display: none; } } @media all and (min-width: 800px) { .mobiletable { display: none; } @media all and (max-width: 1099px) { .pfix { margin-top: -12em;" } }

Mobile First Generic First Load time Calculate styles Total render time Load time Calculate styles Total render time 1135 565.7 1953 1196 536.9 2012 1176 563.5 1936 1116 506.9 1929 1118 563.1 1863 1148 514.4 1853 1174 568.3 1929 1124 507.1 1868 1204 577.2 1924 1115 518.4 1854 1155 554.7 1991 1177 540.8 1905 1112 554.5 1912 1111 504.3 1886 1110 557.9 1854 1104 505.3 1954 1106 544.5 1895 1148 525.4 1881 1162 559.8 1920 1095 508.9 1941 1146 545.9 1897 1115 504.4 1968 1168 566.3 1882 1112 519.8 1861 1105 542.7 1978 1121 515.7 1905 1123 566.6 1970 1090 510.7 1820 1106 514.5 1956 1127 515.2 1986 1135 575.7 1869 1130 504.2 1882 1164 545.6 2450 1169 525.6 1934 1144 565 1894 1092 516 1822 1115 554.5 1955 1091 508.9 1986 1133 554.8 2572 1001 504.5 1812 AVG 1139.55 557.04 1980 1119.1 514.67 1903.15

Mobile First Load time Calculate styles Total render time 1135 565.7 1953
1118 563.1 1863 1174 568.3 1929
1112 554.5 1912
1105 542.7 1978
1106 514.5 1956
1164 545.6 2450
1115 554.5 1955

Generic First Load time Calculate styles Total render time 1196 536.9 2012 1148 514.4 1853 1124 507.1 1868 1111 504.3 1886 1121 515.7 1905 1127 515.2 1986 1169 525.6 1934 1091 508.9 1986

Fig.6. 20 test runs measuring key load/render metrics of mobile first vs generic first CSS.

From my admittedly small dataset, it does seem like my initial suspicion may be correct. On average, I see the Style Recalculation task take 42ms less time which is a 7.6% speed increase, and therefore the overall rendering time also decreases. The difference isn’t mind-blowing, but it is an improvement. I don’t think the dataset is big enough to be 100% conclusive and the test case is a little unrealistic, but I’m very glad not to be seeing a performance degradation.

I would be very interested to see the generic first methodology applied to a real-world existing codebase that has been written in the mobile-first way — the before after metrics would be much more realistic to everyday practice.

And if anyone has suggestions on how to automate this test over a broader set of iterations, please let me know in the comments! I’d imagine there must be a tool that can do this.

Conclusion

To recap on the benefits of this new development methodology...

  • CSS that does exactly as intended, no second guessing;
  • Self-documenting media-queries;
  • A better dev tools experience;
  • Pages that render faster.

I’d like to think I’m not the only person espousing the writing of CSS in this style. If you have already adopted the generic first mindset, hurray! But if not, I think you’ll really like the benefits it brings. I’ve personally benefited greatly from the uncluttered dev tools experience, which in itself will be a huge positive to a lot of devs. the self-documenting nature of this way of writing your media-queries will also have benefits to yourself and the wider team (if you have one). And finally, these benefits won’t cost you anything in performance terms, and in fact have been shown to have marginal speed gains!

Final Word

Like all development methodologies, it may not be for everyone, but I’ve fallen into Generic First CSS quite naturally, I now see it as a valuable way of working that gives me all the benefits of mobile first with some positive new additions that make the tough job of front-end development that little be easier.

Resources Test Case Repo

If you’d like to fire up the test case and give it a go yourself, you can find it on GitHub, I’d love to see some reports from others.

Tools (dm, ra, yk, il)
Kategorier: Amerikanska

Writing A Multiplayer Text Adventure Engine In Node.js

tors, 12/20/2018 - 14:05
Writing A Multiplayer Text Adventure Engine In Node.js Writing A Multiplayer Text Adventure Engine In Node.js Fernando Doglio 2018-12-20T14:05:34+01:00 2019-01-04T13:29:47+00:00

Text adventures were one of the first forms of digital role-playing games out there, back when games had no graphics and all you had was your own imagination and the description you read on the black screen of your CRT monitor.

If we want to get nostalgic, maybe the name Colossal Cave Adventure (or just Adventure, as it was originally named) rings a bell. That was the very first text adventure game ever made.

A picture of an actual text adventure from back in the day. (Large preview)

The image above is how you’d actually see the game, a far cry from our current top AAA adventure games. That being said, they were fun to play and would steal hundreds of hours of your time, as you sat in front of that text, alone, trying to figure out how to beat it.

Understandably so, text adventures have been replaced over the years by games that present better visuals (although, one could argue that a lot of them have sacrificed story for graphics) and, especially in the past few years, the increasing ability to collaborate with other friends and play together. This particular feature is one that the original text adventures lacked, and one that I want to bring back in this article.

Our new book, in which Alla Kholmatova explores how to create effective and maintainable design systems to design great digital products. Meet Design Systems, with common traps, gotchas and the lessons Alla has learned over the years.

Table of Contents → Our Goal

The whole point of this endeavour, as you have probably guessed by now from the title of this article, is to create a text adventure engine that allows you to share the adventure with friends, enabling you to collaborate with them similarly to how you would during a Dungeons & Dragons game (in which, just like with the good ol’ text adventures, there are no graphics to look at).

In creating the engine, the chat server and the client is quite a lot of work. In this article, I’ll be showing you the design phase, explaining things like the architecture behind the engine, how the client will interact with the servers, and what the rules of this game will be.

Just to give you some visual aid of what this is going to look like, here is my goal:

General wireframe for the final UI of the game client (Large preview)

That is our goal. Once we get there, you’ll have screenshots instead of quick and dirty mockups. So, let’s get down with the process. The first thing we’ll cover is the design of the whole thing. Then, we’ll cover the most relevant tools I’ll be using to code this. Finally, I’ll show you some of the most relevant bits of code (with a link to the full repository, of course).

Hopefully, by the end, you’ll find yourself creating new text adventures to try them out with friends!

Design Phase

For the design phase, I’m going to cover our overall blueprint. I’ll try my best not to bore you to death, but at the same time, I think it’s important to show some of the behind-the-scenes stuff that needs to happen before laying down your first line of code.

The four components I want to cover here with a decent amount of detail are:

  • The engine
    This is going to be the main game server. The game rules will be implemented here, and it’ll provide a technologically agnostic interface for any type of client to consume. We’ll implement a terminal client, but you could do the same with a web browser client or any other type you’d like.
  • The chat server
    Because it’s complex enough to have its own article, this service is also going to have its own module. The chat server will take care of letting players communicate with each other during the game.
  • The client
    As stated earlier, this will be a terminal client, one that, ideally, will look similar to the mockup from earlier. It will make use of the services provided by both the engine and the chat server.
  • Games (JSON files)
    Finally, I’ll go over the definition of the actual games. The whole point of this is to create an engine that can run any game, as long as your game file complies with the engine’s requirements. So, even though this will not require coding, I’ll explain how I’ll structure the adventure files in order to write our own adventures in the future.
The Engine

The game engine, or game server, will be a REST API and will provide all of the required functionality.

I went for a REST API simply because — for this type of game — the delay added by HTTP and its asynchronous nature will not cause any trouble. We will, however, have to go a different route for the chat server. But before we start defining endpoints for our API, we need to define what the engine will be capable of. So, let’s get to it.

Feature Description Join a game A player will be able to join a game by specifying the game’s ID. Create a new game A player can also create a new game instance. The engine should return an ID, so that others can use it to join. Return scene This feature should return the current scene where the party is located. Basically, it’ll return the description, with all of the associated information (possible actions, objects in it, etc.). Interact with scene This is going to be one of the most complex ones, because it will take a command from the client and perform that action — things like move, push, take, look, read, to name just a few. Check inventory Although this is a way to interact with the game, it does not directly relate to the scene. So, checking the inventory for each player will be considered a different action. A Word About Movement

We need a way to measure distances in the game because moving through the adventure is one of the core actions a player can take. We will be using this number as a measure of time, just to simplify the gameplay. Measuring time with an actual clock might not be the best, considering these type of games have turn-based actions, such as combat. Instead, we’ll use distance to measure time (meaning that a distance of 8 will require more time to traverse than one of 2, thus allowing us to do things like add effects to players that last for a set amount of “distance points”).

Another important aspect to consider about movement is that we’re not playing alone. For simplicity’s sake, the engine will not let players split the party (although that could be an interesting improvement for the future). The initial version of this module will only let everyone move wherever the majority of the party decides. So, moving will have to be done by consensus, meaning that every move action will wait for the majority of the party to request it before taking place.

Combat

Combat is another very important aspect of these types of games, and one that we’ll have to consider adding to our engine; otherwise, we’ll end up missing on some of the fun.

This is not something that needs to be reinvented, to be honest. Turn-based party combat has been around for decades, so we’ll just implement a version of that mechanic. We’ll be mixing it up with the Dungeons & Dragons concept of “initiative”, rolling a random number in order to keep the combat a bit more dynamic.

In other words, the order in which everyone involved in a fight gets to pick their action will be randomized, and that includes the enemies.

Finally (although I’ll go over this in more detail below), you’ll have items that you can pick up with a set “damage” number. These are the items you’ll be able to use during combat; anything that doesn’t have that property will cause 0 damage to your enemies. We’ll probably add a message when you try to use those objects to fight, so that you know that what you’re trying to do makes no sense.

Client-Server Interaction

Let’s see now how a given client would interact with our server using the previously defined functionality (not thinking about endpoints yet, but we’ll get there in a sec):

(Large preview)

The initial interaction between the client and the server (from the point of view of the server) is the start of a new game, and the steps for it are as follows:

  1. Create a new game.
    The client requests the creation of a new game from the server.
  2. Create chat room.
    Although the name doesn’t specify it, the server is not just creating a chatroom in the chat server, but also setting up everything it needs in order to allow a set of players to play through an adventure.
  3. Return game’s meta data.
    Once the game has been created by the server and the chat room is in place for the players, the client will need that information for subsequent requests. This will mostly be a set of IDs the clients can use to identify themselves and the current game they want to join (more on that in a second).
  4. Manually share game ID.
    This step will have to be done by the players themselves. We could come up with some sort of sharing mechanism, but I will leave that on the wish list for future improvements.
  5. Join the game.
    This one is pretty straightforward. Ince everyone has the game ID, they’ll join the adventure using their client applications.
  6. Join their chat room.
    Finally, the players’ client apps will use the game’s metadata to join their adventure’s chat room. This is the last step required pre-game. Once this is all done, then the players are ready to start adventuring!
Action order for an existing game (Large preview)

Once the prerequisites have all been met, players can start playing the adventure, sharing their thoughts through the party chat, and advancing the story. The diagram above shows the four steps required for that.

The following steps will run as part of the game loop, meaning that they will be repeated constantly until the game ends.

  1. Request scene.
    The client app will request the metadata for the current scene. This is the first step in every iteration of the loop.
  2. Return the meta data.
    The server will, in turn, send back the metadata for the current scene. This information will include things like a general description, the objects found inside it, and how they relate to each other.
  3. Send command.
    This is where the fun begins. This is the main input from the player. It’ll contain the action they want to perform and, optionally, the target of that action (for example, blow candle, grab rock, and so on).
  4. Return the reaction to the command sent.
    This could simply be step two, but for clarity, I added it as an extra step. The main difference is that step two could be considered the beginning of this loop, whereas this one takes into account that you’re already playing, and, thus, the server needs to understand who this action is going to affect (either a single player or all players).

As an extra step, although not really part of the flow, the server will notify clients about status updates that are relevant to them.

The reason for this extra recurring step is because of the updates a player can receive from the actions of other players. Recall the requirement for moving from one place to another; as I said before, once the majority of the players have chosen a direction, then all players will move (no input from all players is required).

The interesting bit here is that HTTP (we’ve already mentioned that the server is going to be a REST API) does not allow for this type of behavior. So, our options are:

  1. perform polling every X amount of seconds from the client,
  2. use some sort of notification system that works in parallel with the client-server connection.

In my experience, I tend to prefer option 2. In fact, I would (and will for this article) use Redis for this kind of behavior.

The following diagram demonstrates the dependencies between services.

Interactions between an client app and the game engine (Large preview) The Chat Server

I will leave the details of the design of this module for the development phase (which is not a part of this article). That being said, there are things we can decide.

One thing we can define is the set of the restrictions for the server, which will simplify our work down the line. And if we play our cards right, we might end up with a service that provides a robust interface, thus allowing us to, eventually, extend or even change the implementation to provide fewer restrictions without affecting the game at all.

  • There will be only one room per party.
    We will not let subgroups be created. This goes hand in hand with not letting the party split. Maybe once we implement that enhancement, allowing for subgroup and custom chat room creation would be a good idea.
  • There will be no private messages.
    This is purely for simplification purposes, but having a group chat is already good enough; we don’t need private messages right now. Remember that whenever you’re working on your minimum viable product, try to avoid going down the rabbit hole of unnecessary features; it’s a dangerous path and one that is hard to get out of.
  • We will not persist messages.
    In other words, if you leave the party, you’ll lose the messages. This will hugely simplify our task, because we won’t have to deal with any type of data storage, nor will we have to waste time deciding on the best data structure to store and recover old messages. It’ll all live in memory, and it will stay there for as long as the chat room is active. Once it’s closed, we’ll simply say goodbye to them!
  • Communication will be done over sockets.
    Sadly, our client will have to handle a double communication channel: a RESTful one for the game engine and a socket for the chat server. This might increase the complexity of the client a bit, but at the same time, it will use the best methods of communication for every module. (There is no real point in forcing REST on our chat server or forcing sockets on our game server. That approach would increase the complexity of the server-side code, which is the one also handling the business logic, so let’s focus on that side for now.)

That’s it for the chat server. After all, it will not be complex, at least not initially. There is more to do when it’s time to start coding it, but for this article, it is more than enough information.

The Client

This is the final module that requires coding, and it is going to be our dumbest one of the lot. As a rule of thumb, I prefer to have my clients dumb and my servers smart. That way, creating new clients for the server becomes much easier.

Just so we’re on the same page, here is the high-level architecture that we should end up with.

Final high level architecture of the entire development (Large preview)

Our simple ClI client will not implement anything very complex. In fact, the most complicated bit we’ll have to tackle is the actual UI, because it’s a text-based interface.

That being said, the functionality that the client application will have to implement is as follows:

  1. Create a new game.
    Because I want to keep things as simple as possible, this will only be done through the CLI interface. The actual UI will only be used after joining a game, which brings us to the next point.
  2. Join an existing game.
    Given the game’s code returned from the previous point, players can use it to join in. Again, this is something you should be able to do without a UI, so this functionality will be part of the process required to start using the text UI.
  3. Parse game definition files.
    We’ll discuss these in a bit, but the client should be able to understand these files in order to know what to show and know how to use that data.
  4. Interact with the adventure.
    Basically, this gives the player the ability to interact with the environment described at any given time.
  5. Maintain an inventory for each player.
    Each instance of the client will contain an in-memory list of items. This list is going to be backed up.
  6. Support chat.
    The client app needs to also connect to the chat server and log the user into the party’s chat room.

More on the client’s internal structure and design later. In the meantime, let’s finish the design stage with the last bit of preparation: the game files.

The Game: JSON Files

This is where it gets interesting because up to now, I’ve covered basic microservices definitions. Some of them might speak REST, and others might work with sockets, but in essence, they’re all the same: You define them, you code them, and they provide a service.

For this particular component, I’m not planning on coding anything, yet we need to design it. Basically, we’re implementing a sort of protocol for defining our game, the scenes inside it and everything inside them.

If you think about it, a text adventure is, at its core, basically a set of rooms connected to each other, and inside them are “things” you can interact with, all tied together with a, hopefully, decent story. Now, our engine will not take care of that last part; that part will be up to you. But for the rest, there is hope.

Now, going back to the set of interconnected rooms, that to me sounds like a graph, and if we also add the concept of distance or movement speed that I mentioned earlier, we have a weighted graph. And that is just a set of nodes that have a weight (or just a number — don’t worry about what it’s called) that represents that path between them. Here is a visual (I love learning by seeing, so just look at the image, OK?):

A weighted graph example (Large preview)

That’s a weighted graph — that’s it. And I’m sure you’ve already figured it out, but for the sake of completeness, let me show you how you would go about it once our engine is ready.

Once you start setting up the adventure, you’ll create your map (like you see on the left of the image below). And then you’ll translate that into a weighted graph, as you can see on the right of the image. Our engine will be able to pick it up and let you walk through it in the right order.

Example graph for a given dungeon (Large preview)

With the weighted graph above, we can make sure players can’t go from the entrance all the way to the left wing. They would have to go through the nodes in between those two, and doing so will consume time, which we can measure using the weight from the connections.

Now, onto the “fun” part. Let’s see how the graph would look like in JSON format. Bear with me here; this JSON will contain a lot of information, but I’ll go through as much of it as I can:

{ "graph": [ { "id": "entrance", "name": "Entrance", "north": { "node": "1stroom", "distance": 1 } }, { "id": "1st room", "name": "1st Room", "south": {"node": "entrance", "distance": 1} , "north": { "node": "bigroom", "distance": 1} } , { "id": "bigroom", "name": "Big room", "south": { "node": "1stroom", "distance": 1}, "north": { "node": "bossroom", "distance": 2}, "east": { "node": "rightwing", "distance": 3} , "west": { "node": "leftwing", "distance": 3} }, { "id": "bossroom", "name": "Boss room", "south": {"node": "bigroom", "distance": 2} } { "id": "leftwing", "name": "Left Wing", "east": {"node": "bigroom", "distance": 3} } { "id": "rightwing", "name": "Right Wing", "west": { "node": "bigroom", "distance": 3 } } ], "game": { "win-condition": { "source": "finalboss", "condition": { "type": "comparison", "left": "hp", "right": "0", "symbol": "<=" } }, "lose-condition": { "source": "player", "condition": { "type": "comparison", "left": "hp", "right": "0", "symbol": "<=" } } }, "rooms": { "entrance": { "description": { "default": "You're at the entrance of the dungeon. There are two lit torches on each wall (one on your right and one on your left). You see only one path: ahead." }, "items": [ { "id": "littorch1", "name": "Lit torch on the right", "triggers": [ { "action": "grab", //grab Lit torch on the right "effect":{ "statusUpdate": "has light", "target": "game", } } ] , "destination": "hand" }, { "id": "littorch2", "name": "Lit torch on the left", "triggers": [ { "action": "grab", //grab Lit torch on the left "effect":{ "statusUpdate": "has light", "target": "game", } } ] , "destination": "hand" } ] }, "1stroom": { "description": { "default": "You're in a very dark room. There are no windows and no source of light, other than the one at the entrance. You get the feeling you're not alone here.", "conditionals": { "has light": "The room you find yourself in appears to be empty, aside from a single chair in the right corner. There appears to be only one way out: deeper into the dungeon." } }, "items": [ { "id": "chair", "name": "Wooden chair", "details": "It's a wooden chair, nothing fancy about it. It appears to have been sitting here, untouched, for a while now.", "subitems": [ { "id": "woodenleg", "name": "Wooden leg", "triggeractions": [ { "action": "break", "target": "chair"}, //break { "action": "throw", "target": "chair"} //throw ], "destination": "inventory", "damage": 2 } ] } ] }, "bigroom": { "description": { "default": "You've reached the big room. On every wall are torches lighting every corner. The walls are painted white, and the ceiling is tall and filled with painted white stars on a black background. There is a gateway on either side and a big, wooden double door in front of you." }, "exits": { "north": { "id": "bossdoor", "name": "Big double door", "status": "locked", "details": "A aig, wooden double door. It seems like something big usually comes through here."} }, "items": [] }, "leftwing": { "description": { "default": "Another dark room. It doesn't look like it's that big, but you can't really tell what's inside. You do, however, smell rotten meat somewhere inside.", "conditionals": { "has light": "You appear to have found the kitchen. There are tables full of meat everywhere, and a big knife sticking out of what appears to be the head of a cow." } }, "items": [ { "id": "bigknife", "name": "Big knife", "destination": "inventory", "damage": 10} ] }, "rightwing": { "description": { "default": "This appear to be some sort of office. There is a wooden desk in the middle, torches lighting every wall, and a single key resting on top of the desk." }, "items": [ { "id": "key", "name": "Golden key", "details": "A small golden key. What use could you have for it?", "destination": "inventory", "triggers": [{ "action": "use", //use on north exit (contextual) "target": { "room": "bigroom", "exit": "north" }, "effect": { "statusUpdate": "unlocked", "target": { "room": "bigroom", "exit": "north" } } } ] } ] }, "bossroom": { "description": { "default": "You appear to have reached the end of the dungeon. There are no exits other than the one you just came in through. The only other thing that bothers you is the hulking giant looking like it's going to kill you, standing about 10 feet from you." }, "npcs": [ { "id": "finalboss", "name": "Hulking Ogre", "details": "A huge, green, muscular giant with a single eye in the middle of his forehead. It doesn't just look bad, it also smells like hell.", "stats": { "hp": 10, "damage": 3 } } ] } } }

I know it looks like a lot, but if you boil it down to a simple description of the game, you have a dungeon comprising six rooms, each one interconnected with others, as shown in the diagram above.

Your task is to move through it and explore it. You’ll find there are two different places where you can find a weapon (either in the kitchen or in the dark room, by breaking the chair). You will also be confronted with a locked door; so, once you find the key (located inside the office-like room), you’ll be able to open it and fight the boss with whatever weapon you’ve collected.

You will either win by killing it or lose by getting killed by it.

Let’s now get into a more detailed overview of the entire JSON structure and its three sections.

Graph

This one will contain the relationship between the nodes. Basically, this section directly translates into the graph we looked at before.

The structure for this section is pretty straightforward. It’s a list of nodes, where every node comprises the following attributes:

  • an ID that uniquely identifies the node among all others in the game;
  • a name, which is basically a human-readable version of the ID;
  • a set of links to the other nodes. This is evidenced by the existence of four possible keys: north”, south, east, and west. We could eventually add further directions by adding combinations of these four. Every link contains the ID of the related node and the distance (or weight) of that relation.
Game

This section will contain the general settings and conditions. In particular, in the example above, this section contains the win and lose conditions. In other words, with those two conditions, we’ll let the engine know when the game can end.

To keep things simple, I’ve added just two conditions:

  • you either win by killing the boss,
  • or lose by getting killed.
Rooms

Here is where most of the 163 lines come from, and it is the most complex of the sections. This is where we’ll describe all of the rooms in our adventure and everything inside them.

There will be a key for every room, using the ID we defined before. And every room will have a description, a list of items, a list of exits (or doors) and a list of non-playable characters (NPCs). Out of those properties, the only one that should be mandatory is the description, because that one is required for the engine to let you know what you’re seeing. The rest of them will only be there if there is something to show.

Let’s look into what these properties can do for our game.

The Description

This item is not as simple as one might think, because your view of a room can change depending on different circumstances. If, for example, you look at the description of the first room, you’ll notice that, by default, you can’t see anything, unless of course, you have a lit torch with you.

So, picking up items and using them might trigger global conditions that will affect other parts of the game.

The Items

These represent all the things” you can find inside a room. Every item shares the same ID and name that the nodes in the graph section had.

They will also have a “destination” property, which indicates where that item should be stored, once picked up. This is relevant because you will be able to have only one item in your hands, whereas you’ll be able to have as many as you’d like in your inventory.

Finally, some of these items might trigger other actions or status updates, depending on what the player decides to do with them. One example of this are the lit torches from the entrance. If you grab one of them, you’ll trigger a status update in the game, which in turn will make the game show you a different description of the next room.

Items can also have “subitems”, which come into play once the original item gets destroyed (through the “break” action, for example). An item can be broken down into several ones, and that is defined in the “subitems” element.

Essentially, this element is just an array of new items, one that also contains the set of actions that can trigger their creation. This basically opens up the possibility to create different subitems based on the actions you perform on the original item.

Finally, some items will have a “damage” property. So, if you use an item to hit an NPC, that value will be used to subtract life from them.

The Exits

This is simply a set of properties indicating the direction of the exit and the properties of it (a description, in case you want to inspect it, its name and, in some cases, its status).

Exits are a separate entity from items because the engine will need to understand if you can actually traverse them based on their status. Exits that are locked will not let you go through them unless you work out how to change their status to unlocked.

The NPCs

Finally, NPCs will be part of another list. They are basically items with statistics that the engine will use to understand how each one should behave. The ones we’ve defined in our example are “hp”, which stands for health points, and “damage”, which, just like the weapons, is the number that each hit will subtract from the player’s health.

That is it for the dungeon I created. It is a lot, yes, and in the future I might consider creating a level editor of sorts, to simplify the creation of the JSON files. But for now, that won’t be necessary.

In case you haven’t realized it yet, the main benefit of having our game defined in a file like this is that we’ll be able to switch JSON files like you did cartridges back in the Super Nintendo era. Just load up a new file and start a new adventure. Easy!

Closing Thoughts

Thanks for reading thus far. I hope you’ve enjoyed the design process I go through to bring an idea to life. Remember, though, that I’m making this up as I go, so we might realize later that something we defined today isn’t going to work, in which case we’ll have to backtrack and fix it.

I’m sure there are a ton of ways to improve the ideas presented here and to make one hell of an engine. But that would require a lot more words than I can put into an article without making it boring for everyone, so we’ll leave it at that for now.

(rb, ra, al, il)
Kategorier: Amerikanska

Sidor