Add A Little Whimsy to Your Forms
Tags: tutorial, beginners, webdev, javascript
Published: 2021-02-22 16:55:06

It's the little things. Recently [jhey.dev](https://jhey.dev) got the honor of making it onto [whimsical.club](https://whimsical.club/). It's "A curated list of sites with an extra bit of fun".

{% tweet 1363121393446105089 %}

What's whimsy? It's that little bit of fun we can add to our projects. Something that gives our audience a little insight into who we are. It's also another way to connect with your viewers. Sarah Drasner summed this up well in a [recent article](https://css-tricks.com/in-defense-of-a-fussy-website/).

> "While we’re all laser-focused on shipping the newest feature with the hottest software and the best Lighthouse scores, I’ve been missing a bit of the joy on the web. Apps are currently conveying little care for UX, guidance, richness, and — well, for humans trying to communicate through a computer, we’re certainly bending a lot to… the computer."

So. Today we're going to add a confetti blast when users submit a form. It could be a little joy when someone signs up for your newsletter. This is what I'm using it for. I added it You can see it in action at [jhey.dev](https://jhey.dev).

* * *

Let's start with a form, a [very basic form](https://codepen.io/jh3y/pen/vYyeayV). For example, this form will take you to the sign-up for my newsletter.

```html
<form class="whimsy-form" action="https://www.getrevue.co/profile/jh3y/add_subscriber" method="post" target="_blank">
  <div class="whimsy-form__group">
    <label>Email address</label>
    <input type="email" required="true"/>
  </div>
  <div class="whimsy-form__actions">
    <input type="submit" value="Subscribe"/>
  </div>
</form>
```

Let's take that further and add some extra markup and styles to make it a little "friendlier". Here's a quick form we've put together with Tailwind.

{% codepen https://codepen.io/jh3y/pen/bGBojrG %}


At this point, it looks fine. You can always add a little "character" here with the copy you use. Use these opportunities to make a connection. "Want to stay in touch? I'd like to share things I learn with you" sounds better than "Sign up for my newsletter". Anyway, that's another post in itself!

But, we want confetti!

![Pitchforks](https://media.giphy.com/media/9RrdV7o2kJFy8/giphy.gif)

Let's hop into some JavaScript. We're going to use the `canvas-confetti` package. It's perfect for our use case. You've got many options for grabbing this. We can use `skypack` for our demos. You might want to install it in your project and `import` it.

```javascript
import confetti from 'https://cdn.skypack.dev/canvas-confetti
```

Then we need to grab the submit button for our form along with the form itself.

```javascript
const FORM = document.querySelector('form')
const SUBMIT = FORM.querySelector('[type="submit"]')
```

Then tie up the confetti blast to our form submission.

```javascript
FORM.addEventListener('submit', () => {
  confetti()
})
```

{% codepen https://codepen.io/jh3y/pen/QWGqVpM %}


Well. That works. But, not how we want it to. For one, we miss the confetti as the browser opens a new tab. When we return, we see the confetti. But, it blasts out from the middle of the screen.

Let's sort that out. We need to prevent the default action for the form from happening. We can `preventDefault` on the event for that. And then, we will get the button position with `getBoundingClientRect`. We can use that to determine where we want to fire the confetti from.

```javascript
import confetti from 'https://cdn.skypack.dev/canvas-confetti'
const FORM = document.querySelector("form")
const SUBMIT = FORM.querySelector('[type="submit"]')

const onSubmit = e => {
  e.preventDefault()
  const BOUNDS = SUBMIT.getBoundingClientRect()
  confetti({
    origin: {
      x: (BOUNDS.x + BOUNDS.width / 2) / window.innerWidth,
      y: (BOUNDS.y + BOUNDS.height / 2) / window.innerHeight,
    },
  })
}

FORM.addEventListener('submit', onSubmit)
```

The last thing to do is submit our form after a desired delay. We could wait until the end of the confetti. But, around a second is likely enough.

```javascript
setTimeout(() => FORM.submit(), 1000)
```

Putting it all together.

```javascript
import confetti from 'https://cdn.skypack.dev/canvas-confetti'
const FORM = document.querySelector("form")
const SUBMIT = FORM.querySelector('[type="submit"]')

const onSubmit = e => {
  e.preventDefault()
  const BOUNDS = SUBMIT.getBoundingClientRect()
  confetti({
    origin: {
      x: (BOUNDS.x + BOUNDS.width / 2) / window.innerWidth,
      y: (BOUNDS.y + BOUNDS.height / 2) / window.innerHeight,
    },
  })
  setTimeout(() => FORM.submit(), 1000)
}

FORM.addEventListener('submit', onSubmit)
```

Hurrah! 🎉 We have a little whimsy for our form.

{% codepen https://codepen.io/jh3y/pen/zYoEJPy %}


It's worth exploring [the docs](https://www.npmjs.com/package/canvas-confetti) for `canvas-confetti`. You can tweak the settings to your tastes and generate different effects. One option we should pay attention to is `disableForReducedMotion`. I'd recommend setting this to "true" so we respect users with vestibular disorders.

```javascript
confetti({
  disableForReducedMotion: true
})
```

Now we have some confetti, we could even take this a little further with some audio. Drop an `audio` element into our markup and use the `preload` attribute.

```html
<audio src="https://assets.codepen.io/605876/horn.mp3" preload="auto"></audio>
```

Using `preload` ensures that the audio is ready for playback as soon as we hit our "Subscribe" button.

All we need to do is tie that into our "onSubmit" function. Grab the audio element and use the "play" method.

```javascript
const AUDIO = FORM.querySelector('audio')
// Then inside "onSubmit"
AUDIO.play()
```

And there we have it. A whimsical sign up form that will give your users a little joy and something to remember when they submit a form. And you can take joy from knowing that you've shipped a little enhancement for your users. If a user disables JavaScript, your form still works without the bells and whistles.

{% codepen https://codepen.io/jh3y/pen/VwmMGGv %}

I figured people might want a React version too. Here you go!

＼ʕ •ᴥ•ʔ／

{% codepen https://codepen.io/jh3y/pen/WNoZPQy %}

The same process but we can use `useRef` to grab the element references.

## That's it!

One way to add a little joy to your sites and apps. Think about combining this with some of the other things we've been looking at. You'll be well on your way to making awesome experiences for your users. As always, [let me know](https://twitter.com/jh3yy) what you think and see you for the next one!

**Stay Awesome! ʕ •ᴥ•ʔ**