Learning path · Level 2 · Tutorial 6

Style Accessible Hover, Focus, and Disabled States

Give links and form controls clear hover, keyboard-focus, and disabled appearances while preserving native behavior and visible focus.

  • Level: Beginner
  • Duration: 15 min
  • 7 steps
Start tutorial Back to the path
Style Accessible Hover, Focus, and Disabled States

What you will go through

  1. Recognize What Each State Communicates Distinguish the messages communicated by hover, focus, and disabled states, then create the local example used throughout this tutorial. 2 min
  2. Add Hover Feedback Without Making It Essential Add pointer-hover feedback that strengthens existing link and button affordances without making hover essential. 2 min
  3. Keep Keyboard Focus Clearly Visible Apply a clear focus indicator with `:focus-visible` while preserving native keyboard behavior. 3 min
  4. Use Real Disabled Behavior Style a native disabled button and distinguish its browser behavior from a merely dimmed control. 2 min
  5. Make Overlapping States Work Together Use source order and matching specificity to keep focus visible during hover and preserve the unavailable appearance of a disabled control. 2 min
  6. Check Contrast Across Interaction States Evaluate text, control, and focus-indicator contrast across interaction states, including the inactive-control exemption. 2 min
  7. Finish and Test the Accessible State Styles Complete the local stylesheet and verify hover, focus, disabled behavior, and relevant contrast evidence. 3 min

What you will learn

  • Style hover and keyboard-focus states so controls remain identifiable without relying only on color.
  • Create a visible focus outline that remains clear when hover and focus occur together.
  • Style a natively disabled control without confusing a dimmed appearance with disabled behavior.
  • Check links and controls with a pointer and keyboard, then verify relevant text and non-text contrast.

Before you start

  • Create Reusable Colors and Surfaces
  • Link Local Pages with Relative Paths
  • Build Accessible Forms with Native Validation

Step 1 of 7

Recognize What Each State Communicates

Distinguish the messages communicated by hover, focus, and disabled states, then create the local example used throughout this tutorial.

Read the signals

Three different messages

Interactive controls can communicate different things:

  • Hover state means a pointer is over an element.
  • Focus identifies the current interaction target, such as a control reached with the keyboard.
  • A disabled control is a supported control that is unavailable.

These are different conditions, even when each changes an element’s appearance. Before any interaction, a link, input, or button still needs a recognizable default appearance so people can identify its purpose.

Compare the interface states

Four views of a workshop signup interface: default controls, a hovered link, a focused input, and an unavailable button.

Default appearance identifies controls. A hover state signals pointer position, focus identifies the current target, and a disabled control signals unavailability.

Start with recognizable controls

Build the local example

Create a folder containing index.html and styles.css. This local starter page includes an underlined link, a labeled input, an enabled reset button, and an unavailable submit button. The browser’s native focus indicator is intentionally left intact for now.

index.html

Save this complete file as index.html.

html
<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Workshop signup</title>
    <link rel="stylesheet" href="styles.css">
  </head>
  <body>
    <main>
      <h1>Workshop signup</h1>
      <p><a class="info-link" href="#details">Workshop details</a></p>

      <form>
        <label for="full-name">Full name</label>
        <input class="field" id="full-name" name="full-name" type="text">
        <p>
          <button class="action" type="reset">Clear form</button>
          <button class="action" type="submit" disabled>Join workshop</button>
          <span>Registration is closed in this demo.</span>
        </p>
      </form>

      <section id="details">
        <h2>Workshop details</h2>
        <p>This local demo has no signup service.</p>
      </section>
    </main>
  </body>
</html>
Expected output
Browser result: a page titled “Workshop signup” with a Workshop details link, a Full name field, a Clear form button, a Join workshop button, its registration message, and a Workshop details section.

Open `index.html` in a browser after also saving `styles.css` in the same folder.

styles.css — base styles

Save this complete starter stylesheet as styles.css. It contains base styles only; later steps add interaction-state styles.

css
/* base styles */
:root {
  --surface: #ffffff;
  --text: #1f2937;
  --accent: #1d4ed8;
  --accent-hover: #1e40af;
  --focus: #111827;
  --border: #6b7280;
  --disabled-bg: #e5e7eb;
  --disabled-text: #4b5563;
}

* {
  box-sizing: border-box;
}

