Complete website overhaul #28

Merged
Patrick_Pluto merged 7 commits from sageTheDm/pages:main into main 2025-10-21 20:44:30 +02:00
78 changed files with 3505 additions and 4049 deletions

1
.gitignore vendored Normal file
View file

@ -0,0 +1 @@
node_modules/

View file

@ -1,2 +0,0 @@
# pages

View file

@ -1,61 +0,0 @@
// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-3.0
/*
* InterstellarDevelopment website
* Copyright (C) 2024 interstellar_development
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
document.addEventListener("DOMContentLoaded", () => {
const menu = document.querySelector(".menu");
const burgerMenu = document.querySelector(".burger-menu");
if (!menu || !burgerMenu) {
console.warn(
"Menu or burger menu element not found. Ensure they exist in the DOM."
);
return;
}
// Toggle the menu visibility
function toggleMenu() {
menu.classList.toggle("active");
if (menu.classList.contains("active")) {
// Add click listener to close menu when clicking outside
document.addEventListener("click", closeMenu);
} else {
// Remove the click listener when menu is closed
document.removeEventListener("click", closeMenu);
}
}
// Close the menu if clicking outside of it
function closeMenu(event) {
if (
!menu.contains(event.target) &&
!event.target.classList.contains("burger-menu")
) {
menu.classList.remove("active");
document.removeEventListener("click", closeMenu);
}
}
// Attach click event to the burger menu button
burgerMenu.addEventListener("click", (event) => {
event.stopPropagation(); // Prevent click from immediately triggering closeMenu
toggleMenu();
});
});
// @license-end

View file

@ -1,6 +0,0 @@
This favicon was generated using the following graphics from Twitter Twemoji:
- Graphics Title: 1f680.svg
- Graphics Author: Copyright 2020 Twitter, Inc and other contributors (https://github.com/twitter/twemoji)
- Graphics Source: https://github.com/twitter/twemoji/blob/master/assets/svg/1f680.svg
- Graphics License: CC-BY 4.0 (https://creativecommons.org/licenses/by/4.0/)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 751 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

View file

@ -1 +0,0 @@
{"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"}

View file

@ -1,68 +0,0 @@
// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-3.0
/*
* interstellar_development website
* Copyright (C) 2024 interstellar_development
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
class Footer extends HTMLElement {
constructor() {
super();
}
connectedCallback() {
this.innerHTML = `
<center>
<footer>
<div class="footer-content">
<p>2024 Interstellar Development</p>
<!-- Hidden Button -->
<button class="secret-button">👀</button>
</div>
</footer>
</center>
`;
// Add event listener for button click
this.querySelector(".secret-button").addEventListener("click", () => {
window.open("secret/index.html", "_blank");
});
}
}
customElements.define("footer-component", Footer);
// CSS for the hidden button
const style = document.createElement("style");
style.textContent = `
.secret-button {
display: none;
position: absolute;
bottom: 10px;
right: 10px;
background-color: transparent;
border: none;
font-size: 24px;
cursor: pointer;
}
.footer-content:hover .secret-button {
display: block;
}
`;
document.head.appendChild(style);
// @license-end

View file

@ -1,46 +0,0 @@
// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-3.0
/*
* interstellar_development website
* Copyright (C) 2024 interstellar_development
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
class Header extends HTMLElement {
constructor() {
super();
}
connectedCallback() {
this.innerHTML = `
<header>
<div class="header-content">
<div><a href="index.html" class="project-name">Interstellar Development</a></div>
<button class="burger-menu" onclick="toggleMenu()"></button>
</div>
</header>
<div class="div-menu">
<ul class="menu">
<li><a href="index.html#project">Our Projects</a></li>
<li><a href="index.html#cards">Our Team</a></li>
<li><a href="index.html#about">About us</a></li>
<li><a href="index.html#vision">Our Vision</a></li>
</ul>
</div>
`;
}
}
customElements.define("header-component", Header);
// @license-end

Binary file not shown.

Before

Width:  |  Height:  |  Size: 156 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 545 KiB

View file

@ -1,233 +1,496 @@
<!--
interstellar development website
Copyright (C) 2024 interstellar_development
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<script src="dropdown.js" type="text/javascript" defer></script>
<script src="header.js" type="text/javascript" defer></script>
<script src="footer.js" type="text/javascript" defer></script>
<link rel="stylesheet" href="styles.css" />
<title>Interstellar Development | Free and Open Source Software</title>
<meta
name="description"
content="We develop high-quality free and open source software, with a focus on gaming solutions that respect user freedom."
/>
<!-- Font Awesome -->
<link
rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"
/>
<!-- Google Fonts -->
<link
href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&family=JetBrains+Mono:wght@400;500;600&display=swap"
rel="stylesheet"
/>
<!-- Main CSS -->
<link rel="stylesheet" href="src/styles/styles.css" />
<link
rel="apple-touch-icon"
sizes="180x180"
href="favicon_io/apple-touch-icon.png"
href="src/favicon/apple-touch-icon.png"
/>
<link
rel="icon"
type="image/png"
sizes="32x32"
href="favicon_io/favicon-32x32.png"
href="src/favicon/favicon-32x32.png"
/>
<link
rel="icon"
type="image/png"
sizes="16x16"
href="favicon_io/favicon-16x16.png"
href="src/favicon/favicon-16x16.png"
/>
<link rel="manifest" href="favicon_io/site.webmanifest" />
<title>Interstellar Development</title>
</head>
<body>
<!-- Custom header component -->
<header-component></header-component>
<!-- Animated background -->
<div class="animated-bg"></div>
<div class="stars" id="stars"></div>
<article>
<section id="project">
<h1>Our Projects</h1>
<h2>Our Games</h2>
<ul>
<a href="webGames/index.html" target="_blank" class="listElement">
<li>Web game collection</li>
</a>
</ul>
<ul>
<li>
<p>Previously we had more unfinished Games listed here.</p>
<p>
We decided against displaying them and giving people the
impression we are working on them currently.
</p>
<p>In the Future we will display the released games here</p>
</li>
</ul>
<h2>Our Other Projects</h2>
<ul>
<a href="foss_alternatives/" target="_blank" class="listElement">
<li>FOSS Alternatives</li>
</a>
<a href="react/" target="_blank" class="listElement">
<li>React</li>
</a>
</ul>
</section>
<!-- Cards section with team members -->
<h1>Our Team</h1>
<section class="cards" id="cards">
<div class="card">
<a
href="https://interstellardevelopment.org/code/Patrick_Pluto"
class="card-link"
target="_blank"
>
<img src="images/Patrick.png" alt="Patrick" />
<h3>Patrick_Pluto</h3>
<p>
The system administrator and our lead coder. He is the one you
will need to blame for bugs in the games
</p>
</a>
<!-- Navigation -->
<nav id="navigation">
<div class="nav-container">
<div class="logo">
<a href="#welcome-screen">Interstellar Development</a>
</div>
<div class="card">
<a
href="https://interstellardevelopment.org/code/sageTheDm"
class="card-link"
target="_blank"
>
<img src="images/sage.png" alt="Sage" />
<h3>sageTheDM</h3>
<p>
Our mostly competent web developer and secondary coder, if you
experience any bugs on the website or spelling mistake he is to
blame
</p>
</a>
</div>
<button
class="mobile-menu-btn"
id="burger-menu"
aria-label="Toggle navigation"
>
<i class="fas fa-bars"></i>
</button>
<div class="card">
<a
href="https://interstellardevelopment.org/code/Patrick_Pluto"
class="card-link"
target="_blank"
>
<img src="images/nicolas.png" alt="Patrick" />
<h3>St. Nicolaus</h3>
<ul class="nav-links" id="main-nav">
<li><a href="#projects">Projects</a></li>
<li><a href="#team">Team</a></li>
<li><a href="#about">About</a></li>
<li><a href="#vision">Vision</a></li>
<li><a href="#contact">Contact</a></li>
</ul>
</div>
</nav>
<main>
<!-- Hero Section -->
<section class="hero" id="welcome-screen">
<div class="container">
<div class="hero-content">
<h1>
Advancing <span class="highlight">Free Software</span> Development
</h1>
<p>
Our game level and asset designer. He is responsible for all
assets in our FreeFTF game. So if an asset looks ugly be mad at
him. Also, we are not sure if he is human or just a drunk wizard
cat but one thing is very clear he is still a novice at his job.
We develop high-quality free and open source software solutions,
with a focus on gaming and applications that respect user freedom.
</p>
</a>
<div class="hero-buttons">
<a href="#projects" class="btn btn-primary">
<i class="fas fa-rocket"></i>
View Our Projects
</a>
<a href="#about" class="btn btn-secondary">
<i class="fas fa-info-circle"></i>
Learn More
</a>
</div>
</div>
</div>
</section>
<!-- About Us section -->
<section id="about">
<h2>About Us</h2>
<p>
Welcome to Interstellar Development! We are a small, passionate
international team dedicated to transforming the programming world
into a free-and-open-source future. Our diverse backgrounds and
experiences fuel our commitment to creating free and open-source
software, particularly in the realm of gaming.
</p>
<p>
Our journey began with a shared vision: to better organize our efforts
in making free and open-source games more efficient and accessible. We
recognized that many current free software games are either lacking in
quality or simply do not exist. This realization inspired us to focus
on developing games that are unplayable on GNU/Linux and FreeBSD
systems, as well as creating free software alternatives for those in
need.
</p>
<p>
At Interstellar Development, we believe that true freedom for computer
users can only be achieved through the use of free software. Thats
why we are committed to licensing all of our projects under copyleft
free and open-source software licenses, ensuring that our games remain
free for everyone to enjoy, forever.
</p>
<p>
While we are not currently accepting donations, we welcome your
support in the form of feedback and suggestions for improvements. If
you wish to contribute financially, we encourage you to donate to the
Free Software Foundation, as without them, we would have never started
this.
</p>
<p>
Join us as we strive to create a vibrant community around free
software and gaming. Together, we can make a difference and pave the
way for a free future!
</p>
<!-- Projects Section -->
<section id="projects" class="section">
<div class="container">
<div class="section-header">
<h2 class="section-title">Our Projects</h2>
<p class="section-subtitle">
High-quality free software solutions built with passion and
dedication
</p>
</div>
<!-- Portfolio Filter Controls -->
<div class="portfolio-controls">
<div class="filter-buttons">
<button class="filter-btn active" data-filter="all">All</button>
<button class="filter-btn" data-filter="gaming">Gaming</button>
<button class="filter-btn" data-filter="software">
Software
</button>
<button class="filter-btn" data-filter="web">Web</button>
</div>
<div class="search-box">
<input
type="text"
id="project-search"
placeholder="Search projects..."
/>
<i class="fas fa-search"></i>
</div>
</div>
<div class="portfolio-grid">
<!-- Gaming Projects -->
<div class="portfolio-grid">
<portfolio-card
icon="fas fa-rocket"
title="Projects Coming Soon"
desc="We're currently working on exciting new free and open source software projects. Stay tuned for updates as we develop high-quality solutions that respect user freedom."
tags="all"
collaboration="Internal Project"
></portfolio-card>
</div>
</div>
</div>
</section>
<section id="vision">
<h2>Our Vision</h2>
<h2>Interstellar Development: Free Software is Our Passion</h2>
<p>
At Interstellar Development, we embarked on this journey to create a
more organized and efficient approach to developing free and
open-source software, with a particular focus on gaming. We recognized
a significant gap in the availability and quality of free software
games, which often fail to meet the expectations of users or simply do
not exist. Our mission is to fill this void and elevate the standards
of free gaming experiences.
</p>
<p>
Our primary goal is to target games that are currently unplayable on
GNU/Linux and FreeBSD systems. We aim to develop high-quality
alternatives that not only provide enjoyable gameplay but also adhere
to the principles of free software. Additionally, we are committed to
identifying and addressing other areas within the software ecosystem
that lack free alternatives, ensuring that users have access to a
diverse range of tools and applications that respect their freedom.
</p>
<p>
At Interstellar Development, we firmly believe that the path to true
freedom for computer users lies in the adoption and promotion of free
software. To this end, we are dedicated to licensing all of our
projects under copyleft free and open-source software licenses. This
commitment ensures that our games and software remain free for
everyone to play, modify, and share, fostering a culture of
collaboration and innovation within the community.
</p>
<p>
We understand that community involvement is crucial to our success.
While we are not currently accepting donations, we invite you to
support us by providing feedback, reporting issues, and suggesting
improvements. Your insights are invaluable in helping us refine our
projects and enhance the user experience.
</p>
<p>
If you feel compelled to contribute financially, we encourage you to
consider donating to the Free Software Foundation. Their unwavering
support and advocacy for free software principles have been
instrumental in our journey, and your contributions to them help
sustain the broader movement that enables us to create these games.
</p>
<p>
Together, we can build a vibrant community around free software and
gaming, paving the way for a future where everyone has access to
high-quality, open-source alternatives. Join us in our mission to make
a difference and champion the cause of free software for all!
</p>
</section>
</article>
<!-- Team Section -->
<section id="team" class="section">
<div class="container">
<div class="section-header">
<h2 class="section-title">Our Team</h2>
<p class="section-subtitle">
A small but dedicated international team committed to free
software
</p>
</div>
<!-- Custom footer component -->
<footer-component></footer-component>
<div class="team-grid">
<div class="team-member">
<div class="member-avatar">
<i class="fas fa-server"></i>
</div>
<h3>Patrick</h3>
<div class="team-role">
Lead Programmer & System Administrator
</div>
<p>
Patrick focuses on backend development and system
administration, managing our infrastructure and core application
logic with expertise in server-side technologies and DevOps.
</p>
</div>
<div class="team-member">
<div class="member-avatar">
<i class="fas fa-palette"></i>
</div>
<h3>Sage</h3>
<div class="team-role">Frontend Developer & Communications</div>
<p>
Sage focuses on frontend development and is in charge of
communication and UI/UX design, ensuring our projects have
intuitive interfaces and excellent user experiences.
</p>
</div>
</div>
</div>
</section>
<!-- About Section -->
<section id="about" class="section">
<div class="container">
<div class="section-header">
<h2 class="section-title">About Us</h2>
</div>
<div class="about-content">
<p>
Interstellar Development is a small international team dedicated
to advancing free and open source software. Our diverse
backgrounds and shared commitment to user freedom drive our
development philosophy.
</p>
<p>
We began with a vision to create high-quality free software,
particularly in gaming where such options are often limited. We
recognized that many free software games either lack polish or
simply don't exist, inspiring us to focus on this under-served
area.
</p>
<p>
At Interstellar Development, we believe true digital freedom can
only be achieved through free software. All our projects are
released under copyleft licenses, ensuring they remain free for
everyone to use, modify, and distribute.
</p>
<p>
While we don't currently accept donations, we welcome feedback and
suggestions. If you wish to support the free software movement, we
encourage donations to the Free Software Foundation, whose work
continues to inspire our journey.
</p>
</div>
</div>
</section>
<!-- Vision Section -->
<section id="vision" class="section">
<div class="container">
<div class="section-header">
<h2 class="section-title">Our Vision</h2>
</div>
<div class="vision-content">
<div class="vision-text">
<p>
Our mission is to create high-quality free software
alternatives, particularly for games that are unavailable on
free operating systems like GNU/Linux and FreeBSD. We aim to
address gaps in the software ecosystem where free alternatives
are lacking.
</p>
</div>
<div class="principles-grid">
<div class="principle-card">
<div class="principle-icon">
<i class="fas fa-unlock"></i>
</div>
<h4>Software Freedom</h4>
<p>
All our projects are released under copyleft licenses to
ensure user freedoms are protected and preserved.
</p>
</div>
<div class="principle-card">
<div class="principle-icon">
<i class="fas fa-users"></i>
</div>
<h4>Community Collaboration</h4>
<p>
We believe in building software with and for the community,
welcoming contributions and feedback.
</p>
</div>
<div class="principle-card">
<div class="principle-icon">
<i class="fas fa-star"></i>
</div>
<h4>Quality Standards</h4>
<p>
We strive to create software that matches or exceeds the
quality of proprietary alternatives.
</p>
</div>
</div>
</div>
</div>
</section>
<!-- Stats Section -->
<section class="stats-section">
<div class="container">
<div class="stats-container">
<div class="stat-item">
<div class="stat-number" data-count="0">0</div>
<div class="stat-label">Projects</div>
</div>
<div class="stat-item">
<div class="stat-number" data-count="2">0</div>
<div class="stat-label">Team Members</div>
</div>
<div class="stat-item">
<div class="stat-number" data-count="3">0</div>
<div class="stat-label">Years Active</div>
</div>
<div class="stat-item">
<div class="stat-number" data-count="100">0</div>
<div class="stat-label">% Free Software</div>
</div>
</div>
</div>
</section>
<!-- Contact Section -->
<section id="contact" class="section">
<div class="container">
<div class="section-header">
<h2 class="section-title">Get In Touch</h2>
<p class="section-subtitle">
Have questions about our projects or want to contribute? We'd love
to hear from you.
</p>
</div>
<div class="contact-container">
<div class="contact-info">
<div class="contact-methods">
<div class="contact-item">
<div class="contact-icon">
<i class="fas fa-envelope"></i>
</div>
<div class="contact-details">
<h4>Email</h4>
<a href="mailto:info@interstellardevelopment.org"
>info@interstellardevelopment.org</a
>
</div>
</div>
<div class="contact-item">
<div class="contact-icon">
<i class="fas fa-code-branch"></i>
</div>
<div class="contact-details">
<h4>Source Code</h4>
<a
href="https://interstellardevelopment.org/code/interstellar_development"
target="_blank"
>View Our Projects</a
>
</div>
</div>
</div>
</div>
<form class="contact-form" id="contact-form">
<div class="form-group">
<label for="name">Your Name</label>
<input type="text" id="name" name="name" required />
</div>
<div class="form-group">
<label for="email">Email Address</label>
<input type="email" id="email" name="email" required />
</div>
<div class="form-group">
<label for="subject">Subject</label>
<select id="subject" name="subject" required>
<option value="">Select a subject</option>
<option value="contribution">Project Contribution</option>
<option value="feedback">Feedback</option>
<option value="inquiry">General Inquiry</option>
<option value="bug">Bug Report</option>
<option value="other">Other</option>
</select>
</div>
<div class="form-group">
<label for="message">Message</label>
<textarea
id="message"
name="message"
rows="5"
required
></textarea>
</div>
<button type="submit" class="btn btn-primary">
<i class="fas fa-paper-plane"></i>
Send Message
</button>
</form>
</div>
</div>
</section>
<!-- Footer -->
<footer class="footer">
<div class="container">
<div class="footer-content">
<div class="footer-brand">
<div class="logo">Interstellar Development</div>
<p>
Developing free and open source software with a focus on user
freedom and quality.
</p>
<div class="footer-social">
<a
href="https://interstellardevelopment.org/code/interstellar_development"
class="social-link"
target="_blank"
aria-label="Source Code"
>
<i class="fas fa-code-branch"></i>
</a>
<a href="#" class="social-link" aria-label="Discord">
<i class="fab fa-discord"></i>
</a>
<a href="#" class="social-link" aria-label="Mastodon">
<i class="fab fa-mastodon"></i>
</a>
</div>
</div>
<div class="footer-links">
<h4>Navigation</h4>
<ul>
<li><a href="#projects">Projects</a></li>
<li><a href="#team">Team</a></li>
<li><a href="#about">About</a></li>
<li><a href="#vision">Vision</a></li>
<li><a href="#contact">Contact</a></li>
</ul>
</div>
<div class="footer-links">
<h4>Projects</h4>
<ul>
<li>
<a href="" target="_blank">Coming soon</a>
</li>
</ul>
</div>
<div class="footer-links">
<h4>Resources</h4>
<ul>
<li>
<a href="https://www.fsf.org/" target="_blank"
>Free Software Foundation</a
>
</li>
<li>
<a href="https://www.gnu.org/" target="_blank">GNU Project</a>
</li>
<li>
<a
href="https://interstellardevelopment.org/code/interstellar_development"
target="_blank"
>Source Code</a
>
</li>
<li>
<a href="mailto:info@interstellardevelopment.org"
>Contact Us</a
>
</li>
</ul>
</div>
</div>
<div class="footer-bottom">
<div class="footer-bottom-content">
<p>&copy; 2025 Interstellar Development. All rights reserved.</p>
<div class="footer-badges">
<span class="badge">
<i class="fas fa-heart"></i>
Committed to Free Software
</span>
</div>
</div>
</div>
</div>
</footer>
</main>
<!-- General scripts -->
<script src="src/js/main.js"></script>
<script src="src/js/navigation.js"></script>
<script src="src/js/form.js"></script>
<script src="src/js/portfolioCard.js"></script>
<script src="src/js/portfolioFilter.js"></script>
<script src="src/js/overview.js"></script>
<script src="src/js/sectionTracker.js"></script>
<!-- Animation scripts -->
<script type="module" src="src/js/animation/starBackground.js"></script>
</body>
</html>

View file

@ -1,47 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Game Landing Page</title>
<link rel="stylesheet" href="styles.css" />
<link
rel="apple-touch-icon"
sizes="180x180"
href="../../favicon_io/apple-touch-icon.png"
/>
<link
rel="icon"
type="image/png"
sizes="32x32"
href="../../favicon_io/favicon-32x32.png"
/>
<link
rel="icon"
type="image/png"
sizes="16x16"
href="../../favicon_io/favicon-16x16.png"
/>
<link rel="manifest" href="../../favicon_io/site.webmanifest" />
</head>
<body>
<div class="landing-page">
<div class="game-background">
<!-- Game is embedded or shown as background -->
<canvas id="gameCanvas"></canvas>
</div>
<div class="content">
<h1>Welcome to the Asteroid Game!</h1>
<p>
In this game, you control a spaceship that shoots at asteroids to
avoid destruction and collect items for power-ups.
</p>
<p>Your goal is to survive as long as possible while scoring points!</p>
<button onclick="window.location.href='secret.html'">Play Game</button>
</div>
</div>
<script src="app.js"></script>
</body>
</html>

View file

@ -1,414 +0,0 @@
"use strict";
// Canvas setup
const canvas = document.getElementById("gameCanvas");
const ctx = canvas.getContext("2d");
const targetFPS = 60;
const targetFrameTime = 1000 / targetFPS;
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
let lastFrameTime = performance.now();
// Game elements
const player = {
x: canvas.width / 2,
y: canvas.height - 60,
width: 40,
height: 40,
color: "white",
speed: 5,
dx: 0,
};
let bullets = [];
let asteroids = [];
let items = [];
let score = 0;
let totalBulletsFired = 0;
let isGameOver = false;
let lastBulletTime = 0;
let ammo = 100; // Ammo counter
// Difficulty control
let asteroidSpawnInterval = 800; // Faster spawn rate
let asteroidSpeedMultiplier = 1.5; // Faster asteroids from the start
let difficultyIncreaseRate = 0.2; // Faster scaling every 10 seconds
// Controls
let canShoot = true; // Flag to control shooting
let rapidFireActive = false;
let shotgunActive = false;
let rainbowActive = false;
let lastShotgunTime = 0;
// Controls for sphere effects
let blueSphereCooldown = 0;
let yellowSphereCooldown = 0;
let greenSphereCooldown = 0;
let rainbowSphereCooldown = 0;
// Sphere types
const sphereTypes = ["blue", "yellow", "green", "rainbow"];
/// Control for left button press and release
function btnMoveLeft(isPressed) {
if (isPressed) {
player.dx = -player.speed; // Start moving left
} else {
player.dx = 0; // Stop moving when button is released
}
}
// Control for shoot button click (simulates spacebar press)
function btnShoot() {
if (canShoot && !isGameOver) {
shootBullet();
canShoot = false; // Prevent shooting until the button is "released"
}
}
// Control for right button press and release
function btnMoveRight(isPressed) {
if (isPressed) {
player.dx = player.speed; // Start moving right
} else {
player.dx = 0; // Stop moving when button is released
}
}
document.getElementById("shootBtn").addEventListener("mouseup", () => {
canShoot = true; // Allow shooting again when button is released
});
window.addEventListener("keydown", (e) => {
if (e.key === "ArrowLeft" || e.key === "a") player.dx = -player.speed;
if (e.key === "ArrowRight" || e.key === "d") player.dx = player.speed;
// Shoot only if it's not a hold, and we can shoot
if (e.key === " " && canShoot && !isGameOver) {
shootBullet();
canShoot = false; // Prevent shooting until the key is released
}
if (e.key === "r" && isGameOver) restartGame();
});
window.addEventListener("keyup", (e) => {
// Stop moving when either the arrow keys or the 'a'/'d' keys are released
if (
e.key === "ArrowLeft" ||
e.key === "ArrowRight" ||
e.key === "a" ||
e.key === "d"
) {
player.dx = 0;
}
// Allow shooting again when the space key is released
if (e.key === " ") {
canShoot = true;
}
});
// Bullet mechanics with cooldown
function shootBullet() {
const now = Date.now();
if (now - lastBulletTime < 100) return; // Enforce cooldown of 0.1 seconds
if (ammo <= 0) return; // Prevent shooting if ammo is empty
lastBulletTime = now;
ammo--; // Decrease ammo
totalBulletsFired++; // Increment total bullets fired
if (rapidFireActive) {
// If rapid fire is active, fire bullets continuously with a short delay
for (let i = 0; i < 3; i++) {
setTimeout(() => {
bullets.push({
x: player.x + player.width / 2 - 2.5,
y: player.y,
width: 5,
height: 10,
color: "yellow",
speed: 7,
});
}, i * 50); // Fire bullets with 50ms delay between shots
}
} else if (shotgunActive) {
// Shotgun effect, firing 3 bullets with a spread
for (let i = -1; i <= 1; i++) {
bullets.push({
x: player.x + player.width / 2 - 2.5,
y: player.y,
width: 5,
height: 10,
color: "yellow",
speed: 7,
angle: i * 10, // Spray the bullets at different angles
});
}
} else {
// Normal bullet
bullets.push({
x: player.x + player.width / 2 - 2.5,
y: player.y,
width: 5,
height: 10,
color: "yellow",
speed: 7,
});
}
}
// Generate random color
function getRandomColor() {
const colors = ["red", "blue", "green", "orange", "purple", "pink"];
return colors[Math.floor(Math.random() * colors.length)];
}
// Asteroid mechanics
function createAsteroid() {
const size = Math.random() * 40 + 30; // Bigger asteroids (min: 30, max: 70)
asteroids.push({
x: Math.random() * canvas.width,
y: -size,
width: size,
height: size,
color: getRandomColor(),
speed: (Math.random() * 3 + 2) * asteroidSpeedMultiplier, // Faster initial speed
});
}
// Item mechanics
function createItem() {
const randomType =
sphereTypes[Math.floor(Math.random() * sphereTypes.length)];
const size = 30;
const x = Math.random() * canvas.width;
items.push({
x: x,
y: -size,
width: size,
height: size,
type: randomType,
color:
randomType === "blue"
? "blue"
: randomType === "yellow"
? "yellow"
: randomType === "green"
? "green"
: "rainbow",
speed: 3,
});
}
// Update game elements
function updatePlayer() {
player.x += player.dx;
if (player.x < 0) player.x = 0;
if (player.x + player.width > canvas.width)
player.x = canvas.width - player.width;
}
function updateBullets() {
bullets.forEach((bullet, index) => {
bullet.y -= bullet.speed;
if (bullet.y + bullet.height < 0) bullets.splice(index, 1);
});
}
function updateAsteroids() {
asteroids.forEach((asteroid, index) => {
asteroid.y += asteroid.speed;
if (asteroid.y > canvas.height) asteroids.splice(index, 1);
});
}
function updateItems() {
items.forEach((item, index) => {
item.y += item.speed;
if (item.y > canvas.height) items.splice(index, 1);
// Check if player collects the item
if (
player.x < item.x + item.width &&
player.x + player.width > item.x &&
player.y < item.y + item.height &&
player.y + player.height > item.y
) {
applyItemEffect(item.type);
items.splice(index, 1); // Remove the item after it is collected
}
});
}
function applyItemEffect(type) {
let points = Math.floor(Math.random() * 5) + 1; // Random points between 1 and 5
if (type === "blue") {
rapidFireActive = true;
setTimeout(() => (rapidFireActive = false), 15000); // 15 seconds of rapid fire
} else if (type === "yellow") {
shotgunActive = true;
setTimeout(() => (shotgunActive = false), 30000); // 30 seconds of shotgun
} else if (type === "green") {
ammo = 100; // Refill ammo
} else if (type === "rainbow") {
rapidFireActive = true;
shotgunActive = true;
setTimeout(() => {
rapidFireActive = false;
shotgunActive = false;
}, 15000); // 15 seconds with all effects
}
score += points; // Add points when an item is collected
}
// Collision detection
function checkCollisions() {
bullets.forEach((bullet, bIndex) => {
asteroids.forEach((asteroid, aIndex) => {
if (
bullet.x < asteroid.x + asteroid.width &&
bullet.x + bullet.width > asteroid.x &&
bullet.y < asteroid.y + asteroid.height &&
bullet.y + bullet.height > asteroid.y
) {
bullets.splice(bIndex, 1);
asteroids.splice(aIndex, 1);
score += Math.floor(Math.random() * 5) + 1; // Add points when an asteroid is destroyed
// Visual feedback for destroyed asteroid
createExplosion(asteroid.x, asteroid.y);
}
});
});
asteroids.forEach((asteroid) => {
if (
player.x < asteroid.x + asteroid.width &&
player.x + player.width > asteroid.x &&
player.y < asteroid.y + asteroid.height &&
player.y + player.height > asteroid.y
) {
isGameOver = true;
}
});
}
// Explosion effect
function createExplosion(x, y) {
ctx.fillStyle = "yellow";
ctx.beginPath();
ctx.arc(x, y, 20, 0, Math.PI * 2);
ctx.fill();
setTimeout(() => ctx.clearRect(x - 20, y - 20, 40, 40), 200); // Clear explosion after 200ms
}
// Draw elements
function drawPlayer() {
ctx.fillStyle = player.color;
ctx.fillRect(player.x, player.y, player.width, player.height);
}
function drawBullets() {
bullets.forEach((bullet) => {
ctx.fillStyle = bullet.color;
ctx.fillRect(bullet.x, bullet.y, bullet.width, bullet.height);
});
}
function drawAsteroids() {
asteroids.forEach((asteroid) => {
ctx.fillStyle = asteroid.color;
ctx.fillRect(asteroid.x, asteroid.y, asteroid.width, asteroid.height);
});
}
function drawItems() {
items.forEach((item) => {
ctx.fillStyle = item.color;
ctx.beginPath();
ctx.arc(
item.x + item.width / 2,
item.y + item.height / 2,
item.width / 2,
0,
Math.PI * 2
);
ctx.fill();
});
}
function drawScore() {
ctx.fillStyle = "white";
ctx.font = "24px Arial";
ctx.fillText(`Score: ${score}`, 20, 40); // Score at top-left corner
}
function drawAmmo() {
ctx.fillStyle = "white";
ctx.font = "24px Arial";
ctx.fillText(`Ammo: ${ammo}`, 20, 70); // Ammo at top-left corner
}
function drawGameOver() {
if (isGameOver) {
ctx.fillStyle = "white";
ctx.font = "40px Arial";
ctx.textAlign = "center";
ctx.fillText("Game Over!", canvas.width / 2, canvas.height / 2 - 40);
ctx.font = "24px Arial";
ctx.fillText(`Total Score: ${score}`, canvas.width / 2, canvas.height / 2);
ctx.fillText(
`Bullets Fired: ${totalBulletsFired}`,
canvas.width / 2,
canvas.height / 2 + 40
);
ctx.fillText(
'Press "R" to Restart',
canvas.width / 2,
canvas.height / 2 + 80
);
}
}
// Restart game
function restartGame() {
window.location.reload();
}
// Main game loop
function gameLoop() {
const currentTime = performance.now();
const elapsedTime = currentTime - lastFrameTime;
if (elapsedTime >= targetFrameTime) {
lastFrameTime = currentTime - (elapsedTime % targetFrameTime);
ctx.clearRect(0, 0, canvas.width, canvas.height);
if (!isGameOver) {
updatePlayer();
updateBullets();
updateAsteroids();
updateItems();
checkCollisions();
}
drawPlayer();
drawBullets();
drawAsteroids();
drawItems();
drawScore();
drawAmmo();
drawGameOver();
if (!isGameOver) {
if (Math.random() < 0.01) createAsteroid(); // 1% chance every frame to spawn an asteroid
if (Math.random() < 0.005) createItem(); // 0.5% chance to spawn an item
}
}
requestAnimationFrame(gameLoop);
}
// Start game loop
requestAnimationFrame(gameLoop);

View file

@ -1,41 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Asteroid Shooter</title>
<link rel="stylesheet" href="style.css" />
<link
rel="apple-touch-icon"
sizes="180x180"
href="../../favicon_io/apple-touch-icon.png"
/>
<link
rel="icon"
type="image/png"
sizes="32x32"
href="../../favicon_io/favicon-32x32.png"
/>
<link
rel="icon"
type="image/png"
sizes="16x16"
href="../../favicon_io/favicon-16x16.png"
/>
<link rel="manifest" href="../../favicon_io/site.webmanifest" />
</head>
<body>
<canvas id="gameCanvas"></canvas>
<!-- Virtual buttons for mobile -->
<div class="controls">
<button id="leftBtn" class="control-btn">Left</button>
<button id="shootBtn" class="control-btn" onclick="btnShoot()">
Shoot
</button>
<button id="rightBtn" class="control-btn">Right</button>
</div>
<script src="game.js"></script>
</body>
</html>

View file

@ -1,43 +0,0 @@
body {
margin: 0;
overflow: hidden;
}
canvas {
display: block;
background: black;
width: 100%;
height: 100%;
}
.controls {
display: none;
position: absolute;
bottom: 20px;
left: 50%;
transform: translateX(-50%);
display: flex;
justify-content: space-between;
width: 80%;
}
.control-btn {
display: none;
padding: 10px;
font-size: 18px;
background-color: rgba(0, 0, 0, 0.6);
color: white;
border: 1px solid #fff;
border-radius: 5px;
cursor: pointer;
flex-grow: 1;
margin: 0 5px;
}
@media (max-width: 600px) {
.control-btn {
display: block;
font-size: 16px;
padding: 12px;
}
}

View file

@ -1,76 +0,0 @@
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body,
html {
height: 100%;
font-family: Arial, sans-serif;
display: flex;
justify-content: center;
align-items: center;
background-color: #000;
color: white;
overflow: hidden;
}
.landing-page {
position: relative;
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
}
.game-background {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.7); /* Dark overlay */
backdrop-filter: blur(8px); /* Apply blur effect to the background */
z-index: -1; /* Ensures it's in the background */
pointer-events: none; /* Prevent interaction with the blurred background */
}
.content {
text-align: center;
z-index: 1; /* Ensure content appears above the game background */
padding: 20px;
max-width: 600px; /* Limit the width of the content */
position: relative;
color: white;
backdrop-filter: blur(
8px
); /* Ensure content has some blur as well for contrast */
}
h1 {
font-size: 2rem;
margin-bottom: 20px;
}
p {
font-size: 1.2rem;
margin-bottom: 30px;
}
button {
padding: 12px 24px;
background-color: #ffcc00;
color: black;
border: none;
font-size: 18px;
cursor: pointer;
border-radius: 5px;
text-transform: uppercase;
transition: background-color 0.3s ease;
}
button:hover {
background-color: #ff9900;
}

