Last year, I worked on a project which contained a reorderable list component – essentially, a group of items that users can rearrange in any order. Though I had no prior experience designing such a pattern, the end result worked well for our purposes. But I was sure that my lack of familiarity meant that there were improvements to the usability and experience that I could implement for the future.

As a result, I dove deep into the pattern – learning, creating, and testing versions of it to deduce as much as I could. In this article, I'm going to channel my findings into how we can create a reorderable list with a great user experience (UX).

Note:

While I'll discuss some development-related topics, this article's primary focus is on the design of the component. If I receive enough interest, I may do a follow-up article which focuses on the pattern’s development.

Defining our reorderable list #

Our first action for the component is to determine what exactly we're building. At its core, we’re creating a list of items which users can change the order of however they wish.

However, we need to make an important distinction: does the order of the items matter or not?

Ordered or unordered? #

A shopping list may function perfectly fine being listed in any order. You might group items by their department or location in the store, but the list order doesn't denote any real sort of value. This is an unordered list.

An ordered list, however, holds explicit meaning in its order. Typically, we use ordered lists to sequence items by importance, priority, or value. A bank statement with a list of transactions is organized in the order they occurred. Or a list of runners who completed a race are listed in the order they finished. The order of those items is important – if we move an item it may affect the way someone interprets that information.

For the example we'll create in this article, we'll use an ordered list with the purpose of ranking our five favorite fruits.

An ordered list of 5 fruits: apple, banana, cherry, grape, and orange.

Populating the list #

Next, we need to think about how the list is introduced to the user. For our fruit rankings, do we populate the list first with options, or do we give the user a blank list that they’ll add items to on their own?

A pre-populated list saves the user time – they can focus purely on ranking and not finding or adding items. It makes the most sense when the number of options matches the list length. But it could also introduce bias if the initial order influences a user's ranking, so that needs to be considered.

Blank lists take away the potential for influence. This may be essential in sensitive cases like elections or voting systems. But requiring the user to add items before ranking them takes a lot more time and effort. And managing two lists of items comes with its own design challenges as well.

We could also use an in-between approach. The user could see a blank list initially with the option to add items manually or populate it with pre-set groups. If this is a repeat task that users return to periodically, such as a weekly poll, it may make sense to start with their previous rankings.

Different situations will call for different approaches. For the purposes of this article we'll pre-populate our fruit list, which will let us focus on the reorderable component itself without the complexity of a second list (though that may be a follow-up post in the future).

Progressive enhancement #

When possible, I push to create projects that use a "progressive enhancement" philosophy. This means building a project on top of a basic foundation that we then, if supported, enhance with more complex features and functionality. This lets users with newer, robust devices get a modern experience while ensuring that users on older or simpler ones aren't left behind and can still use the product.

To create a truly robust, reorderable list with the proper interactivity we'll need to leverage JavaScript (JS). But it's not always a guarantee that JS is available. Users might disable it because of privacy concerns, to save on data, or their device is old and runs smoother without it. Chris Ferdinandi also recently said this in his newsletter:

An estimated 1-2 percent of all JavaScript requests fail. Sometimes the request takes too long and times out. Sometimes the CDN fails. Sometimes an ad blocker gets a bit too aggressive. Sometimes a company’s security policy blocks your file out of an abundance of caution. Sometimes you mistype a variable and shit just doesn’t work.

So starting with a simple, usable interface that we enhance if JS is available ensures the component will always be functional.

Controls #

JS-less functionality means that most modern touches are out the window. We'll need to lean on native form controls for our reordering functionality.

Select element #

A select element (often called a "dropdown") with the item numbers could work well. Because a user can't enter any invalid options, using a select makes the reordering action foolproof. And as a native element, they're familiar to users while having almost universal browser support.

Fruit list where each list item contains a "Move to: " label and a select element. A mouse cursor has opened the first select, displaying a menu of options 2-5. Fruit list on an Android smartphone. Each list item contains a select element. An open select overlay obstructs the view of 4 list items.

However, the select element presents some usability concerns. The largest is that interacting with one requires several steps: opening the menu, navigating the options, choosing one, and then closing the menu. That's 3-4 mouse, touch, and/or keyboard actions at minimum.

