Styling Links and Buttons with CSS

Style links in every state, remove or restyle underlines, build accessible buttons with hover, focus and disabled states, and make link-styled buttons.

Lesson 24 of 49Beginner16 min read

Links and buttons are the parts of a page people actually click, so they deserve careful styling. A good link is easy to spot in a paragraph. A good button looks pressable, reacts when you hover, shows clearly when it has keyboard focus, and looks unavailable when it's disabled. Getting these details right makes a site feel polished, and getting them wrong makes it confusing and inaccessible.

Links have several states that you can style separately: unvisited, visited, hovered, focused and being clicked. Buttons share most of these states and add a disabled one. In this lesson you'll style each state with pseudo-classes, control underlines with the text-decoration family of properties, and build a reusable button style with primary, secondary and outline variations.

You'll also learn an important rule of thumb: use a link (<a>) when something takes you to another page, and a <button> when it does something on the current page. CSS can make either look like the other, but the HTML element should match what it does.

Underlines with text-decoration

PropertyExampleWhat it does
text-decorationnoneRemoves the underline
text-decoration-color#93c5fdUnderline colour
text-decoration-thickness2pxUnderline thickness
text-underline-offset4pxGap between text and underline

Links inside paragraphs should keep an underline, because colour alone isn't enough for people with colour blindness. You can still make it subtle and attractive with a lighter colour and an offset, then make it bolder on hover. In navigation bars and buttons, where the context makes it obvious, removing the underline is fine.

Building a button style

CSS
.btn {
  display: inline-block;
  padding: 10px 18px;
  border: 2px solid transparent;
  border-radius: 8px;
  background: #16a34a;
  color: white;
  font: inherit;
  font-weight: 600;
  text-decoration: none;
  cursor: pointer;
}
  • display: inline-block lets a link styled as a button take padding properly.
  • A transparent border keeps solid and outline buttons the same size.
  • font: inherit stops buttons from using the browser's small system font.
  • cursor: pointer shows the hand cursor. Add :hover, :focus-visible, :active and :disabled states on top.

Focus styles for keyboard users

Many people navigate with the keyboard, pressing Tab to move between links and buttons. The focus ring shows where they are. Never remove it with outline: none unless you replace it. :focus-visible shows a ring for keyboard users but not for mouse clicks, which gives you the best of both:

