diff --git a/index.html b/index.html index 17490cf..d87d7cc 100644 --- a/index.html +++ b/index.html @@ -9,32 +9,37 @@ -
- + +
+

Showcase Your Front-End Creations

@@ -43,74 +48,70 @@

Showcase Your Front-End Creations

-
-
-

Filter Projects

- -
- - -
- -
- - - -
-
- - -
- - -
- - -
- - -
+ +
+
+

Filter Projects

+
+ +
+ +
+ + + +
+
- -
- - Has Open Issues -
+ +
+ + +
+ +
+ + +
+ +
+ + Has Open Issues +
- -
- - -
+ +
+ + +
- -
- -
+ +
+ +
+
+
+
-
-
-
- - +

Featured Projects

@@ -124,137 +125,60 @@

No projects found

+

Developer Reviews

-
-
- Jane Doe -
-

Jane Doe

-

Frontend Developer

-

β€œCodeCanvas has completely changed the way I discover front-end projects. Super intuitive!”

-
-
-
-
-
- Raj Patel -
-

Raj Patel

-

Open Source Contributor

-

β€œA must-have platform for developers looking to showcase their UI/UX skills.”

-
-
-
-
-
- Maria Gonzales -
-

Maria Gonzales

-

Full Stack Developer

-

β€œI’ve found so many cool projects to contribute to. Highly recommended!”

-
-
-
-
-
- Leo Kim -
-

Leo Kim

-

UI/UX Designer

-

β€œLove the simplicity and design. It's like Behance but for front-end developers.”

-
-
-
-
-
- Sara Ahmed -
-

Sara Ahmed

-

JavaScript Enthusiast

-

β€œGreat place to get feedback on your code and build your portfolio!”

-
-
-
+
+
-
-

Contact Us

