-
Notifications
You must be signed in to change notification settings - Fork 30
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
docs: create a Patterns docs section and add 3 patterns (#2332)
* add file for disabled state docs * add patterns nav infra and one docs page * add to home page and create thumbs * Get page loading * flesh out the doc * add interactive patterns * Add empty states - wip * more refinements * remove console log * clean up formattting of NN principlies * format interactions like the other patterns * ready to go empty states * update disabled state feedback * revise empty states content * use thumbnail in Home --------- Co-authored-by: Aiden-Brine <[email protected]>
- Loading branch information
1 parent
cfa7f46
commit b48f362
Showing
19 changed files
with
544 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
import { Meta } from "@storybook/addon-docs"; | ||
import { Figma } from "@storybook/addon-designs/blocks"; | ||
import { InlineLabel, Tabs, Tab } from "@jobber/components"; | ||
|
||
<Meta title="Patterns/Disabled states" /> | ||
|
||
# Disabled states | ||
|
||
| **Platform** | **Status** | | ||
| :--------------------------------------------------------------- | :--------------------------------------------- | | ||
| <InlineLabel>Web</InlineLabel> <InlineLabel>Mobile</InlineLabel> | <InlineLabel color="green">Ready</InlineLabel> | | ||
|
||
This pattern involves how we disable, show, or hide actions based on different | ||
criteria. It becomes a balance in explaining the interface and not overwhelming | ||
users with choice. Below are a few of the common examples and accompanying | ||
guidelines for handling those situations. | ||
|
||
## Goal | ||
|
||
Avoid placing the user in a frustrating state where actions are unavailable and | ||
they do not know how to enable. | ||
|
||
## Use when… | ||
|
||
There's absolutely no other way to avoid a disabled state. This should be a last | ||
resort. This flowchart can help you find alternatives: | ||
|
||
<Figma url="https://www.figma.com/design/HXWXusJPZLmaJNGlKEpiyhXW/Product%2FBase?m=auto&node-id=21011-642&t=SHOjoSApnGE6QQeB-1" /> | ||
|
||
## Solution | ||
|
||
### Avoid disabled states | ||
|
||
Your first goal should be to avoid disabled states entirely. If there is a | ||
condition required for the user to take an action, present the opportunity to | ||
set the required condition before the user is blocked by a disabled state. An | ||
example of this is on our “work objects” which all require a client to be saved. | ||
If the user makes it to the bottom of the form and has not added a client, the | ||
primary CTA is “Select Client“. | ||
|
||
<Figma url="https://www.figma.com/design/HXWXusJPZLmaJNGlKEpiyhXW/Product%2FBase?m=auto&node-id=21013-643&t=SHOjoSApnGE6QQeB-1" /> | ||
|
||
#### Permissions-related unavailability | ||
|
||
When functionality or a feature isn't available because of a user's permissions, | ||
hide the action altogether. | ||
|
||
#### Account-tier-related unavailability | ||
|
||
If functionality is unavailable because of a user’s subscription tier, but we | ||
want to introduce the functionality to encourage them to upgrade, use an inline | ||
element near where the unavailable UI would exist, or a | ||
`Button variation="learning"` in place of the UI to nudge the user to learn | ||
more. | ||
|
||
### Why | ||
|
||
Avoiding disabled states is an accessibility best practice and also generally | ||
helpful from a usability perspective. The more time a user spends trying to | ||
understand why they can’t take an action, the less time they spend getting sh\*t | ||
done. | ||
|
||
Disabling elements is often the easiest path for a development team to manage | ||
conditional states of an interface to design and build, but results in a | ||
less-friendly interface for the user. | ||
|
||
### Implementation | ||
|
||
While you should avoid disabled states, many Atlantis components do offer a | ||
`disabled` boolean property for absolutely necessary cases. If you use the | ||
disabled state, you must ensure that the user can understand _why_ the element | ||
is disabled. | ||
|
||
<Tabs> | ||
<Tab label="Web"> | ||
A [Tooltip](../components/Tooltip) may help explain to the user why they | ||
can’t select an element. Make sure the Tooltip can be triggered both by | ||
focus and hover. | ||
<Figma url="https://www.figma.com/design/HXWXusJPZLmaJNGlKEpiyhXW/Product%2FBase?m=auto&node-id=21049-582&t=SHOjoSApnGE6QQeB-1" /> | ||
</Tab> | ||
<Tab label="Mobile"> | ||
[Text](../component/Text) (level="supportingText") is more helpful for users | ||
in the mobile app as it does not require an additional interaction to | ||
uncover. If an input element (such as a Select or InputText) has “assistive | ||
text” built in, do not use the assistive text in a disabled state, but | ||
provide the supporting Text as a separate element. | ||
<Figma url="https://www.figma.com/design/HXWXusJPZLmaJNGlKEpiyhXW/Product%2FBase?m=auto&node-id=21049-645&t=SHOjoSApnGE6QQeB-1" /> | ||
</Tab> | ||
</Tabs> | ||
|
||
## Related | ||
|
||
- [Interaction](../patterns/interaction) | ||
- [Empty states](../patterns/empty-states) | ||
|
||
## Principles | ||
|
||
- [Visibility of system status](https://www.nngroup.com/articles/visibility-system-status/) | ||
- [Recognition over recall](https://www.nngroup.com/articles/recognition-and-recall/) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,144 @@ | ||
import { Meta, Canvas } from "@storybook/addon-docs"; | ||
import { Figma } from "@storybook/addon-designs/blocks"; | ||
import { | ||
InlineLabel, | ||
Box, | ||
Icon, | ||
Heading, | ||
Text, | ||
Button, | ||
Tabs, | ||
Tab, | ||
} from "@jobber/components"; | ||
|
||
<Meta title="Patterns/Empty states" /> | ||
|
||
# Empty states | ||
|
||
| **Platform** | **Status** | | ||
| :--------------------------------------------------------------- | :--------------------------------------------- | | ||
| <InlineLabel>Web</InlineLabel> <InlineLabel>Mobile</InlineLabel> | <InlineLabel color="green">Ready</InlineLabel> | | ||
|
||
## Goal | ||
|
||
Ensure the user does not hit a dead end when there is no content to display. | ||
|
||
## Use when... | ||
|
||
Use when there _could_ be content in a view, but there is none. Possible reasons | ||
for the “emptiness” may include, but are not limited to… | ||
|
||
- A notifications panel when the user hasn't received any notifications | ||
- A list of user-generated content before user has created any content (for | ||
example, a list of clients when the user doesn’t yet have any clients) | ||
- Dashboards or other data-centric views when the user has not generated any | ||
data (ie Home) | ||
- Search results with no match | ||
- An error has resulted in the user not being able to access content they | ||
otherwise could | ||
|
||
## Solution | ||
|
||
A successful empty state should: | ||
|
||
- communicate system status | ||
- increase learnability of the system | ||
- deliver direct pathways for key tasks | ||
|
||
[Designing Empty States in Complex Applications: 3 Guidelines](https://www.nngroup.com/articles/empty-state-interface-design/#:~:text=Empty%20states%20that%20are%20intentionally,getting%20started%20with%20key%20tasks) | ||
|
||
<Figma url="https://www.figma.com/design/HXWXusJPZLmaJNGlKEpiyhXW/Product%2FBase?m=auto&node-id=21162-662&t=SHOjoSApnGE6QQeB-1" /> | ||
|
||
#### When _not_ to add a CTA | ||
|
||
You might not use a direct CTA in the empty state if: | ||
|
||
- The action to populate the empty state has dependencies on another action | ||
being taken | ||
- Example: Quotes must be approved by clients before they appear in a list of | ||
"approved quotes" | ||
- Example: A payment requires creation and delivery of an invoice, and the | ||
client to make a payment, before it exists | ||
|
||
### In lists | ||
|
||
Adjust your context and actions if the user has no content because of a search | ||
or filter, vs when they have no content at all. | ||
|
||
<Figma url="https://www.figma.com/design/HXWXusJPZLmaJNGlKEpiyhXW/Product%2FBase?m=auto&node-id=21189-604&t=nhEwO89McEzvF5TV-1" /> | ||
|
||
<Figma url="https://www.figma.com/design/HXWXusJPZLmaJNGlKEpiyhXW/Product%2FBase?m=auto&node-id=21162-768&t=SHOjoSApnGE6QQeB-1" /> | ||
|
||
### Error states | ||
|
||
A full-screen "empty" error state can help the user make sense of what's going | ||
on, and how to get back on track. If an entire view goes blank due to an error, | ||
a single Banner can look out of place. | ||
|
||
<Canvas> | ||
<Box direction="column" alignItems="center" gap="base" width="100%"> | ||
<Icon size="large" name="alert" color="critical" /> | ||
<Heading level={4}>Something went wrong</Heading> | ||
<Text>Couldn't load content. Refresh to try again.</Text> | ||
<Button label="Refresh" /> | ||
</Box> | ||
</Canvas> | ||
|
||
### When the user can't add content | ||
|
||
When a card is empty, and there is no way for the user to add content, you | ||
should still show some form of empty state, but without a CTA. | ||
|
||
Do not hide the card, as this state-based hiding and showing may not be | ||
intuitive for the user. For example, see the ”Payments” card here, where the | ||
user needs to create an invoice and interact with their client before they can | ||
create a payment against a job. | ||
|
||
<Figma url="https://www.figma.com/design/HXWXusJPZLmaJNGlKEpiyhXW/Product%2FBase?m=auto&node-id=21341-20062&t=nhEwO89McEzvF5TV-1" /> | ||
|
||
## Why | ||
|
||
By considering and accounting for these often “un-happy” paths in the user’s | ||
journey, we can allow the user to learn how to use Jobber more effectively, | ||
guide them out of troublesome scenarios, and ensure that they never feel like | ||
they’ve hit a dead-end in the product. | ||
|
||
## Implementation | ||
|
||
<Tabs> | ||
<Tab label="Web"> | ||
There's no component but these are some common patterns: | ||
|
||
<Canvas> | ||
<Box direction="column" alignItems="center" gap="base" width="100%"> | ||
<Icon size="large" name="alert" /> | ||
<Heading level={4}>No results found</Heading> | ||
<Text>Try adjusting your search criteria or clearing filters</Text> | ||
<Button variation="subtle" label="Clear Filters" /> | ||
</Box> | ||
</Canvas> | ||
</Tab> | ||
<Tab label="Mobile"> | ||
[EmptyState](../component/EmptyState?mobile) gives you the boilerplate | ||
`Icon` + `Heading` + `Text` + `Button` out of the box but you can also compose it | ||
similar to the web implementation if more flexibility is needed. | ||
</Tab> | ||
</Tabs> | ||
|
||
## Not obvious details | ||
|
||
In some cases, illustrations may be useful in an empty state to bring some | ||
personality to the scenario. If this fits your use case, work with the design | ||
team to find or create an appropriate illustration. | ||
|
||
## Related | ||
|
||
- [Empty State](../component/EmptyState?mobile) mobile component | ||
- [Disabled states](../patterns/disabled-states) | ||
|
||
## Principles | ||
|
||
- [Visibility of system status](https://www.nngroup.com/articles/visibility-system-status/) | ||
- [Consistency and standards](https://www.nngroup.com/articles/consistency-and-standards/) | ||
- [Aesthetics and minimalist design](https://www.nngroup.com/videos/aesthetic-and-minimalist-design/) | ||
- [Help users recognize, diagnose and recover from errors](https://www.nngroup.com/articles/ten-usability-heuristics/#toc-9-help-users-recognize-diagnose-and-recover-from-errors-9) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,128 @@ | ||
import { Canvas, Meta } from "@storybook/addon-docs"; | ||
import { Figma } from "@storybook/addon-designs/blocks"; | ||
import { | ||
Card, | ||
Content, | ||
Disclosure, | ||
InlineLabel, | ||
InputText, | ||
Text, | ||
Tabs, | ||
Tab, | ||
} from "@jobber/components"; | ||
|
||
<Meta title="Patterns/Interaction" /> | ||
|
||
# Interaction | ||
|
||
| **Platform** | **Status** | | ||
| :--------------------------------------------------------------- | :--------------------------------------------- | | ||
| <InlineLabel>Web</InlineLabel> <InlineLabel>Mobile</InlineLabel> | <InlineLabel color="green">Ready</InlineLabel> | | ||
|
||
## Goal | ||
|
||
Provide feedback (or feedforward) to the user in a consistent way to help them | ||
predict what will happen when they engage with Jobber. | ||
|
||
## Use when... | ||
|
||
An interactive element is... being interacted with. | ||
|
||
## Solution | ||
|
||
<Tabs> | ||
<Tab label="Web"> | ||
<Canvas> | ||
<Card header="Interactive element example" url="#"> | ||
<Content> | ||
<Text>This card has examples of hover and focus states for the web</Text> | ||
<InputText placeholder="Inputs change too" /> | ||
</Content> | ||
</Card> | ||
</Canvas> | ||
|
||
#### Hover | ||
- background-color increases contrast with the surface | ||
- cursor changes to a pointer | ||
|
||
#### Focused with keyboard (:focus-visible) | ||
- background-color increases contrast with the surface | ||
- a custom focus ring is applied | ||
|
||
#### Timing | ||
Most state transitions use `var(--timing-base) ease-in` for a pleasant | ||
transition. | ||
|
||
#### CSS implementation | ||
Atlantis components come with these states handled, but if you're extending or | ||
creating custom elements, this is the general pattern used to achieve an "Atlantis" | ||
interactive state. | ||
|
||
```css | ||
.myElement { | ||
background-color: var(--color-surface); | ||
/* apply consistent transitions to background and box-shadow */ | ||
transition: all var(--timing-base) ease-in; | ||
cursor: pointer; | ||
/* hide the default focus ring, but still support "high contrast" modes */ | ||
outline: transparent; | ||
} | ||
|
||
.myElement:hover, | ||
/* use :focus-visible instead of :focus to reduce visual noise for mouse users */ | ||
.myElement:focus-visible { | ||
background-color: var(--color-surface--hover); | ||
} | ||
|
||
.myElement:focus-visible { | ||
box-shadow: var(--shadow-focus); | ||
} | ||
``` | ||
|
||
</Tab> | ||
|
||
<Tab label="Mobile"> | ||
<Figma | ||
collapsable | ||
url="https://www.figma.com/proto/avvgu5SkbBvS8lGVePBsqO/Product%2FMobile?node-id=63935-11952&t=9XDlpJmD0lnq3e97-1" | ||
/> | ||
|
||
#### Pressed | ||
- Most elements will reduce in opacity using the `opacity-pressed` token | ||
|
||
#### Focused inputs | ||
- While there is no system-wide "focus ring" in mobile, input borders change color when focused | ||
|
||
|
||
#### React Native implementation | ||
|
||
```.ts | ||
// as a classname in styles.ts files | ||
|
||
pressed { | ||
opacity: tokens["opacity-pressed"] | ||
} | ||
``` | ||
|
||
```.ts | ||
// as a property on react-native core components | ||
|
||
<TouchableOpacity | ||
activeOpacity={tokens["opacity-pressed"]} | ||
onPress={yourActionHere} | ||
> | ||
``` | ||
</Tab> | ||
|
||
</Tabs> | ||
|
||
## Related | ||
|
||
- [Disabled states](../patterns/disabled-states) | ||
- [Color](../design/color) | ||
- [Animation](../design/animation) | ||
|
||
## Principles | ||
|
||
- [Visibility of system status](https://www.nngroup.com/articles/visibility-system-status/) | ||
- [Consistency and standards](https://www.nngroup.com/articles/consistency-and-standards/) |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Oops, something went wrong.