Styling Buttons and Select Dropdowns
Buttons and select dropdowns require a different approach from text inputs. Buttons need a clean reset before you build their appearance back up. Selects have a hard ceiling on what CSS can control — the visible box is styleable, but the dropdown list itself is not. This lesson covers both.
Styling buttons
Section titled “Styling buttons”The browser button default
Section titled “The browser button default”Before you apply any CSS, a <button> element has:
- A gray background from the OS theme
- A border (style varies by browser)
- The browser’s default font (not your page font)
cursor: default— the arrow cursor, not the pointer
The .btn class in the STO stylesheet will handle the visual appearance, but first you need a clean foundation.
The button reset
Section titled “The button reset”button { cursor: pointer; border: none; background: none; font: inherit; padding: 0;}This removes all browser defaults and gives you a blank canvas. Each property matters:
cursor: pointer— changes the cursor to the hand icon, signaling the element is clickableborder: none— removes the browser’s default borderbackground: none— removes the default gray backgroundfont: inherit— ensures the button uses your page font (same as inputs)padding: 0— removes default padding so your custom padding is the only padding
Building the STO button
Section titled “Building the STO button”The STO site uses a .btn base class with .btn-primary for the green filled style:
.btn { display: inline-block; padding: 0.75rem 1.75rem; border-radius: 4px; font-weight: 700; font-size: 1rem; text-decoration: none; cursor: pointer; border: none; transition: transform 150ms ease, box-shadow 150ms ease;}
.btn-primary { background-color: #2c4a1e; color: #ffffff;}
.btn-primary:hover { background-color: #3a5c26; color: #ffffff;}display: inline-block allows the button to have padding and behave as a block for sizing while still flowing inline with text. This is also how <a> elements used as buttons behave — both <button> and <a class="btn"> receive the same visual treatment.
Button states
Section titled “Button states”Three interactive states matter for buttons:
.btn-primary:hover { background-color: #3a5c26; transform: translateY(-2px); box-shadow: 0 4px 12px rgba(0, 0, 0, 0.12);}
.btn-primary:active { transform: translateY(0); box-shadow: none;}
.btn:disabled,.btn[disabled] { opacity: 0.5; cursor: not-allowed; pointer-events: none;}:hover— lifts the button and deepens the color, signaling interactivity:active— returns the button to its resting position while pressed, giving a tactile “press” feel:disabled— reduces opacity and changes the cursor tonot-allowed;pointer-events: noneprevents click events from reaching the button
Focus state
Section titled “Focus state”Buttons need a focus ring just like inputs:
.btn:focus { outline: none; box-shadow: 0 0 0 3px rgba(44, 74, 30, 0.3);}Keyboard users tab to buttons — the focus ring must be visible.
Styling select dropdowns
Section titled “Styling select dropdowns”What CSS can control
Section titled “What CSS can control”The <select> element has two parts:
- The visible box — the closed state showing the currently selected option; fully styleable
- The dropdown list — the options that appear when clicked; rendered by the OS and not styleable with CSS
You can make the visible box match your design perfectly. The open dropdown will always look like an OS-native list.
Applying input styles to select
Section titled “Applying input styles to select”The select box takes the same padding, border, and background as inputs:
.form-group select { width: 100%; padding: 0.75rem 1rem; border: 1px solid #d8d2c8; border-radius: 4px; background-color: #ffffff; color: #1a1a1a; font: inherit; font-size: 1rem; transition: border-color 150ms ease, box-shadow 150ms ease;}This makes it visually consistent with the text inputs from Lesson 02.
Replacing the native arrow
Section titled “Replacing the native arrow”The browser’s native dropdown arrow is part of the OS rendering. To replace it, first strip the native appearance with appearance: none, then add a custom arrow as a background image:
.form-group select { appearance: none; background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1l5 5 5-5' stroke='%235a5a5a' stroke-width='1.5' fill='none' stroke-linecap='round'/%3E%3C/svg%3E"); background-repeat: no-repeat; background-position: right 1rem center; padding-right: 2.5rem;}The data:image/svg+xml value is an inline SVG encoded directly in the CSS — a small chevron arrow in #5a5a5a (mid-gray). background-position: right 1rem center places it 1rem from the right edge, vertically centered. padding-right: 2.5rem prevents the user’s selected text from running underneath the arrow.
appearance: none is well-supported in all modern browsers. You can add -webkit-appearance: none for older Safari compatibility.
Checkboxes and radio buttons
Section titled “Checkboxes and radio buttons”Checkbox and radio inputs are the most restricted form elements. appearance: none removes their native rendering, leaving a plain box, but rebuilding a fully accessible custom checkbox or radio requires additional HTML and CSS beyond this course’s scope.
For the STO contact form, the exercises use text inputs, email inputs, a select, a textarea, and a submit button — all of which are fully styleable with the techniques in this module.
Exercise
Section titled “Exercise”Style the STO form button and select:
- Add the button reset and
.btn/.btn-primaryrules tostyle.css:
button { cursor: pointer; border: none; background: none; font: inherit; padding: 0;}
.btn { display: inline-block; padding: 0.75rem 1.75rem; border-radius: 4px; font-weight: 700; font-size: 1rem; text-decoration: none; cursor: pointer; border: none; transition: transform 150ms ease, box-shadow 150ms ease;}
.btn-primary { background-color: #2c4a1e; color: #ffffff;}
.btn-primary:hover { background-color: #3a5c26; transform: translateY(-2px); box-shadow: 0 4px 12px rgba(0, 0, 0, 0.12);}
.btn-primary:active { transform: translateY(0); box-shadow: none;}-
Open
contact.html. Hover the “Send Message” button — it should shift green, lift slightly, and animate smoothly. Click and hold — it should return to rest while pressed. -
Add the select styles. Open the page and click the “Interested in” dropdown — the visible box should match the text inputs. The dropdown list itself will still look OS-native, which is expected.
-
Tab to the submit button with your keyboard. Confirm the focus ring appears.
-
In DevTools, add the
disabledattribute to the button: in the Elements panel, double-click the<button>element and typedisabled. The button should fade to 50% opacity and the cursor should change tonot-allowed.
- The button reset (
cursor: pointer; border: none; background: none; font: inherit; padding: 0) strips browser defaults before you build a custom appearance. cursor: pointersignals clickability; always add it to buttons.:hover,:active, and:disabledare the three button states to style.:activegives tactile press feedback;:disabledmakes unavailable buttons visually clear.- Buttons and
<a>elements used as buttons receive the same.btnclass —display: inline-blockmakes padding and sizing behave consistently for both. appearance: noneon<select>removes the native dropdown arrow; add a custom SVG arrow viabackground-imageand adjustpadding-rightto prevent text overlap.- The select dropdown list (the open state) is OS-rendered and cannot be styled with CSS.
Lesson 04 uses Flexbox to control how labels, inputs, and buttons are arranged in the form layout.