<section>
  <span class="text-ring"></span>
</section>
:root {
  --gray-0: #f8f9fa;
  --gray-1: #f1f3f5;
  --gray-2: #e9ecef;
  --gray-3: #dee2e6;
  --gray-4: #ced4da;
  --gray-5: #adb5bd;
  --gray-6: #868e96;
  --gray-7: #495057;
  --gray-8: #343a40;
  --gray-9: #212529;
  --gray-10: #16191d;
  --gray-11: #0d0f12;
  --gray-12: #030507;
  /* Surfaces */
  --text-1: var(--gray-12);
  --text-2: var(--gray-10);
  --text-3: var(--gray-8);
  --text-4: var(--gray-7);
  --surface-1: var(--gray-0);
  --surface-2: var(--gray-1);
  --surface-3: var(--gray-2);
  --surface-4: var(--gray-3);
}

@media (prefers-color-scheme: dark) {
  :root {
    --text-1: var(--gray-1);
    --text-2: var(--gray-3);
    --text-3: var(--gray-5);
    --text-4: var(--gray-6);
    --surface-1: var(--gray-10);
    --surface-2: var(--gray-9);
    --surface-3: var(--gray-8);
    --surface-4: var(--gray-7);
  }
}

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

body {
  display: grid;
  place-items: center;
  min-height: 100vh;
  font-family: 'Google Sans', sans-serif, system-ui;
  overflow: hidden;
  background: var(--surface-3);
  color: var(--text-2);
}

.text-ring {
  position: relative;
}

.text-ring [style*=--index] {
  font-weight: bold;
  font-family: monospace;
  text-transform: uppercase;
  font-size: calc(var(--font-size, 2) * 1rem);
  position: absolute;
  top: 50%;
  left: 50%;
  transform:
    translate(-50%, -50%)
    rotate(calc(360deg / var(--total) * var(--index)))
    translateY(calc(var(--radius, 5) * -1ch));
}

.dg :is(.cr.string, .cr.number) input[type=text] {
  color: white;
  line-height: 1;
}

.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;
}
import { GUI } from "https://cdn.skypack.dev/dat.gui@0.7.9"

const RING = document.querySelector('.text-ring')

const OPTIONS = {
  text: 'Your text here! • ',
  size: 2,
  radius: 5,
}

const CTRL = new GUI()

const onUpdate = () => {
  RING.innerHTML = ''
  const CHARS = OPTIONS.text.split('')
  RING.style.setProperty('--total', CHARS.length)
  RING.style.setProperty('--radius', OPTIONS.radius)
  RING.style.setProperty('--font-size', OPTIONS.size)
  const HIDDEN_CHARS = Object.assign(document.createElement('span'), {
    ariaHidden: true,
  })

  for (let c = 0; c < CHARS.length; c++) {
    HIDDEN_CHARS.innerHTML += `<span style="--index: ${c}">${CHARS[c]}</span>`
  }
  RING.appendChild(HIDDEN_CHARS)
  RING.innerHTML += `<span class="sr-only">${OPTIONS.text}</span>`
}

CTRL.add(OPTIONS, 'text').name('Text').onChange(onUpdate)
CTRL.add(OPTIONS, 'size', 1, 10, 0.1).name('Font size (rem)').onChange(onUpdate)
CTRL.add(OPTIONS, 'radius', 1, 10, 0.1).name('Radius (ch)').onChange(onUpdate)

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.