Everything You Need To Know About Alignment In Flexbox

Everything You Need To Know About Alignment In Flexbox

Everything You Need To Know About Alignment In Flexbox

Rachel Andrew

2018-08-13T14:00:57+02:00
2018-08-13T14:00:50+00:00

In the first article of this series, I explained what happens when you declare display: flex on an element. This time we will take a look at the alignment properties, and how these work with Flexbox. If you have ever been confused about when to align and when to justify, I hope this article will make things clearer!

History Of Flexbox Alignment

For the entire history of CSS Layout, being able to properly align things on both axes seemed like it might truly be the hardest problem in web design. So the ability to properly align items and groups of items was for many of us the most exciting thing about Flexbox when it first started to show up in browsers. Alignment became as simple as two lines of CSS:

See the Pen Smashing Flexbox Series 2: center an item by Rachel Andrew (@rachelandrew) on CodePen.

The alignment properties that you might think of as the flexbox alignment properties are now fully defined in the Box Alignment Specification. This specification details how alignment works across the various layout contexts. This means that we can use the same alignment properties in CSS Grid as we use in Flexbox — and in future in other layout contexts, too. Therefore, any new alignment capability for flexbox will be detailed in the Box Alignment specification and not in a future level of Flexbox.

The Properties

Many people tell me that they struggle to remember whether to use properties which start with align- or those which start with justify- in flexbox. The thing to remember is that:

  • justify- performs main axis alignment. Alignment in the same direction as your flex-direction
  • align- performs cross-axis alignment. Alignment across the direction defined by flex-direction.

Thinking in terms of main axis and cross axis, rather than horizontal and vertical really helps here. It doesn’t matter which way the axis is physically.

Main Axis Alignment With justify-content

We will start with the main axis alignment. On the main axis, we align using the justify-content property. This property deals with all of our flex items as a group, and controls how space is distributed between them.

The initial value of justify-content is flex-start. This is why, when you declare display: flex all your flex items line up against the start of the flex line. If you have a flex-direction of row and are in a left to right language such as English, then the items will start on the left.


The items are all lined up in a row starting on the left
The items line up to the start (Large preview)

Note that the justify-content property can only do something if there is spare space to distribute. Therefore if you have a set of flex items which take up all of the space on the main axis, using justify-content will not change anything.


The container is filled with the items
There is no space to distribute (Large preview)

If we give justify-content a value of flex-end then all of the items will move to the end of the line. The spare space is now placed at the beginning.


The items are displayed in a row starting at the end of the container — on the right
The items line up at the end (Large preview)

We can do other things with that space. We could ask for it to be distributed between our flex items, by using justify-content: space-between. In this case, the first and last item will be flush with the ends of the container and all of the space shared equally between the items.


Items lined up left and right with equal space between them
The spare space is shared out between the items (Large preview)

We can ask that the space to be distributed around our flex items, using justify-content: space-around. In this case, the available space is shared out and placed on each side of the item.


Items spaced out with even amounts of space on each side
The items have space either side of them (Large preview)

A newer value of justify-content can be found in the Box Alignment specification; it doesn’t appear in the Flexbox spec. This value is space-evenly. In this case, the items will be evenly distributed in the container, and the extra space will be shared out between and either side of the items.


Items with equal amounts of space between and on each end
The items are spaced evenly (Large preview)

You can play with all of the values in the demo:

See the Pen Smashing Flexbox Series 2: justify-content with flex-direction: row by Rachel Andrew (@rachelandrew) on CodePen.

These values work in the same way if your flex-direction is column. You may not have extra space to distribute in a column however unless you add a height or block-size to the flex container as in this next demo.

See the Pen Smashing Flexbox Series 2: justify-content with flex-direction: column by Rachel Andrew (@rachelandrew) on CodePen.

Cross Axis Alignment with align-content

If you have added flex-wrap: wrap to your flex container, and have multiple flex lines then you can use align-content to align your flex lines on the cross axis. However, this will require that you have additional space on the cross axis. In the below demo, my cross axis is running in the block direction as a column, and I have set the height of the flex container to 60vh. As this is more than is needed to display my flex items I have spare space vertically in the container.

I can then use align-content with any of the values:

See the Pen Smashing Flexbox Series 2: align-content with flex-direction: row by Rachel Andrew (@rachelandrew) on CodePen.

If my flex-direction were column then align-content would work as in the following example.

See the Pen Smashing Flexbox Series 2: align-content with flex-direction: column by Rachel Andrew (@rachelandrew) on CodePen.

As with justify-content, we are working with the lines as a group and distributing the spare space.

The place-content Shorthand

In the Box Alignment, we find the shorthand place-content; using this property means you can set justify-content and align-content at once. The first value is for align-content, the second for justify-content. If you only set one value then both values are set to that value, therefore:

.container {
    place-content: space-between stretch;
}

Is the same as:

.container {
    align-content: space-between; 
    justify-content: stretch;
}

If we used:

.container {
    place-content: space-between;
}

This would be the same as:

.container {
    align-content: space-between; 
    justify-content: space-between;
}

Cross Axis Alignment With align-items

We now know that we can align our set of flex items or our flex lines as a group. However, there is another way we might wish to align our items and that is to align items in relationship to each other on the cross axis. Your flex container has a height. That height might be defined by the height of the tallest item as in this image.


The container height is tall enough to contain the items, the third item has more content
The container height is defined by the third item (Large preview)

It might instead be defined by adding a height to the flex container:


The container height is taller than needed to display the items
THe height is defined by a size on the flex container (Large preview)

The reason that flex items appear to stretch to the size of the tallest item is that the initial value of align-items is stretch. The items stretch on the cross access to become the size of the flex container in that direction.

Note that where align-items is concerned, if you have a multi-line flex container, each line acts like a new flex container. The tallest item in that line would define the size of all items in that line.

In addition to the initial value of stretch, you can give align-items a value of flex-start, in which case they align to the start of the container and no longer stretch to the height.


The items are aligned to the start
The items aligned to the start of the cross axis (Large preview)

The value flex-end moves them to the end of the container on the cross axis.


Items aligned to the end of the cross axis
The items aligned to the end of the cross axis (Large preview)

If you use a value of center the items all centre against each other:


The items are centered
Centering the items on the cross axis (Large preview)

We can also do baseline alignment. This ensures that the baselines of text line up, as opposed to aligning the boxes around the content.


The items are aligned so their baselines match
Aligning the baselines (Large preview)

You can try these values out in the demo:

See the Pen Smashing Flexbox Series 2: align-items by Rachel Andrew (@rachelandrew) on CodePen.

Individual Alignment With align-self

The align-items property means that you can set the alignment of all of the items at once. What this really does is set all of the align-self values on the individual flex items as a group. You can also use the align-self property on any individual flex item to align it inside the flex line and against the other flex items.

In the following example, I have used align-items on the container to set the alignment for the group to center, but also used align-self on the first and last items to change their alignment value.

See the Pen Smashing Flexbox Series 2: align-self by Rachel Andrew (@rachelandrew) on CodePen.

Why Is There No justify-self?

A common question is why it is not possible to align one item or a group of the items on the main axis. Why is there no -self property for main axis alignment in Flexbox? If you think about justify-content and align-content as being about space distribution, the reason for their being no self-alignment becomes more obvious. We are dealing with the flex items as a group, and distributing available space in some way — either at the start or end of the group or between the items.

If might be also helpful to think about how justify-content and align-content work in CSS Grid Layout. In Grid, these properties are used to distribute spare space in the grid container between grid tracks. Once again, we take the tracks as a group, and these properties give us a way to distribute any extra space between them. As we are acting on a group in both Grid and Flexbox, we can’t target an item on its own and do something different with it. However, there is a way to achieve the kind of layout that you are asking for when you ask for a self property on the main axis, and that is to use auto margins.

Using Auto Margins On The Main Axis

If you have ever centered a block in CSS (such as the wrapper for your main page content by setting a margin left and right of auto), then you already have some experience of how auto margins behave. A margin set to auto will try to become as big as it can in the direction it has been set in. In the case of using margins to center a block, we set the left and right both to auto; they each try and take up as much space as possible and so push our block into the center.

Auto margins work very nicely in Flexbox to align single items or groups of items on the main axis. In the next example, I am achieving a common design pattern. I have a navigation bar using Flexbox, the items are displayed as a row and are using the initial value of justify-content: start. I would like the final item to be displayed separated from the others at the end of the flex line — assuming there is enough space on the line to do so.

I target that item and give it a margin-left of auto. This then means that the margin tries to get as much space as possible to the left of the item, which means the item gets pushed all the way over to the right.

See the Pen Smashing Flexbox Series 2: alignment with auto margins by Rachel Andrew (@rachelandrew) on CodePen.

If you use auto margins on the main axis then justify-content will cease to have any effect, as the auto margins will have taken up all of the space that would otherwise be assigned using justify-content.

Fallback Alignment

Each alignment method details a fallback alignment, this is what will happen if the alignment you have requested can’t be achieved. For example, if you only have one item in a flex container and ask for justify-content: space-between, what should happen? The answer is that the fallback alignment of flex-start is used and your single item will align to the start of the flex container. In the case of justify-content: space-around, a fallback alignment of center is used.

In the current specification you can’t change what the fallback alignment is, so if you would prefer that the fallback for space-between was center rather than flex-start, there isn’t a way to do that. There is a note in the spec which says that future levels may enable this.

Safe And Unsafe Alignment

A more recent addition to the Box Alignment specification is the concept of safe and unsafe alignment using the safe and unsafe keywords.

With the following code, the final item is too wide for the container and with unsafe alignment and the flex container on the left-hand side of the page, the item becomes cut off as the overflow is outside the page boundary.

.container {  
    display: flex;
    flex-direction: column;
    width: 100px;
    align-items: unsafe center;
}

.item:last-child {
    width: 200px;
}

The overflowing item is centered and partly cut off
Unsafe alignment will give you the alignment you asked for but may cause data loss (Large preview)

A safe alignment would prevent the data loss occurring, by relocating the overflow to the other side.

.container {  
    display: flex;
    flex-direction: column;
    width: 100px;
    align-items: safe center;
}

.item:last-child {
    width: 200px;
}

The overflowing item overflows to the right
Safe alignment tries to prevent data loss (Large preview)

These keywords have limited browser support right now, however, they demonstrate the additional control being brought to Flexbox via the Box Alignment specification.

See the Pen Smashing Flexbox Series 2: safe or unsafe alignment by Rachel Andrew (@rachelandrew) on CodePen.

In Summary

The alignment properties started as a list in Flexbox, but are now in their own specification and apply to other layout contexts. A few key facts will help you to remember how to use them in Flexbox:

  • justify- the main axis and align- the cross axis;
  • To use align-content and justify-content you need spare space to play with;
  • The align-content and justify-content properties deal with the items as a group, sharing out space. Therefore, you can’t target an individual item and so there is no -self alignment for these properties;
  • If you do want to align one item, or split a group on the main axis, use auto margins to do so;
  • The align-items property sets all of the align-self values as a group. Use align-self on the flex child to set the value for an individual item.
Smashing Editorial
(il)


Source: Smashing Magazine

Web Performance For Third Party Scripts: SmashingConf Videos

Web Performance For Third Party Scripts: SmashingConf Videos

Web Performance For Third Party Scripts: SmashingConf Videos

The Smashing Editorial

2018-08-08T13:30:35+02:00
2018-08-09T14:08:31+00:00

We are continuing our exploration of video from Smashing Conferences this year with two videos that explore the impact of third party scripts. These scripts can add functionality, and give us valuable information, but what do they cost?

These two talks will help you to assess the third party scripts you might be considering adding to a site, and to be able to advise your clients or team members when the request comes in to add yet another script to a global include file!

Name That Script!

Recorded at the SmashingConf in San Francisco earlier this year, Trent Walton asks how can we objectively measure the value of third party scripts for advertising, A/B testing, or analytics? We need to consider their impact on web performance, user experience, as well as understand if they really help our business goals.

A/B Testing, Ads and Other Third Party Tags

At the SmashingConf in London, Andy Davies approached the same subject from a technical angle, showing us the impact that “just a snippet of JavaScript” can have.

Enjoyed watching these talks? There are many more SmashingConf videos on Vimeo. We’re also getting ready for the upcoming SmashingConf in New York — see you there? 😉

Smashing Editorial
(ra)


Source: Smashing Magazine

Attracting Users To Evaluate Your Product

Attracting Users To Evaluate Your Product

Attracting Users To Evaluate Your Product

Joe Leech

2018-08-05T23:30:38+01:00
2018-08-07T12:41:07+00:00