Another issue is that on touchscreens, interacting with one often triggers a virtual select overlay to appear over a portion of the screen. This isn't ideal for reordering elements, as you'd want to maintain the user's view of the list so that they're less reliant on their memory of the rankings. There's also the issue of the select overlay repeatedly appearing while traversing the form, which could get annoying.

These certainly aren't deal-breakers, but they're worth considering.

Text input #

A text input would simply need a user to type in a number. With text inputs we get the same benefits as selects: familiarity and great support.

Fruit list where each list item contains a "Move to" label and a text input. A mouse hovers over the first input, showing a text cursor. Fruit list on an Android smartphone. Each list item contains a text input. An open numeric keyboard takes up half of the screen, leaving 2 items visible.

But we also now require our users to type something, adding a keyboard to the mix. And since users could type anything, we'd need to implement validation to check it and provide feedback to fix any errors. So that adds a complexity for us.

We also run into a similar issue as the select: when a touchscreen user activates an input, a virtual keyboard appears, obstructing a portion of the screen.

We can at least streamline the typing for those users with the inputmode="numeric" attribute and value on the input to display a numeric keypad. That at least makes it easier and faster to enter a number.

Submit button #

Regardless of the input used, with no JS we'll need to rely on the server to process the form and adjust items accordingly. So we'll need a classic submit button that, when clicked, refreshes the page – passing the form data to the server for it to update the interface.

It's tempting to include a button within each list item for users to process changes right away. But if a user plans on reordering many items, it would be annoying to ask for a page reload after every change. Not to mention it adds a lot of visual clutter.

Fruit list where each list item contains a "Move to" label, a text input, and a button labelled "Go".

We can simplify this by using a single submit button below the list. This removes the visual busyness, not to mention is one less tab stop for keyboard and screen reader users to navigate. Users can still process changes at any time, it's just not mandatory at each step anymore.

Fruit list where each list item contains a "Move to" label and a text input. Below the list is a button labelled "Save and Refresh".

Also important is to ensure we use the type="submit" attribute and value on our button. This ensures that the button is linked properly with the form, so that clicking it or pressing the enter key will submit it to the server without the need for a JS event handler.

Select vs. text input #

The select and text input are really the only two classic form elements that make sense for our fallback functionality – we could try to shoehorn in radio buttons or checkboxes, but that gets confusing quickly.

A list item with a "Move to" label and 5 radio buttons.

Both elements have pros and cons. So I tested each with a small group of users to see what I could learn. While far from extensive testing, I was able to reach a conclusion.

In brief, results supported the text input over the select menu. Asked to reorder three elements, users completed the task much faster with the text inputs than the selects. Anecdotally, the majority commented that they preferred the text input – including the keyboard users, who found it way less cumbersome and required fewer keystrokes.

Instructions #

Since this is a custom component, it’s crucial we provide clear text instructions so any users who need them have them available. While many users might be able to figure it out with a glance, we can’t assume that’s true for everyone.

I opted to use the following, which is short, direct, and thorough.

Rank the following items from best-to-worst. Reorder elements by entering a new value in an item’s text input. Use the submit button to save changes and refresh the list.

Enhanced functionality #

Now that we have our basic functionality, we can look at how we might improve it once JS is available.

Drag-and-drop #

A common interaction that many of us are familiar with is the drag-and-drop. Users can press-and-hold on an element, which they can then "drag" around the interface, then finally "drop" it into a new location by releasing the press. It's common to encounter drag-and-drop where users can move or rearrange elements, so they're likely to expect it here too.

When we can manipulate onscreen elements with physical motions, it gives that world a level of control and movement that feels familiar. So including it here seems like a no-brainer – users can rearrange our list the same way they might their kitchen cupboards.

Downsides of drag-and-drop #

As nice as it will be for reordering our list, drag-and-drop isn’t something that everyone can or wants to use.

Some users have a device or browser where drag-and-drop is either difficult to use or isn't supported at all. Others might not like dragging elements and prefer a different method. There’s also random things that can make it unappealing – cluttered desks, sensitive laptop trackpads, or messy hands while eating.

