Photo by Arif Riyanto on Unsplash
Modal Box Neo Brutalism Design Using HTML, CSS, and JavaScript
Create a modern, responsive modal design in the neo-brutalist style using HTML, CSS, and JavaScript, featuring bold colors, thick borders, and heavy shadows for a raw, unpolished aesthetic. This implementation focuses on striking visuals, accessible interactions, and maintainable code, ideal for dialogs or pop-ups in web interfaces.
Prerequisites
- Basic HTML, CSS, and JavaScript knowledge
- A code editor (e.g., VS Code)
Part 1: Modal Neo Brutalism Design
Step 1: HTML Structure (index.html)
Create a semantic HTML structure for the neo-brutalist modals with trigger buttons, overlay, modal content, and close/confirm buttons.
<div class="demo-container">
<button class="modal-trigger primary-btn" data-modal="modal-primary" aria-label="Open primary modal">Open Primary Modal</button>
<button class="modal-trigger secondary-btn" data-modal="modal-secondary" aria-label="Open secondary modal">Open Secondary Modal</button>
</div>
<div id="modal-primary" class="modal-overlay" role="dialog" aria-labelledby="modal-primary-title" aria-modal="true">
<div class="modal">
<button class="modal-close" aria-label="Close modal">×</button>
<h2 id="modal-primary-title" class="modal-title">Primary Modal</h2>
<p class="modal-content">This is a neo-brutalist modal with a bold, raw aesthetic. Use it for alerts, forms, or confirmations.</p>
<button class="modal-button" aria-label="Confirm action">Confirm</button>
</div>
</div>
<div id="modal-secondary" class="modal-overlay secondary" role="dialog" aria-labelledby="modal-secondary-title" aria-modal="true">
<div class="modal secondary">
<button class="modal-close" aria-label="Close modal">×</button>
<h2 id="modal-secondary-title" class="modal-title">Secondary Modal</h2>
<p class="modal-content">This modal uses an accent color for variety, maintaining the neo-brutalist style.</p>
<button class="modal-button" aria-label="Confirm action">Confirm</button>
</div>
</div>
Step 2: Core CSS Implementation (styles.css)
Style the modal and its elements with neo-brutalist aesthetics: bold colors, thick borders, heavy shadows, and a monospace font. Include Google Fonts for consistent typography.
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600&family=IBM+Plex+Mono:wght@600&display=swap" rel="stylesheet">
:root {
--gray-900: #111827;
--gray-700: #374151;
--gray-200: #e5e7eb;
--gray-100: #f3f4f6;
--white: #ffffff;
--primary-600: #2563eb;
--primary-500: #3b82f6;
--accent-500: #f59e0b;
--spacing-xs: 4px;
--spacing-sm: 8px;
--spacing-md: 12px;
--spacing-lg: 16px;
--radius-sm: 4px;
--radius-md: 6px;
--transition: 0.2s ease;
--shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
--shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
--shadow-neo: 6px 6px 0 var(--gray-900);
--font-sans: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
--font-mono: 'IBM Plex Mono', monospace;
}
.modal-trigger {
padding: var(--spacing-md);
font-family: var(--font-mono);
font-size: 0.9375rem;
font-weight: 600;
text-transform: uppercase;
color: var(--gray-900);
background: var(--primary-500);
border: 3px solid var(--gray-900);
border-radius: var(--radius-sm);
box-shadow: var(--shadow-neo);
transition: transform var(--transition), box-shadow var(--transition);
cursor: pointer;
}
.secondary-btn{
background: var(--accent-500);
}
.modal-trigger:hover {
transform: translate(2px, 2px);
box-shadow: 2px 2px 0 var(--gray-900);
}
.modal-trigger:focus {
outline: none;
box-shadow: 2px 2px 0 var(--accent-500), 0 0 0 3px var(--primary-600);
}
.modal-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
display: flex;
justify-content: center;
align-items: center;
z-index: 1000;
opacity: 0;
visibility: hidden;
transition: opacity var(--transition), visibility var(--transition);
}
.modal-overlay.active {
opacity: 1;
visibility: visible;
}
.modal {
background: var(--white);
border: 4px solid var(--gray-900);
border-radius: var(--radius-md);
box-shadow: var(--shadow-neo);
width: 100%;
max-width: 400px;
padding: var(--spacing-lg);
font-family: var(--font-mono);
transform: translateY(20px);
transition: transform var(--transition);
position: relative;
}
.modal-overlay.active .modal {
transform: translateY(0);
}
.modal.secondary {
border-color: var(--accent-500);
}
.modal-title {
font-size: 1.25rem;
font-weight: 600;
text-transform: uppercase;
color: var(--gray-900);
margin-bottom: var(--spacing-md);
}
.modal-content {
font-size: 0.9375rem;
color: var(--gray-700);
margin-bottom: var(--spacing-lg);
}
.modal-button {
padding: var(--spacing-sm) var(--spacing-md);
font-family: var(--font-mono);
font-size: 0.875rem;
font-weight: 600;
text-transform: uppercase;
color: var(--gray-900);
background: var(--primary-500);
border: 3px solid var(--gray-900);
border-radius: var(--radius-sm);
box-shadow: var(--shadow-neo);
transition: transform var(--transition), box-shadow var(--transition);
cursor: pointer;
width: 100%;
text-align: center;
}
.modal-button:hover {
transform: translate(2px, 2px);
box-shadow: 2px 2px 0 var(--gray-900);
}
.modal-button:focus {
outline: none;
box-shadow: 2px 2px 0 var(--accent-500), 0 0 0 3px var(--primary-600);
}
.modal.secondary .modal-button {
background: var(--accent-500);
}
.modal-close {
position: absolute;
top: var(--spacing-sm);
right: var(--spacing-sm);
background: none;
border: none;
font-size: 1.25rem;
color: var(--gray-900);
cursor: pointer;
padding: var(--spacing-xs);
}
.modal-close:hover,
.modal-close:focus {
color: var(--accent-500);
outline: none;
}
@media (max-width: 640px) {
.modal {
max-width: 90%;
padding: var(--spacing-md);
}
.modal-title {
font-size: 1.125rem;
}
.modal-content {
font-size: 0.875rem;
}
.modal-button {
font-size: 0.75rem;
}
}
Step 3: JavaScript Implementation (script.js)
Add JavaScript to manage modal interactions, including opening, closing, focus trapping, and keyboard accessibility.
document.addEventListener('DOMContentLoaded', () => {
const triggers = document.querySelectorAll('.modal-trigger');
const modals = document.querySelectorAll('.modal-overlay');
const closeButtons = document.querySelectorAll('.modal-close');
const confirmButtons = document.querySelectorAll('.modal-button');
// Open modal
triggers.forEach(trigger => {
trigger.addEventListener('click', () => {
const modalId = trigger.getAttribute('data-modal');
const modal = document.getElementById(modalId);
modal.classList.add('active');
trapFocus(modal);
modal.querySelector('.modal-close').focus();
});
});
// Close modal
closeButtons.forEach(button => {
button.addEventListener('click', () => {
const modal = button.closest('.modal-overlay');
modal.classList.remove('active');
});
});
// Confirm action (demo)
confirmButtons.forEach(button => {
button.addEventListener('click', () => {
const modal = button.closest('.modal-overlay');
modal.classList.remove('active');
console.log('Confirm action triggered');
});
});
// Close on overlay click
modals.forEach(modal => {
modal.addEventListener('click', (e) => {
if (e.target === modal) {
modal.classList.remove('active');
}
});
});
// Close on Escape key
document.addEventListener('keydown', (e) => {
if (e.code === 'Escape') {
modals.forEach(modal => modal.classList.remove('active'));
}
});
// Focus trapping
function trapFocus(modal) {
const focusableElements = modal.querySelectorAll('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])');
const firstElement = focusableElements[0];
const lastElement = focusableElements[focusableElements.length - 1];
modal.addEventListener('keydown', (e) => {
if (e.code === 'Tab') {
if (e.shiftKey) {
if (document.activeElement === firstElement) {
e.preventDefault();
lastElement.focus();
}
} else {
if (document.activeElement === lastElement) {
e.preventDefault();
firstElement.focus();
}
}
}
});
}
});
Explanation
- Layout: The modal is contained within an overlay (`.modal-overlay`) that centers it on the screen. The modal (`.modal`) includes a close button, title, content, and confirm button, styled with a white background, thick borders, and heavy shadows.
- Styling: The modal uses a white background with thick black (
4px) or orange (--accent-500for secondary) borders, offset shadows (--shadow-neo), and a monospace font. Trigger and confirm buttons use blue (--primary-500) or orange backgrounds with matching borders and shadows. The close button is minimal but bold. - Interactivity: JavaScript handles opening/closing modals via trigger buttons, close buttons, overlay clicks, or the Escape key. It logs confirm actions for demo purposes and traps focus within the modal for accessibility.
- Visual Feedback: Opening a modal fades in the overlay and slides the modal up (
transform: translateY(0)). Buttons shift on hover/focus (translate(2px, 2px)) with reduced shadows; focus states add a dual shadow (accent and primary). - Responsiveness: A media query (
@media (max-width: 640px)) adjusts the modal width to 90%, reduces padding, and scales down font sizes for mobile usability, maintaining the neo-brutalist aesthetic. - Accessibility:
role="dialog",aria-labelledby,aria-modal="true", andaria-labelensure screen reader compatibility. Keyboard support (Tab, Escape) and focus trapping keep interactions accessible. High contrast ratios (4.5:1 minimum) and bold typography enhance usability.
Accessibility Features
- Use
role="dialog",aria-labelledby,aria-modal, andaria-labelfor semantic structure - Implement focus trapping to keep keyboard navigation within the modal
- Support keyboard interactions with Tab, Escape, and Enter keys
- Ensure high contrast ratios for text, borders, and backgrounds (4.5:1 minimum)
- Use bold, readable typography (monospace for neo-brutalism)
Golden Rules
- Use CSS variables for consistent theming
- Implement bold borders and heavy shadows for neo-brutalist style
- Ensure accessibility with ARIA, keyboard support, and focus management
- Optimize for mobile with responsive design
Conclusion
A professional neo-brutalist modal should embrace raw, bold aesthetics, provide clear user feedback, remain responsive, and follow accessibility guidelines. This solution uses HTML, CSS, and JavaScript for striking modals with primary and secondary variants, featuring heavy shadows, thick borders, and a monospace font. Experiment with colors, shadow offsets, or add features like dynamic content or form submissions. Test across devices and browsers for usability. Feel free to leave comments with any questions or suggestions!
Comments
Post a Comment