refactor(text-animations): convert GlitchText from CSS file to Tailwind classes

- Remove external GlitchText.css file dependency
- Convert all styles to Tailwind utility classes
- Maintain identical functionality and visual effects
- Improve component maintainability and consistency
This commit is contained in:
Sahil David
2025-07-12 16:40:29 +01:00
parent d2a5837b74
commit 2fb67d9757
2 changed files with 145 additions and 123 deletions

View File

@@ -1,120 +0,0 @@
.glitch {
color: #fff;
font-size: clamp(2rem, 10vw, 8rem);
white-space: nowrap;
font-weight: 900;
position: relative;
margin: 0 auto;
user-select: none;
cursor: pointer;
}
.glitch::after,
.glitch::before {
content: attr(data-text);
position: absolute;
top: 0;
color: #fff;
background-color: #060010;
overflow: hidden;
clip-path: inset(0 0 0 0);
}
.glitch:not(.enable-on-hover)::after {
left: 10px;
text-shadow: var(--after-shadow, -10px 0 red);
animation: animate-glitch var(--after-duration, 3s) infinite linear alternate-reverse;
}
.glitch:not(.enable-on-hover)::before {
left: -10px;
text-shadow: var(--before-shadow, 10px 0 cyan);
animation: animate-glitch var(--before-duration, 2s) infinite linear alternate-reverse;
}
.glitch.enable-on-hover::after,
.glitch.enable-on-hover::before {
content: '';
opacity: 0;
animation: none;
}
.glitch.enable-on-hover:hover::after {
content: attr(data-text);
opacity: 1;
left: 10px;
text-shadow: var(--after-shadow, -10px 0 red);
animation: animate-glitch var(--after-duration, 3s) infinite linear alternate-reverse;
}
.glitch.enable-on-hover:hover::before {
content: attr(data-text);
opacity: 1;
left: -10px;
text-shadow: var(--before-shadow, 10px 0 cyan);
animation: animate-glitch var(--before-duration, 2s) infinite linear alternate-reverse;
}
@keyframes animate-glitch {
0% {
clip-path: inset(20% 0 50% 0);
}
5% {
clip-path: inset(10% 0 60% 0);
}
10% {
clip-path: inset(15% 0 55% 0);
}
15% {
clip-path: inset(25% 0 35% 0);
}
20% {
clip-path: inset(30% 0 40% 0);
}
25% {
clip-path: inset(40% 0 20% 0);
}
30% {
clip-path: inset(10% 0 60% 0);
}
35% {
clip-path: inset(15% 0 55% 0);
}
40% {
clip-path: inset(25% 0 35% 0);
}
45% {
clip-path: inset(30% 0 40% 0);
}
50% {
clip-path: inset(20% 0 50% 0);
}
55% {
clip-path: inset(10% 0 60% 0);
}
60% {
clip-path: inset(15% 0 55% 0);
}
65% {
clip-path: inset(25% 0 35% 0);
}
70% {
clip-path: inset(30% 0 40% 0);
}
75% {
clip-path: inset(40% 0 20% 0);
}
80% {
clip-path: inset(20% 0 50% 0);
}
85% {
clip-path: inset(10% 0 60% 0);
}
90% {
clip-path: inset(15% 0 55% 0);
}
95% {
clip-path: inset(25% 0 35% 0);
}
100% {
clip-path: inset(30% 0 40% 0);
}
}

View File

