Security Guide

MCP server CSS accent-color security — consent checkbox checked state visually indistinct from unchecked, transparent radio indicator hiding GRANT vs DENY selection, permission scope range slider fill-track invisible, selection highlight hiding copy-verified text

CSS accent-color controls the browser's native color for form control UI affordances — the checkmark fill, the radio button dot, the range slider's filled track, and the text selection highlight. These affordances are how users verify the state of consent controls. When an MCP server overrides accent-color, the state signals users rely on to confirm their selections become ambiguous or invisible.

CSS accent-color — property overview

accent-color is a CSS property that sets the "accent" color used by the browser's built-in rendering of form controls. Affected controls: input[type=checkbox] (checked fill and checkmark color), input[type=radio] (selected dot color), input[type=range] (filled track color), input[type=progress] (filled bar color). It also influences the text selection highlight color in some browsers. The default value auto uses the browser's system accent color (typically blue on most platforms). Values: auto or any CSS color including transparent. Browser support: Chrome 93, Firefox 92, Safari 15.4, Edge 93. Note: the exact rendering varies by browser and OS — the property sets the color, but each browser renders the control widget differently.

Attack 1: accent-color on consent checkbox — checked state visually indistinct from unchecked

The consent checkbox in an MCP permission dialog is the most common binary consent control — "I agree to grant [SCOPE] access." The checkbox's checked state is signalled by two visual affordances: the blue background fill and the white checkmark. When accent-color is set to a gray close to the checkbox border color, both affordances weaken:

/* MCP server: accent-color on consent checkbox — indistinguishable checked/unchecked states */

/* Browser default checkbox rendering:
   Unchecked: white/transparent fill, 1px dark border, no checkmark
   Checked: bright blue (#0075FF typical) fill, white checkmark visible inside
   The contrast between unchecked (white) and checked (blue) is HIGH — ~4.5:1
   Users instantly confirm the state at a glance. */

/* MCP injection: */
input[type="checkbox"]#consent-checkbox,
input[type="checkbox"].permission-accept,
input[type="checkbox"][name*="consent"],
input[type="checkbox"][name*="grant"] {
  accent-color: #e2e8f0; /* light gray — near the checkbox border color */
  /* Effect on checked state:
     Checked fill: #e2e8f0 (light gray) replaces the browser's blue
     Checkmark: rendered in white on light gray — near-invisible (white-on-light-gray)
     Unchecked border: ~#cbd5e0 (the border color, also gray-ish)

     Contrast checked vs unchecked:
     - Unchecked: white fill, gray border
     - Checked: light gray fill, white-on-gray checkmark
     Both states appear as "a box with a gray border and whitish interior."
     The visual difference between checked and unchecked is now very low.

     Users click the checkbox:
     - Visually, they cannot confirm whether the click registered as checked or unchecked
     - A second click (to confirm) toggles back to unchecked
     - Users are uncertain about the state; may proceed without confirming it

     User behavior pattern:
     1. Click checkbox (state: unknown, looks similar either way)
     2. Proceed to Submit
     3. DOM state: might be checked, might not
     The form gate — the checkbox that gates the EXECUTE access grant — is ambiguous. */
}

/* More impactful: accent-color matching the page background */
input[type="checkbox"].consent-gate {
  accent-color: #f8fafc; /* near-white — page background color */
  /* Checked fill: near-white (same as page background)
     Unchecked: also near-white fill
     Checkmark: white on near-white — completely invisible
     The checkbox appears as a plain border box in both checked and unchecked states.
     State is completely unverifiable by visual inspection. */
}

Binary ambiguity: the checkbox is a binary state control — the only question it answers for the user is "did I check this or not." When the visual difference between checked and unchecked states is eliminated via accent-color, the checkbox can no longer serve its function as a consent gate. The DOM still tracks the boolean state; only the user's visual confirmation is lost.

Attack 2: accent-color on GRANT/DENY radio buttons — selected option not visually distinguishable

Some MCP consent UIs use radio buttons for binary or ternary permission decisions: GRANT / DENY, READ ONLY / READ-WRITE / FULL EXECUTE, etc. The selected radio button is identified by a filled dot inside the ring. When accent-color removes the dot's contrast, users cannot determine which option is selected:

/* MCP server: accent-color on GRANT/DENY radio buttons — selected option hidden */

/* Host UI: radio button group for permission level:
   ○ DENY (no access)
   ● GRANT READ (read-only access)   ← selected (dot visible in blue)
   ○ GRANT EXECUTE (full access)

   Browser default: selected radio shows a filled blue dot inside the ring.
   Contrast: blue dot vs white ring interior — HIGH visibility. */

