An interaction state of mind
By: Joel Miller
Tags: React, UI, Interaction States, User Experience, Design Systems

A critical factor in making developers and designers work cooperatively and cross-functionally is a shared understanding of design and code. One aspect that disrupts this is interaction states. Time and again, designers forget to design every state, and developers are left guessing how to implement them. A lack of shared understanding creates a frustrating back-and-forth of tweaking, reviewing, and implementing minor changes. When designers and developers both have a common language for talking about interaction states, development time is quicker, and products are better. Product teams enjoy low-bandwidth communication because designers create component designs for every state, and developers get more time to focus on business logic.

In this post, we’ll dive into understanding what interaction states are, their differences, and how to apply them. We’ll also look into interaction states’ problems and how Visly solves them with a design tool that puts states front and center.

What are interaction states?

A definition that explains interaction states well is one by Google’s Material Design:

“States are visual representations used to communicate the status of a component or interactive element.”

These visual representations come in the form of different states: default, hover, pressed (or active), focus, and disabled. The various states help to communicate to a user what to expect while interacting with a component.

Inputs are a great example for showing the different states of a component

Default state

The default state is the base style of a component. Default is the foundation of what interaction states are built on top of. In this state, the component’s design should appear interactive. For example, if it’s an input, don’t make it look disabled by using many light grey shades. Use a color palette that has a high color contrast.

Hover state

A component enters a hover state when a mouse cursor is directly on top of it. This component’s styling is usually subtlety different from the default state and gives enough visual feedback to show that it is interactive. An important thing to remember is that the hover state is typically supported by navigation by a mouse. Mobile devices do not support a hover state as you navigate with your finger. In our Visly design system, our components hover state styles include changing the background color to be lighter, changing the border color, or changing the elevation.

Hover state styling can be done in several subtle ways

Pressed (active) state

A component reaches this state when a user interacts with it. For example, say you are on a website and click on a button. As soon as you click, the component enters pressed (or active) state. Designing this state can sometimes be an afterthought, but it’s essential to include it, as in flat UI design, it’s not always clear when you have clicked on something. The pressed state provides this crucial feedback to the user and lets them know that a component behaves as expected.

Good styling examples of pressed state are in buttons. If a button hover state changes the default background color to be lighter, the pressed background color should be darker. This gives the resemblance of a button being pressed inwards.

Make your pressed state darker than hover state to give a sense of depth to flat design

Focus state

A focus state highlights which component can receive input (for example, a keystroke) at that moment. There can only be focus applied at one component at a time, and it displays in a few ways:

You could click on a component like an input to display the focus state.

You could use your keyboard to navigate a website or app. Using the tab keys will display the focus state on interactive components.

You could program a component to display focus as a starting point. For example, if you open a form popover, you could focus an input inside of it.

Focus state is crucial for accessibility in design. A component without focus is a significant problem for some users, as they will not understand how to interact with it, and some will not be able to use it. Without focus state, it’s impossible to do keyboard navigation. Users with vision impairments will likely be using a screen reader, keyboard, or emulated keyboard. For a component to be accessible, it’s essential for keyboard-only users to see where focus is applied in the UI at all times.

Focus state can only apply to one component at a time

While browsers have default styling for focus state, each has distinct styling. Generally, it’s best practice to design your own focus state, as default browser styles are not accessible enough. The standard UI pattern for focus state is a light blue border around an interactive component. In our Visly design system, we apply a drop-shadow with a 0 X, 0 Y, 0 Blur, and 2px spread. Using a drop-shadow instead of a border allows our components to display the same effect without changing the size.

Hover or Focus?

Hover state and focus state are often confused as they are both used to visually bring attention to the user. However, some key differences are essential to remember. Focus state activates a component for input as well as visually emphasizing its affordance. Hover state does not do this — it acts as a visual cue for the user. Unlike hover state, focus is mouse agnostic. A component can display focus state without the use of a mouse. You can’t enter the hover state of an element through your keyboard or finger if you are using a mobile device.

Hover state vs. Focus state examples

A good example to compare both hover and focus states is in an input component. If you were to hover on an input, you would see some visual feedback that shows it’s interactive. Once you click on the input, it would enter focus state as it is ready for a user to start typing (or receive input).

Disabled

A disabled state shows that a component is inactive until a user meets certain conditions. A good example you might recognize this is in forms. If you’ve ever had to sign up for an account somewhere, chances are you’ve seen a button in a disabled state. In this example, the button component is shown as unavailable until the inputs have been correctly filled. Once you’ve filled in the information, the button displays its default state.

When designing a disabled component, it should appear inactive without losing color contrast or being confused for a hover state. In our Visly design system, we avoid losing color contrast in components by using the default styling, but with a lower opacity. Doing so makes the component look inactive without hindering its accessibility.

Make sure you don’t lose color contrast when designing disabled state

It’s important to make sure that you don’t overuse disabled state. Not implementing this state well can make a component ambiguous and confusing. Interfaces are already complicated enough for users to understand, so figuring out why they can’t use a component adds more complexity. Always ask yourself first, “does this need to be disabled, or can we just hide it?”. If you find yourself in a situation where you need to apply a disabled state to a component, tooltips can help. Briefly explain why the component is disabled to remove any ambiguity for the user.

Using interaction states in code

In CSS, interaction states are known as pseudo-classes . Mozilla defines pseudo-classes as the following:

“A CSS pseudo-class is a keyword added to a selector that specifies a special state of the selected element(s). For example, a:hover can be used to change a button's color when the user's pointer hovers over it.”

