mirror of
https://github.com/System-End/My-website.git
synced 2026-04-19 19:45:10 +00:00
ee
This commit is contained in:
parent
5fc3529d7e
commit
9d73d0c7bf
20 changed files with 456 additions and 251 deletions
134
src/App.js
134
src/App.js
|
|
@ -1,114 +1,34 @@
|
|||
import React, { useEffect, useState } from 'react';
|
||||
import './styles/App.css';
|
||||
import SpotifyList from './components/SpotifyList';
|
||||
import LoadingAnimation from './components/LoadingAnimation';
|
||||
import GithubRepos from './components/GithubRepos';
|
||||
import { Music, Code, Twitch, Github, Cpu, Shield } from 'lucide-react';
|
||||
import React from 'react';
|
||||
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
|
||||
import Navbar from './components/Navbar';
|
||||
import AboutPage from './pages/AboutPage';
|
||||
import APCSPPage from './pages/APCSPPage';
|
||||
import ProjectsPage from './pages/ProjectsPage';
|
||||
import ErrorBoundary from './components/ErrorBoundary';
|
||||
import './App.css';
|
||||
|
||||
const App = () => {
|
||||
const [age, setAge] = useState(0);
|
||||
|
||||
useEffect(() => {
|
||||
// Age calculation
|
||||
const calculateAge = () => {
|
||||
const birthDate = new Date('2009-05-15');
|
||||
const today = new Date();
|
||||
let age = today.getFullYear() - birthDate.getFullYear();
|
||||
const monthDiff = today.getMonth() - birthDate.getMonth();
|
||||
|
||||
if (monthDiff < 0 || (monthDiff === 0 && today.getDate() < birthDate.getDate())) {
|
||||
age--;
|
||||
}
|
||||
return age;
|
||||
};
|
||||
setAge(calculateAge());
|
||||
|
||||
// Particle effect setup
|
||||
const createParticles = () => {
|
||||
const particleContainer = document.querySelector('.particle-container');
|
||||
if (particleContainer) {
|
||||
for (let i = 0; i < 50; i++) {
|
||||
const particle = document.createElement('div');
|
||||
particle.className = 'particle';
|
||||
particle.style.left = `${Math.random() * 100}vw`;
|
||||
particle.style.animationDuration = `${Math.random() * 3 + 2}s`;
|
||||
particle.style.animationDelay = `${Math.random() * 2}s`;
|
||||
particleContainer.appendChild(particle);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
createParticles();
|
||||
}, []);
|
||||
|
||||
const interests = [
|
||||
{ icon: <Code size={24} />, title: 'Programming', description: 'Full-stack development & coding projects' },
|
||||
{ icon: <Cpu size={24} />, title: 'Robotics', description: 'Building & programming robots' },
|
||||
{ icon: <Shield size={24} />, title: 'Cybersecurity', description: 'Network security & ethical hacking' },
|
||||
{ icon: <Music size={24} />, title: 'Music', description: 'Music production & listening' },
|
||||
{ icon: <Twitch size={24} />, title: 'Streaming', description: 'FiveM & variety gaming on Twitch' },
|
||||
{ icon: <Github size={24} />, title: 'Open Source', description: 'Contributing to GitHub projects' }
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="app-container animated-bg">
|
||||
<div className="particle-container" />
|
||||
|
||||
{/* Header Section */}
|
||||
<header className="header neon-text">
|
||||
<h1>EndofTimee</h1>
|
||||
<p className="subtitle">Programmer • Streamer • Foxgirl 🦊</p>
|
||||
</header>
|
||||
|
||||
{/* About Section */}
|
||||
<section className="content-section about-section">
|
||||
<h2 className="neon-text">About Me</h2>
|
||||
<div className="about-content">
|
||||
<p>Hey there! I'm a {age}-year-old transfem programmer and content creator.
|
||||
When I'm not coding or building robots, you can find me streaming on
|
||||
<a href="https://twitch.tv/EndofTimee" target="_blank" rel="noopener noreferrer"
|
||||
className="twitch-link">Twitch</a>!</p>
|
||||
<ErrorBoundary>
|
||||
<Router>
|
||||
<div className="app-container animated-bg">
|
||||
<Navbar />
|
||||
<main className="main-content">
|
||||
<Routes>
|
||||
<Route path="/" element={<AboutPage />} />
|
||||
<Route path="/apcsp" element={<APCSPPage />} />
|
||||
<Route path="/projects" element={<ProjectsPage />} />
|
||||
<Route path="*" element={
|
||||
<div className="error-page">
|
||||
<h1 className="text-glow">404: Page Not Found</h1>
|
||||
<p>Oops! This fox couldn't find what you're looking for.</p>
|
||||
</div>
|
||||
} />
|
||||
</Routes>
|
||||
</main>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Interests Grid */}
|
||||
<section className="content-section interests-section">
|
||||
<h2 className="neon-text">What I Do</h2>
|
||||
<div className="interests-grid">
|
||||
{interests.map((interest, index) => (
|
||||
<div key={index} className="interest-card">
|
||||
<div className="interest-icon">{interest.icon}</div>
|
||||
<h3>{interest.title}</h3>
|
||||
<p>{interest.description}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Streaming Section */}
|
||||
<section className="content-section stream-section">
|
||||
<h2 className="neon-text">Streaming</h2>
|
||||
<div className="stream-content">
|
||||
<p>Join me on Twitch for FiveM roleplay and various other games!
|
||||
I love interacting with chat and building a positive community.</p>
|
||||
<a href="https://twitch.tv/EndofTimee" target="_blank"
|
||||
rel="noopener noreferrer" className="twitch-button">
|
||||
<Twitch className="icon" />
|
||||
Watch Live
|
||||
</a>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* GitHub Section */}
|
||||
<div className="github-section">
|
||||
<GithubRepos />
|
||||
</div>
|
||||
|
||||
{/* Music Section */}
|
||||
<div className="music-section">
|
||||
<SpotifyList />
|
||||
</div>
|
||||
</div>
|
||||
</Router>
|
||||
</ErrorBoundary>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
|||
6
src/assets/images/fox-icons/coding.svg
Normal file
6
src/assets/images/fox-icons/coding.svg
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
|
||||
<path d="M50 10 C20 10 10 40 10 50 C10 80 40 90 50 90 C60 90 90 80 90 50 C90 40 80 10 50 10" fill="#ff9466"/>
|
||||
<path d="M30 50 L40 40 L30 30" stroke="#240046" stroke-width="3" fill="none"/>
|
||||
<path d="M70 50 L60 40 L70 30" stroke="#240046" stroke-width="3" fill="none"/>
|
||||
<rect x="45" y="60" width="10" height="2" fill="#240046"/>
|
||||
</svg>
|
||||
7
src/assets/images/fox-icons/gaming.svg
Normal file
7
src/assets/images/fox-icons/gaming.svg
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
|
||||
<path d="M50 10 C20 10 10 40 10 50 C10 80 40 90 50 90 C60 90 90 80 90 50 C90 40 80 10 50 10" fill="#ff9466"/>
|
||||
<circle cx="35" cy="40" r="5" fill="#240046"/>
|
||||
<circle cx="65" cy="40" r="5" fill="#240046"/>
|
||||
<path d="M35 60 Q50 65 65 60" stroke="#240046" stroke-width="3" fill="none"/>
|
||||
<rect x="30" y="30" width="40" height="20" rx="5" fill="#240046"/>
|
||||
</svg>
|
||||
6
src/assets/images/fox-icons/happy.svg
Normal file
6
src/assets/images/fox-icons/happy.svg
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
|
||||
<path d="M50 10 C20 10 10 40 10 50 C10 80 40 90 50 90 C60 90 90 80 90 50 C90 40 80 10 50 10" fill="#ff9466"/>
|
||||
<circle cx="35" cy="40" r="5" fill="#240046"/>
|
||||
<circle cx="65" cy="40" r="5" fill="#240046"/>
|
||||
<path d="M35 60 Q50 70 65 60" stroke="#240046" stroke-width="3" fill="none"/>
|
||||
</svg>
|
||||
7
src/assets/images/fox-icons/music.svg
Normal file
7
src/assets/images/fox-icons/music.svg
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
|
||||
<path d="M50 10 C20 10 10 40 10 50 C10 80 40 90 50 90 C60 90 90 80 90 50 C90 40 80 10 50 10" fill="#ff9466"/>
|
||||
<circle cx="35" cy="40" r="5" fill="#240046"/>
|
||||
<circle cx="65" cy="40" r="5" fill="#240046"/>
|
||||
<path d="M35 60 Q50 70 65 60" stroke="#240046" stroke-width="3" fill="none"/>
|
||||
<path d="M75 30 L65 35 L65 25 L75 20 Z" fill="#240046"/>
|
||||
</svg>
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
import React from 'react';
|
||||
import '../styles/FoxCard.css';
|
||||
|
||||
const FoxCard = ({ children, className = '' }) => {
|
||||
return (
|
||||
<div className={`fox-card ${className}`}>
|
||||
<div className="fox-ear fox-ear-left" />
|
||||
<div className="fox-ear fox-ear-right" />
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default FoxCard;
|
||||
25
src/components/LoadingFox.js
Normal file
25
src/components/LoadingFox.js
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
import React from 'react';
|
||||
import '../styles/LoadingFox.css';
|
||||
|
||||
const LoadingFox = () => {
|
||||
return (
|
||||
<div className="loading-fox-container">
|
||||
<div className="fox-loader">
|
||||
<div className="fox-face">
|
||||
<div className="fox-ears">
|
||||
<div className="ear left"></div>
|
||||
<div className="ear right"></div>
|
||||
</div>
|
||||
<div className="fox-eyes">
|
||||
<div className="eye left"></div>
|
||||
<div className="eye right"></div>
|
||||
</div>
|
||||
<div className="fox-nose"></div>
|
||||
</div>
|
||||
<div className="loading-text">Loading...</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default LoadingFox;
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
|
||||
import React from 'react';
|
||||
import './LoadingAnimation.css';
|
||||
|
||||
const LoadingAnimation = () => {
|
||||
return (
|
||||
<div className="loading-container">
|
||||
<div className="loading-spinner"></div>
|
||||
<p>Loading...</p>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default LoadingAnimation;
|
||||
|
|
@ -1,16 +1,59 @@
|
|||
import React from 'react';
|
||||
import { Link, useLocation } from 'react-router-dom';
|
||||
import ThemeToggle from './ThemeToggle';
|
||||
import { Home, Code, BookOpen, Twitch } from 'lucide-react';
|
||||
|
||||
function Navbar() {
|
||||
return (
|
||||
<nav>
|
||||
<ul>
|
||||
<li><a href="/">Home</a></li>
|
||||
<li><a href="/pages/APCSP.html">APCSP Project</a></li>
|
||||
<li><a href="/pages/github-repos.html">GitHub Projects</a></li>
|
||||
</ul>
|
||||
<input type="color" id="theme-color-picker" title="Choose your color" />
|
||||
</nav>
|
||||
);
|
||||
}
|
||||
const Navbar = () => {
|
||||
const location = useLocation();
|
||||
|
||||
return (
|
||||
<nav className="navbar">
|
||||
<div className="nav-content">
|
||||
<Link to="/" className="nav-brand">
|
||||
<img src="/logo.jpg" alt="Logo" className="nav-logo" />
|
||||
<span className="text-glow">EndofTimee</span>
|
||||
</Link>
|
||||
|
||||
<div className="nav-links">
|
||||
<Link
|
||||
to="/"
|
||||
className={`nav-link ${location.pathname === '/' ? 'active' : ''}`}
|
||||
>
|
||||
<Home size={20} />
|
||||
<span>About</span>
|
||||
</Link>
|
||||
|
||||
<Link
|
||||
to="/projects"
|
||||
className={`nav-link ${location.pathname === '/projects' ? 'active' : ''}`}
|
||||
>
|
||||
<Code size={20} />
|
||||
<span>Projects</span>
|
||||
</Link>
|
||||
|
||||
<Link
|
||||
to="/apcsp"
|
||||
className={`nav-link ${location.pathname === '/apcsp' ? 'active' : ''}`}
|
||||
>
|
||||
<BookOpen size={20} />
|
||||
<span>APCSP</span>
|
||||
</Link>
|
||||
|
||||
<a
|
||||
href="https://twitch.tv/EndofTimee"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="nav-link"
|
||||
>
|
||||
<Twitch size={20} />
|
||||
<span>Stream</span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<ThemeToggle />
|
||||
</div>
|
||||
</nav>
|
||||
);
|
||||
};
|
||||
|
||||
export default Navbar;
|
||||
52
src/hooks/useGithubRepos.js
Normal file
52
src/hooks/useGithubRepos.js
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
import { useState, useEffect } from 'react';
|
||||
|
||||
const useGithubRepos = () => {
|
||||
const [repos, setRepos] = useState([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState(null);
|
||||
|
||||
useEffect(() => {
|
||||
const fetchRepos = async () => {
|
||||
try {
|
||||
const response = await fetch('https://api.github.com/users/EndofTimee/repos?sort=updated');
|
||||
if (!response.ok) {
|
||||
throw new Error('Failed to fetch repositories');
|
||||
}
|
||||
const data = await response.json();
|
||||
|
||||
// Get additional details for each repo
|
||||
const repoDetails = await Promise.all(
|
||||
data.map(async (repo) => {
|
||||
try {
|
||||
const languagesResponse = await fetch(repo.languages_url);
|
||||
const languages = await languagesResponse.json();
|
||||
return {
|
||||
...repo,
|
||||
languages: Object.keys(languages)
|
||||
};
|
||||
} catch (error) {
|
||||
console.error(`Error fetching languages for ${repo.name}:`, error);
|
||||
return {
|
||||
...repo,
|
||||
languages: []
|
||||
};
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
setRepos(repoDetails);
|
||||
} catch (err) {
|
||||
setError(err.message);
|
||||
console.error('Error fetching repos:', err);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
fetchRepos();
|
||||
}, []);
|
||||
|
||||
return { repos, loading, error };
|
||||
};
|
||||
|
||||
export default useGithubRepos;
|
||||
29
src/hooks/useSpotifyData.js
Normal file
29
src/hooks/useSpotifyData.js
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
import { useState, useEffect } from 'react';
|
||||
|
||||
const useSpotifyData = () => {
|
||||
const [data, setData] = useState(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState(null);
|
||||
|
||||
useEffect(() => {
|
||||
const fetchData = async () => {
|
||||
try {
|
||||
const response = await fetch(`${process.env.REACT_APP_WORKER_URL}/spotify-data`);
|
||||
if (!response.ok) throw new Error('Failed to fetch Spotify data');
|
||||
|
||||
const result = await response.json();
|
||||
setData(result);
|
||||
setLoading(false);
|
||||
} catch (err) {
|
||||
setError(err.message);
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
fetchData();
|
||||
}, []);
|
||||
|
||||
return { data, loading, error };
|
||||
};
|
||||
|
||||
export default useSpotifyData;
|
||||
42
src/pages/APCSPPage.js
Normal file
42
src/pages/APCSPPage.js
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
import React from 'react';
|
||||
import FoxCard from '../components/FoxCard';
|
||||
import { Code, BookOpen, Cpu } from 'lucide-react';
|
||||
|
||||
const APCSPPage = () => {
|
||||
return (
|
||||
<div className="page-container">
|
||||
<FoxCard className="header-card">
|
||||
<h1 className="text-glow">AP Computer Science Principles</h1>
|
||||
<p className="text-gradient">Exploring the foundations of modern computing</p>
|
||||
</FoxCard>
|
||||
|
||||
<div className="content-grid">
|
||||
<FoxCard>
|
||||
<div className="flex items-center gap-4">
|
||||
<Code size={24} className="text-accent-primary" />
|
||||
<h2>Programming Concepts</h2>
|
||||
</div>
|
||||
<p>Learn the creative aspects of programming, abstractions, and algorithms</p>
|
||||
</FoxCard>
|
||||
|
||||
<FoxCard>
|
||||
<div className="flex items-center gap-4">
|
||||
<Cpu size={24} className="text-accent-primary" />
|
||||
<h2>Project Demo</h2>
|
||||
</div>
|
||||
<div className="project-demo">
|
||||
<iframe
|
||||
src="https://drive.google.com/file/d/1JT7nZ82QJh5NIxFVHyewRBR1MLsWohEF/preview"
|
||||
width="100%"
|
||||
height="400"
|
||||
className="rounded-lg"
|
||||
allowFullScreen
|
||||
/>
|
||||
</div>
|
||||
</FoxCard>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default APCSPPage;
|
||||
58
src/pages/AboutPage.js
Normal file
58
src/pages/AboutPage.js
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
import React from 'react';
|
||||
import FoxCard from '../components/FoxCard';
|
||||
import { Heart, Gamepad2, Code, Music } from 'lucide-react';
|
||||
import { SpotifyVisualizer } from '../components/SpotifyVisualizer';
|
||||
|
||||
const AboutPage = () => {
|
||||
const calculateAge = () => {
|
||||
const birthDate = new Date("2009-05-15");
|
||||
const today = new Date();
|
||||
let age = today.getFullYear() - birthDate.getFullYear();
|
||||
const monthDiff = today.getMonth() - birthDate.getMonth();
|
||||
if (monthDiff < 0 || (monthDiff === 0 && today.getDate() < birthDate.getDate())) {
|
||||
age--;
|
||||
}
|
||||
return age;
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="page-container">
|
||||
<FoxCard className="header-card">
|
||||
<h1 className="text-glow">About Me</h1>
|
||||
<p className="text-gradient">Transfem Foxgirl • {calculateAge()} years old • Programmer & Streamer</p>
|
||||
</FoxCard>
|
||||
|
||||
<div className="content-grid">
|
||||
<FoxCard>
|
||||
<div className="flex items-center gap-4">
|
||||
<Code size={24} className="text-accent-primary" />
|
||||
<h2>Tech Interests</h2>
|
||||
</div>
|
||||
<ul className="interest-list">
|
||||
<li>Programming & Development</li>
|
||||
<li>Robotics & Hardware</li>
|
||||
<li>Cybersecurity</li>
|
||||
</ul>
|
||||
</FoxCard>
|
||||
|
||||
<FoxCard>
|
||||
<div className="flex items-center gap-4">
|
||||
<Gamepad2 size={24} className="text-accent-primary" />
|
||||
<h2>Streaming</h2>
|
||||
</div>
|
||||
<p>Find me on <a href="https://twitch.tv/EndofTimee" className="text-accent-neon hover:text-glow" target="_blank" rel="noopener noreferrer">Twitch</a> playing FiveM and other games!</p>
|
||||
</FoxCard>
|
||||
|
||||
<FoxCard>
|
||||
<div className="flex items-center gap-4">
|
||||
<Music size={24} className="text-accent-primary" />
|
||||
<h2>Current Tunes</h2>
|
||||
</div>
|
||||
<SpotifyVisualizer />
|
||||
</FoxCard>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default AboutPage;
|
||||
33
src/pages/ProjectsPage.js
Normal file
33
src/pages/ProjectsPage.js
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
import React from 'react';
|
||||
import FoxCard from '../components/FoxCard';
|
||||
import GithubRepos from '../components/GithubRepos';
|
||||
import useGithubRepos from '../hooks/useGithubRepos';
|
||||
import LoadingFox from '../components/LoadingFox';
|
||||
|
||||
const ProjectsPage = () => {
|
||||
const { repos, loading, error } = useGithubRepos();
|
||||
|
||||
return (
|
||||
<div className="page-container">
|
||||
<FoxCard className="header-card">
|
||||
<h1 className="text-glow">My Projects</h1>
|
||||
<p className="text-gradient">Exploring code, one repo at a time</p>
|
||||
</FoxCard>
|
||||
|
||||
{loading ? (
|
||||
<LoadingFox />
|
||||
) : error ? (
|
||||
<FoxCard className="error-card">
|
||||
<p>Oops! Something went wrong fetching the repositories.</p>
|
||||
<button onClick={() => window.location.reload()} className="retry-button">
|
||||
Try Again
|
||||
</button>
|
||||
</FoxCard>
|
||||
) : (
|
||||
<GithubRepos repos={repos} />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ProjectsPage;
|
||||
|
|
@ -1,57 +0,0 @@
|
|||
/* src/styles/LoadingAnimation.css */
|
||||
.loading-container {
|
||||
position: fixed;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: linear-gradient(135deg, rgba(26, 11, 46, 0.9), rgba(47, 28, 84, 0.9));
|
||||
backdrop-filter: blur(10px);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
z-index: 9999;
|
||||
}
|
||||
|
||||
.loading-spinner {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
border: 4px solid rgba(157, 78, 221, 0.1);
|
||||
border-left: 4px solid #9d4edd;
|
||||
border-radius: 50%;
|
||||
animation: spin 1s linear infinite;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.loading-spinner::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 50%;
|
||||
border: 4px solid transparent;
|
||||
border-left: 4px solid #b249f8;
|
||||
animation: spin 0.5s linear infinite reverse;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
0% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
.loading-text {
|
||||
position: absolute;
|
||||
margin-top: 100px;
|
||||
color: #ffffff;
|
||||
font-size: 1.2rem;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2px;
|
||||
animation: glow 1.5s ease-in-out infinite alternate;
|
||||
}
|
||||
|
||||
@keyframes glow {
|
||||
from {
|
||||
text-shadow: 0 0 5px #e0aaff, 0 0 10px #e0aaff, 0 0 15px #b249f8;
|
||||
}
|
||||
to {
|
||||
text-shadow: 0 0 10px #e0aaff, 0 0 20px #e0aaff, 0 0 30px #b249f8;
|
||||
}
|
||||
}
|
||||
20
src/styles/SpotifyVisualizer.css
Normal file
20
src/styles/SpotifyVisualizer.css
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
.visualizer-container {
|
||||
width: 100%;
|
||||
max-width: 300px;
|
||||
height: 60px;
|
||||
margin: 1rem auto;
|
||||
background: rgba(26, 11, 46, 0.3);
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.music-visualizer {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
@keyframes glow {
|
||||
0% { filter: drop-shadow(0 0 2px var(--accent-neon)); }
|
||||
50% { filter: drop-shadow(0 0 8px var(--accent-neon)); }
|
||||
100% { filter: drop-shadow(0 0 2px var(--accent-neon)); }
|
||||
}
|
||||
12
src/styles/cursor/tail-loading.svg
Normal file
12
src/styles/cursor/tail-loading.svg
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M16 4C16 4 24 8 24 16C24 24 16 28 16 28" stroke="#9d4edd" stroke-width="2">
|
||||
<animateTransform
|
||||
attributeName="transform"
|
||||
attributeType="XML"
|
||||
type="rotate"
|
||||
from="0 16 16"
|
||||
to="360 16 16"
|
||||
dur="1s"
|
||||
repeatCount="indefinite"/>
|
||||
</path>
|
||||
</svg>
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
|
||||
.loading-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100vh;
|
||||
background: black;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.loading-spinner {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
border: 5px solid rgba(255, 255, 255, 0.3);
|
||||
border-top: 5px solid white;
|
||||
border-radius: 50%;
|
||||
animation: spin 1s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
0% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(360deg); }
|
||||
}
|
||||
77
src/styles/pages.css
Normal file
77
src/styles/pages.css
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
/* Page Container */
|
||||
.page-container {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 2rem;
|
||||
min-height: calc(100vh - 4rem);
|
||||
}
|
||||
|
||||
/* Header Card */
|
||||
.header-card {
|
||||
margin-bottom: 2rem;
|
||||
text-align: center;
|
||||
background: var(--gradient-primary);
|
||||
}
|
||||
|
||||
.header-card h1 {
|
||||
font-size: 2.5rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
/* Content Grid */
|
||||
.content-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
||||
gap: 2rem;
|
||||
}
|
||||
|
||||
/* Interest List */
|
||||
.interest-list {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 1rem 0;
|
||||
}
|
||||
|
||||
.interest-list li {
|
||||
padding: 0.5rem 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.interest-list li::before {
|
||||
content: "🦊";
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
|
||||
/* Project Demo */
|
||||
.project-demo {
|
||||
margin-top: 1rem;
|
||||
border-radius: var(--border-radius-lg);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* Responsive Design */
|
||||
@media (max-width: 768px) {
|
||||
.page-container {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.header-card h1 {
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
.content-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
/* Animation Classes */
|
||||
.fade-in {
|
||||
animation: fadeIn 0.5s ease-in;
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from { opacity: 0; transform: translateY(10px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
|
||||
@keyframes rollingCode {
|
||||
0% { transform: translateY(0); opacity: 1; }
|
||||
100% { transform: translateY(100vh); opacity: 0; }
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
overflow: hidden;
|
||||
background: black;
|
||||
color: limegreen;
|
||||
font-family: monospace;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.code-line {
|
||||
position: absolute;
|
||||
top: -10%;
|
||||
width: 100%;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
animation: rollingCode 5s linear infinite;
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue