Overview
The Dev Showcase modal system provides lightbox functionality for certificates and project images. It features smooth animations, keyboard navigation, and prevents body scrolling when modals are active.
Modal Types
Certificate Modal Displays certificates with title and date metadata
Image Modal Full-screen image viewer for project screenshots
Architecture
Core Functions
The modal system uses two simple functions for all interactions:
modal-manager.js (lines 12-22)
function openModal ( modal ) {
if ( ! modal ) return ;
modal . classList . add ( 'active' );
document . body . style . overflow = 'hidden' ;
}
function closeModal ( modal ) {
if ( ! modal ) return ;
modal . classList . remove ( 'active' );
document . body . style . overflow = '' ;
}
The modal system prevents body scrolling by setting overflow: hidden on the body element when a modal is active.
Certificate Modal
HTML Structure
The certificate modal displays both the certificate image and metadata:
< div id = "certificateModal" class = "certificate-modal" >
< div class = "modal-content" >
< div class = "modal-close-btn" id = "certificateModalCloseBtn" >
< svg > <!-- Close icon --> </ svg >
</ div >
< img id = "certificateModalImage" src = "" alt = "" >
< div class = "certificate-modal-info" >
< h3 id = "certificateModalTitle" ></ h3 >
< p id = "certificateModalDate" ></ p >
</ div >
</ div >
</ div >
Event Handler
Certificate cards open the modal with their data:
modal-manager.js (lines 24-37)
document . querySelectorAll ( '.certificate-card' ). forEach ( card => {
card . addEventListener ( 'click' , () => {
const img = card . querySelector ( 'img' );
const name = card . querySelector ( '.certificate-info p:first-child' );
const date = card . querySelector ( '.certificate-info span' );
if ( ! img || ! certModal ) return ;
certModalImg . src = img . src ;
certModalImg . alt = img . alt ;
if ( certModalTitle ) certModalTitle . textContent = name ? name . textContent : '' ;
if ( certModalDate ) certModalDate . textContent = date ? date . textContent : '' ;
openModal ( certModal );
});
});
Certificate Card HTML
< div class = "certificate-card" >
< div class = "certificate-image-container" >
< img src = "cert.jpg" alt = "Certificate name" >
</ div >
< div class = "certificate-info" >
< p > Certificate Title </ p >
< span > June 2024 </ span >
</ div >
</ div >
Image Modal
Simple Image Viewer
The image modal provides a clean, full-screen viewing experience:
modal-manager.js (lines 42-49)
document . querySelectorAll ( '.left-pnl.img-pnl img' ). forEach ( img => {
img . addEventListener ( 'click' , () => {
if ( ! imageModal || ! imageModalImg ) return ;
imageModalImg . src = img . src ;
imageModalImg . alt = img . alt ;
openModal ( imageModal );
});
});
HTML Structure
< div id = "imageModal" class = "image-modal" >
< div class = "modal-content" >
< div class = "modal-close-btn" id = "modalCloseBtn" >
< svg > <!-- Close icon --> </ svg >
</ div >
< img id = "modalImage" src = "" alt = "" >
</ div >
</ div >
Closing Modals
Multiple Close Methods
Modals can be closed in three ways:
The Escape key handler closes all active modals, providing a universal exit method.
CSS Styling
Modal Base Styles
.image-modal ,
.certificate-modal {
position : fixed ;
top : 0 ;
left : 0 ;
width : 100 % ;
height : 100 % ;
background-color : rgba ( 0 , 0 , 0 , 0.9 );
z-index : 2000 ;
display : flex ;
align-items : center ;
justify-content : center ;
opacity : 0 ;
pointer-events : none ;
transition : opacity var ( --transition-speed ) var ( --transition-easing );
}
.image-modal.active ,
.certificate-modal.active {
opacity : 1 ;
pointer-events : auto ;
}
Zoom Animation
Modals animate in with a zoom effect:
.modal-content {
position : relative ;
max-width : 90 % ;
max-height : 90 % ;
animation : modalZoom 0.3 s var ( --transition-easing );
}
@keyframes modalZoom {
from {
transform : scale ( 0.8 );
opacity : 0 ;
}
to {
transform : scale ( 1 );
opacity : 1 ;
}
}
.modal-close-btn {
position : absolute ;
top : -50 px ;
right : 0 ;
width : 40 px ;
height : 40 px ;
background-color : var ( --color-accent-red );
border-radius : 50 % ;
display : flex ;
align-items : center ;
justify-content : center ;
cursor : pointer ;
transition : all var ( --transition-speed ) var ( --transition-easing );
}
.modal-close-btn:hover {
background-color : var ( --color-accent-red-light );
transform : rotate ( 90 deg );
}
.modal-close-btn svg {
width : 24 px ;
height : 24 px ;
stroke : #ffffff ;
stroke-width : 2 ;
stroke-linecap : round ;
}
The close button rotates 90 degrees on hover for a playful interaction.
Certificate Grid
Responsive Grid Layout
.certificates-grid {
display : grid ;
grid-template-columns : repeat ( auto-fill , minmax ( 250 px , 1 fr ));
gap : 1.5 rem ;
margin-top : 2 rem ;
margin-bottom : 2 rem ;
}
Certificate Card Styles
modal.css (lines 101-132)
.certificate-card {
background-color : var ( --color-bg-tertiary );
border : 1 px solid var ( --color-border );
border-radius : 12 px ;
overflow : hidden ;
cursor : pointer ;
transition : all var ( --transition-speed ) var ( --transition-easing );
}
.certificate-card:hover {
border-color : var ( --color-border-hover );
transform : translateY ( -5 px );
}
.certificate-image-container {
width : 100 % ;
height : 180 px ;
overflow : hidden ;
background-color : var ( --color-bg-secondary );
}
.certificate-image-container img {
width : 100 % ;
height : 100 % ;
object-fit : cover ;
transition : transform var ( --transition-speed ) var ( --transition-easing );
}
.certificate-image-container:hover img {
transform : scale ( 1.04 );
}
Project Image Integration
Project images automatically open in the modal:
< div class = "project-info" >
< div class = "left-pnl img-pnl" >
< img src = "project-screenshot.jpg" alt = "Project Screenshot" >
</ div >
< div class = "right-pnl info-pnl" >
< h4 > Project Details </ h4 >
< p > Description... </ p >
</ div >
</ div >
Image Panel Styles
carousel.css (lines 212-221)
.img-pnl img {
width : 100 % ;
border-radius : 8 px ;
cursor : pointer ;
transition : all var ( --transition-speed ) var ( --transition-easing );
}
.img-pnl img :hover {
transform : scale ( 1.05 );
}
Initialization
The modal system initializes on page load:
modal-manager.js (lines 1-60)
document . addEventListener ( 'DOMContentLoaded' , () => {
const certModal = document . getElementById ( 'certificateModal' );
const certModalImg = document . getElementById ( 'certificateModalImage' );
const certModalTitle = document . getElementById ( 'certificateModalTitle' );
const certModalDate = document . getElementById ( 'certificateModalDate' );
const certModalClose = document . getElementById ( 'certificateModalCloseBtn' );
const imageModal = document . getElementById ( 'imageModal' );
const imageModalImg = document . getElementById ( 'modalImage' );
const imageModalClose = document . getElementById ( 'modalCloseBtn' );
// Event listeners setup...
});
Usage Examples
Creating a Certificate Card
< div class = "certificates-grid" >
< div class = "certificate-card" >
< div class = "certificate-image-container" >
< img src = "cert-thumbnail.jpg" alt = "Certificate" >
</ div >
< div class = "certificate-info" >
< p > Microsoft Azure Fundamentals </ p >
< span > March 2024 </ span >
</ div >
</ div >
</ div >
Creating a Project Image
< div class = "left-pnl img-pnl" >
< img src = "project.jpg" alt = "Project Screenshot" >
</ div >
Ensure images have appropriate alt attributes for accessibility.
Features Summary
Prevents background scrolling when modal is open by setting document.body.style.overflow = 'hidden'.
Press Escape to close any active modal.
Clicking the modal background (not the content) closes the modal.
Modals zoom in from 0.8 scale to 1.0 over 0.3 seconds.
Modal background fades in/out using CSS transitions.
Browser Compatibility
Modern browsers : Full support
CSS Grid : Required for certificate grid layout
CSS Animations : All modern browsers
classList API : All modern browsers
Accessibility Considerations
Focus Management
Consider adding focus trap within modal for keyboard navigation
ARIA Labels
Add role="dialog" and aria-modal="true" to modal containers
Screen Reader Support
Include aria-label on close buttons
Alt Text
Always provide descriptive alt text for images