But most importantly, drag-and-drop is not an accessible interaction method. Users may not be physically able to perform the actions, like those with arthritis or shaky hands. Others could have a vision impairment and use screen reader software that can't interact in that manner.

The upcoming Web Content Accessibility Guidelines (WCAG) 2.2 specification addresses this with a new Success Criterion – 2.5.7: Dragging Movements (AA), which states any dragging functionality must also be achievable without dragging. Even though the WCAG 2.2 guidelines aren't standard yet, this is a good practice that can only improve the experience.

The takeaway here is that drag-and-drop should act as an optional, bonus method of interaction. To ensure everyone can easily reorder items, we'll also need a universal set of controls that work all of the time.

Control options #

So in addition to drag-and-drop we'll need controls that are easy to recognize, understand, and use. The specifics of your project might lead you to a different conclusion, especially if you're able to do user testing, but here's a few options that I looked at.

Grab toggles #

A grab toggle is a button within each moveable item that will temporarily toggle it into a "grabbed" state, where it can then be moved via keyboard or clicking. Once it's in the desired position, the item is then toggled into an "un-grabbed" state.

Fruit list where each list item contains a toggle button that says "Grab item". The first element is toggled with the button text reading "Item grabbed".

This might seem appealing since it has parallels with our drag-and-drop functionality, almost as a primitive version of it.

However, this method is fairly cumbersome. Moving just one list item requires several interactions, which is going to get tedious very quickly. We'd much prefer that users are able to perform one action to move an item and be done with it.

It's also not very common, so most users won't be familiar with it or recognize its iconography and would need to learn a multi-step process to use it.

Text input #

We could also just use the text input we used for our fallback functionality. As an improvement, it would be a nicer experience to reorder items in real-time without the need to press a submit button below the list to reload the page.

Fruit list where each list item contains a "Move to" label and a text input.

Though there's an issue with this that we'd need to work around – how do we actually trigger the reordering to happen?

It's generally bad practice to change or move content around upon input – typing, in this case – of a form control. It's unconventional, for starters, as forms traditionally use buttons to initiate actions. It can also be disorienting or difficult for some to perceive.

Additionally, how would we know when a user is done typing? I'd hate for a user to attempt to type "10" into an input, only to lose focus and have it move across the screen after pressing the "1" key.

You could instead move items when a user leaves a text input, but now there's the issue of items moving around when the next input is clicked or tabbed into. That's distracting if the newly-focused item is stationary, but even more so if it's in motion too. So not great either way.

Both of these issues could also be potential failures of WCAG’s 3.2.1: On Focus (A) and 3.2.2: On Input (A) Success Criteria.

If we were to use this, we'd likely need to use individual submit buttons within each list item to fire the reorder actions.

A list item with a "Move to" label, a text input, and a "Go" button.

Number stepper #

Number steppers are popular UI elements that appear as a text input sandwiched between two buttons that increase or decrease its value. They could work here.

Fruit list where each list item contains a "Move to" label and a stepper component, which has a decrease button, a text input with the current list position, and an increase button.

While not native browser components, steppers are fairly common in most of the popular frameworks and design systems in use today – so many users will have encountered them before.

They're also simple to use. Changing value is a simple click without the need of a keyboard. However, it also offers flexibility in that a user can click directly into the text input and use a keyboard if they do prefer.

They also potentially solve the issue we had with the text input, as it would be more expected to reorder elements via the stepper's up and down buttons. But even then, a stepper is still an input element (albeit a custom one) so we're arguably again moving items around upon input. Not to mention, a user can still use the text input portion the traditional way, so that issue doesn't truly go away either way.

Buttons-only #

The number stepper inspires a final idea to try: get rid of input elements completely and use a button-only approach. Since our items can move in two directions – up or down – we'd simply need a button for each direction.

Fruit list where each list item contains a "Move [name] up" and "Move [name] down" button.

Now if a user wants to move an item it's one simple click – which could reasonably be expected upon pressing a button. It's also quick and easy for mouse, touchscreen, and keyboard users. Plus no virtual keyboards, UI overlays, multi-step interactions, or complex instructions to decipher.

