Skip to content
GitLab
    • Explore Projects Groups Snippets
Projects Groups Snippets
  • /
  • Help
    • Help
    • Support
    • Community forum
    • Submit feedback
    • Contribute to GitLab
  • Sign in / Register
  • D design
  • Project information
    • Project information
    • Activity
    • Labels
    • Members
  • Repository
    • Repository
    • Files
    • Commits
    • Branches
    • Tags
    • Contributors
    • Graph
    • Compare
  • Issues 21
    • Issues 21
    • List
    • Boards
    • Service Desk
    • Milestones
  • Merge requests 16
    • Merge requests 16
  • CI/CD
    • CI/CD
    • Pipelines
    • Jobs
    • Schedules
  • Deployments
    • Deployments
    • Environments
    • Releases
  • Packages and registries
    • Packages and registries
    • Package Registry
    • Infrastructure Registry
  • Monitor
    • Monitor
    • Incidents
  • Analytics
    • Analytics
    • Value stream
    • CI/CD
    • Repository
  • Wiki
    • Wiki
  • Snippets
    • Snippets
  • Activity
  • Graph
  • Create a new issue
  • Jobs
  • Commits
  • Issue Boards
Collapse sidebar
  • Primer
  • design
  • Merge requests
  • !212
An error occurred while fetching the assigned milestone of the selected merge_request.

Document guidelines for saving or submitting data

  • Review changes

  • Download
  • Email patches
  • Plain diff
Merged Mike Perrotti requested to merge mp/save-patterns-sans-toggles into main 3 years ago
  • Overview 91
  • Commits 35
  • Pipelines 0
  • Changes 3

Issue: https://github.com/github/primer/issues/36

This is a duplicate of https://github.com/primer/design/pull/209

I got feedback on Slack that we shouldn't show controls that haven't been implemented yet.

As we add more imperative inputs (toggle switch, segmented control), I'll update these guidelines.

Pre-publish todo:

  • Add link to https://primer.style/design/ui-patterns
Compare
  • main (base)

and
  • latest version
    af7b50b2
    35 commits, 2 years ago

3 files
+ 288
- 17

    Preferences

    File browser
    Compare changes
content/u‎i-patterns‎
inde‎x.mdx‎ +25 -16
savin‎g.mdx‎ +260 -0
src/@primer/gats‎by-theme-doctocat‎
nav‎.yml‎ +3 -1
content/ui-patterns/index.mdx
+ 25
- 16
  • View file @ af7b50b2

  • Edit in single-file editor

  • Open in Web IDE