body {
  margin: 0;
  background-color: var(--surface);
  color: var(--text);
  font-family: system-ui, sans-serif;
  line-height: 1.5;
}

main {
  max-width: 40rem;
  margin: 0 auto;
  padding: 2rem;
}

form {
  max-width: 28rem;
}

label {
  display: block;
  margin-top: 1.5rem;
}

.info-link {
  color: var(--accent);
  font-size: 1rem;
  text-decoration: underline;
}

.field {
  display: block;
  width: 100%;
  margin: 0.5rem 0 1rem;
  padding: 0.75rem;
  border: 2px solid var(--border);
  border-radius: 0.25rem;
  background-color: var(--surface);
  color: var(--text);
  font: inherit;
  font-size: 1rem;
}

.action {
  margin-right: 0.5rem;
  padding: 0.75rem 1rem;
  border: 2px solid var(--accent);
  border-radius: 0.25rem;
  background-color: var(--accent);
  color: var(--surface);
  cursor: pointer;
  font: inherit;
  font-size: 1rem;
}
Expected output
Browser result: the link is blue and underlined, the Full name field has a visible gray border, and both buttons have a blue filled base appearance. The browser still provides its native focus indicator when you navigate to an enabled control.

Refresh the browser after saving. The exact native appearance of the disabled button and focus indicator can vary by browser at this starter stage.

Check your understanding

Identify the state

Match each situation with the interaction condition it describes.

Tap an item and then its matching pair.

Step 2 of 7

Add Hover Feedback Without Making It Essential

Add pointer-hover feedback that strengthens existing link and button affordances without making hover essential.

Make affordances persistent

Hover reinforces; it does not explain

A :hover pseudo-class matches while a pointing device is over an element. It can provide useful feedback, but it must not be the first moment a person can recognize what a control does.

Keep the link underlined and the button labeled in their default state. On hover, add another cue: strengthen the link underline, and underline the button text as its background changes. A person using touch may not get the same hover behavior, so labels and instructions must already be visible.

Default and hover cues

Side-by-side interface comparison: the default link is underlined, while its hovered version has a thicker underline; the hovered button is darker and also gains an underline.

Both controls are recognizable before hover. Hover adds a non-color cue: a stronger underline.

Add non-color feedback

Write hover rules

Add these rules in the hover styles section of styles.css, after the base styles. The link’s underline is already present by default; hover makes that underline thicker. The button changes its surface color and gains an underline, so the feedback is not only a hue change.

Hover styles

Add these rules below your base styles in styles.css. Save, refresh the local page, and move a pointer over the link and enabled reset button.

css
.info-link:hover {
  color: var(--accent-hover);
  text-decoration-thickness: 3px;
}

.action:hover {
  background-color: var(--accent-hover);
  text-decoration: underline;
}
Expected output
Default: the Workshop details link remains underlined, and the Clear form button keeps its visible label and blue surface.

Hover the link: its underline becomes 3px thick.

Hover the enabled Clear form button: its background becomes darker and its text becomes underlined.

Check these visual results in the browser; CSS does not produce terminal output.

Tip

Design check

Do not put essential instructions in hover-only content. For example, do not reveal a button’s purpose only when a pointer reaches it. The visible label must communicate the purpose even when hover is unavailable.

Practice the selectors and cues

Complete the hover styles

Complete the four marked parts of these hover rules.

css
.info-link {
  color: var(--accent-hover);
  text-decoration-thickness: ;
}

.action {
  background-color: var(--accent-hover);
  text-decoration: ;
}

Apply and justify the hover design

Diagnose a hover-only design

A draft interface uses a button whose purpose is hidden until hover, and its hover state changes only the background color. Describe how you would repair the design so its purpose is available without hover and its hover state has a non-color cue. Then explain why the original design excludes or confuses some users.

Write at least 120 characters (0/120).

Step 3 of 7

Keep Keyboard Focus Clearly Visible

Apply a clear focus indicator with :focus-visible while preserving native keyboard behavior.

Understand visible focus

Focus is not hover

Focus marks the current target

A control has focus when it is the current target for interaction. :focus matches any focused element, whether focus arrived through the keyboard, a pointer, or another browser action.

:focus-visible is narrower: it matches focused elements when the browser determines that a visible focus indicator is needed. It is not a strict keyboard-only detector. For example, a text input can match :focus-visible after a pointer interaction because typing needs a clear target.