The downside with this approach is that we can only move an item one position per click. So moving an item several places will require several button presses, potentially becoming tedious.

Before we test these control options, let's look at a couple of ways we can tighten up our buttons-only approach.

Labels #

Those button labels make the functionality clear. But visually, it's a bit much. It looks very cluttered, and one could argue that it adds complexity to the interface. We can simplify this by hiding that text and using large, clear icons. Text labels are generally a good practice, but standard arrows are ubiquitous enough symbols on their own for us to feel comfortable they can convey the button actions to our users.

Fruit list where each list item contains two buttons: one with an up arrow icon and another with a down arrow icon. Each button is approximately 32 x 32 pixels in size.

An important note about hiding the button text is that we can't remove that information from the page completely. While it's not visible, the text still needs to be available for users of screen readers and other assistive technology (AT). We can utilize a "visually hidden" CSS class to accomplish this, which leverages the CSS clip property to hide the text while still keeping it programmatically available.

Alternatively, if we're using img elements to display the icons then we could provide this information in the alt attribute.

Here's an example of how that could look:

html
<!-- using a "visually hidden" class with SVG icons --> <button> <span class="visuallyHidden">Move Apple Up</span> <svg aria-hidden="true" focusable="false"><path d="..." /></svg> </button> <!-- using image alt text --> <button> <img src="up-arrow.png" alt="Move Apple Up" /> </button>
css
.visuallyHidden { clip: rect(0 0 0 0); clip-path: inset(50%); height: 1px; overflow: hidden; position: absolute; white-space: nowrap; width: 1px; }

With this, AT users will get an announcement like "Move Apple Up, button" when encountering the button.

Sizing #

We now need to ensure that the buttons are sized correctly for users to easily identify and click. Mouse or trackpad users need them large enough to place a cursor onto without issue – if you've ever clicked on a small button or link and missed, you'll know how frustrating that can be. For touch users, larger targets help with finger accuracy – something that’s more difficult on phones and tablets. There's also users who may experience hand tremors or other impairments, users in shaky environments like cars and trains, and countless other situations where those actions become trickier.

There's actually a WCAG 2.1 Success Criterion we can lean on for guidance here – 2.5.5: Target Size (AAA). In short, it states that targets must be at least 44px x 44px to allow for easy operation. Despite it falling into WCAG’s AAA conformance bucket, it’s relatively easy to implement for the usability benefits we gain. Additionally, interface guidelines from Apple, Google, and Microsoft recommend targets of 44px, 48px, and 40px, respectively. Adrian Roselli covers all of this more in this great article.

We can take the high end of those recommendations and use a button size of 48px. This gives us a nice, large button that’s easy to click and tap. In addition, I made the buttons circular rather than square, an aesthetic change that I felt gave them a more "buttony" look which worked better with the icon-only approach while contrasting the square edges of the list items.

Fruit list where each list item contains two buttons: one with an up arrow icon and another with a down arrow icon. Each button is approximately 48 x 48 pixels in size.

Testing control options #

As I did with the fallback controls, I did some simple testing with a small group of users. Users were asked to reorder all five list items, with each testing two of the four patterns: grab toggles, text input, number stepper, buttons. Drag-and-drop was not available during this test.

The number steppers caused some confusion, as several users expressed surprise that an item moved upon changing the stepper's value. No users attempted to use the stepper's text input to type a value in.

The text input performed adequately, if not a tad clunky. Users were not confused by this pattern, but a couple of them seemed to get mildly annoyed having to switch between mouse and keyboard.

The grab toggles did not perform well at all. Those who jumped right into the other patterns hesitated initially when not recognizing what to do. After reading the instructions, several users still proceeded cautiously, employing a trial-and-error approach. Two users specifically noted not liking the pattern at all.

On to the buttons: for what it's worth, the average task time was the lowest for the buttons (followed by the stepper, text input, and grab toggles). While measuring speed can be flawed, in this case I think we can say the simplicity of the buttons was a boon.