View file

@ -1,33 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Endless runner</title>
<link
rel="apple-touch-icon"
sizes="180x180"
href="../../favicon_io/apple-touch-icon.png"
/>
<link
rel="icon"
type="image/png"
sizes="32x32"
href="../../favicon_io/favicon-32x32.png"
/>
<link
rel="icon"
type="image/png"
sizes="16x16"
href="../../favicon_io/favicon-16x16.png"
/>
<link rel="manifest" href="../../favicon_io/site.webmanifest" />
</head>
<body>
<div class="game-container">
<canvas id="gameCanvas"></canvas>
<button id="restartBtn" onclick="restartGame()">Restart</button>
</div>
<script src="script.js"></script>
</body>
</html>

View file

@ -1,90 +0,0 @@
import { useEffect, useRef, useState } from "react";
export default function EndlessRunner() {
const canvasRef = useRef(null);
const [running, setRunning] = useState(true);
const player = { x: 50, y: 150, width: 20, height: 20, dy: 0 };
const gravity = 0.5;
let obstacles = [];
let score = 0;
useEffect(() => {
const canvas = canvasRef.current;
const ctx = canvas.getContext("2d");
canvas.width = window.innerWidth;
canvas.height = 300;
function update() {
if (!running) return;
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Player physics
player.dy += gravity;
player.y += player.dy;
if (player.y > 150) {
player.y = 150;
player.dy = 0;
}
// Draw player
ctx.fillStyle = "blue";
ctx.fillRect(player.x, player.y, player.width, player.height);
// Obstacles
if (Math.random() < 0.02) {
obstacles.push({ x: canvas.width, y: 150, width: 20, height: 20 });
}
obstacles = obstacles.map((obstacle) => ({
...obstacle,
x: obstacle.x - 5,
}));
obstacles = obstacles.filter(
(obstacle) => obstacle.x + obstacle.width > 0
);
obstacles.forEach((obstacle) => {
ctx.fillStyle = "red";
ctx.fillRect(obstacle.x, obstacle.y, obstacle.width, obstacle.height);
// Collision detection
if (
player.x < obstacle.x + obstacle.width &&
player.x + player.width > obstacle.x &&
player.y < obstacle.y + obstacle.height &&
player.y + player.height > obstacle.y
) {
setRunning(false);
}
});
// Score
score++;
ctx.fillStyle = "black";
ctx.fillText("Score: " + score, 10, 20);
requestAnimationFrame(update);
}
update();
}, [running]);
function jump() {
if (player.y >= 150) {
player.dy = -10;
}
}
return (
<div className="flex flex-col items-center">
<canvas ref={canvasRef} className="border" onClick={jump}></canvas>
{!running && (
<button
onClick={() => window.location.reload()}
className="mt-4 bg-blue-500 text-white px-4 py-2 rounded"
>
Restart
</button>
)}
</div>
);
}

