HTML CSS Tricks

Vertical Wavy Text

RESET

W

a

v

y

 

T

e

x

t

This vertical wavy text is referred to GeeksforGeeks’s How to make a vertical wavy text line using HTML and CSS ? But I add text gradient to it. If you don’t need the wavy and only need the text gradient, you can simply use TailwindCss’s Gradient Color Stops

tsx

Copy

 <div className="text-4xl py-5 wavy">
  {gradientText(
    Array.from('Wavy Text'),
    colorStringToRGB(colors.startColor),
    colorStringToRGB(colors.endColor),
  ).map((stop, index) => (
    <p
      key={index}
      className="inline-block uppercase tracking-widest"
      style={{
        ['--i' as any]: index + 1,
        ['--r1' as any]: stop.startColor.red,
        ['--g1' as any]: stop.startColor.green,
        ['--b1' as any]: stop.startColor.blue,
        ['--r2' as any]: stop.endColor.red,
        ['--g2' as any]: stop.endColor.green,
        ['--b2' as any]: stop.endColor.blue,
      }}
    >
      {stop.char === ' ' ? <>&nbsp;</> : stop.char}
    </p>
  ))}
</div>

CSS

Copy

.wavy {
  position: relative;
}

.wavy p {
  display: inline-block;
  animation: animate 2s ease-in-out infinite;
  background: linear-gradient(
    to right,
    rgb(var(--r1), var(--g1), var(--b1)),
    rgb(var(--r2), var(--g2), var(--b2))
  );
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
  animation-delay: calc(0.1s * var(--i));
}

@keyframes animate {
  0% {
    transform: translateY(0px);
  }

  20% {
    transform: translateY(-20px);
  }

  40%,
  100% {
    transform: translateY(0px);
  }
}

TypeScript

Copy

type Enumerate<
  N extends number,
  Acc extends number[] = [],
> = Acc['length'] extends N
  ? Acc[number]
  : Enumerate<N, [...Acc, Acc['length']]>;

type Range<F extends number, T extends number> = Exclude<
  Enumerate<T>,
  Enumerate<F>
>;

type TColorRed = Range<0, 256>;
type TColorGreen = Range<0, 256>;
type TColorBlue = Range<0, 256>;

interface IRGB {
  red: TColorRed;
  green: TColorGreen;
  blue: TColorBlue;
}

const colorCalculator = (
  color1: IRGB,
  color2: IRGB,
  percentage1: number,
  percentage2: number,
): IRGB => {
  return {
    red: Math.round(
      color1.red * percentage1 + color2.red * percentage2,
    ) as TColorRed,
    green: Math.round(
      color1.green * percentage1 + color2.green * percentage2,
    ) as TColorGreen,
    blue: Math.round(
      color1.blue * percentage1 + color2.blue * percentage2,
    ) as TColorBlue,
  };
};

const gradientText = (text: string[], start: IRGB, end: IRGB) => {
  const steps = 1 / text.length;

  return text.map((char, index) => {
    const ratio1 = steps * (text.length - index);
    const ratio2 = steps * (text.length - index - 1);
    const startColor = colorCalculator(start, end, ratio1, 1 - ratio1);
    const endColor = colorCalculator(start, end, ratio2, 1 - ratio2);

    return { startColor, endColor, char };
  });
};

const colorStringToRGB = (color: string): IRGB => {
  const result = /^#?([a-fd]{2})([a-fd]{2})([a-fd]{2})$/i.exec(color);

  return {
    red: parseInt(result ? result[1] : '0', 16),
    green: parseInt(result ? result[2] : '0', 16),
    blue: parseInt(result ? result[3] : '0', 16),
  } as IRGB;
};
LinkedinGitHub