Users also picked the buttons pattern up the fastest – all ignored the instructions and jumped right in. The additional button presses required to move an item multiple slots didn't seem to matter, as users zipped items around quickly. Perhaps this would be different if our list was longer, say 10 or 20 items.

Based on this, I'm comfortable moving forward with the buttons as our universal controls.

Putting it all together #

Now that we've got our universal controls, let's start to combine everything and polish things up.

Keyboard controls #

It's essential that our component be usable with a keyboard. Not only is it basic usability, it's also a WCAG requirement via the Success Criterion 2.1.1: Keyboard (A) – which asserts that all functionality be operable with a keyboard.

Our component uses native button elements, which are keyboard accessible by default. So users wouldn't have any issues reordering elements. However, tabbing through all of those buttons within the component will get tedious. So we can implement custom keyboard controls to manage focus and make the experience a tad easier.

Reorderable lists aren't an established pattern with de facto controls to lean on. But luckily, the WAI-ARIA Authoring Practices Guide (APG) recommend keyboard conventions to use for custom components. Essentially, the tab key moves focus to and away from a component while other keys, such as arrows, move focus within the component.

Based on those guidelines we'll implement the following functionality:

  • Users can move focus to the list using the tab key.
  • When the list initially receives focus, the focus will be placed on the first list item.
  • The up or down arrow keys move focus to the previous or next item in the list. Focus will wrap around from the last to the first item.
  • The left and right arrow keys move focus between the buttons within each item. Focus will move to the list item again before wrapping to the last and first buttons.
  • The page up and page down keys move focus to the first or last item in the list.
  • The tab key moves focus away from the component to the next interactive element on the page.
  • If a user returns to the list, focus is placed on the last item to have had focus.

Now, keyboard users will get a control scheme that’s similar to other components and patterns out there. In addition, our component now has one tab stop, so users trying to get past the list can easily tab past it.

Focus indicators #

Related to keyboard functionality are focus indicators. Keyboard users need to be able to see which element is active as they navigate our component, especially as items move around and change position. Otherwise they won't be able to use it.

It also satisfies the WCAG Success Criterion 2.4.7: Focus Visible (AA), which requires any keyboard-operable element to have a visible focus indicator.

While mouse or touch users may not need a visible focus indicator, it's certainly helpful. Additionally, people who get distracted easily, have memory issues, multitaskers, and many others can only benefit from a persistent reminder of focus.

Our items and the buttons within them all have a white background with a 1px dark grey border. When focused, we'll change the background to a light teal and the border color to a darker teal.

An item unfocused, an item focused, and an item with a child button focused. The focus indicators consist of a color change from grey to teal to the border and background.

We might be tempted to stop there, but we can do better still. The difference between the grey, white, and teal might not be enough for some to recognize, especially anyone with a form of colorblindness. So we'll not only change the color, but we'll increase the border thickness from 1 to 4 pixels. That way the focus indicator covers more surface area, making it much easier for everyone to see.

An item unfocused, an item focused, and an item with a child button focused. The focus indicators consist of a width change from 1 to 4 pixels and a color change from grey to teal to the border and background.

With the keyboard controls and focus indicators determined, let's look at how that looks in real-time as a keyboard user moves around the component.

The reorderable list is navigated through and interacted with the keyboard, demonstrating the functionality and keyboard indicators.
Video transcript

[Video begins. There is no audio track.]

A reorderable list of 5 fruits is visible. Above the list are text instructions containing a link. Below the list is a button labelled "Save & Continue". The following keyboard actions then occur:

  1. tab: moves focus to the link above the list.
  2. tab: moves focus to the first item in the list.
  3. down 3 times: moves focus from item #1 to item #4.
  4. up 3 times: moves focus from item #4 to item #1.
  5. right 3 times: moves focus to the "up" button within item #1, then the "down" button, and then back to item #1.
  6. left 3 times: moves focus to the "down" button, then the "up" button, and then back to item #1.
  7. page down: moves focus from item #1 to item #5.
  8. page up: moves focus from item #5 to item #1.
  9. down: moves focus to item #2.
  10. tab: moves focus to the button below the list.
  11. shift + tab: moves focus back to item #2.
  12. shift + tab: moves focus to the link above the list.