View file

@ -1,34 +0,0 @@
body {
margin: 0;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
background-color: #f4f4f4;
}
.game-container {
position: relative;
text-align: center;
}
canvas {
border: 2px solid black;
background-color: white;
display: block;
}
#restartBtn {
margin-top: 10px;
padding: 10px 20px;
font-size: 16px;
background-color: #007bff;
color: white;
border: none;
cursor: pointer;
display: none;
}
#restartBtn:hover {
background-color: #0056b3;
}

View file

@ -1,404 +0,0 @@
"use strict";
// Canvas setup
const canvas = document.getElementById("gameCanvas");
const ctx = canvas.getContext("2d");
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
// Game elements
const player = {
x: canvas.width / 2,
y: canvas.height - 60,
width: 40,
height: 40,
color: "white",
speed: 5,
dx: 0,
};
let bullets = [];
let asteroids = [];
let items = [];
let score = 0;
let totalBulletsFired = 0;
let isGameOver = false;
let lastBulletTime = 0;
let ammo = 100; // Ammo counter
// Difficulty control
let asteroidSpawnInterval = 800; // Faster spawn rate
let asteroidSpeedMultiplier = 1.5; // Faster asteroids from the start
let difficultyIncreaseRate = 0.2; // Faster scaling every 10 seconds
// Controls
let canShoot = true; // Flag to control shooting
let rapidFireActive = false;
let shotgunActive = false;
let rainbowActive = false;
let lastShotgunTime = 0;
// Controls for sphere effects
let blueSphereCooldown = 0;
let yellowSphereCooldown = 0;
let greenSphereCooldown = 0;
let rainbowSphereCooldown = 0;
// Sphere types
const sphereTypes = ["blue", "yellow", "green", "rainbow"];
/// Control for left button press and release
function btnMoveLeft(isPressed) {
if (isPressed) {
player.dx = -player.speed; // Start moving left
} else {
player.dx = 0; // Stop moving when button is released
}
}
// Control for shoot button click (simulates spacebar press)
function btnShoot() {
if (canShoot && !isGameOver) {
shootBullet();
canShoot = false; // Prevent shooting until the button is "released"
}
}
// Control for right button press and release
function btnMoveRight(isPressed) {
if (isPressed) {
player.dx = player.speed; // Start moving right
} else {
player.dx = 0; // Stop moving when button is released
}
}
document.getElementById("shootBtn").addEventListener("mouseup", () => {
canShoot = true; // Allow shooting again when button is released
});
window.addEventListener("keydown", (e) => {
if (e.key === "ArrowLeft" || e.key === "a") player.dx = -player.speed;
if (e.key === "ArrowRight" || e.key === "d") player.dx = player.speed;
// Shoot only if it's not a hold, and we can shoot
if (e.key === " " && canShoot && !isGameOver) {
shootBullet();
canShoot = false; // Prevent shooting until the key is released
}
if (e.key === "r" && isGameOver) restartGame();
});
window.addEventListener("keyup", (e) => {
// Stop moving when either the arrow keys or the 'a'/'d' keys are released
if (
e.key === "ArrowLeft" ||
e.key === "ArrowRight" ||
e.key === "a" ||
e.key === "d"
) {
player.dx = 0;
}
// Allow shooting again when the space key is released
if (e.key === " ") {
canShoot = true;
}
});
// Bullet mechanics with cooldown
function shootBullet() {
const now = Date.now();
if (now - lastBulletTime < 100) return; // Enforce cooldown of 0.1 seconds
if (ammo <= 0) return; // Prevent shooting if ammo is empty
lastBulletTime = now;
ammo--; // Decrease ammo
totalBulletsFired++; // Increment total bullets fired
if (rapidFireActive) {
// If rapid fire is active, fire bullets continuously with a short delay
for (let i = 0; i < 3; i++) {
setTimeout(() => {
bullets.push({
x: player.x + player.width / 2 - 2.5,
y: player.y,
width: 5,
height: 10,
color: "yellow",
speed: 7,
});
}, i * 50); // Fire bullets with 50ms delay between shots
}
} else if (shotgunActive) {
// Shotgun effect, firing 3 bullets with a spread
for (let i = -1; i <= 1; i++) {
bullets.push({
x: player.x + player.width / 2 - 2.5,
y: player.y,
width: 5,
height: 10,
color: "yellow",
speed: 7,
angle: i * 10, // Spray the bullets at different angles
});
}
} else {
// Normal bullet
bullets.push({
x: player.x + player.width / 2 - 2.5,
y: player.y,
width: 5,
height: 10,
color: "yellow",
speed: 7,
});
}
}
// Generate random color
function getRandomColor() {
const colors = ["red", "blue", "green", "orange", "purple", "pink"];
return colors[Math.floor(Math.random() * colors.length)];
}
// Asteroid mechanics
function createAsteroid() {
const size = Math.random() * 40 + 30; // Bigger asteroids (min: 30, max: 70)
asteroids.push({
x: Math.random() * canvas.width,
y: -size,
width: size,
height: size,
color: getRandomColor(),
speed: (Math.random() * 3 + 2) * asteroidSpeedMultiplier, // Faster initial speed
});
}
// Item mechanics
function createItem() {
const randomType =
sphereTypes[Math.floor(Math.random() * sphereTypes.length)];
const size = 30;
const x = Math.random() * canvas.width;
items.push({
x: x,
y: -size,
width: size,
height: size,
type: randomType,
color:
randomType === "blue"
? "blue"
: randomType === "yellow"
? "yellow"
: randomType === "green"
? "green"
: "rainbow",
speed: 3,
});
}
// Update game elements
function updatePlayer() {
player.x += player.dx;
if (player.x < 0) player.x = 0;
if (player.x + player.width > canvas.width)
player.x = canvas.width - player.width;
}
function updateBullets() {
bullets.forEach((bullet, index) => {
bullet.y -= bullet.speed;
if (bullet.y + bullet.height < 0) bullets.splice(index, 1);
});
}
function updateAsteroids() {
asteroids.forEach((asteroid, index) => {
asteroid.y += asteroid.speed;
if (asteroid.y > canvas.height) asteroids.splice(index, 1);
});
}
function updateItems() {
items.forEach((item, index) => {
item.y += item.speed;
if (item.y > canvas.height) items.splice(index, 1);
// Check if player collects the item
if (
player.x < item.x + item.width &&
player.x + player.width > item.x &&
player.y < item.y + item.height &&
player.y + player.height > item.y
) {
applyItemEffect(item.type);
items.splice(index, 1); // Remove the item after it is collected
}
});
}
function applyItemEffect(type) {
let points = Math.floor(Math.random() * 5) + 1; // Random points between 1 and 5
if (type === "blue") {
rapidFireActive = true;
setTimeout(() => (rapidFireActive = false), 15000); // 15 seconds of rapid fire
} else if (type === "yellow") {
shotgunActive = true;
setTimeout(() => (shotgunActive = false), 30000); // 30 seconds of shotgun
} else if (type === "green") {
ammo = 100; // Refill ammo
} else if (type === "rainbow") {
rapidFireActive = true;
shotgunActive = true;
setTimeout(() => {
rapidFireActive = false;
shotgunActive = false;
}, 15000); // 15 seconds with all effects
}
score += points; // Add points when an item is collected
}
// Collision detection
function checkCollisions() {
bullets.forEach((bullet, bIndex) => {
asteroids.forEach((asteroid, aIndex) => {
if (
bullet.x < asteroid.x + asteroid.width &&
bullet.x + bullet.width > asteroid.x &&
bullet.y < asteroid.y + asteroid.height &&
bullet.y + bullet.height > asteroid.y
) {
bullets.splice(bIndex, 1);
asteroids.splice(aIndex, 1);
score += Math.floor(Math.random() * 5) + 1; // Add points when an asteroid is destroyed
// Visual feedback for destroyed asteroid
createExplosion(asteroid.x, asteroid.y);
}
});
});
asteroids.forEach((asteroid) => {
if (
player.x < asteroid.x + asteroid.width &&
player.x + player.width > asteroid.x &&
player.y < asteroid.y + asteroid.height &&
player.y + player.height > asteroid.y
) {
isGameOver = true;
}
});
}
// Explosion effect
function createExplosion(x, y) {
ctx.fillStyle = "yellow";
ctx.beginPath();
ctx.arc(x, y, 20, 0, Math.PI * 2);
ctx.fill();
setTimeout(() => ctx.clearRect(x - 20, y - 20, 40, 40), 200); // Clear explosion after 200ms
}
// Draw elements
function drawPlayer() {
ctx.fillStyle = player.color;
ctx.fillRect(player.x, player.y, player.width, player.height);
}
function drawBullets() {
bullets.forEach((bullet) => {
ctx.fillStyle = bullet.color;
ctx.fillRect(bullet.x, bullet.y, bullet.width, bullet.height);
});
}
function drawAsteroids() {
asteroids.forEach((asteroid) => {
ctx.fillStyle = asteroid.color;
ctx.fillRect(asteroid.x, asteroid.y, asteroid.width, asteroid.height);
});
}
function drawItems() {
items.forEach((item) => {
ctx.fillStyle = item.color;
ctx.beginPath();
ctx.arc(
item.x + item.width / 2,
item.y + item.height / 2,
item.width / 2,
0,
Math.PI * 2
);
ctx.fill();
});
}
function drawScore() {
ctx.fillStyle = "white";
ctx.font = "24px Arial";
ctx.fillText(`Score: ${score}`, 20, 40); // Score at top-left corner
}
function drawAmmo() {
ctx.fillStyle = "white";
ctx.font = "24px Arial";
ctx.fillText(`Ammo: ${ammo}`, 20, 70); // Ammo at top-left corner
}
function drawGameOver() {
if (isGameOver) {
ctx.fillStyle = "white";
ctx.font = "40px Arial";
ctx.textAlign = "center";
ctx.fillText("Game Over!", canvas.width / 2, canvas.height / 2 - 40);
ctx.font = "24px Arial";
ctx.fillText(`Total Score: ${score}`, canvas.width / 2, canvas.height / 2);
ctx.fillText(
`Bullets Fired: ${totalBulletsFired}`,
canvas.width / 2,
canvas.height / 2 + 40
);
ctx.fillText(
'Press "R" to Restart',
canvas.width / 2,
canvas.height / 2 + 80
);
}
}
// Restart game
function restartGame() {
window.location.reload();
}
// Main game loop
function gameLoop() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
if (!isGameOver) {
updatePlayer();
updateBullets();
updateAsteroids();
updateItems();
checkCollisions();
}
drawPlayer();
drawBullets();
drawAsteroids();
drawItems();
drawScore();
drawAmmo();
drawGameOver();
if (!isGameOver) {
if (Math.random() < 0.01) createAsteroid(); // 1% chance every frame to spawn an asteroid
if (Math.random() < 0.005) createItem(); // 0.5% chance to spawn an item
}
requestAnimationFrame(gameLoop);
}
// Start game loop
gameLoop();

View file

@ -1,65 +0,0 @@
"use strict";
const targetNum = Math.trunc(Math.random() * 20) + 1;
let highScore = Number(localStorage.getItem("highscore")) || 0;
let userGuess = 10; // Default guess
let currScore = 20;
const screenEl = document.querySelector(".screen");
const msgEl = document.querySelector(".message");
const guessInput = document.querySelector("#guess");
const scoreEl = document.querySelector(".score");
const highScoreEl = document.querySelector(".highScore");
const checkBtn = document.querySelector("#check");
const restartBtn = document.querySelector("#restart");
const incBtn = document.querySelector("#up");
const decBtn = document.querySelector("#down");
const setMsg = (msg) => (msgEl.textContent = msg);
const setScore = (score) =>
(scoreEl.textContent = `Score: ${(currScore = score)}`);
const setHighScore = () => {
highScoreEl.textContent = `Highscore: ${highScore}`;
localStorage.setItem("highscore", highScore);
};
const changeColor = (color) => (screenEl.style.backgroundColor = color);
checkBtn.addEventListener("click", () => {
userGuess = Number(guessInput.textContent);
if (!userGuess || userGuess < 1 || userGuess > 20) {
setMsg("Please enter a valid number between 1 and 20.");
} else if (userGuess === targetNum) {
highScore = Math.max(highScore, currScore);
setHighScore();
setMsg(
currScore !== 20 ? "Correct Number!" : "Are you sure you didn't cheat?"
);
changeColor(currScore !== 20 ? "#1ba100" : "#FFC300");
} else {
setMsg(userGuess > targetNum ? "Too High!" : "Too Low!");
if (currScore > 1) {
setScore(currScore - 1);
} else {
setScore(1);
setMsg("You lost the game!");
changeColor("#a10000");
}
}
});
restartBtn.addEventListener("click", () => location.reload());
incBtn.addEventListener(
"click",
() =>
(guessInput.textContent = Math.min(Number(guessInput.textContent) + 1, 20))
);
decBtn.addEventListener(
"click",
() =>
(guessInput.textContent = Math.max(Number(guessInput.textContent) - 1, 1))
);
guessInput.textContent = userGuess;
setMsg("Guess a number");
setScore(currScore);
setHighScore();