@@ -7,7 +7,6 @@
<script setup lang="ts"> <script setup lang="ts">
import { computed } from 'vue'; import { computed } from 'vue';
import type { CSSProperties } from 'vue'; import type { CSSProperties } from 'vue';
import './GlitchText.css';
interface GlitchTextProps { interface GlitchTextProps {
children: string; children: string;
@@ -40,8 +39,151 @@ const inlineStyles = computed(
}) })
); );
const baseClasses = [
// Base styling
'text-white',
'font-black',
'whitespace-nowrap',
'relative',
'mx-auto',
'select-none',
'cursor-pointer',
'text-[clamp(2rem,10vw,8rem)]',
// Pseudo-elements base
'before:content-[attr(data-text)]',
'before:absolute',
'before:top-0',
'before:text-white',
'before:bg-[#060010]',
'before:overflow-hidden',
'before:[clip-path:inset(0_0_0_0)]',
'after:content-[attr(data-text)]',
'after:absolute',
'after:top-0',
'after:text-white',
'after:bg-[#060010]',
'after:overflow-hidden',
'after:[clip-path:inset(0_0_0_0)]'
];
const normalGlitchClasses = [
// After pseudo-element for normal mode
'after:left-[10px]',
'after:[text-shadow:var(--after-shadow,-10px_0_red)]',
'after:[animation:animate-glitch_var(--after-duration,3s)_infinite_linear_alternate-reverse]',
// Before pseudo-element for normal mode
'before:left-[-10px]',
'before:[text-shadow:var(--before-shadow,10px_0_cyan)]',
'before:[animation:animate-glitch_var(--before-duration,2s)_infinite_linear_alternate-reverse]'
];
const hoverOnlyClasses = [
// Hide pseudo-elements by default
'before:content-[""]',
'before:opacity-0',
'before:[animation:none]',
'after:content-[""]',
'after:opacity-0',
'after:[animation:none]',
// Show and animate on hover
'hover:before:content-[attr(data-text)]',
'hover:before:opacity-100',
'hover:before:left-[-10px]',
'hover:before:[text-shadow:var(--before-shadow,10px_0_cyan)]',
'hover:before:[animation:animate-glitch_var(--before-duration,2s)_infinite_linear_alternate-reverse]',
'hover:after:content-[attr(data-text)]',
'hover:after:opacity-100',
'hover:after:left-[10px]',
'hover:after:[text-shadow:var(--after-shadow,-10px_0_red)]',
'hover:after:[animation:animate-glitch_var(--after-duration,3s)_infinite_linear_alternate-reverse]'
];
const computedClasses = computed(() => { const computedClasses = computed(() => {
const hoverClass = props.enableOnHover ? 'enable-on-hover' : ''; const classes = [...baseClasses];
return `glitch ${hoverClass} ${props.className}`.trim();
if (props.enableOnHover) {
classes.push(...hoverOnlyClasses);
} else {
classes.push(...normalGlitchClasses);
}
if (props.className) {
classes.push(props.className);
}
return classes.join(' ');
}); });
</script> </script>
<style>
@keyframes animate-glitch {
0% {
clip-path: inset(20% 0 50% 0);
}
5% {
clip-path: inset(10% 0 60% 0);
}
10% {
clip-path: inset(15% 0 55% 0);
}
15% {
clip-path: inset(25% 0 35% 0);
}
20% {
clip-path: inset(30% 0 40% 0);
}
25% {
clip-path: inset(40% 0 20% 0);
}
30% {
clip-path: inset(10% 0 60% 0);
}
35% {
clip-path: inset(15% 0 55% 0);
}
40% {
clip-path: inset(25% 0 35% 0);
}
45% {
clip-path: inset(30% 0 40% 0);
}
50% {
clip-path: inset(20% 0 50% 0);
}
55% {
clip-path: inset(10% 0 60% 0);
}
60% {
clip-path: inset(15% 0 55% 0);
}
65% {
clip-path: inset(25% 0 35% 0);
}
70% {
clip-path: inset(30% 0 40% 0);
}
75% {
clip-path: inset(40% 0 20% 0);
}
80% {
clip-path: inset(20% 0 50% 0);
}
85% {
clip-path: inset(10% 0 60% 0);
}
90% {
clip-path: inset(15% 0 55% 0);
}
95% {
clip-path: inset(25% 0 35% 0);
}
100% {
clip-path: inset(30% 0 40% 0);
}
}
</style>