-
-
-
- - -
-
- - -
-
-
- - -
-
- - -
- -
- -
+
- - - - + +
diff --git a/script.js b/script.js index ec8d677..8cf509c 100644 --- a/script.js +++ b/script.js @@ -1,10 +1,9 @@ - - +// ================= Project Data ================= const sampleProjects = [ { id: 1, title: 'Analog Clock Web App', - description: 'A beautifully designed analog clock that updates in real-time using vanilla JavaScript, HTML, and CSS. Perfect for understanding basic DOM manipulation and CSS transformations.', + description: 'A beautifully designed analog clock that updates in real-time using vanilla JavaScript, HTML, and CSS.', repoUrl: 'https://github.com/snehhhcodes/Analog-Clock-Web-App', demoUrl: 'https://snehhhcodes.github.io/Analog-Clock-Web-App/', difficulty: 'beginner', @@ -17,7 +16,7 @@ const sampleProjects = [ { id: 2, title: 'Weather Dashboard', - description: 'A responsive weather application with beautiful animations and detailed forecasts. Features location-based weather data and interactive charts.', + description: 'A responsive weather application with beautiful animations and detailed forecasts.', repoUrl: 'https://github.com/Shivin1016/weatherApp', demoUrl: 'https://shivin1016.github.io/weatherApp/', difficulty: 'intermediate', @@ -30,7 +29,7 @@ const sampleProjects = [ { id: 3, title: 'Task Management App', - description: 'A full-featured task management application with drag-and-drop functionality, real-time updates, and team collaboration features.', + description: 'A full-featured task management app with drag-and-drop functionality and team collaboration.', repoUrl: 'https://github.com/example/task-manager', demoUrl: null, difficulty: 'advanced', @@ -56,7 +55,7 @@ const sampleProjects = [ { id: 5, title: 'Expense Tracker App', - description: 'A simple and intuitive expense tracker app to monitor daily spending, manage budgets, and gain financial insights.', + description: 'A simple and intuitive expense tracker app to monitor daily spending and budgets.', repoUrl: 'https://github.com/DineshPabboju/Expense-Tracker-App', demoUrl: 'https://expense-tracker-app-04.netlify.app/', difficulty: 'intermediate', @@ -69,7 +68,7 @@ const sampleProjects = [ { id: 6, title: 'IMDb Clone', - description: 'A responsive IMDb clone showcasing popular movies with detailed info using TMDb API and modern frontend technologies.', + description: 'A responsive IMDb clone showcasing popular movies with TMDb API.', repoUrl: 'https://github.com/Jils31/IMDB-clone', demoUrl: 'https://imdb-clone-seven-virid.vercel.app/', difficulty: 'intermediate', @@ -77,12 +76,12 @@ const sampleProjects = [ hasDemo: true, hasReadme: true, previewImage: 'assets/image.png', - tags: ['REACT', 'Tailwind CSS', 'Responsive', 'React-Router DOM'] + tags: ['React', 'Tailwind CSS', 'Responsive', 'React-Router DOM'] }, { id: 7, - title: 'PassWord Generator', - description: 'Enable user to create password with specified length and character (uppercase, lowercase , special character and numbers) to meet diverse securing requirements.', + title: 'Password Generator', + description: 'Generates secure passwords with customizable options.', repoUrl: 'https://github.com/Sitaram8472/Generate-password', demoUrl: 'https://password-generator021.netlify.app/', difficulty: 'advanced', @@ -108,560 +107,337 @@ const sampleProjects = [ ]; +// ================= Voting System ================= +let currentProjects = [...sampleProjects]; +let selectedTag = null; - // Store the current projects array - let currentProjects = [...sampleProjects]; - let selectedTag = null; - - // Voting system - class VotingSystem { - constructor() { - this.userFingerprint = this.generateUserFingerprint(); - this.votes = this.loadVotes(); - this.initializeProjectVotes(); - } - - generateUserFingerprint() { - let fingerprint = localStorage.getItem('userFingerprint'); - if (!fingerprint) { - fingerprint = 'user_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9); - localStorage.setItem('userFingerprint', fingerprint); - } - return fingerprint; - } - - loadVotes() { - const savedVotes = localStorage.getItem('projectVotes'); - return savedVotes ? JSON.parse(savedVotes) : {}; - } - - saveVotes() { - localStorage.setItem('projectVotes', JSON.stringify(this.votes)); - } - - initializeProjectVotes() { - sampleProjects.forEach(project => { - if (!this.votes[project.id]) { - this.votes[project.id] = { - count: project.upvotes || 0, - voters: [] - }; - } - }); - this.saveVotes(); - } - - canUserVote(projectId) { - const projectVotes = this.votes[projectId]; - return projectVotes && !projectVotes.voters.includes(this.userFingerprint); - } - - upvoteProject(projectId) { - if (!this.canUserVote(projectId)) { - return { success: false, message: 'You have already voted for this project!' }; - } - - this.votes[projectId].count++; - this.votes[projectId].voters.push(this.userFingerprint); - this.saveVotes(); - - // Update the project in currentProjects array - const project = currentProjects.find(p => p.id === projectId); - if (project) { - project.upvotes = this.votes[projectId].count; - } - - // Update the project in sampleProjects array - const sampleProject = sampleProjects.find(p => p.id === projectId); - if (sampleProject) { - sampleProject.upvotes = this.votes[projectId].count; - } - - return { success: true, newCount: this.votes[projectId].count }; - } - - getProjectVotes(projectId) { - return this.votes[projectId] ? this.votes[projectId].count : 0; - } - - hasUserVoted(projectId) { - const projectVotes = this.votes[projectId]; - return projectVotes && projectVotes.voters.includes(this.userFingerprint); - } - } - - // Initialize voting system - const votingSystem = new VotingSystem(); - - //Store all the unique tags - const allTagSet = new Set(); - sampleProjects.forEach(project => { - project.tags.forEach(tag => allTagSet.add(tag)); - }) - - const uniqueTags = Array.from(allTagSet); - - // DOM elements - const projectsContainer = document.getElementById('projects-container'); - const loadingElement = document.getElementById('loading'); - const emptyStateElement = document.getElementById('empty-state'); - const sortByFilter = document.getElementById('sort-by'); - const difficultyFilter = document.getElementById('difficulty'); - const hasDemoFilter = document.getElementById('has-demo'); - const applyFiltersBtn = document.getElementById('apply-filters'); - const resetFiltersBtn = document.getElementById('reset-filters'); - const searchInput = document.getElementById('search-input'); - const clearSearchBtn = document.getElementById('clear-search'); - const tagFiltersContainer = document.querySelector('.tag-filters'); - - uniqueTags.forEach(tag => { - const button = document.createElement('button'); - button.textContent = tag; - button.classList.add('tag-filter-btn') - button.dataset.tag = tag - tagFiltersContainer.appendChild(button) - }) - - // Initialize the app - function init() { - setTimeout(() => { - hideLoading(); - renderProjects(currentProjects); - setupEventListeners(); - initializeTagFilterListener(); - }, 1000); // Simulate loading time - } - - // Hide loading spinner - function hideLoading() { - loadingElement.style.display = 'none'; - projectsContainer.style.display = 'grid'; - } - - // Setup event listeners - function setupEventListeners() { - applyFiltersBtn.addEventListener('click', applyFilters); - resetFiltersBtn.addEventListener('click', resetFilters); - sortByFilter.addEventListener('change', applyFilters); - - // Search functionality - searchInput.addEventListener('input', handleSearch); - clearSearchBtn.addEventListener('click', clearSearch); - - // Smooth scroll for explore button - document.querySelector('a[href="#projects"]').addEventListener('click', (e) => { - e.preventDefault(); - document.getElementById('projects').scrollIntoView({ - behavior: 'smooth' - }); - }); - } - - function initializeTagFilterListener() { - tagFiltersContainer.addEventListener('click', (e) => { - if (e.target.classList.contains('tag-filter-btn')) { - const clickedTag = e.target.dataset.tag; - - // Toggle selection - if (selectedTag === clickedTag) { - selectedTag = null; - e.target.classList.remove('active'); - } else { - selectedTag = clickedTag; - // Remove active from all buttons - document.querySelectorAll('.tag-filter-btn').forEach(btn => btn.classList.remove('active')); - e.target.classList.add('active'); - } - - applyFilters(); // Re-apply filters based on tag - } - }); - } - - - // Render projects - function renderProjects(projects) { - if (projects.length === 0) { - projectsContainer.style.display = 'none'; - emptyStateElement.style.display = 'block'; - return; - } - - // Sort projects based on selected option - const sortBy = sortByFilter.value; - const sortedProjects = [...projects].sort((a, b) => { - switch (sortBy) { - case 'popularity': - const aVotes = votingSystem.getProjectVotes(a.id); - const bVotes = votingSystem.getProjectVotes(b.id); - return bVotes - aVotes; - - case 'newest': - // Since we don't have dates, sort by ID (assuming higher ID = newer) - return b.id - a.id; - - case 'difficulty': - const difficultyOrder = { 'beginner': 1, 'intermediate': 2, 'advanced': 3 }; - return difficultyOrder[a.difficulty] - difficultyOrder[b.difficulty]; - - case 'alphabetical': - return a.title.localeCompare(b.title); - - default: - return 0; - } - }); - - emptyStateElement.style.display = 'none'; - projectsContainer.style.display = 'grid'; - - projectsContainer.innerHTML = sortedProjects.map((project, index) => { - const hasVoted = votingSystem.hasUserVoted(project.id); - const canVote = votingSystem.canUserVote(project.id); - const voteCount = votingSystem.getProjectVotes(project.id); - const isTopRanked = sortBy === 'popularity' && index < 3 && voteCount > 0; - - return ` -
- ${isTopRanked ? `
#${index + 1}
` : ''} - ${project.previewImage - ? `${project.title}` - : '
No Preview Available
' - } - -
-

${project.title}

- - - -
- - - ${project.difficulty.charAt(0).toUpperCase() + project.difficulty.slice(1)} - - -

${project.description}

- -
- ${project.hasDemo - ? ' Live Demo Available' - : ' Code Only' - } - ${project.hasReadme - ? ' β€’ README Included' - : ' β€’ No README' - } -
- -
- ${project.tags.map((item, index)=>` - ${item} - `).join('')} -
- -
- ${project.hasDemo && project.demoUrl - ? ` - View Demo - ` - : '' - } - -
-
- `; - }).join(''); - } - - // Handle upvote - function handleUpvote(projectId) { - const result = votingSystem.upvoteProject(projectId); - - if (!result.success) { - // Show error message - showNotification(result.message, 'error'); - return; - } - - // Show success message - showNotification('Vote added successfully!', 'success'); - - // Re-render projects to update the upvote count and sorting - renderProjects(applyCurrentFilters()); - - // Add visual feedback - const button = event.target.closest('.upvote-btn'); - if (button) { - button.style.transform = 'scale(1.2)'; - setTimeout(() => { - button.style.transform = 'scale(1)'; - }, 200); - } - } - - // Show notification function - function showNotification(message, type = 'info') { - // Remove existing notifications - const existingNotification = document.querySelector('.vote-notification'); - if (existingNotification) { - existingNotification.remove(); - } - - // Create notification element - const notification = document.createElement('div'); - notification.className = `vote-notification ${type}`; - notification.innerHTML = ` - - ${message} - `; - - // Add to body - document.body.appendChild(notification); - - // Show notification - setTimeout(() => { - notification.classList.add('show'); - }, 10); - - // Hide notification after 3 seconds - setTimeout(() => { - notification.classList.remove('show'); - setTimeout(() => { - if (notification.parentNode) { - notification.remove(); - } - }, 300); - }, 3000); - } - - // Apply filters - function applyFilters() { - const filteredProjects = applyCurrentFilters(); - renderProjects(filteredProjects); - } - - // Apply current filter settings - function applyCurrentFilters() { - let filtered = [...sampleProjects]; - - const difficulty = difficultyFilter.value; - const needsDemo = hasDemoFilter.checked; - const searchTerm = searchInput.value.toLowerCase().trim(); - - // Apply search filter - if (searchTerm) { - filtered = filtered.filter(project => { - const titleMatch = project.title.toLowerCase().includes(searchTerm); - const descriptionMatch = project.description.toLowerCase().includes(searchTerm); - const tagsMatch = project.tags.some(tag => tag.toLowerCase().includes(searchTerm)); - return titleMatch || descriptionMatch || tagsMatch; - }); - } - - if (difficulty !== 'all') { - filtered = filtered.filter(p => p.difficulty === difficulty); - } - - if (needsDemo) { - filtered = filtered.filter(p => p.hasDemo); - } - - if(selectedTag){ - filtered = filtered.filter(project => project.tags.includes(selectedTag)); - } - - return filtered; - } - - // Handle search input with debounce - let searchTimeout; - function handleSearch() { - const searchTerm = searchInput.value.trim(); - - // Show/hide clear button - if (searchTerm) { - clearSearchBtn.style.display = 'flex'; - } else { - clearSearchBtn.style.display = 'none'; - } - - // Clear previous timeout - clearTimeout(searchTimeout); - - // Debounce search to improve performance - searchTimeout = setTimeout(() => { - const filteredProjects = applyCurrentFilters(); - renderProjects(filteredProjects); - }, 300); - } - - // Clear search - function clearSearch() { - searchInput.value = ''; - clearSearchBtn.style.display = 'none'; - const filteredProjects = applyCurrentFilters(); - renderProjects(filteredProjects); - } - - // Reset filters - function resetFilters() { - sortByFilter.value = 'popularity'; - difficultyFilter.value = 'all'; - hasDemoFilter.checked = false; - searchInput.value = ''; - clearSearchBtn.style.display = 'none'; - selectedTag = null; - document.querySelectorAll('.tag-filter-btn').forEach(btn => btn.classList.remove('active')); - renderProjects(sampleProjects); - } - - // Make handleUpvote globally available - window.handleUpvote = handleUpvote; - - // Start the app - document.addEventListener("DOMContentLoaded", init); - // Adding m own version and also added a feature where the input field will get clear on clicking the send message button - function validateForm() { - const name = document.getElementById("name").value.trim(); - const lastname = document.getElementById("lastname").value.trim(); - const email = document.getElementById("email").value.trim(); - const message = document.getElementById("message").value.trim(); - if (!name || !lastname || !email || !message) { - alert("Please fill in all fields."); - return false; - } - if (name.length < 4) { - alert("First Name must be at least 4 letters."); - return false; - } - if (lastname.length < 4) { - alert("Last Name must be at least 4 letters."); - return false; - } - const emailPattern = /^[^ ]+@[^ ]+\.[a-z]{2,3}$/; - if (!email.match(emailPattern)) { - alert("Please enter a valid email."); - return false; - } - // Message must have at least 3 words - const wordCount = message.split(/\s+/).filter(Boolean).length; - if (wordCount < 3) { - alert("Message must be at least 3 words."); - return false; - } - - // Show the overlay - const overlay = document.getElementById("message-overlay"); - overlay.style.opacity = "1"; - overlay.style.pointerEvents = "auto"; - - // Hide the overlay after 3 seconds - setTimeout(() => { - overlay.style.opacity = "0"; - overlay.style.pointerEvents = "none"; - }, 3000); - - // Clear form - document.getElementById("contact-form").reset(); - - return false; // Prevent actual form submission - } - - const toggle = document.getElementById('darkModeToggle'); - const body = document.body; - const icon = document.getElementById('themeIcon'); - - // Load preference - const savedTheme = localStorage.getItem('theme'); - if (savedTheme === 'dark') { - body.classList.add('dark-theme'); - icon.textContent = 'β˜€οΈ'; // Sun in dark mode - } else { - icon.textContent = 'πŸŒ™'; // Moon in light mode - } - - toggle.addEventListener('click', () => { - body.classList.toggle('dark-theme'); - const theme = body.classList.contains('dark-theme') ? 'dark' : 'light'; - localStorage.setItem('theme', theme); - - // Update icon - icon.textContent = theme === 'dark' ? 'β˜€οΈ' : 'πŸŒ™'; - }); - - //Review Section JS - const swiper = new Swiper(".review-swiper", { - loop: true, - slidesPerView: 1, - spaceBetween: 20, - navigation: { - nextEl: ".swiper-button-next", - prevEl: ".swiper-button-prev", - }, - keyboard: { - enabled: true, - }, - mousewheel: { - forceToAxis: true, - }, - grabCursor: true, - speed: 600, - }); - //Scroll to top button functionality - const scrollToTopBtn = document.getElementById("scrollToTopBtn"); - -// Show button when user scrolls down -window.addEventListener("scroll", () => { - scrollToTopBtn.style.display = window.scrollY > 300 ? "block" : "none"; -}); +class VotingSystem { + constructor() { + this.userFingerprint = this.generateUserFingerprint(); + this.votes = this.loadVotes(); + this.initializeProjectVotes(); + } + + generateUserFingerprint() { + let fingerprint = localStorage.getItem('userFingerprint'); + if (!fingerprint) { + fingerprint = 'user_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9); + localStorage.setItem('userFingerprint', fingerprint); + } + return fingerprint; + } -// Scroll to top smoothly -scrollToTopBtn.addEventListener("click", () => { - window.scrollTo({ - top: 0, - behavior: "smooth" + loadVotes() { + const savedVotes = localStorage.getItem('projectVotes'); + return savedVotes ? JSON.parse(savedVotes) : {}; + } + + saveVotes() { + localStorage.setItem('projectVotes', JSON.stringify(this.votes)); + } + + initializeProjectVotes() { + sampleProjects.forEach(project => { + if (!this.votes[project.id]) { + this.votes[project.id] = { + count: project.upvotes || 0, + voters: [] + }; + } }); + this.saveVotes(); + } + + canUserVote(projectId) { + const projectVotes = this.votes[projectId]; + return projectVotes && !projectVotes.voters.includes(this.userFingerprint); + } + + upvoteProject(projectId) { + if (!this.canUserVote(projectId)) { + return { success: false, message: 'You have already voted for this project!' }; + } + + this.votes[projectId].count++; + this.votes[projectId].voters.push(this.userFingerprint); + this.saveVotes(); + + const project = currentProjects.find(p => p.id === projectId); + if (project) { + project.upvotes = this.votes[projectId].count; + } + + return { success: true, newCount: this.votes[projectId].count }; + } + + getProjectVotes(projectId) { + return this.votes[projectId] ? this.votes[projectId].count : 0; + } + + hasUserVoted(projectId) { + const projectVotes = this.votes[projectId]; + return projectVotes && projectVotes.voters.includes(this.userFingerprint); + } +} +const votingSystem = new VotingSystem(); + +// ================= DOM Elements ================= +const projectsContainer = document.getElementById('projects-container'); +const loadingElement = document.getElementById('loading'); +const emptyStateElement = document.getElementById('empty-state'); +const sortByFilter = document.getElementById('sort-by'); +const difficultyFilter = document.getElementById('difficulty'); +const hasDemoFilter = document.getElementById('has-demo'); +const applyFiltersBtn = document.getElementById('apply-filters'); +const resetFiltersBtn = document.getElementById('reset-filters'); +const searchInput = document.getElementById('search-input'); +const clearSearchBtn = document.getElementById('clear-search'); +const tagFiltersContainer = document.querySelector('.tag-filters'); + +// ================= Tag Filter Buttons ================= +const allTagSet = new Set(); +sampleProjects.forEach(project => { + project.tags.forEach(tag => allTagSet.add(tag)); }); -document.addEventListener("DOMContentLoaded", () => { - const faders = document.querySelectorAll('.fade-in'); - - const observer = new IntersectionObserver((entries, observer) => { - entries.forEach(entry => { - if (entry.isIntersecting) { - entry.target.classList.add('animate'); - observer.unobserve(entry.target); +const uniqueTags = Array.from(allTagSet); + +uniqueTags.forEach(tag => { + const button = document.createElement('button'); + button.textContent = tag; + button.classList.add('tag-filter-btn'); + button.dataset.tag = tag; + tagFiltersContainer.appendChild(button); +}); + +// ================= Init ================= +function init() { + setTimeout(() => { + hideLoading(); + renderProjects(currentProjects); + setupEventListeners(); + initializeTagFilterListener(); + setupNavigation(); // hamburger menu + setupDarkMode(); // dark mode toggle + }, 1000); +} + +// ================= UI Helpers ================= +function hideLoading() { + loadingElement.style.display = 'none'; + projectsContainer.style.display = 'grid'; +} + +function showNotification(message, type = 'info') { + const notification = document.createElement('div'); + notification.className = `notification ${type}`; + notification.textContent = message; + document.body.appendChild(notification); + + setTimeout(() => { + notification.classList.add('show'); + }, 10); + + setTimeout(() => { + notification.classList.remove('show'); + setTimeout(() => notification.remove(), 300); + }, 3000); +} + +// ================= Navigation (Hamburger) ================= +function setupNavigation() { + const hamburger = document.getElementById("hamburger"); + const navMenu = document.getElementById("navMenu"); + const navContainer = document.getElementById("navContainer"); + + if (!hamburger || !navMenu || !navContainer) return; + + hamburger.addEventListener("click", () => { + navContainer.classList.toggle("active"); + navMenu.classList.toggle("nav-active"); + hamburger.classList.toggle("open"); + const isExpanded = hamburger.getAttribute("aria-expanded") === "true"; + hamburger.setAttribute("aria-expanded", !isExpanded); + }); + + document.querySelectorAll(".nav-link").forEach((link) => { + link.addEventListener("click", () => { + navContainer.classList.remove("active"); + navMenu.classList.remove("nav-active"); + hamburger.classList.remove("open"); + hamburger.setAttribute("aria-expanded", "false"); + }); + }); +} + +// ================= Dark Mode ================= +function setupDarkMode() { + const toggle = document.getElementById("darkModeToggle"); + const body = document.body; + const icon = document.getElementById("themeIcon"); + + if (!toggle || !icon) return; + + const savedTheme = localStorage.getItem("theme"); + if (savedTheme === "dark") { + body.classList.add("dark-theme"); + icon.textContent = "β˜€οΈ"; + } else { + icon.textContent = "πŸŒ™"; + } + + toggle.addEventListener("click", () => { + body.classList.toggle("dark-theme"); + const theme = body.classList.contains("dark-theme") ? "dark" : "light"; + localStorage.setItem("theme", theme); + icon.textContent = theme === "dark" ? "β˜€οΈ" : "πŸŒ™"; + }); +} + +// ================= Rendering Projects ================= +function renderProjects(projects) { + projectsContainer.innerHTML = ''; + + if (projects.length === 0) { + emptyStateElement.style.display = 'block'; + return; + } else { + emptyStateElement.style.display = 'none'; + } + + projects.forEach(project => { + const projectCard = document.createElement('div'); + projectCard.className = 'project-card fade-in'; + + projectCard.innerHTML = ` + ${project.previewImage ? `${project.title}` : ""} +

${project.title}

+

${project.description}

+
+ ${project.tags.map(tag => `${tag}`).join('')} +
+
+ Repo + ${project.demoUrl ? `Demo` : ""} +
+
+ ${project.difficulty} + +
+ `; + + projectsContainer.appendChild(projectCard); + }); + + setupUpvoteListeners(); +} + +// ================= Upvote Handling ================= +function setupUpvoteListeners() { + document.querySelectorAll('.upvote-btn').forEach(button => { + button.addEventListener('click', () => { + const projectId = parseInt(button.dataset.id); + const result = votingSystem.upvoteProject(projectId); + + if (result.success) { + button.querySelector('.count').textContent = result.newCount; + button.classList.add('voted'); + showNotification('Thanks for your vote! πŸ‘', 'success'); + } else { + showNotification(result.message, 'error'); } }); - }, { threshold: 0.1 }); + }); +} - faders.forEach(fade => observer.observe(fade)); -}); -entries.forEach((entry, index) => { - if (entry.isIntersecting) { - setTimeout(() => { - entry.target.classList.add('animate'); - }, index * 100); // 100ms delay between cards - observer.unobserve(entry.target); +// ================= Filters ================= +function applyFilters() { + let filteredProjects = [...sampleProjects]; + + // difficulty filter + const difficulty = difficultyFilter.value; + if (difficulty !== 'all') { + filteredProjects = filteredProjects.filter(p => p.difficulty === difficulty); } -}); + // demo filter + if (hasDemoFilter.checked) { + filteredProjects = filteredProjects.filter(p => p.hasDemo); + } - const reviewSwiper = new Swiper(".review-swiper", { - loop: true, - autoplay: { - delay: 4000, // 4 seconds per slide - disableOnInteraction: false, // keeps autoplay after manual swipe - }, - navigation: { - nextEl: ".swiper-button-next", - prevEl: ".swiper-button-prev", - }, - speed: 700, // smooth transition + // tag filter + if (selectedTag) { + filteredProjects = filteredProjects.filter(p => p.tags.includes(selectedTag)); + } + + // search filter + const searchTerm = searchInput.value.toLowerCase().trim(); + if (searchTerm) { + filteredProjects = filteredProjects.filter(p => + p.title.toLowerCase().includes(searchTerm) || + p.description.toLowerCase().includes(searchTerm) || + p.tags.some(tag => tag.toLowerCase().includes(searchTerm)) + ); + } + + // sort + const sortBy = sortByFilter.value; + filteredProjects.sort((a, b) => { + if (sortBy === 'popular') return b.upvotes - a.upvotes; + if (sortBy === 'newest') return b.id - a.id; + if (sortBy === 'oldest') return a.id - b.id; + return 0; + }); + + currentProjects = filteredProjects; + renderProjects(currentProjects); +} + +function resetFilters() { + difficultyFilter.value = 'all'; + hasDemoFilter.checked = false; + sortByFilter.value = 'popular'; + searchInput.value = ''; + clearSearchBtn.style.display = 'none'; + selectedTag = null; + + document.querySelectorAll('.tag-filter-btn').forEach(btn => btn.classList.remove('active')); + + currentProjects = [...sampleProjects]; + renderProjects(currentProjects); +} + +// ================= Event Listeners ================= +function setupEventListeners() { + applyFiltersBtn.addEventListener('click', applyFilters); + resetFiltersBtn.addEventListener('click', resetFilters); + + searchInput.addEventListener('input', () => { + clearSearchBtn.style.display = searchInput.value ? 'block' : 'none'; + }); + + clearSearchBtn.addEventListener('click', () => { + searchInput.value = ''; + clearSearchBtn.style.display = 'none'; + applyFilters(); + }); +} + +function initializeTagFilterListener() { + tagFiltersContainer.addEventListener('click', (e) => { + if (e.target.classList.contains('tag-filter-btn')) { + document.querySelectorAll('.tag-filter-btn').forEach(btn => btn.classList.remove('active')); + if (selectedTag === e.target.dataset.tag) { + selectedTag = null; + } else { + selectedTag = e.target.dataset.tag; + e.target.classList.add('active'); + } + applyFilters(); + } }); +} + +// ================= Run App ================= +init(); diff --git a/style.css b/style.css index d313e13..1cd72d4 100644 --- a/style.css +++ b/style.css @@ -1,1210 +1,3 @@ - - * { - margin: 0; - padding: 0; - box-sizing: border-box; - } - - /*dark mode toggle*/ - .theme-toggle { - background: transparent; - border: 2px solid var(--toggle-border); - border-radius: 50%; - width: 40px; - height: 40px; - display: flex; - align-items: center; - justify-content: center; - cursor: pointer; - transition: background 0.3s ease, transform 0.3s ease; - font-size: 1.2rem; - color: var(--toggle-icon); - box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); - } - - .theme-toggle:hover { - background: var(--toggle-hover); - transform: scale(1.1); - } - - .theme-toggle span { - transition: transform 0.3s ease, opacity 0.3s ease; - } - - :root { - --toggle-border: #ddd; - --toggle-icon: #333; - --toggle-hover: #f0f0f0; - --bg-gradient: linear-gradient(135deg, #667eea 0%, #764ba2 100%); - --text-color: #333; - --bg-card: rgba(255, 255, 255, 0.95); - --text-card: #333; - --box-shadow: 0 8px 32px rgba(31, 38, 135, 0.37); - --bg-header: rgba(255, 255, 255, 0.95); - --header-border: rgba(255, 255, 255, 0.18); - --card-bg: rgba(255, 255, 255, 0.95); - --card-border: rgba(255, 255, 255, 0.18); - --card-shadow: 0 8px 32px rgba(31, 38, 135, 0.37); - --card-hover-shadow: 0 20px 40px rgba(31, 38, 135, 0.5); - --nav-link-color: #333; - --nav-link-hover-bg: rgba(102, 126, 234, 0.1); - --project-title-color: #333; - --placeholder-bg: linear-gradient(135deg, #f5f7fa, #c3cfe2); - --placeholder-text: #666; - } - - .dark-theme { - --toggle-border: #888; - --toggle-icon: #f9f9f9; - --toggle-hover: #333; - --bg-gradient: linear-gradient(135deg, #1a1445 0%, #2c1d5c 100%); - --text-color: #e4e4f0; - --bg-card: rgba(30, 25, 60, 0.9); - --text-card: #eee; - --box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4); - --bg-header: rgba(30, 25, 60, 0.9); - --header-border: rgba(255, 255, 255, 0.05); - --card-bg: rgba(30, 25, 60, 0.9); - --card-border: rgba(255, 255, 255, 0.05); - --card-shadow: 0 8px 24px rgba(0, 0, 0, 0.4); - --card-hover-shadow: 0 20px 40px rgba(0, 0, 0, 0.6); - --nav-link-color: #f1f1f1; - --nav-link-hover-bg: rgba(255, 255, 255, 0.1); - --project-title-color: #ffffff; - --placeholder-bg: linear-gradient(135deg, #2a2d3e, #3b3f59); /* deep bluish-purple tone */ - --placeholder-text: #ddd; - } - - body { - font-family: 'Inter', sans-serif; - background: var(--bg-gradient); - color: var(--text-color); - min-height: 100vh; - transition: background 0.3s ease, color 0.3s ease; - } - - -.container, -.filter-container, -.projects-container { - - max-width: 1200px; - margin: 0 auto; - padding: 0 20px; -} -@keyframes Fadeinup { - from { - transform: translateY(20px); - opacity: 0; - } - to { - transform: translateY(0); - opacity: 1; - } -} - -.fade-in { - opacity: 0; /* βœ… start hidden */ - transform: translateY(20px); -} - -.fade-in.animate { - animation: Fadeinup 0.6s ease-out forwards; -} - - /* Header styles */ - .header { - background: var(--bg-header); - backdrop-filter: blur(10px); - box-shadow: var(--box-shadow); - border-bottom: 1px solid var(--header-border); - padding: 1rem 0; - position: sticky; - top: 0; - z-index: 100; - transition: background 0.3s, border-bottom 0.3s, box-shadow 0.3s; - } - - - .header-content { - display: flex; - justify-content: space-between; - align-items: center; - flex-wrap: wrap; - gap: 1rem; - } - - .logo { - font-size: 2rem; - font-weight: 700; - background: linear-gradient(135deg, #667eea, #764ba2); - -webkit-background-clip: text; - -webkit-text-fill-color: transparent; - background-clip: text; - } - - .nav { - display: flex; - gap: 2rem; - align-items: center; - flex-wrap: wrap; - } - - .nav-link { - color: var(--nav-link-color); - text-decoration: none; - font-weight: 500; - padding: 0.5rem 1rem; - border-radius: 8px; - transition: all 0.3s ease; - position: relative; - } - - .nav-link:hover { - background: var(--nav-link-hover-bg); - transform: translateY(-2px); - } - - - .btn-primary { - background: linear-gradient(135deg, #667eea, #764ba2); - color: white; - padding: 0.75rem 1.5rem; - border: none; - border-radius: 12px; - font-weight: 600; - cursor: pointer; - transition: all 0.3s ease; - text-decoration: none; - display: inline-block; - } - - .btn-primary:hover { - transform: translateY(-2px); - box-shadow: 0 10px 25px rgba(102, 126, 234, 0.3); - } - - /* Hero section */ - .hero { - text-align: center; - padding: 4rem 0; - color: white; - } - - .hero h1 { - font-size: 3.5rem; - font-weight: 700; - margin-bottom: 1rem; - text-shadow: 0 4px 8px rgba(0, 0, 0, 0.3); - } - - .hero p { - font-size: 1.2rem; - margin-bottom: 2rem; - opacity: 0.9; - max-width: 600px; - margin-left: auto; - margin-right: auto; - } -/* Light mode filters section */ -.filters { - background: inherit; /* same as page background */ - backdrop-filter: blur(14px); - border-radius: 16px; - padding: 25px; - margin-top: 60px; - border: 2px solid rgba(255, 255, 255, 0.7); /* glowing white border */ - box-shadow: 0 0 14px rgba(255, 255, 255, 0.6); /* white glow */ -} - -/* Dark mode filters section */ -.dark-theme .filters { - background: inherit; - backdrop-filter: blur(14px); - border-radius: 16px; - padding: 25px; - margin-top: 60px; - border: 2px solid rgba(255, 255, 255, 0.8); - box-shadow: 0 0 18px rgba(255, 255, 255, 0.75); /* stronger white glow */ -} - - - -.filters h2 { - font-size: 1.5rem; - font-weight: 700; - margin-bottom: 20px; - display: flex; - align-items: center; - gap: 10px; - color: var(--text-color); -} - -/* Controls grid */ -.filter-controls { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); - gap: 20px; - align-items: center; -} - -.full-width { - grid-column: 1 / -1; -} - -/* Search */ -.search-container { - position: relative; - display: flex; - align-items: center; -} - -.search-container input { - width: 100%; - padding: 12px 42px 12px 38px; - border: 1.5px solid rgba(255, 255, 255, 0.25); - border-radius: 10px; - background: rgba(255, 255, 255, 0.07); - color: var(--text-color); - font-size: 1rem; - transition: all 0.3s ease; -} - -.search-container input:focus { - border-color: var(--primary-color); - box-shadow: 0 0 6px rgba(30, 144, 255, 0.5); - outline: none; -} - -.search-container .search-icon { - position: absolute; - left: 12px; - color: #aaa; - font-size: 1rem; -} - -.search-container .clear-search { - position: absolute; - right: 10px; - background: transparent; - border: none; - font-size: 1rem; - color: #aaa; - cursor: pointer; - transition: color 0.2s ease; -} - -.search-container .clear-search:hover { - color: var(--primary-color); -} - -/* Dropdowns */ -.filter-group select { - width: 100%; - padding: 10px 14px; - border-radius: 10px; - border: 1.5px solid rgba(255, 255, 255, 0.25); - background: rgba(255, 255, 255, 0.07); - color: var(--text-color); - font-size: 1rem; - cursor: pointer; - transition: all 0.3s ease; -} - -.filter-group select:hover, -.filter-group select:focus { - border-color: var(--primary-color); - box-shadow: 0 0 5px rgba(30, 144, 255, 0.4); - outline: none; -} - -/* Checkbox */ -/* Toggle Switch for "Has Open Issues" */ -.checkbox-group { - display: flex; - align-items: center; - gap: 10px; -} - -.switch { - position: relative; - display: inline-block; - width: 46px; - height: 24px; -} - -.switch input { - opacity: 0; - width: 0; - height: 0; -} - -/* Slider (track) */ -.slider { - position: absolute; - cursor: pointer; - top: 0; left: 0; - right: 0; bottom: 0; - background: #fff; /* White when OFF */ - border: 1.5px solid rgba(0, 0, 0, 0.2); - border-radius: 34px; - transition: all 0.3s ease; -} - -/* The knob */ -.slider::before { - position: absolute; - content: ""; - height: 16px; width: 16px; - left: 3px; bottom: 3px; - background: #fff; - border-radius: 50%; - transition: 0.3s ease; - border: 1px solid rgba(0, 0, 0, 0.3); - box-shadow: 0 1px 2px rgba(0,0,0,0.2); -} - -/* Checked state (ON) */ -.switch input:checked + .slider { - background: var(--primary-color); - border-color: var(--primary-color); - box-shadow: 0 0 6px var(--primary-color); /* glowing border */ -} - -.switch input:checked + .slider::before { - transform: translateX(20px); - background: #fff; - border-color: white; -} - -/* Hover effect */ -.switch:hover .slider { - border-color: #000; -} - -.switch-label { - font-size: 0.95rem; - color: var(--text-color); - font-weight: 500; -} - - -/* Buttons */ -.filter-buttons { - display: flex; - gap: 12px; -} - -.filter-buttons button { - padding: 10px 18px; - border-radius: 8px; - font-size: 0.95rem; - font-weight: 600; - cursor: pointer; - background: transparent; - color: var(--text-color); - border: 1.5px solid rgba(255, 255, 255, 0.6); - transition: all 0.3s ease; -} - -.filter-buttons button:hover { - border-color: #000; - background: rgba(0, 0, 0, 0.05); - transform: translateY(-1px); -} - -/* Tag Filters */ -.tag-filters { - display: flex; - flex-wrap: wrap; - gap: 10px; - margin-top: 5px; -} - -.tag-filters .tag { - background: rgba(255, 255, 255, 0.1); - border-radius: 8px; - padding: 6px 12px; - font-size: 0.9rem; - font-weight: 500; - color: var(--text-color); - cursor: pointer; - transition: all 0.3s ease; -} - -.tag-filters .tag:hover { - background: var(--primary-color); - color: #fff; - transform: scale(1.05); -} - - - /* Search styles */ - .search-group { - flex: 1; - min-width: 300px; - } - - .search-container { - position: relative; - display: flex; - align-items: center; - } - - .search-icon { - position: absolute; - left: 12px; - color: #666; - z-index: 1; - } - - .search-container input[type="text"] { - width: 100%; - padding: 12px 40px 12px 40px; - border: 2px solid #ddd; - border-radius: 8px; - font-size: 1rem; - background: var(--card-bg); - color: var(--text-card); - transition: border-color 0.3s ease, box-shadow 0.3s ease; - } - - .search-container input[type="text"]:focus { - outline: none; - border-color: #667eea; - box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1); - } - - .clear-search { - position: absolute; - right: 8px; - background: none; - border: none; - color: #666; - cursor: pointer; - padding: 4px; - border-radius: 50%; - display: flex; - align-items: center; - justify-content: center; - transition: background-color 0.3s ease, color 0.3s ease; - } - - .clear-search:hover { - background-color: #f0f0f0; - color: #333; - } - - .dark-theme .search-container input[type="text"] { - border-color: #444; - background: var(--card-bg); - color: var(--text-card); - } - - .dark-theme .search-container input[type="text"]:focus { - border-color: #667eea; - } - - .dark-theme .clear-search:hover { - background-color: #444; - color: #fff; - } - - .btn-secondary { - background: white; - color: #667eea; - border: 2px solid #667eea; - padding: 0.75rem 1.5rem; - border-radius: 12px; - font-weight: 600; - cursor: pointer; - transition: all 0.3s ease; - } - - .btn-secondary:hover { - background: #667eea; - color: white; - transform: translateY(-2px); - } - - .tag-filters { - display: flex; - flex-wrap: wrap; - gap: 8px; - margin: 10px 0; - } - - .tag-filter-btn { - padding: 6px 12px; - background: #f0f0f0; - border: 1px solid #ccc; - border-radius: 20px; - cursor: pointer; - transition: background 0.3s; - } - - .tag-filter-btn.active { - background: #007bff; - color: #fff; - border-color: #007bff; - } - - /* Projects section */ - .projects-section { - padding: 2rem 0; - } - - .section-title { - font-size: 2rem; - font-weight: 700; - color: white; - margin-bottom: 2rem; - text-align: center; - text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3); - } - - .projects-grid { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(350px, 1fr)); - gap: 2rem; - margin-bottom: 3rem; - } - - .project-card { - background: var(--card-bg); - backdrop-filter: blur(10px); - border-radius: 16px; - padding: 1.5rem; - box-shadow: var(--card-shadow); - border: 1px solid var(--card-border); - transition: all 0.3s ease; - display: flex; - flex-direction: column; - position: relative; - } - - .project-card:hover { - transform: translateY(-5px); - box-shadow: var(--card-hover-shadow); - } - - .project-card.top-ranked { - border: 2px solid #ffd700; - box-shadow: 0 8px 32px rgba(255, 215, 0, 0.2); - } - - .rank-badge { - position: absolute; - top: -10px; - right: -10px; - background: linear-gradient(135deg, #ffd700, #ffed4e); - color: #333; - width: 40px; - height: 40px; - border-radius: 50%; - display: flex; - align-items: center; - justify-content: center; - font-weight: bold; - font-size: 0.9rem; - z-index: 10; - box-shadow: 0 4px 12px rgba(255, 215, 0, 0.3); - border: 2px solid white; - } - - - .project-image { - width: 100%; - height: 200px; - object-fit: cover; - border-radius: 12px; - margin-bottom: 1rem; - } - - .project-placeholder { - width: 100%; - height: 200px; - background: var(--placeholder-bg); - border-radius: 12px; - display: flex; - align-items: center; - justify-content: center; - margin-bottom: 1rem; - color: var(--placeholder-text); - font-weight: 500; - } - - - .project-header { - display: flex; - justify-content: space-between; - align-items: center; - margin-bottom: 1rem; - } - - .project-title { - font-size: 1.25rem; - font-weight: 700; - color: var(--project-title-color); - } - - - .repo-link { - color: #667eea; - font-size: 1.5rem; - transition: all 0.3s ease; - } - - .repo-link:hover { - color: #764ba2; - transform: scale(1.1); - } - - .difficulty-badge { - display: inline-block; - padding: 0.25rem 0.75rem; - border-radius: 20px; - font-size: 0.875rem; - font-weight: 600; - margin-bottom: 1rem; - } - - .difficulty-beginner { - background: rgba(34, 197, 94, 0.1); - color: #22c55e; - } - - .difficulty-intermediate { - background: rgba(251, 191, 36, 0.1); - color: #fbbf24; - } - - .difficulty-advanced { - background: rgba(239, 68, 68, 0.1); - color: #ef4444; - } - - .project-description { - color: #666; - margin-bottom: 1rem; - line-height: 1.6; - flex-grow: 1; - } - - .project-meta { - display: flex; - align-items: center; - gap: 0.5rem; - font-size: 0.875rem; - color: #888; - margin-bottom: 1rem; - } - - .meta-icon { - color: #667eea; - } - - .project-tags { - margin-top: 0.5rem; - margin-bottom: 5px; - display: flex; - flex-wrap: wrap; - gap: 6px; - } - - .tag-badge { - background-color: #e0e0e0; - color: #333; - padding: 4px 8px; - border-radius: 12px; - font-size: 12px; - display: inline-block; - white-space: nowrap; - } -/* Dark mode fix for tags */ -body.dark-theme .tag-badge { - background-color: rgba(102, 126, 234, 0.2); /* subtle purple tint */ - color: #ffffff; /* bright white text */ - border: 1px solid rgba(102, 126, 234, 0.4); /* soft border */ -} - - - .upvote-section { - display: flex; - justify-content: space-between; - align-items: center; - margin-top: auto; - padding-top: 1rem; - border-top: 1px solid #eee; - } - - .upvote-btn { - background: linear-gradient(135deg, #667eea, #764ba2); - color: white; - border: none; - padding: 0.5rem 1rem; - border-radius: 8px; - font-weight: 600; - cursor: pointer; - display: flex; - align-items: center; - gap: 0.5rem; - transition: all 0.3s ease; - position: relative; - } - - .upvote-btn:hover { - transform: translateY(-1px); - box-shadow: 0 4px 12px rgba(102, 126, 234, 0.3); - } - - .upvote-btn.voted { - background: linear-gradient(135deg, #28a745, #20c997); - cursor: default; - } - - .upvote-btn.voted:hover { - transform: none; - box-shadow: 0 2px 8px rgba(40, 167, 69, 0.3); - } - - .upvote-btn:disabled { - opacity: 0.7; - cursor: not-allowed; - } - - .upvote-btn:disabled:hover { - transform: none; - box-shadow: none; - } - - /* Notification styles */ - .vote-notification { - position: fixed; - top: 20px; - right: 20px; - background: white; - color: #333; - padding: 1rem 1.5rem; - border-radius: 8px; - box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); - display: flex; - align-items: center; - gap: 0.5rem; - z-index: 1000; - transform: translateX(400px); - opacity: 0; - transition: all 0.3s ease; - font-weight: 500; - border-left: 4px solid #007bff; - } - - .vote-notification.success { - border-left-color: #28a745; - color: #155724; - } - - .vote-notification.success i { - color: #28a745; - } - - .vote-notification.error { - border-left-color: #dc3545; - color: #721c24; - } - - .vote-notification.error i { - color: #dc3545; - } - - .vote-notification.show { - transform: translateX(0); - opacity: 1; - } - - /* Dark theme notification styles */ - .dark-theme .vote-notification { - background: var(--card-bg); - color: var(--text-color); - box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3); - } - - - - /* REVIEW SECTION */ - .reviews-section { - padding: 60px 0; - cursor: pointer; - } - - .review-section-title { - font-size: 2rem; - font-weight: 700; - color: white; - margin: 5rem; - text-align: center; - text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3); - } - - - .swiper { - width: 100%; - padding: 2rem 0; - position: relative; - cursor:auto; - } - - .swiper-wrapper { - align-items: center; - cursor:auto; - } - - - .swiper-slide { - display: flex; - justify-content: center; - align-items: center; - cursor:auto; - - } - - - .testimonial { - display: flex; - align-items: center; - gap: 1.2rem; - background-color: var(--card-bg, #fff); - padding: 2.3rem 3rem; - border-radius: 16px; - box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08); - transition: transform 0.3s ease, box-shadow 0.3s ease; - border: 2px solid black; - width: 100%; - max-width: 600px; - margin: 0 auto; - min-height: 160px; - } - - .avatar { - width: 150px; - height: 150px; - border-radius: 50%; - object-fit: cover; - border: 3px solid var(--accent-color, #6c63ff); - flex-shrink: 0; - } - - - .testimonial-content { - flex: 1; - display: flex; - flex-direction: column; - justify-content: center; - } - - - .review-name { - font-size: 1.45rem; - font-weight: 900; - margin-bottom: 0.25rem; - color: var(--text-primary, #111); - } - - .review-role { - font-size: 1.15rem; - font-style: italic; - color: var(--text-muted, #777); - margin-bottom: 0.4rem; - } - - .testimonial-text { - font-size: 1rem; - line-height: 1.5; - color: var(--text-secondary, #333); - } - - .swiper-button-prev span, - .swiper-button-next span { - width: 30px; - height: 10px; - background-color: white; - color: black; - border: 2px solid rgb(0, 0, 0); - border-radius: 9px; - font-size: 1.4rem; - font-weight:900; - display: flex; - align-items: center; - justify-content: center; - top: 50%; - transform: translateY(-50%); - z-index: 10; - cursor: pointer; - transition: all 0.3s ease; - padding: 20px; - - } - - .swiper-button-prev::after, - .swiper-button-next::after { - content: ''; - display: none; - } - - .swiper-button-prev span { - left: 20px; - margin-left: 25rem; - } - - .swiper-button-next span{ - right: 20px; - margin-right: 25rem; - } - - - .dark-theme .reviews-section .testimonial { - background-color: rgba(30, 25, 60, 0.9); - border: 2px solid white; - box-shadow: 0 8px 24px rgba(0, 0, 0, 0.4); - } - - .dark-theme .reviews-section .testimonial:hover { - box-shadow: 0 20px 40px rgba(0, 0, 0, 0.6); - } - - .dark-theme .reviews-section .review-name, - .dark-theme .reviews-section .testimonial-text { - color: var(--text-card, #eee); - } - - .dark-theme .reviews-section .review-role { - color: var(--text-muted, #aaa); - } - - - .dark-theme .swiper-button-prev, - .dark-theme .swiper-button-next { - border-color: white; - color: white; - } - - @media (max-width: 1024px) { - .review-section-title { - font-size: 1.8rem; - margin: 4rem 1rem; - } - - .testimonial { - flex-direction: row; - gap: 1rem; - padding: 1.5rem; - max-width: 90%; - } - - .avatar { - width: 120px; - height: 120px; - } - - .review-name { - font-size: 1.2rem; - } - - .review-role, - .testimonial-text { - font-size: 0.95rem; - } - - .swiper-button-prev span, - .swiper-button-next span { - width: 45px; - height: 45px; - padding: 15px; - } - } - - @media (max-width: 768px) { - .review-section-title { - font-size: 1.6rem; - margin: 3rem 1rem 2rem; - } - - .testimonial { - flex-direction: column; - align-items: center; - text-align: center; - gap: 1rem; - padding: 1.2rem; - max-width: 95%; - } - - .avatar { - width: 100px; - height: 100px; - } - - .review-name { - font-size: 1.1rem; - } - - .review-role { - font-size: 0.9rem; - } - - .testimonial-text { - font-size: 0.9rem; - } - - .swiper-button-prev span, - .swiper-button-next span { - width: 40px; - height: 40px; - padding: 12px; - font-size: 1.2rem; - margin: 0; - } - - .swiper-button-prev span { - left: 10px; - } - - .swiper-button-next span { - right: 10px; - } - } - - @media (max-width: 480px) { - .review-section-title { - font-size: 1.4rem; - margin: 2rem 0 1.5rem; - } - - .testimonial { - padding: 1rem; - border-radius: 12px; - } - - .avatar { - width: 80px; - height: 80px; - } - - .review-name { - font-size: 1rem; - } - - .review-role, - .testimonial-text { - font-size: 0.85rem; - } - - .swiper-button-prev span, - .swiper-button-next span { - width: 36px; - height: 36px; - padding: 10px; - font-size: 1rem; - } - - .swiper-button-prev span { - margin-left: 0.5rem; - } - - .swiper-button-next span { - margin-right: 0.5rem; - } - } - - /* Footer */ - .footer { - background: rgba(0, 0, 0, 0.8); - color: white; - text-align: center; - padding: 2rem 0; - margin-top: 3rem; - } - - /* Responsive design */ - @media (max-width: 768px) { - .hero h1 { - font-size: 2.5rem; - } - - .nav { - gap: 1rem; - } - - .filter-controls { - flex-direction: column; - align-items: stretch; - } - - .search-group { - min-width: 100%; - } - - .projects-grid { - grid-template-columns: 1fr; - } - - .header-content { - flex-direction: column; - text-align: center; - } - } - - /* Loading animation */ - .loading { - display: flex; - justify-content: center; - align-items: center; - height: 200px; - color: white; - } - - .spinner { - width: 40px; - height: 40px; - border: 4px solid rgba(255, 255, 255, 0.3); - border-top: 4px solid white; - border-radius: 50%; - animation: spin 1s linear infinite; - } - - @keyframes spin { - 0% { transform: rotate(0deg); } - 100% { transform: rotate(360deg); } - } - - /* Empty state */ - .empty-state { - text-align: center; - color: white; - padding: 3rem 0; - } - - .empty-state i { - font-size: 4rem; - margin-bottom: 1rem; - opacity: 0.5; - } - - .empty-state h3 { - font-size: 1.5rem; - margin-bottom: 0.5rem; - } - - .empty-state p { - opacity: 0.8; - } - - @keyframes bounce { - 0%, 100% { transform: translateY(0); } - 50% { transform: translateY(-5px); } - } - - /* Newly injected code */ - #contact-container { background-color: rgba(255, 255, 255, 0.151); backdrop-filter: blur(2px); @@ -1213,23 +6,26 @@ body.dark-theme .tag-badge { padding: 30px; } -#email { - width: 100% !important; - font-size: 1.2rem; - padding: 18px 18px 18px 50px; +#email, +#message, +#name, +#lastname { width: 100%; + font-size: 1.2rem; + padding: 18px; border: none; border-radius: 15px; line-height: 1.2; box-sizing: border-box; appearance: none; + box-shadow: 10px 10px 40px #ffffff41, inset -10px -10px 40px #00000026; + background: var(--card-bg); + color: var(--text-card); } -#email, -#message, -#name, -#lastname { - box-shadow: 10px 10px 40px #ffffff41, inset -10px -10px 40px #00000026; +/* Extra left padding for email (from master) */ +#email { + padding: 18px 18px 18px 50px; } #email:focus, @@ -1262,6 +58,7 @@ textarea { position: relative; } +/* Message Overlay */ #message-overlay { position: fixed; top: 0; @@ -1328,8 +125,7 @@ textarea { animation: softPulse 3s infinite ease-in-out; } -/* Tweaking button */ - +/* Fill Button */ .fill-btn { position: relative; display: inline-block; @@ -1367,44 +163,18 @@ textarea { } body.dark-theme .fill-btn { - position: relative; - display: inline-block; - padding: 14px 28px; - font-size: 1.2rem; - font-weight: bold; - color: white; background-color: #1a1445; - border: 2px solid white; - overflow: hidden; - cursor: pointer; - z-index: 1; -} - -body.dark-theme .fill-btn::before { - content: ""; - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - background-color: white; - transform: scaleX(0); - transform-origin: left; - transition: transform 0.4s ease-out; - z-index: -1; -} - -body.dark-theme .fill-btn:hover::before { - transform: scaleX(1); } body.dark-theme .fill-btn:hover { color: black; } -@media only screen and (max-width: 1020px) { +/* Responsive adjustments */ +@media (max-width: 1020px) { #contact-container { width: 95%; + padding: 3vw; } .flex-contact { @@ -1417,47 +187,58 @@ body.dark-theme .fill-btn:hover { width: 100%; } - #contact-container { - padding: 3vw; - } - #submit { width: 100%; } } +@media (max-width: 768px) { + .hero h1 { + font-size: 2.5rem; + } + + .filter-controls { + flex-direction: column; + align-items: stretch; + } + + .search-group { + min-width: 100%; + } + + .projects-grid { + grid-template-columns: 1fr; + } + + .header-content { + flex-direction: row; + } +} + /* ------------------------------------------- */ +/* Dark theme enhancements */ .dark-theme .filters { - background: rgba(18, 25, 60, 0.75); - /* dark blue translucent */ backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px); - border-radius: 16px; border: 1px solid rgba(255, 255, 255, 0.1); - box-shadow: 0 0 8px rgba(44, 66, 255, 0.4), 0 0 16px rgba(44, 66, 255, 0.3); - color: #f0f4ff; transition: all 0.3s ease; } .dark-theme .project-card { background: rgba(18, 25, 60, 0.75); - /* dark blue translucent */ backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px); - border-radius: 16px; border: 1px solid rgba(255, 255, 255, 0.1); - box-shadow: 0 0 8px rgba(44, 66, 255, 0.4), 0 0 16px rgba(44, 66, 255, 0.3); - color: #f0f4ff; transition: all 0.3s ease; } @@ -1468,7 +249,6 @@ body.dark-theme button:hover { cursor: pointer; } - /* Dark theme text and border colors */ body.dark-theme h1, body.dark-theme .filter-container h2, @@ -1486,7 +266,6 @@ body.dark-theme textarea, body.dark-theme button, body.dark-theme .projects-container, body.dark-theme .filter-container { - border-color: #ffffff; } @@ -1509,7 +288,7 @@ body.dark-theme button:hover { cursor: pointer; } - +/* Scroll to top button */ #scrollToTopBtn { position: fixed; bottom: 2rem; @@ -1529,8 +308,8 @@ body.dark-theme button:hover { } #scrollToTopBtn:hover { - background-color: #5a67d8; - transform: scale(1.1); + background-color: #5a67d8; + transform: scale(1.1); } .footer-flex-container { @@ -1584,4 +363,4 @@ body.dark-theme button:hover { flex-wrap: wrap; font-size: 14px; gap: 50px; -} \ No newline at end of file +}