Chapter 13: Form Element Styling
Forms are the primary way users interact with your application. However, default browser styles for forms are often inconsistent and outdated. This chapter teaches you how to take full control over inputs, buttons, selects, and text areas to create a cohesive and professional user experience.
1. Resetting the Defaults
Most form elements come with "user-agent styles" (browser defaults) that include bulky borders, specific backgrounds, and strange internal spacing. The first step is often a "clean slate" using appearance: none to remove OS-specific styling.
button, input, select, textarea {
font-family: inherit; /* Use the site's font */
font-size: 100%; /* Prevent scaling issues on mobile */
margin: 0; /* Remove default margins */
box-sizing: border-box;
appearance: none; /* Removes default browser styling */
}
2. Styling Text Inputs and Textareas
Modern inputs should feel tactile and responsive to focus. A common technique is to use a subtle transition on the border color and a soft box-shadow to indicate focus.
.form-input {
width: 100%;
padding: 0.75rem 1rem;
border: 2px solid #e2e8f0;
border-radius: 0.75rem;
background-color: #fff;
color: #1e293b;
transition: all 0.2s ease;
}
.form-input:focus {
outline: none;
border-color: #3b82f6;
box-shadow: 0 0 0 4px rgba(59, 130, 246, 0.1);
}
.form-input::placeholder {
color: #94a3b8;
}
3. Customizing the Select Dropdown
The standard <select> is notoriously hard to style. The modern approach is to hide the default arrow using appearance: none and provide your own using a background image.
.custom-select {
appearance: none;
padding: 0.75rem 2.5rem 0.75rem 1rem;
background-color: #fff;
border: 2px solid #e2e8f0;
border-radius: 0.75rem;
/* Custom Arrow via Data URI or SVG file */
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 24 24' stroke='%2364748b'%3E%3Cpath stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M19 9l-7 7-7-7'%3E%3C/path%3E%3C/svg%3E");
background-repeat: no-repeat;
background-position: right 1rem center;
background-size: 1.25rem;
cursor: pointer;
}
4. Checkboxes and Radios: The "Hidden Input" Pattern
Browser defaults for checkboxes and radios are often small and cannot be easily themed. The solution is to hide the actual input and style a sibling element (usually a span or div) using the :checked pseudo-class.
Checkbox Implementation
/* 1. Hide the real input */
.checkbox-hidden {
position: absolute;
opacity: 0;
width: 0;
height: 0;
}
/* 2. Style the custom box */
.checkbox-box {
display: inline-block;
width: 1.5rem;
height: 1.5rem;
border: 2px solid #cbd5e1;
border-radius: 0.375rem;
background: #fff;
transition: all 0.2s;
}
/* 3. Style the 'Checked' state */
.checkbox-hidden:checked + .checkbox-box {
background-color: #3b82f6;
border-color: #3b82f6;
/* Use a checkmark background or pseudo-element */
}
Radio Implementation
.radio-circle {
border-radius: 50%; /* Radios are traditionally circles */
}
.radio-hidden:checked + .radio-circle {
box-shadow: inset 0 0 0 4px #fff; /* Creates the inner dot effect */
background-color: #3b82f6;
}
5. Styling Buttons
Buttons need clear hierarchy (primary vs. secondary) and obvious hover/active states.
.btn {
display: inline-flex;
align-items: center;
justify-content: center;
padding: 0.625rem 1.25rem;
font-weight: 600;
border-radius: 0.375rem;
cursor: pointer;
transition: all 0.2s;
}
.btn-primary {
background-color: #2563eb;
color: #fff;
border: none;
}
.btn-primary:hover {
background-color: #1d4ed8;
}
.btn-primary:active {
transform: scale(0.98);
}
6. Layout: Labeling and Accessibility
Always pair inputs with <label> tags. Use display: flex or grid to create clean form rows. A good practice is to stack labels above inputs on mobile but place them side-by-side on larger screens.
.form-group {
display: flex;
flex-direction: column;
gap: 0.5rem;
margin-bottom: 1.5rem;
}
.form-label {
font-size: 0.875rem;
font-weight: 600;
color: #475569;
text-transform: uppercase;
letter-spacing: 0.05em;
}
7. Debugging Checklist
- Are inputs too small on iOS? (Ensure
font-sizeis at least16pxto prevent auto-zoom). - Is the focus ring visible? (Never remove
outlinewithout providing a clear:focusstyle). - Do buttons have a
cursor: pointer? - Is the text contrast high enough for readability?
- Are labels properly linked to inputs using the
forandidattributes?