add sliders styles for vintage TV interface; update index.css to include new sliders stylesheet
This commit is contained in:
		
							
								
								
									
										281
									
								
								src/App.tsx
									
									
									
									
									
								
							
							
						
						
									
										281
									
								
								src/App.tsx
									
									
									
									
									
								
							| @@ -1,4 +1,4 @@ | ||||
| import React from 'react'; | ||||
| import React, { useRef, useState, useEffect } from 'react'; | ||||
| import { Terminal } from 'lucide-react'; | ||||
| import TerminalButton from './components/TerminalButton'; | ||||
| import ProfileContent from './components/ProfileContent'; | ||||
| @@ -8,15 +8,31 @@ import SkillsContent from './components/SkillsContent'; | ||||
| import ResumeContent from './components/ResumeContent'; | ||||
| import PostsContent from './components/PostsContent'; | ||||
|  | ||||
| import './styles/sliders.css'; // Import the new sliders CSS | ||||
|  | ||||
| type SliderRefType = React.RefObject<HTMLDivElement>; | ||||
|  | ||||
| function App() { | ||||
|   const [activeSection, setActiveSection] = React.useState('about'); | ||||
|   const [isBooting, setIsBooting] = React.useState(true); | ||||
|   const [bootPhase, setBootPhase] = React.useState(0); | ||||
|    | ||||
|   // New state for sliders | ||||
|   const [greenIntensity, setGreenIntensity] = useState(50); // 0 to 100 | ||||
|   const [glitchIntensity, setGlitchIntensity] = useState(25); // 0 to 100 | ||||
|    | ||||
|   // New state for glitch effects | ||||
|   const [flickerOpacity, setFlickerOpacity] = useState(1); // 1 is normal, less than 1 creates flicker | ||||
|   const [showScanline, setShowScanline] = useState(false); // Controls the high-contrast scanline | ||||
|   const [scanlinePosition, setScanlinePosition] = useState(0); // Position of scanline (0-100%) | ||||
|    | ||||
|   // Refs for the slider thumbs | ||||
|   const greenSliderRef = useRef<HTMLDivElement>(null); | ||||
|   const glitchSliderRef = useRef<HTMLDivElement>(null); | ||||
|  | ||||
|   // TV Boot-up sequence | ||||
|   React.useEffect(() => { | ||||
|     if (!isBooting) return; | ||||
|  | ||||
|     // Phase 0: Initial black screen (0ms) | ||||
|     // Phase 1: Quick white flash (100ms) | ||||
|     // Phase 2: Static noise (600ms) | ||||
| @@ -40,10 +56,188 @@ function App() { | ||||
|     }; | ||||
|   }, [isBooting]); | ||||
|  | ||||
|   // Screen flickering effect based on glitch intensity | ||||
|   useEffect(() => { | ||||
|     if (glitchIntensity <= 30) { | ||||
|       setFlickerOpacity(1); // No flickering | ||||
|       return; | ||||
|     } | ||||
|      | ||||
|     let flickerInterval: number; | ||||
|     const flickerEffect = () => { | ||||
|       // The higher the glitch intensity, the more frequent the flickers | ||||
|       const flickerFrequency = Math.max(300, 1500 - (glitchIntensity * 10)); | ||||
|        | ||||
|       flickerInterval = window.setInterval(() => { | ||||
|         // Intensity affects how dim the screen gets during flickers | ||||
|         const minOpacity = Math.max(0.7, 0.95 - (glitchIntensity / 100) * 0.5); | ||||
|          | ||||
|         // Create random flickering effect | ||||
|         const randomFlicker = Math.random() * (1 - minOpacity) + minOpacity; | ||||
|         setFlickerOpacity(randomFlicker); | ||||
|          | ||||
|         // Reset back to normal after a short delay | ||||
|         setTimeout(() => { | ||||
|           setFlickerOpacity(1); | ||||
|         }, 50 + Math.random() * 100); | ||||
|       }, flickerFrequency); | ||||
|     }; | ||||
|      | ||||
|     flickerEffect(); | ||||
|      | ||||
|     return () => { | ||||
|       clearInterval(flickerInterval); | ||||
|     }; | ||||
|   }, [glitchIntensity]); | ||||
|    | ||||
|   // Horizontal scanline effect for higher glitch intensity | ||||
|   useEffect(() => { | ||||
|     if (glitchIntensity <= 50) { | ||||
|       setShowScanline(false); | ||||
|       return; | ||||
|     } | ||||
|      | ||||
|     let scanlineInterval: number; | ||||
|     const scanlineEffect = () => { | ||||
|       // The higher the intensity, the more frequent the scanline appears | ||||
|       const scanlineFrequency = Math.max(1000, 5000 - (glitchIntensity * 40)); | ||||
|        | ||||
|       scanlineInterval = window.setInterval(() => { | ||||
|         // Show the scanline | ||||
|         setShowScanline(true); | ||||
|          | ||||
|         // Animate the scanline movement | ||||
|         const duration = 300 + Math.random() * 700; // 300-1000ms | ||||
|         const startTime = Date.now(); | ||||
|          | ||||
|         const animateScanline = () => { | ||||
|           const elapsed = Date.now() - startTime; | ||||
|           const progress = Math.min(1, elapsed / duration); | ||||
|            | ||||
|           setScanlinePosition(progress * 100); | ||||
|            | ||||
|           if (progress < 1) { | ||||
|             requestAnimationFrame(animateScanline); | ||||
|           } else { | ||||
|             // Hide the scanline when animation completes | ||||
|             setShowScanline(false); | ||||
|           } | ||||
|         }; | ||||
|          | ||||
|         requestAnimationFrame(animateScanline); | ||||
|       }, scanlineFrequency); | ||||
|     }; | ||||
|      | ||||
|     scanlineEffect(); | ||||
|      | ||||
|     return () => { | ||||
|       clearInterval(scanlineInterval); | ||||
|     }; | ||||
|   }, [glitchIntensity]); | ||||
|  | ||||
|   // Function to handle slider dragging | ||||
|   const handleSliderDrag = ( | ||||
|     event: React.MouseEvent,  | ||||
|     sliderRef: SliderRefType, | ||||
|     setValueFn: React.Dispatch<React.SetStateAction<number>> | ||||
|   ) => { | ||||
|     event.preventDefault(); | ||||
|      | ||||
|     if (!sliderRef.current) return; | ||||
|      | ||||
|     const track = sliderRef.current.parentElement; | ||||
|     if (!track) return; | ||||
|      | ||||
|     const trackRect = track.getBoundingClientRect(); | ||||
|     const trackHeight = trackRect.height; | ||||
|     const trackTop = trackRect.top; | ||||
|      | ||||
|     const handleMouseMove = (moveEvent: MouseEvent) => { | ||||
|       const y = moveEvent.clientY; | ||||
|       let percentage = 100 - (((y - trackTop) / trackHeight) * 100); | ||||
|        | ||||
|       // Clamp value between 0 and 100 | ||||
|       percentage = Math.min(100, Math.max(0, percentage)); | ||||
|        | ||||
|       setValueFn(percentage); | ||||
|     }; | ||||
|      | ||||
|     const handleMouseUp = () => { | ||||
|       document.removeEventListener('mousemove', handleMouseMove); | ||||
|       document.removeEventListener('mouseup', handleMouseUp); | ||||
|     }; | ||||
|      | ||||
|     document.addEventListener('mousemove', handleMouseMove); | ||||
|     document.addEventListener('mouseup', handleMouseUp); | ||||
|   }; | ||||
|  | ||||
|   // Calculate green color based on intensity | ||||
|   const calculateGreenBackground = () => { | ||||
|     // Map the intensity from 0-100 to appropriate color values | ||||
|     const baseIntensity = Math.floor((greenIntensity / 100) * 35); // Max 35 | ||||
|     return { | ||||
|       backgroundColor: `rgba(0, ${baseIntensity}, 0, 0.95)`, | ||||
|       backgroundImage: ` | ||||
|         linear-gradient(0deg,  | ||||
|           rgba(0, ${Math.max(5, baseIntensity + 3)}, 0, 1) 0%,  | ||||
|           rgba(0, ${Math.max(15, baseIntensity + 10)}, 0, 1) 100%), | ||||
|         radial-gradient( | ||||
|           circle at center,  | ||||
|           rgba(0, ${Math.max(20, baseIntensity + 20)}, 0, 1) 0%,  | ||||
|           rgba(0, ${baseIntensity}, 0, 1) 100% | ||||
|         ) | ||||
|       ` | ||||
|     }; | ||||
|   }; | ||||
|  | ||||
|   // Calculate glitch effects based on intensity | ||||
|   const calculateGlitchEffects = () => { | ||||
|     const whiteNoiseOpacity = 0.02 + (glitchIntensity / 100) * 0.2; | ||||
|     const chromaticAberrationAmount = (glitchIntensity / 100) * 1; | ||||
|     const scanLinesOpacity = 0.1 + (glitchIntensity / 100) * 1; | ||||
|      | ||||
|     return { | ||||
|       whiteNoiseOpacity, | ||||
|       chromaticAberrationAmount, | ||||
|       scanLinesOpacity | ||||
|     }; | ||||
|   }; | ||||
|    | ||||
|   const glitchEffects = calculateGlitchEffects(); | ||||
|  | ||||
|   return ( | ||||
|     <div className="min-h-screen bg-gray-900 flex items-center justify-center overflow-hidden"> | ||||
|       {/* TV Container with fixed proportions */} | ||||
|       <div className="w-[95%] max-w-[1400px] relative h-[90vh]"> | ||||
|         {/* Vertical Sliders - Moved outside the TV frame */} | ||||
|         <div className="tv-sliders-container"> | ||||
|           {/* Green Intensity Slider */} | ||||
|           <div className="tv-slider-wrapper"> | ||||
|             <div className="tv-slider-track"> | ||||
|               <div  | ||||
|                 ref={greenSliderRef} | ||||
|                 className="tv-slider-thumb"  | ||||
|                 style={{ bottom: `${greenIntensity}%` }} | ||||
|                 onMouseDown={(e) => handleSliderDrag(e, greenSliderRef, setGreenIntensity)} | ||||
|               ></div> | ||||
|             </div> | ||||
|             <span className="tv-slider-label">Green</span> | ||||
|           </div> | ||||
|            | ||||
|           {/* Glitch Intensity Slider */} | ||||
|           <div className="tv-slider-wrapper"> | ||||
|             <div className="tv-slider-track"> | ||||
|               <div  | ||||
|                 ref={glitchSliderRef} | ||||
|                 className="tv-slider-thumb"  | ||||
|                 style={{ bottom: `${glitchIntensity}%` }} | ||||
|                 onMouseDown={(e) => handleSliderDrag(e, glitchSliderRef, setGlitchIntensity)} | ||||
|               ></div> | ||||
|             </div> | ||||
|             <span className="tv-slider-label">Glitch</span> | ||||
|           </div> | ||||
|         </div> | ||||
|  | ||||
|         {/* TV Frame */} | ||||
|         <div className="absolute inset-0 tv-frame"> | ||||
|           {/* TV frame texture overlay */} | ||||
| @@ -87,16 +281,48 @@ function App() { | ||||
|               </> | ||||
|             )} | ||||
|              | ||||
|             {/* Static noise texture underlay */} | ||||
|             <div className="white-noise"></div> | ||||
|  | ||||
|             {/* Screen Content with fixed height */} | ||||
|             <div className="relative h-full w-full p-6 font-mono text-[#00FF00] overflow-hidden z-10 screen-content"> | ||||
|             {/* Static noise texture underlay - controlled by glitch slider */} | ||||
|             <div  | ||||
|               className="white-noise"  | ||||
|               style={{ opacity: glitchEffects.whiteNoiseOpacity }} | ||||
|             ></div> | ||||
|              | ||||
|             {/* Screen opacity flicker effect */} | ||||
|             <div  | ||||
|               className="absolute inset-0 z-45 pointer-events-none transition-opacity duration-50" | ||||
|               style={{  | ||||
|                 opacity: 1 - flickerOpacity, // Inverse of flickerOpacity to create dimming | ||||
|                 backgroundColor: 'rgba(0, 0, 0, 0.95)' // Black overlay for flickering | ||||
|               }} | ||||
|             ></div> | ||||
|              | ||||
|             {/* High-contrast horizontal scanline for high glitch intensity */} | ||||
|             {showScanline && ( | ||||
|               <div  | ||||
|                 className="absolute left-0 right-0 z-45 pointer-events-none" | ||||
|                 style={{  | ||||
|                   top: `${scanlinePosition}%`,  | ||||
|                   height: '4px', | ||||
|                   backgroundColor: 'rgba(255, 255, 255, 0.7)', | ||||
|                   boxShadow: '0 0 10px rgba(255, 255, 255, 0.7), 0 0 5px rgba(255, 255, 255, 0.5)', | ||||
|                   borderTop: '1px solid rgba(255, 255, 255, 0.9)', | ||||
|                   borderBottom: '1px solid rgba(255, 255, 255, 0.9)' | ||||
|                 }} | ||||
|               ></div> | ||||
|             )} | ||||
|              | ||||
|             {/* Screen Content with fixed height - controlled by green slider */} | ||||
|             <div  | ||||
|               className="relative h-full w-full p-6 font-mono text-[#00FF00] overflow-hidden z-10 screen-content" | ||||
|               style={{ | ||||
|                 ...calculateGreenBackground(), | ||||
|                 opacity: flickerOpacity // Apply flickering effect | ||||
|               }} | ||||
|             > | ||||
|               <div className="animate-pulse mb-4"> | ||||
|                 <Terminal className="inline-block mr-2" /> | ||||
|                 <span className="text-sm">echo "Hey there!"</span> | ||||
|               </div> | ||||
|  | ||||
|               {/* Split Screen Container with fixed height */} | ||||
|               <div className="flex gap-8 h-[calc(100%-3rem)]"> | ||||
|                 {/* Left Column - Navigation */} | ||||
| @@ -138,10 +364,8 @@ function App() { | ||||
|                     SHELL | ||||
|                   </TerminalButton> | ||||
|                 </div> | ||||
|  | ||||
|                 {/* Vertical Separator with embossed effect */} | ||||
|                 <div className="vertical-separator"></div> | ||||
|  | ||||
|                 {/* Right Column - Content with scrolling */} | ||||
|                 <div className="flex-1 overflow-y-auto pr-4 custom-scrollbar"> | ||||
|                   {activeSection === 'about' && <ProfileContent />} | ||||
| @@ -155,30 +379,45 @@ function App() { | ||||
|                 </div> | ||||
|               </div> | ||||
|             </div> | ||||
|  | ||||
|             {/* Enhanced Scan Lines Effect */} | ||||
|             <div className="scan-lines"></div> | ||||
|  | ||||
|             {/* Chromatic Aberration Effect */} | ||||
|             <div className="chromatic-aberration"></div> | ||||
|  | ||||
|             {/* Enhanced Scan Lines Effect - controlled by glitch slider */} | ||||
|             <div  | ||||
|               className="scan-lines" | ||||
|               style={{ | ||||
|                 backgroundImage: `repeating-linear-gradient( | ||||
|                   to bottom, | ||||
|                   rgba(0, 0, 0, 0) 0px, | ||||
|                   rgba(0, 0, 0, 0) 1px, | ||||
|                   rgba(0, 0, 0, ${glitchEffects.scanLinesOpacity}) 1px, | ||||
|                   rgba(0, 0, 0, ${glitchEffects.scanLinesOpacity}) 2px | ||||
|                 )` | ||||
|               }} | ||||
|             ></div> | ||||
|              | ||||
|             {/* Chromatic Aberration Effect - controlled by glitch slider */} | ||||
|             <div  | ||||
|               className="chromatic-aberration" | ||||
|               style={{ | ||||
|                 boxShadow: ` | ||||
|                   inset ${glitchEffects.chromaticAberrationAmount}px 0 0 rgba(255, 0, 0, 0.05), | ||||
|                   inset -${glitchEffects.chromaticAberrationAmount}px 0 0 rgba(0, 255, 255, 0.05) | ||||
|                 ` | ||||
|               }} | ||||
|             ></div> | ||||
|              | ||||
|             {/* Multiple Glass Reflection Layers */} | ||||
|             <div className="glass-reflection"></div> | ||||
|             <div className="glass-reflection-2"></div> | ||||
|             <div className="corner-shine"></div> | ||||
|  | ||||
|             {/* Screen Glare - Moving reflection */} | ||||
|             <div className="screen-glare"></div> | ||||
|           </div> | ||||
|         </div> | ||||
|  | ||||
|         {/* TV Controls - Skeuomorphic Knobs */} | ||||
|         <div className="absolute -bottom-12 right-20 flex space-x-8"> | ||||
|           <div className="tv-knob" style={{transform: 'rotate(-30deg)'}}></div> | ||||
|           <div className="tv-knob" style={{transform: 'rotate(15deg)'}}></div> | ||||
|           <div className="tv-knob" style={{transform: 'rotate(-10deg)'}}></div> | ||||
|         </div> | ||||
|  | ||||
|         {/* TV Speaker Grills - Enhanced */} | ||||
|         <div className="absolute -bottom-6 left-20 flex space-x-1.5"> | ||||
|           {[...Array(10)].map((_, i) => ( | ||||
| @@ -191,7 +430,6 @@ function App() { | ||||
|             ></div> | ||||
|           ))} | ||||
|         </div> | ||||
|  | ||||
|         {/* TV Brand Label */} | ||||
|         <div  | ||||
|           className="absolute -bottom-10 left-1/2 transform -translate-x-1/2 bg-gradient-to-b from-gray-700 to-gray-900 px-4 py-1 rounded" | ||||
| @@ -209,7 +447,6 @@ function App() { | ||||
|             RETRO·VISION | ||||
|           </span> | ||||
|         </div> | ||||
|  | ||||
|         {/* Power indicator light */} | ||||
|         <div className="absolute -bottom-8 right-[12%] w-3 h-3 rounded-full bg-gradient-to-b from-green-500 to-green-700"  | ||||
|           style={{ | ||||
|   | ||||
| @@ -6,4 +6,5 @@ | ||||
| @import './styles/buttons.css'; | ||||
| @import './styles/scrollbars.css'; | ||||
| @import './styles/skills.css'; | ||||
| @import './styles/matrix.css'; | ||||
| @import './styles/matrix.css'; | ||||
| @import './styles/sliders.css'; | ||||
							
								
								
									
										116
									
								
								src/styles/sliders.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										116
									
								
								src/styles/sliders.css
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,116 @@ | ||||
| /* Vintage TV Sliders */ | ||||
| .tv-sliders-container { | ||||
|   position: absolute; | ||||
|   left: -60px; /* Moved further left */ | ||||
|   top: 50%; | ||||
|   transform: translateY(-50%); | ||||
|   display: flex; | ||||
|   flex-direction: column; | ||||
|   gap: 50px; | ||||
|   z-index: 100; /* Increased z-index to ensure visibility */ | ||||
|   pointer-events: auto; /* Ensure clicks are registered */ | ||||
| } | ||||
|  | ||||
| .tv-slider-wrapper { | ||||
|   position: relative; | ||||
|   width: 40px; | ||||
|   height: 180px; | ||||
|   background: linear-gradient(90deg, #1a1a1a, #2a2a2a); | ||||
|   border-radius: 10px 0 0 10px; | ||||
|   box-shadow: | ||||
|     inset 1px 1px 3px rgba(255, 255, 255, 0.1), | ||||
|     inset -1px -1px 3px rgba(0, 0, 0, 0.5), | ||||
|     3px 3px 8px rgba(0, 0, 0, 0.6); | ||||
|   padding: 10px 0; | ||||
|   display: flex; | ||||
|   flex-direction: column; | ||||
|   align-items: center; | ||||
| } | ||||
|  | ||||
| /* Slider track */ | ||||
| .tv-slider-track { | ||||
|   position: relative; | ||||
|   width: 8px; | ||||
|   height: 140px; | ||||
|   background: #0a0a0a; | ||||
|   border-radius: 4px; | ||||
|   box-shadow: | ||||
|     inset 0 1px 3px rgba(0, 0, 0, 0.8), | ||||
|     inset 0 -1px 1px rgba(255, 255, 255, 0.05); | ||||
| } | ||||
|  | ||||
| /* Slider notches */ | ||||
| .tv-slider-track::before { | ||||
|   content: ''; | ||||
|   position: absolute; | ||||
|   left: 50%; | ||||
|   top: 5%; | ||||
|   bottom: 5%; | ||||
|   width: 2px; | ||||
|   transform: translateX(-50%); | ||||
|   background: repeating-linear-gradient( | ||||
|     to bottom, | ||||
|     rgba(255, 255, 255, 0.08) 0px, | ||||
|     rgba(255, 255, 255, 0.08) 1px, | ||||
|     transparent 1px, | ||||
|     transparent 10px | ||||
|   ); | ||||
| } | ||||
|  | ||||
| /* Slider thumb */ | ||||
| .tv-slider-thumb { | ||||
|   position: absolute; | ||||
|   width: 24px; | ||||
|   height: 12px; | ||||
|   left: 50%; | ||||
|   background: linear-gradient(to bottom, #4a4a4a, #2a2a2a); | ||||
|   border-radius: 3px; | ||||
|   transform: translateX(-50%); | ||||
|   box-shadow: | ||||
|     0 1px 2px rgba(0, 0, 0, 0.6), | ||||
|     0 -1px 1px rgba(255, 255, 255, 0.1), | ||||
|     inset 0 1px 1px rgba(255, 255, 255, 0.2), | ||||
|     inset 0 -1px 1px rgba(0, 0, 0, 0.3); | ||||
|   cursor: pointer; | ||||
|   z-index: 2; | ||||
| } | ||||
|  | ||||
| .tv-slider-thumb::before { | ||||
|   content: ''; | ||||
|   position: absolute; | ||||
|   top: 3px; | ||||
|   left: 3px; | ||||
|   right: 3px; | ||||
|   height: 2px; | ||||
|   background: rgba(255, 255, 255, 0.15); | ||||
|   border-radius: 1px; | ||||
| } | ||||
|  | ||||
| .tv-slider-thumb::after { | ||||
|   content: ''; | ||||
|   position: absolute; | ||||
|   bottom: 3px; | ||||
|   left: 3px; | ||||
|   right: 3px; | ||||
|   height: 1px; | ||||
|   background: rgba(0, 0, 0, 0.2); | ||||
|   border-radius: 1px; | ||||
| } | ||||
|  | ||||
| /* Slider labels */ | ||||
| .tv-slider-label { | ||||
|   font-family: 'Courier New', monospace; | ||||
|   font-size: 10px; | ||||
|   color: rgba(255, 255, 255, 0.5); | ||||
|   text-transform: uppercase; | ||||
|   margin-top: 8px; | ||||
|   letter-spacing: 0.5px; | ||||
|   text-shadow: 0 1px 1px rgba(0, 0, 0, 0.8); | ||||
|   white-space: nowrap; | ||||
|   transform: rotate(-90deg); | ||||
|   position: absolute; | ||||
|   bottom: -25px; | ||||
|   width: 80px; | ||||
|   text-align: center; | ||||
|   left: -20px; | ||||
| } | ||||
		Reference in New Issue
	
	Block a user