<section>
  <h1 class="ring"></h1>
</section>
@import url('https://unpkg.com/open-props/normalize.min.css');
@import url('https://unpkg.com/open-props/open-props.min.css');

*,
*:after,
*:before {
	box-sizing: border-box;
}

body {
	/* display: grid;
	place-items: center;
	align-content: center;
	overflow: hidden; */
  overflow: hidden;
	display: grid;
	place-items: center;
	min-height: 100vh;
	font-family:  'Google Sans', sans-serif, system-ui;
	accent-color: var(--red-6);
}

pre {
	padding: var(--size-2);
	overflow: auto;
}

ul:not(.closed) > .code-block {
	height: auto !important;
	line-height: 1 !important;
}

.sr-only {
	position: absolute;
	width: 1px;
	height: 1px;
	padding: 0;
	margin: -1px;
	overflow: hidden;
	clip: rect(0, 0, 0, 0);
	white-space: nowrap;
	border-width: 0;
}

*:focus-visible {
	outline-color: var(--red-6);
}

input::selection {
	background: var(--red-6);
	color: var(--text-1);
	background: hsl(0 100% 50%);
	color: hsl(0 0% 100%);
}

/*
 * To get the radius or the hypoteneuse.
 * If you know the inner angle and the length of the side
 * The end / Math.sin(innerAngle) should provide the radius
*/

section:first-of-type {
	min-height: 250px;
	height: calc(2 * var(--buffer));
	aspect-ratio: 1;
	display: grid;
	place-items: center;
}

section:last-of-type {
	display: grid;
	gap: 2rem;
}

form {
	display: inline-grid;
	grid-template-columns: auto auto;
	gap: 0.5rem 1rem;
	justify-content: center;
}

:where(p,ul,ol,dl,h6) {
  font-size: var(--font-size-0);
}

.ring {
/* 	--character-width: 1ch; */
	--inner-angle: calc((360 / var(--char-count)) * 1deg);
	--character-width: 1;
	font-family: monospace;
	text-transform: uppercase;
	font-size: calc(var(--font-size, 1) * 1rem);
	position: relative;
}
@media (prefers-reduced-motion: no-preference) {
  .ring {
  	animation: spin 6s infinite linear;  
  }
  @keyframes spin {
    to {
      rotate: -360deg;
    }
  }
}

.char {
	display: inline-block;
	position: absolute;
	top: 50%;
	left: 50%;
/* 	line-height: 1; */
	transform:
		translate(-50%, -50%)
		rotate(calc(var(--inner-angle) * var(--char-index)))
		translateY(var(--radius));
}
import { GUI } from 'https://cdn.skypack.dev/dat.gui'

const canTrig = CSS.supports('(top: calc(sin(1) * 1px))')
const HEADING = document.querySelector('h1')
const CTRL = new GUI()

const OPTIONS = {
  SPACING: 1,
  SIZE: 1,
  TEXT: 'Made by Jhey with CSS Trig functions • '
}

const genCode = () => `
.ring {
  --char-count: ${HEADING.children.length};
  --inner-angle: calc((360 / var(--char-count, ${HEADING.children.length})) * 1deg);
  --character-width: ${OPTIONS.SPACING.toFixed(1)};
  --radius: calc((var(--character-width, ${OPTIONS.SPACING.toFixed(1)}) / ${canTrig ? 'sin(var(--inner-angle))' : Math.sin(
          360 / HEADING.children.length / (180 / Math.PI)
        )}) * -1ch);
  --font-size: ${OPTIONS.SIZE.toFixed(1)}rem;
  font-family: monospace;
  text-transform: uppercase;
  font-size: calc(var(--font-size, 1) * 1rem);
  animation: rotation 6s infinite linear;
  position: relative;
}
.char {
  display: inline-block;
  position: absolute;
  top: 50%;
  left: 50%;
  transform:
    translate(-50%, -50%)
    rotate(calc(var(--inner-angle) * var(--char-index)))
    translateY(var(--radius));
}
@keyframes rotation {
  to {
    rotate: -360deg;
  }
}
  `.trim()

const CSS_CODE = Object.assign(document.createElement('li'), {
  className: 'code-block',
  innerHTML: `<pre><code>${genCode()}</code></pre>`
})

const HTML_CODE = Object.assign(document.createElement('li'), {
  className: 'code-block',
  innerHTML: `<pre><code>${HEADING.outerHTML}</code></pre>`
})

const onUpdate = () => {
  // Make the ring text
  const text = OPTIONS.TEXT
  // 1. Take the text and split it into spans...
  const chars = text.split('')
  HEADING.innerHTML = ''
  HEADING.style.setProperty('--char-count', chars.length)

  for (let c = 0; c < chars.length; c++) {
    HEADING.innerHTML += `<span aria-hidden="true" class="char" style="--char-index: ${c};">${chars[c]}</span>`
  }
  HEADING.innerHTML += `<span class="sr-only">${OPTIONS.TEXT}</span>`
  // Set the styles
  HEADING.style.setProperty('--font-size', OPTIONS.SIZE)
  HEADING.style.setProperty('--character-width', OPTIONS.SPACING)
  HEADING.style.setProperty(
    '--radius',
    canTrig
      ? 'calc((var(--character-width) / sin(var(--inner-angle))) * -1ch'
      : `calc(
      (${OPTIONS.SPACING} / ${Math.sin(
          360 / HEADING.children.length / (180 / Math.PI)
        )})
      * -1ch
    )`
  )

  if (HEADING.children.length > 3) {
    document.documentElement.style.setProperty(
      '--buffer',
      canTrig
        ? `calc((${OPTIONS.SPACING} / sin(${
            360 / HEADING.children.length
          }deg)) * ${OPTIONS.SIZE}rem)`
        : `calc((${OPTIONS.SPACING} / ${Math.sin(
            360 / HEADING.children.length / (180 / Math.PI)
          )}) * ${OPTIONS.SIZE}rem)`
    )
  }
  CSS_CODE.innerHTML = `<pre><code>${genCode()}</code></pre>`
  // HTML_CODE.innerText = `<pre><code>${HEADING.outerHTML}</code></pre>`
}

CTRL.add(OPTIONS, 'SPACING', 0.5, 2, 0.1).name('Spacing (ch)').onChange(onUpdate)
CTRL.add(OPTIONS, 'SIZE', 0.25, 2, 0.1).name('Size (rem)').onChange(onUpdate)
CTRL.add(OPTIONS, 'TEXT').name('Text').onChange(onUpdate)
// const MARKUP = CTRL.addFolder('HTML')
const STYLES = CTRL.addFolder('CSS')

STYLES.domElement.querySelector('ul').appendChild(CSS_CODE)
// MARKUP.domElement.querySelector('ul').appendChild(HTML_CODE)

// const CODE = document.querySelector('#code')

onUpdate()
View Compiled
Run Pen

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.