Photo by Gustas Brazaitis on Unsplash
Select Option Neo Brutalism Design Using HTML, CSS, and JavaScript
Create a modern, responsive select dropdown 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 form elements requiring single-selection options in web interfaces.
Prerequisites
- Basic HTML, CSS, and JavaScript knowledge
- A code editor (e.g., VS Code)
Part 1: Select Neo Brutalism Design
Step 1: HTML Structure (index.html)
Create a semantic HTML structure for the neo-brutalist select dropdowns with a container, hidden input, toggle button, and dropdown options.
<div class="select-container" role="group" aria-label="Neo-brutalist select dropdowns">
<div class="select-wrapper">
<label for="select-primary" class="visually-hidden">Primary select</label>
<input type="text" id="select-primary" class="select-input" readonly aria-hidden="true">
<div class="select-toggle" role="combobox" aria-expanded="false" aria-controls="select-primary-options" tabindex="0">
<span class="select-value">Option 1</span>
</div>
<div id="select-primary-options" class="select-dropdown" role="listbox">
<div class="select-option" role="option" data-value="Option 1" aria-selected="true">Option 1</div>
<div class="select-option" role="option" data-value="Option 2">Option 2</div>
<div class="select-option" role="option" data-value="Option 3">Option 3</div>
</div>
</div>
<div class="select-wrapper secondary">
<label for="select-secondary" class="visually-hidden">Secondary select</label>
<input type="text" id="select-secondary" class="select-input" readonly aria-hidden="true">
<div class="select-toggle" role="combobox" aria-expanded="false" aria-controls="select-secondary-options" tabindex="0">
<span class="select-value">Option A</span>
</div>
<div id="select-secondary-options" class="select-dropdown" role="listbox">
<div class="select-option" role="option" data-value="Option A" aria-selected="true">Option A</div>
<div class="select-option" role="option" data-value="Option B">Option B</div>
<div class="select-option" role="option" data-value="Option C">Option C</div>
</div>
</div>
</div>
Step 2: Core CSS Implementation (styles.css)
Style the select dropdown 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: 4px 4px 0 var(--gray-900);
--font-sans: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
--font-mono: 'IBM Plex Mono', monospace;
}
.select-container {
display: flex;
flex-direction: column;
gap: var(--spacing-md);
background: var(--white);
padding: var(--spacing-lg);
border-radius: var(--radius-md);
box-shadow: var(--shadow-md);
width: 100%;
max-width: 400px;
}
.select-wrapper {
position: relative;
width: 100%;
}
.select-input {
position: absolute;
opacity: 0;
width: 0;
height: 0;
}
.select-toggle {
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
padding: var(--spacing-sm) var(--spacing-md);
font-family: var(--font-mono);
font-size: 0.9375rem;
font-weight: 600;
text-transform: uppercase;
color: var(--gray-900);
background: var(--white);
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;
box-sizing: border-box;
}
.select-toggle::after {
content: "\25BC";
font-size: 0.75rem;
color: var(--gray-900);
}
.select-toggle:hover {
transform: translate(2px, 2px);
box-shadow: 2px 2px 0 var(--gray-900);
}
.select-toggle:focus,
.select-input:focus ~ .select-toggle {
outline: none;
box-shadow: 2px 2px 0 var(--accent-500), 0 0 0 3px var(--primary-600);
}
.select-wrapper.secondary .select-toggle {
border-color: var(--accent-500);
}
.select-wrapper.secondary .select-toggle:focus,
.select-wrapper.secondary .select-input:focus ~ .select-toggle {
box-shadow: 2px 2px 0 var(--primary-500), 0 0 0 3px var(--accent-500);
}
.select-dropdown {
position: absolute;
top: calc(100% + var(--spacing-xs));
left: 0;
width: 100%;
background: var(--white);
border: 3px solid var(--gray-900);
border-radius: var(--radius-sm);
box-shadow: var(--shadow-neo);
max-height: 150px;
overflow-y: auto;
display: none;
z-index: 1000;
}
.select-wrapper.active .select-dropdown {
display: block;
}
.select-option {
padding: var(--spacing-sm) var(--spacing-md);
font-family: var(--font-mono);
font-size: 0.9375rem;
font-weight: 600;
text-transform: uppercase;
color: var(--gray-900);
cursor: pointer;
transition: background var(--transition);
}
.select-option:hover,
.select-option:focus {
background: var(--primary-500);
color: var(--white);
outline: none;
}
.select-wrapper.secondary .select-option:hover,
.select-wrapper.secondary .select-option:focus {
background: var(--accent-500);
}
.select-option[aria-selected="true"] {
background: var(--gray-200);
}
@media (max-width: 640px) {
.select-container {
padding: var(--spacing-md);
gap: var(--spacing-sm);
}
.select-toggle {
font-size: 0.875rem;
padding: var(--spacing-xs) var(--spacing-sm);
}
.select-option {
font-size: 0.875rem;
padding: var(--spacing-xs) var(--spacing-sm);
}
}
/* Visually hidden class for accessibility */
.visually-hidden {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
border: 0;
}
Step 3: JavaScript Implementation (script.js)
Add JavaScript to manage select dropdown interactions, including toggling, option selection, and keyboard accessibility.
document.addEventListener('DOMContentLoaded', () => {
const selectWrappers = document.querySelectorAll('.select-wrapper');
selectWrappers.forEach(wrapper => {
const toggle = wrapper.querySelector('.select-toggle');
const dropdown = wrapper.querySelector('.select-dropdown');
const options = wrapper.querySelectorAll('.select-option');
const input = wrapper.querySelector('.select-input');
const valueSpan = wrapper.querySelector('.select-value');
// Toggle dropdown
function toggleDropdown() {
const isActive = wrapper.classList.contains('active');
document.querySelectorAll('.select-wrapper').forEach(w => w.classList.remove('active'));
if (!isActive) {
wrapper.classList.add('active');
toggle.setAttribute('aria-expanded', 'true');
}
}
// Close dropdown
function closeDropdown() {
wrapper.classList.remove('active');
toggle.setAttribute('aria-expanded', 'false');
}
// Select option
function selectOption(option) {
const value = option.getAttribute('data-value');
options.forEach(opt => opt.setAttribute('aria-selected', 'false'));
option.setAttribute('aria-selected', 'true');
valueSpan.textContent = value;
input.value = value;
closeDropdown();
console.log(`Selected: ${value}`);
}
// Click to toggle
toggle.addEventListener('click', toggleDropdown);
// Click to select
options.forEach(option => {
option.addEventListener('click', () => selectOption(option));
});
// Keyboard support
toggle.addEventListener('keydown', (e) => {
if (e.code === 'Enter' || e.code === 'Space') {
e.preventDefault();
toggleDropdown();
}
});
options.forEach((option, index) => {
option.setAttribute('tabindex', '0');
option.addEventListener('keydown', (e) => {
if (e.code === 'Enter' || e.code === 'Space') {
e.preventDefault();
selectOption(option);
} else if (e.code === 'ArrowDown') {
e.preventDefault();
const next = options[index + 1] || options[0];
next.focus();
} else if (e.code === 'ArrowUp') {
e.preventDefault();
const prev = options[index - 1] || options[options.length - 1];
prev.focus();
} else if (e.code === 'Escape') {
closeDropdown();
toggle.focus();
}
});
});
// Close on outside click
document.addEventListener('click', (e) => {
if (!wrapper.contains(e.target)) {
closeDropdown();
}
});
});
});
Explanation
- Layout: Select dropdowns are housed in a flexbox container with a white background, shadow, and rounded corners, mimicking a card. Each select wrapper includes a hidden input, toggle button (`.select-toggle`), and dropdown (`.select-dropdown`) with options (`.select-option`), styled for neo-brutalist clarity.
- Styling: The toggle uses a white background with thick black (
3px) or orange (--accent-500for secondary) borders, offset shadows (--shadow-neo), and a monospace font. The dropdown mirrors this style, with options highlighting in blue (--primary-500) or orange on hover/focus. A downward arrow (`::after`) indicates interactivity. - Interactivity: JavaScript toggles the dropdown on click or Enter/Space, selects options on click or keyboard, and closes on outside click or Escape. It updates the toggle text and hidden input, logging selections for demo purposes.
- Visual Feedback: Toggling shifts the button (
translate(2px, 2px)) with a reduced shadow; focus adds a dual shadow (accent and primary). Options highlight on hover/focus, and the selected option has a gray background (--gray-200). - Responsiveness: A media query (
@media (max-width: 640px)) reduces container padding, toggle, and option sizes for mobile usability, maintaining the neo-brutalist aesthetic. - Accessibility:
role="group",role="combobox",role="listbox",role="option", and ARIA attributes (`aria-expanded`, `aria-controls`, `aria-selected`) ensure screen reader compatibility. Keyboard support (Enter, Space, Arrow keys, Escape) and a visually hidden label enhance usability. High contrast ratios (4.5:1 minimum) and bold typography ensure readability.
Accessibility Features
- Use
role="combobox",role="listbox",aria-expanded", andaria-selectedfor semantic structure - Include visually hidden labels for screen readers
- Support keyboard navigation with Enter, Space, Arrow keys, and Escape
- 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 and keyboard support
- Optimize for mobile with responsive design
Conclusion
A professional neo-brutalist select dropdown should embrace raw, bold aesthetics, provide clear user feedback, remain responsive, and follow accessibility guidelines. This solution uses HTML, CSS, and JavaScript for striking select dropdowns with primary and secondary variants, featuring heavy shadows, thick borders, and a monospace font. Experiment with colors, shadow offsets, or add features like dynamic options or form validation. Test across devices and browsers for usability. Feel free to leave comments with any questions or suggestions!
Comments
Post a Comment