(This is a sponsored article.) The entire ecosystem in which we are designing and researching the user experience is shifting and changing constantly. Traditional UX skills need to be expanded to meet the reality of the modern digital ecosystem. Understanding the user is essential to the job, but you also need to understand the wider user context. How do they discover they have a need? How do they find and evaluate a product to meet that need?

This three-part series outlines the three phases of the product life cycle, the future of UX, and the skills and approach you’ll need to design modern digital products.

  • Part 1: Attraction
    Going out there to get users to evaluate your product.
  • Part 2: Activation
    Signing up, onboarding users, asking for payment.
  • Part 3: Retention
    Encouraging users to come back and keep using and paying for your product.

Due to their technical skills, creativity and deep understanding of user needs, UXers are in a perfect position to apply marketing, SEO and growth-hacking tools and processes to their work.

For focused UX efforts, it’s all about knowing user outcomes at each stage of their journey.

1. Attraction


attraction
Large preview

Getting Started

The days of changing the text on one button and having a dramatic effect on the user experience are behind us. Luckily, we have the processes and skills in our UX toolbox to meet this changing world.

More often than not, there are many small usability and experience issues throughout a user journey that cumulatively create a poor experience.

Mapping out the full user life cycle will help us discover and fix these problems. It’s often the case that a problem at the very beginning of the user journey only surfaces when a user drops out further along in the product life cycle.

We need data to help us understand how UX professional can improve performance. We’ll need user research data, business metrics, data to frame decisions made when improving UX, and metrics to help us understand the business values.


Marketing metrics tracked by team employing growth hacking.
Marketing metrics tracked by team employing growth hacking. (Source). (Large preview)

Plotting Out the Journey

When we talk about the attraction phase, we’re talking about users discovering they have a need, discovering our product and visiting our website to see if our product meets their needs.

Within the life cycle, we can split the larger three phases into smaller phases to help us plan our approach. In this case, we can use Philip Kotler’s model (expanded to six steps by Bryony Thomas):

  1. Awareness: realizing they have a need;
  2. Interest: looking for something to help with that need;
  3. Evaluation: looking at products that help with their need;
  4. Trial: trying the product to see if it meets their need;
  5. Adoption: choosing a product and using it for a while;
  6. Loyalty: deciding to continue using the product or switching to a different one.

We’re interested in the first three parts, which fall under the attraction phase.


the attraction phase
Large preview

We’ll look into trial, adoption and loyalty in future parts of this series.

We’ll use the customer life cycle to align user needs and expectations — what they want and when they need it — to business metrics. We’ll also look at a tool and UX process to use at each step on the journey.

As we move through the process we’ll use the example of a money management app that helps people understand what they are spending and save money.

1. Awareness: They Understand That They Have A Need

The first battle isn’t fought on the ground but in the mind of the customer.
It isn’t fought with your built out solution but instead with an offer.

The Science of How Customers Buy Anything

This is most challenging phase because there is very little that is concrete in terms of user need.

Users can’t articulate what they want, but by looking at how they complete a task or the context of their life, we can identify the problems they face, how they address (or don’t!) the problems now, and potential product features to address the problems.

The goal here is to identify unmet, hidden user needs. This is something Amazon, for example, is very good at.


The secret to Amazon’s success? Be the first to uncover hidden needs.
The secret to Amazon’s success? Be the first to uncover hidden needs. Jeff Bezos, founder of amazon.com. (Large preview)

How To Identify A Need And A Solution Using Fro-Tos

A good technique to use here is to plot the current problem as articulated by the user and then the result of that problem being solved.

Al Ramadan, in his book Play Bigger, named this overarching science category design.

Category design takes people on a journey. We refer to it as creating a from/to. Actually, we use a shorthand term: frotos. Remember, a great new category is one that solves a problem people didn’t know they had, or solves an obvious problem no one thought could be solved.

You have to help them move from the way they used to think, to a new frame of reference. This is what it means to condition the market. You have to first define and market the problem — and only then can you help people understand that you can solve the problem better than anyone else.

The “from” is the problem the user is facing. The “to” is the solution your product offers. The solution described here are the words the user uses to solve the problem.

If we take the example of our money management tool, in user research, we would identify the from as:

I don’t have much money left at the end of the month. Why?

The user then identifies the to as:

I need to something to help me analyze what I spend.

Put the two together and you have frotos: a definition of the problem and an articulation of the solution.

There is a slidedeck that has a good overview of Play Bigger and its techniques.

Bonus: You can also use the jobs-to-be-done timeline as a great tool to map the intent phase.

User research helps us uncover the hidden needs and identify the frotos.

User Research To Uncover Frotos And Other Useful Details

Traditionally, user research has been focused on the experience of the product. We need to expand user research to include all parts of the user acquisition phase.

It’s not easy to research with users who aren’t yet interacting with you. We can turn to the same tools that we are using to raise awareness to also find users to research with.

Recruit and conduct research with users who might respond to your product’s messaging by using Facebook ads or Google demographic targeting. You can then use a tool like Ethn.io to ask them a few questions to aid with recruitment.

The value in researching users who are in the user acquisition phase is that they don’t bring any preconceptions of your product. In fact, when you are reaching out to users for them to give you feedback, don’t talk much about who you are researching for.

Ethnographic and contextual research is the most useful tool here. Going out and observing users in their homes and offices is a great technique. Watching a user go through a typical task will help you identify their needs. You can’t simply ask users what their unmet needs are because they won’t know. The only true way to get to unmet need is to observe behavior.

With our money management app, we might observe that some potential users under 30 years of age don’t have much money left at the end of the month to save or are curious about how much they spend on coffee.

The user research can also uncover any common identifiable traits (and patterns of behavior) that your users show, such as age-related (for example, they are under 30) or interests they share (love of coffee). We can use these traits to target them in our messaging.

The goal from the user research is to uncover unmet needs and identify the frotos: the from state and the to state.

An example of a froto might be:

FROM
I love coffee, but it can get expensive. I wonder how much I spend a month on coffee?

TO
I need to know how much I spend on expensive luxuries like coffee, so that I can reduce my spend.

We can also use the jobs-to-be-done interview framework to help identify unmet needs.

Journey Maps To Understand The Details

Taking the frotos and other learnings, you can add more detail to the journey by mapping out the steps and behaviors at a granular level.

Niall O’Connor has a great overview of how to build a journey and experience map.

Below is a high-level journey map for our money management app, showing needs mapped against each phase of the life cycle.


Our money management app can help people understand their current spending.
In the awareness phase, we can see how the need is quite abstract, but we can clearly see a need for our product. Our money management app can help people understand their current spending. (Large preview)

Personas To Target

Personas are a divisive issue in the UX world. For the purpose of targeting users in the intent stage, we need to know demographic traits and interests.

We can then use tools such as Facebook ads to target the users who will respond to our frotos.


Facebook ad targeting
Facebook ad targeting: We can see how easy it is to find the users we are looking for based on their interests and age group. (Large preview)

In Facebook ads, we can target a specific age group who are coffee lovers. We can use this to target users who might be in the market for our product because they might spend a lot on coffee. Let’s roll up our sleeves and start designing the interactive elements to support this behavior.

Prototyping Attraction

Prototyping and wireframing have traditionally been limited to designing just the product. A modern UXer needs to take prototyping into the wider context and design interactions from the very beginning of the user journey. Rapid prototyping interactions at each step of the product life cycle to gather user feedback and validate ideas can save a lot of time, money and effort later.

For our money management app, we’ll design a Facebook ad to target potential users. We know what copy to include in the ad from our frotos.


An example showing how easy it is to create a Facebook ad prototype interaction.
An example showing how easy it is to create a Facebook ad prototype interaction. (Large preview)

When we get our target users and run user testing on the prototype, we’re testing the entire user experience — from awareness onwards — receiving high-quality UX insights from all parts of the user journey.

The attraction phase is really important for the user experience because it’s where expectations are set. As shown below, we need to use the tools and UX activities at our disposal to design the interactions with our user as we would design the interactions within the product.


An overview of tools and activities to use to improve the UX during the attraction phase.
An overview of tools and activities to use to improve the UX during the attraction phase. (Large preview)

2. Interest

The interest phase is characterized by the user looking for a product to help with the frotos we identified during the awareness phase.

Here, we’ll be working with our SEO colleagues, which means we UXers need to know the tools and processes that SEO practitioners use to help design the search and discovery journey.

Back To The Experience Map To Review The Interest Phase

We used user research to identify the frotos and the questions and information at each step of the journey.


We used user research to identify the frotos and the questions and information at each step of the journey.
Large preview

If we take the interest phase, we can see that the user has come to the conclusion they need something to:

  • Analyze what I spend, and
  • Manage my money.

We can take these interest statements and look to search and keyword-planning tools to refine the language used.

Using Google’s Keyword Planner:


Google’s Keyword Planner shows the suggested terms to target.
Google’s Keyword Planner shows the suggested terms to target. (Large preview)

We are offered the following:


After selecting a keyword, we are shown alternatives we might not have considered.
After selecting a keyword, we are shown alternatives we might not have considered. (Large preview)

Google’s documentation has some more useful help with the search terms report.

We can see from the related search terms what other words our target audience might type in when looking for our product. These words will help us design and improve the search user experience.

You can also use the free Google keyword research tool from SERPS.com to help define the terms used by your users to describe the problem. The higher the volume, the more likely a person is to search for that term.


A list of related search terms based on our initial query. Also shown is the relative popularity of each term.
A list of related search terms based on our initial query. Also shown is the relative popularity of each term. (Large preview)

We can use these search terms to refine the language we use when building the next part of our prototype.

Design The Ad In Your Prototype Tool

We can use Google’s Keyword Planner to design the interest phase of our prototype. You can update the text and the design will change in real time. This is the best approach because Google is constantly changing the format of paid and organic search listings, and any design templates will be quickly out of date.


Creating the ad in Google’s tool shows a live preview of how it will look.
Creating the ad in Google’s tool shows a live preview of how it will look. (Large preview)

You can also live prototype the ad in using Google’s tools on desktop and mobile.


You can preview the ad on desktop and mobile.
You can preview the ad on desktop and mobile. (Large preview)

Our prototype now contains details for the first two subphases of the attraction part of the user life cycle.

Now that we have generated interest in the product, we need to start looking at how our user will evaluate our product to see if it is something they would want to invest time in.

3. Evaluation

The evaluation phase is all about the first visit to our website and that all-important first impression.

We need to look at where users are landing from, be it Facebook ads, Google search results or indeed other routes to our product, such as YouTube, Instagram or Pinterest.

Google Analytics can tell us the most common landing pages and where people come from. A report named “Network Referrals” can help.


We can see here that Facebook is major source of inbound traffic.
We can see here that Facebook is major source of inbound traffic. (Large preview)

SiteTuners’ custom Google Analytics report identifies landing pages with a high bounce rate. We can interpret these as pages users are interested in, but users can’t find what they need or the messaging might not resonate with them. This report is fantastic for UXers to find pages that are causing problems and need to be improved.


Google Analytics shows pages with high-traffic and high-bounce rates
Google Analytics shows pages with high-traffic and high-bounce rates (i.e. problematic pages). (Large preview)

Quick Sprout’s tool is great for evaluating landing pages to give you some clues as to why the page we identified from the custom report is failing.

Prototype The Landing Page

User research has helped us define what our users need at each step, and we’ve mapped out those needs. If it’s an existing product, then we know which landing pages are causing us problems.

The journey map can help us determine the type of copy to include on the landing page — what the user is expecting to see, what questions they need answering and what concerns they may have.


The three parts of the attraction phase and user questions and information needs.
The three parts of the attraction phase and user questions and information needs. (Large preview)

We can then directly translate the user needs into the design for the landing page.


A quick mockup of the landing page meeting the user questions and information needs.
A quick mockup of the landing page meeting the user questions and information needs. (Large preview)

Understanding and mapping the problems users have, the solutions they need, as well as the questions they have when evaluating will make designing this page a straightforward task. If we have an existing but underperforming landing page, we’ll know what content the user is expecting and can evaluate and recommend what needs to change.

Previously, when prototyping we may have used lorem ipsum text. Now, we don’t need to because we have the copy we need. We can design the calls to action to reflect the problems our users are facing, increasing the likelihood of them using our product. No more need for lorem ipsum!

This landing page is just the start. In the next UX life cycle article, we’ll look at further enhancements.

Here’s more great guidance on How To Design An Effective Mobile Landing Page.

User Research The Journey, Including The Landing Page

We can now use the prototype to user test the whole attraction journey, from initial awareness to evaluation. Another Smashing Magazine article has some great suggestions to help with your user research.

Just Scratching The Surface

We’ve looked at how UXers can learn from other disciplines, such as marketing and SEO, to better understand, research, design and improve the attraction phase of the product life cycle.

If you’d like to learn more, I suggest these great books:

In the next part of the series, we’ll look at the next phase, activation: helping users to sign up, onboard and pay for your product.

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

Smashing Editorial
(ra, yk, il)


Source: Smashing Magazine

We Are Just Getting Started: 1,000 Smashing Members

We Are Just Getting Started: 1,000 Smashing Members

We Are Just Getting Started: 1,000 Smashing Members

Vitaly Friedman

2018-08-03T13:50:31+02:00
2018-08-03T11:54:40+00:00

We’ve all been there: bringing a new product to the market is a tough nut to crack. It requires patience, commitment, and a bit of healthy stubbornness. That’s the exact attitude we started off with when we launched our shiny new Smashing Membership last October — a friendly and respectful community that keeps this website alive, along with books, webinars, discounts, networking, and a seasoned selection of fancy cats.

Thanks to the generous support of Smashing Members, we’re incredibly honored to have crossed the 1,000 members mark today. This is a very important day for us, and frankly, it’s quite flattering to see 1,000 people actively supporting our little site and sharing our goals. In fact, with Membership, sometimes it feels like walking around a small town in which everyone knows each other and their friends, and so we know many members by name, and we’ve also met some of them at Smashing Conferences. It’s a wonderful family that shares similar values and wants to get better at their work. But it’s also a family that wants to bring along a shift in the industry.

The People Behind The Scenes

When looking at obscure avatars and nicknames appearing in remote corners of the web, it’s easy to forget that there are actually real people behind them. That’s why it was important for us that the Membership experience is focused around real names and real faces of the community members — both on the new Smashing Magazine website and in our Membership Slack channel. It’s the people who shape the community and make it feel like home, and so Membership should become a humane product, with approachable and friendly authors, contributors and attendees.

We reached out to a few members to ask them why out of all the wonderful resources on the web, they chose to support the red, cat-friendly, and quirky Smashing Magazine, and what they found useful or remarkably painful during their Membership so far.

Allen Brady

Allen Brady

Allen is based in Knoxville, TN. He is passionate about building great experiences on the web for companies and their audiences. Currently he is learning to make the web more accessible with great HTML, CSS and inclusive design.

“I wanted to support Smashing Magazine as soon as they launched their membership program because not only have they been an excellent resource over the years with all the fantastic articles, books and conferences, but also because they’re so great at amplifying the voices in this industry, which is really important. I know that with my membership I’m getting a diverse range of perspectives that’s really helped shape me into a better developer.

The best part about this membership is the community. There’s a fantastic Slack group where you can talk about your projects, ask for help or just chat about whatever. The webinars have also been great. My favorite part is that we get to chat with the hosts and each other during the live recording. Involving the community in everything they can seems to be a theme with Smashing Magazine. It sets them apart from other resources out there and I love it.”

Verena Vertonghen

Verena Vertonghen

Verena‘s journey in the development world started 6 years ago. She studied Multimedia Technology with a specialisation in Web&UX in Antwerp.

“Now I’m a front-end developer who also picks up some design challenges from time to time. I love creating all sorts of things and going on walks with my dogs. Because Smashing Magazine has been an invaluable learning resource for me throughout my studies and my career. I decided on membership to support the continuation of all the great work that Smashing Magazine already offers. But also because you get even more goodies when you do.

Some of the things I really like about it are the eBooks, previews to articles, webinars and the Slack channel that gives me the opportunity to connect with people that have a similar profile. The user experience is overall really great, and SmashingMag cat mascot gives a very playful and personal vibe!”

Emily Serven

Emily Serven

Emily is a recent college graduate and new member of the workforce. In her spare time, she like practicing photography, listening to foreign music, and occasionally playing Overwatch.

“I remember checking SmashingMag regularly as far back as middle school and have always loved the quality and steady quantity of content. I know I can trust the quality of writing on Smashing (especially considering there’s so much content and noise from other places to sift through nowadays)!

I’m also a cat person, for sure. I’ve found the available resources to be really useful (eBook library and book discounts), and I can’t wait for the printed magazine to come out. That’s the other thing about Smashing; even though the medium in which I express my work as a web dev is primarily digital, Smashing still recognizes the value of well-produced and attractive physical media. I love getting the physical books for that reason.

Oh, another thing. I used to freelance more back in middle school until I got my full-time position recently. When I found Smashing for the first time, I really loved how it really ‘got’ me and my job. There was coding, but also design (Photoshop, UX, etc.) and freelance articles specifically. It all felt very well balanced. I think that helped me develop my dev skills and the other auxiliary talents in a way that led to my holistic view of dev nowadays, too.

Arthur Leonov

Arthur Leonov

Arthur is a product designer that also codes front-end. He is a firm believer that merging design and technology can solve even the most difficult problems.

“I’m a designer that codes front-end. What a combo, right? I also believe that merging design and technology can solve even the most difficult problems in this world. The Smashing community keeps me inspired and informed day in and day out.

I catch up on SmashingMag every morning because it is one of the few online magazines in this industry who puts a lot of emphasis on good quality, relevant, and practical content.”

It might sound like an overstatement, but these people have already made a difference. They’ve helped us initiate projects that we wouldn’t be able to support otherwise. Now, don’t get me wrong: with dwindling ad revenues facing us, of course our aim was to earn enough with the help of the Membership to keep the magazine independent and self-funded. But that’s just one side of the story. Our aim was also to support design education and new voices in the industry; reward great people doing great work; foster open, diverse, inclusive and accessible initiatives. Last but not least, we wanted to help community events and projects, and the people behind them.

Did we achieve any of these goals with the money we’ve earned? I’m glad you asked.

So How Much Money Did We Earn? Total: $33,128

Initially, we were hoping to provide a larger financial support for new design/tech education initiatives and open-source projects, but with limited resources we had to be more realistic and pragmatic. We reached 1,013 Smashing Members in 257 days, with 30 supporters, 562 members, and 421 smashers. That makes a current total of $6,689 gross per month for August 2019.

Since the launch of the Membership, Smashing Members contributed a total of $33,128 net over the course of 10 months (including current month):

MonthNet revenue
Total$33,128
November 2017$1,104
December 2017$1,530
January 2018$2,130
February 2018$2,181
March 2018$2,748
April 2018$4,015
May 2018$4,440
June 2018$4,750
July 2018$4,990
August 2018$5,240

It goes without saying that these kind contributions massively helped us cover monthly costs, from maintenance to honorarium for authors, in particular:

  • Honorarium for authors contributing articles and chapters for Smashing Magazine, our eBooks and printed books,
  • Honorarium for reviewers, editors, proofreaders, illustrator Ricardo Gimenes and front-end developer Ilya Pukhalski,
  • Honorarium for all webinar speakers,
  • All design education initiatives and community support is enabled by Smashing Membership,
  • All money was reinvested in the Magazine and Membership projects.

From day one, we kept things fully transparent; we’ve been sharing monthly reports on how much money we’ve earned and how we spent it. So here’s what happened since the launch of Membership last year.

Smashing TV: 24 Live Sessions

Each month, we are proud to host 2 curated webinars for Smashing Members. We’ve teamed up with active members of the community to run 1:1 interactive sessions with Smashing Members. Overall, we ran 24 Smashing TV webinars on front-end, UX, ethics, performance, accessibility and design workflow. With Marcy Sutton, Val Head, Dan Rose, Ada Rose Cannon, Martin Splitt, Michael Riethmueller, Sara Soueidan, dina Amin, Rachel Andrew and Dan Mall, among others.

The goal of every session is to be highly practical and provide actionable insights and learnings — be it in front-end or in user experience. Everyone can also suggest topics for upcoming webinars in the Membership Slack channel, and we’ll invite speakers to cover the topic. Of course, live recordings of these sessions are available as well, and are later released publicly for free for everybody.

Smashing TV: “Smashing Big Bang Redesign” with Vitaly Friedman

Design/Tech Courses And Trainings

These days there is always something to do, learn, or wrap your head around these days, and because all of us tend to get lost in small details, video tutorials and courses can be quite helpful. There are of course huge video course platforms which are wonderful, but there are also many fantastic one-man-show-teachers out there in the community who produce courses and tutorials for everybody to learn from.

That’s why we’ve teamed up with some of these teachers to provide community discounts for training and video courses. For example, for a “Debugging” course run by Remy Sharp, or “DevTools Web Performance Course” by Umar Hansa, or “CSS Layouts Course” by Rachel Andrew or “React/ES6” courses by Wes Bos — with many more courses coming up over the next months.

Supporting Community Initiatives

It’s not easy to maintain and grow a community, and we are proud to help community initiatives around the world to connect like-minded designers and developers.

Here are the projects we’ve supported so far:

If you are running a meet-up or a community in your city, we’d be happy to support you as well. Just drop us a line and tell us a bit about your community, and we’ll make it happen!

Smashing Book 6: New Frontiers In Web Design

It took us a while, but we are almost there. The brand new Smashing Book 6 is coming out early September, with contributions by Laura Elizabeth, Marcy Sutton, Rachel Andrew, Mike Riethmuller, Harry Roberts, Lyza Gardner, Yoav Weiss, Adrian Zumbrunnen, Greg Nudelman, Ada Rose Cannon, and yours truly.

It explores common pain points and solutions from real-world projects: be it accessibility in times of single-page apps, performance loading patterns, making design systems work in real-life, AR/VR, responsive art-direction, building an advanced service worker and designing for next-gen interfaces. A book packed with practical advice for designers and developers alike, designed and illustrated by Chiara Aliotta.


The cover of the Smashing Book 6, with geometric objects shaping the letter S.
Smashing Book 6 is coming. Shipping of the book will start late September, but you can already start reading the first chapters if you order your copy today. (Large preview)

The book is being finished as we speak, but we’ve been slowly releasing chapters, so Members can actually start reading the book already before its official release. All new books and eBooks — as well as upcoming Smashing Print magazine (currently in the works) — is made available for Members free of charge. But that goes without saying, doesn’t it?

Smashing Diversity Program

There is a huge amount of discrimination out there, and not everybody is getting a fair chance even though they deserve one. That’s why we’ve launched a Smashing Diversity program, providing conference and workshop tickets for students, non-profits, and people who might not be able to afford a conference ticket or attend a workshop. We also make sure that our conference volunteers can attend the sessions they’d love to see.

Beyond that, please ping us if there is a way we can help you become a better speaker. To support and encourage new voices in the industry, I’ll be heading to Paris for Mozilla’s Tech Speaker program to provide mentorship, training, and opportunities to up-and-coming speakers from all over the world.

Support New Wave Of Digital Education

Tiego Pedras and Sara Ramos run the New Digital School, a new design education initiative in Porto, Portugal. In fact, they spoke about their project at SmashingConf Freiburg last year. Their goal is to provide students with better front-end and design education to be ready for real-life world.


Each group of students had their own project to work on at The New Digital School.
Each group of students had their own project to work on at The New Digital School. (Image source: Tiago Pedras) (Large preview)

Students presented their projects and shared their results with the group.
Students presented their projects and shared their results with the group. (Image source: Tiago Pedras) (Large preview)

For two years in a row now, I was honored to be able to explore the current state of front-end, interface design, and responsive art-direction with students from all over the world. In February this year, I headed to Porto to spend a week with students from India, Malaysia, Portugal, France and USA for an entire week. Each group of students was working on their own project, ranging from interactive VR storytelling to (hello, Miguel and Sarthak!) to Olympics leaderboards (and hello to you, Prashant and Marissa!).

It might not sound like a big deal, but it was so rewarding to see the sparkle in the eyes of the students as they were working on their projects. Being able to provide an experience that hopefully many students will remember was a huge privilege and a remarkable moment in the entire experience. And it was all possible thanks to the contributions of our Smashing Members. I couldn’t be more proud of this effort.

Berlin Design Campus

Late June is usually quite slow, with most projects slowly fading into sleep mode. Well, it was quite the opposite for us. For June, we teamed up with Prjctr Design School (Kyiv, Ukraine) to run Berlin Design Campus — a week-long trip to Berlin to explore digital design agencies and studios with students from Ukraine. It was our first initiative to improve design education by setting up a project of our own.

We visited the offices of Mozilla (thanks, Emanuela and Amin!), SinnerSchrader (thanks, Martin!), EdenSpiekermann (thanks, Daniel!), Hort (thanks, Eike!), Fjord (thanks, Simon and Jake!), Contentful (thanks, Ben!), Matteo Cevucci (previously EdenSpiekermann, Thoughtworks) with hands-on workshops in those companies throughout the week.


