Features Implemented:
Responsive 4-Column Layout:
- Uses CSS Grid for desktop view (4 columns)
- Switches to 2 columns on medium screens (tablets)
- Stacks to 1 column on mobile devices
Profile Column:
- Circular profile image with border
- Centered with caption
- Soft shadow and hover effect
Basic Details Column:
- Student name as heading
- Field of study as subheading
- Short bio paragraph
Portfolio Column:
- List of 5 projects with clickable links
- Hover effects for better interactivity
Contact Column:
- Contact form with name, email, and message fields
- JavaScript alert on form submission
- Download CV button with distinct styling
Design Elements:
- Clean, minimal aesthetic
- Soft shadows and rounded corners
- Hover animations for interactivity
- Professional color scheme
- Legible typography
Technical Implementation:
- Pure HTML, CSS, and JavaScript (no external libraries)
- CSS Grid for layout
- Media queries for responsiveness
- Simple form validation and feedback
The placeholder image can be replaced with an actual student photo by changing the src attribute in the profile image tag. Similarly, the download CV link can be updated to point to an actual PDF file.
/* CSS Reset */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Poppins', sans-serif;
background-color: #f5f5f5;
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
padding: 20px;
}
.counter-container {
background-color: white;
border-radius: 12px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
padding: 30px;
width: 100%;
max-width: 400px;
text-align: center;
}
.counter-title {
font-size: 1.5rem;
color: #333;
margin-bottom: 20px;
font-weight: 500;
}
.counter-value {
font-size: 3rem;
font-weight: 600;
color: #4a6cf7;
margin-bottom: 15px;
}
.counter-label {
font-size: 1rem;
color: #666;
margin-bottom: 25px;
}
.stats-container {
display: flex;
justify-content: space-around;
margin-top: 20px;
padding-top: 20px;
border-top: 1px solid #eee;
}
.stat-box {
text-align: center;
}
.stat-value {
font-size: 1.2rem;
font-weight: 500;
color: #4a6cf7;
}
.stat-label {
font-size: 0.8rem;
color: #888;
margin-top: 5px;
}
.reset-btn {
margin-top: 20px;
padding: 8px 16px;
background-color: #f5f5f5;
border: none;
border-radius: 6px;
color: #666;
cursor: pointer;
font-family: 'Poppins', sans-serif;
transition: all 0.2s;
}
.reset-btn:hover {
background-color: #eee;
}
/* Animation */
@keyframes pulse {
0% { transform: scale(1); }
50% { transform: scale(1.05); }
100% { transform: scale(1); }
}
.pulse {
animation: pulse 0.5s ease-in-out;
}
document.addEventListener('DOMContentLoaded', function() {
// DOM Elements
const totalVisitorsEl = document.getElementById('totalVisitors');
const todayVisitorsEl = document.getElementById('todayVisitors');
const lastVisitEl = document.getElementById('lastVisit');
const resetBtn = document.getElementById('resetBtn');
// Initialize or get visitor data from localStorage
let visitorData = JSON.parse(localStorage.getItem('visitorData')) || {
total: 0,
today: 0,
lastVisit: null,
lastVisitDate: null
};
// Get current date in YYYY-MM-DD format
const today = new Date().toISOString().split('T')[0];
// Check if last visit was today
if (visitorData.lastVisitDate === today) {
// Same day visit - increment today's count
visitorData.today += 1;
} else {
// New day - reset today's count
visitorData.today = 1;
}
// Update visitor data
visitorData.total += 1;
visitorData.lastVisit = new Date().toString();
visitorData.lastVisitDate = today;
// Save to localStorage
localStorage.setItem('visitorData', JSON.stringify(visitorData));
// Animate counter
animateCounter(totalVisitorsEl, visitorData.total);
animateCounter(todayVisitorsEl, visitorData.today);
// Format and display last visit time
if (visitorData.lastVisit) {
const lastVisitDate = new Date(visitorData.lastVisit);
const options = {
hour: '2-digit',
minute: '2-digit',
day: 'numeric',
month: 'short'
};
lastVisitEl.textContent = lastVisitDate.toLocaleTimeString('en-US', options);
}
// Reset button functionality
resetBtn.addEventListener('click', function() {
if (confirm('Are you sure you want to reset the visitor counter?')) {
localStorage.removeItem('visitorData');
animateCounter(totalVisitorsEl, 0);
animateCounter(todayVisitorsEl, 0);
lastVisitEl.textContent = 'Never';
// Reset visitor data object
visitorData = {
total: 0,
today: 0,
lastVisit: null,
lastVisitDate: null
};
}
});
// Counter animation function
function animateCounter(element, target) {
const duration = 2000; // Animation duration in ms
const start = 0;
const increment = target / (duration / 16); // 60fps
let current = start;
const timer = setInterval(() => {
current += increment;
if (current >= target) {
clearInterval(timer);
current = target;
element.classList.add('pulse');
setTimeout(() => element.classList.remove('pulse'), 500);
}
element.textContent = Math.floor(current).toLocaleString();
}, 16);
}
});
إرسال تعليق
Thank you
Learning robo team