Practical examples of how to build a simple hover state prototype with some of the most popular prototyping tools on the market.

There are tons of options available for UX designers to prototype a user experience and you may not know what are the pros and cons of each tool just yet. The main goal of this post is to give you a taste of each tool’s prototyping workflow by taking you through the process of building a simple hover state example. By the end of these exercises, you should have a high-level idea of the jobs that are suitable for each of the tools.

Since the goal is to get a quick understanding of the prototyping tool’s workflow, we don’t need to be too fancy. A simple button hover prototype that alters the button background color should do the job. Below are the two component states we will be animating between.

The two states we’ll be prototyping

Here is a quick spec of the button background style:

  • Default background color: #3B42F4
  • Hover background color: #767BF8 (or 70% opacity of the default)

Instead of Invision Studio, we’ll do an Invision web example because it is the most popular prototyping tool based on 2018 Design Tools Survey. Since this is a screen linking tool, the only way to prototype the hover state is to build a hover link hotspot.

Step 1: Start by setting up two screens with each of the button states. You will need to design the buttons in another design tool of your choosing then upload the two states to Invision.

Step 2: Setup a hotspot over the default button with ‘Screen as Overlay’ link to the hover screen. Make sure to un-check the ‘Stay on target screen’ to allow the hover out.

Result: You should now have a functioning button hover state.


Invision is fairly straightforward and that’s its advantage. You can essentially prototype hover states between any two static screens. Updating design is as easy as uploading a new mockup.


The transition animation is limited and lacking sequence. Therefore if you require elements to transition one after another with fancier effects then this may not be the right tool of choice.

Framer has come a long way from being viewed as a super-advanced prototyping with code tool to now a design tool with prototyping capabilities. There are a couple of ways you can prototype a hover state in Framer X. We are going to go over the code override method because that’s more unique to Framer and it’s not too complex.

Step 1: Create the button designs as a Frame (Need to be in a frame to attach override to). We only need the default button here as we can apply the hover state as an override.

Step 2: With your button frame selected, hop over to the properties panel on the right and add an override. Go with the default ‘Example’ file, and for the override dropdown, choose the hover option.

Step 3: If you click on the preview prototype option at the top right corner (play icon) you can interact with this button and see it’s current hover state, which is a default scaling effect taken from the example override file.

Step 4: So far we’ve applied a hover state to your button design layer and applied an example hover override to it. Let’s look at the override code and modify it to meet our spec. From the Override properties panel, click on ‘Edit Code’. You should then see a code editor (don’t worry, we won’t be coding much to make this example work).

Step 5: What we need to do now is to modify the “Hover() function” starting on line 9 that looks like the code block below. Instead of the “whileHover: { scale: 0.8 }”, which monitors a mouse hover to the button layer and when it’s being hovered it scales the layer to 80% of its original size, we want to modify this to “whileHover: { opacity: 0.7 }” as shown below:

export function Hover(): Override {   return {      whileHover: { opacity: 0.7 },   }}

Result: The override now should adjust the opacity of the layer to 70% of the original opacity to simulate the hover effect we want.


The override code components (not covered in this post) are flexible and powerful methods to build high-fidelity prototypes. You can prototype anything that’s feasible from a technical perspective.

The override on top of the design layer is a good half step to leverage some of the code-level prototyping features but not completely jumping into the full code setup.


The coding part always have a bit of a learning curve for designers regardless of how elementary. While Framer is a powerful tool, it requires a good amount of time invested to learn the tool if the designer is new to coding.

Native prototyping feature was introduced since Sketch 49. However, the feature only supports artboard linking on click and lacks hover interaction. Therefore, to prototype hover animation we need to turn to Sketch plugins. There are a few animation plugins available. For this example, we will use the Anima plugin.

Step 1: You need to set up an artboard with the default button style.

Step 2: Assuming you have the plugin installed and activated. On the bottom right panel, you should see the Anima plugin section. We’ll use the interaction feature under the prototype tab. Select the button background and hit that ‘create’ button.

Step 3: You will then be taken to the Anima editor, where a new state of the button (this is where you specify what the hover state is) will be created for you. To build the minimal hover state prototype:

  1. Select the default/state1 button background layer
  2. A blue lightning bolt icon will be revealed. Click on that icon and connect the line to state2
  3. Choose the on ‘On Mouse Enter’ option. This is to set what interaction should trigger the state transition
  4. Select the state2 button background and adjust the opacity to 70% to set the hover style
  5. Repeat similar steps to connect the state2 button back to state1 ‘On Mouse Leave’
  6. Click on ‘Back to Sketch” on the very top left of your screen to exit the editor

Result: Back to the sketch interface, at the bottom right you should see the Anima section with a ‘Preview in Browser’ button. Click on that will spin up a preview browser tab that lets you play with the button hover state prototype.