[Video ends.]

Drag-and-drop details #

Adding the drag-and-drop functionality back in, let's cover a few crucial details to tighten it up.

Drag indicators #

The items need a visual hint so users can identify them as draggable elements. These indicators typically appear as a cluster of lines or dots that mimic tactile grips we know from the real world.

Screenshots of poor drag indicators. On the left is the iOS Mail app drag indicator, with three horizontal grey lines over a white background with a color contrast ratio of 1.7:1. On the right is Gmail's desktop drag indicator, which is a 6 grey circles over a light grey background with a contrast ratio of 1.4:1.

Even though drag indicators often appear subtle, such as a light grey, they don’t usually have adequate contrast. The image above shows drag indicators in iOS's Mail app and Gmail's web desktop, both of which have poor contrast (1.7:1 and 1.4:1, respectively).

The WCAG Success Criterion 1.4.11: Non-text Contrast (AA) requires that any information needed to identify a UI element's functionality have at least a 3:1 contrast ratio. Since drag indicators help identify our items as draggable, they need to fulfill this requirement. So we'll use a medium grey that gives us almost a 7:1 contrast ratio.

Two versions of a list item with a drag indicator icon. The top icon is a light grey with a low 1.6:1 contrast ratio. The bottom icon is a medium grey with a good 6.7:1 contrast ratio.

Cursor feedback #

For mouse users, we'll provide feedback in the form of a cursor change when they interact with an item. All platforms have native cursors that are used in system drag-and-drop encounters – usually in the form of an open hand (to indicate an item can be picked up) and a closed fist (to show an item's been grabbed). By using them we'll be giving the user another familiar sign.

Two versions of a mouse cursor over a list item. The first is an open hand hovering over a draggable item. The second is a closed fist after grabbing the item.

We can add these with the cursor property in CSS, and use JS to toggle a modifier class on the mousedown and mouseup events:

css
/* set open hand cursor on element */ .item { cursor: grab; } /* set closed hand cursor on element */ /* add to element on 'mousedown' event, remove on 'mouseup' event */ .__grabbing { cursor: grabbing; }

Drag-and-drop behavior #

Most implementations I see in the wild reorder items in real-time. So while dragging an item, it swaps places with elements as they touch. This may seem handy, but it can potentially confuse or frustrate the user.

Dragging an item over another isn't explicit enough of an action. Users can change their mind, not know where items can be dropped, or intend to drag an item through one target to reach another. If items switch places anytime they touch, it can result in accidental reordering.

Instead, we show them drop feedback – an indicator that a "drop" action can occur there. That way they know what is and isn't allowed and can make the choice themselves. It's only when they release their mouse that they have "submitted" the change and we move the items.

Now, let's look at actions that can occur when a user does make a drop.

Swapping items #

Users may want to swap two items without affecting any others. If the only option was to move a single item, they'd have to do two actions: one to move the first item to its destination; and another to move the second item to where the first item originated. So being able to swap positions in one move is important.

We can do this by making each list item not only draggable, but a drop target as well. Users that drag an item over another will see a drop feedback informing that a drop can occur.

The reorderable list is interacted with via a user's mouse, demonstrating the functionality of swapping items by dragging them onto other list items.
Video transcript

[Video begins. There is no audio track.]

A reorderable list of 5 fruits is visible. A user's mouse appears onscreen and does the following actions:

  1. Mouse clicks and drags item #2.
  2. Item is dragged over item #3, triggering its drop target state. The item is dragged around the entirety of item #3's boundaries, demonstrating its visibility underneath the dragged item.
  3. Item is dropped, moving item #2 into position #3 and item #3 into position #2.
  4. Mouse clicks and drags item #3.
  5. Item is dragged over item #2, triggering its drop target state.
  6. Item is dropped, moving item #3 into position #2 and item #2 into position #3.
  7. Mouse clicks and drags on item #2.
  8. Item is dragged over item #3, trigger its drop target state.
  9. Item is dropped, moving item #2 into position #3 and item #3 into position #2.

[Video ends.]

