Excellent implementation! Your design for WebApp Showcase Wizardry is a modern, sleek, and highly interactive platform. I'll provide some observations and potential enhancements:
Browse filesDesign Strengths
Vanta.js Background
Dynamic globe background adds depth
Subtle opacity allows content to remain readable
Color scheme complements dark theme
Sidebar Navigation
Clean, iconographic design
Hover effects enhance interactivity
User profile section adds personalization
Upload Modal
Comprehensive file upload interface
Clear instructions
Drag and drop functionality
Detailed project metadata collection
App Cards
Gradient backgrounds
Technology tags
View count
Preview button
Hover animations
Potential Enhancements
1. Accessibility Improvements
Copy
// Add ARIA labels for better screen reader support
document.querySelector('.upload-btn').setAttribute('aria-label', 'Upload New Web Application');
2. Advanced Drag and Drop
Copy
dropzone.addEventListener('drop', (e) => {
e.preventDefault();
const files = e.dataTransfer.files;
// File type validation
const allowedTypes = [
'text/html',
'text/css',
'application/javascript',
'application/zip'
];
const validFiles = Array.from(files).filter(file =>
allowedTypes.includes(file.type)
);
if (validFiles.length === 0) {
alert('Please upload valid web application files');
return;
}
handleFiles(validFiles);
});
3. Search Functionality
Copy
const searchInput = document.querySelector('input[type="search"]');
const appCards = document.querySelectorAll('.app-card');
searchInput.addEventListener('input', (e) => {
const searchTerm = e.target.value.toLowerCase();
appCards.forEach(card => {
const title = card.querySelector('h3').textContent.toLowerCase();
const tags = Array.from(card.querySelectorAll('.bg-gray-700'))
.map(tag => tag.textContent.toLowerCase());
const isVisible = title.includes(searchTerm) ||
tags.some(tag => tag.includes(searchTerm));
card.style.display = isVisible ? 'block' : 'none';
});
});
4. Local Storage for User Preferences
Copy
// Save sidebar state
const sidebar = document.querySelector('aside');
const sidebarToggle = document.getElementById('sidebar-toggle');
sidebarToggle.addEventListener('click', () => {
sidebar.classList.toggle('hidden');
localStorage.setItem('sidebarState', sidebar.classList.contains('hidden') ? 'closed' : 'open');
});
// Restore sidebar state on page load
document.addEventListener('DOMContentLoaded', () => {
const savedState = localStorage.getItem('sidebarState');
if (savedState === 'closed') {
sidebar.classList.add('hidden');
}
});
- index.html +29 -5
|
@@ -286,7 +286,6 @@ VANTA.GLOBE({
|
|
| 286 |
color: 0x2ecc71,
|
| 287 |
backgroundColor: 0x1a1a1a
|
| 288 |
});
|
| 289 |
-
|
| 290 |
// Modal functionality
|
| 291 |
const uploadBtn = document.getElementById('upload-btn');
|
| 292 |
const uploadModal = document.getElementById('upload-modal');
|
|
@@ -294,7 +293,13 @@ VANTA.GLOBE({
|
|
| 294 |
const dropzone = document.getElementById('dropzone');
|
| 295 |
uploadBtn.addEventListener('click', () => {
|
| 296 |
uploadBtn.setAttribute('aria-label', 'Open upload modal');
|
| 297 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 298 |
uploadModal.classList.remove('hidden');
|
| 299 |
document.body.style.overflow = 'hidden';
|
| 300 |
});
|
|
@@ -313,19 +318,36 @@ uploadModal.classList.remove('hidden');
|
|
| 313 |
dropzone.addEventListener('dragleave', () => {
|
| 314 |
dropzone.classList.remove('dragover');
|
| 315 |
});
|
| 316 |
-
|
| 317 |
dropzone.addEventListener('drop', (e) => {
|
| 318 |
e.preventDefault();
|
| 319 |
dropzone.classList.remove('dragover');
|
| 320 |
const files = e.dataTransfer.files;
|
| 321 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 322 |
// Handle file upload
|
| 323 |
});
|
| 324 |
-
|
| 325 |
// Initialize feather icons
|
| 326 |
feather.replace();
|
| 327 |
|
| 328 |
// Search functionality
|
|
|
|
| 329 |
const appCards = document.querySelectorAll('.app-card');
|
| 330 |
|
| 331 |
searchInput.addEventListener('input', (e) => {
|
|
@@ -333,10 +355,12 @@ uploadModal.classList.remove('hidden');
|
|
| 333 |
|
| 334 |
appCards.forEach(card => {
|
| 335 |
const title = card.querySelector('h3').textContent.toLowerCase();
|
|
|
|
| 336 |
const tags = Array.from(card.querySelectorAll('.bg-gray-700'))
|
| 337 |
.map(tag => tag.textContent.toLowerCase());
|
| 338 |
|
| 339 |
const isVisible = title.includes(searchTerm) ||
|
|
|
|
| 340 |
tags.some(tag => tag.includes(searchTerm));
|
| 341 |
|
| 342 |
card.style.display = isVisible ? 'block' : 'none';
|
|
|
|
| 286 |
color: 0x2ecc71,
|
| 287 |
backgroundColor: 0x1a1a1a
|
| 288 |
});
|
|
|
|
| 289 |
// Modal functionality
|
| 290 |
const uploadBtn = document.getElementById('upload-btn');
|
| 291 |
const uploadModal = document.getElementById('upload-modal');
|
|
|
|
| 293 |
const dropzone = document.getElementById('dropzone');
|
| 294 |
uploadBtn.addEventListener('click', () => {
|
| 295 |
uploadBtn.setAttribute('aria-label', 'Open upload modal');
|
| 296 |
+
uploadModal.classList.remove('hidden');
|
| 297 |
+
document.body.style.overflow = 'hidden';
|
| 298 |
+
});
|
| 299 |
+
|
| 300 |
+
// Add ARIA labels for accessibility
|
| 301 |
+
document.querySelector('#upload-btn').setAttribute('aria-label', 'Upload New Web Application');
|
| 302 |
+
document.querySelector('#close-modal').setAttribute('aria-label', 'Close upload modal');
|
| 303 |
uploadModal.classList.remove('hidden');
|
| 304 |
document.body.style.overflow = 'hidden';
|
| 305 |
});
|
|
|
|
| 318 |
dropzone.addEventListener('dragleave', () => {
|
| 319 |
dropzone.classList.remove('dragover');
|
| 320 |
});
|
|
|
|
| 321 |
dropzone.addEventListener('drop', (e) => {
|
| 322 |
e.preventDefault();
|
| 323 |
dropzone.classList.remove('dragover');
|
| 324 |
const files = e.dataTransfer.files;
|
| 325 |
+
|
| 326 |
+
// File type validation
|
| 327 |
+
const allowedTypes = [
|
| 328 |
+
'text/html',
|
| 329 |
+
'text/css',
|
| 330 |
+
'application/javascript',
|
| 331 |
+
'application/zip'
|
| 332 |
+
];
|
| 333 |
+
|
| 334 |
+
const validFiles = Array.from(files).filter(file =>
|
| 335 |
+
allowedTypes.some(type => file.type.includes(type.replace('*', '')))
|
| 336 |
+
);
|
| 337 |
+
|
| 338 |
+
if (validFiles.length === 0) {
|
| 339 |
+
alert('Please upload valid web application files (HTML, CSS, JS or ZIP)');
|
| 340 |
+
return;
|
| 341 |
+
}
|
| 342 |
+
|
| 343 |
+
console.log('Valid files dropped:', validFiles);
|
| 344 |
// Handle file upload
|
| 345 |
});
|
|
|
|
| 346 |
// Initialize feather icons
|
| 347 |
feather.replace();
|
| 348 |
|
| 349 |
// Search functionality
|
| 350 |
+
const searchInput = document.querySelector('input[type="search"]');
|
| 351 |
const appCards = document.querySelectorAll('.app-card');
|
| 352 |
|
| 353 |
searchInput.addEventListener('input', (e) => {
|
|
|
|
| 355 |
|
| 356 |
appCards.forEach(card => {
|
| 357 |
const title = card.querySelector('h3').textContent.toLowerCase();
|
| 358 |
+
const description = card.querySelector('p').textContent.toLowerCase();
|
| 359 |
const tags = Array.from(card.querySelectorAll('.bg-gray-700'))
|
| 360 |
.map(tag => tag.textContent.toLowerCase());
|
| 361 |
|
| 362 |
const isVisible = title.includes(searchTerm) ||
|
| 363 |
+
description.includes(searchTerm) ||
|
| 364 |
tags.some(tag => tag.includes(searchTerm));
|
| 365 |
|
| 366 |
card.style.display = isVisible ? 'block' : 'none';
|