add sliders styles for vintage TV interface; update index.css to include new sliders stylesheet
This commit is contained in:
parent
144acbd355
commit
41572df5af
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 { Terminal } from 'lucide-react';
|
||||||
import TerminalButton from './components/TerminalButton';
|
import TerminalButton from './components/TerminalButton';
|
||||||
import ProfileContent from './components/ProfileContent';
|
import ProfileContent from './components/ProfileContent';
|
||||||
@ -8,15 +8,31 @@ import SkillsContent from './components/SkillsContent';
|
|||||||
import ResumeContent from './components/ResumeContent';
|
import ResumeContent from './components/ResumeContent';
|
||||||
import PostsContent from './components/PostsContent';
|
import PostsContent from './components/PostsContent';
|
||||||
|
|
||||||
|
import './styles/sliders.css'; // Import the new sliders CSS
|
||||||
|
|
||||||
|
type SliderRefType = React.RefObject<HTMLDivElement>;
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
const [activeSection, setActiveSection] = React.useState('about');
|
const [activeSection, setActiveSection] = React.useState('about');
|
||||||
const [isBooting, setIsBooting] = React.useState(true);
|
const [isBooting, setIsBooting] = React.useState(true);
|
||||||
const [bootPhase, setBootPhase] = React.useState(0);
|
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
|
// TV Boot-up sequence
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (!isBooting) return;
|
if (!isBooting) return;
|
||||||
|
|
||||||
// Phase 0: Initial black screen (0ms)
|
// Phase 0: Initial black screen (0ms)
|
||||||
// Phase 1: Quick white flash (100ms)
|
// Phase 1: Quick white flash (100ms)
|
||||||
// Phase 2: Static noise (600ms)
|
// Phase 2: Static noise (600ms)
|
||||||
@ -40,10 +56,188 @@ function App() {
|
|||||||
};
|
};
|
||||||
}, [isBooting]);
|
}, [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 (
|
return (
|
||||||
<div className="min-h-screen bg-gray-900 flex items-center justify-center overflow-hidden">
|
<div className="min-h-screen bg-gray-900 flex items-center justify-center overflow-hidden">
|
||||||
{/* TV Container with fixed proportions */}
|
{/* TV Container with fixed proportions */}
|
||||||
<div className="w-[95%] max-w-[1400px] relative h-[90vh]">
|
<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 */}
|
{/* TV Frame */}
|
||||||
<div className="absolute inset-0 tv-frame">
|
<div className="absolute inset-0 tv-frame">
|
||||||
{/* TV frame texture overlay */}
|
{/* TV frame texture overlay */}
|
||||||
@ -87,16 +281,48 @@ function App() {
|
|||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Static noise texture underlay */}
|
{/* Static noise texture underlay - controlled by glitch slider */}
|
||||||
<div className="white-noise"></div>
|
<div
|
||||||
|
className="white-noise"
|
||||||
{/* Screen Content with fixed height */}
|
style={{ opacity: glitchEffects.whiteNoiseOpacity }}
|
||||||
<div className="relative h-full w-full p-6 font-mono text-[#00FF00] overflow-hidden z-10 screen-content">
|
></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">
|
<div className="animate-pulse mb-4">
|
||||||
<Terminal className="inline-block mr-2" />
|
<Terminal className="inline-block mr-2" />
|
||||||
<span className="text-sm">echo "Hey there!"</span>
|
<span className="text-sm">echo "Hey there!"</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Split Screen Container with fixed height */}
|
{/* Split Screen Container with fixed height */}
|
||||||
<div className="flex gap-8 h-[calc(100%-3rem)]">
|
<div className="flex gap-8 h-[calc(100%-3rem)]">
|
||||||
{/* Left Column - Navigation */}
|
{/* Left Column - Navigation */}
|
||||||
@ -138,10 +364,8 @@ function App() {
|
|||||||
SHELL
|
SHELL
|
||||||
</TerminalButton>
|
</TerminalButton>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Vertical Separator with embossed effect */}
|
{/* Vertical Separator with embossed effect */}
|
||||||
<div className="vertical-separator"></div>
|
<div className="vertical-separator"></div>
|
||||||
|
|
||||||
{/* Right Column - Content with scrolling */}
|
{/* Right Column - Content with scrolling */}
|
||||||
<div className="flex-1 overflow-y-auto pr-4 custom-scrollbar">
|
<div className="flex-1 overflow-y-auto pr-4 custom-scrollbar">
|
||||||
{activeSection === 'about' && <ProfileContent />}
|
{activeSection === 'about' && <ProfileContent />}
|
||||||
@ -155,30 +379,45 @@ function App() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{/* Enhanced Scan Lines Effect - controlled by glitch slider */}
|
||||||
{/* Enhanced Scan Lines Effect */}
|
<div
|
||||||
<div className="scan-lines"></div>
|
className="scan-lines"
|
||||||
|
style={{
|
||||||
{/* Chromatic Aberration Effect */}
|
backgroundImage: `repeating-linear-gradient(
|
||||||
<div className="chromatic-aberration"></div>
|
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 */}
|
{/* Multiple Glass Reflection Layers */}
|
||||||
<div className="glass-reflection"></div>
|
<div className="glass-reflection"></div>
|
||||||
<div className="glass-reflection-2"></div>
|
<div className="glass-reflection-2"></div>
|
||||||
<div className="corner-shine"></div>
|
<div className="corner-shine"></div>
|
||||||
|
|
||||||
{/* Screen Glare - Moving reflection */}
|
{/* Screen Glare - Moving reflection */}
|
||||||
<div className="screen-glare"></div>
|
<div className="screen-glare"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* TV Controls - Skeuomorphic Knobs */}
|
{/* TV Controls - Skeuomorphic Knobs */}
|
||||||
<div className="absolute -bottom-12 right-20 flex space-x-8">
|
<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(-30deg)'}}></div>
|
||||||
<div className="tv-knob" style={{transform: 'rotate(15deg)'}}></div>
|
<div className="tv-knob" style={{transform: 'rotate(15deg)'}}></div>
|
||||||
<div className="tv-knob" style={{transform: 'rotate(-10deg)'}}></div>
|
<div className="tv-knob" style={{transform: 'rotate(-10deg)'}}></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* TV Speaker Grills - Enhanced */}
|
{/* TV Speaker Grills - Enhanced */}
|
||||||
<div className="absolute -bottom-6 left-20 flex space-x-1.5">
|
<div className="absolute -bottom-6 left-20 flex space-x-1.5">
|
||||||
{[...Array(10)].map((_, i) => (
|
{[...Array(10)].map((_, i) => (
|
||||||
@ -191,7 +430,6 @@ function App() {
|
|||||||
></div>
|
></div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* TV Brand Label */}
|
{/* TV Brand Label */}
|
||||||
<div
|
<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"
|
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
|
RETRO·VISION
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Power indicator light */}
|
{/* 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"
|
<div className="absolute -bottom-8 right-[12%] w-3 h-3 rounded-full bg-gradient-to-b from-green-500 to-green-700"
|
||||||
style={{
|
style={{
|
||||||
|
@ -6,4 +6,5 @@
|
|||||||
@import './styles/buttons.css';
|
@import './styles/buttons.css';
|
||||||
@import './styles/scrollbars.css';
|
@import './styles/scrollbars.css';
|
||||||
@import './styles/skills.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;
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user