complete revamp

This commit is contained in:
2026-02-19 23:23:56 -10:00
parent 893389da53
commit 3a236a47dc
31 changed files with 134 additions and 12253 deletions

View File

@@ -1,179 +0,0 @@
# Contributing to ZanePersonal
Thank you for your interest in contributing to this project! While this is primarily a personal website, contributions are welcome.
## Commit Message Format
This project follows the [Conventional Commits](https://www.conventionalcommits.org/) specification for commit messages.
### Format
```
<type>(<scope>): <subject>
<body>
<footer>
```
### Types
- **feat**: A new feature
- **fix**: A bug fix
- **docs**: Documentation changes
- **style**: Code style changes (formatting, missing semi-colons, etc.)
- **refactor**: Code refactoring without changing functionality
- **perf**: Performance improvements
- **test**: Adding or updating tests
- **build**: Changes to build system or dependencies
- **ci**: Changes to CI configuration
- **chore**: Other changes that don't modify src or test files
### Examples
```
feat(effects): add new particle collision detection
fix(animations): resolve timing issue with typewriter effect
docs(readme): update installation instructions
style(css): apply consistent naming convention
refactor(modules): extract sound utilities to separate module
perf(effects): optimize particle rendering loop
test(interactions): add unit tests for ripple effect
build(vite): update build configuration for better tree-shaking
ci(github-actions): add accessibility testing workflow
```
## Development Workflow
1. **Fork the repository** (if you're an external contributor)
2. **Clone your fork**
```bash
git clone https://github.com/YOUR_USERNAME/ZanePersonal.git
cd ZanePersonal
```
3. **Install dependencies**
```bash
npm install
```
4. **Create a feature branch**
```bash
git checkout -b feat/your-feature-name
```
5. **Make your changes**
- Write clean, readable code
- Follow existing code style
- Add comments where necessary
- Update documentation if needed
6. **Run quality checks**
```bash
npm run lint # Check for linting errors
npm run format # Format code with Prettier
npm run build # Ensure project builds successfully
```
7. **Commit your changes**
```bash
git add .
git commit -m "feat(scope): your descriptive message"
```
8. **Push to your fork**
```bash
git push origin feat/your-feature-name
```
9. **Create a Pull Request**
- Provide a clear description of changes
- Reference any related issues
- Ensure CI checks pass
## Code Style Guidelines
### JavaScript
- Use ES6+ features
- Prefer `const` over `let`, avoid `var`
- Use arrow functions for callbacks
- Keep functions small and focused
- Add JSDoc comments for complex functions
- Follow the existing modular structure
### CSS
- Use CSS custom properties (variables) defined in `:root`
- Follow BEM-like naming conventions where appropriate
- Group related properties together
- Use meaningful class names
- Avoid overly specific selectors
### HTML
- Use semantic HTML5 elements
- Include proper ARIA labels for accessibility
- Ensure all images have alt text
- Maintain proper heading hierarchy
## Testing
While this project doesn't currently have automated tests, please ensure:
- All features work in modern browsers (Chrome, Firefox, Safari, Edge)
- Responsive design works on mobile, tablet, and desktop
- No console errors or warnings
- Accessibility features work with keyboard navigation
## Performance Considerations
- Keep total bundle size under 150KB (compressed)
- Optimize images before adding them
- Minimize use of heavy libraries
- Test animations on lower-end devices
- Use lazy loading where appropriate
## Accessibility Standards
- Maintain WCAG 2.1 Level AA compliance
- Test with screen readers
- Ensure keyboard navigation works
- Provide sufficient color contrast
- Include focus indicators on interactive elements
## Pull Request Checklist
Before submitting a PR, ensure:
- [ ] Code follows the project's style guidelines
- [ ] All linting checks pass (`npm run lint`)
- [ ] Code is properly formatted (`npm run format`)
- [ ] Project builds successfully (`npm run build`)
- [ ] Changes are tested in multiple browsers
- [ ] Documentation is updated if needed
- [ ] Commit messages follow conventional commits format
- [ ] PR description clearly explains the changes
## Questions or Issues?
Feel free to:
- Open an issue for bugs or feature requests
- Start a discussion for questions
- Email [contact@zane.org](mailto:contact@zane.org)
## License
By contributing, you agree that your contributions will be licensed under the MIT License.
---
Thank you for contributing! 🎉

21
LICENSE
View File

@@ -1,21 +0,0 @@
MIT License
Copyright (c) 2024 ZaneDev
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

176
README.md
View File

@@ -1,176 +0,0 @@
# ZanePersonal
A modern, interactive personal website showcasing creative web development and design. Available at [https://zane.org](https://zane.org)
## 🎯 Purpose
This personal website serves as:
- A creative portfolio and personal brand showcase
- An experimental playground for modern web technologies
- A demonstration of interactive UI/UX design principles
- A learning platform for exploring cutting-edge web features
## ✨ Features
### Visual Effects
- **Matrix Rain Background** - Animated character rainfall effect
- **Custom Cursor** - Interactive cursor with trailing particles
- **Mouse Trail Effects** - Particle system following cursor movement
- **Glitch Effects** - Periodic text glitching animations
- **Interactive Background** - Particle network with connection lines
### User Experience
- **Smooth Animations** - Staggered entrance animations for all elements
- **Sound Effects** - Audio feedback for interactions (can be browser intensive)
- **Easter Eggs** - Hidden features and secret interactions:
- Konami Code activation
- Avatar click counter
- "ZANE" keyboard sequence
- Double-click brand name glitch
- **Micro-interactions** - Magnetic button effects, tilt animations, ripple effects
- **Responsive Design** - Optimized for all screen sizes
### Technical Features
- **Fully Static** - Compatible with cheap hosting platforms (no backend required)
- **Modular Architecture** - Well-organized code split into logical modules
- **Performance Optimized** - Minified assets and efficient animations
- **SEO Enhanced** - Proper meta tags, structured data, sitemap
- **Accessible** - Semantic HTML, ARIA labels, keyboard navigation
- **Open Source** - MIT licensed for learning and inspiration
## 🛠 Tech Stack
### Core Technologies
- **HTML5** - Semantic markup with proper accessibility attributes
- **CSS3** - Modern features including custom properties, animations, and grid
- **JavaScript (ES6+)** - Modular architecture using ES modules
### Development Tools
- **Vite** - Fast build tool and development server
- **ESLint** - Code quality and consistency checking
- **Prettier** - Automated code formatting
- **Imagemin** - Image optimization
### Libraries & APIs
- **Web Audio API** - For sound effect generation
- **Canvas API** - For particle effects and visual animations
- **Intersection Observer API** - For scroll-based animations
## 📁 Project Structure
```
ZanePersonal/
├── assets/
│ ├── css/
│ │ └── styles.css # Main stylesheet with design tokens
│ ├── js/
│ │ ├── main.js # Application entry point
│ │ └── modules/
│ │ ├── animations.js # Animation utilities
│ │ ├── dom.js # DOM manipulation helpers
│ │ ├── easter-eggs.js # Hidden features
│ │ ├── effects.js # Visual effects (particles, cursor, etc.)
│ │ ├── interactions.js # UI micro-interactions
│ │ └── sound.js # Sound effects
│ └── images/
│ └── Zane.jpg # Profile image
├── index.html # Main HTML file
├── robots.txt # Search engine directives
├── sitemap.xml # Site structure for SEO
├── LICENSE # MIT License
├── package.json # Project dependencies
├── .eslintrc.json # ESLint configuration
├── .prettierrc.json # Prettier configuration
└── README.md # This file
```
## 🚀 Getting Started
### Prerequisites
- Node.js 16+ and npm
### Installation
1. Clone the repository:
```bash
git clone https://github.com/ZaneThePython/ZanePersonal.git
cd ZanePersonal
```
2. Install dependencies:
```bash
npm install
```
3. Start development server:
```bash
npm run dev
```
4. Open your browser to `http://localhost:5173`
### Building for Production
```bash
npm run build
```
The optimized files will be in the `dist/` directory.
## 🧪 Development
### Code Quality
- **Linting**: `npm run lint`
- **Formatting**: `npm run format`
- **Format Check**: `npm run format:check`
### Performance Budget
Target metrics:
- Total page size: <150KB (compressed)
- First Contentful Paint: <1.5s
- Time to Interactive: <3.5s
- Lighthouse Score: >90
## 🤝 Contributing
While this is a personal project, suggestions and feedback are welcome! Feel free to:
- Open an issue for bugs or feature requests
- Submit a pull request for improvements
- Star the repository if you find it interesting
## 📄 License
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
## 🙏 Acknowledgments
- Font: [Inter](https://fonts.google.com/specimen/Inter) by Rasmus Andersson
- Inspiration from various creative developer portfolios
- Built with ☕ and countless hours of experimentation
## 📬 Contact
- Email: [contact@zane.org](mailto:contact@zane.org)
- GitHub: [@ZaneThePython](https://github.com/ZaneThePython)
- Website: [https://zane.org](https://zane.org)
---
**Note:** This website is not FDA approved (because websites don't need FDA approval, obviously! 😄)
_Last updated: November 2025_

View File

@@ -1,71 +0,0 @@
# Security Policy
## Supported Versions
This is a personal website project. The latest version on the `main` branch is always supported.
| Version | Supported |
| ------- | ------------------ |
| Latest | :white_check_mark: |
| Older | :x: |
## Reporting a Vulnerability
If you discover a security vulnerability in this project, please report it responsibly:
### Email
Send details to [contact@zane.org](mailto:contact@zane.org) with:
- Description of the vulnerability
- Steps to reproduce
- Potential impact
- Suggested fix (if any)
### What to Expect
- **Response time**: Within 48 hours
- **Updates**: Regular communication about the issue
- **Fix timeline**: Depends on severity and complexity
- **Disclosure**: Coordinated disclosure after fix is deployed
### Please Do Not
- Create public issues for security vulnerabilities
- Exploit vulnerabilities beyond proof-of-concept
- Access data that doesn't belong to you
- Perform DoS attacks
## Security Measures
This project implements several security best practices:
### Code Security
- **Input Sanitization**: All user inputs are sanitized (minimal as site is static)
- **Dependencies**: Regular security audits via `npm audit`
- **Linting**: ESLint configured to catch common security issues
- **CSP Ready**: Content Security Policy headers can be added by hosting provider
### Build Security
- **Dependency Scanning**: GitHub Dependabot enabled
- **Automated Updates**: Security patches applied automatically
- **CI/CD**: All code passes linting and build checks
### Best Practices
- No sensitive data in repository
- No API keys or credentials in code
- HTTPS enforced on live site
- Regular dependency updates
- Minimal external dependencies
## Known Limitations
As a static personal website:
- No backend or database
- No user authentication
- No data collection or storage
- Minimal attack surface
## Security Updates
Security fixes are released as soon as possible after discovery. Check the [CHANGELOG](CHANGELOG.md) for security-related updates.
---
*Last updated: November 2024*

BIN
Zane.jpg

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

View File

@@ -1,983 +0,0 @@
/* CSS Custom Properties (Design Tokens) */
:root {
/* Colors */
--color-primary: #007acc;
--color-primary-light: #00aaff;
--color-primary-dark: #005a9c;
--color-accent: #ff6b6b;
--color-accent-alt: #4ecdc4;
/* Background Colors */
--bg-primary: #1a1a1a;
--bg-secondary: #2a2a2a;
--bg-tertiary: #333;
/* Text Colors */
--text-primary: #ffffff;
--text-secondary: #ccc;
--text-muted: #888;
--text-disabled: #666;
/* Spacing */
--spacing-xs: 0.5rem;
--spacing-sm: 1rem;
--spacing-md: 1.5rem;
--spacing-lg: 2rem;
--spacing-xl: 3rem;
/* Font Sizes */
--font-size-xs: 0.8rem;
--font-size-sm: 0.9rem;
--font-size-base: 1rem;
--font-size-md: 1.1rem;
--font-size-lg: 1.3rem;
--font-size-xl: 2rem;
--font-size-2xl: 2.5rem;
/* Border Radius */
--radius-sm: 8px;
--radius-md: 12px;
--radius-lg: 15px;
--radius-full: 50%;
/* Shadows */
--shadow-sm: 0 4px 15px rgba(0, 0, 0, 0.2);
--shadow-md: 0 8px 32px rgba(0, 0, 0, 0.3);
--shadow-lg: 0 15px 40px rgba(0, 0, 0, 0.4);
/* Transitions */
--transition-fast: 0.1s ease;
--transition-base: 0.3s ease;
--transition-slow: 0.6s ease;
/* Z-index layers */
--z-background: -2;
--z-background-effects: -1;
--z-content: 1;
--z-effects: 1000;
--z-modal: 10000;
}
/* Reset and base styles */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Inter', sans-serif;
background-color: #1a1a1a;
color: #ffffff;
min-height: 100vh;
overflow-x: hidden;
position: relative;
cursor: none;
}
/* Custom Cursor */
.custom-cursor {
position: fixed;
width: 20px;
height: 20px;
background: radial-gradient(
circle,
rgba(0, 122, 204, 0.8) 0%,
rgba(0, 122, 204, 0.4) 50%,
transparent 100%
);
border-radius: 50%;
pointer-events: none;
z-index: 9999;
transition: transform 0.1s ease;
mix-blend-mode: difference;
}
.custom-cursor-trail {
position: fixed;
width: 8px;
height: 8px;
background: rgba(0, 122, 204, 0.6);
border-radius: 50%;
pointer-events: none;
z-index: 9998;
transition: all 0.3s ease;
}
/* Grid background */
body::before {
content: '';
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-image: radial-gradient(circle, #333 1px, transparent 1px);
background-size: 30px 30px;
background-position:
0 0,
15px 15px;
opacity: 0.3;
z-index: -1;
pointer-events: none;
animation: gridMove 20s linear infinite;
}
@keyframes gridMove {
0% {
transform: translate(0, 0);
}
100% {
transform: translate(30px, 30px);
}
}
/* Animated gradient background */
body::after {
content: '';
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: linear-gradient(-45deg, #1a1a1a, #2a2a2a, #1a1a1a, #333);
background-size: 400% 400%;
animation: gradientShift 15s ease infinite;
z-index: -2;
pointer-events: none;
}
@keyframes gradientShift {
0% {
background-position: 0% 50%;
}
50% {
background-position: 100% 50%;
}
100% {
background-position: 0% 50%;
}
}
/* Main container */
.container {
min-height: 100vh;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
position: relative;
padding: 2rem;
}
/* Avatar Section */
.avatar-section {
display: flex;
flex-direction: column;
align-items: center;
margin-bottom: 3rem;
position: relative;
filter: drop-shadow(0 0 20px rgba(0, 122, 204, 0.3));
}
.avatar {
width: 120px;
height: 120px;
border-radius: 50%;
background: linear-gradient(135deg, #2a2a2a, #1a1a1a);
border: 3px solid #333;
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 1.5rem;
box-shadow:
0 8px 32px rgba(0, 0, 0, 0.3),
0 0 0 0 rgba(0, 122, 204, 0.4),
inset 0 0 20px rgba(0, 122, 204, 0.1);
position: relative;
overflow: hidden;
transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
cursor: pointer;
transform-style: preserve-3d;
}
.avatar::before {
content: '';
position: absolute;
top: -3px;
left: -3px;
right: -3px;
bottom: -3px;
background: linear-gradient(45deg, #007acc, #00aaff, #007acc, #00aaff);
border-radius: 50%;
z-index: -1;
opacity: 0;
transition: opacity 0.3s ease;
animation: rotate 3s linear infinite;
}
.avatar:hover::before {
opacity: 1;
}
.avatar:hover {
transform: scale(1.1) rotateY(10deg) rotateX(5deg);
box-shadow:
0 15px 50px rgba(0, 0, 0, 0.4),
0 0 30px rgba(0, 122, 204, 0.6),
inset 0 0 30px rgba(0, 122, 204, 0.2);
border-color: #007acc;
}
@keyframes rotate {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
/* Profile Image */
.profile-image {
width: 100%;
height: 100%;
object-fit: cover;
border-radius: 50%;
animation:
sway 45s ease-in-out infinite,
pulseSlow 6s ease-in-out infinite;
}
@keyframes sway {
0% {
transform: rotate(-10deg);
}
50% {
transform: rotate(10deg);
}
100% {
transform: rotate(-10deg);
}
}
@keyframes pulseSlow {
0%,
100% {
transform: scale(1);
filter: drop-shadow(0 0 0 rgba(0, 122, 204, 0));
}
50% {
transform: scale(1.05);
filter: drop-shadow(0 0 12px rgba(0, 122, 204, 0.25));
}
}
/* Brand Name */
.brand-name {
font-size: 2.5rem;
font-weight: 600;
background: linear-gradient(135deg, #ffffff, #007acc, #00aaff, #ff6b6b, #4ecdc4);
background-size: 300% 300%;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
text-align: center;
letter-spacing: -0.02em;
margin-bottom: 0.5rem;
animation:
gradientText 4s ease infinite,
neonPulse 2s ease-in-out infinite alternate;
cursor: pointer;
transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
text-shadow:
0 0 5px rgba(0, 122, 204, 0.5),
0 0 10px rgba(0, 122, 204, 0.3),
0 0 15px rgba(0, 122, 204, 0.2);
position: relative;
}
.brand-name:hover {
transform: scale(1.1) rotateX(5deg);
filter: drop-shadow(0 0 20px rgba(0, 122, 204, 0.8));
text-shadow:
0 0 10px rgba(0, 122, 204, 0.8),
0 0 20px rgba(0, 122, 204, 0.6),
0 0 30px rgba(0, 122, 204, 0.4);
}
@keyframes gradientText {
0% {
background-position: 0% 50%;
}
50% {
background-position: 100% 50%;
}
100% {
background-position: 0% 50%;
}
}
@keyframes neonPulse {
0% {
filter: drop-shadow(0 0 5px rgba(0, 122, 204, 0.5));
text-shadow:
0 0 5px rgba(0, 122, 204, 0.5),
0 0 10px rgba(0, 122, 204, 0.3),
0 0 15px rgba(0, 122, 204, 0.2);
}
100% {
filter: drop-shadow(0 0 15px rgba(0, 122, 204, 0.8));
text-shadow:
0 0 10px rgba(0, 122, 204, 0.8),
0 0 20px rgba(0, 122, 204, 0.6),
0 0 30px rgba(0, 122, 204, 0.4);
}
}
/* Tagline */
.tagline {
font-size: 1.1rem;
color: #888;
text-align: center;
font-weight: 400;
margin-bottom: 2rem;
position: relative;
animation: fadeInUp 1s ease 0.8s both;
}
@keyframes fadeInUp {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
/* About Section */
.about-section {
max-width: 600px;
text-align: center;
margin-bottom: 3rem;
position: relative;
animation: slideInLeft 1s ease 1s both;
}
@keyframes slideInLeft {
from {
opacity: 0;
transform: translateX(-30px);
}
to {
opacity: 1;
transform: translateX(0);
}
}
.about-text {
font-size: 1.1rem;
line-height: 1.7;
color: #ccc;
font-weight: 400;
}
.not-for-hire-note {
margin-top: 0.5rem;
font-size: 0.9rem;
color: rgba(128, 128, 128, 0.5);
opacity: 0;
animation: fadeInOpacity 0.3s ease forwards;
animation-delay: 10s;
}
@keyframes fadeInOpacity {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
/* Navigation */
.navigation {
display: flex;
flex-direction: column;
gap: 1rem;
position: relative;
align-items: center;
animation: slideInRight 1s ease 1.2s both;
}
@keyframes slideInRight {
from {
opacity: 0;
transform: translateX(30px);
}
to {
opacity: 1;
transform: translateX(0);
}
}
.nav-button {
background: linear-gradient(135deg, #2a2a2a, #1a1a1a);
border: 2px solid #333;
border-radius: 12px;
padding: 1rem 2rem;
color: #ffffff;
font-size: 1.1rem;
font-weight: 500;
font-family: 'Inter', sans-serif;
cursor: pointer;
transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
min-width: 140px;
text-align: center;
position: relative;
overflow: hidden;
text-decoration: none;
display: inline-block;
box-shadow:
0 4px 15px rgba(0, 0, 0, 0.2),
0 0 0 0 rgba(0, 122, 204, 0.3);
transform-style: preserve-3d;
}
.nav-button::after {
content: '';
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background: linear-gradient(90deg, transparent, rgba(0, 122, 204, 0.3), transparent);
transition: left 0.6s ease;
}
.nav-button:hover::after {
left: 100%;
}
.nav-button.active {
background: #333;
border-color: #444;
}
.nav-button::before {
content: '';
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.1), transparent);
transition: left 0.5s ease;
}
.nav-button:hover::before {
left: 100%;
}
.nav-button:hover {
background: linear-gradient(135deg, #333, #2a2a2a);
border-color: #007acc;
transform: translateY(-5px) scale(1.05) rotateX(5deg);
box-shadow:
0 15px 40px rgba(0, 122, 204, 0.4),
0 0 20px rgba(0, 122, 204, 0.6);
text-shadow: 0 0 10px rgba(0, 122, 204, 0.8);
}
.nav-button:active {
transform: translateY(0);
}
/* Disclaimer */
.disclaimer {
position: fixed;
bottom: 2rem;
left: 2rem;
color: #666;
font-size: 0.9rem;
font-style: italic;
}
.disclaimer p {
margin: 0;
}
/* Content Sections */
.content-section {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(26, 26, 26, 0.95);
backdrop-filter: blur(10px);
display: none;
align-items: center;
justify-content: center;
z-index: 1000;
opacity: 0;
transition: opacity 0.3s ease;
}
.content-section.active {
display: flex;
opacity: 1;
}
.content-wrapper {
max-width: 800px;
padding: 2rem;
text-align: center;
}
.content-wrapper h2 {
font-size: 2.5rem;
margin-bottom: 2rem;
color: #ffffff;
}
.project-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 2rem;
margin-top: 2rem;
}
.project-card {
background: #2a2a2a;
border: 2px solid #333;
border-radius: 12px;
padding: 2rem;
transition: all 0.3s ease;
}
.project-card:hover {
transform: translateY(-5px);
border-color: #444;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3);
}
.project-card h3 {
color: #ffffff;
margin-bottom: 1rem;
font-size: 1.3rem;
}
.project-card p {
color: #ccc;
line-height: 1.6;
}
.github-link {
text-decoration: none;
display: inline-block;
margin-top: 2rem;
}
.github-button {
background: #333;
border: 2px solid #444;
border-radius: 12px;
padding: 1rem 2rem;
color: #ffffff;
font-size: 1.1rem;
font-weight: 500;
cursor: pointer;
transition: all 0.3s ease;
font-family: 'Inter', sans-serif;
}
.github-button:hover {
background: #444;
border-color: #555;
transform: translateY(-2px);
}
.contact-info {
margin-top: 2rem;
}
.contact-methods {
display: flex;
justify-content: center;
gap: 2rem;
margin-top: 2rem;
flex-wrap: wrap;
}
.contact-link {
color: #ffffff;
text-decoration: none;
padding: 1rem 2rem;
border: 2px solid #333;
border-radius: 12px;
background: #2a2a2a;
transition: all 0.3s ease;
font-weight: 500;
}
.contact-link:hover {
background: #333;
border-color: #444;
transform: translateY(-2px);
}
/* Projects Page Specific Styles */
.projects-main-content {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(26, 26, 26, 0.95);
backdrop-filter: blur(10px);
display: flex;
align-items: center;
justify-content: center;
z-index: 1000;
overflow-y: auto;
padding: 2rem 0;
}
.projects-intro {
color: #ccc;
font-size: 1.2rem;
margin-bottom: 3rem;
text-align: center;
}
.project-tech {
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
margin: 1rem 0;
}
.tech-tag {
background: #333;
color: #ffffff;
padding: 0.3rem 0.8rem;
border-radius: 20px;
font-size: 0.8rem;
font-weight: 500;
border: 1px solid #444;
}
.project-links {
display: flex;
gap: 1rem;
margin-top: 1.5rem;
}
.project-link {
color: #ffffff;
text-decoration: none;
padding: 0.5rem 1rem;
border: 1px solid #333;
border-radius: 8px;
background: #2a2a2a;
transition: all 0.3s ease;
font-size: 0.9rem;
font-weight: 500;
}
.project-link:hover {
background: #333;
border-color: #444;
transform: translateY(-1px);
}
.contact-message {
font-size: 1.5rem;
color: #ffffff;
font-weight: 500;
text-align: center;
margin-top: 2rem;
}
/* Skills Section */
.skills-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 2rem;
margin-top: 2rem;
}
.skill-category {
background: #2a2a2a;
border: 2px solid #333;
border-radius: 12px;
padding: 2rem;
transition: all 0.3s ease;
}
.skill-category:hover {
transform: translateY(-5px);
border-color: #444;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3);
}
.skill-category h3 {
color: #ffffff;
margin-bottom: 1.5rem;
font-size: 1.3rem;
text-align: center;
}
.skill-tags {
display: flex;
flex-wrap: wrap;
gap: 0.8rem;
justify-content: center;
}
.skill-tag {
background: linear-gradient(135deg, #333, #444);
color: #ffffff;
padding: 0.5rem 1rem;
border-radius: 25px;
font-size: 0.9rem;
font-weight: 500;
border: 1px solid #555;
transition: all 0.3s ease;
}
.skill-tag:hover {
background: linear-gradient(135deg, #444, #555);
transform: translateY(-2px);
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.3);
}
/* Featured Project Styles */
.project-card.featured {
border: 2px solid #007acc;
background: linear-gradient(135deg, #2a2a2a, #1f1f1f);
position: relative;
overflow: hidden;
}
.project-card.featured::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 3px;
background: linear-gradient(90deg, #007acc, #00aaff, #007acc);
animation: shimmer 2s infinite;
}
@keyframes shimmer {
0% {
transform: translateX(-100%);
}
100% {
transform: translateX(100%);
}
}
.project-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 1rem;
}
.featured-badge {
background: linear-gradient(135deg, #007acc, #00aaff);
color: #ffffff;
padding: 0.3rem 0.8rem;
border-radius: 15px;
font-size: 0.8rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.5px;
}
/* Close button for content sections */
.close-button {
position: absolute;
top: 2rem;
right: 2rem;
background: none;
border: none;
color: #666;
font-size: 2rem;
cursor: pointer;
transition: color 0.3s ease;
z-index: 1001;
}
.close-button:hover {
color: #ffffff;
transform: scale(1.1);
}
/* Scroll Animation Classes */
.animate-in {
animation: slideInUp 0.6s ease forwards;
}
@keyframes slideInUp {
from {
opacity: 0;
transform: translateY(30px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
/* Enhanced hover effects */
.project-card {
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
.project-card:hover {
transform: translateY(-8px) scale(1.02);
box-shadow: 0 15px 40px rgba(0, 0, 0, 0.4);
}
/* Improved tech tag styling */
.tech-tag {
background: linear-gradient(135deg, #333, #444);
color: #ffffff;
padding: 0.4rem 0.9rem;
border-radius: 20px;
font-size: 0.85rem;
font-weight: 500;
border: 1px solid #444;
transition: all 0.3s ease;
display: inline-block;
}
.tech-tag:hover {
background: linear-gradient(135deg, #444, #555);
transform: translateY(-2px);
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.3);
}
/* Responsive Design */
@media (max-width: 768px) {
.container {
padding: 1rem;
}
.avatar-section {
margin-bottom: 2rem;
}
.navigation {
align-items: center;
}
.brand-name {
font-size: 2rem;
}
.tagline {
font-size: 1rem;
}
.about-text {
font-size: 1rem;
}
.nav-button {
min-width: 120px;
padding: 0.8rem 1.5rem;
font-size: 1rem;
}
.content-wrapper {
padding: 1rem;
}
.content-wrapper h2 {
font-size: 2rem;
}
.project-grid {
grid-template-columns: 1fr;
gap: 1rem;
}
.skills-grid {
grid-template-columns: 1fr;
gap: 1rem;
}
.contact-methods {
flex-direction: column;
align-items: center;
gap: 1rem;
}
.disclaimer {
bottom: 1rem;
left: 1rem;
font-size: 0.8rem;
}
.close-button {
top: 1rem;
right: 1rem;
font-size: 1.5rem;
}
}
@media (max-width: 480px) {
.avatar {
width: 100px;
height: 100px;
}
.mug {
width: 50px;
height: 42px;
}
.mug-body {
width: 42px;
height: 34px;
}
.brand-name {
font-size: 1.8rem;
}
.nav-button {
min-width: 100px;
padding: 0.7rem 1.2rem;
font-size: 0.9rem;
}
}
/* Popup styles */
.popup {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: rgba(26, 26, 26, 0.95);
padding: 20px 40px;
border-radius: 12px;
border: 2px solid #333;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3);
z-index: 1000;
opacity: 1;
transition: opacity 0.3s ease;
}
.popup-content {
color: #ffffff;
font-size: 1.1rem;
text-align: center;
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

View File

@@ -1,119 +0,0 @@
// Main application entry point
import {
animateOnLoad,
animateSkillTags,
animateProjectCards,
addTypingAnimation,
} from './modules/animations.js';
import {
addMouseTrail,
addMatrixRain,
addCustomCursor,
addInteractiveBackground,
} from './modules/effects.js';
import { addEasterEggs, startAutoGlitch, addKeyboardInteractions } from './modules/easter-eggs.js';
import { addSoundEffects } from './modules/sound.js';
import {
addMicroInteractions,
addTextEffects,
addScrollEffects,
addButtonMorphing,
} from './modules/interactions.js';
import { getNavElements } from './modules/dom.js';
// Navigation functions
function showContentSection(sectionName) {
hideAllContentSections();
const targetSection = document.getElementById(`${sectionName}-content`);
if (targetSection) {
targetSection.classList.add('active');
setTimeout(() => {
targetSection.style.opacity = '1';
}, 10);
}
}
function hideAllContentSections() {
const { contentSections } = getNavElements();
contentSections.forEach((section) => {
section.classList.remove('active');
section.style.opacity = '0';
});
}
// Initialize navigation
function initNavigation() {
const { navButtons, closeButtons, contentSections } = getNavElements();
navButtons.forEach((button) => {
button.addEventListener('click', function (e) {
const section = this.getAttribute('data-section');
if (section) {
e.preventDefault();
showContentSection(section);
}
});
});
closeButtons.forEach((button) => {
button.addEventListener('click', function (e) {
e.preventDefault();
hideAllContentSections();
});
});
contentSections.forEach((section) => {
section.addEventListener('click', function (e) {
if (e.target === this) {
hideAllContentSections();
}
});
});
document.addEventListener('keydown', function (e) {
if (e.key === 'Escape') {
hideAllContentSections();
}
});
}
// Initialize all features
function initializeApp() {
// Navigation
initNavigation();
// Animations
animateOnLoad();
animateSkillTags();
animateProjectCards();
addTypingAnimation();
// Visual effects
addMouseTrail();
addMatrixRain();
addCustomCursor();
addInteractiveBackground();
// Easter eggs
addEasterEggs();
startAutoGlitch();
addKeyboardInteractions();
// Sound
addSoundEffects();
// Interactions
addMicroInteractions();
addTextEffects();
addScrollEffects();
addButtonMorphing();
}
// Start when DOM is ready
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initializeApp);
} else {
initializeApp();
}

View File

@@ -1,122 +0,0 @@
// Animation utilities and functions
import { DOM, getMainElements } from './dom.js';
// Animate elements on page load
export function animateOnLoad() {
const { avatar, brandName, disclaimer } = getMainElements();
const navButtons = DOM.getAll('.nav-button');
// Set initial states
if (avatar) {
avatar.style.opacity = '0';
avatar.style.transform = 'translateY(30px)';
}
if (brandName) {
brandName.style.opacity = '0';
brandName.style.transform = 'translateY(30px)';
}
navButtons.forEach((button, index) => {
button.style.opacity = '0';
button.style.transform = 'translateX(30px)';
button.style.transitionDelay = `${index * 0.1}s`;
});
if (disclaimer) {
disclaimer.style.opacity = '0';
}
// Animate in sequence
setTimeout(() => {
if (avatar) {
avatar.style.transition = 'all 0.8s ease';
avatar.style.opacity = '1';
avatar.style.transform = 'translateY(0)';
}
}, 200);
setTimeout(() => {
if (brandName) {
brandName.style.transition = 'all 0.8s ease';
brandName.style.opacity = '1';
brandName.style.transform = 'translateY(0)';
}
}, 400);
setTimeout(() => {
navButtons.forEach((button) => {
button.style.transition = 'all 0.6s ease';
button.style.opacity = '1';
button.style.transform = 'translateX(0)';
});
}, 600);
setTimeout(() => {
if (disclaimer) {
disclaimer.style.transition = 'all 0.8s ease';
disclaimer.style.opacity = '1';
}
}, 800);
}
// Typing animation
export function typeWriter(element, text, speed = 100) {
if (!element) return;
let i = 0;
element.innerHTML = '';
function type() {
if (i < text.length) {
element.innerHTML += text.charAt(i);
i++;
setTimeout(type, speed);
}
}
type();
}
// Animate skill tags with stagger effect
export function animateSkillTags() {
const skillTags = DOM.getAll('.skill-tag');
skillTags.forEach((tag, index) => {
tag.style.opacity = '0';
tag.style.transform = 'translateY(20px)';
setTimeout(() => {
tag.style.transition = 'all 0.5s ease';
tag.style.opacity = '1';
tag.style.transform = 'translateY(0)';
}, index * 100);
});
}
// Animate project cards with stagger effect
export function animateProjectCards() {
const projectCards = DOM.getAll('.project-card');
projectCards.forEach((card, index) => {
card.style.opacity = '0';
card.style.transform = 'translateY(30px)';
setTimeout(() => {
card.style.transition = 'all 0.6s ease';
card.style.opacity = '1';
card.style.transform = 'translateY(0)';
}, index * 150);
});
}
// Add typing animation for tagline
export function addTypingAnimation() {
const { tagline } = getMainElements();
if (tagline) {
const originalText = tagline.textContent;
tagline.textContent = '';
setTimeout(() => {
typeWriter(tagline, originalText, 100);
}, 2000);
}
}

View File

@@ -1,42 +0,0 @@
// DOM utility functions and element caching
export const DOM = {
// Cache frequently accessed elements
cache: {},
// Get and cache element
get(selector) {
if (!this.cache[selector]) {
this.cache[selector] = document.querySelector(selector);
}
return this.cache[selector];
},
// Get all and cache elements
getAll(selector) {
if (!this.cache[selector]) {
this.cache[selector] = document.querySelectorAll(selector);
}
return this.cache[selector];
},
// Clear cache
clearCache() {
this.cache = {};
},
};
// Navigation elements
export const getNavElements = () => ({
navButtons: DOM.getAll('.nav-button'),
contentSections: DOM.getAll('.content-section'),
closeButtons: DOM.getAll('.close-button'),
});
// Main UI elements
export const getMainElements = () => ({
avatar: DOM.get('.avatar'),
brandName: DOM.get('.brand-name'),
tagline: DOM.get('.tagline'),
aboutText: DOM.get('.about-text'),
disclaimer: DOM.get('.disclaimer'),
});

View File

@@ -1,246 +0,0 @@
// Easter eggs and interactive features
import { getMainElements } from './dom.js';
// Show notification
export function showNotification(message) {
const notification = document.createElement('div');
notification.style.position = 'fixed';
notification.style.top = '20px';
notification.style.right = '20px';
notification.style.background = 'linear-gradient(135deg, #007acc, #00aaff)';
notification.style.color = 'white';
notification.style.padding = '1rem 2rem';
notification.style.borderRadius = '10px';
notification.style.boxShadow = '0 10px 30px rgba(0, 122, 204, 0.3)';
notification.style.zIndex = '10000';
notification.style.transform = 'translateX(100%)';
notification.style.transition = 'transform 0.3s ease';
notification.textContent = message;
document.body.appendChild(notification);
setTimeout(() => {
notification.style.transform = 'translateX(0)';
}, 100);
setTimeout(() => {
notification.style.transform = 'translateX(100%)';
setTimeout(() => {
notification.remove();
}, 300);
}, 3000);
}
// Konami Code activation
function activateKonamiCode() {
const body = document.body;
body.style.animation = 'rainbow 2s ease infinite';
const style = document.createElement('style');
style.textContent = `
@keyframes rainbow {
0% { filter: hue-rotate(0deg); }
100% { filter: hue-rotate(360deg); }
}
`;
document.head.appendChild(style);
setTimeout(() => {
body.style.animation = '';
style.remove();
}, 5000);
showNotification('🎉 Konami Code Activated! You found the secret!');
}
// Avatar Easter Egg
function activateAvatarEasterEgg() {
const { avatar } = getMainElements();
if (!avatar) return;
avatar.style.animation = 'spin 1s linear infinite, bounce 0.5s ease infinite';
const style = document.createElement('style');
style.textContent = `
@keyframes spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
@keyframes bounce {
0%, 100% { transform: translateY(0); }
50% { transform: translateY(-20px); }
}
`;
document.head.appendChild(style);
setTimeout(() => {
avatar.style.animation = '';
style.remove();
}, 3000);
showNotification('🔄 Avatar Spin Mode Activated!');
}
// Brand Easter Egg
function activateBrandEasterEgg() {
const { brandName } = getMainElements();
if (!brandName) return;
const originalText = brandName.textContent;
const glitchTexts = ['Z4n3D3v', 'Z@n3D3v', 'ZaneDev', 'ZANE_DEV', 'zanedev'];
let glitchIndex = 0;
const glitchInterval = setInterval(() => {
brandName.textContent = glitchTexts[glitchIndex];
glitchIndex = (glitchIndex + 1) % glitchTexts.length;
}, 100);
setTimeout(() => {
clearInterval(glitchInterval);
brandName.textContent = originalText;
}, 2000);
showNotification('⚡ Glitch Mode Activated!');
}
// Trigger glitch effect
export function triggerGlitch(durationMs = 1000) {
const { brandName } = getMainElements();
if (!brandName) return;
const originalText = brandName.textContent;
const glitchTexts = ['Z4n3D3v', 'Z@n3D3v', 'ZaneDev', 'ZANE_DEV', 'zanedev'];
let glitchIndex = 0;
const glitchInterval = setInterval(() => {
brandName.textContent = glitchTexts[glitchIndex];
glitchIndex = (glitchIndex + 1) % glitchTexts.length;
}, 100);
setTimeout(() => {
clearInterval(glitchInterval);
brandName.textContent = originalText;
}, durationMs);
}
// Auto Glitch Mode
export function startAutoGlitch() {
setTimeout(() => triggerGlitch(1000), 100);
setInterval(() => triggerGlitch(1000), 20000);
}
// Add all Easter eggs
export function addEasterEggs() {
let clickCount = 0;
const { avatar, brandName } = getMainElements();
// Konami Code
const konamiCode = [38, 38, 40, 40, 37, 39, 37, 39, 66, 65];
let konamiIndex = 0;
document.addEventListener('keydown', function (e) {
if (e.keyCode === konamiCode[konamiIndex]) {
konamiIndex++;
if (konamiIndex === konamiCode.length) {
activateKonamiCode();
konamiIndex = 0;
}
} else {
konamiIndex = 0;
}
});
// Avatar click counter
if (avatar) {
avatar.addEventListener('click', function () {
clickCount++;
if (clickCount === 5) {
activateAvatarEasterEgg();
clickCount = 0;
}
});
}
// Brand name secret
if (brandName) {
brandName.addEventListener('dblclick', function () {
activateBrandEasterEgg();
});
}
}
// Secret "ZANE" sequence
function activateSecretMode() {
const body = document.body;
body.style.animation = 'rainbow 1s ease infinite';
const style = document.createElement('style');
style.textContent = `
@keyframes rainbow {
0% { filter: hue-rotate(0deg); }
100% { filter: hue-rotate(360deg); }
}
`;
document.head.appendChild(style);
setTimeout(() => {
body.style.animation = '';
style.remove();
}, 3000);
showNotification('🎉 Secret "ZANE" sequence activated!');
}
// Keyboard Interactions
export function addKeyboardInteractions() {
let keySequence = [];
const secretKeys = ['z', 'a', 'n', 'e'];
document.addEventListener('keydown', function (e) {
keySequence.push(e.key.toLowerCase());
if (keySequence.length > secretKeys.length) {
keySequence.shift();
}
// Check for secret sequence
if (keySequence.join('') === secretKeys.join('')) {
activateSecretMode();
keySequence = [];
}
// Add visual feedback for key presses
const keyElement = document.createElement('div');
keyElement.textContent = e.key.toUpperCase();
keyElement.style.position = 'fixed';
keyElement.style.left = Math.random() * window.innerWidth + 'px';
keyElement.style.top = Math.random() * window.innerHeight + 'px';
keyElement.style.color = '#007acc';
keyElement.style.fontSize = '2rem';
keyElement.style.fontWeight = 'bold';
keyElement.style.pointerEvents = 'none';
keyElement.style.zIndex = '10000';
keyElement.style.animation = 'keyPress 1s ease-out forwards';
document.body.appendChild(keyElement);
setTimeout(() => {
keyElement.remove();
}, 1000);
});
// Add CSS for key press animation
const style = document.createElement('style');
style.textContent = `
@keyframes keyPress {
0% {
opacity: 1;
transform: scale(1) translateY(0);
}
100% {
opacity: 0;
transform: scale(0.5) translateY(-50px);
}
}
`;
document.head.appendChild(style);
}

View File

@@ -1,275 +0,0 @@
// Visual effects: particles, cursor, matrix rain, etc.
import { DOM } from './dom.js';
// Particle class for managing individual particles
class Particle {
constructor(x, y) {
this.x = x;
this.y = y;
this.size = Math.random() * 3 + 1;
this.speedY = Math.random() * 1 + 0.5;
this.speedX = (Math.random() - 0.5) * 0.5;
this.opacity = 1;
}
update() {
this.y += this.speedY;
this.x += this.speedX;
this.opacity -= 0.01;
}
draw(ctx) {
ctx.fillStyle = `rgba(0, 122, 204, ${this.opacity})`;
ctx.beginPath();
ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2);
ctx.fill();
}
}
// Add mouse trail effect with falling particles
export function addMouseTrail() {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
document.body.appendChild(canvas);
canvas.style.position = 'fixed';
canvas.style.top = '0';
canvas.style.left = '0';
canvas.style.pointerEvents = 'none';
canvas.style.zIndex = '1000';
function resizeCanvas() {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
}
window.addEventListener('resize', resizeCanvas);
resizeCanvas();
let particles = [];
let mouseX = 0;
let mouseY = 0;
document.addEventListener('mousemove', (e) => {
mouseX = e.clientX;
mouseY = e.clientY;
if (Math.random() > 0.5) {
particles.push(new Particle(mouseX, mouseY));
}
});
function animate() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
particles = particles.filter((particle) => {
particle.update();
particle.draw(ctx);
return (
particle.opacity > 0 &&
particle.y < canvas.height &&
particle.x > 0 &&
particle.x < canvas.width
);
});
requestAnimationFrame(animate);
}
animate();
}
// Matrix Rain Effect
export function addMatrixRain() {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
canvas.style.position = 'fixed';
canvas.style.top = '0';
canvas.style.left = '0';
canvas.style.width = '100%';
canvas.style.height = '100%';
canvas.style.pointerEvents = 'none';
canvas.style.zIndex = '-1';
canvas.style.opacity = '0.1';
document.body.appendChild(canvas);
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
const matrix = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ123456789@#$%^&*()*&^%+-/~{[|`]}';
const matrixArray = matrix.split('');
const font_size = 10;
const columns = canvas.width / font_size;
const drops = [];
for (let x = 0; x < columns; x++) {
drops[x] = 1;
}
function drawMatrix() {
ctx.fillStyle = 'rgba(0, 0, 0, 0.04)';
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = '#007acc';
ctx.font = font_size + 'px arial';
for (let i = 0; i < drops.length; i++) {
const text = matrixArray[Math.floor(Math.random() * matrixArray.length)];
ctx.fillText(text, i * font_size, drops[i] * font_size);
if (drops[i] * font_size > canvas.height && Math.random() > 0.975) {
drops[i] = 0;
}
drops[i]++;
}
}
setInterval(drawMatrix, 35);
window.addEventListener('resize', () => {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
});
}
// Custom Cursor
export function addCustomCursor() {
const cursor = document.createElement('div');
cursor.className = 'custom-cursor';
document.body.appendChild(cursor);
const trail = document.createElement('div');
trail.className = 'custom-cursor-trail';
document.body.appendChild(trail);
let mouseX = 0,
mouseY = 0;
let trailX = 0,
trailY = 0;
document.addEventListener('mousemove', (e) => {
mouseX = e.clientX;
mouseY = e.clientY;
cursor.style.left = mouseX - 10 + 'px';
cursor.style.top = mouseY - 10 + 'px';
});
function animateTrail() {
trailX += (mouseX - trailX) * 0.1;
trailY += (mouseY - trailY) * 0.1;
trail.style.left = trailX - 4 + 'px';
trail.style.top = trailY - 4 + 'px';
requestAnimationFrame(animateTrail);
}
animateTrail();
const interactiveElements = DOM.getAll('a, button, .avatar, .brand-name');
interactiveElements.forEach((el) => {
el.addEventListener('mouseenter', () => {
cursor.style.transform = 'scale(2)';
cursor.style.background =
'radial-gradient(circle, rgba(255, 107, 107, 0.8) 0%, rgba(255, 107, 107, 0.4) 50%, transparent 100%)';
});
el.addEventListener('mouseleave', () => {
cursor.style.transform = 'scale(1)';
cursor.style.background =
'radial-gradient(circle, rgba(0, 122, 204, 0.8) 0%, rgba(0, 122, 204, 0.4) 50%, transparent 100%)';
});
});
}
// Interactive Background
export function addInteractiveBackground() {
const canvas = document.createElement('canvas');
canvas.style.position = 'fixed';
canvas.style.top = '0';
canvas.style.left = '0';
canvas.style.width = '100%';
canvas.style.height = '100%';
canvas.style.pointerEvents = 'none';
canvas.style.zIndex = '-2';
canvas.style.opacity = '0.3';
document.body.appendChild(canvas);
const ctx = canvas.getContext('2d');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
const particles = [];
const particleCount = 50;
class BgParticle {
constructor() {
this.x = Math.random() * canvas.width;
this.y = Math.random() * canvas.height;
this.vx = (Math.random() - 0.5) * 2;
this.vy = (Math.random() - 0.5) * 2;
this.size = Math.random() * 3 + 1;
this.opacity = Math.random() * 0.5 + 0.2;
}
update() {
this.x += this.vx;
this.y += this.vy;
if (this.x < 0 || this.x > canvas.width) this.vx *= -1;
if (this.y < 0 || this.y > canvas.height) this.vy *= -1;
}
draw() {
ctx.beginPath();
ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2);
ctx.fillStyle = `rgba(0, 122, 204, ${this.opacity})`;
ctx.fill();
}
}
for (let i = 0; i < particleCount; i++) {
particles.push(new BgParticle());
}
function animate() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
particles.forEach((particle) => {
particle.update();
particle.draw();
});
// Draw connections
particles.forEach((particle, i) => {
particles.slice(i + 1).forEach((otherParticle) => {
const dx = particle.x - otherParticle.x;
const dy = particle.y - otherParticle.y;
const distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 100) {
ctx.beginPath();
ctx.moveTo(particle.x, particle.y);
ctx.lineTo(otherParticle.x, otherParticle.y);
ctx.strokeStyle = `rgba(0, 122, 204, ${0.1 * (1 - distance / 100)})`;
ctx.lineWidth = 1;
ctx.stroke();
}
});
});
requestAnimationFrame(animate);
}
animate();
window.addEventListener('resize', () => {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
});
}

View File

@@ -1,201 +0,0 @@
// Interactive UI elements and micro-interactions
import { DOM, getMainElements } from './dom.js';
// Add ripple effect
function createRipple(element, e) {
const ripple = document.createElement('span');
const rect = element.getBoundingClientRect();
const size = Math.max(rect.width, rect.height);
const x = e.clientX - rect.left - size / 2;
const y = e.clientY - rect.top - size / 2;
ripple.style.width = ripple.style.height = size + 'px';
ripple.style.left = x + 'px';
ripple.style.top = y + 'px';
ripple.style.position = 'absolute';
ripple.style.borderRadius = '50%';
ripple.style.background = 'rgba(0, 122, 204, 0.3)';
ripple.style.transform = 'scale(0)';
ripple.style.animation = 'ripple 0.6s linear';
ripple.style.pointerEvents = 'none';
element.style.position = 'relative';
element.style.overflow = 'hidden';
element.appendChild(ripple);
setTimeout(() => {
ripple.remove();
}, 600);
}
// Add micro-interactions
export function addMicroInteractions() {
const buttons = DOM.getAll('.nav-button');
// Magnetic effect to buttons
buttons.forEach((button) => {
button.addEventListener('mousemove', function (e) {
const rect = this.getBoundingClientRect();
const x = e.clientX - rect.left - rect.width / 2;
const y = e.clientY - rect.top - rect.height / 2;
this.style.transform = `translate(${x * 0.1}px, ${y * 0.1}px) scale(1.05)`;
});
button.addEventListener('mouseleave', function () {
this.style.transform = 'translate(0, 0) scale(1)';
});
});
// Tilt effect to avatar
const { avatar } = getMainElements();
if (avatar) {
avatar.addEventListener('mousemove', function (e) {
const rect = this.getBoundingClientRect();
const x = e.clientX - rect.left - rect.width / 2;
const y = e.clientY - rect.top - rect.height / 2;
const rotateX = (y / rect.height) * 20;
const rotateY = (x / rect.width) * -20;
this.style.transform = `perspective(1000px) rotateX(${rotateX}deg) rotateY(${rotateY}deg) scale(1.1)`;
});
avatar.addEventListener('mouseleave', function () {
this.style.transform = 'perspective(1000px) rotateX(0deg) rotateY(0deg) scale(1)';
});
}
// Ripple effect to all clickable elements
const clickableElements = DOM.getAll('a, button, .avatar, .brand-name');
clickableElements.forEach((element) => {
element.addEventListener('click', function (e) {
createRipple(this, e);
});
});
// Add CSS for ripple animation
const style = document.createElement('style');
style.textContent = `
@keyframes ripple {
to {
transform: scale(4);
opacity: 0;
}
}
`;
document.head.appendChild(style);
}
// Add text effects
export function addTextEffects() {
const { brandName, tagline } = getMainElements();
// Add letter-by-letter animation to brand name
if (brandName) {
const text = brandName.textContent;
brandName.innerHTML = '';
text.split('').forEach((letter, index) => {
const span = document.createElement('span');
span.textContent = letter === ' ' ? '\u00A0' : letter;
span.style.display = 'inline-block';
span.style.animation = `letterBounce 0.6s ease forwards`;
span.style.animationDelay = `${index * 0.1}s`;
span.style.opacity = '0';
brandName.appendChild(span);
});
}
// Add hover effect to tagline
if (tagline) {
tagline.addEventListener('mouseenter', function () {
this.style.transform = 'scale(1.1) rotate(1deg)';
this.style.textShadow = '0 0 20px rgba(0, 122, 204, 0.8)';
});
tagline.addEventListener('mouseleave', function () {
this.style.transform = 'scale(1) rotate(0deg)';
this.style.textShadow = 'none';
});
}
// Add CSS for letter animation
const style = document.createElement('style');
style.textContent = `
@keyframes letterBounce {
0% {
opacity: 0;
transform: translateY(20px) rotate(10deg);
}
50% {
transform: translateY(-10px) rotate(-5deg);
}
100% {
opacity: 1;
transform: translateY(0) rotate(0deg);
}
}
`;
document.head.appendChild(style);
}
// Add scroll effects
export function addScrollEffects() {
let ticking = false;
function updateScrollEffects() {
const scrolled = window.pageYOffset;
const parallax = scrolled * 0.5;
document.body.style.setProperty('--scroll', `${parallax}px`);
const { avatar } = getMainElements();
if (avatar) {
const scale = Math.max(0.8, 1 - scrolled * 0.001);
avatar.style.transform = `scale(${scale})`;
}
ticking = false;
}
function requestTick() {
if (!ticking) {
requestAnimationFrame(updateScrollEffects);
ticking = true;
}
}
window.addEventListener('scroll', requestTick);
}
// Button morphing effects
export function addButtonMorphing() {
const buttons = DOM.getAll('.nav-button');
buttons.forEach((button) => {
// Add pulse effect on focus
button.addEventListener('focus', function () {
this.style.animation = 'pulse 1s ease-in-out infinite';
});
button.addEventListener('blur', function () {
this.style.animation = '';
});
});
// Add CSS for button effects
const style = document.createElement('style');
style.textContent = `
@keyframes spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
@keyframes pulse {
0%, 100% { transform: scale(1); }
50% { transform: scale(1.05); }
}
`;
document.head.appendChild(style);
}

View File

@@ -1,54 +0,0 @@
// Sound effects
import { DOM } from './dom.js';
// Play sound effect
export function playSound(type) {
const audioContext = new (window.AudioContext || window.webkitAudioContext)();
if (type === 'click') {
const oscillator = audioContext.createOscillator();
const gainNode = audioContext.createGain();
oscillator.connect(gainNode);
gainNode.connect(audioContext.destination);
oscillator.frequency.setValueAtTime(800, audioContext.currentTime);
oscillator.frequency.exponentialRampToValueAtTime(400, audioContext.currentTime + 0.1);
gainNode.gain.setValueAtTime(0.1, audioContext.currentTime);
gainNode.gain.exponentialRampToValueAtTime(0.01, audioContext.currentTime + 0.1);
oscillator.start(audioContext.currentTime);
oscillator.stop(audioContext.currentTime + 0.1);
} else if (type === 'hover') {
const oscillator = audioContext.createOscillator();
const gainNode = audioContext.createGain();
oscillator.connect(gainNode);
gainNode.connect(audioContext.destination);
oscillator.frequency.setValueAtTime(600, audioContext.currentTime);
oscillator.frequency.exponentialRampToValueAtTime(800, audioContext.currentTime + 0.05);
gainNode.gain.setValueAtTime(0.05, audioContext.currentTime);
gainNode.gain.exponentialRampToValueAtTime(0.01, audioContext.currentTime + 0.05);
oscillator.start(audioContext.currentTime);
oscillator.stop(audioContext.currentTime + 0.05);
}
}
// Add sound effects to buttons
export function addSoundEffects() {
const buttons = DOM.getAll('.nav-button');
buttons.forEach((button) => {
button.addEventListener('click', function () {
playSound('click');
});
button.addEventListener('mouseenter', function () {
playSound('hover');
});
});
}

BIN
buttons/nostalga.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 603 B

View File

@@ -1,61 +0,0 @@
import js from '@eslint/js';
import html from 'eslint-plugin-html';
import prettier from 'eslint-config-prettier';
export default [
js.configs.recommended,
prettier,
{
files: ['**/*.js', '**/*.html'],
plugins: {
html,
},
languageOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
globals: {
console: 'readonly',
window: 'readonly',
document: 'readonly',
navigator: 'readonly',
requestAnimationFrame: 'readonly',
setTimeout: 'readonly',
setInterval: 'readonly',
clearInterval: 'readonly',
clearTimeout: 'readonly',
IntersectionObserver: 'readonly',
__dirname: 'readonly',
},
},
rules: {
'no-unused-vars': 'warn',
'no-console': 'off',
'prefer-const': 'warn',
'no-var': 'warn',
},
},
{
files: ['tests/**/*.js'],
languageOptions: {
globals: {
global: 'writable',
beforeEach: 'readonly',
describe: 'readonly',
it: 'readonly',
expect: 'readonly',
vi: 'readonly',
},
},
},
{
ignores: [
'node_modules/**',
'dist/**',
'build/**',
'*.min.js',
'*.min.css',
'script.js',
'styles.css',
],
},
];

View File

@@ -1,111 +1,42 @@
<!doctype html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta
name="description"
content="ZaneDev - A certified epik guy who likes to make random stuff. Check out my projects and get in touch!"
/>
<meta name="keywords" content="ZaneDev, developer, programming, projects, web development" />
<meta name="author" content="ZaneDev" />
<meta property="og:title" content="ZaneDev - Personal Website" />
<meta
property="og:description"
content="Bored guy who likes to make random stuff. Check out my projects and connect with me!"
/>
<meta property="og:type" content="website" />
<meta property="og:url" content="https://zanedev.com" />
<meta name="robots" content="index, follow" />
<title>ZaneDev :P</title>
<link rel="icon" type="image/jpeg" href="assets/images/Zane.jpg" />
<link rel="stylesheet" href="assets/css/styles.css" />
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap"
rel="stylesheet"
/>
</head>
<body>
<main class="container" role="main" aria-label="Main content">
<!-- Avatar Section -->
<header class="avatar-section" role="banner">
<div class="avatar" tabindex="0" role="img" aria-label="ZaneDev's profile avatar">
<img src="assets/images/Zane.jpg" alt="ZaneDev's Profile Picture" class="profile-image" />
</div>
<h1 class="brand-name" tabindex="0">ZaneDev</h1>
<p class="tagline">Certified Epik Guy</p>
</header>
<!-- About Section -->
<section class="about-section" aria-labelledby="about-heading">
<h2 id="about-heading" class="sr-only">About</h2>
<p class="about-text">Bored guy who likes to make random stuff.</p>
<p class="not-for-hire-note" aria-live="polite">(not for hire btw)</p>
</section>
<!-- Navigation Section -->
<nav class="navigation" role="navigation" aria-label="Main navigation">
<a
href="https://zdtt.zane.org"
class="nav-button"
target="_blank"
rel="noopener noreferrer"
aria-label="Explore and try ZDTT (opens in new tab)"
>
<span>ZDTT</span>
</a>
<a
href="https://github.com/ZaneThePython"
class="nav-button"
target="_blank"
rel="noopener noreferrer"
aria-label="Visit GitHub profile (opens in new tab)"
>
<span>GitHub</span>
</a>
<a
href="mailto:contact@zane.org"
class="nav-button"
aria-label="Send email to contact@zane.org"
>
<span>Contact</span>
</a>
</nav>
<!-- Disclaimer -->
<footer class="disclaimer" role="contentinfo">
<p><em>*Note* This website is not FDA approved</em></p>
</footer>
</main>
<!-- Screen reader only class for accessibility -->
<style>
.sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border-width: 0;
}
</style>
<script type="module" src="assets/js/main.js"></script>
<!-- Add structured data for better SEO -->
<script type="application/ld+json">
{
"@context": "http://schema.org",
"@type": "Person",
"name": "ZaneDev",
"url": "https://zanedev.com",
"sameAs": ["https://github.com/ZaneThePython"]
}
</script>
</body>
</html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Zane :P</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<header>
<a href="index.html"><img class="logo" src="logo.gif" alt="logo"></a>
<br>
<br>
<img class="eeto" src="https://anlucas.neocities.org/under-con.gif" alt="Under Construction">
</header>
<h1 class="content">Welcome to my website :D</h1>
<p>Hello! I am Zane, a young person that feels like 2000s internet was peak. If you are web surfing then I think you willl enjoy going to the <a href="links.html">links page</a> but if you are just here randomly then settle down and enjoy the song I picked out for you.</p>
<div class="music">
<h1>Background Music</h1>
<audio controls autoplay loop>
<source src="music.wav" type="audio/wav">
Your browser does not support the audio element.
</audio>
<p>Music by <a href="https://tangermusic.bandcamp.com">Tanger</a> (not me)</p>
</div>
<footer>
<br><br><br>
<img class="eeto" src="">
<img class="eeto" src="https://jack-dawlia.neocities.org/image/gif/i-love-physical-media-button-cd.gif">
<img class="eeto" src="https://jack-dawlia.neocities.org/image/blue-favourite.gif">
<img class="eeto" src="https://jack-dawlia.neocities.org/image/i-love-portal.gif">
<img class="eeto" src="https://jack-dawlia.neocities.org/image/gif/jd-house-huh-button.gif">
<img class="eeto" src="https://jack-dawlia.neocities.org/image/gif/button-lover-button.gif">
<img class="eeto" src="https://jack-dawlia.neocities.org/image/gif/bad-at-spelling-button.gif">
<img class="eeto" src="https://jack-dawlia.neocities.org/image/right-to-repair.gif">
<img class="eeto" src="https://anlucas.neocities.org/browser.gif">
<img class="eeto" src="https://anlucas.neocities.org/linux_now.gif">
<img class="eeto" src="https://anlucas.neocities.org/piracy.gif">
<img class="eeto" src="https://anlucas.neocities.org/prod_625_17265.gif">
</footer>
</body>
</html>

32
links.html Normal file
View File

@@ -0,0 +1,32 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Zane :P</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<header>
<a href="index.html"><img class="logo" src="logo.gif" alt="logo"></a>
<br>
<br>
<img class="eeto" src="https://anlucas.neocities.org/under-con.gif" alt="Under Construction">
</header>
<h1>Check out my internet neighbors!</h1>
<p>These people are on the internet and have cool websites to link to on my website.</p>
<a class="why-are-you-looking-at-my-code" href="https://melonking.net"><img class="why-are-you-looking-at-my-code" src="https://lu.tiny-universes.net/images-links/melonking.gif" alt="Melonking"></a>
<a class="why-are-you-looking-at-my-code" href="https://plumbum.neocities.org/"><img class="why-are-you-looking-at-my-code" src="https://plumbum.neocities.org/Button.png"></a>
<a class="why-are-you-looking-at-my-code" href="https://nostalgic.neocities.org"><img class="why-are-you-looking-at-my-code" src="https://zane.org/buttons/nostalgic.gif"></a>
<a class="why-are-you-looking-at-my-code" href="https://cinni.net"><img class="why-are-you-looking-at-my-code" src="https://cinni.net/images/button/cinni_angelbutton3.gif"></a>
<a class="why-are-you-looking-at-my-code" href="https://hotlinecafe.com"><img class="why-are-you-looking-at-my-code" src="https://hotlinecafe.com/buttons/hotlinecafebutton3.gif"></a>
<a class="why-are-you-looking-at-my-code" href="https://raining-starss.neocities.org/index.html"><img class="why-are-you-looking-at-my-code" src="https://raining-starss.neocities.org/raining-starss-button%20(1).gif"></a>
<a class="why-are-you-looking-at-my-code" href="https://starlocked.neocities.org" "target="_blank"><img class="why-are-you-looking-at-my-code" src="https://64.media.tumblr.com/de2c05764f3939758461f8ff16f049c5/tumblr_punr4uSNSB1wqs2v9o1_100.gif"></a>
<h2>Link Me!</h2>
<p>Please add me to your links page using this simple line of code!</p>
<p>&lt;a href="https://zane.org"&gt;&lt;img src="https://zane.org/logo.gif"&gt;&lt;/a&gt;</p>
<a class="why-are-you-looking-at-my-code" href="https://zane.org"><img class="why-are-you-looking-at-my-code" src="https://zane.org/logo.gif"></a>
</body >
</html>

BIN
logo.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
music.wav Normal file

Binary file not shown.

7128
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,48 +0,0 @@
{
"name": "zanepersonal",
"version": "2.0.0",
"description": "Modern, interactive personal website with creative visual effects and animations",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview",
"test": "vitest",
"test:ui": "vitest --ui",
"test:run": "vitest run",
"coverage": "vitest run --coverage",
"lint": "eslint . --ext .js,.html",
"lint:fix": "eslint . --ext .js,.html --fix",
"format": "prettier --write \"**/*.{js,html,css,json,md}\"",
"format:check": "prettier --check \"**/*.{js,html,css,json,md}\""
},
"keywords": [
"personal-website",
"portfolio",
"interactive",
"web-animations",
"particle-effects",
"creative-coding"
],
"author": "ZaneDev <contact@zane.org>",
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/ZaneThePython/ZanePersonal.git"
},
"homepage": "https://zane.org",
"devDependencies": {
"@vitest/ui": "^4.0.8",
"eslint": "^9.39.1",
"eslint-config-prettier": "^10.1.8",
"eslint-plugin-html": "^8.1.3",
"imagemin": "^9.0.1",
"imagemin-mozjpeg": "^10.0.0",
"imagemin-pngquant": "^10.0.0",
"jsdom": "^27.1.0",
"prettier": "^3.6.2",
"terser": "^5.44.1",
"vite": "^7.2.2",
"vitest": "^4.0.8"
}
}

View File

@@ -1,8 +0,0 @@
User-agent: *
Allow: /
# Sitemap location
Sitemap: https://zane.org/sitemap.xml
# Crawl-delay (optional, adjust based on server capacity)
Crawl-delay: 1

1285
script.js

File diff suppressed because it is too large Load Diff

View File

@@ -1,9 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
<loc>https://zane.org/</loc>
<lastmod>2024-11-08</lastmod>
<changefreq>monthly</changefreq>
<priority>1.0</priority>
</url>
</urlset>

61
style.css Normal file
View File

@@ -0,0 +1,61 @@
body {
background-color: #101010;
display: flex;
flex-direction: column;
align-items: center;
}
header {
color: #fff;
padding: 20px;
text-align: center;
}
.content {
color: #fff;
padding: 20px;
text-align: center;
}
span {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
img {
height: 100px;
image-rendering: pixelated;
}
.logo {
height: 150px;
width: auto;
}
p {
color: #fff;
}
h1 {
color: #fff;
}
h2 {
color: #fff;
}
h3 {
color: #fff;
}
.eeto {
height: 31px;
width: auto;
image-rendering: pixelated;
}
a {
color: #5f95ff;
text-decoration: none;
}
.why-are-you-looking-at-my-code {
height: 62px;
width: auto;
image-rendering: pixelated;
margin: 1px;
display: inline;
}

View File

@@ -1,923 +0,0 @@
/* Reset and base styles */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Inter', sans-serif;
background-color: #1a1a1a;
color: #ffffff;
min-height: 100vh;
overflow-x: hidden;
position: relative;
cursor: none;
}
/* Custom Cursor */
.custom-cursor {
position: fixed;
width: 20px;
height: 20px;
background: radial-gradient(
circle,
rgba(0, 122, 204, 0.8) 0%,
rgba(0, 122, 204, 0.4) 50%,
transparent 100%
);
border-radius: 50%;
pointer-events: none;
z-index: 9999;
transition: transform 0.1s ease;
mix-blend-mode: difference;
}
.custom-cursor-trail {
position: fixed;
width: 8px;
height: 8px;
background: rgba(0, 122, 204, 0.6);
border-radius: 50%;
pointer-events: none;
z-index: 9998;
transition: all 0.3s ease;
}
/* Grid background */
body::before {
content: '';
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-image: radial-gradient(circle, #333 1px, transparent 1px);
background-size: 30px 30px;
background-position:
0 0,
15px 15px;
opacity: 0.3;
z-index: -1;
pointer-events: none;
animation: gridMove 20s linear infinite;
}
@keyframes gridMove {
0% {
transform: translate(0, 0);
}
100% {
transform: translate(30px, 30px);
}
}
/* Animated gradient background */
body::after {
content: '';
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: linear-gradient(-45deg, #1a1a1a, #2a2a2a, #1a1a1a, #333);
background-size: 400% 400%;
animation: gradientShift 15s ease infinite;
z-index: -2;
pointer-events: none;
}
@keyframes gradientShift {
0% {
background-position: 0% 50%;
}
50% {
background-position: 100% 50%;
}
100% {
background-position: 0% 50%;
}
}
/* Main container */
.container {
min-height: 100vh;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
position: relative;
padding: 2rem;
}
/* Avatar Section */
.avatar-section {
display: flex;
flex-direction: column;
align-items: center;
margin-bottom: 3rem;
position: relative;
filter: drop-shadow(0 0 20px rgba(0, 122, 204, 0.3));
}
.avatar {
width: 120px;
height: 120px;
border-radius: 50%;
background: linear-gradient(135deg, #2a2a2a, #1a1a1a);
border: 3px solid #333;
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 1.5rem;
box-shadow:
0 8px 32px rgba(0, 0, 0, 0.3),
0 0 0 0 rgba(0, 122, 204, 0.4),
inset 0 0 20px rgba(0, 122, 204, 0.1);
position: relative;
overflow: hidden;
transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
cursor: pointer;
transform-style: preserve-3d;
}
.avatar::before {
content: '';
position: absolute;
top: -3px;
left: -3px;
right: -3px;
bottom: -3px;
background: linear-gradient(45deg, #007acc, #00aaff, #007acc, #00aaff);
border-radius: 50%;
z-index: -1;
opacity: 0;
transition: opacity 0.3s ease;
animation: rotate 3s linear infinite;
}
.avatar:hover::before {
opacity: 1;
}
.avatar:hover {
transform: scale(1.1) rotateY(10deg) rotateX(5deg);
box-shadow:
0 15px 50px rgba(0, 0, 0, 0.4),
0 0 30px rgba(0, 122, 204, 0.6),
inset 0 0 30px rgba(0, 122, 204, 0.2);
border-color: #007acc;
}
@keyframes rotate {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
/* Profile Image */
.profile-image {
width: 100%;
height: 100%;
object-fit: cover;
border-radius: 50%;
animation:
sway 45s ease-in-out infinite,
pulseSlow 6s ease-in-out infinite;
}
@keyframes sway {
0% {
transform: rotate(-10deg);
}
50% {
transform: rotate(10deg);
}
100% {
transform: rotate(-10deg);
}
}
@keyframes pulseSlow {
0%,
100% {
transform: scale(1);
filter: drop-shadow(0 0 0 rgba(0, 122, 204, 0));
}
50% {
transform: scale(1.05);
filter: drop-shadow(0 0 12px rgba(0, 122, 204, 0.25));
}
}
/* Brand Name */
.brand-name {
font-size: 2.5rem;
font-weight: 600;
background: linear-gradient(135deg, #ffffff, #007acc, #00aaff, #ff6b6b, #4ecdc4);
background-size: 300% 300%;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
text-align: center;
letter-spacing: -0.02em;
margin-bottom: 0.5rem;
animation:
gradientText 4s ease infinite,
neonPulse 2s ease-in-out infinite alternate;
cursor: pointer;
transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
text-shadow:
0 0 5px rgba(0, 122, 204, 0.5),
0 0 10px rgba(0, 122, 204, 0.3),
0 0 15px rgba(0, 122, 204, 0.2);
position: relative;
}
.brand-name:hover {
transform: scale(1.1) rotateX(5deg);
filter: drop-shadow(0 0 20px rgba(0, 122, 204, 0.8));
text-shadow:
0 0 10px rgba(0, 122, 204, 0.8),
0 0 20px rgba(0, 122, 204, 0.6),
0 0 30px rgba(0, 122, 204, 0.4);
}
@keyframes gradientText {
0% {
background-position: 0% 50%;
}
50% {
background-position: 100% 50%;
}
100% {
background-position: 0% 50%;
}
}
@keyframes neonPulse {
0% {
filter: drop-shadow(0 0 5px rgba(0, 122, 204, 0.5));
text-shadow:
0 0 5px rgba(0, 122, 204, 0.5),
0 0 10px rgba(0, 122, 204, 0.3),
0 0 15px rgba(0, 122, 204, 0.2);
}
100% {
filter: drop-shadow(0 0 15px rgba(0, 122, 204, 0.8));
text-shadow:
0 0 10px rgba(0, 122, 204, 0.8),
0 0 20px rgba(0, 122, 204, 0.6),
0 0 30px rgba(0, 122, 204, 0.4);
}
}
/* Tagline */
.tagline {
font-size: 1.1rem;
color: #888;
text-align: center;
font-weight: 400;
margin-bottom: 2rem;
position: relative;
animation: fadeInUp 1s ease 0.8s both;
}
@keyframes fadeInUp {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
/* About Section */
.about-section {
max-width: 600px;
text-align: center;
margin-bottom: 3rem;
position: relative;
animation: slideInLeft 1s ease 1s both;
}
@keyframes slideInLeft {
from {
opacity: 0;
transform: translateX(-30px);
}
to {
opacity: 1;
transform: translateX(0);
}
}
.about-text {
font-size: 1.1rem;
line-height: 1.7;
color: #ccc;
font-weight: 400;
}
.not-for-hire-note {
margin-top: 0.5rem;
font-size: 0.9rem;
color: rgba(128, 128, 128, 0.5);
opacity: 0;
animation: fadeInOpacity 0.3s ease forwards;
animation-delay: 10s;
}
@keyframes fadeInOpacity {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
/* Navigation */
.navigation {
display: flex;
flex-direction: column;
gap: 1rem;
position: relative;
align-items: center;
animation: slideInRight 1s ease 1.2s both;
}
@keyframes slideInRight {
from {
opacity: 0;
transform: translateX(30px);
}
to {
opacity: 1;
transform: translateX(0);
}
}
.nav-button {
background: linear-gradient(135deg, #2a2a2a, #1a1a1a);
border: 2px solid #333;
border-radius: 12px;
padding: 1rem 2rem;
color: #ffffff;
font-size: 1.1rem;
font-weight: 500;
font-family: 'Inter', sans-serif;
cursor: pointer;
transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
min-width: 140px;
text-align: center;
position: relative;
overflow: hidden;
text-decoration: none;
display: inline-block;
box-shadow:
0 4px 15px rgba(0, 0, 0, 0.2),
0 0 0 0 rgba(0, 122, 204, 0.3);
transform-style: preserve-3d;
}
.nav-button::after {
content: '';
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background: linear-gradient(90deg, transparent, rgba(0, 122, 204, 0.3), transparent);
transition: left 0.6s ease;
}
.nav-button:hover::after {
left: 100%;
}
.nav-button.active {
background: #333;
border-color: #444;
}
.nav-button::before {
content: '';
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.1), transparent);
transition: left 0.5s ease;
}
.nav-button:hover::before {
left: 100%;
}
.nav-button:hover {
background: linear-gradient(135deg, #333, #2a2a2a);
border-color: #007acc;
transform: translateY(-5px) scale(1.05) rotateX(5deg);
box-shadow:
0 15px 40px rgba(0, 122, 204, 0.4),
0 0 20px rgba(0, 122, 204, 0.6);
text-shadow: 0 0 10px rgba(0, 122, 204, 0.8);
}
.nav-button:active {
transform: translateY(0);
}
/* Disclaimer */
.disclaimer {
position: fixed;
bottom: 2rem;
left: 2rem;
color: #666;
font-size: 0.9rem;
font-style: italic;
}
.disclaimer p {
margin: 0;
}
/* Content Sections */
.content-section {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(26, 26, 26, 0.95);
backdrop-filter: blur(10px);
display: none;
align-items: center;
justify-content: center;
z-index: 1000;
opacity: 0;
transition: opacity 0.3s ease;
}
.content-section.active {
display: flex;
opacity: 1;
}
.content-wrapper {
max-width: 800px;
padding: 2rem;
text-align: center;
}
.content-wrapper h2 {
font-size: 2.5rem;
margin-bottom: 2rem;
color: #ffffff;
}
.project-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 2rem;
margin-top: 2rem;
}
.project-card {
background: #2a2a2a;
border: 2px solid #333;
border-radius: 12px;
padding: 2rem;
transition: all 0.3s ease;
}
.project-card:hover {
transform: translateY(-5px);
border-color: #444;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3);
}
.project-card h3 {
color: #ffffff;
margin-bottom: 1rem;
font-size: 1.3rem;
}
.project-card p {
color: #ccc;
line-height: 1.6;
}
.github-link {
text-decoration: none;
display: inline-block;
margin-top: 2rem;
}
.github-button {
background: #333;
border: 2px solid #444;
border-radius: 12px;
padding: 1rem 2rem;
color: #ffffff;
font-size: 1.1rem;
font-weight: 500;
cursor: pointer;
transition: all 0.3s ease;
font-family: 'Inter', sans-serif;
}
.github-button:hover {
background: #444;
border-color: #555;
transform: translateY(-2px);
}
.contact-info {
margin-top: 2rem;
}
.contact-methods {
display: flex;
justify-content: center;
gap: 2rem;
margin-top: 2rem;
flex-wrap: wrap;
}
.contact-link {
color: #ffffff;
text-decoration: none;
padding: 1rem 2rem;
border: 2px solid #333;
border-radius: 12px;
background: #2a2a2a;
transition: all 0.3s ease;
font-weight: 500;
}
.contact-link:hover {
background: #333;
border-color: #444;
transform: translateY(-2px);
}
/* Projects Page Specific Styles */
.projects-main-content {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(26, 26, 26, 0.95);
backdrop-filter: blur(10px);
display: flex;
align-items: center;
justify-content: center;
z-index: 1000;
overflow-y: auto;
padding: 2rem 0;
}
.projects-intro {
color: #ccc;
font-size: 1.2rem;
margin-bottom: 3rem;
text-align: center;
}
.project-tech {
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
margin: 1rem 0;
}
.tech-tag {
background: #333;
color: #ffffff;
padding: 0.3rem 0.8rem;
border-radius: 20px;
font-size: 0.8rem;
font-weight: 500;
border: 1px solid #444;
}
.project-links {
display: flex;
gap: 1rem;
margin-top: 1.5rem;
}
.project-link {
color: #ffffff;
text-decoration: none;
padding: 0.5rem 1rem;
border: 1px solid #333;
border-radius: 8px;
background: #2a2a2a;
transition: all 0.3s ease;
font-size: 0.9rem;
font-weight: 500;
}
.project-link:hover {
background: #333;
border-color: #444;
transform: translateY(-1px);
}
.contact-message {
font-size: 1.5rem;
color: #ffffff;
font-weight: 500;
text-align: center;
margin-top: 2rem;
}
/* Skills Section */
.skills-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 2rem;
margin-top: 2rem;
}
.skill-category {
background: #2a2a2a;
border: 2px solid #333;
border-radius: 12px;
padding: 2rem;
transition: all 0.3s ease;
}
.skill-category:hover {
transform: translateY(-5px);
border-color: #444;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3);
}
.skill-category h3 {
color: #ffffff;
margin-bottom: 1.5rem;
font-size: 1.3rem;
text-align: center;
}
.skill-tags {
display: flex;
flex-wrap: wrap;
gap: 0.8rem;
justify-content: center;
}
.skill-tag {
background: linear-gradient(135deg, #333, #444);
color: #ffffff;
padding: 0.5rem 1rem;
border-radius: 25px;
font-size: 0.9rem;
font-weight: 500;
border: 1px solid #555;
transition: all 0.3s ease;
}
.skill-tag:hover {
background: linear-gradient(135deg, #444, #555);
transform: translateY(-2px);
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.3);
}
/* Featured Project Styles */
.project-card.featured {
border: 2px solid #007acc;
background: linear-gradient(135deg, #2a2a2a, #1f1f1f);
position: relative;
overflow: hidden;
}
.project-card.featured::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 3px;
background: linear-gradient(90deg, #007acc, #00aaff, #007acc);
animation: shimmer 2s infinite;
}
@keyframes shimmer {
0% {
transform: translateX(-100%);
}
100% {
transform: translateX(100%);
}
}
.project-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 1rem;
}
.featured-badge {
background: linear-gradient(135deg, #007acc, #00aaff);
color: #ffffff;
padding: 0.3rem 0.8rem;
border-radius: 15px;
font-size: 0.8rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.5px;
}
/* Close button for content sections */
.close-button {
position: absolute;
top: 2rem;
right: 2rem;
background: none;
border: none;
color: #666;
font-size: 2rem;
cursor: pointer;
transition: color 0.3s ease;
z-index: 1001;
}
.close-button:hover {
color: #ffffff;
transform: scale(1.1);
}
/* Scroll Animation Classes */
.animate-in {
animation: slideInUp 0.6s ease forwards;
}
@keyframes slideInUp {
from {
opacity: 0;
transform: translateY(30px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
/* Enhanced hover effects */
.project-card {
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
.project-card:hover {
transform: translateY(-8px) scale(1.02);
box-shadow: 0 15px 40px rgba(0, 0, 0, 0.4);
}
/* Improved tech tag styling */
.tech-tag {
background: linear-gradient(135deg, #333, #444);
color: #ffffff;
padding: 0.4rem 0.9rem;
border-radius: 20px;
font-size: 0.85rem;
font-weight: 500;
border: 1px solid #444;
transition: all 0.3s ease;
display: inline-block;
}
.tech-tag:hover {
background: linear-gradient(135deg, #444, #555);
transform: translateY(-2px);
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.3);
}
/* Responsive Design */
@media (max-width: 768px) {
.container {
padding: 1rem;
}
.avatar-section {
margin-bottom: 2rem;
}
.navigation {
align-items: center;
}
.brand-name {
font-size: 2rem;
}
.tagline {
font-size: 1rem;
}
.about-text {
font-size: 1rem;
}
.nav-button {
min-width: 120px;
padding: 0.8rem 1.5rem;
font-size: 1rem;
}
.content-wrapper {
padding: 1rem;
}
.content-wrapper h2 {
font-size: 2rem;
}
.project-grid {
grid-template-columns: 1fr;
gap: 1rem;
}
.skills-grid {
grid-template-columns: 1fr;
gap: 1rem;
}
.contact-methods {
flex-direction: column;
align-items: center;
gap: 1rem;
}
.disclaimer {
bottom: 1rem;
left: 1rem;
font-size: 0.8rem;
}
.close-button {
top: 1rem;
right: 1rem;
font-size: 1.5rem;
}
}
@media (max-width: 480px) {
.avatar {
width: 100px;
height: 100px;
}
.mug {
width: 50px;
height: 42px;
}
.mug-body {
width: 42px;
height: 34px;
}
.brand-name {
font-size: 1.8rem;
}
.nav-button {
min-width: 100px;
padding: 0.7rem 1.2rem;
font-size: 0.9rem;
}
}
/* Popup styles */
.popup {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: rgba(26, 26, 26, 0.95);
padding: 20px 40px;
border-radius: 12px;
border: 2px solid #333;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3);
z-index: 1000;
opacity: 1;
transition: opacity 0.3s ease;
}
.popup-content {
color: #ffffff;
font-size: 1.1rem;
text-align: center;
}

View File

@@ -1,40 +0,0 @@
import { describe, it, expect, beforeEach, vi } from 'vitest';
import { typeWriter } from '../assets/js/modules/animations.js';
describe('Animations Module', () => {
beforeEach(() => {
vi.useFakeTimers();
});
describe('typeWriter', () => {
it('should type text character by character', async () => {
const element = document.createElement('div');
document.body.appendChild(element);
const text = 'Hello';
typeWriter(element, text, 50);
// Wait for first setTimeout to execute
await vi.runAllTimersAsync();
// Complete text should be shown
expect(element.innerHTML).toBe('Hello');
});
it('should handle null element gracefully', () => {
expect(() => {
typeWriter(null, 'text', 50);
}).not.toThrow();
});
it('should handle empty text', () => {
const element = document.createElement('div');
document.body.appendChild(element);
typeWriter(element, '', 50);
vi.advanceTimersByTime(100);
expect(element.innerHTML).toBe('');
});
});
});

View File

@@ -1,69 +0,0 @@
import { describe, it, expect, beforeEach } from 'vitest';
import { DOM, getNavElements, getMainElements } from '../assets/js/modules/dom.js';
describe('DOM Module', () => {
beforeEach(() => {
// Setup basic HTML structure
document.body.innerHTML = `
<div class="container">
<div class="avatar">Avatar</div>
<h1 class="brand-name">ZaneDev</h1>
<p class="tagline">Tagline</p>
<nav class="navigation">
<a href="#" class="nav-button">Button 1</a>
<a href="#" class="nav-button">Button 2</a>
</nav>
</div>
`;
});
describe('DOM utility', () => {
it('should cache and retrieve elements', () => {
const avatar = DOM.get('.avatar');
expect(avatar).toBeTruthy();
expect(avatar.textContent).toBe('Avatar');
// Should return cached version on second call
const avatarAgain = DOM.get('.avatar');
expect(avatarAgain).toBe(avatar);
});
it('should get all elements matching selector', () => {
const buttons = DOM.getAll('.nav-button');
expect(buttons).toHaveLength(2);
});
it('should clear cache', () => {
DOM.get('.avatar');
expect(Object.keys(DOM.cache).length).toBeGreaterThan(0);
DOM.clearCache();
expect(Object.keys(DOM.cache).length).toBe(0);
});
});
describe('getMainElements', () => {
it('should return main UI elements', () => {
const elements = getMainElements();
expect(elements.avatar).toBeTruthy();
expect(elements.brandName).toBeTruthy();
expect(elements.tagline).toBeTruthy();
});
});
describe('getNavElements', () => {
it('should return navigation elements', () => {
// Add required elements for navigation
document.body.innerHTML += `
<div class="content-section"></div>
<button class="close-button">Close</button>
`;
const elements = getNavElements();
expect(elements.navButtons).toBeTruthy();
expect(elements.navButtons.length).toBeGreaterThan(0);
});
});
});

View File

@@ -1,34 +0,0 @@
// Test setup file
import { beforeEach, vi } from 'vitest';
// Mock Web Audio API
global.AudioContext = vi.fn().mockImplementation(() => ({
createOscillator: vi.fn().mockReturnValue({
connect: vi.fn(),
frequency: {
setValueAtTime: vi.fn(),
exponentialRampToValueAtTime: vi.fn(),
},
start: vi.fn(),
stop: vi.fn(),
}),
createGain: vi.fn().mockReturnValue({
connect: vi.fn(),
gain: {
setValueAtTime: vi.fn(),
exponentialRampToValueAtTime: vi.fn(),
},
}),
destination: {},
currentTime: 0,
}));
// Mock requestAnimationFrame
global.requestAnimationFrame = vi.fn((callback) => setTimeout(callback, 16));
global.cancelAnimationFrame = vi.fn(clearTimeout);
// Reset DOM before each test
beforeEach(() => {
document.body.innerHTML = '';
document.head.innerHTML = '';
});

View File

@@ -1,49 +0,0 @@
import { defineConfig } from 'vite';
import { resolve } from 'path';
export default defineConfig({
root: '.',
build: {
outDir: 'dist',
assetsDir: 'assets',
minify: 'terser',
terserOptions: {
compress: {
drop_console: true,
drop_debugger: true,
},
},
rollupOptions: {
input: {
main: resolve(__dirname, 'index.html'),
},
output: {
assetFileNames: (assetInfo) => {
let extType = assetInfo.name.split('.').at(1);
if (/png|jpe?g|svg|gif|tiff|bmp|ico/i.test(extType)) {
extType = 'images';
}
if (/css/i.test(extType)) {
extType = 'css';
}
return `assets/${extType}/[name]-[hash][extname]`;
},
chunkFileNames: 'assets/js/[name]-[hash].js',
entryFileNames: 'assets/js/[name]-[hash].js',
},
},
},
server: {
port: 5173,
open: true,
},
test: {
globals: true,
environment: 'jsdom',
setupFiles: './tests/setup.js',
coverage: {
reporter: ['text', 'html'],
exclude: ['node_modules/', 'dist/', 'tests/'],
},
},
});