nirjan-dev / learning-to-code Goto Github PK
View Code? Open in Web Editor NEWI'm using the issues on this repo to track my journey of becoming a better coder
I'm using the issues on this repo to track my journey of becoming a better coder
#Align the primary button to the left edge of the inputs
In his article reporting on eye-tracking research, Luke Wroblewski says the primary button should be aligned with the left-hand edge of the input:
"illuminate a clear path to completion. Aligning inputs and actions with a strong vertical axis clearly communicates how to go about completing a form."This layout also helps screen magnifier users to see it without having to pan across.
#Put the back button above the form
Joe said putting the back button at the top works well because it’s:
in a similar place to where most browsers put the browser back button
most likely to be needed soon after the user lands on a page that looks wrong or if the user wants to check what they just entered
probably not needed once the user fills out the form—if they fill out the form and click back, they will lose their answers
#Put tangentially related actions above the form
You'll often see the ‘forgot password’ link next to the password field, but this is problematic because users:
expect the tab key to focus the next field/button
might have to scroll to find the link
might waste time typing in their email address before clicking the link
Putting the link above the form solves all of these problems.
#Place extra buttons based on what they do
Luke Wroblewski's research shows the cancel button should be to the right of the primary button and styled less prominently as a link.
But putting the cancel button below the primary button has some advantages:
Firstly, it conforms to forms expert, Caroline Jarrett’s rule, make it harder to find destructive buttons.
Secondly, as explained in the back button and additional links sections, the cancel button is not directly related to the form itself so it makes sense to put it below the primary button.
Lastly, it frees up space for other, directly related buttons to be on the same row. If you put lots of buttons in a row then it’s harder for users to work out which is most important.
##2. Put the ‘add another’ button above the primary button
Putting the ‘add another’ button above the primary button has the following benefits:
users don't have to go past the primary button to select it, which conforms to Caroline Jarrett’s rule, put buttons in a sensible order
the primary button stays consistently located on the left hand side as explained earlier
like Erik Kennedy explains in The 3 Laws of Locality, it's located where it affects change—next to the field being cloned
Sometimes users might need to save their progress on a long form.
Putting the ‘save and exit’ button above the primary button implies it’s more important, when it isn’t.
Putting it below can cause an unwieldy list of stacked buttons and uses the area reserved for the cancel button.
That leaves us with putting it next to the primary button which makes sense as the action is directly related to the form.
##In some single field forms put the button next to the input
On rare occasions, you can put the button next to the input, which is often seen in global search forms in site headers.
While there’s nothing especially wrong with putting the button below the input, putting it next to it saves space and looks a bit neater.
But do not do this on standard forms that happen to have just 1 field. It’s inconsistent and unconventional.
In this special case, put the buttons above the form.
This is another example that works because of Erik Kennedy’s rule, if a control affects change across an entire area, put it above that area.
Putting the buttons above the form also leaves room below the list for things like pagination which is often needed in these types of interfaces.
Align the primary button to the left edge of the inputs
Put the back button above the form
Put tangentially related actions above the form
Place extra buttons based on what they do
In some single field forms put the button next to the input
Put buttons on multi select forms above the form
Ask as many questions as possible. Talk with users to understand them and don’t be afraid to ask even the most obvious questions. You might be surprised by the unexpected answers you’ll receive.
Ideas are great but are they answering a real problem people have?
Don’t build on assumptions. Making assumptions means believing things are a certain way with little to no evidence. You should avoid this mentality if you want to become a good UX designer.
Test and validate your ideas. Many of us have had moments when we come up with what we think is a great idea, and we’re ready to jump in and start building. Ideas are great but are they answering a real problem people have? You need to test and validate (or invalidate) ideas before you start working on them.
Design with people’s mental models in mind. People spend a lot of their time online using different websites and apps, so they expect yours to work similarly. Don’t reinvent the wheel just for the sake of it!
What people say and what they do can be totally different. User interviews are essential to the design process, but it’s also important that you do observational research like field studies to better understand user behavior.
Don’t think features, think user needs. This means starting with the specific problems and needs users have and trying to solve those with product solutions—and not the other way around.
Be open-minded, listen, empathize. It’s impossible not to have assumptions—that’s how our brain works. The important thing is to listen, be open-minded, and accept when we’re wrong.
Understand business requirements as much as users. Learning what the business goals are is as important as understanding your target users. When you have a view of the whole picture, you’ll be less prone to build what you think is required and design solutions that address actual needs.
Embrace the technical side to understand how products are built. You don’t have to be a developer to be a great designer, but you should understand how the technology works, and what its possibilities and limitations are. For instance, user experiences should be built with performance in mind, and knowing what performance is and how to optimize it will help you build great products.
Be a team player. Your team’s common goal is to build a great product. As a designer, you need to put your ego aside and know that everyone on your team works towards this goal. This requires you to collaborate with fellow designers but also with product managers, developers, and others.
The best UX designers are curious, interested in people’s behavior, and look to understand the reasons behind users’ actions.
(link to the article)[https://blog.maze.design/things-ux-designer-should-know/]
Pros of dropdowns
Cons of dorpdowns
Users sometimes miss them
“Drop-down fields consistently appear high up in our client’s Most Corrected reports too. This report tells clients which fields their users revisited in order to correct data.”
They hide available options by default
“Miller and Jarret (2001) see the advantage of radio buttons in the fact that all options are visible at once, whereas the advantage of drop-down menus lies in the saving of screen real estate.”
They slow users down
“Survey participants completed the radio button form (n = 354) an average of 2.5 seconds faster than the form with select menu buttons (n = 354). This was significantly faster at a 95% confidence level.”
dropdown alternatives
Switch / Toggle
If you have an input with two options, a switch/toggle can be used.
Radio buttons
Radio buttons can be used to replace a dropdown in most cases. The advantage of using them over dropdowns is that the options are easily scannable without forcing user interaction to view the available options.
Text fields
Text fields are the preferred input when the user knows the input from memory or are following a set format.
Common examples:
Birth date
Credit card expiration date
Custom solutions
Some designers have avoided the problems with dropdowns by opting for a custom solution.
A great example is the country dropdown for Google Flights. It’s a great example of using the compact nature of a closed menu while expanding to a multi column list that’s easily scannable.
Best practices for dropdowns
Use descriptive labels. Use a label for the dropdown and a label for the default selection. Using a label outside of the dropdown is helpful for users to remember what the field is for when correcting mistakes. Using a descriptive label inside a dropdown is helpful for users with screen readers to make sense for the dropdown.
Sort options in a predictable way. It’s tempting to sort options by how frequently they’re picked, but listing options in a unpredictable way forces users to slow down. Stick with alphabetical in most cases and make exceptions when there are other predictable patterns (like days of the week).
Use short, descriptive options. Descriptions for the available options get cut off around 35 characters, so try to stick with options with shorter names or you’ll risk getting them cut off and becoming unreadable.
An analysis was brought up by WebAIM, called WebAIM Million in February 2019. It analyzed the accessibility of the top million website’s homepages and to their shock, found out 98.7% of them didn’t meet the WCAG 2 Conformance (Web Content Accessibility Guidelines). When looking at the results, advocating for accessibility and teaching about it in software teams becomes significant.
Use native elements whenever possible. If the native element does not meet your requirements and if you have decided to create a Rich UI component, then use aria attributes to make them accessible. Using it incorrectly can affect negatively, that’s why make sure you familiarize yourself with ARIA Authoring Practices. It describes aria attributes you need to focus on building various custom UI components. Furthermore, you can use aria states rather than adding a class to style elements in the Rich UI components. This makes sure that the screen reader users can interact with the UI components and you don’t have duplicate style identifiers.
Page titles are a part of HTML’s head. So since Single Page Applications have a single HTML file, i.e., index.html, it requires only a single title tag, but its content should vary across pages. To do this easily, Angular assists you with a Title service and Meta service to update them.
Keyboard Navigation: Keyboard navigation is critical for many users who have motor disabilities. Making sure that the tab order is in a logical order will help users navigate the website using just the keyboard. When considering Keyboard Navigation, we need to be aware that TAB key is not the only key we need to support, there are other keys that you have to account for (Refer ARIA Authoring Practices). Angular CDK provides ListKeyManager to help maintain keyboard interaction for components such as menus, dropdowns, selects, list boxes, etc.
Focus Management: Knowing where the focus goes while using the application is really important for accessibility because we want users who do not use a mouse (screen reader/keyboard users) to be directed to the right place when an interaction occurs or when the route changes. To force the focus on an element, you can use the focus method of a DOM node. However, this could get quite complicated in advanced use cases. The Angular CDK provides the FocusManager and the FocusTrap services to handle them. Focus Management also comprises of trapping the focus within a particular container, especially in modal dialogs and drawers. You want the focus to enter inside such components and not leave it until it closes. Angular CDK provides FocusTrap class as well as cdkFocusTrap directive to trap the focus. You might also want to consider Skip Navigation to help users skip the navigation and go to the content directly.
Codelyzer is included in all the Angular CLI projects. Enable the accessibility rules to detect a few common accessibility issues in Angular templates. In the video below, you can see how it detects issues in the template at the development time.
Let’s imagine we have the following TypeScript interfaces:
export interface Animal {
weight: number;
}
export interface Bird extends Animal {
wings: number;
fly: Function;
}
export interface Fish extends Animal {
fins: number;
swim: Function;
}
In our code we deal with all kinds of animals so any function we declare looks more or less like so:
function observe(animal: Animal) {
// ...
}
There’s an issue though. What if we need to know specifically whether we have a Bird? While checking for animal.wings is perfectly valid JavaScript, TypeScript errors out with Property 'wings' does not exist on type 'Animal'. Which makes sense: the Animal interface only declares a weight property.
The solution, then, is to use what’s called a Union Type:
function observe(animal: Bird | Fish) {
// ...
}
A union type describes a value that can be one of several types. Here animal can either be a Bird or a Fish. And since a Bird has wings TypeScript won’t complain if we make a check for that property.
However, this has some drawbacks:
The type declaration is wider, affecting the shape of the code.
Adding a new animal classification like Reptile will potentially require changing a lot of type declarations.
What should we do then?
Well, we can hide (don’t export) the base interface and export Animal as a union of all animal classification types:
interface BaseAnimal {
weight: number;
}
export interface Bird extends BaseAnimal {
wings: number;
fly: Function;
}
export interface Fish extends BaseAnimal {
fins: number;
swim: Function;
}
export type Animal = Bird | Fish;
Now to add Reptile we would only need to update the Animal export.
As an added benefit, this technique allows TypeScript to better infer types.
Whereas before we had to do this:
let animal: BaseAnimal;
if ('wings' in animal) {
(animal as Bird).fly(); // TypeScript will throw an error unless animal is cast to Bird.
}
Now we can do this:
let animal: Animal;
if ('wings' in animal) {
animal.fly(); // TypeScript already knows animal is a Bird!
}
This is subjective and depends on what you’re trying to do. Each typeface has its own personality, so you’ll have to find one that aligns with your brand or the content it’s communicating. Typefaces have to be considered within the context that they’re being applied. Are we looking for formality? Maybe start with the serif family. Warm, fun and friendly? That might be a cue for sans-serif. Heck, there’s a time and a place even for Comic Sans… really! Just know that there is no hard science to choosing a font, and even the most trained of typographers are considering contextual cues to find the "right" one.
The only way to find out if two fonts are complementary is to see them together. Tools like FontPair and TypeConnection can help with this. If a font catches your eye when surfing the web, WhatFont is a great browser extension to help identify it. Another great resource is Typewolf which allows you to see examples of great web typography, including different font combinations in use:
If you’re building a complex web app UI, you might need a range of font weights to establish a hierarchy in different contexts. For something less complex, say a blog, you’ll likely be fine with just a couple.
Consider the legibility of a font
Some typefaces are harder to read than others. Stay away from elaborate fonts when it comes to paragraphs, and if you must have tiny text somewhere, make sure its typeface is legible at those tiny sizes.
These faux styles tend to reduce legibility and break the text’s visual cohesion because the browser can only do so much with what it’s given. Stick to fonts that offer true bold/italics styles, if you can.
Making a font bold isn’t as simple as adding an outline to the text, and italics are more than slanted letters. Good fonts have specific bold and italic styles, without which the browser tries to "fake" it:
There are many other things that can (and probably should) be taken into consideration, including:
Language support: Some fonts have glyphs for foreign characters, which may be a requirement for a project geared toward a multilingual audience or that contains multilingual content.
Ligatures: Some fonts (typically serifs) have glyphs to replace otherwise awkward characters, like ffi and ffl which are multiple characters in a single glyph.
File size: You’re a developer, so I know you care about performance. Some fonts come in bigger files than others and those can cause a site to take a hit on load times.
You can use the @font-face declaration to define your custom font:
@font-face {
font-family: 'MyWebFont';
src: url('webfont.eot'); /* IE9 Compat Modes /
src: url('webfont.eot?#iefix') format('embedded-opentype'), / IE6-IE8 /
url('webfont.woff2') format('woff2'), / Super Modern Browsers /
url('webfont.woff') format('woff'), / Pretty Modern Browsers /
url('webfont.ttf') format('truetype'), / Safari, Android, iOS /
url('webfont.svg#svgFontName') format('svg'); / Legacy iOS */
}
The right size scale depends on the use case. As a guideline, many people choose to use a modular scale. That refers to a scale based on a constant ratio for successive text elements in the hierarchy.
Example of a modular scale with a ratio of 1.4
Modular Scales is a tool by Tim Brown, who popularized this approach, which makes it easy to visualize different size scales.
Here are some general guidelines to define a good line height:
Increase line-height for thick fonts
Increase line-height when fonts are a dark color
Increase line-height for long-form content
One school of thought prescribes the use of paragraph line height as a baseline unit from which all other spacing is derived. This includes gaps between paragraphs and headings, and padding between the text and other page elements.
Under this system, you might set the line height of a heading to twice the line height of paragraphs, and leave a one-line gap between a heading and paragraph. Handily, you can use the same em units to define margins and padding, so there’s really no need to hard-code anything.
For larger text, like headings and titles, you’ll often want to reduce the space between letters. And a little bit of space goes a long way. For example, -0.02em is a tiny decimal, but a good starting point, which can be tweaked until it looks just right to your eye. The only time you should think about increasing letter spacing is when dealing with stylistically unusual text — things like all-capped titles and even some number sequences.
The length of a line of text is more important than you might think
It’s unpleasant for our eyes to move long distances while reading, so the width of lines should be deliberate. General consensus is that a good width is somewhere between 60 and 70 characters per line. If you find that a line of text (especially for long-form content) drastically falls outside this range, then start adjusting.
Use repetition for consistency and maintainability
f, for example, you are working on styles for a new paragraph, first consider if there is existing content that has a similar style and try to use the same CSS class. If not, you can create a new class with a generic name that can be repeated elsewhere in your site. Think .paragraph--emphasize as opposed to .footer__paragraph--emphasize or .heading-1 as opposed to .hero__site-title. The first examples can be used across your site as opposed to the second which are scoped to specific components. You can even add a prefix like text- to indicate that the class is used specifically for text styles. This method will reduce the CSS file size and complexity while making it much easier to update global styles in the future.
You can use Sass or CSS variables so that the values are kept consistent across the project. A system might look like this—but use whatever you’re comfortable with because naming things is hard:
$space-sm
$space-med
$space-lg
$space-xl
$space-xxl
Step 1: Know your mood
You have to know the mood or attitude of your site and brand before choosing colors. Look at your content and decide what you are trying to communicate. Is it funny, informative, retro, loud, somber? Typically, you can boil down the mood of your site to a few adjectives. For example, you might summarize The North Face as adventurous and rugged while Apple would be minimalistic and beautiful.
Step 2: Find your main color
With your mood in mind, try to visualize a color that represents it. Start with the color’s saturation (how intense the color is) and brightness (how close the color is to white or black). If your mood is upbeat or flashy, a lighter (more saturated) color is probably best. If your mood is serious or reserved, a darker (less saturated) color is better.
Next, choose a hue. Hue refers to what most people think of as colors—where does is fall on the rotation of the color wheel? The hue of a color is what gives it the most meaning. People tend to associate hues with certain ideas. For instance, red is often associated with power or danger and green relates to money or nature. It can be helpful to look at similar websites or brands to see what colors they use—although you don’t need to follow their lead. Don’t be afraid to experiment!
Color wheel showing saturation and brightness versus hue.
Step 3: Add supporting colors
Sometimes, two or three main colors are needed, but this is not necessary. Think about the colors of different brands. Some use a single color, and others have a main color and one or two that support it. Coca-Cola uses its distinct red. IKEA is mostly blue with some yellow. Tide is orange with some blue and yellow. Depending on your site’s mood, you might need a few colors. Try using a tool like Adobe Color or Coolors), both of which allow you to add a main color and then try different color relationships, like complementary or monochromatic, to quickly see if any work well.
Step 4: Expand your palette
Now that you’ve narrowed things down and found your main color(s), it’s time to expand your scope with a palette that gives your project versatility and constraint—here’s a methodology I’ve found helpful. Tints and shades are the trick here. Tints are made by mixing your main color(s) with white, and shades are made by mixing with black. You can quickly create an organized system with Sass color functions:
link to original article
I'm using the tutorial in the docs to learn about storybook. This is the first time I'm learning storybook. My aim is to design, develop and document better react components and maybe create a component library for personal use in my projects in the future.
function Grid({ as: Element, ...props }) {
return <Element className="grid" {...props} />
}
Grid.defaultProps = {
as: 'div',
};
function MyCustomDropdown(props) {
const stateReducer = (state, action) => {
if (action.type === Dropdown.actions.CLOSE) {
buttonRef.current.focus();
}
};
return (
<>
<Dropdown stateReducer={stateReducer} {...props} />
<Button ref={buttonRef}>Open</Button>
</>
}
Duolingo's Top 8 User Retention Tactics:
📬 Lifecycle Emails
Overall Experience Score:
A
🚧 Reduce Friction
🏔 Lower Difficulty
🎁 "Welcome Back Reward"
🔥 Streaks
💎 Sunk-Cost Effect
📱 Phone Notification
🙊 Notification Auto-Filtering
1. Read the docs and the FAQ 📚
2. Install, use, and follow the ESLint plugin 👨🏫
3. Don't think about Lifecycles, think about synchronizing side effects to state 🔄
4. Know that React is fast by default and do some digging before applying performance optimizations pre-maturely 🏎💨
5. Avoid testing implementation details 🔬
On the web we can use the primitives given to us by the platform (div
, span
, section
, etc) when we have no use for a system-level name and we can give names to more complex components, such as "Tooltip", "Pagination", or "DangerZone". This ability to name (and importantly, not name) components is built into component driven libraries such as React.
export default class Tooltip extends React.Component { ... }
export const Pagination = props => (...)
Especially when it comes to CSS, we run into problems quickly because classnames are the base primitive of CSS APIs. This means that that historically CSS has forced us to over-index on creating names for everything, even those objects that we shouldn't name. We've tried to solve these problems through manual approaches like Block Element Modifier (BEM), mirroring the hand-crafted approach our design tools use. The problem with approaches like BEM is that we have no way to leave objects unnamed. BEM, and approaches like it, represent a local maximum for how we use CSS. Until we get away from the requirement to name everything we will be stuck with the problems naming everything leaves us with. Consider the following real-world example from Bootstrap, a popular library that offers a CSS API.
<nav aria-label="Pagination Example">
<ul class="pagination pagination-lg">
<li class="page-item disabled">
<a class="page-link" href="#" aria-label="previous">
<span aria-hidden="true">«</span>
<span class="sr-only">Previous</span>
</a>
</li>
<li class="page-item"><a class="page-link" href="#">1</a></li>
<li class="page-item active">
<a class="page-link" href="#">2 <span class="sr-only">(current)</span></a>
</li>
<li class="page-item"><a class="page-link" href="#">3</a></li>
<li class="page-item">
<a class="page-link" href="#" aria-label="next">
<span aria-hidden="true">»</span>
<span class="sr-only">Next</span>
</a>
</li>
</ul>
</nav>
As it turns out, CSS is too low level to accurately reflect the language we use to talk about the components of our system. When given a pure CSS API we have to compose the various pieces together ourselves. There is no way to check (besides reading the documentation) that the way we've used the classes and written the markup is supported. There's also confusion caused by the fact that we name every element. Do we really need a name for page-link
? We already reference the object using page-item
and we know it's a link because we're using an anchor tag. We're also manually specifying accessibility concerns which opens up the potential to break accessibility accidentally if you don't know when to use aria labels. What does it mean for a page item to be disabled? Can the entire pagination component be disabled? How do we evolve our CSS API if we can't make guarantees about how consumers are using it?
At this point we've touched on a few of the naming issues relating to how we build and implement our language's styles. Now we'll look to what different abstractions can bring us to solve some of them. Remember that we're using specific technologies to talk about general problems so the same approach to abstracting can be useful whether your platform's styling primitives are CSS or something else.
A CSS style sheet (yes, you read that correctly. A Cascading Style Sheets style sheet) is an abstract concept that represents a style sheet as defined by the CSS specification. The CSS Object Model (CSSOM) defines a set of parsing and serialization rules for Media Queries, Selectors, and CSS itself (CSS itself is a spec composed of multiple specs). Using these built-in platform APIs we can build a higher level abstraction that allows us to solve the naming issues we've discussed previously.
Components can be though of in levels of sophistication. As you ascend higher in the chain, the component is more re-usable but also takes more effort to maintain. The list, from lowest to highest is:
Emotion is a library that introduces a new set of APIs for the creation, composition, and naming of styles on top of the platform's CSS APIs. We'll explore the usage of these APIs from a component oriented perspective from the least nameable objects to the most depended on components of our language. Although we'll be talking about emotion in the context of React, the library can be used without React and integrating with other component abstractions as well
Every product has a need for soup. Soup is a shorthand for the one off styles, that extra div you need to work around a platform issue, or glue that binds two slightly-incompatible abstractions together. Soup, perhaps more than any other level, needs to be written in a way that is easy to delete because there are two basic evolutionary paths for soup.
The option we want to enable people to choose is option 2. Option 1 is the state of much CSS (and other code) in the world. The styles written for soup are often bad offenders of being hard to understand messes of code and because soup is often the glue between abstractions, bad abstractions cause compoundingly worse soup. Some of the reasons people extend indefinitely are
All of these reasons have something in common: They have large components of fear driving behavior (often the fear of breaking production). To be successful at taking Option 2, we need to remove the fear that deleting a line of code might break something completely unrelated.
Deleting and rewriting code also allows you to come at it with fresh perspective for potential new abstractions, so we want to be able to easily take the soup and bring it up into the next level on the component hierarchy as a sub-component.
Emotion provides an API for creating soup that co-locates the styles with the rendering path and allows access to theming (more on theming later). This API is called the CSS prop and we can use it in combination with another core feature, the css tagged template literal. Let's start with a small example: Overriding the color of a paragraph to give it more emphasis on the page. We really like pink, so we'll use hotpink
to highlight this section of text.
const MyContent = () => (
<div>
<p>Some blog post where I am writing about things!</p>
<p
css={css`
color: hotpink;
`}
>
Making this paragraph stand out with color
</p>
<p>Some more content after the callout</p>
</div>
);
Notice how we've co-located one-off styles in a way that allows us to easily delete the styles with the paragraph that uses them. It's very clear that none of the other paragraphs use this styling, so we don't need to worry about tracking down other usage of it. Using the CSS Prop API, we don't need to give a name to this one paragraph that happens to have a different color but is otherwise exactly the same as every other paragraph.
Looking at a larger example, we can take the pagination code from earlier and use the CSS Prop to remove names from objects that don't need them. Our first abstraction is going to be at the page-item
level, so we'll create a PageItem
component for our markup to live in.
cont PageItem = ({ children }) => (
<li className="page-item">
<a className="page-link" href="#">{children}</a>
</li>
)
Converting this to use the CSS Prop allows us to remove the global nature of page-link
. It no longer references a classname that could exist anywhere in our codebase and thus we've removed the ability for unexpected results when we render this component.
import { css } from "@emotion/core";
const PageItem = ({ children }) => (
<li className="page-item">
<a
css={css`
position: relative;
display: block;
&:hover {
z-index: 2;
}
`}
href="#"
>
{children}
</a>
</li>
);
The default options are perhaps one of the strongest levers we have when building new products, tools, and even interacting with people day to day. Most people end up taking the default path with few opting out. Knowing that we have an incredible amount of power to determine what the majority of people will do means that we have a responsibility to make the best option, the easy option.
When applying that to CSS, that means we need to change the default from naming everything to making naming opt-in. By default, objects in our system should be unnamed. When the abstractions we use rise to the level of reusable components we should have an easy path from soup to a more structured approach with named objects. Emotion's css
tagged template literal is unnamed by default, you can not get a classname from it without additional effort (admittedly the effort is small, but once again defaults are overwhelmingly followed). Being unnamed by default means we can use the css
prop and tagged template literal to elevate to named object only that which has been reused multiple times.
Now that we've seen how to create soup let's take a look at how we can move one level up the chain and start creating sub-components. Sub-components are named components that compose together to create a larger component or system of components.
Let's say we wanted to lay out application out in a classic sidebar/content area structure like this:
We could set up our app's layout in a component using the css prop as such:
import { css } from "@emotion/core";
const AppLayout = ({ sidebar, children }) => (
<div
css={css`
max-width: 960px;
margin: auto;
display: flex;
`}
>
<aside
css={css`
flex: 1;
`}
>
{sidebar}
</aside>
<section
css={css`
flex: 3;
`}
>
{children}
</section>
</div>
);
This can be fine in the case that we have a single app and a single layout (for example, we could use this component to build a Gatsby page layout using wrapPageElement
APIs). In this case, it can still be beneficial to name the components in our layout for clarity. We could call them SidebarArea
and ContentArea
to denote that we've allocated this space to the sidebar navigation and the content respectively.
import { css } from "@emotion/core";
const SidebarArea = ({ children, ...props }) => (
<aside
css={css`
flex: 1;
`}
{...props}
>
{children}
</aside>
);
const ContentArea = ({ children, ...props }) => (
<section
css={css`
flex: 3;
`}
{...props}
>
{children}
</section>
);
const AppLayout = ({ sidebar, children }) => (
<div
css={css`
max-width: 960px;
margin: auto;
display: flex;
`}
>
<SidebarArea>{sidebar}</SidebarArea>
<ContentArea>{children}</ContentArea>
</div>
);
This approach documents that we have some infrastructure dedicated to providing an area for each section of the page. It also allows us to not name the containing div
, which would likely have an equally generic name such as Container
right now. Renaming div
as Container
may or may not yield benefits depending on how we evolve over time. We could, for example, have a MarketingSiteContainer
and an ApplicationContainer
in the future and in the interest of avoiding premature abstraction, we've chosen to not name it yet.
In our pagination example, let's say we wanted to mirror Bootstrap's API specifically because we weren't just building a pagination component, but we were trying to build a "bootstrap in React" library that stayed as true to the original as possible. We could create two components to mirror the page-item
and page-link
nomenclature.
import { css } from "@emotion/core";
const PageItem = ({ children, ...props }) => (
<li
css={css`
&:first-child {
...;
}
`}
{...props}
>
{children}
</li>
);
const PageLink = ({ children, ...props }) => (
<a
css={css`
position: relative;
display: block;
&:hover {
z-index: 2;
}
`}
{...props}
>
{children}
</a>
);
We would then use these in place of the original elements
import React from "react";
import { render } from "react-dom";
render(
<nav aria-label="Pagination Example">
<ul className="pagination pagination-lg">
<PageItem disabled>
<PageLink href="#" aria-label="previous">
<span aria-hidden="true">«</span>
<span className="sr-only">Previous</span>
</PageLink>
</PageItem>
<PageItem>
<PageLink href="#">1</PageLink>
</PageItem>
<PageItem active>
<PageLink href="#">
2 <span className="sr-only">(current)</span>
</PageLink>
</PageItem>
<PageItem>
<PageLink href="#">3</PageLink>
</PageItem>
<PageItem>
<PageLink href="#" aria-label="next">
<span aria-hidden="true">»</span>
<span className="sr-only">Next</span>
</PageLink>
</PageItem>
</ul>
</nav>,
document.getElementById("root")
);
Something interesting we've done here is that we have a few props being passed in to our PageItem
component (specifically active
and disabled
). If we go back to our component definition, we can use these props to change the way we style the component. In this example we're choosing to show a different style of cursor based on whether the pagination item is disabled or not.
const PageItem = ({ children, disabled, ...props }) => (
<li
css={css`
cursor: ${disabled ? "auto" : "pointer"};
&:first-child {
...;
}
`}
{...props}
>
{children}
</li>
);
We are now progressively evolving our system into one that we can talk about at a high level. We'll talk more about creating component APIs and composing styles later. For now just know that it's possible to access props and change how a component styles itself based on those props.
The biggest difference between public and private sub-components is where they're located relative to the components that use them. A private sub-component is a styled-component in the same file as the component that uses it. This sub-component is not exported, but is named and abstracted out of the core component. This is useful to do when components start to get more complicated but aren't ready to be fully abstracted into their own files.
For our pagination example, it's likely that PageItem
, PageLink
and Pagination
are all going to be used by the consumer, so we'd want them to be public. Imagine a directory structure that has the component name as the folder name, with each sub-component broken out into its own file.
➜ tree pagination
pagination
├── index.js
├── page-item.js
└── page-link.js
0 directories, 3 files
We can use this structure to indicate that Pagination
uses page-item
and page-link
as sub-components in the rendering tree. We've structured our filesystem in the same way the React DevTools will show them in the DOM, making them easier to find.
The index.js
file could look like this.
export { default as PageItem } from "./page-item";
export { default as PageLink } from "./page-link";
export default styled.ul`
display: flex;
`;
and be used in the same way as before:
import Pagination, { PageItem, PageLink } from "./pagination";
const MyThing = () => (
<Pagination size="large">
<PageItem disabled>
<PageLink href="#" aria-label="previous">
<span aria-hidden="true">«</span>
<span className="sr-only">Previous</span>
</PageLink>
</PageItem>
</Pagination>
);
Our Pagination
components have been in the codebase a bit now and are starting to see some usage outside of the original file we built them for. It's time to pull it out into our local components folder. A local components folder is like a staging ground for the design system. Many components won't make it to this level, and even less will make it out of the components folder into the design system.
Our sub-components are likely officially public at this point and we have to start thinking about how people are going to use the components together. If we've followed Option 2, (deleting and rewriting code), it's likely that we can simply move the pagination folder to our components folder, rename some imports and call it a day. We may also want to take this opportunity to rethink some of the easier API changes if we're noticed friction as the component gets wider use outside of the original context.
Now our Pagination
component doesn't just have wide usage in our application, but other products and sites need pagination as well. We have to move it out of our project into a more globally accessible place.
Assuming that we already have a multi-package repo set up so that we have a place to put our design system components we now start thinking harder about the way this component will impact the language (and thus, every product it touches). It's time to do a rethink of the core Pagination
API.
When building components to be reusable we can take roughly two approaches
Extensible components often come in groups that can be replaced without additional core API changes. An Icon
component, for example, can be written to take an svg as an argument, allowing a very light weight process for adding an icon that may be only useful in a single location.
import { House } from "../house.svg";
import Icon from "our-icon";
<Icon svg={House} />;
Alternatively, we could lock down the Icon
API by making it possible to only specify the name of a pre-allocated set of icons. This provides a conceptually simpler API and also means that the users of this component have to engage in a heavier weight process to introduce new icons.
import Icon from "our-icon";
<Icon name="house" />;
The tradeoffs in these approaches are in how many people you need to maintain the core libraries and in the failure modes you choose (When the user of my component finds themselves in a situation the component doesn't work for, do they override the component or do they fork the component).
With some API focus in mind let's take a look at the output of our pagination. It has looked something like this from the beginning:
<nav aria-label="Pagination Example">
<ul class="pagination pagination-lg">
<li class="page-item disabled">
<a class="page-link" href="#" aria-label="previous">
<span aria-hidden="true">«</span>
<span class="sr-only">Previous</span>
</a>
</li>
<li class="page-item"><a class="page-link" href="#">1</a></li>
<li class="page-item active">
<a class="page-link" href="#">2 <span class="sr-only">(current)</span></a>
</li>
<li class="page-item"><a class="page-link" href="#">3</a></li>
<li class="page-item">
<a class="page-link" href="#" aria-label="next">
<span aria-hidden="true">»</span>
<span class="sr-only">Next</span>
</a>
</li>
</ul>
</nav>
One of the first abstractions we can start to notice are blocks of elements behaving as a single unit, such as the<li>
always wrapping an <a>
tag while the <a>
tag can have a variety of content. The <li>
and the <a>
are acting as a single unit in this case, so let's call that unit Item
.
The <nav>
and <ul>
tags are also used as a single unit. Our pagination is always going to be considered a navigation element, so we can bundle them together too. We'll use Pagination
for this. Finally, we have the option of encapsulating the previous and next buttons as logic in our pagination component itself, which leads us to another interesting question. Do we need to expose the Item
component at all?
We'll start with the Pagination
component. It's two elements combined so we'll avoid using a styled component here and because neither of the sub-elements need to be named (because we're naming the block), we'll use the css Prop
export default ({ label, ...props }) => (
<nav aria-label={label}>
<ul
css={css`
display: flex;
list-style-type: none;
margin: 0;
padding: 0;
`}
{...props}
>
{children}
</ul>
</nav>
);
Notice that since we still have the open question of whether we need the Item
component exposed to the user or not, we use children
to pass through anything the user decides they need.
The other variation we need to account for is the size
. When looking at the bootstrap code for different sizes, there's a mixin used to define larger and smaller sizes, while the default size is left implicit.
.pagination-lg {
@include pagination-size(
$pagination-padding-y-lg,
$pagination-padding-x-lg,
$font-size-lg,
$line-height-lg,
$border-radius-lg
);
}
.pagination-sm {
@include pagination-size(
$pagination-padding-y-sm,
$pagination-padding-x-sm,
$font-size-sm,
$line-height-sm,
$border-radius-sm
);
}
The mixin is defined as a collection of other mixins and some variables.
@mixin pagination-size(
$padding-y,
$padding-x,
$font-size,
$line-height,
$border-radius
) {
.page-link {
padding: $padding-y $padding-x;
font-size: $font-size;
line-height: $line-height;
}
.page-item {
&:first-child {
.page-link {
@include border-left-radius($border-radius);
}
}
&:last-child {
.page-link {
@include border-right-radius($border-radius);
}
}
}
}
It is interesting to note that the mixin applies no styles to the Pagination
elements, but rather overrides the sub-elements page-link
and page-item
. To make use of these styles, we'll co-locate them with their respective components. This has the additional benefit that the individual sub-components contain all the logic needed to render themselves in every variation they need to be rendered in, which makes it easier to test, modify, and use.
We still need to allow users to specify the size on the Pagination
component though, since it's a global API that applies to all direct sub-components. We can modify the children
prop we're using to inject our size
prop by mapping over the children
and cloning the elements. (note: We use React.Children.map
because it has some special handling for React children types).
export default ({ label, size, ...props }) => (
<nav aria-label={label}>
<ul
css={css`
display: flex;
list-style-type: none;
margin: 0;
padding: 0;
`}
{...props}
>
{React.Children.map(children, child =>
React.cloneElement(child, { size, ...child.props })
)}
</ul>
</nav>
);
The positioning of size
that we've chosen is interesting because it establishes an order of precedence for how the size
prop will apply. In this case, it means that children can override the Pagination
component's size
prop. This is another example of choosing extensibility vs locking down APIs. Passing the prop down to children also allows us to define an explicit contract that others (or ourselves, if we don't expose the individual pieces) could implement to extend the API and create different types of pagination sub-components.
Item
is our next component. It's also a combination of two elements so we'll take the same approach and use the CSS Prop.
If we look at the CSS for page-item
, we see that it's really only applying styles conditionally to the page-link
. Since we aren't relying on a pure CSS API anymore, we don't need to use approaches that reach into other objects as often and can move the styles into the anchor itself. This co-location of the variants of a particular element means that we have fewer places to look when trying to figure out why a particular element is rendering a particular way. This means we can work faster, with more confidence, and most importantly delete without fear.
.page-item {
&:first-child {
.page-link {
margin-left: 0;
@include border-left-radius($border-radius);
}
}
&:last-child {
.page-link {
@include border-right-radius($border-radius);
}
}
&.active .page-link {
z-index: 1;
color: $pagination-active-color;
background-color: $pagination-active-bg;
border-color: $pagination-active-border-color;
}
&.disabled .page-link {
color: $pagination-disabled-color;
pointer-events: none;
// Opinionated: remove the "hand" cursor set previously for .page-link
cursor: auto;
background-color: $pagination-disabled-bg;
border-color: $pagination-disabled-border-color;
}
}
From the html output we've been looking at and the SASS code, we can consolidate the list of variations we're going to need to support.
First item
Last item
Disabled
Active
hover
focus
Notably:
`disabled` and `active` can not happen at the same time. `disabled` always wins if both are applied.
`hover` applies differently based on whether `active` or `disabled` are applied.
focus is independent of anything
There are styles that apply only to the first or last `Item`
The bootstrap classnames API is well coded in that is makes overlapping states apply consistently (.disabled
always takes precedence over .active
regardless of the classname application order). We can do better in our component API because we have richer data structures to work with, so let's focus on making illegal states unrepresentable.
We can represent the possible options for props with a pseudocode object like this:
type Props = {
is: IsVariants,
position: PosVariants
};
type IsVariants = DISABLED | ACTIVE;
type PosVariants = FIRST | MIDDLE | LAST;
focus
and hover
states could be included in the prop API for documentation reasons (to force the rendering of the Item
component in a specific state), but we'll leave them to just CSS for now.
A basic abstraction could look like this:
const Item = ({ children, is, position, href = "#" }) => (
<li>
<a href={href}>{children}</a>
</li>
);
The SASS code from Bootstrap uses a bunch of variables to define the visual aspects of the pagination component and some interaction behaviors (hover, focus, etc).
.page-link {
position: relative;
display: block;
padding: $pagination-padding-y $pagination-padding-x;
margin-left: -$pagination-border-width;
line-height: $pagination-line-height;
color: $pagination-color;
background-color: $pagination-bg;
border: $pagination-border-width solid $pagination-border-color;
&:hover {
z-index: 2;
color: $pagination-hover-color;
text-decoration: none;
background-color: $pagination-hover-bg;
border-color: $pagination-hover-border-color;
}
&:focus {
z-index: 2;
outline: $pagination-focus-outline;
box-shadow: $pagination-focus-box-shadow;
}
// Opinionated: add "hand" cursor to non-disabled .page-link elements
&:not(:disabled):not(.disabled) {
cursor: pointer;
}
}
To avoid losing this ability to define different sizes and colors, we'll take a simple pass as using theming to preserve the variables.
const Item = ({ children, is, position, href = "#" }) => (
<li>
<a
href={href}
css={({ pagination }) => css`
position: relative;
display: block;
padding: ${pagination.paddingY} ${pagination.paddingX};
margin-left: ${-pagination.borderWidth};
line-height: ${pagination.lineHeight};
color: ${pagination.color};
background-color: ${pagination.bg};
border: ${pagination.borderWidth} solid ${pagination.borderColor};
&:hover {
z-index: 2;
color: ${pagination.hoverColor};
text-decoration: none;
background-color: ${pagination.hoverBg};
border-color: ${pagination.hoverBorderColor};
}
&:focus {
z-index: 2;
outline: ${pagination.focusOutline};
box-shadow: ${pagination.focusBoxShadow};
}
// Opinionated: add "hand" cursor to non-disabled .page-link elements
&:not(:disabled):not(.disabled) {
cursor: pointer;
}
`}
>
{children}
</a>
</li>
);
The full list of theming variables from all of the SASS looks something like this. note: these have been calculated by following the trail of SASS variables through multiple layers. If we were building an entire library based on Bootstrap we could include those extra variables here as well.
const theme = {
pagination: {
activeBg: "#007bff",
activeBorderColor: "#007bff",
activeColor: "#fff",
bg: "#fff",
borderColor: "#dee2e6",
borderRadius: ".25rem",
borderRadiusLg: ".3rem",
borderRadiusSm: ".2rem",
borderWidth: TODO,
color: "#007bff ",
disabledBg: "#fff",
disabledBorderColor: "#dee2e6",
disabledColor: "#6c757d",
focusBoxShadow: "0 0 0 0.2rem ",
focusOutline: 0,
fontSize: "1rem",
fontSizeLg: "1.25rem",
fontSizeSm: "0.875rem",
hoverBg: "#e9ecef",
hoverBorderColor: "#dee2e6",
hoverColor: "rgba(0,123,255, 0.25)",
lineHeight: "1.25",
lineHeightLg: "1.5",
lineHeightSm: "1.5",
paddingX: ".75rem",
paddingXLg: "1.5rem",
paddingXSm: ".5rem",
paddingY: "5rem",
paddingYLg: ".75rem",
paddingYSm: ".25rem"
}
};
From here on out we can take our Pagination
components and continue to generalize them until we get to a tested, documented, re-usable component. We can use abstractions that return different styles for sizes, position in a list, and more:
const positionStyles = ({ position }) => {
switch (position) {
case "first":
return css``;
case "last":
return css``;
default:
return css``;
}
};
const isStyles = ({ is }) => {
switch (is) {
case "disabled":
return css``;
case "active":
return css``;
default:
return css``;
}
};
The point of this post is to rethink the defaults that we use to construct UI. We should not name everything by default, choosing only to give names when there is meaning to communicate. I also want to introduce you to the idea that the css
prop allows us to execute on all of the levels of our design system components, from scratchy one-offs up to fully re-usable components that are part of a public library. We can even give names to styles that matter with the css template literal or by using composable objects.
1 Directing Focus
Performance Tip #1: Inline Critical CSS
async load of css with this line
Performance Tip #4: Compress Text-based Assets
As a general rule, always compress your assets, using something like gzip. But don’t use compression on your images and other binary files — you should optimize these in advance at source. Compression could actually end up making them bigger.
And, if you can, avoid compressing files that are 1500 bytes or smaller. The smallest TCP packet size is 1500 bytes, so by compressing to, say, 800 bytes, you save nothing, as it’s still transmitted in the same byte packet. Again, the cost is negligible, but wastes some compression CPU time on the server and decompression CPU time on the client.
Performance Tip #5: Give Resource Hints To The Browser
The browser will guess at what the highest priority assets are, but you can provide a resource hint using the tag, instructing the browser to download the asset as soon as possible. It’s a good idea to preload fonts, logos and anything else that appears above the fold.
Performance Tip #6: Set cache-control Headers On All Cacheable Assets
Performance Tip #7: Lazy-load Your Images
Performance Tip #8: Use The Right Format For Your Images
Performance Tip #9: Avoid Writing Blocking Script Calls
Performance Tip #10: Don’t Make Assumptions About Data Connections
Performance Tip #11: Use Web Workers For Your JavaScrip
Performance Tip #12: Use Service Workers To Provide Offline Support
Performance Tip #13: Don’t Preload Video
Performance Tip #14: Optimize For First Page Load
Make sure people come Back
When building a freemium model, make sure to keep retention high by giving real value in your free version.1
The goal is to play the long game and keep users happy.
💎Pro Tip: Treat your freemium as an acquisition tool, not a revenue tool.
Miller's Law
The average person can only keep 7 (plus or minus 2) items in their working memory.1
Luckily, one week has 7 days, meaning people are more likely to remember their gym sessions from a week before.
This makes the weekly stats really motivating; people can suddenly keep track of their effort easily.
Investment Loop
When users input something in the product in anticipation of a future benefit, it increases perceived value and loads the next trigger.1
Having personal goals that remind you to exercise is the logical step for Strava to make the switch from being a feed of activities to a lifestyle companion.
Find what motivates people to Convert to Premium
"FREE" will get you a lot of people knocking at your door. Make sure your free product matches the intent of your premium offer to maximize conversion.1
Strava has constant growth due to its freemium acquisition model, however, it seems they're still trying to identify what people are willing to pay for.2
💎Pro Tip: Find out why current premium members pay for your service and reverse engineer their desires!
The Power of Micro Trials
When you ask a user to switch from freemium to premium, make it easy to experience the value he/she will be getting if they pay. Like giving a free trial except for a specific feature.
In this case, I can only do a wild guess of what this feature can give me, since it looks exactly like the section below.
Banner Blindness
Once you see a CTA that doesn't resonate with you, the subconscious brain starts to filter the information.1
💎Pro Tip: Make sure you keep things dynamic by trying new benefits in your CTAs. You never know what could be the main motivator!
#Skeuomorphism
Skeuomorphism is where a digital object mimics its real world counterpart1 to facilitate transition to new technology.
Charging a car is still a new concept for most of the population. Displaying a 3D car & a battery widget similar to those in smartphones makes this technology more approachable (familiarity bias)
#Emphasize Value Over Cost
Instead of focusing on what your customers are spending, show them what the value they are earning.1
In the current context, bringing more focus to the benefits (gasoline savings, environmental advantages) would help people associate charging with an even more positive experience.
This "savings" marketing strategy is ubiquitous on Tesla's website2, hence why not seeing it here is surprising.
#The Power of Storytelling
Sharing a story, a vision and a mission is one of the most powerful ways to turn your customers into ambassadors.1
This interface could also be used to remind customers of Tesla's mission2:
"Accelerate the world’s transition to sustainable energy."
This brand message is very present on Tesla's Real-Time Carbon Impact Map3.
#Prompts & Triggers
Small nudges placed on our regular paths remind us to take action.
According to the Fogg Behavioral Model (B=MAP)1, someone might be extremely motivated to talk about your brand (e.g. a Tesla fan), but without a prompt, few will take action.1
#Peak-End Rule
We judge our experiences almost entirely by their peaks (pleasant and unpleasant) and how they ended.1
By highlighting the range gains & gasoline savings, Tesla could associate a positive emotion to the end of the charging experience.
This would help the "Cue → Routine → Reward" loop2, making the charging habit more rewarding and more likely to be talked about.
link to the article
Before the browser can request a resource from a server, it must establish a connection. Establishing a secure connection involves three steps:
Look up the domain name and resolve it to an IP address.
Set up a connection to the server.
Encrypt the connection for security.
Taking care of all that ahead of time makes applications feel much faster. This post explains how to achieve that with two resource hints: and .
Taking care of all that ahead of time makes applications feel much faster. This post explains how to achieve that with two resource hints: and .
Preconnecting is only effective for domains other than the origin domain, so you shouldn't use it for your site. Caution: Only preconnect to critical domains you will use soon because the browser closes any connection that isn't used within 10 seconds. Unnecessary preconnecting can delay other important resources, so limit the number of preconnected domains and test the impact preconnecting makes.Some types of resources, such as fonts, are loaded in anonymous mode. For those you must set the crossorigin attribute with the preconnect hint:
If you omit the crossorigin attribute, the browser only performs the DNS lookup.
If a page needs to make connections to many third-party domains, preconnecting all of them is counterproductive. The preconnect hint is best used for only the most critical connections. For all the rest, use to save time on the first step, the DNS lookup, which usually takes around 20–120 ms.
DNS resolution is initiated similarly to preconnect: by adding a tag to the of the document.
Browser support for dns-prefetch is slightly different from preconnect support, so dns-prefetch can serve as a fallback for browsers that don't support preconnect.
Recommended
— To safely implement the fallback technique use separate link tags.
Not recommended
— Implementing dns-prefetch fallback in the same tag causes a bug in Safari where preconnect gets cancelled.
link to the article
describe: ‘classname’
↪ constant values used in the test
↪ mock declarations
↪ classUnderTest declaration
↪ beforeEach to setup the test
↪ describe: ‘methodname’:
↪ it ‘should return this when input is so and so’
↪ it ‘should return this value if input is something else’
↪ describe: ‘other method name’:
↪ it‘should return this when input is so and so’
↪ it‘should return this value if input is something else’
Every unit test has 3 parts. First part is the setup, where you set certain values and mock the necessary method calls. Second part is the actual call of the method you are testing. The last part is the verification, where you will check if the output is correct or the right methods have been called. For readability, leave a blank line between each part, so you can clearly see from a glance what your setup, call and verification part is.
You start with your empty test:
it('should only return the cars where the color is black', () => {
});
Then you write the method call you want to test:
it('should only return the cars where the color is black', () => {
const actual = componentUnderTest.getAllBlackCars();
});
Then you write your verification part:
it('should only return the cars where the color is black', () => {
const actual = componentUnderTest.getAllBlackCars();
expect(actual).toEqual([aBlackCadillac]);
verify(carService.getCars()).once();
});
After this, you run your test once. It should fail. Then you write the setup you need to get to the expected output:
it('should only return the cars where the color is black', () => {
when(carService.getCars()).thenReturn([aBrownCar, aRedFerrari, aRedPorsche, aBlackCadillac]);
const actual = componentUnderTest.getAllBlackCars();
expect(actual).toEqual([aBlackCadillac]);
verify(carService.getCars()).once();
});
This is a good way to work when you write unit tests. Writing the verification part before your setup, allows you to see that your test is actually able to fail.
I looked at different solutions, and the one that gave me the least problems, considering all the issues with dates (including DST), was this: starting from the starting date, we add one day until the date represents a date after the end date.
Here’s the code:
const numberOfNightsBetweenDates = (startDate, endDate) => {
const start = new Date(startDate) //clone
const end = new Date(endDate) //clone
let dayCount = 0
while (end > start) {
dayCount++
start.setDate(start.getDate() + 1)
}
return dayCount
}
I first clone the dates we are given, because dates are objects, and we get a reference to that object. This means that using setDate() in the function would also affect the variable outside of this function - not something we look forward to!
If instead you want to get the number of days between 2 dates (say, today to tomorrow is 2 days), just change while (end > start) to while (end >= start). That would work. Or increase the dayCount starting point to 1.
There’s a task that should be simple, but sometimes it leads to hours of research on the Web: uploading files to the server.
In this tutorial I explain you how to do so using fetch.
Given a form with a file input field:
We attach a change event handler on it:
document.querySelector('#fileUpload').addEventListener('change', event => {
handleImageUpload(event)
})
and we manage the bulk of our logic in the handleImageUpload() function:
const handleImageUpload = event => {
const files = event.target.files
const formData = new FormData()
formData.append('myFile', files[0])
fetch('/saveImage', {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(data => {
console.log(data)
})
.catch(error => {
console.error(error)
})
}
In this example we POST to the /saveImage endpoint.
We initialize a new FormData object and we assign it to the formData variable, and we append there the uploaded file. If we have more than one file input element, we’d have more than one append() call.
The data variable inside the second then() will contain the JSON parsed return data. I’m assuming your server will give you JSON as a response.
In how to upload a file using Fetch I explained how to upload a file to a server using Fetch.
In this post I’m going to show you part 2: how to use Node.js, and in particular Express, to handle uploaded files.
Install the express-fileupload npm module:
npm install express-fileupload
and add it to your middleware:
import fileupload from 'express-fileupload'
//or
const fileupload = require('express-fileupload')
After you created your Express app, add:
app.use(
fileupload(),
//...
This is needed because otherwise the server can’t parse file uploads.
Now uploaded files are provided in req.files. If you forget to add that middleware, req.files would be undefined.
app.post('/saveImage', (req, res) => {
const image = req.files.myFile
const path = __dirname + '/images/' + image.name
image.mv(path, (error) => {
if (error) {
console.error(error)
res.writeHead(500, {
'Content-Type': 'application/json'
})
res.end(JSON.stringify({ status: 'error', message: error }))
return
}
res.writeHead(200, {
'Content-Type': 'application/json'
})
res.end(JSON.stringify({ status: 'success', path: '/images/' + image.name }))
})
})
This is the smallest amount of code needed to handle files.
We call the mv property of the uploaded image. That is provided to us by the express-fileupload module. We move it to path and then we communicate the success (or an error!) back to the client.
Given a Date object:
const date = new Date('July 22, 2018 07:22:13')
there are lots of methods that will generate a string representing that date.
There are a few built-in ones. I list them all, along with a comment that shows a sample output:
date.toString()
// "Sun Jul 22 2018 07:22:13 GMT+0200 (Central European Summer Time)"
date.toTimeString() //"07:22:13 GMT+0200 (Central European Summer Time)"
date.toUTCString() //"Sun, 22 Jul 2018 05:22:13 GMT"
date.toDateString() //"Sun Jul 22 2018"
date.toISOString() //"2018-07-22T05:22:13.000Z" (ISO 8601 format)
date.toLocaleString() //"22/07/2018, 07:22:13"
date.toLocaleTimeString() //"07:22:13"
You are not limited to those, of course - you can use more low level methods to get a value out of a date, and construct any kind of result you want:
date.getDate() //22
date.getDay() //0 (0 means sunday, 1 means monday..)
date.getFullYear() //2018
date.getMonth() //6 (starts from 0)
date.getHours() //7
date.getMinutes() //22
date.getSeconds() //13
date.getMilliseconds() //0 (not specified)
date.getTime() //1532236933000
date.getTimezoneOffset() //-120 (will vary depending on where you are and when you check - this is CET during the summer). Returns the timezone difference expressed in minutes
Those all depend on the current timezone of the computer. There are equivalent UTC versions of these methods, that return the UTC value rather than the values adapted to your current timezone:
date.getUTCDate() //22
date.getUTCDay() //0 (0 means sunday, 1 means monday..)
date.getUTCFullYear() //2018
date.getUTCMonth() //6 (starts from 0)
date.getUTCHours() //5 (not 7 like above)
date.getUTCMinutes() //22
date.getUTCSeconds() //13
date.getUTCMilliseconds() //0 (not specified)
You can call Object.entries() to generate an array with all its enumerable properties, and loop through that, using any of the above methods:
Object.entries(items).map(item => {
console.log(item)
})
Object.entries(items).forEach(item => {
console.log(item)
})
for (const item of Object.entries(items)) {
console.log(item)
}
Consider including the type of work in the title so we can easily see your specialities at a glance.
For example, instead of “Nike Air Max” say “Nike Air Max ecommerce experience.”
Set the scene for your reader with a quick sentence explaining what this project is all about.
What’s the product? What was the briefing? What challenge did you set out to solve? Did you have a certain idea or expectation for the project when you began?
In a paragraph or two, explain how you worked through the project. Why did you choose that approach? Did you take a unique angle or notice some surprising insight?
Ask yourself “why” a thousand times, and then answer those questions.
What happened? Did you feel proud of the result? Did it increase the client’s sales by 2000%? A case study should ideally be a success story. If it's not, tell us why it's meaningful and what you learned from it.
This could be as simple as listing “art direction & design” beside the project summary or listing the full team like a film.
If someone scrolls through your case study and only reads the headlines and captions, they should still understand your project.
link to full article
link to case study
Then remember these tips (see this twitter thread for related visuals and comments):
1—👤INDIVIDUALS > SEGMENTS: Find ways to detect individual preferences and anticipate needs. (e.g.: 90% of Shaquille O'Neal's last 100 Airbnb bookings had King Size Beds—let's suggest him a default filter so that he can sleep comfortably!)
2—📊SEGMENT > AVERAGES: Relying on "averages" in data can be risky. Use customer segments & cohorts to analyze behavior whenever possible. (e.g.: "Work Trip" filters may be good for "Corporate Travelers" who deduct travel expenses, but not for frugal "Digital Nomads").
3—💎CONTEXT > DATA: Data tells you "what". Context tells you "why". e.g.: Both "a couple" & "two business partners" would use "Number of People: 2" in their filters… but their expectations are very different for the results 😉 (2 beds vs 1 bed).
4—⏰TIMING: Customization at the right moment can work wonders to nudge users in the right direction (e.g.: if I use "today" as a booking "from" date, Airbnb nudges me to use the "Instant Book" filter. Brilliant.)
5—🎩PERSISTENCE : Ask for high-inertia preferences once and persist them. Avoid pushing your users through a "Filter Fest" every time they use your product.
💡 INSIGHTS
Here are all of the marketing, psychological & UX insights covered in the case study:
link to insights
A useful cheat sheet for working with react and typescript link to cheatsheet
Stories = Retention
When building a product or a game, users need to understand the purpose of what they do.
Games have become really good at immersing players into a story, to increase retention and engagement.1
However, here, it's hard to understand what the user fights against
Scarcity
Humans place a higher value on a scarce object and a lower value on those that are in abundance.1
Here, the scarce comes from the limited time "Tour" and the shop with its daily selections.
It is clearly used to add value to items that don't necessarily have any.
Loot Boxes: Engineered Addiction
Loot boxes are virtual rewards that you can pay for to have a chance at getting a virtual item.
They are now widely spread because they generate more than 25% of all gaming revenues by creating gambling addictions.1
The government is starting to pay attention and recommends to control them under gambling laws.
Shaping
Shaping is the process of reinforcing behaviors that are closer to the target behavior.1
In this case, this is a very scammy approach to encourage gambling behaviors, by making users familiar with the action of "firing the pipe" during the onboarding.
Dark Pattern - hiding the real value of money
Game designers have found ways to create complex game economics to dissociate money from the game with multiple game currencies.1 One of the drawbacks is users getting confused and spending large sums of money.
In this case, how much is a coin? After a few approximations 1000 coins = ~8$
Why not make it clear in the game?
Faking Real players?
Why lie and do as if players were real?1
Some might say, it's to prepare their multiplayer launch. But why "prepare" it. Why not be clear that you're racing against bots. It'll make that multiplayer launch that much more exciting!
As of now, it serves an arguable purpose of increasing your desire to gamble to get those items.
Providing Exit Points
Usually, inviting users to leave your platform after a particular milestone is a very healthy practice. It can even potentially increase retention.1
However, here, it doesn't gracefully invite you to come back tomorrow, it just stops rewarding you for your efforts.
Here are 5 things to avoid when building your revenue model:
⓵ 😈 FAKING SCARCITY: Scarcity is a powerful nudge to help people make decisions when it’s real. If you fake or add scarce for no good reason, then people will react the opposite way.
⓶ 🎲 LOTTERY: When building game or product dynamics, make sure fundamental progress doesn’t rely on a lottery. This is pure gambling and will discourage the majority of users. Plus, it'll be illegal soon...
⓷ 🛑 LIMITING DAILY REWARDS: Encouraging people to come back another day after reaching a milestone is a great way to increase retention. However, don’t stop potential progress if people want to continue.
⓸ 🤝 FAKING INTERACTIONS: Everything that’s fake is just bad… Especially when you fake human beings.
⓹ 🧠 SHAPING DARK BEHAVIORS: Slowly rewarding users for behaviors that are closer and closer to the target behaviors is a great way to build engagement, except when it’s used to create an addiction for gambling purposes.
link to the article
Use relative units — not absolute — for fonts
It’s generally considered best practice for accessibility to set your base font-size to 100% on the element, and use relative units to handle all other font-size rules in your CSS.
If you set your root font size to 100% or 16px, you can be certain that any other fonts using rem units will be relative to that 16px.
Approach #1: Responsive typography
html {
font-size: 100%;
}
h1 {
font-size: 2.25rem; // 36px
}
@media (min-width: 700px){
h1 {
font-size: 3.5rem; // 56px
}
}
@media (min-width: 1000px){
h1 {
font-size: 4.75rem; // 76px
}
}
Potential issues
One problem that comes with using media queries for your CSS styles is that it’s not completely future-proof.
While the majority of current phones are at most 414px wide and tablets are at most 800px wide, we can safely assume that those device widths will increase as technology advances.
So our breakpoints will probably need to be updated at some point. (This isn’t so much of a practical issue as it is one of principles.)
Approach #2: Fluid typography
h1 {
font-size: calc(1.3rem + 3.6vw);
}
Here’s an example of limiting the font to be no less than 36px and no greater than 76px:
h1 {
font-size: 2.25rem; // 36px
}
@media (min-width: 414px){
h1 {
font-size: calc(1.3rem + 3.6vw); // fluidly scale
}
}
@media (min-width: 1440px){
h1 {
font-size: 4.75rem; // 76px
}
}
Approach #3: Fluid typography with “locks”
h1 {
font-size: calc(2.25rem + (((100vw - 20rem) / (90 - 20))) * (4.75 - 2.25));
}
@mixin calc-font-size($min-vw, $max-vw, $min-font-size, $max-font-size){
font-size: calc(#{$min-font-size}rem + ((100vw - #{$min-vw}rem) / (#{$max-vw} - #{$min-vw})) * (#{$max-font-size} - #{$min-font-size}));
}
h1 {
font-size: 2.25rem;
}
@media (min-width: 414px){
h1 {
@include calc-font-size(25.875, 90, 2.25, 4.75);
}
@media (min-width: 1440px){
h1 {
font-size: 4.75rem;
}
}
We start with a design pattern called a singleton. It is one of the most well-known patterns, and therefore it is a good starting point. In its core, it restricts a class to have just one instance and ensures that it is globally accessible. It might come in handy when you need to manage something from across your whole application.
By design, singletons create an instance of a class if it does not yet exist. Otherwise, they return the reference to an existing instance.
class Singleton {
static instance;
constructor() {
// your logic here
}
static getInstance() {
if (Singleton.instance) {
return Singleton.instance;
}
Singleton.instance = new Singleton();
return Singleton.instance;
}
}
The code above looks fine at first glance, but it has some issues. Nothing restricts us from calling the Singleton constructor directly. This is when the TypeScript might come in handy.
class Singleton {
private static instance?: Singleton;
private constructor() {
// your logic here
}
static getInstance() {
if (Singleton.instance) {
return Singleton.instance;
}
Singleton.instance = new Singleton();
return Singleton.instance;
}
}
By making the Singleton constructor private, we can only call it from within the getInstance function.
Another approach that we can take is to return an instance straight from within the constructor.
class Singleton {
static instance;
constructor() {
if (Singleton.instance) {
return Singleton.instance;
}
Singleton.instance = this;
// your logic here
}
}
new Singleton() === new Singleton() // true
The above makes it a bit less transparent because someone might not be aware that the constructor returns the same object every time.
Singletons have a lot in common with global variables. This is also why they often are discouraged because they share their disadvantages as it might make your application less readable. Whether we consider singletons good or bad, they are one of the fundamental design patterns. Understanding them might one day come in handy. Even if you don’t decide to write them yourself, you might encounter them in some applications.
Technically speaking, object literals in JavaScript are singletons. Think about it! An object occupies a unique memory location and once it is created, there can be no other object like this. Every time we call the object user, we are essentially returning a reference to this object. Hence, a singleton!
var user = {
name: 'Shruti Kapoor',
location: 'SF',
intro: function() {
return this.name + ', ' + this.location
}}
However, do not confuse Singleton design pattern with single instances. The Singleton design pattern is a specific type of single instance with the following characteristics:
When should I use singleton?
Singletons should be used when
Here is an excellent example of singleton as an IIFE from Rob Dodson’s article:
var User;
(function() {
var instance;User = function User() {
if (instance) {
return instance;
}instance = this;// all the functionality
this.firstName = 'John';
this.lastName = 'Doe';return instance;
};
}());
Beware, singletons are considered an anti-pattern and make unit-testing extremely difficult.
One reason that tends to come up over and over again on the internets is that of a "logging" class (which you mentioned). In this case, a Singleton can be used instead of a single instance of a class because a logging class usually needs to be used over and over again ad nauseam by every class in a project. If every class uses this logging class, dependency injection becomes cumbersome.
Logging is a specific example of an "acceptable" Singleton because it doesn't affect the execution of your code. Disable logging, code execution remains the same. Enable it, same same. Misko puts it in the following way in Root Cause of Singletons, "The information here flows one way: From your application into the logger. Even though loggers are global state, since no information flows from loggers into your application, loggers are acceptable."
I'm sure there are other valid reasons as well. Alex Miller, in "Patterns I Hate", talks of service locators and client side UI's also being possibly "acceptable" choices.
A Singleton candidate must satisfy three requirements:
controls concurrent access to a shared resource.
access to the resource will be requested from multiple, disparate parts of the system.
there can be only one object.
If your proposed Singleton has only one or two of these requirements, a redesign is almost always the correct option.
For example, a printer spooler is unlikely to be called from more than one place (the Print menu), so you can use mutexes to solve the concurrent access problem.
A simple logger is the most obvious example of a possibly-valid Singleton, but this can change with more complex logging schemes.
Another typical pattern found in JavaScript applications is the module design pattern. Separating the code of our app into modules plays a significant role in keeping our codebase neatly organized.
Some time ago, a popular approach would be to enclose a piece of code in an Immediately Invoked Function Expression (IIFE). This is because all JavaScript files share the same scope.
We can also export the hello function by returning something from our Immediately Invoked Function Expression.
in hello.js
const helloModule = (function(){
function hello() {
console.log('Hello world!');
}
return {
hello
}
})();
in main.js
helloModule.hello(); // Hello world!
As the JavaScript language evolved, we found other ways to deal with the above issue. One of them are ES6 modules, where every module has its file. Modern browsers already support them. You can also use them with Webpack.
The nullish coalescing operator is another upcoming ECMAScript feature that goes hand-in-hand with optional chaining, and which our team has been involved with championing in TC39.
You can think of this feature - the ?? operator - as a way to “fall back” to a default value when dealing with null or undefined. When we write code like
let x = foo ?? bar();
this is a new way to say that the value foo will be used when it’s “present”; but when it’s null or undefined, calculate bar() in its place.
Again, the above code is equivalent to the following.
let x = (foo !== null && foo !== undefined) ?
foo :
bar();
The ?? operator can replace uses of || when trying to use a default value. For example, the following code snippet tries to fetch the volume that was last saved in localStorage (if it ever was); however, it has a bug because it uses ||.
function initializeAudio() {
let volume = localStorage.volume || 0.5
// ...
}
At its core, optional chaining lets us write code where TypeScript can immediately stop running some expressions if we run into a null or undefined. The star of the show in optional chaining is the new ?. operator for optional property accesses. When we write code like
let x = foo?.bar.baz();
this is a way of saying that when foo is defined, foo.bar.baz() will be computed; but when foo is null or undefined, stop what we’re doing and just return undefined.”
More plainly, that code snippet is the same as writing the following.
let x = (foo === null || foo === undefined) ?
undefined :
foo.bar.baz();
Note that if bar is null or undefined, our code will still hit an error accessing baz. Likewise, if baz is null or undefined, we’ll hit an error at the call site. ?. only checks for whether the value on the left of it is null or undefined - not any of the subsequent properties.
You might find yourself using ?. to replace a lot of code that performs repetitive nullish checks using the && operator.
// Before
if (foo && foo.bar && foo.bar.baz) {
// ...
}
// After-ish
if (foo?.bar?.baz) {
// ...
}
Keep in mind that ?. acts differently than those && operations since && will act specially on “falsy” values (e.g. the empty string, 0, NaN, and, well, false), but this is an intentional feature of the construct. It doesn’t short-circuit on valid data like 0 or empty strings.
Optional chaining also includes two other operations. First there’s the optional element access which acts similarly to optional property accesses, but allows us to access non-identifier properties (e.g. arbitrary strings, numbers, and symbols):
/**
* Get the first element of the array if we have an array.
* Otherwise return undefined.
*/
function tryGetFirstElement<T>(arr?: T[]) {
return arr?.[0];
// equivalent to
// return (arr === null || arr === undefined) ?
// undefined :
// arr[0];
}
There’s also optional call, which allows us to conditionally call expressions if they’re not null or undefined.
async function makeRequest(url: string, log?: (msg: string) => void) {
log?.(`Request started at ${new Date().toISOString()}`);
// roughly equivalent to
// if (log != null) {
// log(`Request started at ${new Date().toISOString()}`);
// }
const result = (await fetch(url)).json();
log?.(`Request finished at at ${new Date().toISOString()}`);
return result;
}
The “short-circuiting” behavior that optional chains have is limited property accesses, calls, element accesses - it doesn’t expand any further out from these expressions. In other words,
let result = foo?.bar / someComputation()
doesn’t stop the division or someComputation() call from occurring. It’s equivalent to
let temp = (foo === null || foo === undefined) ?
undefined :
foo.bar;
let result = temp / someComputation();
That might result in dividing undefined, which is why in strictNullChecks, the following is an error.
function barPercentage(foo?: { bar: number }) {
return foo?.bar / 100;
// ~~~~~~~~
// Error: Object is possibly undefined.
}
When localStorage.volume is set to 0, the page will set the volume to 0.5 which is unintended. ?? avoids some unintended behavior from 0, NaN and "" being treated as falsy values.
You can optimize react re-renders by lifting an expensive component up to a parent and passing it as a prop.
link to the article
The goal of the website and its content
The goal here is to present my new course and myself as a credible and capable person to teach about the topic. The website is not about reading long articles, it needs to get a visitor’s attention quickly.
Body text or headings
Are we choosing a font for body text or for headings? Since the goal is to captivate visitors, I wanted to focus on choosing the right font for the headings.
The text
Reading the text we’re designing for is fundamental—how can you choose a font for something you have no idea what it’s about? Reading samples of text should be right at the start of the process. Don’t use Lorem Ipsum, if you can’t get samples of content, try to find a similar website and “borrow” its content until you do get them.
For UX Buddy, I already had the title, the value proposition and the name. So I was good to go.
I recently received an email from a designer called Jared. He went through the typography lessons that I offer as part of the free course and he was very grateful for them. He said he learned a lot but also had one important question: how do I go about choosing a font for my project?
He told me that he works for a design agency and choosing fonts for websites they work on is something he does all the time. It’s also something that frustrates him a lot since he never studied design and typography, so he mostly does it by his “gut feeling”. That, or he copies a font from a website he likes. He’s aware of his limitations when it comes to making original font choices and his frustration grows every day. And he’s not alone.
Since I launched my free web typography course in 2017, I tend to receive a couple of similar emails every month. I wrote about choosing fonts more in details in my web typography book and I also explained my process with a concrete example. But not everyone buys the book so I decided to explain my process again, with another example—a new personal project I recently started working on.
UX Buddy
UX Buddy will be an online course for UX and product designers that are looking to take the next steps in their careers. I had to switch a lot of jobs before I found the one that aligns with what I want to do and what I get to do. So I want to share my experience and help guide designers with less experience towards finding a better UX job.
I work for GitLab where we assign a UX buddy to newly-joined designers to help them get started (the UX buddy is simply another designer on the team tasked to help out the new designer). The first few months are overwhelming for new designers so UX buddies help out by guiding them, explaining how stuff works and encouraging them to do certain things. With this course, I want to do the same for the designers that got stuck in their UX career. But instead of being their buddy only after they join a company, I want to help out with everything that comes before that—finding good UX companies, writing case studies, interviewing, support etc.
The finished website for UX Buddy
The finished website for UX Buddy (Source)
So this is where the idea for UX Buddy came from and what its goals are. These are really good starting points for defining the branding. Because the course revolves around my experience as a job seeker, and also as an interviewer in the hiring process, I wanted the branding to reflect my personality. I wanted it to be minimalistic but not sterile. Warm but not unprofessional. To the point instead of beating around the bush. I then did a two-day design sprint to come up with the value proposition of the course and some content for the website. The goal was to present the course on a single web page. What I came up with was:
Get a better UX job
for the title of the page, and:
This course is not just about creating your UX portfolio, it’s about you getting an awesome UX job where you’ll do the best work of your life.
for the value proposition. This was great and more than enough to start working on the website and, with it, choosing the font.
Choosing the font
Ok, now how do we go about actually choosing a font for our project? In my web typography book, I recommend seven things to consider, the key three are the following:
The goal of the website and its content
The goal here is to present my new course and myself as a credible and capable person to teach about the topic. The website is not about reading long articles, it needs to get a visitor’s attention quickly.
Body text or headings
Are we choosing a font for body text or for headings? Since the goal is to captivate visitors, I wanted to focus on choosing the right font for the headings.
The text
Reading the text we’re designing for is fundamental—how can you choose a font for something you have no idea what it’s about? Reading samples of text should be right at the start of the process. Don’t use Lorem Ipsum, if you can’t get samples of content, try to find a similar website and “borrow” its content until you do get them.
For UX Buddy, I already had the title, the value proposition and the name. So I was good to go.
What I was looking for
A Guide to Recognising Font Styles
Related | ~ 8 min read
A Guide to Recognising Font Styles
At this point, I had some text to work with, I knew what its goal was and I also knew I was focusing on finding the perfect font for headings. That, combined with the branding that reflects my personality, led me towards using a geometric sans serif font, my favourite style. If you don’t know the basic font styles and how to tell them apart, check out my guide to recognising font styles. It’s the very first step in making original font choices.
I first looked through the fonts of this style on Google Fonts and Adobe Fonts but couldn’t find a match. For example, I knew I wanted the font to have single-storey “a” and “g” lowercase letter styles.
I just love the simplicity of them and I thought it aligns well with the minimalistic and simplistic branding I was after. I couldn’t find any fonts like that on those font providers so I went on to look on myfonts.com. I found a couple of great matches there: Larsseit, Nexa, Axiforma, Mont, Sofia Pro and Gilroy. I also knew I wanted to use the bold weight as the primary style, to help evoke that feeling of friendliness. Here they are, already set in the primary colour I was planning to use.
Let’s recall the guidelines I established for the branding of the project:
Minimalistic but not sterile
Warm but not unprofessional
To the point instead of beating around the bush These were great for the next step of narrowing down and making a choice.
This concludes this practical example of choosing a font. I established branding guidelines from the start and came up with the content I could work with. From there on, I went looking for suitable fonts and kept narrowing down until I was left with the best option. With this approach, I get a font choice that’s well-aligned with the goal of the project and its branding but also works well with its content. It’s much better than using a font that just looks good on a website I stumbled upon.
link to the article
Myth 1: The WCAG requirements are always optimal.
he WCAG contrast ratios don’t always account for the high luminance contrast of white text. White is pure luminance with no hue or saturation, which is the strongest form for contrast. Therefore, it makes sense why the button with white text is easier to read (source).
The reason the contrast ratios failed with the white text is that it has high luminance and is on a background with high luminance. Light text on a light background is rendered low contrast computationally. Your design is supposed to satisfy what people see, not computational algorithms. It’s why the designer’s eye should always play a part in the equation.
The WCAG are guidelines to help designers choose the right color contrasts. The adage, “The map is not the territory,” applies here. Don’t confuse models of reality with reality itself.
Myth 2: Text needs to meet the AAA requirement, or it’s inaccessible.
A vision loss of 20/80 is rare among the general population and mostly affects the elderly suffering from age-related eye diseases. A study found that most low vision is related to aging (source). If the majority of your user base is 70 or older, meeting the AAA requirement is beneficial. The standard is 70 or older because visual acuity starts to decline among users with healthy eyes at that age (source).
Meeting the AA requirement is sufficient for the majority of users. The AA requirement constitutes a contrast ratio of 4.5:1 to compensate for the loss of contrast sensitivity by users with a 20/40 vision loss. A study found the “majority of persons maintain at least fair acuity (20/40 or better) into their 80’s” (source). This finding means that meeting the AA requirement will make your text accessible to the majority of users.
Myth 3: Interface components have the same contrast ratio standard as text.
Many make the mistake of holding interface components to the same contrast ratio standard as text when they are different. Interface components have a contrast ratio of 3:1, while text is 4.5:1. Text requires a higher contrast because users need to read it. Interface components don’t require reading and have a lower standard
Many nuances affect text contrast, such as font size and weight. Large text sizes (18 pt) and text with heavier font weights (14 pt bold) require lower contrast ratios (source). Not only that, but certain interface components are exempt from the requirement. Before you hold an interface component or text to a contrast ratio standard, make sure you’re applying it correctly in the right situations.
Myth 4: Gray text and buttons are inaccessible and look disabled.
Another common myth is that gray text is inaccessible. Many assume users can’t read gray text because it looks low contrast. Sometimes this may be true, but other times it’s a false assumption. For example, the button below has gray text and some would assume it’s inaccessible. However, running it through a contrast checker shows that it’s not only AA compliant, but the ratio is well above the standard.
The other myth you might hear is that a gray button is inaccessible because it doesn’t meet the contrast ratio standard. It turns out the success criteria for buttons doesn’t require a visual boundary indicating the hit area. If a button with text has a border, there is no contrast requirement beyond the text contrast (source). Therefore, the gray button that most would assume is inaccessible passes the contrast requirement.
This success criteria also means that icons next to buttons don’t have a contrast requirement as long as the text label meets the 4.5:1 contrast ratio. However, if an icon is without a text label, the 3:1 contrast ratio requirement applies to the icon.
There’s also the myth that gray buttons look disabled, which is often parroted by biased observers who don’t understand the proper signifier for inactive components. Disabled buttons are signified by the lack of contrast to the text label. When a button is hard to read, users don’t bother with it, which is the intent of a disabled button. Not to mention, the contrast requirement does not apply to inactive components.
Myth 5: Color blind users can’t tell the difference between contrasting colors.
If you’re using competing color hues to differentiate states, you need another visual cue besides color. But if you’re only using color contrast to differentiate states, it’s likely accessible to color blind users.
There are various types of color blindness, but the ones you should focus on the most are red-green deficiencies. Red-green color blindness affects more than 99% of all color blind people (source). There are several color blindness simulators you can choose from on Chrome extensions, such as Colorblindly.
Myth 6: Using a color cue alone isn’t sufficient in conveying information.
Myth 7: An accessible design meets the needs of every user on the planet.
Every designer would love to meet the needs of every user on the planet. As much as designers strive for this, it’s impossible to achieve. Even if you followed the WCAG to the tee, there’s always some user who will find your design uneasy on their eyes.
Instead of striving for an ideal based on an unachievable fantasy, strive for one based on an achievable reality. The reality is that an accessible design can’t meet the needs of every user, but it can meet the needs of as many users as possible.
Understanding this truth means accepting the fact that a minority of users won’t have as good of an experience as the majority. But luckily for the minority, there are assistive technologies with high contrast modes available to them. Designers who truly understand accessibility will strive to achieve the realistic ideal, not the fantasy one.
The Nuances of Color Contrast Accessibility
Accessibility should always be a priority when designing for users. The WCAG guidelines are an effective tool to help you achieve an accessible design of the highest standard. These myths are not caused by the WCAG guidelines. They are caused by people who misinterpret, misrepresent, and misuse the guidelines. It’s time to put these myths to rest.
Understanding the nuances of color contrast accessibility will help you meet the WCAG standards accurately. When others project color contrast accessibility myths onto your design, you can correct them. You’ll stay true to visual simplicity and aesthetics while balancing it with accessibility at the same time. The result is an inclusive interface that satisfies almost everyone.
Gather info What is this thing trying to do? Use tools DevTools are your best friend during this phase
Know where to look (and use tools) Dev tools for client side Error logs Sentry LogRocket The most experienced people in any field know how to ask the right questions. Some of this will come with experience and nothing else. If you’ve seen a problem before, it’s easier to solve
Look at the end game What are you really trying to do here? Don’t focus so much on the tech that you miss the bigger picture
Read Again Error logs provide the best clues. Read them closely. Actually read your code — don’t skim it. Write comments while reading it, or follow existing comments — good for documenting, but also for structuring your thoughts.
Make it simple (break it into smaller parts) Limit the number of inputs and outputs Get it working in a limited capacity (e.g. safe mode, Codepen, etc.) Comment out major sections of code until you have a working example Does this problem exist outside of the framework? Does this work in a clean environment?
Take yourself out of your environment You should be able to take a look at the problem at all zoom levels Does it work locally but not on the server? Does it work in other browsers?
Stay calm It’s easy to get nervous or worked up when the stakes are high It won’t serve you to panic. If you are panicking, take a 10 min walk to deep breath Take a shower, lift weights (seriously)
Talk it over Getting the perspective of another developer can be invaluable
Make things obvious Use debugger or label logs — don’t let it be ambiguous For CSS bugs, use primary colors to make things stand out Use the right tool to make the problem stand out Layers for CSS issues Network for network issues Performance tab (etc.)
Use Git correctly to free up your techniques If you’re code commits are up to date, you can heavily modify code without fear of deleting things — just revert to a previous commit once you find the issue and fix.
Don’t jump at solutions Take the time to fully dissect the problem Question you assumptions It can’t possibly be a problem with ____. Well maybe it is. Wes once spent hours trying to diagnose a check engine light when the gas cap was lose
Get good at pattern matching This comes with experience When did this problem start? Did we deploy any code? Did we change any logic?
Get good at googling Being able to describe your problem is key.
Be Concise
This was one of the key principals highlighted by Allison Rung, UX Writer in Google. Less is more, but concise doesn’t only mean short. It’s a balance between the number of words used and the message that those words bring across. A message is concise when every single word has a purpose.
Be Clear
Although you will only be working with 2 to 4 words, language is the core of your CTA design. The words you choose will affect how your user perceives the intended action of the button. Using the wrong words may communicate something different, resulting in a reluctance to move forward or a negative experience of unintended results.
Be Focused
As mentioned previously, buttons can be used to guide the user through the conversion flow, but too much directional signages might cause confusion instead of clarity. It’s important to limit your usage of buttons within a single user interface to avoid cognitive overload — the fewer choices a user has, the easier a decision can be made.
Use Contrasting Colors
The color you use for your buttons depend on various factors; the background, your brand color, or even the nature of the intended action. While there’s no right color to use, it’s important to select one which contrasts against its background. If the background is blue, then it's best to use either yellow or red to create an impact.
With good contrast, the button stands out amongst the other elements and demands the user’s attention.
Utilize Smart Placements
I mentioned above that contrasting colors are effective in catching the user’s attention, but utilize smart placements can further increase those chances. There are two types of smart placements; one is by understanding the user’s mental model and the second the user’s journey.
Implement Visual Hierarchy
Most of the times, CTA buttons come in a pair; “log in & sign up”, “confirm & cancel”, etc. That’s why it’s important to clarify your primary CTA and create a visual hierarchy to make it the focus. This can be done by designing the primary button in a bolder way (using a contrasting color) and placing it on the right. While the secondary action could be represented using a ghost button or just a simple text link.
Test, Test, and Test
While the above tips would definitely serve to improve your button design, the only way to perfect your CTA button is through multiple A/B testings to find out what works best for your users; the copy, colors, size, placement, shape, and style would differ based on your interface design and user persona.
link to the article
An article showing how to set up a gatsby starter with typescript, eslint husky and prettier
A useful handbook for learning TypeScript link to book
Hover, focus, and active states should be styled different.
There’s a simple reason: They’re different states!
:hover triggers when a user brings their mouse over an element.
Hover states are usually represented by a change in background-color (and/or color). The difference in states doesn’t have to be obvious because users already know they hovered on something.
Styling focus states
:focus activates when an element receives focus. Elements can receive focus in two ways:
When users tab into a focusable element
When users click on a focusable element
Focusable elements are:
Links (<a>)
Buttons (<button>)
Form elements (input, textarea, etc.)
Elements with tabindex
Here are a few important points to note:
Users cannot tab into an element with tabindex="-1", but they can click on it. The click triggers focus.
On Safari and Firefox (Mac), clicks do not focus the <button> element. More info here.
When you click on a link (<a>), focus remains on the link until you lift your finger from your mouse. When you lift your finger, the focus gets redirected elsewhere if the href points to a valid id on the same page.
Here are a few important points to note:
Users cannot tab into an element with tabindex="-1", but they can click on it. The click triggers focus.
On Safari and Firefox (Mac), clicks do not focus the <button> element. More info here.
When you click on a link (<a>), focus remains on the link until you lift your finger from your mouse. When you lift your finger, the focus gets redirected elsewhere if the href points to a valid id on the same page.
You can use a combination of outline, border, and box-shadow properties to create nice focus styles. I share how to do this in “Creating a custom focus style”.
button {
background-color: #dedede;
}
button:hover {
background-color: #aaa;
}
button:focus {
outline: none;
box-shadow: 0 0 0 3px lightskyblue;
}
When you interact with things in real life, you expect some sort of feedback. For example, if you push a button, you expect the button to get pressed.
This feedback is useful on websites too. You can style the “push button” moment with :active. :active triggers when you interact with an element. Interacting here means:
Holding down your left mouse button on an element (even non-focusable ones)
Holding down the Space key (on buttons)
The magic combination allows users to get feedback when they hover, focus, and interact with an element. Here’s the code you need:
.element:hover,
.element:active {
/* Change background/text color */
}
.element:focus {
/* Show outline /*
}
But if you think there’s not enough affordance, you’d want to style :hover, :focus, and :active separately.
.element:hover {
/* Change background/text color */
}
.element:active {
/* Another change in background/text color */
}
.element:focus {
/* Show outline /*
}
link to the article
Parent-child Hover Effect
Since the pseudo-element belongs to its parent element, there are some unusual use cases for that
Increasing the clickable area size
By adding a pseudo-element to a link, the clickable area around it will get bigger. This is very useful and will enhance the experience for the user. Let’s take an example:
Overlays
Let’s suppose that there is an element with a background image, and the design has a gradient overlay with blending mode set to color. Pseudo-elements can help with that!
.hero {
position: relative;
height: 300px;
background: url("image.jpg") center/cover;
}
.hero:after {
content: "";
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
background-image: linear-gradient(180deg, #851717 0%, #30328C 100%);
mix-blend-mode: color;
}
Wrapped Shadows
I’m not sure if the naming is correct, but this is what I got. Back in the days, I used to create a shadow that is skewed at the edges. It has a little subtle effect. Guess what! It’s possible to do them with pseudo-elements.
.elem {
position: relative;
display: flex;
align-items: center;
max-width: 400px;
background: #fff;
padding: 2rem 1rem;
font-size: 1.5rem;
margin: 2rem auto;
text-align: center;
box-sizing: border-box;
}
Adding pseudo-elements
Then, I added :before and :after pseudo-elements with a width of 50% for each of them (I added a different background for each one for explaining purposes).
.elem {
position: relative;
display: flex;
align-items: center;
max-width: 400px;
background: #fff;
padding: 2rem 1rem;
font-size: 1.5rem;
margin: 2rem auto;
text-align: center;
box-sizing: border-box;
}
.elem:before,
.elem:after {
content: "";
position: absolute;
top: 3px;
width: 50%;
height: 100%;
z-index: -1;
background: linear-gradient(to bottom, transparent, #000);
filter: blur(3px);
opacity: 0.3;
}
.elem:before {
left: 0;
transform: skewY(-2deg);
}
.elem:after {
right: 0;
transform: skewY(2deg);
}
Styling Links Based on Its File Extention
a[href$=".pdf"]:before {
content: "";
display: inline-block;
vertical-align: middle;
margin-right: 8px;
width: 18px;
height: 18px;
background: url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/182774/np_pdf_377198_000000.svg) center/20px no-repeat;
padding: 3px;
}
Greet your respondents: Title and introduction
Your title is the shortest and most accurate description of what’s to come. Users tend to skim a form’s content, and hardly any will read a detailed description carefully. That’s why it’s so crucial to capture a form’s purpose in as few words as possible.
The title can be followed by a brief description of what the respondent can expect from the form. Keep this as neutral as possible: you want to make sure that your respondents answer honestly, rather than trying to meet your expectations. Even something as simple as outlining a goal may unwittingly coax your respondent into trying to achieve that goal.
If your form is guaranteed to take a substantial amount of time to finish, alert users to this in advance.
Place related headers and subheaders
Users should be able to glance at a header and immediately know what’s expected of them, without having to read the rest of the text.
The best way to test this is by reading your headers in isolation – do they still make sense?
Break questions with dividers
Dividers matter. In the case of traditional forms, using dividers is the best way to reduce overwhelm by visually breaking questions up. There doesn’t need to be a huge amount of visual difference, and too much contrast can distract.
Decide whether you’ll use multi-page or single page form
If there are only one or two topics, a single page form is your best bet. But if a form has multiple sections, multiple pages are required to break up the conversation. Think about first impressions: a user is likely to feel intimidated if faced with a one-page form containing (what looks like) hundreds of fields
Emphasize Calls-to-Actions (CTAs)
Successful CTAs emphasize the ACTION part: by clicking this button, what will the user DO? Generic labels like “send,” “submit” or “process” won’t cut it. The more descriptive, the better.
To eliminate uncertainty, try answering the question “I want to…” from the user’s perspective. For example, if it’s an inquiry form for a service, it could be ‘Request My Free Consultation.’
Need more convincing? In this study, Unbounce found that even just changing ‘start your free trial’ to ‘start my free trial’ increased clicks of the call to action by 90%.
Where possible, use checkboxes and radio buttons rather than dropdowns, as they require less cognitive load to process. As Luke Wroblewski once remarked: “dropdowns should be the UI of last resort.”
7.Never forget “Thanks” page
Remember your respondents are humans that have given you some of their time. So don’t finish abruptly – always say thank you.
Simple doesn’t equal dumb – it equals readable. That means Plain English, with maximum clarity. Each word should be the shortest, most straightforward version available – that means ‘but’ instead of ‘however.’
Make it personal
A form should feel like a friendly conversation between you and the respondent. Make it personal by using pronouns like “I,” “you” and “your.”
Eliminate passive sentences
Writing packs more of a punch when it’s active (John wrote a letter of complaint), not passive (A letter of complaint was written by John).
Passive writing tends to be lengthier and less focused.
Cut as many words as possible
Many writers fall into the trap of thinking that the more words they use, the smarter they sound. Not so. When it comes to form-writing – or indeed, any kind of writing – cutting words is almost always more effective than adding them.
Use contracted versions of words
Using contracted versions of words (e.g. can’t, isn’t) instead of their serious, full-formed alternatives (cannot, is not)) keeps writing light, friendly and conversational.
Cleave long sentences
Lengthy, meandering sentences induce overwhelm. So do dense blocks of text.
For most readers, up to 20 words per-sentence – and up to three sentences per-paragraph – is just right. Splice anything longer in two. Short sentences aren’t inferior.
Blank space, bullet points, tables – anything else that breaks through the fog of heavy instructions – will make your reader breathe a sigh of relief.
14. Review the written content
Throughout my blogging journey, I’ve learned (the hard way) that good writing is 30% composition and 70% editing.
Of course, cost vs. benefit is subjective, and form-filling usually stems from obligation, rather than an activity respondents hope to gain something from. We can’t ensure benefit, unless we offer our respondents a reward. But we can minimize costs.
Chunk text
Chunking is a handy memory technique: we use it for our bank PINS, social security numbers and locker codes. It refers to the process of arranging information into ‘chunks,’ making the content easier to retain, process and recall.
Define formatting requirements
When filling out a form, no one likes guessing games. Password requirements, syntax rules, numbered spacing: if a field requires a certain input, make it visible.
Applied to UX, Hicks Law is an ode to deliberate elimination: limiting navigation choices and giving users clear but restricted options. Because as design flexibility increases, its usability decreases.
Less really is more.
18. Cut ruthlessly (again)
What purpose does that link serve? Or that button on the top right? If it’s not adding, it’s taking away. Every word of copy, every picture, bell or whistle that isn’t 100% necessary will decrease your form’s conversion rate.
19. Reduce the need for typing
Typing is the most time-consuming and intensive aspect of online forms, and it often leads to errors – especially on mobiles. Replacing text boxes with buttons and sliders and using autocomplete will reduce effort and increase conversions.
Using conditional logic will reduce the time it takes to complete your form by not displaying questions that are irrelevant to a user, making them less likely to abandon the task ahead (stat about abandonment).
Form design should be consistent, but that doesn’t mean it can’t integrate little moments of surprise. By using non-standard visual UI elements – like clickable images and toggled sliders – you can make form-filling more enjoyable and intuitive.
Did you know: 90% of our initial judgement of a product is based on color alone?
In fact, according to marketing guru Neil Patel, color is “85% of the reason you purchased a specific product.” The right combination can improve readership by 40%, comprehension by 73%, and learning by 78%.
38% of people will stop engaging with a page if the content or layout is unattractive. In other words, if your web forms aren’t beautiful to look at (and easy to fill out), they’re a waste of time.
Stagger questions from easy to hard
If you can release questions from easy at the beginning of the form to hard at the end (without sacrificing logical order), users will speed through the initial stages of the form. This, in turn, will trigger the streak effect: the satisfaction generated by quick progress and a sense of momentum, that makes users reluctant to break the streak. Meaning they will continue and are more likely to hang in when the form becomes more demanding.
Illustrate the progress
Reflect user’s advancement at all times. The closer respondents feel to their goal, the more likely they are to push themselves towards it. If your form is multi-page, indicate how many pages they have left to complete.
Brainstorm the questions
All forms rely on questions. When it comes to brainstorming questions, it’s best to start with the end goal and work backward.
So your first question is: What’s the purpose of your form? Is it onboarding? Feedback? Research?
Jot down the knowledge you hope to gain from the form, as precisely as possible. Articulate this as questions (with a question mark at the end) instead of snippets from your train of thought. Give yourself enough time to erase and start again.
Structuring online forms is key for success.
Once you’ve brainstormed, trimmed and refined a final list of questions, it’s time to organize them. Arrange them into groups and subgroups with a ‘theme’ header that holds them together, e.g. contact details, work experience, etc.
Structuring online forms is key for success.
Once you’ve brainstormed, trimmed and refined a final list of questions, it’s time to organize them. Arrange them into groups and subgroups with a ‘theme’ header that holds them together, e.g. contact details, work experience, etc.
Each question, and section, should nudge the respondent on to the next. Big gaps or leaps forward are confusing, so think of the way a form develops step-by-step in a recognizable sequence.
29. Decide on “compulsory vs. optional vs. ‘nice-to-have’”
It’s best practice to limit your form to compulsory questions only. Optional questions needlessly lengthen a form and irritate the user: “Where did you hear about us?” “Would you like to receive marketing emails?”
But what about questions that aren’t compulsory, but would be very ‘nice to have’? Place them at the end of the form as an optional follow-up. This way, they will feel less invasive, and won’t affect your conversion rate.
Avoid the pitfall of meandering questions by scanning them for the words ‘and/’or.’ Seen one? Cleave the question in half.
The clearer the question, the clearer the answer. The clearer the answer, the clearer the data.
31. Give shortcuts
A good form carves a clear route to completion, then carefully guides users along with clues, cues and direction. The shorter that route, the higher the chance of completion. So offer shortcuts if you can.
Some examples of helpful shortcuts:
Zip code lookup
When asking users to fill out their address, it’s best practice to just ask for a house number and postcode/zip code, and then use a lookup service to suggest the full address.
The form needs to engage the attention of the right audience – so, who is that audience comprised of?
Thinking in a broad, foggy mass of people won’t help. To focus your mind, hone on on one person – or, a ‘buyer persona.’ This will tell you more than any general group can.
Imagine a fictitious ideal customer with a job, personality, family, hopes, and dreams. Bring this person into sharp focus. Where do they live and work? What are their opinions and values? How do they relate to your business?
If you find out what is meaningful to this imagined person, you will be better-placed to gather questions that lead to meaningful data.
This is the person you need to keep referring back to. This is the person whose answers you need.
Google’s UX researchers found that aligning labels above fields on the left-hand side increased form completion time, because it requires less ‘visual fixations.’
Eye-tracking studies have shown that simple one-column layouts are better than multi-column layouts with questions positioned side-by-side.
The only exception to this rule is when asking for dates (day, month, year) or time (hours and minutes), where multiple fields are expected to be on one line.
One-thing-per-page is a psychological technique defined as:
…” splitting up a complex process into multiple smaller pieces, and placing those smaller pieces on screens of their own.”
Essentially, users only have one important thing to focus on.
One piece of information to understand.
One decision to make.
One question to answer.
A chaos-free page encourages users to stay on task.
Always adjust the width of the field so it’s just long enough to contain all the characters of the input (users should be able to see their full entry) and no longer.
1 in 12 men have some degree of color blindness.
When displaying validation errors or success messages, don’t rely on using green or red text, (as red-green color blindness is relatively common). Use texts, icons or something else. JotForm Cards warns the user with a micro-animation that shakes ‘no’ when an error has been made.
38. Specify errors inline
Show the user where the error occurred and provide a reason.
If you must use validation, ensure that it’s inline (to the right of the field) and reports errors early on.
Don’t wait until a user hits submit to report validation errors. But equally, inline validation should not be real-time, as this is likely to report errors before a user has completed the field.
You ask for an email address, and you receive a response with no @ sign. You request a phone number, and half your answers don’t contain enough digits.
Typos shouldn’t be a barrier to your form’s usability.
If there’s a lot of variation in how users answer a field (for example, responding to ‘phone number’ with +12345678912, +44 12345678912, 012345678912), convert these to a consistent format.
The Baymard Institute analyzed checkout forms and found that a checkout process that is too long or too complicated is one of the top reasons for abandonment during checkout. So cut fields, cut, and then cut again.
When entering sensitive details such as credit card details, users will be on high-alert to anything that seems dodgy. A recent study revealed that 17% of shoppers left a page without paying due to security concerns.
Professional payment forms put users at ease, whereas anything that looks ‘off’ will put them on edge. That’s why you should be cautious about building a payment form from scratch – even the tiniest errors or inconsistencies can scare users off.
People are increasingly concerned about privacy and information security. If you must ask for sensitive information, make sure you explain why it is needed, using support text below the field.
Giving users the option to save their address and payment information makes the process quicker and more streamlined – especially on fiddly devices like mobile. It also gives repeat customers a sense of reward and loyalty.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.