If you are designing with Sketch then the ability to prototype within the same environment is helpful.


The prototyping features out of the box may not support exactly what you are trying to simulate. You’ll need to lean on third-party plugins, which has various complexity and learning curve.

Figma recently released more prototyping features that allow designers to build more advanced prototypes within the design environment. The new feature makes it fairly easy to build our example so let’s walk through that.

Step 1: Set up two frames with each button state design layers.

Step 2: With the default button background layer selected, toggle to the prototype tab located within the right panel.

Step 3: Click on the dropdown to select the ‘While Hovering’ option, second dropdown to ‘Swap with’, set the last dropdown to the name of your second frame.

Result: You should now see an arrow connecting the default button background to the second frame. Click on the present (play icon) located at the top right corner will allow you to play with the hover prototype.


With the new Smart Animate feature, prototyping a hover interaction is fairly easy once you have the before and after states created.


It lacks support for setting custom animation sequences. If you need to animate multiple items in the same interaction with various speed then that is currently not possible.

There are more tools out there to help with UX prototyping and there isn’t a perfect tool for every job. If you are serious about learning how to prototype well, finding the right tool(s) for the job is critical. Small exercises like the hover examples in this post can help you wrap your head around the strength and weaknesses of a tool.

Thanks for taking the time to go through each of the examples. Hopefully, you now have a high-level idea of how to use each of the tools mentioned and you will have some baseline knowledge when you need to choose a tool to prototype your designs in the future.


16th Oct 2019

I’ve been styling :hover, :focus, and :active states the same way for years. I can’t remember when I started styling this way. Here’s the code I always use:

