JSFiddle - React, Tailwind, and code Playground

by Chris Ball

HTML

<link href="https://fonts.cdnfonts.com/css/lexend-deca" rel="stylesheet" />
<style>@import url('https://fonts.cdnfonts.com/css/lexend-deca');</style>
<div id="cursor"></div>

<div>
	<span>See more</span>
	<span>See EVEN more</span>
</div>

<button class="small">Buy Tickets</button>
<button>Buy Tickets</button>
<button class="large">Buy Tickets</button>

<div class="var var-one">Variant One</div>
<div class="var var-two">Variant Two</div>

<canvas></canvas>

CSS

:root {
  --her-purple: #4E3366;
  --her-pink: #FFCCD0;
}

html, body {
  font-family: 'Lexend Deca', sans-serif;
  height: 100%;
}

#cursor {
  background: var(--her-pink);
  border-radius: 500px;
  /* padding: 0.5em; */
  left:0;
  line-height: 1;
  pointer-events: none!important;
  position: fixed;
  top:0;
  transition: transform 80ms, opacity 600ms;
  z-index: 20000;

  & .contents {
    white-space: nowrap;
  }

  &.transition-contents .contents {
    transition-property: height width;
    transition-duration: 200ms;
    transition-delay: 150ms;
  }
}

@property --button_blur-start-color{
  syntax: "<color>";
  inherits: true;
  initial-value: transparent;
}

button {
  --button_blur-start: 0.5em;
  --button_blur-end: 3.5em;
  --button_hover-bg-color: hsl(from var(--her-pink) h s calc(l + 6));
  /* --button_hover-bg-color: transparent; */
  --x-pos: 0;
  --y-pos: 0;

  background-color: transparent;
  background-image: radial-gradient(
    circle at var(--x-pos) var(--y-pos),
    var(--button_blur-start-color) var(--button_blur-start),
    transparent var(--button_blur-end)
  );
  border: none;
  border-radius: 100vmax;
  color: var(--her-purple);
  font-family: inherit;
  font-size: inherit;
  font-feature-settings: "ss01" 1;
  font-variation-settings: "wght" 550;
  overflow: hidden;
  padding: 0.5em 0.75em;
  transition: background-color 600ms, --button_blur-start-color 300ms;

  /* Size variants */
  &.large { font-size: 21px }
  &.small { font-size: 13px }

  &:hover {
    --button_blur-start-color: var(--her-pink);
    background-color: var(--button_hover-bg-color);
  }
}

.var {
  background: #faece5;
  margin: 0 0 1rem;
  padding: 1rem;
}

TypeScript

/****************************************
 * Button Shit
 ****************************************/
let buttons = document.querySelectorAll('button')
let output = document.querySelector('#output')

;[...buttons].forEach((button) => {
	button.addEventListener('mousemove', ({ offsetX, offsetY }) => {
		requestAnimationFrame(() => {
			button.setAttribute('style', `--x-pos:${offsetX}px; --y-pos:${offsetY}px;`)
		})
	})
})

/****************************************
 * Cursor Shit
 ****************************************/

type Variation ={
	selector: string
	content: string
	width?: string
}
type Variations = Array<Variation>

type Config = {
	selector?: String
	hiddenOn?: Array<String>
	delay?: Number
	fontFamily?: String
	fontSize?: String
	variations?: Variations
}

class Cursory {
	// Configurable
	selector = '#cursor'
	hiddenOn = []
	delay = 1
	fontSize = '1em'
	variations = []

	#el = null
	#elContents
	#cursorX = 0
	#cursorY = 0
	#x = 0
	#y = 0
	#isHidden = false

	constructor(config: Config) {
		Object.assign(this, config)
		this.#el = document.querySelector(this.selector)

		this.#elContents = document.createElement("div")
		this.#elContents.classList.add('contents')
		this.#el.appendChild(this.#elContents)

		this.addListeners()
		this.measureVariantContent()
		requestAnimationFrame(this.updateLoop)
	}

	addListeners = () => {
		document.body.addEventListener('mousemove', (e) => this.onMouseMove(e))
		document.documentElement.addEventListener('mouseleave', () => this.hide())
		document.documentElement.addEventListener('mouseenter', () => this.show())
	}

	onMouseMove = (e: MouseEvent) => {
		this.#cursorX = e.clientX
		this.#cursorY = e.clientY

		const target = e.target as HTMLElement
		const variation = this.variations.find(v => target.closest(v.selector))
		variation
			? this.injectContent(variation)
			: this.removeContent()
		const shouldHide = this.hiddenOn.find(h =>...