You can apply several different pseudo-classes to elements, but not all can apply to the same type. For example, if you have a link, you can only use a hover state ( a:hover ) or a pressed state ( a:active ). You would not be able to apply to a disabled state because CSS does not support this. However, you can use hover, pressed, focus ( input:focus ), and disabled states ( input:disabled ) in inputs.

Using pseudo-classes for states for simple elements like links is straightforward. However, when you want to apply states to multiple elements in the same component or want to make it look disabled, things can get frustrating quickly. The reason for this is because CSS has no concept of a ‘component.’

Not-so-simple states

If you’re a developer and you use a component that has a link with an icon next to it, you’ve probably run into some annoying issues with pseudo-classes. One common use case is to show the same hover state for both the link and icon. Let’s say you want the link and icon to turn blue when you hover over the component. Achieving this is frustrating with selectors, because you have to do something like the following:

.container:hover > * {

color: blue;

}

Instead of specifying the icon and text to be blue on hover, you must apply pseudo-class to the container. Doing so ensures all the direct children ( > * ) within the .container:hover element will receive the color: blue .

Another method would be to track the hover state manually by using react hooks, like so:

const [hovered, setHovered] = useState() return (

<Container onMouseEnter={() => setHovered(true)} onMouseLeave={() => setHovered(false)}>

<Text style={hovered && { color: blue }}/>

<Icon style={hovered && { color: blue }}/>

</Container>

)

Here, we listen to mouse enter and mouse leave events to keep track of our container’s hover state. Using this information, we apply the correct color to the text and icon. Instead of keeping all of your styling changes in CSS where they should be, you end up having to change styling where you manage business logic.

Disabled(ish) state

Another use case you might have run into is disabled state. As mentioned before, disabled state only exists in CSS for certain elements (buttons, labels, and inputs). If you have a component that you want to look disabled that doesn’t fall into this category, you’ll have to hack your way around this problem.

If you wanted to create a disabled component, you would have to create a new class called disabled. This becomes a problem because you’re handling it differently, and it gets even more problematic when interacting with the component. Let’s take a look at the example below:

.mydiv.disabled { opacity: 0.5; }

.mybutton:disabled { opacity: 0.5; }

While this component has the styling to appear disabled, it’s not in terms of its functionality. If you have click events on this component, it would still work. You could add pointer-event: none so that there are no mouse events at all, but this not always what you want. Using this would make it challenging to add tooltips because it won't receive information that the element has hovered.

One final thing you would have to consider is ordering. As disabled state in HTML is just a class, remembering how styles inherit adds even more complexity. Creating a simple component like a link with an icon next to it becomes complex when you want to add hover, pressed, focus, and disabled states. Managing multiple methods for such a simple component isn’t scalable. One tool that has designed states for scalability is Visly.

How Visly handles interaction states

Visly is a design tool where designers and developers can rapidly design and build production-ready components. It acts as a tool to manage design systems that today live in code and design files, making Visly a quicker and more powerful way to build React components. Today, design tools are not built with code in mind; it’s generally an afterthought. In Visly, interaction states (including focus and accessibility) are first-class features. By default, all components built from Primitives are interactive in Visly, and for non-primitive components, that functionality is just a click away. The states a component can be in are default, hover, pressed, focus, and disabled.

Adding interaction states

In the Visly editor, you can choose whether to add or remove interaction states from your component. To add interaction states to a component, click on the … icon next to the canvas's component name. Select 'add interaction state,'. You'll see five frames on the canvas with each state labeled above. Showing each interaction state on the canvas means that you'll never forget about designing states for a component again. Now, you'll be able to visually see each state and ensure you have a consistent approach to state design.

Adding interaction states to a component in Visly

Overriding state styles

Managing state styles in different ways is now a thing of the past. In Visly, we handle all interaction states in the same way, meaning you don’t have to worry about using classes, variables, or any other hacks. To configure styles for an interaction state in Visly, all you need to do is click on the layer you want to edit in the frame that corresponds to the interaction state you wish to configure.

Visly shows each state on a separate frame

Let’s say you want to edit the hover state and change the background color to be lighter. Select the background container, then navigate to the right-hand styles panel. The collapsed sections in the styles panel mean that the state doesn’t override any styles. Like in code, properties in Visly inherit from the default state. If we want to change the background color, we need to override the style by clicking on the + button in the background color section.

Overriding styles in Visly

Overriding works the same for all states. By default, every component style will inherit from the default state until you specify otherwise. You can even test this out in the canvas by changing the default styles. You’ll be able to see how the other component frames are affected.

Editing states for multiple elements

One of the issues mentioned earlier was the difficulty in changing the styling of multiple elements in code. In Visly, this is trivial. If you have a component with several elements and want them to change on hover, select each layer you wish to edit and override the style.

Disabled state in Visly

Styling disabled state is like any other state — simply override the styles you want to change. In Visly, all components support the disabled state. This state is the only one not handled internally by the component. To put it in a disabled state, you need to set the disabled prop on the component. When this flag is set, the component will render with any styles you've configured for the disabled state in the editor. It will become non-interactive, meaning it will stop listening and responding to any events.

import { MyComponent } from '../visly' <MyComponent disabled />

Test your states with Preview Mode

It’s difficult to quickly preview your styling changes and test the interaction states of a component in code. In Visly, it’s never been easier. The editor comes with a built-in preview mode, which allows you to interact with your components and test out each state. It’s essential not to forget the importance of testing, even if it’s for something minor like a component state.

Preview mode in Visly let’s you test your components as you build them.

Event handlers

Interactive Visly components support all the standard event handlers for user interactions and accessibility events that default React elements support. They are the same events, so you can even refer to the React documentation on how to use these.