View file

@ -1,72 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Guess My Number</title>
<link rel="stylesheet" href="styles.css" />
<link
rel="apple-touch-icon"
sizes="180x180"
href="../../favicon_io/apple-touch-icon.png"
/>
<link
rel="icon"
type="image/png"
sizes="32x32"
href="../../favicon_io/favicon-32x32.png"
/>
<link
rel="icon"
type="image/png"
sizes="16x16"
href="../../favicon_io/favicon-16x16.png"
/>
<link rel="manifest" href="../../favicon_io/site.webmanifest" />
</head>
<body>
<div class="gameboy">
<!-- Game Boy Screen -->
<div class="screen">
<article class="game">
<h1>Guess My Number - Game</h1>
<p class="message"></p>
<div class="guess-display">
<span id="guess"></span>
</div>
<p class="score"></p>
<p class="highScore"></p>
<div class="description">
<h2>Description</h2>
<p>Guess a number between 1 and 20</p>
<p>A = check</p>
<p>B = Reload</p>
<p>▲ = increases guess by one</p>
<p>▼ = decreases guess by one</p>
</div>
</article>
</div>
<!-- Controls -->
<div class="controls">
<!-- D-Pad on the left -->
<div class="dpad">
<button class="dpad-btn up" id="up"></button>
<button class="dpad-btn left" id="left"></button>
<div class="dpad-center"></div>
<button class="dpad-btn right" id="right"></button>
<button class="dpad-btn down" id="down"></button>
</div>
<!-- A and B Buttons on the right -->
<div class="action-buttons">
<button class="btn" id="check">A</button>
<button class="btn" id="restart">B</button>
</div>
</div>
</div>
<script src="game.js"></script>
<script src="styles.js"></script>
</body>
</html>

View file

@ -1,189 +0,0 @@
/* Base Reset */
body,
html {
margin: 0;
padding: 0;
font-family: monospace;
background-color: #3a2d56;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
}
/* GameBoy Layout */
.gameboy {
background-color: #5f4c82; /* Game Boy Color purple shell */
width: 441px;
height: 735px;
border-radius: 20px;
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.6);
display: flex;
flex-direction: column;
align-items: center;
padding: 10px;
position: relative;
}
@media (max-width: 768px) {
.gameboy {
width: 100vw;
height: 100vh;
border-radius: 0;
}
}
/* Screen */
.screen {
background-color: #306230; /* Game Boy green screen */
border: 4px solid #0f380f;
width: 90%;
height: 55%;
margin-top: 20px;
border-radius: 10px;
display: flex;
justify-content: center;
align-items: center;
box-shadow: inset 0 4px 8px rgba(0, 0, 0, 0.5);
overflow: hidden;
}
.game {
text-align: center;
width: 90%;
}
/* Titles */
h1 {
font-size: 2rem; /* Increased font size */
margin-bottom: 10px;
text-transform: uppercase;
}
/* Guess Display */
.guess-display {
background-color: #9bbc0f;
color: #0f380f;
border: 2px solid #0f380f;
font-size: 2rem; /* Increased font size */
width: 80px; /* Increased width */
text-align: center;
margin: 10px auto;
padding: 10px; /* Increased padding */
border-radius: 4px;
}
/* Messages */
.message,
.score,
.highScore {
font-size: 1.4rem; /* Increased font size */
margin: 5px 0;
}
.description,
.description p {
font-size: 1.2rem;
margin: 0 auto;
padding: 0 auto;
}
/* Controls Section */
.controls {
margin-top: 20px;
display: flex;
justify-content: space-between;
width: 80%;
align-items: center;
}
/* D-Pad */
.dpad {
position: relative;
width: 120px; /* Increased size */
height: 120px; /* Increased size */
}
/* Base Styling for D-Pad Buttons */
.dpad-btn {
background-color: #0f380f;
color: #9bbc0f;
border: none;
border-radius: 5px;
position: absolute;
width: 42px;
height: 42px;
font-size: 1.5rem; /* Increased size */
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
z-index: 1;
}
.dpad-btn.up {
top: 0;
left: 50%;
transform: translateX(-50%);
}
.dpad-btn.down {
bottom: 0;
left: 50%;
transform: translateX(-50%);
}
.dpad-btn.left {
top: 50%;
left: 0;
transform: translateY(-50%);
}
.dpad-btn.right {
top: 50%;
right: 0;
transform: translateY(-50%);
}
/* D-Pad Center to Connect Buttons */
.dpad-center {
background-color: #0f380f;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 40px;
height: 40px;
border: 2px solid #9cbc0f00;
z-index: 0;
border-radius: 5px;
}
/* A and B Buttons */
.action-buttons {
display: flex;
flex-direction: column;
justify-content: space-between;
height: 140px; /* Increased height */
}
.btn {
background-color: #0f380f;
color: #9bbc0f;
border: 2px solid #9bbc0f;
border-radius: 50%;
width: 60px;
height: 60px;
font-size: 1.8rem; /* Increased font size */
cursor: pointer;
transition: transform 0.1s, background-color 0.2s;
}
.btn:hover {
background-color: #9bbc0f;
color: #0f380f;
}
.btn:active {
transform: scale(0.9);
}

View file

@ -1,148 +0,0 @@
"use strict";
const left = document.querySelector("#left");
const right = document.querySelector("#right");
const gameboy = document.querySelector(".gameboy");
const html = document.documentElement;
const body = document.body;
const screen = document.querySelector(".screen");
const dpadButtons = document.querySelectorAll(".dpad-btn");
const dpadCenter = document.querySelector(".dpad-center"); // Darker variant
const actionButtons = document.querySelectorAll(".btn");
const colors = [
{
gameboyColor: "#B39DDB",
htmlColor: "#D1C4E9",
screenColor: "#E1BEE7",
buttonColor: "#673AB7",
buttonTextColor: "#FFFFFF",
dpadCenterColor: "#5E35B1",
},
{
gameboyColor: "#FFC107",
htmlColor: "#FFF9C4",
screenColor: "#FFEB3B",
buttonColor: "#FF9800",
buttonTextColor: "#000000",
dpadCenterColor: "#EF6C00",
},
{
gameboyColor: "#8BC34A",
htmlColor: "#C5E1A5",
screenColor: "#A5D6A7",
buttonColor: "#FF5722",
buttonTextColor: "#FFFFFF",
dpadCenterColor: "#E64A19",
},
{
gameboyColor: "#F44336",
htmlColor: "#FFCDD2",
screenColor: "#EF9A9A",
buttonColor: "#E91E63",
buttonTextColor: "#FFFFFF",
dpadCenterColor: "#C2185B",
},
{
gameboyColor: "#03A9F4",
htmlColor: "#BBDEFB",
screenColor: "#90CAF9",
buttonColor: "#FFEB3B",
buttonTextColor: "#000000",
dpadCenterColor: "#0277BD",
},
{
gameboyColor: "#FF7043",
htmlColor: "#FFCCBC",
screenColor: "#FFAB91",
buttonColor: "#FF5722",
buttonTextColor: "#FFFFFF",
dpadCenterColor: "#D84315",
},
{
gameboyColor: "#9C27B0",
htmlColor: "#E1BEE7",
screenColor: "#D1C4E9",
buttonColor: "#7B1FA2",
buttonTextColor: "#FFFFFF",
dpadCenterColor: "#6A1B9A",
},
{
gameboyColor: "#FFD700",
htmlColor: "#FFF9C4",
screenColor: "#FFF59D",
buttonColor: "#FF9800",
buttonTextColor: "#FFFFFF",
dpadCenterColor: "#F57F17",
},
{
gameboyColor: "#009688",
htmlColor: "#B2DFDB",
screenColor: "#80CBC4",
buttonColor: "#4CAF50",
buttonTextColor: "#FFFFFF",
dpadCenterColor: "#00796B",
},
{
gameboyColor: "#795548",
htmlColor: "#D7CCC8",
screenColor: "#A1887F",
buttonColor: "#9E9E9E",
buttonTextColor: "#000000",
dpadCenterColor: "#5D4037",
},
{
gameboyColor: "#FF5733",
htmlColor: "#FFCCCB",
screenColor: "#FFABAB",
buttonColor: "#C70039",
buttonTextColor: "#FFFFFF",
dpadCenterColor: "#B71C1C",
},
{
gameboyColor: "#00BCD4",
htmlColor: "#B2EBF2",
screenColor: "#80DEEA",
buttonColor: "#00ACC1",
buttonTextColor: "#FFFFFF",
dpadCenterColor: "#00838F",
},
];
let currentColorIndex = localStorage.getItem("gameboyColorIndex")
? parseInt(localStorage.getItem("gameboyColorIndex"))
: 0;
function updateGameBoyColor() {
gameboy.style.backgroundColor = colors[currentColorIndex].gameboyColor;
html.style.backgroundColor = colors[currentColorIndex].htmlColor;
body.style.backgroundColor = colors[currentColorIndex].htmlColor;
screen.style.backgroundColor = colors[currentColorIndex].screenColor;
dpadButtons.forEach((button) => {
button.style.backgroundColor = colors[currentColorIndex].buttonColor;
button.style.color = colors[currentColorIndex].buttonTextColor;
});
// Using darker dpad center color
dpadCenter.style.backgroundColor = colors[currentColorIndex].dpadCenterColor;
dpadCenter.style.color = colors[currentColorIndex].buttonTextColor;
actionButtons.forEach((button) => {
button.style.backgroundColor = colors[currentColorIndex].buttonColor;
button.style.color = colors[currentColorIndex].buttonTextColor;
});
}
left.addEventListener("click", () => {
currentColorIndex = (currentColorIndex - 1 + colors.length) % colors.length;
localStorage.setItem("gameboyColorIndex", currentColorIndex);
updateGameBoyColor();
});
right.addEventListener("click", () => {
currentColorIndex = (currentColorIndex + 1) % colors.length;
localStorage.setItem("gameboyColorIndex", currentColorIndex);
updateGameBoyColor();
});
updateGameBoyColor();

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 949 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 573 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 455 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 176 KiB

View file

@ -1,118 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Secret Game Collection</title>
<link rel="stylesheet" href="styles.css" />
<link
rel="apple-touch-icon"
sizes="180x180"
href="../favicon_io/apple-touch-icon.png"
/>
<link
rel="icon"
type="image/png"
sizes="32x32"
href="../favicon_io/favicon-32x32.png"
/>
<link
rel="icon"
type="image/png"
sizes="16x16"
href="../favicon_io/favicon-16x16.png"
/>
<link rel="manifest" href="../favicon_io/site.webmanifest" />
</head>
<body>
<header>
<h1>Secret Game Collection</h1>
</header>
<main>
<div class="grid-container">
<a
href="asteroidDestroyer/explenation.html"
target="_blank"
class="item"
>
<img src="images/asteroid.png" alt="Image can't be displayed" />
<h2>Secret Asteroid Shooter</h2>
<div class="description">
<p>
In this game, you control a spaceship that shoots at asteroids to
avoid destruction and collect items for power-ups.
</p>
<p>
Your goal is to survive as long as possible while scoring points!
</p>
</div>
</a>
<a href="#" target="_blank" class="item">
<img src="images/default.jpeg" alt="Image can't be displayed" />
<h2>Secret Blackjack</h2>
<div class="description">
<p>
Try to beat the dealer by getting a hand value as close to 21 as
possible without going over.
</p>
</div>
</a>
<a href="#" target="_blank" class="item">
<img src="images/default.jpeg" alt="Image can't be displayed" />
<h2>Snake</h2>
<div class="description">
<p>
Guide the snake to eat food and grow longer while avoiding
collisions with the walls and itself.
</p>
</div>
</a>
<a href="#" target="_blank" class="item">
<img src="images/default.jpeg" alt="Image can't be displayed" />
<h2>Solitaire</h2>
<div class="description">
<p>
A classic card game where the objective is to move all cards to
foundation piles in ascending order.
</p>
</div>
</a>
<a href="mineSweeper/index.html" target="_blank" class="item">
<img src="images/minesweeper.png" alt="Image can't be displayed" />
<h2>Minesweeper</h2>
<div class="description">
<p>
Uncover squares on a grid while avoiding hidden mines, using
numbers to deduce safe spots.
</p>
</div>
</a>
<a href="guessMyNumber/index.html" target="_blank" class="item">
<img src="images/number.jpeg" alt="Image can't be displayed" />
<h2>Guess My Number</h2>
<div class="description">
<p>
A simple game where you try to guess a randomly chosen number
within a certain range.
</p>
</div>
</a>
<a href="#" target="_blank" class="item">
<img src="images/default.jpeg" alt="Image can't be displayed" />
<h2>Endless Runner</h2>
<div class="description">
<p>
Run through an endless landscape, avoiding obstacles and
collecting items to score points.
</p>
</div>
</a>
</div>
</main>
<footer>
<p>&copy; 2025 Game Collection</p>
</footer>
</body>
</html>

View file

@ -1,60 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Minesweeper</title>
<link rel="stylesheet" href="styles.css" />
<link
rel="apple-touch-icon"
sizes="180x180"
href="../../favicon_io/apple-touch-icon.png"
/>
<link
rel="icon"
type="image/png"
sizes="32x32"
href="../../favicon_io/favicon-32x32.png"
/>
<link
rel="icon"
type="image/png"
sizes="16x16"
href="../../favicon_io/favicon-16x16.png"
/>
<link rel="manifest" href="../../favicon_io/site.webmanifest" />
</head>
<body>
<div id="settings">
<h1>Minesweeper</h1>
<label for="gridSize">Grid Size:</label>
<input
type="number"
id="gridSize"
min="6"
max="25"
value="9"
aria-label="Grid Size"
/>
<label for="bombs">Number of Bombs:</label>
<input
type="number"
id="bombs"
min="1"
max="300"
value="9"
aria-label="Number of Bombs"
/>
<button id="startGame">Start Game</button>
</div>
<div class="container">
<canvas id="game"></canvas>
</div>
<script src="script.js"></script>
</body>
</html>

View file

