Introducing Nuvyx UI v1.0.0

Glitch Button

Cyberpunk-inspired buttons with glitch effects that can be triggered on hover or click.

Always Glitching

ACTIVE
ACTIVE
ACTIVE
ACTIVE

Custom Colors

nuvyx UI

Download Button

DOWNLOAD
SHOW INTERACTIVE DEMO

Installation Guide

1

Install Dependencies

Tailwind Merge

npm install tailwind-merge
2

Copy Component Code

Glitch Button.tsx
TypeScript
1"use client";
2import React, { useState, useEffect, useRef } from "react";
3import { twMerge } from "tailwind-merge";
4
5interface GlitchButtonProps {
6  children: React.ReactNode;
7  className?: string;
8  onClick?: () => void;
9  glitchIntensity?: "low" | "medium" | "high";
10  glitchOnHover?: boolean;
11  glitchAlways?: boolean;
12  glitchColors?: {
13    primary?: string;
14    secondary?: string;
15  };
16  borderColor?: string;
17}
18
19export const GlitchButton: React.FC<GlitchButtonProps> = ({
20  children,
21  className = "",
22  onClick,
23  glitchIntensity = "medium",
24  glitchOnHover = true,
25  glitchAlways = false,
26  glitchColors = {
27    primary: "#ef00ef",
28    secondary: "#00ffff",
29  },
30  borderColor = "white",
31}) => {
32  const [isHovering, setIsHovering] = useState(false);
33  const [isClicked, setIsClicked] = useState(false);
34  const buttonRef = useRef<HTMLDivElement>(null);
35  const [bgColorClass, setBgColorClass] = useState("bg-gray-900");
36  const [textColorClass, setTextColorClass] = useState("text-white");
37  const [isRounded, setIsRounded] = useState(false);
38
39  useEffect(() => {
40    const bgMatch = className.match(/bg-[a-z]+-[0-9]+/);
41    if (bgMatch) {
42      setBgColorClass(bgMatch[0]);
43    }
44    const textMatch = className.match(/text-[a-z]+-[0-9]+/);
45    if (textMatch) {
46      setTextColorClass(textMatch[0]);
47    }
48    setIsRounded(
49      className.includes("rounded") ||
50        className.includes("rounded-lg") ||
51        className.includes("rounded-md") ||
52        className.includes("rounded-sm") ||
53        className.includes("rounded-full")
54    );
55  }, [className]);
56
57  useEffect(() => {
58    const intensityMultiplier =
59      glitchIntensity === "low" ? 0.5 : glitchIntensity === "high" ? 2 : 1;
60
61    const styleId = "glitch-button-styles";
62    if (!document.getElementById(styleId)) {
63      const styleTag = document.createElement("style");
64      styleTag.id = styleId;
65      styleTag.innerHTML = `
66        @keyframes glitchFx1 {
67          0%, 100% { transform: translateX(0); }
68          10% { transform: translateX(0); }
69          20% { transform: translateX(${-4 * intensityMultiplier}px); }
70          30% { transform: translateX(0); }
71          40% { transform: translateX(${12 * intensityMultiplier}px); }
72          50% { transform: translateX(${8 * intensityMultiplier}px); }
73          60% { transform: translateX(${24 * intensityMultiplier}px); }
74          70% { transform: translateX(${2 * intensityMultiplier}px); }
75          80% { transform: translateX(${-4 * intensityMultiplier}px); }
76          90% { transform: translateX(0); }
77        }
78        @keyframes glitchFx2 {
79          0% { transform: translateX(${4 * intensityMultiplier}px); }
80          10% { transform: translateX(${-12 * intensityMultiplier}px); }
81          20% { transform: translateX(${-2 * intensityMultiplier}px); }
82          30% { transform: translateX(${1 * intensityMultiplier}px); }
83          40%, 50%, 60%, 70% { transform: translateX(0); }
84          80% { transform: translateX(${4 * intensityMultiplier}px); }
85          90% { transform: translateX(${-2 * intensityMultiplier}px); }
86          100% { transform: translateX(${-15 * intensityMultiplier}px); }
87        }
88        @keyframes glitchFx3 {
89          0% { transform: translateX(${4 * intensityMultiplier}px); }
90          10% { transform: translateX(0); }
91          20% { transform: translateX(${4 * intensityMultiplier}px); }
92          30% { transform: translateX(0); }
93          40% { transform: translateX(${-4 * intensityMultiplier}px); }
94          50% { transform: translateX(0); }
95          60% { transform: translateX(${-4 * intensityMultiplier}px); }
96          70% { transform: translateX(0); }
97          80% { transform: translateX(${12 * intensityMultiplier}px); }
98          90% { transform: translateX(0); }
99          100% { transform: translateX(${-12 * intensityMultiplier}px); }
100        }
101        
102        @keyframes glitchSkew {
103          0% { transform: skew(0deg); }
104          10% { transform: skew(${1 * intensityMultiplier}deg); }
105          20% { transform: skew(${-2 * intensityMultiplier}deg); }
106          30% { transform: skew(${1.5 * intensityMultiplier}deg); }
107          40% { transform: skew(${-1 * intensityMultiplier}deg); }
108          50% { transform: skew(${2 * intensityMultiplier}deg); }
109          60% { transform: skew(${-1.5 * intensityMultiplier}deg); }
110          70% { transform: skew(${1.5 * intensityMultiplier}deg); }
111          80% { transform: skew(${-2 * intensityMultiplier}deg); }
112          90% { transform: skew(${1 * intensityMultiplier}deg); }
113          100% { transform: skew(0deg); }
114        }
115        
116        @keyframes flickerAnimation {
117          0% { opacity: 1; }
118          19% { opacity: 1; }
119          20% { opacity: 0; }
120          21% { opacity: 1; }
121          49% { opacity: 1; }
122          50% { opacity: 0.7; }
123          51% { opacity: 1; }
124          79% { opacity: 1; }
125          80% { opacity: 0.8; }
126          81% { opacity: 1; }
127          100% { opacity: 1; }
128        }
129        
130        @keyframes clickGlitch {
131          0%, 100% { clip-path: inset(0 0 0 0); }
132          20% { clip-path: inset(${20 * intensityMultiplier}% 0 ${
133        40 * intensityMultiplier
134      }% 0); }
135          40% { clip-path: inset(${50 * intensityMultiplier}% 0 ${
136        20 * intensityMultiplier
137      }% 0); }
138          60% { clip-path: inset(${30 * intensityMultiplier}% 0 ${
139        60 * intensityMultiplier
140      }% 0); }
141          80% { clip-path: inset(${10 * intensityMultiplier}% 0 ${
142        70 * intensityMultiplier
143      }% 0); }
144        }
145        
146        .glitch-layer-1 {
147          animation: glitchFx1 ${800 / intensityMultiplier}ms infinite step-end;
148        }
149        .glitch-layer-2 {
150          animation: glitchFx2 ${900 / intensityMultiplier}ms infinite step-end;
151        }
152        .glitch-layer-3 {
153          animation: glitchFx3 ${
154            1000 / intensityMultiplier
155          }ms infinite step-end;
156        }
157        .glitch-skew {
158          animation: glitchSkew ${
159            1200 / intensityMultiplier
160          }ms infinite step-end;
161        }
162        .flicker-animation {
163          animation: flickerAnimation 2s infinite;
164        }
165        .click-glitch {
166          animation: clickGlitch 500ms step-end forwards;
167        }
168      `;
169      document.head.appendChild(styleTag);
170    }
171  }, [glitchIntensity]);
172
173  const handleMouseEnter = () => {
174    setIsHovering(true);
175  };
176
177  const handleMouseLeave = () => {
178    setIsHovering(false);
179  };
180
181  const handleClick = () => {
182    if (onClick) {
183      onClick();
184    }
185    setIsClicked(true);
186    setTimeout(() => setIsClicked(false), 500);
187  };
188
189  const showGlitch = glitchAlways || (glitchOnHover && isHovering) || isClicked;
190
191  const borderStyle = {
192    boxShadow: `inset ${borderColor} 5px 5px 0px, inset ${borderColor} -5px 5px 0px, inset ${borderColor} 5px -5px 0px, inset ${borderColor} -5px -5px 0px`,
193  };
194
195  const containerClasses = twMerge(
196    "relative cursor-pointer font-mono overflow-hidden",
197    "text-4xl",
198    "bg-gray-900",
199    "text-white",
200    "p-4 m-1.5",
201    "group",
202    "hover:shadow-none",
203    "hover:bg-gradient-to-r hover:from-fuchsia-600 hover:via-white hover:to-cyan-400",
204    isClicked ? "click-glitch" : "",
205    className
206  );
207
208  const textShadowStyle = {
209    textShadow: `
210      -1.5px -1.5px 0 ${glitchColors.primary}, 
211      1.5px 1.5px 0 ${glitchColors.secondary}
212    `,
213  };
214
215  const glitchLayerShadowTop = {
216    boxShadow: `inset ${borderColor} 5px 5px 0px, inset ${borderColor} -5px 0px 0px`,
217  };
218
219  const glitchLayerShadowMiddle = {
220    boxShadow: `inset ${borderColor} 5px 0px 0px, inset ${borderColor} -5px 0px 0px`,
221  };
222
223  const glitchLayerShadowBottom = {
224    boxShadow: `inset ${borderColor} 5px -5px 0px, inset ${borderColor} -5px 0px 0px`,
225  };
226
227  return (
228    <div
229      ref={buttonRef}
230      onClick={handleClick}
231      onMouseEnter={handleMouseEnter}
232      onMouseLeave={handleMouseLeave}
233      className={containerClasses}
234      style={borderStyle}
235    >
236      <span
237        className={`
238        block
239        ${showGlitch ? "opacity-0" : "opacity-100"} 
240        transition-opacity
241        ${glitchAlways ? "flicker-animation" : ""}
242      `}
243      >
244        {children}
245      </span>
246
247      {showGlitch && (
248        <div
249          className={`
250          absolute inset-0 
251          overflow-hidden 
252          ${isClicked ? "glitch-skew" : ""}
253        `}
254        >
255          <div
256            className={`
257            absolute left-0 w-full h-1/3 top-0 
258            ${bgColorClass}
259            ${isRounded ? "rounded-t-lg" : ""}
260            overflow-hidden
261            glitch-layer-1
262          `}
263            style={glitchLayerShadowTop}
264          >
265            <div
266              className={`
267                absolute w-full ${textColorClass}
268                top-0 left-0 right-0
269                flex items-center justify-center
270                h-full
271              `}
272              style={textShadowStyle}
273            >
274              <div className="transform translate-y-0 mt-[45px]">
275                {children}
276              </div>
277            </div>
278          </div>
279
280          <div
281            className={`
282            absolute left-0 w-full h-1/3 top-1/3 
283            ${bgColorClass}
284            overflow-hidden
285            glitch-layer-2
286          `}
287            style={glitchLayerShadowMiddle}
288          >
289            <div
290              className={`
291                absolute w-full ${textColorClass}
292                top-0 left-0 right-0
293                flex items-center justify-center
294                h-[300%] -translate-y-1/3
295              `}
296              style={textShadowStyle}
297            >
298              <div>{children}</div>
299            </div>
300          </div>
301
302          <div
303            className={`
304            absolute left-0 w-full h-1/3 top-2/3 
305            ${bgColorClass}
306            ${isRounded ? "rounded-b-lg" : ""}
307            overflow-hidden
308            glitch-layer-3
309          `}
310            style={glitchLayerShadowBottom}
311          >
312            <div
313              className={`
314                absolute w-full ${textColorClass}
315                top-0 left-0 right-0
316                flex items-center justify-center
317                h-[300%] -translate-y-2/3
318              `}
319              style={textShadowStyle}
320            >
321              <div>{children}</div>
322            </div>
323          </div>
324        </div>
325      )}
326    </div>
327  );
328};
329
3

Final Steps

Update Import Paths

Make sure to update the import paths in the component code to match your project structure. For example, change @/components/ui/button to match your UI components location.

Props

NameTypeDefaultDescription
glitchIntensitystring"medium"The intensity of the glitch effect. Possible values: "low", "medium", "high".
glitchOnHoverbooleantrueWhether to trigger glitch effects when hovering over the button.
borderColorstring"white"Custom border color for the glitch effect.
glitchAlwaysbooleanfalseWhether to continuously display the glitch effect regardless of hover/click state.
glitchColorsobject{ primary: '#ef00ef', secondary: '#00ffff' }Custom colors for the glitch effect. The object should include properties: primary and secondary.
classNamestring""Additional CSS classes to apply. Background and text colors are detected from these classes.
onClickfunctionundefinedFunction to call when the button is clicked.
childrenReact.ReactNodeundefinedContent of the button.

Examples

SYSTEM ACCESS
SYSTEM ACTIVE
SYSTEM ACTIVE
SYSTEM ACTIVE
SYSTEM ACTIVE
DOWNLOAD
WARNING
SUBSCRIBE
ENTER THE VOID
MORE INFO