Branding and visuals for Berlin Design Campus, designed by Prjctr Design School in Kiev, Ukraine.
We couldn’t be more proud to team up with Prjctr Design School from Kiev, Ukraine who are trying change the education landscape in Kiev, Ukraine, and London. Visuals were designed by the Prjctr team as well. (Image source) (Large preview)

We visited both design agencies and larger consultancy firms, spoke with local freelancers, artists and entrepreneurs. We’ve set up informal evening meetings in which students could ask questions, and we organized visits to offices so students could see how other professionals work. It was a fascinating week with practical insights you would never get otherwise; a look behind the scenes in actual real-life projects with early prototypes that failed and hands-on exercises to work on.


Ukrainian students visiting design agencies in Berlin.
During Berlin Design Campus, we visited a number of offices in Berlin. One of them was Mozilla’s office, with a hands-on workshop by Amin al Hazwani. (Image source) (Large preview)

You never get to visit or see how designers in those respected companies work, and what their processes look like. So happy and honored to be a part of this little initiative, and looking forward to more adventures in the future. Again, made possible through contributions of wonderful Smashing Members.

New SmashingConf Experience

With a few more resources available to us, we were able to focus on exploring new formats for Smashing Conferences. Being inspired by our Italian friends from the NoSlidesConf, we tested a brand new format in Toronto earlier this year: interactive live sessions in which speakers were not allowed to use slides (be it Powerpoint, Keynote or Reveal.js). Instead, we encouraged speakers to show how they work, how they design and build, what their setup looks like, and give audience insights into how they think as they make progress in their work.


Gemma O’Brien presenting with no slides at SmashingConf Toronto 2018
One of those unforgettable moments. When Gemma O’Brien brought her entire studio to SmashingConf Toronto, a conference where speakers weren’t allowed to use slides. We’ll be rolling out this format at all Smashing events in 2019. (Image credit: Marc Thiele) (Large preview)

Instead of speaking in front of a podium, we set up a coffee shop-alike setting with speakers sitting at the desk and literally walking the audience through their thought process. It was a quite special event. Some speakers felt challenged and excited about the new format, and attendees appreciated the fact that every session was unique and pushed the speakers outside their comfort zones. That’s why we’ll be rolling out this format for SmashingConf 2019, along with lightning talks, design nights, and a book exchange board. It goes without saying: all Smashing Members are getting a heavy discount on all Smashing Conferences.

Giving Back To The Community

Of course, Smashing Magazine has always been free, but with Rachel Andrew joining us on board last year, we now have a strong and keen Editor-in-Chief focusing on getting the best articles out there every single day. Since then, we’ve published 87 articles — all thoroughly reviewed and edited by the Smashing Editorial team. We refocus back on the heart of it all — yours truly Smashing Magazine.

We are committed to make the content we get out there accessible to as many people as possible. That goes for our eBooks as well. That’s why we also publicly released “Inclusive Design Patterns” eBook by Heydon Pickering (PDF, ePUB, Amazon Kindle), a wonderful book on inclusive design patterns — for free. Why? Because accessibility matters.

We Are Just Getting Started

1,000 is a first major milestone for us. Not many people know it, but the entire Smashing team is actually quite small, with just 13 of us floating from one project to another. Frankly, we might be a bit slow at times, but we are trying our best to bring along a positive change to our industry.

We need less craziness and narrow-mindedness around us, and we need more respect, care, and constructive help. That’s the goal we are aiming to provide with the Smashing Membership, with our next projects, and with your help. There might be something in it for you, too. We are in it for a long game. We are, after all, just getting started.

Huge thank you to Cosima Mielke for helping with preparations of this article, and Scott Whitehead for his kind support and work on the Smashing Membership. You are truly smashing!

Smashing Editorial
(cm, sw, il)


Source: Smashing Magazine

What Happens When You Create A Flexbox Flex Container?

What Happens When You Create A Flexbox Flex Container?

What Happens When You Create A Flexbox Flex Container?

Rachel Andrew

2018-08-02T14:00:35+02:00
2018-08-02T14:02:18+00:00

In a short series of articles, I’m going to spend some time in detailed unpacking of Flexbox — in the same way I have done in the past with grid. We’ll have a look at the things Flexbox was designed for, what it really does well, and why we might not choose it as a layout method. In this article, we will take a detailed look at what actually happens when you add display: flex to your stylesheet.

A Flex Container, Please!

In order to use Flexbox, you need an element that will be the flex container. In your CSS, you use display: flex:

See the Pen Smashing Flexbox Series 1: display: flex; by Rachel Andrew (@rachelandrew) on CodePen.

Let us spend a little while thinking about what display: flex really means. In the Display Module Level 3, each value of display is described as actually being a combination of two things: an inner display model, and an outer display model. When we add display: flex, we are really defining display: block flex. The outer display type of our flex container is block; it acts like a block level element in normal flow. The inner display type is flex, so items directly inside our container will participate in flex layout.

This is something you might never have really thought about but probably understand anyway. The flex container acts like any other block on your page. If you have a paragraph following by a flex container, both of these things behave as we have become accustomed to block elements behaving.

We can also define our container with a value of inline-flex which is like using display: inline flex, i.e. a flex container that acts like an inline level element, with children that participate in flex layout. The children of our inline flex container behave in the same way that children of our block flex container behave; the difference is how the container itself behaves in the overall layout.

See the Pen Smashing Flexbox Series 1: display: inline-flex; by Rachel Andrew (@rachelandrew) on CodePen.

This concept of elements having an outer display type, which defines how they behave as a box on the page (plus an inner display type) dictating how their children behave is quite useful. You can apply this thinking to any box in CSS. How does this element act? How do the children of this element act? The answers relate to their outer and inner display models.

Rows Or Columns?

Once we have defined our flex container, some initial values come into play. Without our adding any extra properties, the flex items display as a row. This happens because the initial value of the flex-direction property is row. If you don’t set it, you get a row.

The flex-direction property is how we set the direction of the main axis. Other values for flex-direction are:

  • column
  • row-reverse
  • column-reverse

With our items in a row, the items are placed with the first item at the start edge of the inline dimension and display in the order that they appear in the source. In the specification, this edge is described as main-start:


main-start is at the beginning of the row
main-start is at the start of the inline dimension (Large preview)

If we use the value column, the items begin to lay out from the start edge of the block dimension and therefore form a column.


Items laid out as a column, main-start is at the top
main-start is the start of the block dimension (Large preview)

When we use row-reverse, the location of main-start and main-end are switched; therefore, the items lay themselves out one after the other ending up in reverse order.


Items start at the end of the row
main-start is at the end of the inline dimension (Large preview)

The value column-reverse does the same thing. It’s important to remember that these values don’t “switch the order of items” although this is what we see happening, they change the place where the flow of items starts: by switching where main-start is. So our items do display in reverse order, but that is because they start laying out at the other end of the container.

It is also important to remember that when this happens, the effect is purely visual. We are asking the items to display themselves starting at the end edge; they are still flowing in the same order and this is the order that your screen reader uses and also the order they can be tabbed through. You should never use row-reverse when what you really want to do is change the order of the items. Make that change in your document source.

The Two Axes Of Flexbox

We have already exposed an important feature of flexbox: the ability to switch the main axis from row to column. This axis switching is why I think that often it is easier to understand things like alignment in Grid Layout first. With Grid, working in two dimensions, you can align on both axes in pretty much the same way. Flexbox is a little trickier because different things happen depending on whether you are working with the main axis, or the cross axis.

We have already encountered the main axis, i.e. the axis that you define as the value of flex-direction. The cross axis is the other dimension. If you have set flex-direction: row, your main axis is along the row, and your cross axis is down the columns. With flex-direction: column, the main axis is down the column and your cross axis along the rows. It is here where we need to explore another important feature of Flexbox, and that is the fact that it is not tied to the physical dimensions of the screen. We don’t talk about a row running from left to right, or a column from top to bottom, because that is not always the case.

Writing Modes

When I described row and column above, I mentioned the block and inline dimensions. This article is written in English, which is a horizontal writing mode. This means that when you ask Flexbox to give you a row, you get a horizontal display of your flex items. In this case, main-start is on the left — the place in which sentences start in English.

If I were working in a right-to-left language such as Arabic, then the start edge would be on the right:

See the Pen Smashing Flexbox Series 1: row with rtl text by Rachel Andrew (@rachelandrew) on CodePen.

The initial values of flexbox mean that if all I do is create a flex container, my items would start on the right and be displayed moving towards the left. The start edge in the inline direction is the place where sentences start in the writing mode you are using.

If you happen to be in a vertical writing mode and ask for a row, your row will run vertically, because that is the way in which rows of text run in a vertical language. You can try this by adding the writing-mode property to your flex container and setting it to the value vertical-lr. Now, when you set flex-direction to row, you get a vertical column of items.

See the Pen Smashing Flexbox Series 1: row with a vertical writing mode by Rachel Andrew (@rachelandrew) on CodePen.

So a row can run horizontally, with a main-start of the left or the right, and also run vertically with main-start at the top. It’s still a flex-direction of row even if our horizontal text accustomed minds find it hard to think of a row running vertically!

To cause the items to lay themselves out in the block dimension, we set the value of flex-direction to column or column-reverse. In English (or in Arabic), we then see the items displaying one on top of the other down the page, starting at the top of the container.

In a Vertical Writing Mode, the Block dimension runs across the page, as this is the direction blocks are laid out in those writing modes. If you ask for a column in vertical-lr, your blocks will run left to right vertically:

See the Pen Smashing Flexbox Series 1: column in vertical-lr writing mode by Rachel Andrew (@rachelandrew) on CodePen.

However, no matter in which direction the blocks are displayed, if you are working with a column then you are working in the block dimension.

Understanding the fact that a row or a column can run in different physical directions is helpful in understanding some of the terminology being used for Grid and Flexbox. We don’t refer to ‘left and right’ or ‘top and bottom’ in Flexbox and Grid because we don’t make any assumption as to the writing mode of our document. All of CSS is becoming more writing mode aware; if you are interested in some other properties and values being implemented to make the rest of CSS behave in this same way, read my article on Logical Properties and Values.

As a summary, remember that:

  • flex-direction: row

    • main axis = inline dimension
    • main-start will be where sentences begin in that writing mode
    • cross axis = block dimension
  • flex-direction: column

    • main axis = block dimension
    • main-start will be where blocks start to lay out in that writing mode
    • cross axis = inline dimension

Initial Alignment

Some other things happen when we apply display: flex. Some initial alignment happens. In a future article in this series, we will take a good look at alignment; however, in our exploration of display: flex, we should look at the initial values that are applied.

Note: It is worth noting that while these alignment properties started life in the Flexbox specification, the Box Alignment specification will ultimately supersede those defined in the Flexbox specification, as explained in the Flexbox specification.

Main-Axis Alignment

The initial value of justify-content is set to flex-start. It is as if our CSS was:

.container {
    display: flex;
    justify-content: flex-start;
}

This is the reason that our flex items line up at the start edge of the flex container. It’s also the reason why when we set row-reverse they switch to the end edge because that edge then becomes the start of the main axis.

When you see an alignment property which begins with justify-, then it applies to the main axis in Flexbox. So justify-content performs main-axis alignment and aligns our items to the start.

The other possible values for justify-content are:

  • flex-end
  • center
  • space-around
  • space-between
  • space-evenly (added in Box Alignment)

These values deal with the distribution of available space in the flex container. This is why the items are moved around, or spaced out. If you add justify-content: space-between, then any available space is shared out between the items. However, this can only happen if there is free space to start with. If you had a tightly packed flex container (with no extra space after all the items had been laid out), then justify-content would do nothing at all.

You can see this if you switch your flex-direction to column. Without a height on the flex container there is no free space, so setting justify-content: space-between won’t achieve anything. If you add a height and make it so that the container is taller than is required to display the items, then the property has an effect:

See the Pen Smashing Flexbox Series 1: column with a height by Rachel Andrew (@rachelandrew) on CodePen.

Cross-Axis Alignment

Items are also aligned on the cross axis with a single line flex container; the alignment that we are performing is to align the boxes against each other in the line. In the next example, one of our boxes has more content in than all the others. Something is telling the other boxes to stretch to the same height. That something is the align-items property, which has an initial value of stretch:

See the Pen Smashing Guide to Layout: clearfix by Rachel Andrew (@rachelandrew) on CodePen.

When you see an alignment property which begins with align- and you are in flexbox, then you are dealing with cross-axis alignment, and align-items aligns the items within the flex line. The other possible values are:

  • flex-start
  • flex-end
  • center
  • baseline