I use the color purple for visual drop feedback, which works well since it's not typically used in interfaces to denote success, failure, or other information. It also contrasts the teal used as the accent color on the page. Users who encounter it can now associate the color with drag-and-drop throughout our site. Depending on your design system, you might choose a different color.

In addition to purple, we'll use a thick, dashed border of 8px. This thickness and style contrast the item’s own solid, 4px focused border, allowing color-blind users to perceive the change. It also ensures that even if the item is dragged exactly above the target, the drop feedback is still visible.

Two list items being drag targets with thick dashed purple borders. The first is an item dragged partially over the target, showing the border changes to bottom element. The second is an item dragged exactly over the target, with the border width allowing it to still be visible from underneath.
Moving items #

Alternatively, users might want change an item's position while retaining the order of the others. For example, we want to move the #5 item to #1, but keep the order of the 1-4 elements intact. If the only choice was to swap items, users would have to do multiple steps to get things in the right order.

To accomplish this we'll use the gaps between items as active drop targets as well. Users who drag an item into one of the gaps will see an indicator that it can also be dropped there. When that happens, the item will drop into place while shifting the other elements to make room.

The reorderable list is interacted with via a user's mouse, demonstrating the functionality of moving items by dragging them onto the gaps between items.
Video transcript

[Video begins. There is no audio track.]

A reorderable list of 5 fruits is visible. A user's mouse appears onscreen and does the following actions:

  1. Mouse clicks and drags on item #3.
  2. Item is dragged over item #4, triggering its drop target state.
  3. Item is dragged over the gap between items #4 and #5, triggering its drop state.
  4. Item is dropped on the gap, moving item #3 into position #4 and item #4 into position #3.
  5. Mouse clicks and drags on item #4.
  6. Item is dragged past item #5 into the gap following it.
  7. Item is dropped on the gap, moving item #4 into position #5 and item #5 into position #4.
  8. Mouse clicks and drags on item #5.
  9. Item is dragged past all items into the gap above item #1.
  10. Item is dropped on the gap, moving item #5 into position #1 and items #1-4 all down one position.
  11. Mouse clicks and drags on item #3.
  12. Item is dragged down the list to the gap following item #5.
  13. Item is dropped on the gap, moving item #3 into position #5 and items #4-5 up one position.

[Video ends.]

As above, the drop indicator will use a thick, purple line. Importantly here, the length of the line must be wider than the length of the items so that it's visible regardless of where the dragged item is positioned.

Two gaps being drag targets as a thick dashed purple line. The first is an item dragged partially over the gap, showing the line appearing in the gap. The second is an item dragged exactly over the gap, with the length of the line allowing it to still be seen on either side of the dragged item.

Animated transitions #

Regardless of the method used to reorder items, a nice touch is to animate the items when they change position.

As much as I love animation, much of it doesn't serve any real purpose. In this case, animation can help users visualize the reordering that occurs. If items instantaneously move to different positions then users could miss what just happened, a phenomenon known as change blindness. By using a short transition, we give them an observable confirmation of their change, helping them keep track of which items are where.

We need a transition duration that's short enough to feel responsive and snappy, yet not short enough where it's jumpy or unnoticeable. But we also need to be wary that it's too long where it feels slow, unresponsive, or keeps users waiting. Val Head has a great writeup on transition durations that is a good reference here.

Using trial-and-error, I landed on a duration of .25 seconds. That seemed the most appropriate for moving an item one spot. But there will also be times when an item moves several spots, and that duration might be too short to perceive or animate smoothly. To compensate for this we’ll add a small amount of additional duration for each slot of distance an item has to travel. That will allow items to move smoothly regardless of the distance.

The reorderable list uses animated transitions of .25s when moving one position. When moving multiple positions, the duration is lengthened in correlation with the distance.
Video transcript

[Video begins. There is no audio track.]

A reorderable list of 5 fruits is visible. A user's mouse appears onscreen and does the following actions:

  1. The down button in item #1 is clicked 4 times, swapping it with the item below it each time until is is in position #5. The items quickly slide from their old to their new position each time.
  2. Mouse clicks and drags item #5.
  3. Item is dragged up the list over item #1.
  4. Item is dropped, moving item #5 into position #1. The dropped item quickly slides from the place it was dropped into its new position. Item #1 slides down the list into position #5, with a longer duration.
  5. Mouse clicks and drags item #1.
  6. Item is dragged down the list over item #4.
  7. Item is dropped, moving item #1 into position #4. The dropped item quickly slides from the place it was dropped into its new position. Item #4 slides up the list into position #1, with a longer duration.