Keep the browser’s native focus indicator unless you provide an equally clear authored replacement. Never remove an outline globally just to change its appearance.

A focus indicator sits outside the control

An underlined link, text input, and button each shown with a dark outline outside the control and a visible gap around it.

An outline can remain separate from a control’s border and background, helping the current target stand out.

Keyboard behavior remains native

Use Tab to move forward through reachable native controls and Shift+Tab to move backward. With focus on a link, Enter activates it. With focus on a native button, Enter or Space activates it.

State CSS changes appearance only; it must not replace these native keyboard behaviors. Test the local page in a browser after adding the styles.

Author a clear replacement

Focus styles

Add this section after the hover styles in styles.css. It gives the link, field, and action button the same clear focus indicator.

css
/* focus styles */
.info-link:focus-visible,
.field:focus-visible,
.action:focus-visible {
  outline: 3px solid var(--focus);
  outline-offset: 3px;
}
Expected output
Focused link, field, or button: 3px dark outline (#111827)
Gap between control edge and outline: 3px
Control box dimensions: unchanged

After saving and refreshing the local page, this is the visual result whenever one of these controls matches `:focus-visible`. The outline is drawn outside the element, so its box dimensions do not change.

Tip

Why use an outline?

outline is drawn outside the element, so it does not change the element’s box dimensions. outline-offset creates space between the element and its outline.

Keep space around focusable controls. A nearby container with clipping overflow can cut off an outline, making focus harder to see. Do not add a global rule that removes outlines.

Check your understanding

Choose the accurate statement

Which statement correctly distinguishes :focus and :focus-visible?

Evaluate the rule

True or false: button:focus { outline: none; } is safe when no other focus indicator is provided.

Apply visible focus

Complete the focus rule

Complete the five blanks to apply the authored focus indicator and identify the unsafe outline-removal rule.

css
.info-link,
.field:focus-visible,
.action:focus-visible {
  outline: ;
  outline-offset: ;
}

/*  matches every focused element. */
/* Never use  as the only focus feedback. */

Step 4 of 7

Use Real Disabled Behavior

Style a native disabled button and distinguish its browser behavior from a merely dimmed control.

Start with native behavior

Behavior and appearance are different

A disabled control gets its unavailable behavior from HTML, not from a faded appearance. On a supported control such as a <button>, the disabled attribute prevents operation and removes the button from sequential keyboard focus: Tab moves past it.

The :disabled pseudo-class selects a control that is already disabled. CSS can make that state recognizable, but CSS does not create the behavior. An enabled button with muted colors—or only a different cursor—can still be activated and reached with Tab.

A link does not support native disabled behavior through a disabled attribute. Giving that attribute to an anchor does not stop it from working as a link.

Compare native and visual states

A diagram compares a native disabled button that Tab skips, a visually dimmed enabled button that Tab reaches, and a link that remains reachable.

Only a button that is actually disabled is both unavailable for operation and skipped by sequential Tab navigation.

Use the supported HTML attribute

Keep the unavailable action explicit

Keep the native attribute on the unavailable submit button and keep the explanation visible beside it. The message tells people why the action is unavailable; muted color is not the only cue.

What each case does

These cases can look similar, but the browser behavior is different.

Native behavior versus visual styling
SituationWhen activatedSequential Tab
Button with the disabled attributeDoes not operateSkipped
Enabled button styled to look mutedStill operatesReached
Link with a disabled attributeStill navigatesReached

Mark up the unavailable action

Keep this markup in index.html. The disabled attribute supplies the unavailable button behavior, while the adjacent text supplies a persistent explanation.

html
<form>
  <label for="full-name">Full name</label>
  <input class="field" id="full-name" name="full-name" type="text">
  <p>
    <button class="action" type="reset">Clear form</button>
    <button class="action" type="submit" disabled>Join workshop</button>
    <span>Registration is closed in this demo.</span>
  </p>
</form>
Expected output
Visible result:
Full name
[ text field ]
[ Clear form ] [ Join workshop ] Registration is closed in this demo.

Behavior:
The Join workshop button cannot submit the form. Sequential Tab navigation reaches the text field and Clear form, but skips Join workshop.

Browser-rendering result after saving `index.html` and refreshing the local page.

Give the disabled control a clear appearance

Style the state CSS selects

Add this rule in the disabled styles section of styles.css, after the hover and focus sections. It selects the button that HTML has already disabled and gives it a muted but readable appearance. These declarations do not disable an enabled button; the HTML attribute remains necessary.

Add disabled styles

Append this rule to the disabled styles section. Save and refresh the local page.

css
/* disabled styles */
.action:disabled {
  border-color: var(--disabled-bg);
  background-color: var(--disabled-bg);
  color: var(--disabled-text);
  cursor: not-allowed;
  text-decoration: none;
}
Expected output
Visible result:
The Join workshop button has a light muted background, readable dark muted text, no underline, and an unavailable cursor under a pointer.

Behavior remains native HTML behavior:
Pointer, Enter, and Space do not activate Join workshop. Sequential Tab navigation skips it. The message “Registration is closed in this demo.” remains visible beside the button.

Browser-rendering result when used with the disabled Join workshop button in `index.html`. Exact visual rendering varies slightly by browser.

Tip

Keep the reason visible

Do not communicate unavailability with color alone. The readable message beside the button explains why the action cannot be used, including for someone who does not notice the muted appearance.

Connect HTML behavior and CSS styling

Complete the native attribute

Complete the native attribute on this unavailable submit button.

html
<button class="action" type="submit" >Join workshop</button>

Complete the matching CSS selector

Complete the pseudo-class that selects the genuinely unavailable action.

css
.action: {
  background-color: var(--disabled-bg);
  color: var(--disabled-text);
}

Check the behavior predictions

Predict operation and Tab behavior

Match each case to its browser behavior.

Tap an item and then its matching pair.

Step 5 of 7

Make Overlapping States Work Together

Use source order and matching specificity to keep focus visible during hover and preserve the unavailable appearance of a disabled control.

See the combined states

States can overlap

An enabled control can be in a hover state and match :focus-visible at the same time. The selectors describe separate conditions; neither state cancels the other.

Give them separate visual jobs. Hover changes the button background and adds an underline. The focus indicator uses an outline outside the control. When a keyboard-focused button is also under the pointer, both cues can remain visible.

A disabled control can also be under a pointer and match :hover. Its unavailable appearance must therefore replace enabled hover feedback.

Two overlapping outcomes

Comparison of an enabled button showing hover feedback plus an outer focus outline, and a disabled button remaining muted under a pointer.

Hover plus focus-visible preserves both cues. Hover plus disabled keeps the unavailable appearance.

Tip

Check the rendered result

Inspect the result in the browser. Selector names do not give one state automatic priority: when rules affect the same property, the cascade resolves the result.

Resolve conflicts with the cascade

Use source order deliberately

Keep the state sections in this order: hover, focus, then disabled. Hover and focus-visible set different properties, so their feedback can coexist.

.action:hover and .action:disabled have equal specificity: each contains one class and one pseudo-class. They both set background-color and text-decoration. Because the disabled rule comes later, it replaces the hover values for a disabled button under the pointer.

State rules in source order

Keep these state sections in this order in styles.css, below the base styles. Save and refresh, then inspect both combined conditions in your browser.

css
/* hover styles */
.info-link:hover {
  color: var(--accent-hover);
  text-decoration-thickness: 3px;
}

.action:hover {
  background-color: var(--accent-hover);
  text-decoration: underline;
}

/* focus styles */
.info-link:focus-visible,
.field:focus-visible,
.action:focus-visible {
  outline: 3px solid var(--focus);
  outline-offset: 3px;
}

/* disabled styles */
.action:disabled {
  border-color: var(--disabled-bg);
  background-color: var(--disabled-bg);
  color: var(--disabled-text);
  cursor: not-allowed;
  text-decoration: none;
}
Expected output
Enabled .action with hover plus focus-visible:
- The background uses var(--accent-hover).
- The text is underlined.
- The 3px focus outline remains visible outside the button, separated by a 3px offset.

Disabled .action under the pointer:
- The later disabled declarations keep the disabled background and disabled text color.
- The text decoration is none, so the enabled hover underline does not appear.

This CSS produces visual browser results rather than console text. Check the enabled reset button with keyboard focus while the pointer is over it, then move the pointer over the disabled submit button.

Warning

Protect the focus indicator

Do not add a broad or later rule that sets outline: none; on a matching focused control. It can erase the authored focus indicator. Inspect the rendered overlap instead of assuming that a focus selector always wins.

Apply the overlap rules

Repair and predict the overlap

A short stylesheet puts .action:disabled immediately before .action:hover. Both selectors match when the unavailable Join workshop button is under the pointer, and both have equal specificity.

Without changing either selector, describe the repair. Write the three disabled declarations that must replace the conflicting hover properties, and state where the disabled rule belongs. Then predict both final results:

  1. An enabled .action is hovered and matches :focus-visible.
  2. A disabled .action is hovered.

Use the established custom properties and focus-outline values.

Write at least 180 characters (0/180).

Step 6 of 7

Check Contrast Across Interaction States

Evaluate text, control, and focus-indicator contrast across interaction states, including the inactive-control exemption.

Identify the relevant color pair

Measure the cue that communicates

Contrast is a comparison between a cue and the color directly beside it, not a judgment based on the whole screen.

For this normal-size interface, enabled text needs at least 4.5:1 against its background. Visual information needed to identify an enabled control—such as an essential input boundary or button fill—needs at least 3:1 against adjacent colors. An authored focus indicator also needs at least 3:1.

Ask whether the feature communicates something necessary. A purely decorative border does not need its own target. But if a boundary is what makes a white input recognizable as a control, it is an essential non-text cue.

Find the adjacent color

Diagram showing an underlined link, filled button, input boundary, and offset focus outline, each compared with the color immediately beside it.

Compare each text, boundary, fill, or focus indicator with its adjacent background. For an offset outline, the surface beside the outline is the relevant adjacent color.

Check every changed pair

Record state-specific measurements

Enter the supplied hexadecimal values into a contrast checker and interpret the ratio by the cue’s purpose and state. Small display-rounding differences between checkers are normal.

The button label and its fill are separate pairs: white label text is compared with the blue fill, while the blue fill is compared with the surrounding white surface when that fill helps identify the enabled control. Likewise, the hovered link is a changed text pair that needs its own check.

Workshop signup contrast checks

These WCAG contrast readings use the colors in styles.css. Each row identifies the foreground or indicator, its adjacent background, and the requirement for that specific cue.

Compare each measured ratio with the requirement that applies to the cue’s role and interaction state.
Foreground or indicatorAdjacent backgroundState and purposeRatioRequirement
--text (#1f2937)--surface (#ffffff)Default ordinary text14.7:14.5:1
--accent (#1d4ed8)--surface (#ffffff)Default link text6.70:14.5:1
--accent-hover (#1e40af)--surface (#ffffff)Hovered link text8.72:14.5:1
--surface (#ffffff)--accent (#1d4ed8)Enabled button label6.70:14.5:1
--surface (#ffffff)--accent-hover (#1e40af)Hovered enabled button label8.72:14.5:1
--accent (#1d4ed8)--surface (#ffffff)Enabled button fill, when essential6.70:13:1
--accent-hover (#1e40af)--surface (#ffffff)Hovered button fill, when essential8.72:13:1
--border (#6b7280)--surface (#ffffff)Input boundary, when essential4.83:13:1
--focus (#111827)--surface (#ffffff)Focus outline beside offset gap17.7:13:1
--disabled-text (#4b5563)--disabled-bg (#e5e7eb)Disabled control labelAbout 6.1:1Exempt; readability check

Apply the exemption carefully

Example

Interpret focus and disabled results

The focus outline is a non-text focus indicator. Because outline-offset: 3px; leaves the page surface between the control and the outline, compare --focus with --surface, not automatically with the blue button fill inside the outline.

Inactive controls are exempt from the normal text and non-text contrast requirements. That exemption is not permission to make labels unreadable. The disabled button still has a readable label and the persistent explanation, “Registration is closed in this demo.” Together, they communicate unavailability without relying only on muted color.

Tip

Keep the scope precise

These checks examine selected state cues, not the accessibility of an entire page. Use the actual adjacent colors and the role of each cue instead of assuming that every border or muted color has the same requirement.

Prepare an assessment from the readings

Five state cards

Five compact interface cards, from left to right: enabled button, input boundary, offset focus outline, disabled button, and underlined link.

Read the cards from left to right as pairs A through E. The table provides the exact colors and checker readings.

New readings to evaluate

Use these new checker readings. On the next screen, decide which enabled cues meet their applicable threshold, which enabled cue needs correction, and what should be done about the inactive label.

The table provides measurements only. Interpret each result by the role and state of its cue.
PairForeground or indicatorAdjacent backgroundState and roleRatio
A#ffffff#1d4ed8Enabled button label6.70:1
B#9a9a9a#ffffffEnabled input boundary2.82:1
C#333333#ffffffAuthored focus outline beside offset gap12.63:1
D#9ca3af#e5e7ebDisabled control label2.04:1
E#6b7280#ffffffHovered link text4.83:1

Assess the contrast findings

Assess the interaction-state cues

Using pairs A through E from the previous screen, classify each pair. For every enabled text, essential non-text cue, or focus indicator, state whether it meets its applicable threshold and why. Identify the pair that needs correction. Then explain why pair D is exempt as an inactive control but should still be improved for readability.

Write at least 180 characters (0/180).

Step 7 of 7

Finish and Test the Accessible State Styles

Complete the local stylesheet and verify hover, focus, disabled behavior, and relevant contrast evidence.

1. Assemble the base stylesheet

Assemble the final stylesheet

Replace the contents of styles.css with these two code blocks in order. Together, they are the final reference stylesheet for the local Workshop signup page. This base section keeps every enabled control recognizable before interaction: the link is underlined, the field has a visible boundary, and the enabled button has a filled surface.

Persistent affordances and focus

A signup interface with an underlined link, bordered input, blue enabled button, muted unavailable button, and a dark focus outline around the enabled button.

Controls remain identifiable before interaction. The focus indicator is a separate cue that can remain visible with hover.

Base styles

Part 1 of 2 — save this base section first in styles.css.

css
/* base styles */
:root {
  --surface: #ffffff;
  --text: #1f2937;
  --accent: #1d4ed8;
  --accent-hover: #1e40af;
  --focus: #111827;
  --border: #6b7280;
  --disabled-bg: #e5e7eb;
  --disabled-text: #4b5563;
}

* {
  box-sizing: border-box;
}

body {
  margin: 0;
  background-color: var(--surface);
  color: var(--text);
  font-family: system-ui, sans-serif;
  line-height: 1.5;
}

main {
  max-width: 40rem;
  margin: 0 auto;
  padding: 2rem;
}

form {
  max-width: 28rem;
}

label {
  display: block;
  margin-top: 1.5rem;
}

.info-link {
  color: var(--accent);
  font-size: 1rem;
  text-decoration: underline;
}

.field {
  display: block;
  width: 100%;
  margin: 0.5rem 0 1rem;
  padding: 0.75rem;
  border: 2px solid var(--border);
  border-radius: 0.25rem;
  background-color: var(--surface);
  color: var(--text);
  font: inherit;
  font-size: 1rem;
}

.action {
  margin-right: 0.5rem;
  padding: 0.75rem 1rem;
  border: 2px solid var(--accent);
  border-radius: 0.25rem;
  background-color: var(--accent);
  color: var(--surface);
  cursor: pointer;
  font: inherit;
  font-size: 1rem;
}
Expected output
After refresh:
The Workshop details link is blue and underlined.
The Full name field has a visible gray border.
Clear form is a filled blue button with white text.
Join workshop still uses its browser-default disabled appearance until the state rules are added.

This stylesheet produces visual output in the browser after you save and refresh `index.html`.

2. Add the ordered state sections

Hover, focus, and disabled styles

Part 2 of 2 — append these state sections after the base styles. The order lets the later disabled rule replace matching hover properties.

css
/* hover styles */
.info-link:hover {
  color: var(--accent-hover);
  text-decoration-thickness: 3px;
}

.action:hover {
  background-color: var(--accent-hover);
  text-decoration: underline;
}

/* focus styles */
.info-link:focus-visible,
.field:focus-visible,
.action:focus-visible {
  outline: 3px solid var(--focus);
  outline-offset: 3px;
}

/* disabled styles */
.action:disabled {
  border-color: var(--disabled-bg);
  background-color: var(--disabled-bg);
  color: var(--disabled-text);
  cursor: not-allowed;
  text-decoration: none;
}
Expected output
After refresh:
Hovering Workshop details makes its underline thicker.
Hovering Clear form darkens its blue background and underlines its label.
A focus-visible link, field, or enabled button has a dark 3px outline with a visible gap.
A keyboard-focused Clear form button keeps that outline while hovered.
Join workshop is muted, has no underline under the pointer, cannot be activated, and is skipped by Tab navigation.

Test these rendered results with a pointer and keyboard after saving and refreshing the page.

Final contrast checkpoints

Use a contrast checker with the supplied colors. Each row identifies the foreground, indicator, or essential fill; its adjacent background; the state; and the threshold to apply. The inactive row is a readability record, not a required pass under the inactive-control exemption.

Approximate contrast readings for the local Workshop signup state styles
PairStateRatioThreshold
#1d4ed8 text on #ffffffDefault link6.70:14.5:1 text
#1e40af text on #ffffffHovered link8.72:14.5:1 text
#ffffff text on #1d4ed8Enabled button label6.70:14.5:1 text
#1d4ed8 fill on #ffffffEnabled button fill6.70:13:1 non-text
#ffffff text on #1e40afHovered button label8.72:14.5:1 text
#1e40af fill on #ffffffHovered button fill8.72:13:1 non-text
#6b7280 border on #ffffffEnabled field boundary4.83:13:1 non-text
#111827 outline on #ffffffFocus indicator17.7:13:1 non-text
#4b5563 text on #e5e7ebDisabled control label6.0:1Exempt; keep readable

Tip

Interpret the readings

The outline is offset from its control, so its adjacent color is the white page surface in this example. Record both button-label pairs and both button-fill pairs: text contrast and essential control-fill contrast answer different questions.

3. Repair an overlapping-state rule

Repair the disabled-state rule

Complete the disabled declarations so the unavailable action retains its muted appearance when a pointer is over it.

css
.action:hover {
  background-color: var(--accent-hover);
  text-decoration: underline;
}

.action:disabled {
  background-color: ;
  color: ;
  text-decoration: ;
}

4. Record the final evidence

Run the manual check

Save both files and refresh the local page. Gather evidence with these trials:

  1. Before hover, confirm that the link is underlined, the field has a boundary, and Clear form is visibly a button. Confirm that no purpose or instruction appears only on hover.
  2. Hover the link and Clear form.
  3. Use Tab and Shift+Tab. With focus on Workshop details, press Enter.
  4. Type Pointer test in the field, then activate Clear form with the pointer and confirm the field empties.
  5. Type Keyboard test in the field, Tab back to Clear form, then activate it with Enter or Space and confirm the field empties.
  6. Keep Clear form keyboard-focused and move the pointer over it. Confirm that its hover cue and focus indicator both remain visible.
  7. Continue with Tab and confirm Join workshop is skipped. Move the pointer over it and confirm its unavailable appearance remains and it cannot be activated.

Use the table readings as focused evidence for these states, not as a complete accessibility audit.

Final verification record

Write a concise test record for your local page. Report: persistent affordances and hover feedback; Tab and Shift+Tab movement and Enter on the details link; the separate pointer and keyboard reset trials; the combined hover and focus-visible result; and the skipped, unavailable Join workshop action.

Then record every table pair using its foreground or indicator color, adjacent background, interaction state, ratio, and threshold. State whether each enabled pair meets its threshold, and identify the disabled-label pair as exempt while noting its readability.

Write at least 450 characters (0/450).

Summary

State-style checklist complete

  • Enabled controls remain recognizable without hover-only information.
  • Hover feedback and a focus indicator can stay visible together.
  • A real disabled control is skipped by sequential keyboard navigation and remains unavailable under the pointer.
  • State-specific contrast records identify the cue, adjacent color, ratio, and threshold.

Tutorial complete

Congratulations! You completed: Style Accessible Hover, Focus, and Disabled States

You completed and manually checked accessible hover, focus, and disabled states on the local Workshop signup page.

Download the App now to have access to + 5000 free courses, exercises, certificates and lots of content without paying anything!

  • 100% free online courses from start to finish

    Thousands of online courses in video, ebooks and audiobooks.

  • More than 60 thousand free exercises

    To test your knowledge during online courses

  • Valid free Digital Certificate with QR Code

    Generated directly from your cell phone's photo gallery and sent to your email

Cursa app on the ebook screen, the video course screen and the course exercises screen, plus the course completion certificate