If you do not want the boxes to all stretch to the height of the tallest, then setting align-self: flex-start will cause them all to align to the start edge of the cross axis.

See the Pen Smashing Flexbox Series 1: align-items: flex-start by Rachel Andrew (@rachelandrew) on CodePen.

Initial Values For The Flex Items

Finally, the flex items themselves also have initial values, they are set to:

  • flex-grow: 0
  • flex-shrink: 1
  • flex-basis: auto

This means that our items will not grow by default to fill the available space on the main axis. If flex-grow were set to a positive value, this would cause the items to grow and take up any available space.

The items can shrink, however, as flex-shrink is set to the positive value of 1. This means that if we have a very narrow flex container, then the items will get as small as they can before any overflow happens. This is sensible behavior; in general, we want things to stay inside their boxes and not overflow if there is space to display them.

In order to get the best possible layout by default, flex-basis is set to auto. We will have a proper look at what that means in a future article in this series, however, most of the time you can think of auto as “big enough to fit the content”. What you will see happen, when you have flex items that fill the container, and one of those items has a larger amount of content than the others, the larger item will be given more space.

See the Pen Smashing Flexbox Series 1: initial values of flex items by Rachel Andrew (@rachelandrew) on CodePen.

This is Flexbox’s flexibility in action. With a flex-basis of auto and no sizing applied to the items, the flex items have a base size of the max-content size. This would be the size they would be if they stretched out and did no wrapping whatsoever. Then, space is taken away from each item in proportion, detailed in the following note in the flexbox specification.

“Note: The flex shrink factor is multiplied by the flex base size when distributing negative space. This distributes negative space in proportion to how much the item is able to shrink, so that e.g. a small item won’t shrink to zero before a larger item has been noticeably reduced.”

The larger item has less space taken away and so we get the final layout. You can compare the two screenshots below, both taken using the example above. However, in the first screenshot, the third box has a smaller amount of content, and therefore our columns have a more equal distribution of space.


The example with a larger item shows the item taking up more space
The items flex to give the larger item more room (Large preview)

Flexbox here is helping us to end up with a reasonable end result given no other input from the person writing the CSS. Rather than reduce the space evenly and end up with a very tall item with a couple words on each line, it assigns that item more space to lay itself out. Within this kind of behavior is the key to the real use cases for Flexbox. Flexbox is at its best when used to lay sets of things out — along one axis — in a flexible and content aware way. I’m touching on a little of the detail here, but we will take a proper look at these algorithms later in this series.

Summary

In this article, I’ve taken the initial values of Flexbox, in order to explain what actually happens when you say display: flex. It’s a surprising amount once you begin to unpack it, and contained within these few properties are many of the key features of flex layouts.

Flex layouts are flexible: they try to make good choices by default about your content — squishing and stretching to get the best readability. Flex layouts are writing mode aware: the directions of row and column relate to the writing mode being used. Flex layouts allow alignment of the items as a group on the main axis, by choosing how space is distributed. They allow alignment of items within their flex line, moving the items on the cross axis in relationship to each other. Importantly, flex layouts understand how big your content is, and try to make good basic decisions in order to display it. In future articles, we will explore these areas in more depth, and consider further exactly when and why we might choose to use Flexbox.

Smashing Editorial
(il)


Source: Smashing Magazine

User Experience Psychology And Performance: SmashingConf Videos

User Experience Psychology And Performance: SmashingConf Videos

User Experience Psychology And Performance: SmashingConf Videos

The Smashing Editorial

2018-08-01T13:30:35+02:00
2018-08-01T12:00:50+00:00

Today, we’d like to shine a light on two videos from our archives as we explore two very different approaches to User Experience (UX). The first explores how we relate our websites to the needs and situations of our visitors, trying to meet them where they are emotionally. The second is a detailed technical exploration into how we measure and track the data around performance as it relates to user experience.

The second video may seem unrelated to the first video; however, while the collecting and analyzing of data might seem very impersonal, the improvements we can make based on the information makes a real difference to the experience of the people we build our sites to serve.

Designing Powerful User Experiences With Psychology

Recorded at the SmashingConf in San Francisco earlier this year, Joe Leech explains how psychology impacts user experience. Joe explains the frustrations people using our products face, and the things happening in their everyday lives and environment that can make interacting with our websites and applications difficult. He goes on to help us understand how we can design in a way to help these visitors rather than frustrate them.

How’s The UX On The Web, Really?

Once you have created a great user experience, how do you know that it is really working well? Especially in terms of site performance, we can track how people are using our sites and examine that data to see what is really happening.

At the SmashingConf in London, Ilya Grigorik was the Mystery Speaker and spoke about the ways to assess performance in real terms, and benchmark your application against other destinations on the web.

Enjoyed listening to these talks? There are many more SmashingConf videos on Vimeo. We’re also getting ready for the upcoming SmashingConf in New York — see you there? 😉

Smashing Editorial
(ra, il)


Source: Smashing Magazine

Sunshine All Day Every Day (August 2018 Wallpapers Edition)

Sunshine All Day Every Day (August 2018 Wallpapers Edition)

Sunshine All Day Every Day (August 2018 Wallpapers Edition)

Cosima Mielke

2018-07-31T13:11:56+02:00
2018-07-31T13:11:49+00:00

Everybody loves a beautiful wallpaper to freshen up their desktops. So to cater for new and unique artworks on a regular basis, we embarked on our monthly wallpapers adventure nine years ago, and since then, countless artists and designers from all over the world have accepted the challenge and submitted their designs to it. It wasn’t any different this time around, of course.

This post features wallpapers created for August 2018. Each of them comes in versions with and without a calendar and can be downloaded for free. A big thank-you to everyone who participated!

Finally, as a little bonus, we also collected some “oldies but goodies” from previous August editions in this collection. Please note, that they only come in a non-calendar version. Which one will make it to your desktop this month?

Please note that:

  • All images can be clicked on and lead to the preview of the wallpaper,
  • We respect and carefully consider the ideas and motivation behind each and every artist’s work. This is why we give all artists the full freedom to explore their creativity and express emotions and experience 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.

Submit your wallpaper

We are always looking for creative designers and artists to be featured in our wallpapers posts. So if you have an idea for a wallpaper, please don’t hesitate to submit your design. We’d love to see what you’ll come up with. Join in! →

Purple Haze

“Meet Lucy: she lives in California, loves summer and sunbathing at the beach. This is our Jimi Hendrix Experience tribute. Have a lovely summer!” — Designed by PopArt Web Design from Serbia.

Purple Haze

Coffee Break Time

Designed by Ricardo Gimenes from Sweden.

Coffee Break Time

A Midsummer Night’s Dream

“Inspired by William Shakespeare.” — Designed by Sofie Lee from South Korea.

A Midsummer Night’s Dream

This August, Be The Best!

“Here is the August monthly calendar to remind you of your as well as your team’s success in the previous months. Congratulations, you guys deserved all the success that came your way. Hope you continue this success this month and in the coming months.” — Designed by Webandcrafts from India.

This August, Be The Best!

No Drama LLama

“Llamas are showing up everywhere around us, so why not on our desktops too?” — Designed by Melissa Bogemans from Belgium.

No Drama LLama

The Colors Of Life

“The countenance of the clown is a reflection of our own feelings and emotions of life in the most colorful way portrayed with a deeper and stronger expression whether it is a happy clown or a sad clown. The actions of the clown signify your uninhibited nature — the faces of life in its crudest form — larger, louder, and in an undiluted way.” — Designed by Acowebs from India.

The Colors Of Life

Hello August

“August brings me to summer, and summer brings me to fruit. In the hot weather there is nothing better than a fresh piece of fruit.” — Designed by Bram Wieringa from Belgium.

Hello August

Exploring Thoughts

“Thoughts, planning, daydreams are simply what minds do. It’s following the human impulse to explore the unexplored, question what doesn’t ring true, dig beneath the surface of what you think you know to formulate your own reality, and embrace the inherent ‘now’ of life. The main character here has been created blending texture and composition. Thoughts will never have an end.” — Designed by Sweans from London.

Exploring Thoughts

Chilling At The Beach

“In August it’s Relaxation Day on the 15th so that’s why I decided to make a wallpaper in which I showcase my perspective of relaxing. It’s a wallpaper where you’re just chilling at the beach with a nice cocktail and just looking at the sea and looking how the waves move. That is what I find relaxing! I might even dip my feet in the water and go for a swim if I’m feeling adventurous!” — Designed by Senne Mommens from Belgium.

Chilling At The Beach

Let Peace Reign

“The freedom and independence sprouts from unbiased and educated individuals that build the nation for peace, prosperity and happiness to reign in the country for healthy growth.” — Designed by Admission Zone from India.

Let Peace Reign

On The Ricefields Of Batad

“Somebody once told me that I should make the most out of vacation. So there I was, carefully walking on a stone ridge in the ricefields of Batad. This place is hidden high up in the mountains. Also August is harvesting season.” — Designed by Miguel Lammens from Belgium.

On The Ricefields Of Batad

Fantasy

Designed by Ilse van den Boogaart from The Netherlands.

Fantasy

Oldies But Goodies

The past nine years have brought forth lots of inspiring wallpapers, and, well, it’d be a pity to let them gather dust somewhere down in the archives. That’s why we once again dug out some goodies from past August editions that are bound to make a great fit on your desktop still today. Please note that these wallpapers, thus, don’t come with a calendar.

Happiness Happens In August

“Many people find August one of the happiest months of the year because of holidays. You can spend days sunbathing, swimming, birdwatching, listening to their joyful chirping, and indulging in sheer summer bliss. August 8th is also known as the Happiness Happens Day, so make it worthwhile.” — Designed by PopArt Studio from Serbia.

Happiness Happens In August

Psst, It’s Camping Time…

“August is one of my favorite months, when the nights are long and deep and crackling fire makes you think of many things at once and nothing at all at the same time. It’s about these heat and cold which allow you to touch the eternity for a few moments.” — Designed by Igor Izhik from Canada.

Psst, It’s Camping Time...

Bee Happy!

“August means that fall is just around the corner, so I designed this wallpaper to remind everyone to ‘bee happy’ even though summer is almost over. Sweeter things are ahead!” — Designed by Emily Haines from the United States.

Bee Happy!

Hello Again

“In Melbourne it is the last month of quite a cool winter so we are looking forward to some warmer days to come.” — Designed by Tazi from Australia.

Hello Again

A Bloom Of Jellyfish

“I love going to aquariums – the colours, patterns and array of blue hues attract the nature lover in me while still appeasing my design eye. One of the highlights is always the jellyfish tanks. They usually have some kind of light show in them, which makes the jellyfish fade from an intense magenta to a deep purple – and it literally tickles me pink. On a recent trip to uShaka Marine World, we discovered that the collective noun for jellyfish is a bloom and, well, it was love-at-first-collective-noun all over again. I’ve used some intense colours to warm up your desktop and hopefully transport you into the depths of your own aquarium.” — Designed by Wonderland Collective from South Africa.

A Bloom Of Jellyfish

Let Us Save The Tigers

“Let us take a pledge to save these endangered species and create a world that is safe for them to live and perish just like all creatures.” — Designed by Acodez IT Solutions from India.

Let Us Save The Tigers

Shades

“It’s sunny outside (at least in the Northern Hemisphere!), so don’t forget your shades!” — Designed by James Mitchell from the United Kingdom.

Shades

Ahoy

Designed by Webshift 2.0 from South Africa.

Monthly Quality Desktop Wallpaper - August 2012

About Everything

“I know what you’ll do this August. 🙂 Because August is about holiday. It’s about exploring, hiking, biking, swimming, partying, feeling and laughing. August is about making awesome memories and enjoying the summer. August is about everything. An amazing August to all of you!” — Designed by Ioana Bitin from Bucharest, Romania.

About Everything

Shrimp Party

“A nice summer shrimp party!” — Designed by Pedro Rolo from Portugal.

Shrimp Party

The Ocean Is Waiting

“In August, make sure you swim a lot. Be cautious though.” — Designed by Igor Izhik from Canada.

The Ocean Is Waiting

Oh La La… Paris Night

“I like the Paris night! All is very bright!” — Designed by Verónica Valenzuela from Spain.

Oh la la.... Paris night

World Alpinism Day

“International Day of Alpinism and Climbing.” Designed by cheloveche.ru from Russia.

World Alpinism Day

Estonian Summer Sun

“This is a moment from Southern Estonia that shows amazing summer nights.” Designed by Erkki Pung / Sviiter from Estonia.