// Not the best approach. I'll explain why in this article
.selector {
  &:active {
    // Styles here

As I paid more attention to keyboard accessibility (and therefore paying more attention to focus), I began to think we should not style hover, focus, and active states the same way.

Hover, focus, and active states should be styled different.

There’s a simple reason: They’re different states!

Today, I want to show you a magical way to style all three states effortlessly.

Let’s start with :hover.

Styling hover states

:hover triggers when a user brings their mouse over an element.

Hover states are usually represented by a change in background-color (and/or color). The difference in states doesn’t have to be obvious because users already know they hovered on something.

button {
  background-color: #dedede;

button:hover {
  background-color: #aaa;
On hover, button darkens slightly.

Styling focus states

:focus activates when an element receives focus. Elements can receive focus in two ways:

  1. When users tab into a focusable element
  2. When users click on a focusable element

Focusable elements are:

  1. Links ()
  2. Buttons (
  3. Form elements (input, textarea, etc.)
  4. Elements with tabindex

Here are a few important points to note:

  1. Users cannot tab into an element with tabindex="-1", but they can click on it. The click triggers focus.
  2. On Safari and Firefox (Mac), clicks do not focus the
  3. When you click on a link (), focus remains on the link until you lift your finger from your mouse. When you lift your finger, the focus gets redirected elsewhere if the href points to a valid id on the same page.

For focus, we’re more concerned about users tabbing into elements than clicking on elements.

When a user hits tab, they don’t know where the focus will go to. They can only guess. This is why we need a prominent change a user’s attention attention to the focused element.

The default focus style is okay most of the time. If you want to design your own focus, think about these four things:

  1. Adding an outline
  2. Creating animations with movement
  3. Changing background-color
  4. Changing color

Since background-color and color changes often accompany :hover, it makes sense that outlines or animations should accompany :focus.

You can use a combination of outline, border, and box-shadow properties to create nice focus styles. I share how to do this in “Creating a custom focus style”.

button {
  background-color: #dedede;

button:hover {
  background-color: #aaa;

button:focus {
  outline: none;
  box-shadow: 0 0 0 3px lightskyblue;
Focus a button with Tab. When focused, shows an outline with box-shadow.

Styling active states

When you interact with things in real life, you expect some sort of feedback. For example, if you push a button, you expect the button to get pressed.

This feedback is useful on websites too. You can style the “push button” moment with :active. :active triggers when you interact with an element. Interacting here means:

  1. Holding down your left mouse button on an element (even non-focusable ones)
  2. Holding down the Space key (on buttons)
button:active {
  background-color: #333;
  border-color: #333;
  color: #eee;
Changes background-color and color when user holds their left mouse button down on the button.

Two weird things to take note of:

  1. Holding down Space triggers :active on buttons, but holding down Enter doesn’t.
  2. Enter triggers links but it doesn’t create create an active state. Space doesn’t trigger links at all.

Links have a default active style. They turn red when they get clicked.

By default, links turn red when they get clicked.

The relationship between active and focus

When you hold down the left mouse button on a focusable element, you trigger the active state. You also trigger the focus state at the same time.

When you release the left mouse button, focus remains on the element

? is true for most focusable elements except links and buttons.

For links:

  1. When you hold down left mouse button: Triggers :active and :focus state on Firefox and Chrome Only triggers active on Safari (tested on Mac only)
  2. When you release left mouse button: :focus remains on link (if the link’s href does not match an id on the same page). On Safari, focus goes back to .

For buttons:

  1. When you hold down left mouse button: Triggers :active and :focus state on Chrome only. Does not trigger :focus at all in Safari and Firefox (Mac). I wrote about this strange behavior here.

If you want clicks to focus on buttons, you need to add this JavaScript as early as you can. (As for why, you can read the article I linked to above for more information).

document.addEventListener('click', event => {
  if (event.target.matches('button')) {

Once you have this code, click behaviour on buttons become:

  1. When you hold down left mouse button: Triggers :active in all browsers. Triggers :focus on Chrome only.
  2. When you release left mouse button: Triggers :focus on Safari and Firefox (Mac). :focus remains on button for other browsers.
Button behavior on Safari.
Button’s behavior on Safari after adding the JavaScript snippet above.

Now you know about hover, focus, and active states, I want to talk about styling all three.

The magic combination

The magic combination allows users to get feedback when they hover, focus, and interact with an element. Here’s the code you need:

.element:active {
  /* Change background/text color */ 

.element:focus {
  /* Show outline /* 

For mouse users:

  1. When the user hovers over an element, background-color (and/or color) changes. They get feedback.
  2. When the user clicks on an element, focus outline shows. They get feedback.
Mouse users receive feedback on hover and on click.

For keyboard users:

  1. When the user tabs into an element, focus outline shows. They get feedback.
  2. When they interact with the element, background-color (and/or color) changes. They get feedback.
Keyboard users receive focus on Tab and on interaction.

Best of both worlds!

  1. I have not tested the magic combination thoroughly. This is a proof of concept. I’d appreciate it if you help me with some tests and let me know how it fares.
  2. If you run tests, don’t use Codepen. Focus states for links are weird on Codepen. If you hover over a link, the focus outline gets removed. Why? I don’t know. Sometimes I think it’s best to test stuff like these without any fancy tools. Just plain ol’ HTML, CSS, JS.

The non-magic (but might be better) combination

Like I mentioned above, clicks on buttons have a weird behavior in Safari and Firefox (Mac). If you added the JavaScript snippet I showed you, the magic combination still works. But it’s not perfect.

For Safari and Firefox (Mac), this is what happens:

  1. When users hold their mouse button down, nothing changes.
  2. When users lift their mouse button up, the element gets focus
Button behavior on click in Safari.

If you think this is enough affordance, then the magic combination works. You can stop here.

But if you think there’s not enough affordance, you’d want to style :hover, :focus, and :active separately.

.element:hover {
  /* Change background/text color */ 

.element:active {
  /* Another change in background/text color */

.element:focus {
  /* Show outline /* 
Button behavior on Safari if you styled all three states.

That’s it! Hope you learned something today!

Thanks for reading. Did this article help you out? If it did, I hope you consider sharing it. You might help someone else out. Thanks so much!


Pure CSS pagination with hover effect for pages or sliders. Hover effect made with liner gradient for background-image and other CSS background properties: background-size, background-position.

-webkit-background-clip, -webkit-text-stroke, -webkit-text-fill-color used for text effects.
Compatible browsers: Chrome, Edge, Firefox, Opera, Safari.

1 / 4

@import url('https://fonts.googleapis.com/css?family=Maven Pro:500,700&display=swap');

body {
  display: flex;
  min-height: 100vh;
  overflow: hidden;

div.wrapper {
  margin: auto;

button {
  border: none;
  margin: 0;
  padding: 0;
  font-family: 'Maven Pro', sans-serif;
  font-size: 80px;
  font-weight: 700;
  cursor: pointer;
  -webkit-background-clip: text;
  -webkit-text-stroke: 1px #313131;
  -webkit-text-fill-color: transparent;
  background-image: linear-gradient(133deg, rgba(255,0,0,1) 0%, rgba(255,0,0,1) 50%, rgba(255,255,255,1) 50%, rgba(255,255,255,1) 100%);
  background-size: 300%;
  background-position: 100% 0;
  transition: background-position .5s ease-in, font-size .2s ease-in;

button.disabled {
  -webkit-text-stroke: .7px #bebebe;
  background: none;
  cursor: default;

button:hover {
  background-position: 0 0;

.pagination-state {
  position: relative;
  display: inline-block;
  top: -17px;
  width: 60px;
  text-align: center;
  font-family: 'Maven Pro', sans-serif;
  font-size: 12px;
  font-weight: 500;
  color: #313131;
  transition: top .2s ease-in;

@media screen and (max-width: 640px) {
  button {
    font-size: 60px;
  .pagination-state {
    top: -11px;

@media screen and (max-width: 420px) {
  button {
    font-size: 40px;
  .pagination-state {
    top: -6px;