CSS Tutorial
CSS Project: Button Set
Primary, ghost, and danger buttons with hover, focus, and disabled states that stay readable.
What you will build
A small action row for Harbor studio: a primary button, a ghost button, a danger button, and a disabled twin. Each state must stay readable. Hover darkens. Focus draws a ring. Disabled looks quiet and does not look clickable.
Buttons are not only color. Padding, radius, and type size decide whether a tap target feels like a control or a label. You will keep the markup as real <button> elements, then paint the set in CSS.
Open an example with Try it in CSS. That loads /css/try — a live page preview.
Properties you will use
| Property | Job on this page |
|---|---|
:hover | Darkens the fill or the border on pointer |
:focus-visible | Draws a ring without hiding the native outline idea |
:disabled | Lowers contrast and blocks pointer events |
border-radius | Rounds every button the same amount |
transition | Softens the color change so hover is not a flash |
Never remove outline and leave nothing in its place. A focus ring is part of the set, not an extra flourish.
Build in slices
First paint the three live buttons. Shared padding and radius go on button. Variants only change color.
Example
<style>
body {
font-family: system-ui, sans-serif;
background: #f0f9ff;
color: #0c4a6e;
margin: 2rem;
}
.actions { display: flex; flex-wrap: wrap; gap: 0.7rem; }
button {
font: inherit;
font-weight: 700;
padding: 0.65rem 1.15rem;
border-radius: 0.55rem;
cursor: pointer;
border: 2px solid transparent;
}
.primary { background: #0284c7; color: #fff; }
.ghost { background: #fff; color: #0284c7; border-color: #0284c7; }
.danger { background: #fff; color: #b91c1c; border-color: #b91c1c; }
</style>
<div class="actions">
<button class="primary" type="button">Book a table</button>
<button class="ghost" type="button">View menu</button>
<button class="danger" type="button">Cancel booking</button>
</div>Then add hover, focus, and disabled. The disabled button uses the primary colors at half strength andcursor: not-allowed.
Example
<style>
button { transition: background 0.15s ease, color 0.15s ease, box-shadow 0.15s ease; }
.primary:hover { background: #0369a1; }
.ghost:hover { background: #e0f2fe; }
.danger:hover { background: #fef2f2; }
button:focus-visible {
outline: 3px solid #38bdf8;
outline-offset: 3px;
}
button:disabled {
background: #bae6fd;
color: #0c4a6e;
border-color: transparent;
cursor: not-allowed;
opacity: 0.7;
}
</style>
<button class="primary" type="button" disabled>Sold out</button>Tab through the row at /css/try. If you cannot see which button has focus, the ring is too faint or missing.
Complete page
Harbor studio uses this set on booking pages. The complete document includes a short heading, the four controls, and a note about the sold-out brunch sitting. Paste the whole file into the CSS preview.
Example
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Harbor studio — buttons</title>
<style>
* { box-sizing: border-box; }
body {
margin: 0;
min-height: 100vh;
display: grid;
place-items: center;
background: #e0f2fe;
font-family: system-ui, sans-serif;
color: #0c4a6e;
}
.panel {
width: min(36rem, 92vw);
background: #fff;
border-radius: 1.1rem;
padding: 1.6rem 1.5rem 1.7rem;
box-shadow: 0 14px 30px rgba(12, 74, 110, 0.12);
}
h1 { margin: 0 0 0.35rem; font-size: 1.45rem; }
.lead { margin: 0 0 1.2rem; color: #075985; }
.actions {
display: flex;
flex-wrap: wrap;
gap: 0.7rem;
}
button {
font: inherit;
font-weight: 700;
padding: 0.65rem 1.15rem;
border-radius: 0.55rem;
cursor: pointer;
border: 2px solid transparent;
transition: background 0.15s ease, color 0.15s ease, box-shadow 0.15s ease;
}
.primary { background: #0284c7; color: #fff; }
.primary:hover:not(:disabled) { background: #0369a1; }
.ghost { background: #fff; color: #0284c7; border-color: #0284c7; }
.ghost:hover { background: #e0f2fe; }
.danger { background: #fff; color: #b91c1c; border-color: #b91c1c; }
.danger:hover { background: #fef2f2; }
button:focus-visible {
outline: 3px solid #38bdf8;
outline-offset: 3px;
}
button:disabled {
background: #bae6fd;
color: #0c4a6e;
border-color: transparent;
cursor: not-allowed;
opacity: 0.75;
}
</style>
</head>
<body>
<section class="panel">
<h1>Saturday brunch at Harbor studio</h1>
<p class="lead">Book a window table, read the board, or cancel. Sold-out sittings stay visible but quiet.</p>
<div class="actions">
<button class="primary" type="button">Book a table</button>
<button class="ghost" type="button">View menu</button>
<button class="danger" type="button">Cancel booking</button>
<button class="primary" type="button" disabled>9:30 sitting sold out</button>
</div>
</section>
</body>
</html>Practice tasks
- Add a fourth live variant named quiet: pale sky fill, navy text, no border. Include hover and focus.
- Widen padding to
0.85rem 1.4remand raise radius to999pxso the set reads as pills. - Disable the danger button as well. Confirm both disabled controls skip the hover darken at /css/try.