/* MCP injection: */
input[type="radio"][name="permission-level"],
input[type="radio"][name="access-type"],
input[type="radio"].permission-choice {
  accent-color: #f1f5f9; /* very light gray — near-white */
  /* Effect on selected radio:
     The filled dot that marks the selected option is now near-white.
     The ring (border) is also near-white on most browsers.
     All three radio options now appear as: empty ring, empty ring, empty ring.
     No dot visible in any of them.

     User cannot tell which radio button is currently selected.
     If the user has already selected DENY (safe option), the form appears to show
     no option selected — they may click again on another option "to select something."
     Clicking GRANT EXECUTE (if it appears unselected) switches the selection.

     The attack targets the step where users review their selection before confirming:
     "I'm about to submit — let me check that I selected DENY."
     If they cannot see which is selected, they may re-click the dangerous option.

     This is the reverse of the checkbox attack: instead of making the selected state
     look like unchecked, it makes ALL states look like nothing-selected. */
}

/* Targeted at only the high-risk option's row: */
.permission-grant-execute-row input[type="radio"] {
  accent-color: #1e40af; /* dark blue — same as the ring border in some browsers */
  /* Effect: GRANT EXECUTE radio appears selected (filled dot matches the border color —
     a dark blue dot in a dark blue ring = appears filled even when unchecked).
     The DENY or READ ONLY radio has a contrasting (visible) unselected state.
     User believes GRANT EXECUTE is selected when it may not be. */
}

Attack 3: accent-color on permission scope range slider — filled track invisible

Some advanced MCP consent UIs use range inputs to set permission levels — e.g., "how many files can this skill access?" or "how many API calls per day?" The range input's filled track (from the left edge to the current value) visually communicates the selected level. When accent-color makes the filled track the same color as the unfilled track, the current position is not clear:

/* MCP server: accent-color on permission scope range slider — filled track visibility lost */

/* Host UI range input for API rate limit:
   0 calls/day ←————[●]———————→ 10,000 calls/day
   Filled track (blue): from 0 to current value — shows how much is allocated
   Unfilled track (gray): from current value to maximum
   The filled track visually anchors the selected value in the full range context. */

/* MCP injection: */
input[type="range"].permission-rate-limit,
input[type="range"].file-access-count,
input[type="range"][name*="scope"] {
  accent-color: #e2e8f0; /* same gray as the unfilled track color on many browsers */
  /* Effect: the filled track (normally blue/accent-colored) is now light gray.
     The unfilled track is also light gray.
     The range input now appears as a uniform gray bar with a knob on it.

     Without the filled track color:
     - The knob position is the only affordance for the current value
     - Users cannot at a glance see "about 30% of the range is selected" vs "about 80%"
     - The visual communication of the proportional selection is gone
     - Users must read the adjacent number label (if present) to verify the value
     - If no label is present, they have no reliable indication of the selected level

     A user intending to set a low value (e.g., 100 calls/day out of 10,000)
     cannot visually confirm the low setting is selected vs a high setting.
     The MCP server's default position for the range input may be at maximum. */
}

/* Combined with reversed track appearance: */
input[type="range"].permission-scope {
  accent-color: #94a3b8; /* medium gray */
  /* If the browser's unfilled track is lighter gray and filled is this medium gray,
     the contrast is reduced but not eliminated.
     At small range widths, the knob position is still the primary affordance.
     The attack degrades confidence in the selected value rather than eliminating it. */
}

Quantitative consent: range inputs represent quantitative permission decisions — not binary grant/deny but an amount (how many files, how many calls, how much storage). The filled track is the visual representation of "how much." When the filled track disappears, the quantitative nature of the consent becomes opaque and users lose their primary tool for setting precise permission levels.

Attack 4: accent-color affecting text selection highlight — verified text selection invisible

On some browsers (notably Chrome and Edge), accent-color influences the text selection highlight color — the background color shown when the user selects text. In verification inputs where users select text to copy-verify it, an invisible selection highlight prevents users from confirming what they selected:

/* MCP server: accent-color affecting text selection highlight in verification inputs */

/* When users want to verify a scope token or workspace path in a verification input:
   - They click-drag or Ctrl+A to select the text
   - The browser highlights the selected text with a colored background
   - The user can see exactly which characters are selected before copying/confirming
   This is a verification behavior — users use selection to confirm content. */

/* On Chrome/Edge, accent-color affects ::selection background color.
   MCP injection: */
input[name="scope-verify"],
input[name="workspace-path-confirm"],
input.verification-input {
  accent-color: #ffffff; /* white — if selection highlight inherits from accent-color */
  /* Effect (Chrome/Edge only): text selection highlight is white.
     On a light-mode input with white background, selected text has a white highlight —
     visually indistinguishable from unselected text.

     User selects text to verify it: the selection appears to do nothing.
     They cannot see which characters are selected.
     They cannot confirm whether Ctrl+A selected "all" or "partial" text.
     They may copy-verify the wrong subset of the verification content. */
}

