Radio Button Neo Brutalism Design Using HTML, CSS, and JavaScript

Radio Button Neo Brutalism Design Using HTML, CSS, and JavaScript

Radio Button Neo Brutalism Design Using HTML, CSS, and JavaScript

Create a modern, responsive radio button 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: Radio Button Neo Brutalism Design

Step 1: HTML Structure (index.html)

Create a semantic HTML structure for the neo-brutalist radio buttons with a container, input, custom radio button, and label, wrapped in a label for accessibility.

<div class="radio-container" role="radiogroup" aria-label="Neo-brutalist radio buttons">
    <label class="radio-wrapper">
        <input type="radio" name="radio-group" id="radio-primary" class="radio-input" aria-label="Primary radio button">
        <span class="radio-custom"></span>
        <span class="radio-label">Primary Option</span>
    </label>
    <label class="radio-wrapper secondary">
        <input type="radio" name="radio-group" id="radio-secondary" class="radio-input" aria-label="Secondary radio button">
        <span class="radio-custom"></span>
        <span class="radio-label">Secondary Option</span>
    </label>
</div>

Step 2: Core CSS Implementation (styles.css)

Style the radio buttons and labels with neo-brutalist aesthetics: bold colors, thick borders, heavy shadows, and a monospace font. Include Google Fonts for consistent typography.


: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;
}

.radio-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;
}

.radio-wrapper {
    display: flex;
    align-items: center;
    gap: var(--spacing-sm);
    cursor: pointer;
    position: relative;
}

.radio-input {
    position: absolute;
    opacity: 0;
    width: 20px;
    height: 20px;
    cursor: pointer;
    z-index: 1;
}

.radio-custom {
    display: inline-block;
    width: 20px;
    height: 20px;
    background: var(--white);
    border: 3px solid var(--gray-900);
    border-radius: 50%;
    box-shadow: var(--shadow-neo);
    transition: all var(--transition);
    position: relative;
    flex-shrink: 0;
}

.radio-custom::after {
    content: '';
    position: absolute;
    display: none;
    left: 50%;
    top: 50%;
    width: 12px;
    height: 12px;
    transform: translate(-50%, -50%);
    background-color: var(--white);
    border-radius: 50%;
}

.radio-input:checked ~ .radio-custom {
    background: var(--primary-500);
    transform: translate(2px, 2px);
    box-shadow: 2px 2px 0 var(--gray-900);
}

.radio-input:checked ~ .radio-custom::after {
    display: block;
}

.radio-wrapper.secondary .radio-input:checked ~ .radio-custom {
    background: var(--accent-500);
}

.radio-input:focus ~ .radio-custom {
    box-shadow: 2px 2px 0 var(--accent-500), 0 0 0 3px var(--primary-600);
}

.radio-wrapper.secondary .radio-input:focus ~ .radio-custom {
    box-shadow: 2px 2px 0 var(--primary-500), 0 0 0 3px var(--accent-500);
}

.radio-label {
    font-family: var(--font-mono);
    font-size: 0.9375rem;
    font-weight: 600;
    text-transform: uppercase;
    color: var(--gray-900);
    cursor: pointer;
    user-select: none;
    margin-left: var(--spacing-sm);
}

@media (max-width: 640px) {
    .radio-container {
        padding: var(--spacing-md);
        gap: var(--spacing-sm);
    }

    .radio-custom {
        width: 16px;
        height: 16px;
    }

    .radio-input {
        width: 16px;
        height: 16px;
    }

    .radio-custom::after {
        width: 10px;
        height: 10px;
    }

    .radio-label {
        font-size: 0.875rem;
    }
}

Step 3: JavaScript Implementation (script.js)

Add JavaScript to enhance accessibility by supporting keyboard interactions and logging state changes for demo purposes.

document.addEventListener('DOMContentLoaded', () => {
    const radios = document.querySelectorAll('.radio-input');

    radios.forEach(radio => {
        // Add keyboard support for Space key to select radio
        radio.addEventListener('keydown', (e) => {
            if (e.code === 'Space') {
                e.preventDefault();
                radio.checked = true;
                radio.dispatchEvent(new Event('change'));
            }
        });

        // Log change for demo purposes
        radio.addEventListener('change', () => {
            if (radio.checked) {
                console.log(`${radio.id} is selected`);
            }
        });
    });
});

Explanation

  • Layout: Radio buttons are housed in a flexbox container with a white background, shadow, and rounded corners, mimicking a card. Each radio wrapper is a label containing a hidden input, custom radio span, and label text, aligned horizontally for clarity.
  • Styling: Custom radio buttons use a white background with thick black borders (3px) or orange (--accent-500 for secondary), offset shadows (--shadow-neo), and a monospace font for labels. Checked states use blue (--primary-500) or orange backgrounds with a circular dot. Labels are uppercase and bold.
  • Interactivity: CSS handles checked and focus states with background changes, transforms, and shadows. JavaScript selects radio buttons with the Space key and logs changes for demo purposes, ensuring only one option is selected per group.
  • Visual Feedback: Checking shifts the custom radio (translate(2px, 2px)) with a reduced shadow and changes the background; focus adds a dual shadow (accent and primary). A circular dot (`::after`) appears on checked state.
  • Responsiveness: A media query (@media (max-width: 640px)) reduces container padding, radio size, and font sizes for mobile usability, maintaining the neo-brutalist aesthetic.
  • Accessibility: role="radiogroup" and aria-label on the container, plus aria-label on inputs, ensure screen reader compatibility. The label wrapper enhances clickability. Keyboard support (Tab, Space) and high contrast ratios (4.5:1 minimum) enhance usability. The monospace font is bold and readable.
UX Tip: Neo-brutalist radio buttons should feel bold and tactile with heavy shadows and stark contrasts. Ensure accessibility with clear labels, ARIA attributes, and keyboard support. In production, add form integration, validation, or dynamic logic and test readability across devices.

Accessibility Features

  • Use role="radiogroup" and aria-label for semantic structure
  • Wrap inputs in labels for enhanced clickability and accessibility
  • Ensure high contrast ratios for text, borders, and backgrounds (4.5:1 minimum)
  • Support keyboard navigation with Tab and Space keys
  • 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 radio button should embrace raw, bold aesthetics, provide clear user feedback, remain responsive, and follow accessibility guidelines. This solution uses HTML, CSS, and JavaScript for striking radio buttons with primary and secondary variants, featuring heavy shadows, a circular dot indicator, and a monospace font. Experiment with colors, shadow offsets, or add features like form validation or dynamic selections. Test across devices and browsers for usability. Feel free to leave comments with any questions or suggestions!

Comments