Estonian Summer Sun

Aunt Toula At The Beach

“A memory from my childhood summer vacations.” — Designed by Poppie Papanastasiou from Greece.

Aunt Toula At The Beach

Flowing Creativity

Designed by Creacill, Carole Meyer from Luxembourg.

Flowing creativity

Searching for Higgs Boson

Designed by Vlad Gerasimov from Russia.

Monthly Quality Desktop Wallpaper - August 2012

Unforgettable Summer Night

Designed by BootstrapDash from India.

Unforgettable Summer Night

Join In Next Month!

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


Source: Smashing Magazine

What Do You Need To Know When Converting A Flash Game Into HTML5?

What Do You Need To Know When Converting A Flash Game Into HTML5?

What Do You Need To Know When Converting A Flash Game Into HTML5?

Tomasz Grajewski

2018-07-30T14:00:26+02:00
2018-07-30T14:19:07+00:00

With the rise of HTML5 usage, many companies start redoing their most popular titles to get rid of outdated Flash and match their products to the latest industry standards. This change is especially visible in the Gambling/Casino & Entertainment industries and has been happening for several years now, so a decent selection of titles has already been converted.

Unfortunately, when browsing the Internet, you can quite often stumble upon examples of a seemingly hasty job, which results in the lover quality of the final product. That’s why it’s a good idea for game developers to dedicate some of their time for getting familiar with the subject of Flash to HTML5 conversion and learning which mistakes to avoid before getting down to work.

Among the reasons for choosing JavaScript instead of Flash, apart from the obvious technical issues, is also the fact that changing your game design from SWF to JavaScript can yield a better user experience, which in turn give it a modern look. But how to do it? Do you need a dedicated JavaScript game converter to get rid of this outdated technology? Well, Flash to HTML5 conversion can be a piece of cake — here’s how to take care of it.

Recommended reading: Principles Of HTML5 Game Design

How To Improve HTML5 Game Experience

Converting a game to another platform is an excellent opportunity to improve it, fix its issues, and increase the audience. Below are few things that can be easily done and are worth considering:

  • Supporting mobile devices
    Converting from Flash to JavaScript allows reaching a broader audience (users of mobile devices); support for touchscreen controls usually needs to be implemented into the game, too. Luckily, both Android and iOS devices now also support WebGL, so 30 or 60 FPS rendering usually can be easily achieved. In many cases, 60 FPS won’t cause any problems, which will only improve with time, as mobile devices become more and more performant.

  • Improving performance
    When it comes to comparing ActionScript and JavaScript, the latter is faster than the first one. Other than that, converting a game is a good occasion to revisit algorithms used in game code. With JavaScript game development you can optimize them or completely strip unused code that’s left by original developers.
  • Fixing bugs and making improvements to the gameplay
    Having new developers looking into game’s source code can help to fix known bugs or discover new and very rare ones. This would make playing the game less irritating for the players, which would make them spend more time on your site and encourage to try your other games.
  • Adding web analytics
    In addition to tracking the traffic, web analytics can also be used to gather knowledge on how players behave in a game and where they get stuck during gameplay.
  • Adding localization
    This would increase the audience and is important for kids from other countries playing your game. Or maybe your game is not in English and you want to support that language?

Why Skipping HTML And CSS For In-Game UI Will Improve Game Performance

When it comes to JavaScript game development, it may be tempting to leverage HTML and CSS for in-game buttons, widgets, and other GUI elements. My advice is to be careful here. It’s counterintuitive, but actually leveraging DOM elements is less performant on complex games and this gains more significance on mobile. If you want to achieve constant 60 FPS on all platforms, then resigning from HTML and CSS may be required.

Non-interactive GUI elements, such as health bars, ammo bars, or score counters can be easily implemented in Phaser by using regular images (the Phaser.Image class), leveraging the .crop property for trimming and the Phaser.Text class for simple text labels.

Such interactive elements as buttons and checkboxes can be implemented by using the built-in Phaser.Button class. Other, more complex elements can be composed of different simple types, like groups, images, buttons and text labels.

Note: Each time you instantiate a Phaser.Text or PIXI.Text object, a new texture is created to render text onto. This additional texture breaks vertex batching, so be careful not to have too many of them.

How To Ensure That Custom Fonts Have Loaded

If you want to render text with a custom vector font (e.g. TTF or OTF), then you need to ensure that the font has already been loaded by the browser before rendering any text. Phaser v2 doesn’t provide a solution for this purpose, but another library can be used: Web Font Loader.

Assuming that you have a font file and include the Web Font Loader in your page, then below is a simple example of how to load a font:

Make a simple CSS file that will be loaded by Web Font Loader (you don’t need to include it in your HTML):

@font-face {
    // This name you will use in JS
    font-family: 'Gunplay';
    // URL to the font file, can be relative or absolute
    src: url('../fonts/gunplay.ttf') format('truetype');
    font-weight: 400;
}

Now define a global variable named WebFontConfig. Something as simple as this will usually suffice:

var WebFontConfig = {
   'classes': false,
   'timeout': 0,
   'active': function() {
       // The font has successfully loaded...
   },
   'custom': {
       'families': ['Gunplay'],
       // URL to the previously mentioned CSS
       'urls': ['styles/fonts.css']
   }
};

It the end, remember to put your code in the ‘active’ callback shown above. And that’s it!

How To Make It Easier For Users To Save The Game

To persistently store local data in ActionScript you would use the SharedObject class. In JavaScript, the simple replacement is localStorage API, which allows storing strings for later retrieval, surviving page reloads.

Saving data is very simple:

var progress = 15;
localStorage.setItem('myGame.progress', progress);

Note that in the above example the progress variable, which is a number, will be converted to a string.

Loading is simple too, but remember that retrieved values will be strings or null if they don’t exists.

var progress = parseInt(localStorage.getItem('myGame.progress')) || 0;

Here we’re ensuring that the return value is a number. If it doesn’t exist, then 0 will be assigned to the progress variable.

You can also store and retrieve more complex structures, for example, JSON:

var stats = {'goals': 13, 'wins': 7, 'losses': 3, 'draws': 1};
localStorage.setItem('myGame.stats', JSON.stringify(stats));
…
var stats = JSON.parse(localStorage.getItem('myGame.stats')) || {};

There are some cases when the localStorage object won’t be available. For example, when using the file:// protocol or when a page is loaded in a private window. You can use the try and catch statement to ensure your code will both continue working and use default values, what is shown in the example below:

try {
    var progress = localStorage.getItem('myGame.progress');
} catch (exception) {
    // localStorage not available, use default values
}

Another thing to remember is that the stored data is saved per domain, not per URL. So if there is a risk that many games are hosted on a single domain, then it’s better to use a prefix (namespace) when saving. In the example above 'myGame.' is such a prefix and you usually want to replace it with the name of the game.

Note: If your game is embedded in an iframe, then localStorage won’t persist on iOS. In this case, you would need to store data in the parent iframe instead.

How To Leverage Replacing Default Fragment Shader

When Phaser and PixiJS render your sprites, they use a simple internal fragment shader. It doesn’t have many features because it’s tailored for a speed. However, you can replace that shader for your purposes. For example, you can leverage it to inspect overdraw or support more features for rendering.

Below is an example of how to supply your own default fragment shader to Phaser v2:

function preload() {
    this.load.shader('filename.frag', 'shaders/filename.frag');
}

function create() {
    var renderer = this.renderer;
    var batch = renderer.spriteBatch;
    batch.defaultShader = 
        new PIXI.AbstractFilter(this.cache.getShader('filename.frag'));
    batch.setContext(renderer.gl);
}

Note: It’s important to remember that the default shader is used for ALL sprites as well as when rendering to a texture. Also, keep in mind that using complex shaders for all in-game sprites will greatly reduce rendering performance.

How To Change Tinting Method With A Default Shader

Custom default shader can be used to replace default tinting method in Phaser and PixiJS.

Tinting in Phaser and PixiJS works by multiplying texture pixels by a given color. Multiplication always darkens colors, which obviously is not a problem; it’s simply different from the Flash tinting. For one of our games, we needed to implement tinting similar to Flash and decided that a custom default shader could be used. Below is an example of such fragment shader:

// Specific tint variant, similar to the Flash tinting that adds
// to the color and does not multiply. A negative of a color
// must be supplied for this shader to work properly, i.e. set
// sprite.tint to 0 to turn whole sprite to white.
precision lowp float;

varying vec2 vTextureCoord;
varying vec4 vColor;

uniform sampler2D uSampler;

void main(void) {
    vec4 f = texture2D(uSampler, vTextureCoord);
    float a = clamp(vColor.a, 0.00001, 1.0);
    gl_FragColor.rgb = f.rgb * vColor.a + clamp(1.0 - vColor.rgb/a, 0.0, 1.0) * vColor.a * f.a;
    gl_FragColor.a = f.a * vColor.a;
}

This shader lightens pixels by adding a base color to the tint one. For this to work, you need to supply negative of the color you want. Therefore, in order to get white, you need to set:

sprite.tint = 0x000000;  // This colors the sprite to white
Sprite.tint = 0x00ffff;  // This gives red

The result in our game looks like this (notice how tanks flash white when hit):

Custom default shader (tanks flashing white).

How To Inspect Overdraw To Detect Fill Rate Issues

Replacing default shader can also be leveraged to help with debugging. Below I’ve explained how overdraw can be detected with such a shader.

Overdrawing happens when many or all pixels on the screen are rendered multiple times. For example, many objects taking the same place and being rendered one over another. How many pixels a GPU can render per second is described as fill rate. Modern desktop GPUs have excessive fill rate for usual 2D purposes, but mobile ones are a lot slower.

There is a simple method of finding out how many times each pixel on the screen is written by replacing the default global fragment shader in PixiJS and Phaser with this one:

void main(void) {
    gl_FragColor.rgb += 1.0 / 7.0;
}

This shader lightens pixels that are being processed. The number 7.0 indicates how many writes are needed to turn pixel white; you can tune this number to your liking. In other words, lighter pixels on screen were written several times, and white pixels were written at least 7 times.

This shader also helps to find both “invisible” objects that for some reason are still rendered and sprites that have excessive transparent areas around that need to be stripped (GPU still needs to process transparent pixels in your textures).


Example of the Overdraw shader in action in game development
Overdraw shader in action. (Large preview)

The picture on the left shows how a player sees the game, while the one on the right displays the effect of applying the overdraw shader to the same scene.

Why Physics Engines Are Your Friends

A physics engine is a middleware that’s responsible for simulating physics bodies (usually rigid body dynamics) and their collisions. Physics engines simulate 2D or 3D spaces, but not both. A typical physics engine will provide:

  • object movement by setting velocities, accelerations, joints, and motors;
  • detecting collisions between various shape types;
  • calculating collision responses, i.e. how two objects should react when they collide.

At Merixstudio, we’re big fans of the Box2D physics engine and used it on a few occasions. There is a Phaser plugin that works well for this purpose. Box2D is also used in the Unity game engine and GameMaker Studio 2.

While a physics engine will speed-up your development, there is a price you’ll have to pay: reduced runtime performance. Detecting collisions and calculating responses is a CPU-intensive task. You may be limited to several dozen dynamic objects in a scene on mobile phones or face degraded performance, as well as reduced frame rate deep below 60 FPS.


Example of the difference in the scene of a game with and withour Phaser physics debug overlay displayed on top
Phaser’s physics debug overlay. (Large preview)

The left part of the image is a scene from a game, while the right side shows the same scene with Phaser physics debug overlay displayed on top.

How To Export Sounds From A .fla File

If you have a Flash game sound effects inside of a .fla file, then exporting them from GUI is not possible (at least not in Adobe Animate CC 2017) due to the lack of menu option serving this purpose. But there is another solution — a dedicated script that does just that:

function normalizeFilename(name) {
   // Converts a camelCase name to snake_case name
   return name.replace(/([A-Z])/g, '_$1').replace(/^_/, '').toLowerCase();
}

function displayPath(path) {
   // Makes the file path more readable
   return unescape(path).replace('file:///', '').replace('|', ':');
}


fl.outputPanel.clear();

if (fl.getDocumentDOM().library.getSelectedItems().length > 0)
   // Get only selected items
   var library = fl.getDocumentDOM().library.getSelectedItems();
else
   // Get all items
   var library = fl.getDocumentDOM().library.items;

// Ask user for the export destination directory
var root = fl.browseForFolderURL('Select a folder.');
var errors = 0;