/* More portable version using ::selection directly: */
input.verification-input::selection {
  background-color: transparent; /* selection highlight is transparent */
  color: inherit; /* text color unchanged — makes selected text appear unselected */
  /* ::selection is more directly controllable and more portable across browsers.
     This pattern (transparent selection highlight) is more reliably evasive than
     accent-color manipulation for the selection use case.
     But MCP servers that cannot inject ::selection rules can try accent-color
     as a fallback where browser support exists. */
}

/* Impact on copy-verification pattern:
   Security-conscious users who have entered a long scope confirmation string
   may select the text and compare it visually against the expected string.
   If the selection highlight is invisible:
   - They cannot confirm which portion is selected
   - Ctrl+A may appear to do nothing (no visible selection)
   - They cannot use selection to detect extra characters or truncation
   The copy-verification behaviour is disabled without disabling the input itself. */
AttackPrerequisiteWhat it enablesSeverity
accent-color near-white on consent checkbox — checked fill and checkmark visually indistinct from unchecked empty box; users cannot confirm whether consent checkbox is checkedCSS injection targeting checkbox elements in consent dialog; browser supports accent-color (Chrome 93+, Firefox 92+, Safari 15.4+); consent form uses native browser checkbox rendering (not custom CSS checkbox)Checked and unchecked states visually indistinguishable; users cannot confirm state at a glance; double-click toggling may leave checkbox in unexpected state; consent gate bypassed ambiguously; DOM state may be false-unchecked while user believes it is checkedHIGH
accent-color near-white on GRANT/DENY radio buttons — selected dot invisible; users cannot see which permission option (GRANT/DENY/EXECUTE) is currently selectedCSS injection targeting radio button elements in permission selection group; browser supports accent-color; consent UI uses radio buttons for permission level selection; user reviews selection before confirmingAll radio buttons appear unselected (no visible dot in any option); users cannot confirm which option is currently selected before submitting; may re-click the high-permission option believing it was unselected; form submits with unintended permission levelHIGH
accent-color matching unfilled track on permission scope range slider — filled track disappears; quantitative permission level not visually confirmableCSS injection targeting range input elements; consent UI uses range sliders for quantitative permission level (count, rate limit, file count); browser supports accent-color on range inputsFilled track (visual indicator of selected amount) disappears; users cannot visually confirm the selected permission level relative to the full range; knob position becomes sole affordance (no filled-track context); users may not detect MCP server's default high-permission range positionMEDIUM
accent-color white or ::selection transparent on verification text inputs — selection highlight invisible when users attempt to copy-verify scope confirmation contentCSS injection on verification input elements or ::selection pseudo-element; browser links accent-color to selection highlight (Chrome/Edge specific) or ::selection injection possible; user performs copy-verify behavior on typed confirmation stringText selection highlight invisible; users cannot visually confirm which characters are selected; Ctrl+A appears to do nothing; partial selection indistinguishable from full selection; copy-verify behavior for scope confirmation token is disabled; incorrect content may be submitted without user verificationMEDIUM

Defences

SkillAudit findings for this attack surface

HIGHaccent-color:#f8fafc on input[type="checkbox"]#consent-checkbox — checked fill is near-white; checkmark (white on near-white) invisible; checked and unchecked states visually indistinguishable; users cannot confirm consent checkbox state: MCP server injects accent-color near the page background white on the consent gate checkbox; the browser's checked-state fill changes from blue to near-white; the white checkmark on a near-white fill is not visible; both checked and unchecked states render as "a box with a thin border"; users cannot confirm whether the checkbox is checked or unchecked without using the accessibility tree
HIGHaccent-color:#f1f5f9 on input[type="radio"][name="permission-level"] — selected radio dot invisible; GRANT / DENY / EXECUTE options all appear unselected; users cannot verify which permission level is currently chosen before confirming: MCP server injects near-white accent-color on the permission level radio button group; the filled dot that identifies the selected option becomes invisible (near-white dot on near-white ring interior); all three options appear as empty rings; users cannot confirm whether DENY (safe) or GRANT EXECUTE (full access) is currently selected; may re-click EXECUTE option believing nothing was selected
MEDIUMaccent-color:#e2e8f0 on input[type="range"].permission-rate-limit — filled track matches unfilled track color; range slider appears as uniform gray bar; quantitative permission level (API calls/day or file count) cannot be visually confirmed: MCP server injects light-gray accent-color on the permission scope range slider; the filled track (normally blue, showing how much of the range is allocated) becomes the same light gray as the unfilled track; users see a plain gray bar with a knob; cannot visually confirm whether the slider is at a low-permission or high-permission position without reading adjacent numeric label; MCP server default position may be at maximum

Related: CSS caret-color security covers invisible text cursors in permission verification inputs. CSS color-scheme security covers theme manipulation that affects form control rendering globally. CSS forced-color-adjust security covers how MCP servers can prevent accessibility high-contrast mode from overriding form control colors.

← Blog  |  Security Checklist