@ -1,192 +0,0 @@
document.getElementById("startGame").addEventListener("click", function () {
const gridSize = parseInt(document.getElementById("gridSize").value);
const bombs = parseInt(document.getElementById("bombs").value);
document.getElementById("settings").style.display = "none";
document.getElementById("game").style.display = "block";
renderGame(gridSize, bombs);
});
const canvas = document.getElementById("game");
const ctx = canvas.getContext("2d");
class Minesweeper {
constructor(width, height, bombs) {
this.size = 25;
this.field = new Array(width);
this.bombs = new Array(width);
for (let x = 0; x < this.field.length; x++) {
this.field[x] = new Array(height);
this.bombs[x] = new Array(height);
for (let y = 0; y < this.field[x].length; y++) {
this.field[x][y] = 1;
this.bombs[x][y] = false;
}
}
this.bombAmount =
bombs > (width * height) / 2 ? (width * height) / 2 : bombs;
this.width = width;
this.height = height;
this.firstClick = false;
this.drawField();
}
generateBombs() {
for (let i = 0; i < this.bombAmount; i++) {
let x = Math.floor(Math.random() * this.width);
let y = Math.floor(Math.random() * this.height);
this.bombs[x][y] || this.field[x][y] == 0
? i--
: (this.bombs[x][y] = true);
}
this.firstClick = true;
}
getNearbyBombs(x, y) {
let counter = 0;
for (let newX = x - 1; newX <= x + 1; newX++) {
for (let newY = y - 1; newY <= y + 1; newY++) {
if (
newX < this.field.length &&
newX >= 0 &&
newY < this.field[0].length &&
newY >= 0
) {
this.bombs[newX][newY] ? counter++ : {};
}
}
}
return counter;
}
checkWin() {
for (let x = 0; x < this.field.length; x++) {
for (let y = 0; y < this.field[x].length; y++) {
if (this.field[x][y] != 0 && !this.bombs[x][y]) {
return;
} else if (this.field[x][y] == 0 && this.bombs[x][y]) {
alert(`[ERROR]: Square (${x}|${y}) should not be a bomb!`);
this.bombs[x][y] = false;
}
}
}
this.drawField();
alert("You won!");
window.location.reload();
}
markSquare(x, y) {
if (x < this.field.length && y < this.field[0].length) {
switch (this.field[x][y]) {
case 1:
this.field[x][y]++;
break;
case 2:
this.field[x][y]++;
break;
case 3:
this.field[x][y] = 1;
break;
default:
break;
}
this.drawField();
}
}
uncoverSquare(x, y) {
if (x < this.field.length && x >= 0 && y < this.field[0].length && y >= 0) {
if (this.bombs[x][y] && this.field[x][y] == 1) {
alert("You lost!");
window.location.reload();
} else if (this.field[x][y] == 1) {
this.field[x][y] = 0;
!this.firstClick ? this.generateBombs() : {};
if (this.getNearbyBombs(x, y) == 0) {
for (let newX = x - 1; newX <= x + 1; newX++) {
for (let newY = y - 1; newY <= y + 1; newY++) {
if (
newX < this.field.length &&
newX >= 0 &&
newY < this.field[0].length &&
newY >= 0
) {
this.field[newX][newY] == 1
? this.uncoverSquare(newX, newY)
: {};
}
}
}
}
}
this.checkWin();
this.drawField();
}
}
drawSquare(x, y, type) {
ctx.lineWidth = 3;
let uncovered = (x + y) % 2 === 0 ? "#D3D3D3" : "#A9A9A9";
let covered = (x + y) % 2 === 0 ? "#4CAF50" : "#81C784";
ctx.fillStyle = type != 0 ? covered : uncovered;
ctx.fillRect(x * this.size, y * this.size, this.size, this.size);
ctx.strokeStyle = "#000";
ctx.strokeRect(x * this.size, y * this.size, this.size, this.size);
if (type != 1) {
const fontSize = this.size / 2;
const number = this.getNearbyBombs(x, y);
let finalPrint;
ctx.font = `${fontSize}px sans-serif`;
ctx.fillStyle = "#000";
type == 0 ? (finalPrint = number ? number : " ") : {};
type == 2 ? (finalPrint = "🚩") : {};
type == 3 ? (finalPrint = "❓") : {};
ctx.fillText(
finalPrint,
x * this.size + fontSize / (type == 0 ? 1.5 : 1.8),
y * this.size + fontSize * 1.3
);
}
}
drawField() {
if (window.innerWidth > window.innerHeight) {
this.size = window.innerHeight / (this.field[0].length + 4);
} else {
this.size = window.innerWidth / (this.field.length + 4);
}
canvas.width = this.size * this.field.length;
canvas.height = this.size * this.field[0].length;
const offsetX = (canvas.width - this.field.length * this.size) / 2;
const offsetY = (canvas.height - this.field[0].length * this.size) / 2;
for (let x = 0; x < this.field.length; x++) {
for (let y = 0; y < this.field[x].length; y++) {
this.drawSquare(x, y, this.field[x][y], offsetX, offsetY);
}
}
}
}
const renderGame = (gridSize, bombs) => {
let field = new Minesweeper(gridSize, gridSize, bombs);
window.addEventListener("resize", () => field.drawField());
canvas.addEventListener("click", (event) => {
const rect = canvas.getBoundingClientRect();
const x = Math.floor((event.clientX - rect.left) / field.size);
const y = Math.floor((event.clientY - rect.top) / field.size);
field.uncoverSquare(x, y);
});
canvas.addEventListener("contextmenu", (event) => {
event.preventDefault();
const rect = canvas.getBoundingClientRect();
const x = Math.floor((event.clientX - rect.left) / field.size);
const y = Math.floor((event.clientY - rect.top) / field.size);
field.markSquare(x, y);
});
};

View file

@ -1,104 +0,0 @@
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body,
html {
height: 100%;
margin: 0;
font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
background-color: #121212;
color: #e0e0e0;
}
#settings {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
text-align: center;
margin: auto;
background-color: #1e1e1e;
padding: 40px;
border-radius: 12px;
box-shadow: 0 6px 15px rgba(0, 0, 0, 0.5);
width: 100%;
max-width: 600px;
}
h1 {
font-size: 2.5em;
margin-bottom: 20px;
color: #007bff;
}
label {
margin-bottom: 12px;
font-size: 20px;
color: #d1d1d1;
}
input[type="number"],
input[type="range"],
span {
padding: 12px;
margin-bottom: 20px;
width: 100%;
max-width: 400px;
text-align: center;
border: 1px solid #444;
border-radius: 6px;
background-color: #333;
color: #e0e0e0;
font-size: 18px;
transition: border-color 0.3s ease;
}
input[type="number"]:focus,
input[type="range"]:focus {
border-color: #007bff;
outline: none;
}
button {
padding: 12px 24px;
background-color: #007bff;
color: white;
border: none;
border-radius: 6px;
cursor: pointer;
transition: background-color 0.3s ease, transform 0.2s ease;
font-size: 18px;
}
button:hover {
background-color: #0056b3;
transform: translateY(-2px);
}
button:active {
transform: translateY(0);
}
canvas {
display: none;
}
@media (max-width: 600px) {
#settings {
font-size: 16px;
}
input[type="number"],
input[type="range"],
button {
width: 90%;
max-width: none;
}
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -1,95 +0,0 @@
"use strict";
const cvs = document.getElementById("snake");
const ctx = cvs.getContext("2d");
const box = 20;
let snake = [{ x: 9 * box, y: 10 * box }];
let food = {
x: Math.floor(Math.random() * 19 + 1) * box,
y: Math.floor(Math.random() * 19 + 1) * box,
};
let score = 0;
let d;
let game;
document.addEventListener("keydown", direction);
function direction(event) {
let key = event.keyCode;
if ((key == 37 || key == 65) && d != "RIGHT") d = "LEFT";
else if ((key == 38 || key == 87) && d != "DOWN") d = "UP";
else if ((key == 39 || key == 68) && d != "LEFT") d = "RIGHT";
else if ((key == 40 || key == 83) && d != "UP") d = "DOWN";
}
function collision(head, array) {
return array.some((part) => head.x == part.x && head.y == part.y);
}
function draw() {
ctx.fillStyle = "#0f380f";
ctx.fillRect(0, 0, cvs.width, cvs.height);
ctx.fillStyle = "red";
ctx.fillRect(food.x, food.y, box, box);
for (let i = 0; i < snake.length; i++) {
ctx.fillStyle = i == 0 ? "lime" : "white";
ctx.fillRect(snake[i].x, snake[i].y, box, box);
ctx.strokeStyle = "black";
ctx.strokeRect(snake[i].x, snake[i].y, box, box);
}
let snakeX = snake[0].x;
let snakeY = snake[0].y;
if (d == "LEFT") snakeX -= box;
if (d == "UP") snakeY -= box;
if (d == "RIGHT") snakeX += box;
if (d == "DOWN") snakeY += box;
if (snakeX == food.x && snakeY == food.y) {
score++;
food = {
x: Math.floor(Math.random() * 19 + 1) * box,
y: Math.floor(Math.random() * 19 + 1) * box,
};
} else {
snake.pop();
}
let newHead = { x: snakeX, y: snakeY };
if (
snakeX < 0 ||
snakeX >= cvs.width ||
snakeY < 0 ||
snakeY >= cvs.height ||
collision(newHead, snake)
) {
clearInterval(game);
document.getElementById("restartBtn").style.display = "block";
return;
}
snake.unshift(newHead);
ctx.fillStyle = "white";
ctx.font = "20px Arial";
ctx.fillText("Score: " + score, 10, 20);
}
function restartGame() {
snake = [{ x: 9 * box, y: 10 * box }];
food = {
x: Math.floor(Math.random() * 19 + 1) * box,
y: Math.floor(Math.random() * 19 + 1) * box,
};
score = 0;
d = "";
document.getElementById("restartBtn").style.display = "none";
game = setInterval(draw, 150);
}
document.getElementById("restartBtn").addEventListener("click", restartGame);
game = setInterval(draw, 150);

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

View file

@ -1,67 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Snake - Game</title>
<link rel="stylesheet" href="styles.css" />
<link
rel="apple-touch-icon"
sizes="180x180"
href="../../favicon_io/apple-touch-icon.png"
/>
<link
rel="icon"
type="image/png"
sizes="32x32"
href="../../favicon_io/favicon-32x32.png"
/>
<link
rel="icon"
type="image/png"
sizes="16x16"
href="../../favicon_io/favicon-16x16.png"
/>
<link rel="manifest" href="../../favicon_io/site.webmanifest" />
</head>
<body>
<div class="gameboy">
<!-- Game Boy Screen -->
<div class="screen">
<article class="game">
<h1 class="title" id="title">Snake - Game</h1>
<div class="description" id="description">
<h2>Description</h2>
<p>Eat as many apples and grow as much as possible</p>
<p>◀ or A or arrow left = move left</p>
<p>▶ or D or arrow right = move right</p>
<p>▲ or W or arrow up = move up</p>
<p>▼ or S or arrow down = move down</p>
</div>
<canvas id="snake" width="400" height="400"></canvas>
</article>
</div>
<!-- Controls -->
<div class="controls">
<!-- D-Pad on the left -->
<div class="dpad">
<button class="dpad-btn up" id="up"></button>
<button class="dpad-btn left" id="left"></button>
<div class="dpad-center"></div>
<button class="dpad-btn right" id="right"></button>
<button class="dpad-btn down" id="down"></button>
</div>
<!-- A, B and start button on the right -->
<div class="action-buttons">
<button class="start-btn btn" id="start">Start</button>
<button class="btn" id="a">A</button>
<button class="btn" id="b">B</button>
</div>
</div>
</div>
<script src="game.js"></script>
<script src="styles.js"></script>
</body>
</html>

View file

@ -1,238 +0,0 @@
/* Base Reset */
body,
html {
margin: 0;
padding: 0;
font-family: monospace;
background-color: #3a2d56;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
}
/* GameBoy Layout */
.gameboy {
background-color: #5f4c82;
width: 441px;
height: 735px;
border-radius: 20px;
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.6);
display: flex;
flex-direction: column;
align-items: center;
padding: 10px;
position: relative;
}
@media (max-width: 768px) {
.gameboy {
width: 100vw;
height: 100vh;
border-radius: 0;
}
}
/* Screen */
.screen {
background-color: black;
border: 4px solid #0f380f;
width: 90%;
height: 55%;
margin-top: 20px;
border-radius: 10px;
display: flex;
justify-content: center;
align-items: center;
box-shadow: inset 0 4px 8px rgba(0, 0, 0, 0.5);
overflow: hidden;
}
.game {
text-align: center;
width: 90%;
}
/* Titles */
h1 {
font-size: 2rem;
margin-bottom: 10px;
text-transform: uppercase;
color: #9bbc0f;
}
.description,
.description p {
font-size: 1.2rem;
margin: 0 auto;
padding: 0 auto;
color: white;
}
/* Grid container */
#grid {
display: grid;
grid-template-columns: repeat(10, 1fr); /* Adjust to match gridSize */
grid-template-rows: repeat(10, 1fr); /* Adjust to match gridSize */
width: 400px; /* Adjust as needed */
height: 400px; /* Adjust as needed */
border: 2px solid #0f380f;
margin: 20px auto;
/* initially hide */
display: none;
}
/* Individual cells */
.cell {
width: 100%;
height: 100%;
}
.cell.light-green {
background-color: #9bbc0f;
}
.cell.dark-green {
background-color: #0f380f;
}
/* Snake styling */
.snake {
background-color: #e600ff; /* Snake color */
z-index: 1000;
}
/* Apple styling */
.apple {
background-color: red; /* Apple color */
z-index: 999;
}
/* Controls Section */
.controls {
margin-top: 20px;
display: flex;
justify-content: space-between;
width: 80%;
align-items: center;
}
/* D-Pad */
.dpad {
position: relative;
width: 120px;
height: 120px;
}
/* Base Styling for D-Pad Buttons */
.dpad-btn {
background-color: #0f380f;
color: #9bbc0f;
border: none;
border-radius: 5px;
position: absolute;
width: 42px;
height: 42px;
font-size: 1.5rem;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
z-index: 1;
}
.dpad-btn.up {
top: 0;
left: 50%;
transform: translateX(-50%);
}
.dpad-btn.down {
bottom: 0;
left: 50%;
transform: translateX(-50%);
}
.dpad-btn.left {
top: 50%;
left: 0;
transform: translateY(-50%);
}
.dpad-btn.right {
top: 50%;
right: 0;
transform: translateY(-50%);
}
/* D-Pad Center to Connect Buttons */
.dpad-center {
background-color: #0f380f;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 40px;
height: 40px;
border: 2px solid transparent;
z-index: 0;
border-radius: 5px;
}
/* A and B Buttons */
.action-buttons {
display: flex;
flex-direction: column;
justify-content: space-between;
height: 200px;
}
.btn {
background-color: #0f380f;
color: #9bbc0f;
border: 2px solid #9bbc0f;
border-radius: 50%;
width: 60px;
height: 60px;
font-size: 1.8rem;
cursor: pointer;
transition: transform 0.1s, background-color 0.2s;
}
.btn:hover {
background-color: #9bbc0f;
color: #0f380f;
}
.btn:active {
transform: scale(0.9);
}
/* Start Button */
.start-btn {
background-color: #0f380f;
color: #9bbc0f;
border: 2px solid #9bbc0f;
border-radius: 5px;
width: 100px;
height: 40px;
font-size: 1.2rem;
cursor: pointer;
transition: transform 0.1s, background-color 0.2s;
margin-bottom: 20px;
}
.start-btn:hover {
background-color: #9bbc0f;
color: #0f380f;
}
.start-btn:active {
transform: scale(0.9);
}
/* Hidden Canvas for Debugging or Fallback */
canvas {
display: none;
z-index: 1000;
}

View file

@ -1,134 +0,0 @@
"use strict";
const aBtn = document.querySelector("#a");
const bBtn = document.querySelector("#b");
const gameboy = document.querySelector(".gameboy");
const html = document.documentElement;
const body = document.body;
const dpadButtons = document.querySelectorAll(".dpad-btn");
const dpadCenter = document.querySelector(".dpad-center"); // Darker variant
const actionButtons = document.querySelectorAll(".btn");
const colors = [
{
gameboyColor: "#B39DDB",
htmlColor: "#D1C4E9",
buttonColor: "#673AB7",
buttonTextColor: "#FFFFFF",
dpadCenterColor: "#5E35B1",
},
{
gameboyColor: "#FFC107",
htmlColor: "#FFF9C4",
buttonColor: "#FF9800",
buttonTextColor: "#000000",
dpadCenterColor: "#EF6C00",
},
{
gameboyColor: "#8BC34A",
htmlColor: "#C5E1A5",
buttonColor: "#FF5722",
buttonTextColor: "#FFFFFF",
dpadCenterColor: "#E64A19",
},
{
gameboyColor: "#F44336",
htmlColor: "#FFCDD2",
buttonColor: "#E91E63",
buttonTextColor: "#FFFFFF",
dpadCenterColor: "#C2185B",
},
{
gameboyColor: "#03A9F4",
htmlColor: "#BBDEFB",
buttonColor: "#FFEB3B",
buttonTextColor: "#000000",
dpadCenterColor: "#0277BD",
},
{
gameboyColor: "#FF7043",
htmlColor: "#FFCCBC",
buttonColor: "#FF5722",
buttonTextColor: "#FFFFFF",
dpadCenterColor: "#D84315",
},
{
gameboyColor: "#9C27B0",
htmlColor: "#E1BEE7",
buttonColor: "#7B1FA2",
buttonTextColor: "#FFFFFF",
dpadCenterColor: "#6A1B9A",
},
{
gameboyColor: "#FFD700",
htmlColor: "#FFF9C4",
buttonColor: "#FF9800",
buttonTextColor: "#FFFFFF",
dpadCenterColor: "#F57F17",
},
{
gameboyColor: "#009688",
htmlColor: "#B2DFDB",
buttonColor: "#4CAF50",
buttonTextColor: "#FFFFFF",
dpadCenterColor: "#00796B",
},
{
gameboyColor: "#795548",
htmlColor: "#D7CCC8",
buttonColor: "#9E9E9E",
buttonTextColor: "#000000",
dpadCenterColor: "#5D4037",
},
{
gameboyColor: "#FF5733",
htmlColor: "#FFCCCB",
buttonColor: "#C70039",
buttonTextColor: "#FFFFFF",
dpadCenterColor: "#B71C1C",
},
{
gameboyColor: "#00BCD4",
htmlColor: "#B2EBF2",
buttonColor: "#00ACC1",
buttonTextColor: "#FFFFFF",
dpadCenterColor: "#00838F",
},
];
let currentColorIndex = localStorage.getItem("gameboyColorIndex")
? parseInt(localStorage.getItem("gameboyColorIndex"))
: 0;
function updateGameBoyColor() {
gameboy.style.backgroundColor = colors[currentColorIndex].gameboyColor;
html.style.backgroundColor = colors[currentColorIndex].htmlColor;
body.style.backgroundColor = colors[currentColorIndex].htmlColor;
dpadButtons.forEach((button) => {
button.style.backgroundColor = colors[currentColorIndex].buttonColor;
button.style.color = colors[currentColorIndex].buttonTextColor;
});
// Using darker dpad center color
dpadCenter.style.backgroundColor = colors[currentColorIndex].dpadCenterColor;
dpadCenter.style.color = colors[currentColorIndex].buttonTextColor;
actionButtons.forEach((button) => {
button.style.backgroundColor = colors[currentColorIndex].buttonColor;
button.style.color = colors[currentColorIndex].buttonTextColor;
});
}
aBtn.addEventListener("click", () => {
currentColorIndex = (currentColorIndex - 1 + colors.length) % colors.length;
localStorage.setItem("gameboyColorIndex", currentColorIndex);
updateGameBoyColor();
});
bBtn.addEventListener("click", () => {
currentColorIndex = (currentColorIndex + 1) % colors.length;
localStorage.setItem("gameboyColorIndex", currentColorIndex);
updateGameBoyColor();
});
updateGameBoyColor();