CSS
.btn:focus-visible { outline: 3px solid #f59e0b; outline-offset: 2px; }

CSS Links and Buttons Syntax

Link and button states

a { color: #1d4ed8; text-decoration-thickness: 1px; text-underline-offset: 3px; }
a:hover { text-decoration-thickness: 2px; }
.btn { padding: 10px 18px; border-radius: 8px; cursor: pointer; }
.btn:hover { background: #15803d; }
.btn:focus-visible { outline: 3px solid #f59e0b; outline-offset: 2px; }
.btn:active { transform: translateY(1px); }
.btn:disabled { opacity: 0.5; cursor: not-allowed; }

CSS Links and Buttons Summary

  • Style link states in the order :link, :visited, :hover, :focus-visible, :active.
  • Keep underlines in body text; style them with text-decoration-color, thickness and text-underline-offset.
  • A button style needs padding, a border, a radius, font: inherit and cursor: pointer.
  • Never remove focus outlines without a replacement; :focus-visible targets keyboard users.
  • Use <a> for navigation and <button> for actions, then style either as needed.

CSS Links and Buttons Examples

Each example runs in the page. Edit the code and press Run to see what changes.

Example 1

Attractive text links

Hover the links, then press Tab to see the focus ring.

p { font: 18px/1.6 Georgia, serif; }

a {
  color: #1d4ed8;
  text-decoration-color: #93c5fd;
  text-decoration-thickness: 2px;
  text-underline-offset: 4px;
  transition: text-decoration-color 0.2s;
}

a:hover { text-decoration-color: currentColor; }
a:focus-visible { outline: 2px solid #1d4ed8; outline-offset: 3px; border-radius: 2px; }
ResultEdit the code, then press Run or Ctrl+Enter
Line-by-line explanation
  1. Lines 4 to 7A light blue underline set a little below the text.
  2. Line 8The underline colour fades smoothly on hover.
  3. Line 11On hover the underline turns the same colour as the text.
  4. Line 12Keyboard users get a clear outline.
Example 2

A button set with variations

.btn {
  display: inline-block;
  padding: 10px 18px;
  margin: 4px;
  border: 2px solid transparent;
  border-radius: 8px;
  background: #16a34a;
  color: white;
  font: 600 15px sans-serif;
  text-decoration: none;
  cursor: pointer;
  transition: background 0.2s, transform 0.1s;
}

.btn:hover { background: #15803d; }
.btn:active { transform: translateY(1px); }
.btn:focus-visible { outline: 3px solid #f59e0b; outline-offset: 2px; }

.secondary { background: #1f2937; }
.secondary:hover { background: #111827; }
.outline { background: transparent; color: #16a34a; border-color: #16a34a; }
.outline:hover { background: #f0fdf4; }

.btn:disabled { opacity: 0.5; cursor: not-allowed; }
ResultEdit the code, then press Run or Ctrl+Enter
Line-by-line explanation
  1. Lines 1 to 13One base class holds everything the buttons share.
  2. Lines 15 to 17Hover, press and keyboard focus states.
  3. Lines 19 to 22Variations only change colours.
  4. Line 24Disabled buttons look faded and show a not-allowed cursor.
Example 3

An icon button with a large tap area

.icon-btn {
  min-width: 44px;
  min-height: 44px;
  border: 0;
  border-radius: 50%;
  background: #f3f4f6;
  font-size: 18px;
  cursor: pointer;
}
.icon-btn:hover { background: #e5e7eb; }

.pill {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 10px 20px; border: 0; border-radius: 999px;
  background: #f97316; color: white; font: 600 15px sans-serif; cursor: pointer;
}
.pill span { font-size: 18px; }
ResultEdit the code, then press Run or Ctrl+Enter
Line-by-line explanation
  1. Lines 2 to 3A 44 by 44 pixel button is easy to tap.
  2. Line 13Flexbox lines up the text and the plus sign.
  3. Line 14A radius of 999px makes a pill shape at any size.

Common CSS Links and Buttons Mistakes

Removing the focus outline

Don't
button:focus { outline: none; }
Do
button:focus-visible { outline: 3px solid #f59e0b; outline-offset: 2px; }

Without a focus style, keyboard users can't see where they are on the page.

Hover before visited

Don't
a:hover { color: red; }
a:visited { color: purple; } /* hover never shows on visited links */
Do
a:visited { color: purple; }
a:hover { color: red; }

A div that pretends to be a button

Don't
/* <div class="btn" onclick="save()">Save</div> */
.btn { cursor: pointer; }
Do
/* <button class="btn" type="button" onclick="save()">Save</button> */
.btn { cursor: pointer; }

A <div> can't be reached with Tab or pressed with Enter. Style a real <button> instead.

CSS Links and Buttons in Real Projects

Buttons in design systems

Every design system, from GitHub's Primer to Google's Material, starts with a button component that has a few variants (primary, secondary, outline, danger), a few sizes, and hover, focus, active and disabled states. The colour contrast between button text and background is checked against accessibility guidelines (at least 4.5 to 1 for normal text), and tap targets meet minimum sizes for mobile.

CSS Links and Buttons Exercises

Answer each one and check it instantly. If you get stuck, open the hint before the answer.

Fill in the blankseasy

Remove the underline from navigation links.

nav a {
  : none;
}
Multiple choiceeasy

Which pseudo-class styles a link while the mouse is over it?

Multiple choicemedium

Which selector shows a focus ring for keyboard users but not mouse clicks?

Multiple choicemedium

Which property moves the underline further from the text?

Fix the bugmedium

The button should have the hand cursor, inherit the page font, and show a 3px solid outline on keyboard focus instead of none. Fix the CSS.

.page { font-family: Georgia, serif; }
.btn { cursor: default; padding: 10px 16px; }
.btn:focus-visible { outline: none; }
ResultEdit the code, then press Run or Ctrl+Enter
Checks
  • Pointer cursor
  • Inherits the page font
  • Focus ring is defined

CSS Links and Buttons Quiz

5 questions to check what you learned.

  1. Why keep underlines on links inside paragraphs?

    • They load faster
    • Browsers require them
    • Colour alone isn't enough for everyone to spot links
    • They help SEO
  2. What is the recommended order for link states?

    • hover, link, active, visited
    • active, hover, visited, link
    • Any order works
    • link, visited, hover, active
  3. Why give buttons a transparent border?

    • For accessibility
    • So solid and outline buttons stay the same size
    • It's required
    • To add a shadow
  4. When should you use <a> rather than <button>?

    • When it takes the user to another page or URL
    • When it's blue
    • When it submits a form
    • When it opens a menu
  5. Which property can :visited change?

    • color
    • font-size
    • background-image
    • display

CSS Links and Buttons Challenge

A call-to-action button

  1. Give .cta padding: 12px 24px, border-radius: 999px, background: rgb(124, 58, 237) and color: rgb(255, 255, 255)
  2. Remove its underline and give it cursor: pointer
  3. On :hover, change the background to rgb(109, 40, 217)
  4. Add a :focus-visible style with outline: 3px solid rgb(245, 158, 11)
.cta { display: inline-block; font: 600 16px sans-serif; }
ResultEdit the code, then press Run or Ctrl+Enter
Checks
  • Pill-shaped purple button
  • White text, no underline
  • Pointer cursor
  • Hover and focus states