@@ -4,50 +4,59 @@ title: UI patterns
import {Grid, Flex, Box, Link, Text, Label} from '@primer/components'
<Grid gridTemplateColumns={['1fr', null, null, null, '1fr 2fr']} gridGap={4}>
<div>
<Grid gridGap={4}>
{/* <div>
<Link href="/design/ui-patterns/button-usage">
{/* <img role="presentation" src="https://user-images.githubusercontent.com/293280/123880841-54bb9280-d8f8-11eb-9a6f-4c33c8196fbf.png" /> */}
<img role="presentation" src="https://user-images.githubusercontent.com/293280/123880841-54bb9280-d8f8-11eb-9a6f-4c33c8196fbf.png" />
</Link>
</div>
</div> */}
<div>
<Link href="/design/ui-patterns/button-usage" fontWeight="bold">Button usage</Link>
<Box as="p">Buttons are a fundamental building block of the GitHub UI. These guidelines summarize how to use buttons across the product.</Box>
</div>
<div>
{/* <div>
<Link href="/design/ui-patterns/empty-states">
{/* <img role="presentation" src="https://user-images.githubusercontent.com/2313998/138136338-046aa39b-51bc-4009-b465-b9d47500ee88.png" /> */}
<img role="presentation" src="https://user-images.githubusercontent.com/2313998/138136338-046aa39b-51bc-4009-b465-b9d47500ee88.png" />
</Link>
</div>
</div> */}
<div>
<Link href="/design/ui-patterns/empty-states" fontWeight="bold">Empty states</Link>
<Box as="p">Empty states are used to fill spaces when no content has been added yet, or is temporarily empty due to the nature of the feature. These guidelines demonstrate best practices for using the blankslate component and designing empty states.</Box>
</div>
<div>
{/* <div>
<Link href="/design/ui-patterns/feature-onboarding">
{/* <img role="presentation" src="https://user-images.githubusercontent.com/2313998/138136341-c85ccaf9-f83f-4933-9fde-9a7b9c630fe4.png" /> */}
<img role="presentation" src="https://user-images.githubusercontent.com/2313998/138136341-c85ccaf9-f83f-4933-9fde-9a7b9c630fe4.png" />
</Link>
</div>
</div> */}
<div>
<Link href="/design/ui-patterns/feature-onboarding" fontWeight="bold">Feature onboarding</Link>
<Box as="p">Onboarding is a virtual unboxing experience that helps users get started with a feature. This is a guide for designing onboarding for the product and does not include what to do for marketing pages, email announcements, social media, etc.</Box>
</div>
<div>
{/* <div>
<Link href="/design/ui-patterns/messaging">
{/* <img role="presentation" src="https://user-images.githubusercontent.com/2313998/138136341-c85ccaf9-f83f-4933-9fde-9a7b9c630fe4.png" /> */}
<img role="presentation" src="https://user-images.githubusercontent.com/2313998/138136341-c85ccaf9-f83f-4933-9fde-9a7b9c630fe4.png" />
</Link>
</div>
</div> */}
<div>
<Link href="/design/ui-patterns/messaging" fontWeight="bold">Messaging</Link>
<Box as="p">Messaging components are used to provide important and relevant information to the user, including feedback, contextual information, product updates, and more. Primer includes three different messaging components: toasts, flash alerts, popovers.</Box>
</div>
<div>
{/* <div>
<Link href="/design/ui-patterns/progressive-disclosure">
{/* <img role="presentation" src="https://user-images.githubusercontent.com/2313998/138136341-c85ccaf9-f83f-4933-9fde-9a7b9c630fe4.png" /> */}
<img role="presentation" src="https://user-images.githubusercontent.com/2313998/138136341-c85ccaf9-f83f-4933-9fde-9a7b9c630fe4.png" />
</Link>
</div>
</div> */}
<div>
<Link href="/design/ui-patterns/progressive-disclosure" fontWeight="bold">Progressive disclosure</Link>
<Box as="p">These guidelines summarize how to use progressive disclosure, as well as guiding principles, best practices, and implementation support.</Box>
</div>
{/* <div>
<Link href="/design/ui-patterns/saving">
<img role="presentation" src="" />
</Link>
</div> */}
<div>
<Link href="/design/ui-patterns/saving" fontWeight="bold">Saving</Link>
<Box as="p">Saving and submitting data is critical to task completion. Use these guidelines to ensure your workflows are consistent and accessible.</Box>
</div>
</Grid>
content/ui-patterns/saving.mdx 0 → 100644
+ 260
- 0
  • View file @ af7b50b2

  • Edit in single-file editor

  • Open in Web IDE