View file

@ -1,197 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Snake Game - GameBoy Style</title>
<style>
/* Base Reset */
body,
html {
margin: 0;
padding: 0;
font-family: monospace;
background-color: #3a2d56;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
}
/* GameBoy Layout */
.gameboy {
background-color: #5f4c82;
width: 441px;
height: 735px;
border-radius: 20px;
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.6);
display: flex;
flex-direction: column;
align-items: center;
padding: 10px;
position: relative;
}
@media (max-width: 768px) {
.gameboy {
width: 100vw;
height: 100vh;
border-radius: 0;
}
}
/* Screen */
.screen {
background-color: black;
border: 4px solid #0f380f;
width: 90%;
height: 55%;
margin-top: 20px;
border-radius: 10px;
display: flex;
justify-content: center;
align-items: center;
box-shadow: inset 0 4px 8px rgba(0, 0, 0, 0.5);
overflow: hidden;
}
canvas {
border: 1px solid black;
}
/* Restart Button */
#restartBtn {
padding: 10px 20px;
font-size: 16px;
cursor: pointer;
margin-top: 20px;
background-color: #0f380f;
color: white;
border: none;
border-radius: 5px;
display: none;
}
#restartBtn:hover {
background-color: #9bbc0f;
}
/* Titles */
h1 {
font-size: 1.8rem;
margin-bottom: 10px;
text-transform: uppercase;
color: #9bbc0f;
}
</style>
</head>
<body>
<div class="gameboy">
<h1>Snake Game</h1>
<div class="screen">
<canvas id="snake" width="400" height="400"></canvas>
</div>
<button id="restartBtn">Restart</button>
</div>
<script>
"use strict";
const cvs = document.getElementById("snake");
const ctx = cvs.getContext("2d");
const box = 20;
let snake = [{ x: 9 * box, y: 10 * box }];
let food = {
x: Math.floor(Math.random() * 19 + 1) * box,
y: Math.floor(Math.random() * 19 + 1) * box,
};
let score = 0;
let d;
let game;
document.addEventListener("keydown", direction);
function direction(event) {
let key = event.keyCode;
if ((key == 37 || key == 65) && d != "RIGHT") d = "LEFT";
else if ((key == 38 || key == 87) && d != "DOWN") d = "UP";
else if ((key == 39 || key == 68) && d != "LEFT") d = "RIGHT";
else if ((key == 40 || key == 83) && d != "UP") d = "DOWN";
}
function collision(head, array) {
return array.some((part) => head.x == part.x && head.y == part.y);
}
function draw() {
ctx.fillStyle = "#0f380f";
ctx.fillRect(0, 0, cvs.width, cvs.height);
ctx.fillStyle = "red";
ctx.fillRect(food.x, food.y, box, box);
for (let i = 0; i < snake.length; i++) {
ctx.fillStyle = i == 0 ? "lime" : "white";
ctx.fillRect(snake[i].x, snake[i].y, box, box);
ctx.strokeStyle = "black";
ctx.strokeRect(snake[i].x, snake[i].y, box, box);
}
let snakeX = snake[0].x;
let snakeY = snake[0].y;
if (d == "LEFT") snakeX -= box;
if (d == "UP") snakeY -= box;
if (d == "RIGHT") snakeX += box;
if (d == "DOWN") snakeY += box;
if (snakeX == food.x && snakeY == food.y) {
score++;
food = {
x: Math.floor(Math.random() * 19 + 1) * box,
y: Math.floor(Math.random() * 19 + 1) * box,
};
} else {
snake.pop();
}
let newHead = { x: snakeX, y: snakeY };
if (
snakeX < 0 ||
snakeX >= cvs.width ||
snakeY < 0 ||
snakeY >= cvs.height ||
collision(newHead, snake)
) {
clearInterval(game);
document.getElementById("restartBtn").style.display = "block";
return;
}
snake.unshift(newHead);
ctx.fillStyle = "white";
ctx.font = "20px Arial";
ctx.fillText("Score: " + score, 10, 20);
}
function restartGame() {
snake = [{ x: 9 * box, y: 10 * box }];
food = {
x: Math.floor(Math.random() * 19 + 1) * box,
y: Math.floor(Math.random() * 19 + 1) * box,
};
score = 0;
d = "";
document.getElementById("restartBtn").style.display = "none";
game = setInterval(draw, 150);
}
document
.getElementById("restartBtn")
.addEventListener("click", restartGame);
game = setInterval(draw, 150);
</script>
</body>
</html>

View file