for (var i = 0; i < library.length; i++) {
   var item = library[i];
   if (item.itemType !== 'sound')
       continue;

   var path = root + '/';

   if (item.originalCompressionType === 'RAW')
       path += normalizeFilename(item.name.split('.')[0]) + '.wav';
   else
       path += normalizeFilename(item.name);

   var success = item.exportToFile(path);
   if (!success)
       errors += 1;
   fl.trace(displayPath(path) + ': ' + (success ? 'OK' : 'Error'));
}

fl.trace(errors + ' error(s)');

How to use the script to export sound files:

  1. Save the code above as a .jsfl file on your computer;
  2. Open a .fla file with Adobe Animate;
  3. Select ‘Commands’ → ‘Run Command’ from the top menu and select the script in the dialogue that opens;
  4. Now another dialogue file pops up for selecting export destination directory.

And done! You should now have WAV files in the specified directory. What’s left to do is convert them to, for example, MP3’s, OGG, or AAC.

How To Use MP3s In Flash To HTML5 Convertions

The good old MP3 format is back, as some patents have expired and now every browser can decode and play MP3’s. This makes development a bit easier since finally there’s no need to prepare two separate audio formats. Previously you needed, for instance, OGG and AAC files, while now MP3 will suffice.

Nonetheless, there are two important things you need to remember about MP3:

  • MP3’s need to decode after loading, what can be time-consuming, especially on mobile devices. If you see a pause after all your assets have loaded, then it probably means that MP3’s being decoded;
  • gaplessly playing looped MP3’s is a little problematic. The solution is to use mp3loop, about which you can read in the article posted by Compu Phase.

So, Why Should You Convert Flash To JavaScript?

As you can see, Flash to JavaScript conversion is not impossible if you know what to do. With knowledge and skill, you can stop struggling with Flash and enjoy the smooth, entertaining games created in JavaScript. Don’t try to fix Flash — get rid of it before everyone is forced to do so!

Want To Learn More?

In this article, I was focused mainly on Phaser v2. However, a newer version of Phaser is now available, and I strongly encourage you to check it out, as it introduced a plethora of fresh, cool features, such as multiple cameras, scenes, tilemaps, or Matter.js physics engine.

If you are brave enough and want to create truly remarkable things in browsers, then WebGL is the right thing to learn from the ground up. It’s a lower level of abstraction than various game-building frameworks or tools but allows to achieve greater performance and quality even if you work on 2D games or demos. Among many websites which you may find useful when learning the basics of WebGL would be WebGL Fundamentals (uses interactive demos). In addition to that, to find out more about WebGL feature adoption rates, check WebGL Stats.

Always remember that there’s no such thing as too much knowledge — especially when it comes to game development!

Smashing Editorial
(rb, ra, yk, il)


Source: Smashing Magazine

Logging Activity With The Web Beacon API

Logging Activity With The Web Beacon API

Logging Activity With The Web Beacon API

Drew McLellan

2018-07-27T13:40:14+02:00
2018-07-27T14:14:35+00:00

The Beacon API is a JavaScript-based Web API for sending small amounts of data from the browser to the web server without waiting for a response. In this article, we’ll look at what that can be useful for, what makes it different from familiar techniques like XMLHTTPRequest (‘Ajax’), and how you can get started using it.

If you know why you want to use Beacon already, feel free to jump directly to the Getting Started section.

What Is The Beacon API For?

The Beacon API is used for sending small amounts of data to a server without waiting for a response. That last part is critical and is the key to why Beacon is so useful — our code never even gets to see a response, even if the server sends one. Beacons are specifically for sending data and then forgetting about it. We don’t expect a response and we don’t get a response.

Think of it like a postcard sent home when on vacation. You put a small amount of data on it (a bit of “Wish you were here” and “The weather’s been lovely”), put it in the mailbox, and you don’t expect a response. No one sends a return postcard saying “Yes, I do wish I was there actually, thank you very much!”

For modern websites and applications, there’s a number of use cases that fall very neatly into this pattern of send-and-forget.

Tracking Stats And Analytics Data

The first use case that comes to mind for most people is analytics. Big solutions like Google Analytics might give a good overview of things like page visits, but what if we wanted something more customized? We could write some JavaScript to track what’s happening in a page (maybe how a user interacts with a component, how far they’ve scrolled to, or which articles have been displayed before they follow a CTA) but we then need to send that data to the server when the user leaves the page. Beacon is perfect for this, as we’re just logging the data and don’t need a response.

There’s no reason we couldn’t also cover the sort of mundane tasks often handled by Google Analytics, reporting on the user themselves and the capability of their device and browser. If the user has a logged in session, you could even tie those stats back to a known individual. Whatever data you gather, you can send it back to the server with Beacon.

Debugging And Logging

Another useful application for this behavior is logging information from your JavaScript code. Imagine you have a complex interactive component on your page that works perfectly for all your tests, but occasionally fails in production. You know it’s failing, but you can’t see the error in order to begin debugging it. If you can detect a failure in the code itself, you could then gather up diagnostics and use Beacon to send it all back for logging.

In fact, any logging task can usefully be performed using Beacon, be that creating save-points in a game, collecting information on feature use, or recording results from a multivariate test. If it’s something that happens in the browser that you want the server to know about, then Beacon is likely a contender.

Can’t We Already Do This?

I know what you’re thinking. None of this is new, is it? We’ve been able to communicate from the browser to the server using XMLHTTPRequest for more than a decade. More recently we also have the Fetch API which does much the same thing with a more modern promise-based interface. Given that, why do we need the Beacon API at all?

The key here is that because we don’t get a response, the browser can queue up the request and send it without blocking execution of any other code. As far as the browser is concerned, it doesn’t matter if our code is still running or not, or where the script execution has got to, as there’s nothing to return it can just background the sending of the HTTP request until it’s convenient to send it.

That might mean waiting until CPU load is lower, or until the network is free, or even just sending it right away if it can. The important thing is that the browser queues the beacon and returns control immediately. It does not hold things up while the beacon sends.

To understand why this is a big deal, we need to look at how and when these sorts of requests are issued from our code. Take our example of an analytics logging script. Our code may be timing how long the users spend on a page, so it becomes critical that the data is sent back to the server at the last possible moment. When the user goes to leave a page, we want to stop timing and send the data back home.

Typically, you’d use either the unload or beforeunload event to execute the logging. These are fired when the user does something like following a link on the page to navigate away. The trouble here is that code running on one of the unload events can block execution and delay the unloading of the page. If unloading of the page is delayed, then the loading next page is also delayed, and so the experience feels really sluggish.

Keep in mind how slow HTTP requests can be. If you’re thinking about performance, typically one of the main factors you try to cut down on is extra HTTP requests because going out to the network and getting a response can be super slow. The very last thing you want to do is put that slowness between the activation of a link and the start of the request for the next page.

Beacon gets around this by queuing the request without blocking, returning control immediately back to your script. The browser then takes care of sending that request in the background without blocking. This makes everything much faster, which makes users happier and lets us all keep our jobs.

Getting Started

So we understand what Beacon is, and why we might use it, so let’s get started with some code. The basics couldn’t be simpler:

let result = navigator.sendBeacon(url, data);

The result is boolean, true if the browser accepted and queued the request, and false if there was a problem in doing so.

Using navigator.sendBeacon()

navigator.sendBeacon takes two parameters. The first is the URL to make the request to. The request is performed as an HTTP POST, sending any data provided in the second parameter.

The data parameter can be in one of several formats, all if which are taken directly from the Fetch API. This can be a Blob, a BufferSource, FormData or URLSearchParams — basically any of the body types used when making a request with Fetch.

I like using FormData for basic key-value data as it’s uncomplicated and easy to read back.

// URL to send the data to
let url = '/api/my-endpoint';
    
// Create a new FormData and add a key/value pair
let data = new FormData();
data.append('hello', 'world');
    
let result = navigator.sendBeacon(url, data);
    
if (result) { 
  console.log('Successfully queued!');
} else {
  console.log('Failure.');
}

Browser Support

Support in browsers for Beacon is very good, with the only notable exceptions being Internet Explorer (works in Edge) and Opera Mini. For most uses, that should be fine, but it’s worth testing for support before trying to use navigator.sendBeacon.

That’s easy to do:

if (navigator.sendBeacon) {
  // Beacon code
} else {
  // No Beacon. Maybe fall back to XHR?
}

If Beacon isn’t available and your request is important, you could fall back to a blocking method such as XHR. Depending on your audience and purpose, you might equally choose to not bother.

An Example: Logging Time On A Page

To see this in practice, let’s create a basic system to time how long a user stays on a page. When the page loads we’ll note the time, and when the user leaves the page we’ll send the start time and current time to the server.

As we only care about time spent (not the actual time of day) we can use performance.now() to get a basic timestamp as the page loads:

let startTime = performance.now();

If we wrap up our logging into a function, we can call it when the page unloads.

let logVisit = function() {
  // Test that we have support
  if (!navigator.sendBeacon) return true;
      
  // URL to send the data to, e.g.
  let url = '/api/log-visit';
      
  // Data to send
  let data = new FormData();
  data.append('start', startTime);
  data.append('end', performance.now());
  data.append('url', document.URL);
      
  // Let's go!
  navigator.sendBeacon(url, data);
};

Finally, we need to call this function when the user leaves the page. My first instinct was to use the unload event, but Safari on a Mac seems to block the request with a security warning, so beforeunload works just fine for us here.

window.addEventListener('beforeunload', logVisit);

When the page unloads (or, just before it does) our logVisit() function will be called and provided the browser supports the Beacon API our beacon will be sent.

(Note that if there is no Beacon support, we return true and pretend it all worked great. Returning false would cancel the event and stop the page unloading. That would be unfortunate.)

Considerations When Tracking

As so many of the potential uses for Beacon revolve around tracking of activity, I think it would be remiss not to mention the social and legal responsibilities we have as developers when logging and tracking activity that could be tied back to users.

GDPR

We may think of the recent European GDPR laws as they related to email, but of course, the legislation relates to storing any type of personal data. If you know who your users are and can identify their sessions, then you should check what activity you are logging and how it relates to your stated policies.

Often we don’t need to track as much data as our instincts as developers tell us we should. It can be better to deliberately not store information that would identify a user, and then you reduce your likelihood of getting things wrong.

DNT: Do Not Track

In addition to legal requirements, most browsers have a setting to enable the user to express a desire not to be tracked. Do Not Track sends an HTTP header with the request that looks like this:

DNT: 1

If you’re logging data that can track a specific user and the user sends a positive DNT header, then it would be best to follow the user’s wishes and anonymize that data or not track it at all.

In PHP, for example, you can very easily test for this header like so:

if (!empty($_SERVER['HTTP_DNT'])) { 
  // User does not wish to be tracked ... 
}

In Conclusion

The Beacon API is a really useful way to send data from a page back to the server, particularly in a logging context. Browser support is very broad, and it enables you to seamlessly log data without negatively impacting the user’s browsing experience and the performance of your site. The non-blocking nature of the requests means that the performance is much faster than alternatives such as XHR and Fetch.

If you’d like to read more about the Beacon API, the following sites are worth a look.

Smashing Editorial
(ra, il)


Source: Smashing Magazine

WordPress Notifications Made Easy

WordPress Notifications Made Easy

WordPress Notifications Made Easy

Jakub Mikita

2018-07-26T14:20:27+02:00
2018-07-26T13:44:26+00:00

WordPress doesn’t offer any kind of notification system. All you can use is the wp_mail() function, but all of the settings have to be hardcoded, or else you have to create a separate settings screen to allow the user tweak the options. It takes many hours to write a system that is reliable, configurable and easy to use. But not anymore. I’ll show you how to create your own notification system within minutes with the free Notification plugin. By notification, I mean any kind of notification. Most of the time, it will be email, but with the plugin we’ll be using, you can also send webhooks and other kinds of notifications.

While creating a project for one of my clients, I encountered this problem I’ve described. The requirement was to have multiple custom email alerts with configurable content. Instead of hardcoding each and every alert, I decided to build a system. I wanted it to be very flexible, and the aim was to be able to code new scenarios as quickly as possible.

The code I wrote was the start of a great development journey. It turned out that the system I created was flexible enough that it could work as a separate package. This is how the Notification plugin was born.

Suppose you want to send an email about a user profile being updated by one of your website’s members. WordPress doesn’t provide that functionality, but with the Notification plugin, you can create such an email in minutes. Or suppose you want to synchronize your WooCommerce products with third-party software by sending a webhook to a separate URL every time a new product is published. That’s easy to do with the plugin, too.

Lessons Learned While Developing WordPress Plugins

Good plugin development and support lead to more downloads. More downloads mean more money and a better reputation. Learn how you can develop good-quality products with seven golden rules. Read more →