---
title: Saving
---
import {Box, Text} from '@primer/components'
<Box sx={{fontSize: 3}} class="lead" as="p">
Save patterns help users store and update their content and configuration throughout GitHub. These changes should be represented in the UI accurately, quickly, and obviously. Their behavior should inspire confidence and trust.
</Box>
## Explicit saving
When designing a form, start with an explicit saving pattern. Avoid mixing explicit and automatic save patterns on a single page with multiple forms, and never mix save patterns in a single form. For example: if I upload a profile photo in the "Public profile" form, that change should not be saved until I explicitly submit the whole form.
Explicitly saved forms can be saved by clicking a submit button or by pressing Enter while focused on an input.
If there is an error saving the data, the user's data should be preserved in the form and they should be given [feedback](#feedback) about the failure.
<DoDontContainer>
<Do>
<img alt="A form broken into two sections where one section has a dropdown that automatically saves" src="https://user-images.githubusercontent.com/2313998/151887935-980adacd-41aa-47cb-8756-eeaa1a6d0f2b.png" />
<Caption>Separate auto-saving controls from an explicitly saved form</Caption>
</Do>
<Dont>
<img alt="A form with a single section and it contains a dropdown that automatically saves" src="https://user-images.githubusercontent.com/2313998/151887936-d41900ca-2d63-43ac-bf82-55b4bba0df61.png" />
<Caption>Don't mix auto-saving controls and explicitly saved controls in the same form</Caption>
</Dont>
</DoDontContainer>
### Unsaved changes
If a user tries to navigate away from a page with a form that has unsaved changes, you have the option to warn them that their changes will be lost using the [`beforeunload` event](https://developer.mozilla.org/en-US/docs/Web/API/Window/beforeunload_event). Modern browsers prevent developers from customizing the `alert` message, so we're limited to a generic message like "Do you want to leave this site? Changes you made may not be saved."
### Declarative controls
To ensure your forms are accessible, you should use explicit saving (and not automatic saving) in declarative controls. These are:
- Text inputs
- Checkbox groups
- Radio button groups
- Browser-native `<select>` dropdown menus
- Multi-select dropdown menus
<DoDontContainer>
<Do>
<img src="https://user-images.githubusercontent.com/2313998/151623711-ad294946-fd67-428d-96b2-e4f534857c8a.png" />
<Caption>Use save and cancel buttons in dropdowns that allow multiple selections</Caption>
</Do>
<Dont>
<img src="https://user-images.githubusercontent.com/2313998/151623713-12028e6d-86ba-4ae6-b46b-d3c88c0fa2c0.png" />
<Caption>Don't automatically save selections once the dropdown is closed</Caption>
</Dont>
</DoDontContainer>
### Calls to action
When data is not automatically saved after the user makes changes, buttons are used to submit or cancel the changes.
#### Text
All configuration flows should have calls to action that include an obvious active verb such as "Create", "Save", "Delete", or "Update" (for example, "Create" a new repository, "Add" an SSH key to your profile, "Save" security preferences, "Update" a repository's description, "Delete" an old email address).
When defining these calls to actions, make sure to:
1. Keep the text as succinct and clear as possible
2. Add the item’s name to the button text in cases where it may be unclear to the user what is being changed
<DoDontContainer>
<Do>
<img src="https://user-images.githubusercontent.com/2313998/152432685-08d8a944-c802-44ed-89e0-e9d3aa87771b.png" />
<Caption>Use descriptive text with an active verb</Caption>
</Do>
<Dont>
<img src="https://user-images.githubusercontent.com/2313998/152432682-2e102919-114a-43ce-a446-5501d287084a.png" />
<Caption>Don't use vague text</Caption>
</Dont>
</DoDontContainer>
#### Appearance
- If the save button is used to save every input on the page, use the **primary** button appearance.
- If the save button refers to a segment of controls on the page, use the **secondary** button appearance.
- If the save button has a corresponding cancel button, use the **primary** button appearance for the save button, and **secondary** for the cancel button.
For more guidance on button usage, see the [button interface guidelines](/ui-patterns/button-usage).
<Box mb={3} display="flex" alignItems="flex-start" justifyContent="space-between" flexDirection={["column", "column", "column", "column", "row"]} sx={{gap: 3}}>
<Box mb={3} display="flex" alignItems="center" flexDirection="column" mx="auto" sx={{gap: 2}}>
<img
width="456"
src="https://user-images.githubusercontent.com/2313998/152432687-e119f44c-9325-4ae7-b277-e8559a4eb37b.png"
/>
<Text as="p" mt="0" align="center" fontSize={1}>Controls all belong to one form</Text>
<img
width="456"
alt="A small form in a dialog that has 'Cancel' and 'Save' buttons at the bottom right"
src="https://user-images.githubusercontent.com/2313998/151623724-d59d5967-5484-4e81-a4c2-1109409956ee.png"
/>
<Text as="p" mt="0" align="center" fontSize={1}>A form with a button to save and a button to cancel</Text>
</Box>
<Box mb={3} display="flex" alignItems="center" flexDirection="column" mx="auto" sx={{gap: 2}}>
<img
width="456"
src="https://user-images.githubusercontent.com/2313998/152432688-e74f67b2-a777-4c5f-be45-37a43615e6ab.png"
/>
<Text as="p" mt="0" align="center" fontSize={1}>Controls divided into multiple sections</Text>
</Box>
</Box>
#### State
Do not disable or hide a form's save button even if the form is invalid or has not been changed. Disabled buttons cannot be focused using the Tab key, and disabled button styles are typically low-contrast and difficult to read.
<DoDontContainer>
<Do>
<img src="https://user-images.githubusercontent.com/2313998/151623709-2a4257d6-e8d0-4156-b928-c7097981791c.png" />
<Caption>Always keep the save button enabled</Caption>
</Do>
<Dont>
<img src="https://user-images.githubusercontent.com/2313998/151623715-3b298004-b624-43dc-875c-2590c08070e7.png" />
<Caption>Don't disable the save button if the form is invalid or unchanged</Caption>
</Dont>
</DoDontContainer>
#### Placement
Place save buttons somewhere intuitive and easily accessible.
- There should only be one save button on a page.
- Place save buttons somewhere intuitive and easily accessible. If all of the fields save automatically, do not include a save button. This makes it unclear when data is being saved. See the guidelines on [explicit saving](#explicit-saving) and [automatic saving](#automatic-saving) for more information.
- Allow only one edit mode activated at a time if you're designing a page with content that can be edited separately (such as an issue title). Pages that contain multiple save buttons can cause confusion about which save button saves which group. Multiple save buttons with the same label can cause confusion for people who use assistive technologies like screen readers and voice recognition software.
<DoDontContainer>
<Do>
<img alt="A form with one save button at the bottom" src="https://user-images.githubusercontent.com/2313998/151887934-cfcc5130-53a8-4f40-ba36-97148ead72c0.png" />
<Caption>Have one save button per form</Caption>
</Do>
<Dont>
<img alt="A form with two sections. Each section has it's own save button." src="https://user-images.githubusercontent.com/2313998/151887938-1fa6b5d7-c5c1-4010-ab99-e5c37bff8c0a.png" />
<Caption>Don't have one save button per section of a form</Caption>
</Dont>
</DoDontContainer>
##### Bottom-left (default)
If there is a button to submit and a button to cancel, the cancel button should go to the **right** of the submit button.
<Box mb={3} display="flex" alignItems="flex-start" flexDirection={["column", "column", "column", "column", "row"]} sx={{gap: 3}}>
<img
width="456"
alt="A form with the controls and save button left aligned"
src="https://user-images.githubusercontent.com/2313998/155623608-440c8f2c-dbb1-4b3c-a76e-7f01af63b3a8.png"
/>
<Box as="p" mt="0">Default to placing the submit button at the bottom left of the form. Labels and inputs are left-aligned and run from top-to-bottom, so users would naturally move their eyes down and to the left.</Box>
</Box>
##### Bottom-right (dialogs and comments)
If there is a button to submit and a button to cancel, the button to cancel should go to the **left** of the submit button.
<Box mb={3} display="flex" alignItems="flex-start" flexDirection={["column", "column", "column", "column", "row"]} sx={{gap: 3}}>
<img
width="456"
alt="A small form in a dialog that has 'Cancel' and 'Save' buttons at the bottom right"
src="https://user-images.githubusercontent.com/2313998/151623724-d59d5967-5484-4e81-a4c2-1109409956ee.png"
/>
<Box as="p" mt="0">If the save button is used in a dialog, place the button at the bottom right of the dialog. Right-aligned dialog buttons feel familiar because it's a common pattern across various platforms such as Windows, macOS, and Android.</Box>
</Box>
<Box mb={3} display="flex" alignItems="flex-start" flexDirection={["column", "column", "column", "column", "row"]} sx={{gap: 3}}>
<img
width="456"
alt="A multi-line text input with 'Close with Comment' and 'Comment' buttons right-aligned below the input"
src="https://user-images.githubusercontent.com/2313998/158623777-a4bb25ec-e1a0-4feb-bd56-e46a69c7f768.png"
/>
<Box as="p" mt="0">
If the button is used to send a message such as a comment on an issue, right-align the button below the text area. This is where GitHub has traditionally placed the <strong>Submit</strong> button for issues and pull requests, and it's common practice in other apps to place a <strong>Send</strong> button to the right.
</Box>
</Box>
<!--
TODO:
Consider how we might define more patterns to keep the "Save" button easily accessible on long forms:
- add save and cancel buttons on top AND bottom?
- use a sticky header with save and cancel buttons?
-->
##### Adjacent to inline editing input
To simplify the interface, some content may be edited inline without taking the user to a separate flow.
An input used for inline editing should have a save button placed very close to visually connect the input with the button.
<img
width="960"
alt="Two images of a pull request header. The first image shows the title as read-only text. The second image shows a text input to edit the pull request title with 'Cancel' and 'Save' buttons below the input."
src="https://user-images.githubusercontent.com/2313998/158624334-fcd4e268-d144-4420-98ed-819c51631e7e.png"
/>
##### Other
If you believe your submit button will not be intuitive or easily accessible using any of the placement options listed here, you may consider alternatives and optionally propose a new Primer pattern.
## Automatic saving
Automatic saving applies changes as they are made. Automatic saving should be used when the user expects instant feedback from the change they made.
<Box mb={3} display="flex" alignItems="center" flexDirection="column" mt={6} mx="auto" sx={{gap: 2, maxWidth: '450px'}}>
<Box display="inline-block" overflow="hidden" borderWidth="1px" borderColor="border.default" borderStyle="solid" borderRadius="12px">
<img
width="456"
src="https://user-images.githubusercontent.com/2313998/152013139-02eb5213-bb19-4c8d-8b19-01f1d73602d1.gif"
alt="Opening the 'Watch' dropdown menu and changing. It saves automatically."
/>
</Box>
<Text as="p" mt="0" align="center" fontSize={1}>If a form field is saved automatically, it should be obvious whether or not it saved without using text or visual indicators. A visual indicator for whether or not a form field has been saved successfully could be mistaken as a validation status.</Text>
</Box>
There are a few issues to bear in mind with autosaving [declarative controls](#declarative-controls).
- Text inputs: Users expect to set a value and then submit. Sensitive information could be unintentionally submitted, such as a change password input.
- Checkbox groups: It could be unclear whether or not the selection has been saved. Users may accidentally submit a selection clicked by accident.
- Radio button groups: Screenreader users can't read the options without selecting them, which could accidentally apply a selection the user didn't want.
- Browser-native `<select>` dropdowns: Similarly to radio button groups, options could be selected when a user focuses the select and uses the arrow keys to read through each option.
### Dropdown menu accessibility
Semantic menus should only have a list of options to select from and should not have a save button in their dropdown. As soon as additional features (search inputs, filters, tabs, etc) are added, the component should be treated like a dialog with a button to submit and a button to cancel.
## Feedback
If the change is not obvious within the user's viewport, provide feedback to let the user know that the change occurred successfully.
- If the change occurred without a redirect or page refresh, you can use the [toast component](https://primer.style/design/ui-patterns/messaging#toasts).
- If the change occurred and the page refreshes or redirects, you can use a [flash banner component](https://primer.style/design/ui-patterns/messaging#flash-alerts).
<Note variant="warning">If your design is being implemented in github.com, avoid using a toast for now. The toast components used on github.com have accessibility issues that need to be fixed.</Note>
## Error forgiveness
Before submitting a form with potentially destructive consequences (for example, delete a repository), ask the user to confirm they want to submit the form.
For risky changes, provide a way to undo those changes after saving.
<Box display="flex" alignItems="center" flexDirection="column"mx="auto" sx={{gap: 1}}>
<Box display="inline-block" overflow="hidden" borderWidth="1px" borderColor="border.default" borderStyle="solid" borderRadius="12px">
<img width="960" src="https://user-images.githubusercontent.com/2313998/152434199-a067b037-baf8-4093-8796-b0bb58344223.png" />
</Box>
<Text as="p" mt="0" align="center" fontSize={1}>After merging a pull request, a button appears to revert the changes</Text>
</Box>
## Redirecting
When creating new content like issues, discussions, or even larger entities such as repositories or organizations, you may redirect the user to the entity they just created at the end of the flow.
In some cases, such as forking or using a template, you may need to hold the user at a waiting page while the entity is generated. In these cases, make the user feel confident that something is happening and that their configuration details will not be lost.
<Box overflow="hidden" borderWidth="1px" borderColor="border.default" borderStyle="solid" borderRadius="12px">
<img src="https://user-images.githubusercontent.com/2313998/151446009-58bfb92f-00e1-4a15-a2a3-f1afb6fb4031.gif" alt="Screen recording of a user forking a repository" />
</Box>
src/@primer/gatsby-theme-doctocat/nav.yml
+ 3
- 1
  • View file @ af7b50b2

  • Edit in single-file editor

  • Open in Web IDE


@@ -40,7 +40,9 @@
- title: Messaging
url: /ui-patterns/messaging
- title: Progressive disclosure
url: /ui-patterns/progressive-disclosure
url: /ui-patterns/progressive-disclosure
- title: Saving
url: /ui-patterns/saving
- title: Components
url: /components
children:
0 Assignees
None
Assign to
0 Reviewers
None
Request review from
Labels
0
None
0
None
    Assign labels
  • Manage project labels

Milestone
No milestone
None
None
Time tracking
No estimate or time spent
Lock merge request
Unlocked
0
0 participants
Reference:
Source branch: mp/save-patterns-sans-toggles

Menu

Explore Projects Groups Snippets