@ -1,145 +0,0 @@
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: "Courier New", Courier, monospace;
background-color: #0d0d0d;
color: #b0b0b0;
margin: 0;
line-height: 1.6;
background-image: url("images/background.jpg");
background-size: cover; /* Adjust size for tape appearance */
}
header {
background-color: #222; /* Fully opaque background */
color: #b0b0b0;
text-align: center;
padding: 1em 0;
font-size: 2rem;
text-shadow: 1px 1px 3px rgba(0, 0, 0, 0.7);
animation: neonFlicker 1.5s infinite;
}
/* Create the flickering neon light effect */
@keyframes neonFlicker {
0% {
text-shadow: 0 0 5px #ffcc00, 0 0 10px #ffcc00, 0 0 15px #ffcc00,
0 0 20px #ffcc00, 0 0 30px #ffcc00, 0 0 40px #ffcc00, 0 0 50px #ffcc00;
}
20% {
text-shadow: 0 0 3px #ffcc00, 0 0 7px #ffcc00, 0 0 10px #ffcc00,
0 0 15px #ffcc00, 0 0 20px #ffcc00;
}
40% {
text-shadow: 0 0 5px #ffcc00, 0 0 15px #ffcc00, 0 0 25px #ffcc00;
}
60% {
text-shadow: 0 0 5px #ffcc00, 0 0 10px #ffcc00, 0 0 15px #ffcc00,
0 0 20px #ffcc00, 0 0 30px #ffcc00;
}
80% {
text-shadow: 0 0 3px #ffcc00, 0 0 7px #ffcc00, 0 0 10px #ffcc00;
}
100% {
text-shadow: 0 0 5px #ffcc00, 0 0 10px #ffcc00, 0 0 15px #ffcc00,
0 0 20px #ffcc00, 0 0 30px #ffcc00, 0 0 40px #ffcc00;
}
}
footer {
background-color: #111;
color: #b0b0b0;
text-align: center;
padding: 1em 0;
margin-top: 20px;
}
.grid-container {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 20px;
padding: 20px;
}
.item {
position: relative;
background-color: #1a1a1a;
border-radius: 10px;
overflow: hidden;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.8);
transition: transform 0.3s ease, box-shadow 0.3s ease, filter 0.3s ease;
width: 100%;
height: 400px;
display: flex;
flex-direction: column;
}
.item img {
width: 100%;
height: 100%;
object-fit: cover;
filter: brightness(0.6);
}
.item .description {
padding: 30px;
font-size: 1rem;
color: #ccc;
background-color: rgba(0, 0, 0, 0.8);
border-radius: 0 0 10px 10px;
flex-grow: 1;
}
p {
text-decoration: none;
}
.item:hover {
transform: scale(1.05);
box-shadow: 0 8px 30px rgba(0, 0, 0, 0.9);
filter: brightness(1.1);
}
.item:hover img {
transform: scale(1.1);
filter: brightness(1.1);
}
.item h2 {
position: absolute;
top: 10%;
left: 50%;
transform: translateX(-50%);
color: #ffffff;
font-size: 1.8rem;
background-color: rgba(0, 0, 0, 0.9);
padding: 5px 15px;
border-radius: 5px;
text-align: center;
opacity: 0;
transition: opacity 0.3s ease, transform 0.3s ease;
}
.item:hover h2 {
opacity: 1;
transform: translateX(-50%) translateY(-10px);
}
@media (max-width: 800px) {
header {
font-size: 1.5rem;
}
.item {
height: auto;
width: auto;
}
.grid-container {
grid-template-columns: repeat(1, 1fr);
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 795 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

BIN
src/favicon/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View file

Before

Width:  |  Height:  |  Size: 173 KiB

After

Width:  |  Height:  |  Size: 173 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 385 KiB

After

Width:  |  Height:  |  Size: 385 KiB

Before After
Before After

311
src/js/animation/Meteor.js Normal file
View file

@ -0,0 +1,311 @@
class Meteor {
constructor(container) {
this.element = document.createElement("div");
this.element.style.position = "absolute";
this.container = container;
this.speed = Math.random() * 3 + 4;
this.life = 1.0;
this.particles = [];
this.colorType = Math.floor(Math.random() * 4);
this.colors = this.getMeteorColors();
this.containerRect = container.getBoundingClientRect();
// Start from random edge with PIXEL-based positioning
const edge = Math.floor(Math.random() * 4);
switch (edge) {
case 0:
// Top
this.x = Math.random() * this.containerRect.width;
this.y = -20;
case 1:
// Right
this.x = this.containerRect.width + 20;
this.y = Math.random() * this.containerRect.height;
case 2:
// Bottom
this.x = Math.random() * this.containerRect.width;
this.y = this.containerRect.height + 20;
case 3:
// Left
this.x = -20;
this.y = Math.random() * this.containerRect.height;
default:
console.log("Error creating Meteor direction");
}
const centerX = this.containerRect.width / 2 + (Math.random() - 0.5) * 200;
const centerY = this.containerRect.height / 2 + (Math.random() - 0.5) * 200;
this.angle =
Math.atan2(centerY - this.y, centerX - this.x) +
(Math.random() - 0.5) * 0.8;
this.dx = Math.cos(this.angle) * this.speed;
this.dy = Math.sin(this.angle) * this.speed;
this.init();
}
getMeteorColors() {
const colorSchemes = [
{
head: "rgba(255, 255, 255, 1)",
core: "rgba(255, 230, 150, 0.9)",
mid: "rgba(255, 180, 80, 0.7)",
outer: "rgba(255, 100, 50, 0.5)",
glow: "rgba(255, 200, 100, 0.9)",
},
{
head: "rgba(255, 255, 255, 1)",
core: "rgba(180, 220, 255, 0.9)",
mid: "rgba(100, 180, 255, 0.7)",
outer: "rgba(50, 100, 255, 0.5)",
glow: "rgba(100, 180, 255, 0.9)",
},
{
head: "rgba(255, 255, 255, 1)",
core: "rgba(220, 180, 255, 0.9)",
mid: "rgba(200, 100, 255, 0.7)",
outer: "rgba(180, 50, 255, 0.5)",
glow: "rgba(200, 150, 255, 0.9)",
},
{
head: "rgba(255, 255, 255, 1)",
core: "rgba(180, 255, 200, 0.9)",
mid: "rgba(80, 255, 150, 0.7)",
outer: "rgba(50, 200, 100, 0.5)",
glow: "rgba(150, 255, 180, 0.9)",
},
];
return colorSchemes[this.colorType];
}
init() {
const size = Math.random() * 4 + 10;
const colors = this.colors;
const head = document.createElement("div");
head.style.width = `${size}px`;
head.style.height = `${size}px`;
head.style.borderRadius = "50%";
head.style.background = `
radial-gradient(
circle at 30% 30%,
${colors.head} 0%,
${colors.core} 30%,
${colors.mid} 60%,
${colors.outer} 100%
)
`;
head.style.boxShadow = `
0 0 ${size * 4}px ${colors.glow},
0 0 ${size * 2}px rgba(255, 255, 255, 0.8),
inset 0 0 ${size * 0.5}px rgba(255, 255, 255, 0.9)
`;
head.style.position = "absolute";
head.style.left = "50%";
head.style.top = "50%";
head.style.transform = "translate(-50%, -50%)";
head.style.zIndex = "3";
head.style.filter = "blur(0.5px)";
// Create multi-layer tail
this.createTailLayer(size * 3, size * 1.2, colors.core, 1, "1px");
this.createTailLayer(size * 5, size * 2, colors.mid, 0.6, "2px");
this.createTailLayer(size * 8, size * 3, colors.outer, 0.3, "3px");
// Assemble meteor
this.element.appendChild(head);
this.element.style.left = `${this.x}px`;
this.element.style.top = `${this.y}px`;
this.element.style.zIndex = "10";
this.element.style.filter = "blur(0.5px)";
this.head = head;
this.size = size;
this.container.appendChild(this.element);
this.createTrailParticles();
}
createTailLayer(length, width, color, opacity, blur) {
const tail = document.createElement("div");
const tailAngle = Math.atan2(this.dy, this.dx) + Math.PI;
tail.style.width = `${length}px`;
tail.style.height = `${width}px`;
tail.style.position = "absolute";
tail.style.left = "50%";
tail.style.top = "50%";
tail.style.transformOrigin = "left center";
tail.style.transform = `translate(0, -50%) rotate(${tailAngle}rad)`;
tail.style.background = `linear-gradient(to right,
${color} 0%,
${color.replace(")", ", 0.8)").replace("rgba", "rgba")} 20%,
${color.replace(")", ", 0.4)").replace("rgba", "rgba")} 50%,
${color.replace(")", ", 0.1)").replace("rgba", "rgba")} 80%,
transparent 100%)`;
tail.style.opacity = opacity;
tail.style.filter = `blur(${blur})`;
tail.style.pointerEvents = "none";
tail.style.zIndex = "2";
tail.style.borderRadius = "0 50% 50% 0";
this.element.appendChild(tail);
this.tail = tail;
}
createTrailParticles() {
for (let i = 0; i < 5; i++) {
this.addTrailParticle();
}
}
addTrailParticle() {
const particle = document.createElement("div");
const size = Math.random() * 2 + 1;
const colors = this.colors;
particle.style.width = `${size}px`;
particle.style.height = `${size}px`;
particle.style.borderRadius = "50%";
particle.style.background = colors.mid;
particle.style.position = "absolute";
particle.style.left = `${this.x}px`;
particle.style.top = `${this.y}px`;
particle.style.boxShadow = `0 0 ${size * 3}px ${colors.glow}`;
particle.style.opacity = "0.7";
particle.style.zIndex = "1";
particle.style.pointerEvents = "none";
this.container.appendChild(particle);
this.particles.push({
element: particle,
life: 1.0,
x: this.x,
y: this.y,
vx: (Math.random() - 0.5) * 0.5,
vy: (Math.random() - 0.5) * 0.5,
size: size,
});
}
update() {
this.x += this.dx;
this.y += this.dy;
this.life -= 0.005;
this.element.style.left = `${this.x}px`;
this.element.style.top = `${this.y}px`;
this.element.style.opacity = this.life;
// Update tail direction
const tailAngle = Math.atan2(this.dy, this.dx) + Math.PI;
const tails = this.element.querySelectorAll("div:not(.particle)");
tails.forEach((tail) => {
if (tail !== this.head) {
tail.style.transform = `translate(0, -50%) rotate(${tailAngle}rad)`;
tail.style.opacity = this.life;
}
});
if (Math.random() < 0.3) {
this.addTrailParticle();
}
// Update existing particles with pixel positioning
for (let i = this.particles.length - 1; i >= 0; i--) {
const p = this.particles[i];
p.life -= 0.02;
p.x += p.vx;
p.y += p.vy;
p.element.style.left = `${p.x}px`;
p.element.style.top = `${p.y}px`;
p.element.style.opacity = p.life;
p.element.style.transform = `scale(${p.life})`;
if (p.life <= 0) {
p.element.remove();
this.particles.splice(i, 1);
}
}
if (
this.x < -50 ||
this.x > this.containerRect.width + 50 ||
this.y < -50 ||
this.y > this.containerRect.height + 50 ||
this.life <= 0
) {
// Fade out particles
this.particles.forEach((p) => {
p.element.style.transition = "opacity 0.5s";
p.element.style.opacity = "0";
setTimeout(() => p.element.remove(), 500);
});
this.remove();
return false;
}
return true;
}
remove() {
if (this.life > 0.3) {
this.createExplosion();
}
setTimeout(() => {
this.element.remove();
}, 300);
}
createExplosion() {
for (let i = 0; i < 8; i++) {
const particle = document.createElement("div");
const size = Math.random() * 3 + 2;
const colors = this.colors;
particle.style.width = `${size}px`;
particle.style.height = `${size}px`;
particle.style.borderRadius = "50%";
particle.style.background = colors.core;
particle.style.position = "absolute";
particle.style.left = `${this.x}px`;
particle.style.top = `${this.y}px`;
particle.style.boxShadow = `0 0 ${size * 4}px ${colors.glow}`;
particle.style.opacity = "0.8";
particle.style.zIndex = "2";
this.container.appendChild(particle);
// Animate explosion
const angle = (i / 8) * Math.PI * 2;
const distance = Math.random() * 20 + 10;
const duration = Math.random() * 500 + 500;
particle.animate(
[
{
transform: "translate(0, 0) scale(1)",
opacity: 0.8,
},
{
transform: `translate(${Math.cos(angle) * distance}px, ${
Math.sin(angle) * distance
}px) scale(0.1)`,
opacity: 0,
},
],
{
duration: duration,
easing: "cubic-bezier(0.4, 0, 0.2, 1)",
}
).onfinish = () => particle.remove();
}
}
}
export default Meteor;

63
src/js/animation/Star.js Normal file
View file

@ -0,0 +1,63 @@
class Star {
constructor(container) {
this.element = document.createElement("div");
this.element.classList.add("star");
this.container = container;
this.speed = Math.random() * 0.5 + 0.1;
// Random direction angle (0 to 360 degrees)
this.angle = Math.random() * Math.PI * 2;
this.dx = Math.cos(this.angle) * this.speed;
this.dy = Math.sin(this.angle) * this.speed;
this.x = Math.random() * 100;
this.y = Math.random() * 100;
this.color = this.getRandomStarColor();
this.init();
}
getRandomStarColor() {
const colors = [
"rgba(255, 255, 255, 0.9)", // Pure white
"rgba(255, 250, 200, 0.9)", // Warm white
"rgba(200, 220, 255, 0.9)", // Cool blue-white
"rgba(255, 220, 180, 0.9)", // Yellow-white
"rgba(180, 200, 255, 0.9)", // Blue-white
];
return colors[Math.floor(Math.random() * colors.length)];
}
init() {
const size = Math.random() * 3 + 0.5;
this.element.style.width = `${size}px`;
this.element.style.height = `${size}px`;
this.element.style.left = `${this.x}%`;
this.element.style.top = `${this.y}%`;
this.element.style.background = this.color;
this.element.style.boxShadow = `0 0 ${size * 2}px ${this.color}`;
this.element.style.animationDelay = `${Math.random() * 5}s`;
this.element.style.animationDuration = `${3 + Math.random() * 4}s`;
this.container.appendChild(this.element);
}
update() {
this.x += this.dx * 0.05;
this.y += this.dy * 0.05;
// Wrap around when star goes off screen
if (this.y > 100) this.y = 0;
if (this.y < 0) this.y = 100;
if (this.x > 100) this.x = 0;
if (this.x < 0) this.x = 100;
this.element.style.left = `${this.x}%`;
this.element.style.top = `${this.y}%`;
}
remove() {
this.element.remove();
}
}
export default Star;

View file

@ -0,0 +1,81 @@
import Star from "./Star.js";
import Meteor from "./Meteor.js";
const starInstances = [];
const meteorInstances = [];
function createStars() {
const stars = document.getElementById("stars");
const count = 400;
stars.innerHTML = "";
starInstances.length = 0;
for (let i = 0; i < count; i++) {
const star = new Star(stars);
starInstances.push(star);
}
}
function injectStarCSS() {
const style = document.createElement("style");
style.textContent = `
.star {
position: absolute;
border-radius: 50%;
animation: twinkle ease-in-out infinite;
}
@keyframes twinkle {
0%, 100% { opacity: 1; }
50% { opacity: 0.3; }
}
`;
document.head.appendChild(style);
}
function trySpawnMeteor() {
const spawnChance = 0.12;
if (Math.random() < spawnChance) {
const stars = document.getElementById("stars");
const newMeteor = new Meteor(stars);
meteorInstances.push(newMeteor);
}
}
function animateStars() {
starInstances.forEach((star) => star.update());
// Update meteors and remove dead ones
for (let i = meteorInstances.length - 1; i >= 0; i--) {
if (!meteorInstances[i].update()) {
meteorInstances.splice(i, 1);
}
}
requestAnimationFrame(animateStars);
}
function createMeteorBurst() {
for (let i = 0; i < 3; i++) {
setTimeout(() => {
const stars = document.getElementById("stars");
const newMeteor = new Meteor(stars);
meteorInstances.push(newMeteor);
}, i * 200);
}
}
function init() {
injectStarCSS();
createStars();
animateStars();
setInterval(trySpawnMeteor, 2000);
setInterval(createMeteorBurst, 15000);
}
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", init);
} else {
init();
}

22
src/js/form.js Normal file
View file

@ -0,0 +1,22 @@
// Form submission
function setupForm() {
const form = document.getElementById("contact-form");
if (form) {
form.addEventListener("submit", function (e) {
e.preventDefault();
// Simple form validation
const name = document.getElementById("name").value;
const email = document.getElementById("email").value;
const message = document.getElementById("message").value;
if (name && email && message) {
// In a real implementation, you would send the form data to a server
alert("Thank you for your message! We will get back to you soon.");
form.reset();
} else {
alert("Please fill in all required fields.");
}
});
}
}

27
src/js/main.js Normal file
View file

@ -0,0 +1,27 @@
// Initialize everything when DOM is loaded
document.addEventListener("DOMContentLoaded", function () {
// Initialize components
if (typeof createStars === "function") createStars();
if (typeof updateNavigation === "function") updateNavigation();
if (typeof setupForm === "function") setupForm();
if (typeof setupMobileNavigation === "function") setupMobileNavigation();
// Set up event listeners
window.addEventListener("scroll", updateNavigation);
// Smooth scrolling for navigation links
document.querySelectorAll('a[href^="#"]').forEach((anchor) => {
anchor.addEventListener("click", function (e) {
e.preventDefault();
const targetId = this.getAttribute("href");
if (targetId === "#") return;
const targetElement = document.querySelector(targetId);
if (targetElement) {
targetElement.scrollIntoView({
behavior: "smooth",
});
}
});
});
});

106
src/js/navigation.js Normal file
View file

@ -0,0 +1,106 @@
// Navigation JavaScript
let lastScrollY = window.scrollY;
let ticking = false;
function updateNavigation() {
const navigation = document.getElementById("navigation");
const scrolled = window.scrollY > 50;
if (scrolled) {
navigation.classList.add("scrolled");
// Hide/show nav on scroll
if (window.scrollY > lastScrollY && window.scrollY > 100) {
navigation.classList.add("hidden");
} else {
navigation.classList.remove("hidden");
}
} else {
navigation.classList.remove("scrolled", "hidden");
}
lastScrollY = window.scrollY;
ticking = false;
}
function requestTick() {
if (!ticking) {
requestAnimationFrame(updateNavigation);
ticking = true;
}
}
// Mobile navigation functionality
function setupMobileNavigation() {
const burgerMenu = document.getElementById("burger-menu");
const mainNav = document.getElementById("main-nav");
const body = document.body;
// Create overlay for mobile menu
const overlay = document.createElement("div");
overlay.className = "nav-overlay";
document.body.appendChild(overlay);
if (burgerMenu && mainNav) {
burgerMenu.addEventListener("click", function () {
const isActive = mainNav.classList.contains("active");
if (isActive) {
// Close menu
mainNav.classList.remove("active");
burgerMenu.classList.remove("active");
overlay.classList.remove("active");
body.classList.remove("nav-open");
} else {
// Open menu
mainNav.classList.add("active");
burgerMenu.classList.add("active");
overlay.classList.add("active");
body.classList.add("nav-open");
}
});
// Close menu when clicking on overlay
overlay.addEventListener("click", function () {
mainNav.classList.remove("active");
burgerMenu.classList.remove("active");
overlay.classList.remove("active");
body.classList.remove("nav-open");
});
// Close menu when clicking on a link
const navLinks = mainNav.querySelectorAll("a");
navLinks.forEach((link) => {
link.addEventListener("click", function () {
mainNav.classList.remove("active");
burgerMenu.classList.remove("active");
overlay.classList.remove("active");
body.classList.remove("nav-open");
// Update active state
navLinks.forEach((l) => l.classList.remove("active"));
this.classList.add("active");
});
});
// Set initial active state based on current hash
function setActiveNavLink() {
const currentHash = window.location.hash || "#home";
navLinks.forEach((link) => {
if (link.getAttribute("href") === currentHash) {
link.classList.add("active");
} else {
link.classList.remove("active");
}
});
}
// Update active state on hash change
window.addEventListener("hashchange", setActiveNavLink);
setActiveNavLink();
}
}
// Initialize navigation
window.addEventListener("scroll", requestTick);
window.addEventListener("load", updateNavigation);

57
src/js/overview.js Normal file
View file

@ -0,0 +1,57 @@
// Counter animation for stats
document.addEventListener("DOMContentLoaded", function () {
const counters = document.querySelectorAll(".stat-number");
let hasCounted = false;
function animateCounters() {
if (hasCounted) return;
counters.forEach((counter) => {
const target = parseInt(counter.getAttribute("data-count"));
const duration = 2000; // 2 seconds
const frameDuration = 1000 / 60; // 60 frames per second
const totalFrames = Math.round(duration / frameDuration);
let frame = 0;
const counterInterval = setInterval(() => {
frame++;
const progress = frame / totalFrames;
const currentCount = Math.round(target * progress);
counter.textContent = currentCount;
if (frame === totalFrames) {
clearInterval(counterInterval);
}
}, frameDuration);
counter.classList.add("animated");
});
hasCounted = true;
}
// Check if element is in viewport
function isInViewport(element) {
const rect = element.getBoundingClientRect();
return (
rect.top >= 0 &&
rect.left >= 0 &&
rect.bottom <=
(window.innerHeight || document.documentElement.clientHeight) &&
rect.right <= (window.innerWidth || document.documentElement.clientWidth)
);
}
// Check on scroll and on load
function checkCounters() {
const statsContainer = document.querySelector(".stats-container");
if (statsContainer && isInViewport(statsContainer)) {
animateCounters();
window.removeEventListener("scroll", checkCounters);
}
}
window.addEventListener("scroll", checkCounters);
checkCounters(); // Check on page load
});

228
src/js/portfolioCard.js Normal file
View file

@ -0,0 +1,228 @@
class PortfolioCard extends HTMLElement {
constructor() {
super();
const shadow = this.attachShadow({ mode: "open" });
const icon = this.getAttribute("icon") || "fas fa-code";
const title = this.getAttribute("title") || "Project Title";
const desc = this.getAttribute("desc") || "Project description goes here.";
const tags = (this.getAttribute("tags") || "").split(",");
const link = this.getAttribute("link") || "#";
const collaboration = this.getAttribute("collaboration") || "";
shadow.innerHTML = `
<!-- Font Awesome inside Shadow DOM -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<!-- Use global styles.css from index.html -->
<link rel="stylesheet" href="src/styles/styles.css">
<style>
/* Collaboration badge styling */
.collaboration-badge {
display: inline-flex;
align-items: center;
gap: 0.4rem;
background: linear-gradient(135deg, #3b82f6, #2563eb);
color: white;
padding: 0.3rem 0.8rem;
border-radius: 20px;
font-size: 0.75rem;
margin-top: 0.5rem;
margin-bottom: 1rem;
animation: pulse 2s infinite;
}
@keyframes pulse {
0% {
box-shadow: 0 0 0 0 rgba(59, 130, 246, 0.4);
}
70% {
box-shadow: 0 0 0 10px rgba(59, 130, 246, 0);
}
100% {
box-shadow: 0 0 0 0 rgba(59, 130, 246, 0);
}
}
/* Enhanced card animations */
.portfolio-item {
opacity: 0;
transform: translateY(20px);
animation: fadeInUp 0.6s ease forwards;
display: flex;
flex-direction: column;
height: 100%;
}
@keyframes fadeInUp {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
/* Portfolio title with icon */
.portfolio-title {
display: flex;
align-items: center;
gap: 0.5rem;
}
.portfolio-content {
display: flex;
flex-direction: column;
flex: 1;
padding: 1.5rem;
}
.portfolio-tags {
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
margin-bottom: 1.5rem;
}
/* Full-width Enhanced Button Styles */
.portfolio-btn-container {
margin-top: auto;
padding: 0 1.5rem 1.5rem;
}
.portfolio-btn {
width: 100%;
position: relative;
overflow: hidden;
z-index: 1;
padding: 1rem 1.8rem;
border-radius: 8px;
font-weight: 600;
letter-spacing: 0.5px;
text-transform: uppercase;
font-size: 0.9rem;
transition: all 0.4s cubic-bezier(0.165, 0.84, 0.44, 1);
border: 2px solid transparent;
background: linear-gradient(135deg, var(--purple), var(--purple-dark));
color: white;
box-shadow: 0 4px 15px var(--shadow-purple);
display: flex;
justify-content: center;
align-items: center;
gap: 0.8rem;
text-decoration: none;
text-align: center;
}
.portfolio-btn::before {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: linear-gradient(135deg, var(--purple-dark), var(--purple-muted));
z-index: -1;
transition: transform 0.6s cubic-bezier(0.165, 0.84, 0.44, 1);
transform: scaleX(0);
transform-origin: right;
}
.portfolio-btn:hover::before {
transform: scaleX(1);
transform-origin: left;
}
.portfolio-btn:hover {
transform: translateY(-3px);
box-shadow: 0 8px 25px rgba(168, 85, 247, 0.4);
border-color: var(--purple-light);
}
.portfolio-btn:active {
transform: translateY(0);
box-shadow: 0 4px 15px var(--shadow-purple);
}
.portfolio-btn i {
font-size: 1rem;
transition: transform 0.3s ease;
}
.portfolio-btn:hover i {
transform: translateX(4px);
}
.portfolio-btn::after {
content: '';
position: absolute;
top: -50%;
left: -50%;
width: 200%;
height: 200%;
background: linear-gradient(
to bottom right,
rgba(255, 255, 255, 0.2),
rgba(255, 255, 255, 0.1) 20%,
rgba(255, 255, 255, 0) 50%,
rgba(255, 255, 255, 0) 100%
);
transform: rotate(30deg) translateY(-150%);
transition: transform 0.6s cubic-bezier(0.165, 0.84, 0.44, 1);
}
.portfolio-btn:hover::after {
transform: rotate(30deg) translateY(150%);
}
.portfolio-btn:focus {
outline: 2px solid var(--purple-light);
outline-offset: 2px;
}
/* Responsive adjustments */
@media (max-width: 768px) {
.portfolio-btn {
padding: 0.9rem 1.5rem;
font-size: 0.85rem;
}
}
</style>
<div class="portfolio-item">
<div class="portfolio-img">
<i class="${icon}"></i>
</div>
<div class="portfolio-content">
<h3 class="portfolio-title">
<i class="${icon}"></i> ${title}
</h3>
<p class="portfolio-desc">${desc}</p>
${
collaboration
? `
<div class="collaboration-badge">
<i class="fas fa-users"></i> ${collaboration}
</div>
`
: ""
}
<div class="portfolio-tags">
${tags
.map((tag) => `<span class="tag">${tag.trim()}</span>`)
.join("")}
</div>
</div>
<div class="portfolio-btn-container">
<a href="${link}" target="_blank" class="portfolio-btn">
<i class="fas fa-arrow-right"></i> Explore Project
</a>
</div>
</div>
`;
}
}
customElements.define("portfolio-card", PortfolioCard);

95
src/js/portfolioFilter.js Normal file
View file

@ -0,0 +1,95 @@
// Portfolio filtering functionality
document.addEventListener("DOMContentLoaded", function () {
// Get all portfolio items
const portfolioItems = document.querySelectorAll("portfolio-card");
const filterButtons = document.querySelectorAll(".filter-btn");
const searchInput = document.getElementById("project-search");
// Create a container for no results message
const noResults = document.createElement("div");
noResults.className = "no-results";
noResults.innerHTML = `
<i class="fas fa-search"></i>
<h3>No projects found</h3>
<p>Try adjusting your search or filter criteria</p>
`;
// Function to filter portfolio items
function filterPortfolio() {
const activeFilter =
document.querySelector(".filter-btn.active").dataset.filter;
const searchTerm = searchInput.value.toLowerCase();
let visibleItems = 0;
portfolioItems.forEach((item) => {
const tags = item.getAttribute("tags").toLowerCase();
const title = item.getAttribute("title").toLowerCase();
const desc = item.getAttribute("desc").toLowerCase();
// Check if item matches the active filter
const matchesFilter =
activeFilter === "all" ||
tags.includes(activeFilter) ||
title.includes(activeFilter) ||
desc.includes(activeFilter);
// Check if item matches the search term
const matchesSearch =
searchTerm === "" ||
title.includes(searchTerm) ||
desc.includes(searchTerm) ||
tags.includes(searchTerm);
// Show or hide the item based on filters
if (matchesFilter && matchesSearch) {
item.style.display = "block";
visibleItems++;
// Add animation for appearing items
item.style.animation = "fadeInUp 0.5s ease forwards";
} else {
item.style.display = "none";
}
});
// Show no results message if needed
const portfolioGrid = document.querySelector(".portfolio-grid");
const existingNoResults = portfolioGrid.querySelector(".no-results");
if (visibleItems === 0) {
if (!existingNoResults) {
portfolioGrid.appendChild(noResults);
}
} else if (existingNoResults) {
portfolioGrid.removeChild(existingNoResults);
}
}
// Add click event listeners to filter buttons
filterButtons.forEach((button) => {
button.addEventListener("click", function () {
// Remove active class from all buttons
filterButtons.forEach((btn) => btn.classList.remove("active"));
// Add active class to clicked button
this.classList.add("active");
// Filter portfolio items
filterPortfolio();
});
});
// Add input event listener to search field
searchInput.addEventListener("input", filterPortfolio);
// Add keyboard shortcut for search (Ctrl/Cmd + F)
document.addEventListener("keydown", function (e) {
if ((e.ctrlKey || e.metaKey) && e.key === "f") {
e.preventDefault();
searchInput.focus();
}
});
// Initialize filter on page load
filterPortfolio();
});

111
src/js/sectionTracker.js Normal file
View file

@ -0,0 +1,111 @@
// Section tracking and navigation highlighting
document.addEventListener("DOMContentLoaded", function () {
const sections = document.querySelectorAll("section[id]");
const navLinks = document.querySelectorAll(".nav-links a");
// Configuration
const offset = 100; // Offset for when section becomes "active" (accounts for fixed nav)
let isScrolling = false;
function getCurrentSection() {
let currentSection = "";
const scrollPosition = window.scrollY + offset;
// Find which section we're currently in
sections.forEach((section) => {
const sectionTop = section.offsetTop;
const sectionHeight = section.offsetHeight;
const sectionId = section.getAttribute("id");
if (
scrollPosition >= sectionTop &&
scrollPosition < sectionTop + sectionHeight
) {
currentSection = sectionId;
}
});
// Special case: if we're at the very top, activate the first section
if (window.scrollY < 100) {
currentSection = sections[0]?.getAttribute("id") || "";
}
return currentSection;
}
function updateActiveNavLink() {
const currentSection = getCurrentSection();
navLinks.forEach((link) => {
const href = link.getAttribute("href");
// Remove active class from all links
link.classList.remove("active");
// Add active class to matching link
if (href === `#${currentSection}`) {
link.classList.add("active");
}
});
}
// Throttle scroll events for better performance
function handleScroll() {
if (!isScrolling) {
window.requestAnimationFrame(() => {
updateActiveNavLink();
isScrolling = false;
});
isScrolling = true;
}
}
// Listen for scroll events
window.addEventListener("scroll", handleScroll);
// Update on page load
updateActiveNavLink();
// Also update when clicking nav links (for smooth scroll)
navLinks.forEach((link) => {
link.addEventListener("click", function (e) {
// Remove active from all
navLinks.forEach((l) => l.classList.remove("active"));
// Add active to clicked link
this.classList.add("active");
// Let the scroll handler update it properly after scroll completes
setTimeout(updateActiveNavLink, 100);
});
});
// Handle hash changes (browser back/forward)
window.addEventListener("hashchange", function () {
setTimeout(updateActiveNavLink, 100);
});
// Intersection Observer for more precise tracking (progressive enhancement)
if ("IntersectionObserver" in window) {
const observerOptions = {
rootMargin: "-20% 0px -70% 0px", // Trigger when section is roughly in the middle of viewport
threshold: 0,
};
const observer = new IntersectionObserver((entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
const sectionId = entry.target.getAttribute("id");
navLinks.forEach((link) => {
link.classList.remove("active");
if (link.getAttribute("href") === `#${sectionId}`) {
link.classList.add("active");
}
});
}
});
}, observerOptions);
// Observe all sections
sections.forEach((section) => observer.observe(section));
}
});