In this article, you’ll learn how to integrate the plugin in your own application and how to create an advanced WordPress notification system more quickly and easily than ever.

In this article, we’ll cover:

  1. how to install the plugin,
  2. the idea behind the plugin and its architecture,
  3. creating a custom scenario for notifications,
  4. creating the action (step 1 of the process),
  5. creating the trigger (step 2 of the process),
  6. creating the custom notification type,
  7. how to enable white-label mode and bundle the plugin in your package.

Installing The Plugin

To create your own scenarios, you are going to need the Notification plugin. Just install it from the WordPress.org repository in your WordPress dashboard, or download it from the GitHub repository.

Large preview

Later in the article, you’ll learn how to hide this plugin from your clients and make it work as an integrated part of your plugin or theme.

The Idea Of The Plugin

Before going into your code editor, you’ll need to know what the plugin’s architecture looks like. The plugin contains many various components, but its core is really a few abstract classes.

The main components are:

  • The notification
    This could be an email, webhook, push notification or SMS.
  • The trigger
    This is what sends the notification. It’s effectively the WordPress action.
  • The merge tag
    This is a small portion of the dynamic content, like {post_title}.

To give you a better idea of how it all plays together, you can watch this short video:

The core of the Notification plugin is really just an API. All of the default triggers, like Post published and User registered are things built on top of that API.

Because the plugin was created for developers, adding your own triggers is very easy. All that’s required is a WordPress action, which is just a single line of code and a class declaration.

Custom Scenario

Let’s devise a simple scenario. We’ll add a text area and button to the bottom of each post, allowing bugs in the article to be reported. Then, we’ll trigger the notification upon submission of the form.

This scenario was covered in another article, “Submitting Forms Without Reloading the Page: AJAX Implementation in WordPress”.

For simplicity, let’s make it a static form, but there’s no problem putting the action in an AJAX handler, instead of in the wp_mail() function.

Let’s create the form.

The Form

add_filter( 'the_content', 'report_a_bug_form' );
function report_a_bug_form( $content ) {

    // Display the form only on posts.
    if ( ! is_single() ) {
        return $content;
    }

    // Add the form to the bottom of the content.
    $content .= '<form action="' . admin_url( 'admin-post.php' ) . '" method="POST">
        <input type="hidden" name="post_id" value="' . get_ID() . '">
        <input type="hidden" name="action" value="report_a_bug">
        <textarea name="message" placeholder="' . __( 'Describe what's wrong...', 'reportabug' ) . '"></textarea>
        <button>' . __( 'Report a bug', 'reportabug' ) . '</button>
    </div>';

    return $content;

}

Please note that many components are missing, like WordPress nonces, error-handling and display of the action’s result, but these are not the subject of this article. To better understand how to handle these actions, please read the article mentioned above.

Preparing The Action

To trigger the notification, we are going to need just a single action. This doesn’t have to be a custom action like the one below. You can use any of the actions already registered in WordPress core or another plugin.

The Form Handler And Action

add_action( 'admin_post_report_a_bug', 'report_a_bug_handler' );
add_action( 'admin_post_nopriv_report_a_bug', 'report_a_bug_handler' );
function report_a_bug_handler() {

    do_action( 'report_a_bug', $_POST['post_id'], $_POST['message'] );

    // Redirect back to the article.
    wp_safe_redirect( get_permalink( $_POST['post_id'] ) );
    exit;

}

You can read more on how to use the admin-post.php file in the WordPress Codex.

This is all we need to create a custom, configurable notification. Let’s create the trigger.

Registering The Custom Trigger

The trigger is just a simple class that extends the abstract trigger. The abstract class does all of the work for you. It puts the trigger in the list, and it handles the notifications and merge tags.

Let’s start with the trigger declaration.

Minimal Trigger Definition

class ReportBug extends BracketSpaceNotificationAbstractsTrigger {

    public function __construct() {

        // Add slug and the title.
        parent::__construct(
            'reportabug',
            __( 'Bug report sent', 'reportabug' )
        );

        // Hook to the action.
        $this->add_action( 'report_a_bug', 10, 2 );

    }

    public function merge_tags() {}

}

All you need to do is call the parent constructor and pass the trigger slug and nice name.

Then, we can hook into our custom action. The add_action method is very similar to the add_action() function; so, the second parameter is the priority, and the last one is the number of arguments. Only the callback parameter is missing because the abstract class does that for us.

Having the class, we can register it as our new trigger.

register_trigger( new ReportBug() );

This is now a fully working trigger. You can select it from the list when composing a new notification.



(Large preview)

Although the trigger is working and we can already send the notification we want, it’s not very useful. We don’t have any way to show the recipient which post has a bug and what the message is.

This would be the time, then, to register some merge tags and set up the trigger context with the action parameters we have: the post ID and the message.

To do this, we can add another method to the trigger class. This is the action callback, where we can catch the action arguments.

Handling Action Arguments

public function action( $post_ID, $message ) {

    // If the message is empty, don't send any notifications.
    if ( empty( $message ) ) {
        return false;
    }

    // Set the trigger properties.
    $this->post    = get_post( $post_ID );
    $this->message = $message;

}

Note the return false; statement. If you return false from this method, the trigger will be stopped, and no notification will be sent. In our case, we don’t want a notification to be submitted with an empty message. In the real world, you’d want to validate that before the form is sent.

Then, we just set the trigger class’ properties, the complete post object and the message. Now, we can use them to add some merge tags to our trigger. We can just fill the content of the merge_tags method we declared earlier.

Defining Merge Tags

public function merge_tags() {

    $this->add_merge_tag( new BracketSpaceNotificationDefaultsMergeTagUrlTag( array(
        'slug'        => 'post_url',
        'name'        => __( 'Post URL', 'reportabug' ),
        'resolver'    => function( $trigger ) {
            return get_permalink( $trigger->post->ID );
        },
    ) ) );

    $this->add_merge_tag( new BracketSpaceNotificationDefaultsMergeTagStringTag( array(
        'slug'        => 'post_title',
        'name'        => __( 'Post title', 'reportabug' ),
        'resolver'    => function( $trigger ) {
            return $trigger->post->post_title;
        },
    ) ) );

    $this->add_merge_tag( new BracketSpaceNotificationDefaultsMergeTagHtmlTag( array(
        'slug'        => 'message',
        'name'        => __( 'Message', 'reportabug' ),
        'resolver'    => function( $trigger ) {
            return nl2br( $trigger->message );
        },
    ) ) );

    $this->add_merge_tag( new BracketSpaceNotificationDefaultsMergeTagEmailTag( array(
        'slug'        => 'post_author_email',
        'name'        => __( 'Post author email', 'reportabug' ),
        'resolver'    => function( $trigger ) {
            $author = get_userdata( $trigger->post->post_author );
            return $author->user_email;
        },
    ) ) );

}

This will add four merge tags, all ready to use while a notification is being composed.

The merge tag is an instance of a special class. You can see that there are many types of these tags, and we are using them depending on the value that is returned from the resolver. You can see all merge tags in the GitHub repository.

All merge tags are added via the add_merge_tag method, and they require the config array with three keys:

  • slug
    The static value that will be used in the notification (i.e. {post_url}).
  • name
    The translated label for the merge tag.
  • resolver
    The function that replaces the merge tag with the actual value.

The resolver doesn’t have to be the closure, as in our case, but using it is convenient. You can pass a function name as a string or an array if this is a method in another class.

In the resolver function, only one argument is available: the trigger class instance. Thus, we can access the properties we just set in the action method and return the value we need.

And that’s all! The merge tags are not available to use with our trigger, and we can set up as many notifications of the bug report as we want.



(Large preview)

Creating The Custom Notification Type

The Notification plugin offers not only custom triggers, but also custom notification types. The plugin ships with two types, email and webhook, but it has a simple API to register your own notifications.

It works very similarly to the custom trigger: You also need a class and a call to one simple function to register it.

I’m showing only an example; the implementation will vary according to the system you wish to integrate. You might need to include a third-party library and call its API or operate in WordPress’ file system, but the guide below will set you up with the basic process.

Let’s start with a class declaration:

class CustomNotification extends BracketSpaceNotificationAbstractsNotification {

    public function __construct() {

        // Add slug and the title.
        parent::__construct( 
            'custom_notification',
            __( 'Custom Notification', 'textdomain' )
        );

    }

    public function form_fields() {}

    public function send( BracketSpaceNotificationInterfacesTriggerable $trigger ) {}

}

In the constructor, you must call the parent’s class constructor and pass the slug and nice name of the notification.

The form_fields method is used to create a configuration form for notifications. (For example, the email notification would have a subject, body, etc.)

The send method is called by the trigger, and it’s where you can call the third-party API that you wish to integrate with.

Next, you have to register it with the register_notification function.

register_trigger( new CustomNotification() );

The Notification Form

There might be a case in which you have a notification with no configuration fields. That’s fine, but most likely you’ll want to give the WordPress administrator a way to configure the notification content with the merge tags.

That’s why we’ll register two fields, the title and the message, in the form_fields method. It looks like this:

public function form_fields() {

    $this->add_form_field( new BracketSpaceNotificationDefaultsFieldInputField( array(
        'label'       => __( 'Title', 'textdomain' ),
        'name'        => 'title',
        'resolvable'  => true,
        'description' => __( 'You can use merge tags', 'textdomain' ),
    ) ) );

    $this->add_form_field( new BracketSpaceNotificationDefaultsFieldTextareaField( array(
        'label'       => __( 'Message', 'textdomain' ),
        'name'        => 'message',
        'resolvable'  => true,
        'description' => __( 'You can use merge tags', 'textdomain' ),
    ) ) );

}

As you can see, each field is an object and is registered with the add_form_field method. For the list of all available field types, please visit the GitHub repository.

Each field has the translatable label, the unique name and a set of other properties. You can define whether the field should be resolved with the merge tags with the resolvable key. This means that when someone uses the {post_title} merge tag in this field, it will be changed with the post’s actual title. You can also provide the description field for a better user experience.

At this point, your custom notification type can be used in the plugin’s interface with any available trigger type.



(Large preview)

Sending The Custom Notification

In order to make it really work, we have to use the send method in our notification class declaration. This is the place where you can write an API call or use WordPress’ file system or any WordPress API, and do whatever you like with the notification data.

This is how you can access it:

public function send( BracketSpaceNotificationInterfacesTriggerable $trigger ) {

    $title   = $this->data['title'];
    $message = $this->data['message'];

    // @todo Write the integration here.

}

At this point, all of the fields are resolved with the merge tags, which means the variables are ready to be shipped.

That gives you endless possibilities to integrate WordPress with any service, whether it’s your local SMS provider, another WordPress installation or any external API you wish to communicate with.

White Labeling And Bundling The Plugin

It’s not ideal to create a dependency of a plugin that can be easily deactivated and uninstalled. If you are building a system that really requires the Notification plugin to be always available, you can bundle the plugin in your own code.

If you’ve used the Advanced Custom Fields plugin before, then you are probably familiar with the bundling procedure. Just copy the plugin’s files to your plugin or theme, and invoke the plugin manually.

The Notification plugin works very similarly, but invoking the plugin is much simpler than with Advanced Custom Fields.

Just copy the plugin’s files, and require one file to make it work.

require_once( 'path/to/plugin/notification/load.php' );

The plugin will figure out its location and the URLs.

But bundling the plugin might not be enough. Perhaps you need to completely hide that you are using this third-party solution. This is why the Notification plugin comes with a white-label mode, which you can activate at any time.

It also is enabled as a single call to a function:

notification_whitelabel( array(
    // Admin page hook under which the Notifications will be displayed.
    'page_hook'       => 'edit.php?post_type=page',
    // If display extensions page.
    'extensions'      => false,
    // If display settings page.
    'settings'        => false,
    // Limit settings access to user IDs.
    // This works only if settings are enabled.
    'settings_access' => array( 123, 456 ),
) );

By default, calling this function will hide all of the default triggers.

Using both techniques, white labeling and bundling, will completely hide any references to the plugin’s origin, and the solution will behave as a fully integrated part of your system.

Conclusion

The Notification plugin is an all-in-one solution for any custom WordPress notification system. It’s extremely easy to configure, and it works out of the box. All of the triggers that are registered will work with any notification type, and if you have any advanced requirements, you can save some time by using an existing extension.

If you’d like to learn more details and advanced techniques, go to the documentation website.

I’m always open to new ideas, so if you have any, you can reach out to me here in the comments, via the GitHub issues or on Twitter.

Download the plugin from the repository, and give it a try!

Smashing Editorial
(ra, yk, il)


Source: Smashing Magazine