[Video ends.]

We also need to respect users who might not want animation due to preference, health concerns, or what have you. Using the CSS prefers-reduced-motion media query, we can detect if a user has set this within their OS. If so, we could change an item sliding across the screen to a softer fade-in-fade-out (or even remove animation completely). Here's a simplified version of how that could look:

css
.__dropped { animation: slide .25s ease-in-out; } @media (prefers-reduced-motion: reduce) { .__dropped { animation: fade .35s ease-in-out; } }

There's a WCAG Success Criterion that deals with interaction-triggered animation – 2.3.3: Animation from Interactions (AAA). This requires that any non-essential animation caused by an interaction is able to be turned off. One could argue that our short transitions are essential to help users understand that items have moved positions. But either way, using the above technique makes it easy to honor the user's choice. We could also go a step further and include an animation toggle in our site's settings for users to control site-wide, which is bulletproof option.

Instructions #

Let's rewrite our instructions so that they're inclusive of our latest additions. We need to cover the buttons, the drag-and-drop functionality, and keyboard instructions.

Rank the following fruits from best-to-worst. Use an item's up and down buttons to reorder. You can also drag-and-drop an item onto another to swap positions, or drop an item above or below another to shift them.

To reorder via text input, visit our [link] classic interface [/link].

Keyboard instructions: use the up and down arrow keys to navigate list items; use the left and right arrow keys to access an item's up and down buttons.
Instructions above the fruit list.

We've summarized everything briefly yet clearly. An important thing to note is that sentence with the link. Even though we've progressively enhanced the interface, we still give users the option to opt-out and visit the low-fi version if they prefer.

Follow-up testing #

To make sure there weren't any last issues, I tested the newest version of the component. The users were asked to reorder all five list items, with no other instructions given.

Like the previous test of the buttons-only pattern, users picked it up almost immediately – even the users who hadn't tested the previous version of that pattern. All testers completed the task quickly and without issue.

Interestingly, most used the buttons to reorder the items over the drag-and-drop. This could be because some who had tested the pattern previously gravitated toward what they had used before. The general feedback I received was that the buttons appeared intuitive and would have immediate action.

My hypothesis is that drag-and-drop becomes more practical the longer the list gets, as moving an item 10 places is a better use case than moving it just a few. Something to test another time!

I did ask several users to complete the task specifically using drag-and-drop. Each picked it up quickly, teaching it to themselves by dragging items and viewing the feedback from the other items and gaps. They remarked that most draggable functionality they encounter solely moves items, so it was a pleasant surprise that they could both move and swap items here. One user spent extra time just dragging items around, saying it was fun to use and play with.

The two users that used a keyboard had no issues tabbing to the list and then navigating within it. They remarked that it was easy to move items around. And since the focus remains on a pressed button after moving, that moving an item even several places is just a few quick presses of the enter key. This is encouraging, but it will need to be tested with non-sighted keyboard users as well to confirm the screen reader experience – if this is you and you'd like to test it out please reach out to me.

Demo #

Feel free to try out the embedded CodePen demo below (or visit it here) to see how the component turned out. Please note that the page is not fully-functional nor is it production-ready – much of the code could be refactored and improved.

See the Pen Re-orderable List pattern v2 by Darin (@dsenneff) on CodePen.

Final thoughts #

A reorderable list is a complex widget that can be confusing or difficult for your users if it's not designed well. I hope I've helped to highlight some of the details, planning, and considerations that need to go into making it as usable as possible.

The small test groups I used to test the patterns discussed here may not match your own project's users and their needs. Nor did I test with any users who use assistive technology or have a disability. So please test widely with your own users and reach your own conclusions.

Thank you so much for reading. If you have feedback, thoughts, or questions, feel free to contact me here or on Twitter.