1943
src/styles/styles.css Normal file

File diff suppressed because it is too large Load diff

View file

@ -1,432 +0,0 @@
/*
interstellar_development website
Copyright (C) 2024 interstellar_development
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
:root {
--background-color: #0b0c1d;
--text-color: #c7d5e0;
--accent-color: #ffdd55;
--accent-hover-color: #ffd700;
--dark-blue: rgba(31, 42, 64, 1);
--dark-blue-translucent: rgba(31, 42, 64, 0.9);
--light-blue: rgba(46, 58, 95, 0.8);
--border-radius: 8px;
--transition-speed: 0.3s;
--box-shadow: 0 2px 15px rgba(0, 0, 0, 0.7);
--font-size-large: 2.5em;
--font-size-medium: 1.8em;
}
/* Reset and normalize */
*,
*::before,
*::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
/* Body styling */
body {
background-color: var(--background-color);
color: var(--text-color);
font-family: "Arial", sans-serif;
line-height: 1.6;
padding: 0 20px;
background: url("images/star.jpg") no-repeat center center fixed;
background-size: cover;
}
/* Header Styling */
/* Header Styling */
header {
background-color: var(--dark-blue);
height: 5em;
position: fixed;
width: 100%;
top: 0;
left: 0;
padding: 15px 20px;
box-shadow: var(--box-shadow);
backdrop-filter: blur(5px);
z-index: 100;
margin-bottom: 0px;
}
/* Burger Menu Styling */
.burger-menu {
background: none;
border: none;
color: #ffffff;
font-size: 1.8em;
cursor: pointer;
display: block;
padding: 0;
z-index: 110;
}
/* Dropdown Menu (Hidden by Default) */
.div-menu {
z-index: 1;
background-color: var(--light-blue);
width: 100%;
position: fixed;
top: 0;
left: 0;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
padding: 0;
margin-top: 0px;
height: auto;
}
.div-menu li {
margin: 0;
padding: 1em;
z-index: 1;
}
.div-menu a {
width: 100%;
padding: 8px 0;
text-align: center;
color: #ffffff;
text-decoration: none;
border-radius: 5px;
transition: background-color 0.3s ease;
display: block;
}
.div-menu a:hover {
background-color: #34495e;
}
/* Menu Animation and Styling */
.menu {
display: flex;
flex-direction: column;
background-color: var(--light-blue);
position: absolute;
top: -50vh;
left: 0;
width: 100%;
z-index: 5;
padding: 0;
margin: 0;
list-style: none;
justify-content: center;
text-align: center;
font-weight: bolder;
font-size: large;
transition: top 0.5s ease-in-out;
}
.menu.active {
display: flex;
top: 4em;
z-index: 5;
}
/* Header Content Container */
.header-content {
display: flex;
justify-content: space-between;
align-items: center;
max-width: 1200px;
margin: 0 auto;
padding: 0 20px;
z-index: 10;
}
/* Project Name Styling */
.project-name {
font-size: 2em;
color: var(--accent-color);
text-decoration: none;
transition: color var(--transition-speed), text-shadow var(--transition-speed);
}
.project-name:hover {
color: var(--accent-hover-color);
text-shadow: 0 0 10px var(--accent-hover-color);
}
/* Article styling */
article {
max-width: 800px;
margin: 6.25em auto;
padding: 20px;
background-color: var(--dark-blue-translucent);
border-radius: var(--border-radius);
box-shadow: var(--box-shadow);
backdrop-filter: blur(5px);
}
article h1 {
font-size: var(--font-size-large);
margin-bottom: 20px;
color: var(--accent-color);
text-shadow: 0 0 15px var(--accent-color);
}
article p {
color: var(--text-color);
margin-bottom: 20px;
hyphens: auto;
}
/* Download list */
article h2 {
font-size: var(--font-size-medium);
margin: 20px 0 10px;
color: var(--accent-color);
text-shadow: 0 0 10px var(--accent-color);
}
article ul {
list-style-type: none;
padding: 0;
}
article ul a li {
background-color: var(--light-blue);
margin-bottom: 10px;
border-radius: var(--border-radius);
padding: 10px;
transition: background-color var(--transition-speed),
box-shadow var(--transition-speed);
}
article ul a li:hover {
background-color: rgba(68, 80, 124, 0.9);
box-shadow: 0 0 10px var(--accent-color);
}
article ul a li {
text-decoration: none;
color: var(--accent-color);
font-weight: bold;
}
/* Footer styling */
footer {
background-color: var(--dark-blue);
padding: 10px 20px;
color: var(--text-color);
width: 100%;
position: fixed;
bottom: 0;
left: 0;
box-shadow: 0 -2px 15px rgba(0, 0, 0, 0.7);
backdrop-filter: blur(5px);
}
.footer-content {
text-align: center;
font-size: 0.9em;
}
/* Card container styles */
.cards {
display: grid;
grid-template-columns: repeat(3, 1fr); /* Display 3 cards per line */
gap: 20px;
margin-top: 50px;
}
/* Ensure the <a> tag covers the entire card */
section .card a {
display: flex; /* Use flex to make <a> fill the card and align content */
flex-direction: column;
justify-content: center; /* Vertically center the content */
align-items: center; /* Horizontally center the content */
text-decoration: none;
color: inherit;
height: 100%;
width: 100%;
padding: 20px;
}
/* Card styles */
section .card {
text-align: center;
list-style: none;
background: linear-gradient(
180deg,
rgba(0, 0, 50, 0.9),
rgba(10, 10, 100, 0.9),
rgba(30, 30, 150, 0.9)
);
border-radius: 12px;
box-shadow: 0 5px 20px rgba(0, 0, 50, 0.8), 0 0 10px rgba(255, 255, 255, 0.1);
border: 1px solid #2e3a60;
position: relative;
overflow: hidden;
display: flex;
flex-direction: column;
justify-content: space-between;
transition: background 0.5s ease, transform 0.4s ease, box-shadow 0.5s ease;
}
/* Hover effect */
section .card:hover {
transform: translateY(-8px);
background: linear-gradient(
180deg,
rgba(30, 30, 150, 0.9),
rgba(40, 0, 100, 0.9),
rgba(100, 0, 150, 0.9)
);
box-shadow: 0 10px 30px rgba(0, 0, 100, 0.7), 0 0 20px rgba(255, 221, 85, 0.8);
}
section .card img {
height: 80px;
width: 80px;
object-fit: cover;
border-radius: 50%;
margin: 0 auto 15px;
box-shadow: 0 0 15px rgba(255, 221, 85, 0.5);
}
section .card h3 {
margin: 10px 0;
font-size: 1.4em;
font-weight: bold;
color: rgba(255, 221, 85, 1);
text-shadow: 0 0 15px rgba(255, 221, 85, 0.9);
}
section .card p {
flex-grow: 1;
color: rgba(200, 220, 255, 0.8);
margin-bottom: 10px;
text-shadow: 0 0 8px rgba(255, 255, 255, 0.2);
}
section .card::before {
content: "";
position: absolute;
top: -20px;
right: -20px;
width: 60px;
height: 60px;
border-radius: 50%;
background: radial-gradient(
circle,
rgba(255, 255, 255, 0.1),
rgba(255, 255, 255, 0.02)
);
box-shadow: 0 0 50px rgba(255, 255, 255, 0.5);
animation: spin 8s linear infinite;
}
section .card .suit-icon {
position: absolute;
bottom: 10px;
right: 10px;
width: 24px;
height: 24px;
opacity: 0.7;
transition: opacity var(--transition-speed);
}
section .card:hover .suit-icon {
opacity: 1;
}
/* Keyframes for spinning element */
@keyframes spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
/* Form styling */
form {
max-width: 600px;
margin: 0 auto;
background: var(--dark-blue-translucent);
padding: 20px;
border-radius: var(--border-radius);
box-shadow: var(--box-shadow);
backdrop-filter: blur(5px);
}
form label,
form input,
form textarea {
color: var(--text-color);
background-color: var(--light-blue);
border: 1px solid #3a4b7f;
border-radius: var(--border-radius);
padding: 10px;
margin: 10px 0;
width: 100%;
}
form input[type="submit"] {
background-color: var(--accent-color);
color: #fff;
border: none;
cursor: pointer;
transition: background-color var(--transition-speed);
}
form input[type="submit"]:hover {
background-color: var(--accent-hover-color);
}
/* Footer Styling */
footer {
background-color: var(--dark-blue);
padding: 10px 20px;
color: var(--text-color);
text-align: center;
font-size: 0.9em;
}
@media (max-width: 768px) {
.cards {
grid-template-columns: 1fr; /* 1 card per line on smaller screens */
}
header ul {
flex-direction: column;
gap: 10px;
}
article {
margin: 12em 10px;
padding: 15px;
}
section .card {
padding: 12px;
}
section .card img {
height: 60px;
width: 60px;
}
.project-name {
font-size: 1.3em;
}
}

View file

@ -1,110 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Game Collection</title>
<link rel="stylesheet" href="styles.css" />
<link
rel="apple-touch-icon"
sizes="180x180"
href="../favicon_io/apple-touch-icon.png"
/>
<link
rel="icon"
type="image/png"
sizes="32x32"
href="../favicon_io/favicon-32x32.png"
/>
<link
rel="icon"
type="image/png"
sizes="16x16"
href="../favicon_io/favicon-16x16.png"
/>
<link rel="manifest" href="../favicon_io/site.webmanifest" />
</head>
<body>
<header>
<h1>Game Collection</h1>
</header>
<main>
<div class="grid-container">
<a href="" target="_blank" class="item">
<img
src="../secret/../secret/images/default.jpeg"
alt="Image can't be displayed"
/>
<h2>Snake</h2>
<div class="description">
<p>
Guide the snake to eat food and grow longer while avoiding
collisions with the walls and itself.
</p>
</div>
</a>
<a href="" target="_blank" class="item">
<img
src="../secret/images/default.jpeg"
alt="Image can't be displayed"
/>
<h2>Solitaire</h2>
<div class="description">
<p>
A classic card game where the objective is to move all cards to
foundation piles in ascending order.
</p>
</div>
</a>
<a href="../secret/mineSweeper/index.html" target="_blank" class="item">
<img
src="../secret/images/minesweeper.png"
alt="Image can't be displayed"
/>
<h2>Minesweeper</h2>
<div class="description">
<p>
Uncover squares on a grid while avoiding hidden mines, using
numbers to deduce safe spots.
</p>
</div>
</a>
<a
href="../secret/guessMyNumber/index.html"
target="_blank"
class="item"
>
<img
src="../secret/images/number.jpeg"
alt="Image can't be displayed"
/>
<h2>Guess My Number</h2>
<div class="description">
<p>
A simple game where you try to guess a randomly chosen number
within a certain range.
</p>
</div>
</a>
<a href="" target="_blank" class="item">
<img
src="../secret/images/default.jpeg"
alt="Image can't be displayed"
/>
<h2>Endless Runner</h2>
<div class="description">
<p>
Run through an endless landscape, avoiding obstacles and
collecting items to score points.
</p>
</div>
</a>
</div>
</main>
<footer>
<p>&copy; 2025 Game Collection</p>
</footer>
</body>
</html>

View file

@ -1,120 +0,0 @@
/* Reset and box-sizing */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
/* General Styles */
body {
font-family: Arial, sans-serif;
background-color: #282c34;
color: #ffffff;
margin: 0;
}
header {
background-color: #4caf50;
color: white;
text-align: center;
padding: 1em 0;
font-size: 1.5rem;
}
footer {
background-color: #333;
color: white;
text-align: center;
padding: 1em 0;
margin-top: 20px;
}
/* Grid Styles */
.grid-container {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 20px; /* Space between items */
padding: 20px; /* Space around the grid */
}
/* Game Item */
.item {
position: relative;
background-color: #444;
border-radius: 10px;
overflow: hidden;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
transition: transform 0.3s ease, box-shadow 0.3s ease, filter 0.3s ease;
width: 100%; /* Ensure it takes full width of the column */
height: 400px; /* Set a fixed height for all items */
display: flex;
flex-direction: column; /* Stack children vertically */
}
/* Ensure the image takes the top part of the card */
.item img {
width: 100%;
height: 100%; /* Set a height for the image */
object-fit: cover;
}
.item .description {
padding: 30px;
font-size: 1rem;
color: #ddd;
background-color: rgba(0, 0, 0, 0.5);
border-radius: 0 0 10px 10px;
flex-grow: 1; /* Allow description to take remaining space */
}
p {
text-decoration: none;
}
/* Hover effect for scaling and glowing */
.item:hover {
transform: scale(1.05);
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.4);
filter: brightness(1.2);
}
.item:hover img {
transform: scale(1.1); /* Slight zoom-in effect for the image */
filter: brightness(1.1); /* Increase image brightness */
}
.item h2 {
position: absolute;
top: 10%;
left: 50%;
transform: translateX(-50%);
color: white;
font-size: 1.5rem;
background-color: rgba(0, 0, 0, 0.6);
padding: 5px 15px;
border-radius: 5px;
text-align: center;
opacity: 0;
transition: opacity 0.3s ease, transform 0.3s ease;
}
.item:hover h2 {
opacity: 1;
transform: translateX(-50%) translateY(-10px); /* Move the title upwards with hover */
}
/* Mobile Optimization */
@media (max-width: 600px) {
header {
font-size: 1.2rem;
}
.item {
height: auto; /* Allow auto height on mobile for better responsiveness */
width: auto;
}
.grid-container {
grid-template-columns: repeat(1, 1fr);
}
}