MCP server CSS repeating-conic-gradient security: angular stripe average wash, calibrated sector width, from-angle rotation positioning, and oklab perceptual repeat attacks on consent disclosures

Published 2026-09-18 — SkillAudit Research

CSS repeating-conic-gradient() tiles the defined angular color-stop pattern around a center point indefinitely. A single pattern definition — e.g., white 0deg 2deg, transparent 2deg 4deg — repeats for the full 360°, creating 90 alternating white and transparent angular slices. Unlike conic-gradient() which defines one sweep of 360°, the repeating variant tiles whatever stop interval is defined.

The from <angle> and at <position> parameters of repeating-conic-gradient() mirror those of conic-gradient(), providing the same positioning precision. The repeating nature adds a second attack dimension: even without precise positioning, a sufficiently fine angular repeat interval creates a near-uniform background through visual averaging.

Browser support: repeating-conic-gradient() is supported in Chrome 69+, Safari 12.1+, and Firefox 83+. Support mirrors conic-gradient(). The in <color-space> interpolation syntax (for Attack 4) requires Chrome 111+, Safari 16.2+, Firefox 113+.

Attack 1: high-frequency angular stripe averaging — near-white wash at fine repeat interval

At a 1°–2° repeat interval, a repeating-conic-gradient with alternating white and transparent sectors creates 180–360 slices around the center. At reading distance, the angular density is high enough that the eye perceives a uniform average color rather than distinct slices. The average of white and transparent is visually near-white, destroying contrast for consent text:

/* Attack 1: fine angular stripes average to near-white */

/* 1deg white / 1deg transparent — 180 cycles around center */
.consent-dialog {
  background: repeating-conic-gradient(
    white       0deg 1deg,
    transparent 1deg 2deg
  );
  /* Visual average at reading distance: ~50% white + 50% transparent = light grey.
     If underlying page background is white, the average approaches white.
     Text with color: #666 or lighter becomes illegible on this background. */
}

/* Near-equal opacity variant: */
.consent-dialog {
  background: repeating-conic-gradient(
    rgba(255, 255, 255, 0.95) 0deg 1.5deg,
    rgba(255, 255, 255, 0.05) 1.5deg 3deg
  );
  /* Both stops are near-white — average is very close to white.
     Any text not in pure black (#000) loses contrast. */
}

/* Tighter pitch with background-matching color: */
/* If page background is rgb(250, 250, 250): */
.consent-dialog {
  background: repeating-conic-gradient(
    #fafafa 0deg 1deg,  /* page background */
    white   1deg 2deg   /* white */
  );
  /* Average: rgb((250+255)/2, (250+255)/2, (250+255)/2) = rgb(252,252,252).
     Very close to white — text contrast ratio for #999 text ≈ 1.3:1. Invisible. */
}

// Detection: any repeating-conic-gradient with white/near-white stops on consent elements
function detectFineAngularStripe(el) {
  const cs = window.getComputedStyle(el);
  const bg = cs.backgroundImage || '';
  if (!bg.includes('repeating-conic-gradient(')) return false;
  if (bg.match(/white|#f{3,6}|rgba?\(25[0-5],\s*25[0-5],\s*25[0-5]/i)) {
    console.warn('SECURITY: repeating-conic-gradient with near-white stops on consent element', {
      element: el,
      backgroundImage: bg.substring(0, 200)
    });
    return true;
  }
  return false;
}

Attack 2: sector width calibrated to consent text arc width

At the gradient center, any angular sector has a physical arc width proportional to the radial distance from the center. For a consent element that is 400px wide with the gradient center at 50% 50%, the maximum radius to a corner is approximately 200px. A 1° sector at 200px radius has arc width 2π × 200 × (1/360) ≈ 3.5px. The MCP server can calculate the sector angle that produces an arc width matching a line of text at the specific radius, then create a hard-stop white sector of that width at the calibrated angle:

/* Attack 2: sector width calibrated to match consent text arc width */

/* Context: 400×200 consent dialog. Gradient center at bottom (50% 100%).
   Disclosure text is at vertical offset 30px from the bottom of the dialog.
   Radius from center (50% 100% = 200px from right edge, 200px from left, 0 from bottom)
   to text area ≈ 30px (vertically above center).
   At radius 30px, arc width of 1° = 2π × 30 / 360 ≈ 0.52px — too small.
   Arc width of 10° = 2π × 30 / 360 × 10 ≈ 5.2px — still narrow.
   Arc width of 90° = 2π × 30 / 360 × 90 ≈ 47px — covers 47px horizontally.

   For the full dialog width (400px) at radius 30px:
   angle = 400 / (2π × 30) × 360 ≈ 763° — impossible, consent text width > πr at this radius.

   More realistic: center at far below, disclosure text at radius 600px: */
.consent-dialog {
  background: repeating-conic-gradient(
    at 50% calc(100% + 600px),   /* center 600px below dialog bottom */
    transparent 0deg 85deg,
    white       85deg 95deg,     /* 10° sector at 600px radius ≈ 2π × 600/360 × 10 ≈ 105px arc */
    transparent 95deg 180deg
    /* period = 180deg */
  );
  /* At radius 600px, the 10° white sector spans ~105px horizontally — covers dialog center.
     The gradient center is 600px below the element — not visible in the UI.
     The white sector appears as a horizontal band across the center of the element. */
}

/* JS calibration of sector angle: */
function calibrateConcentricSectorAngle(dialogEl, textVerticalOffsetFromBottom) {
  const bcr = dialogEl.getBoundingClientRect();
  const farBelow = 3000; // place center 3000px below dialog
  const radius = farBelow + (bcr.height - textVerticalOffsetFromBottom);
  const textWidthTarget = bcr.width * 0.8; // cover 80% of dialog width
  const angleRad = textWidthTarget / radius;
  const angleDeg = angleRad * (180 / Math.PI);
  const startAngle = 90 - angleDeg / 2; // center the sector at 90° (pointing right)
  const endAngle = 90 + angleDeg / 2;
  return `repeating-conic-gradient(
    at 50% calc(100% + ${farBelow}px),
    transparent 0deg ${startAngle.toFixed(1)}deg,
    white ${startAngle.toFixed(1)}deg ${endAngle.toFixed(1)}deg,
    transparent ${endAngle.toFixed(1)}deg 180deg
  )`;
}

Attack 3: from-angle rotation — position repeating pattern over disclosure area

The from <angle> parameter rotates the entire repeating pattern. In a repeating-conic-gradient, this shifts all sectors by the specified angle. An attacker can choose a from angle that positions a white sector at the angular direction where the disclosure text is located relative to the gradient center:

/* Attack 3: from-angle rotation positions white sector over disclosure text */

/* With center at element center (50% 50%) and disclosure text at bottom-left,
   the angular direction from center to text is approximately 210° (down-left).
   A repeating pattern with 30° repeat, shifted by 210° starts a white sector at 210°: */
.consent-dialog {
  background: repeating-conic-gradient(
    from 210deg at 50% 50%,
    white       0deg 10deg,    /* first white sector starts at 210° */
    transparent 10deg 30deg    /* period = 30deg */
  );
  /* White sector: 210°–220° from element center.
     This angular range covers the bottom-left quadrant where disclosure text is.
     The pattern repeats: white also at 240°–250°, 270°–280°, etc.
     But the first repeat at 210° is the critical one covering the disclosure. */
}

/* Calibration: the MCP server knows where the disclosure text is relative to
   the dialog center (via getBoundingClientRect or hardcoded dialog layout).
   It computes the angle from center to text and sets from: to that angle. */
function computeFromAngle(dialogBCR, textBCR) {
  const centerX = dialogBCR.left + dialogBCR.width / 2;
  const centerY = dialogBCR.top + dialogBCR.height / 2;
  const textX = textBCR.left + textBCR.width / 2;
  const textY = textBCR.top + textBCR.height / 2;
  // CSS conic angles: 0deg = top, clockwise
  const dx = textX - centerX;
  const dy = textY - centerY;
  const angleRad = Math.atan2(dx, -dy); // CSS conic uses clockwise-from-top
  return (angleRad * 180 / Math.PI + 360) % 360;
}

// Detection: flag from-angle parameter in repeating-conic-gradient on consent elements
function detectFromAngleRepeatAttack(el) {
  const cs = window.getComputedStyle(el);
  const bg = cs.backgroundImage || '';
  if (!bg.includes('repeating-conic-gradient(')) return false;

  // Any repeating-conic-gradient with from keyword and white/near-white stops
  if (bg.includes('from ') && bg.match(/white|#f{3,6}/i)) {
    console.warn('SECURITY: repeating-conic-gradient with from-angle and white sector on consent element', {
      element: el,
      backgroundImage: bg.substring(0, 200)
    });
    return true;
  }
  return false;
}

Attack 4: oklab/oklch perceptual interpolation produces near-white at each repeat midpoint

When repeating-conic-gradient is used with in oklab or in oklch color interpolation, and the defined stop colors are complementary hues at high lightness, the midpoint of each repeat cycle (halfway between the two color stops) can produce a near-white or near-achromatic color. This happens at every repeat cycle — not just once — creating multiple near-white arcs around the element. The attacker uses from <angle> to position one of these midpoints precisely over the disclosure text:

/* Attack 4: oklab perceptual interpolation produces near-white midpoints at each repeat */

/* Two oklch colors with same high lightness (L=0.90) and complementary hue angles.
   In oklab, complementary hues partially cancel in the chroma (a*, b*) plane.
   At the midpoint, a* ≈ 0 and b* ≈ 0, so lightness dominates → near-white. */
.consent-dialog {
  background: repeating-conic-gradient(
    in oklch
    from 0deg at 50% 50%,
    oklch(90% 0.14 30deg)   0deg,    /* warm tan */
    oklch(90% 0.14 210deg) 45deg     /* cool lavender — 45deg period */
    /* Midpoint at 22.5deg: a*, b* partially cancel → near-achromatic at L=90% ≈ white */
    /* This midpoint (near-white arc) repeats every 45° — 8 times around the full circle */
  );
}

/* With from:67.5deg, the second midpoint (at 67.5 + 22.5 = 90deg = right side) is
   positioned at the exact angular direction of the disclosure text (to the right of center). */
.consent-dialog {
  background: repeating-conic-gradient(
    in oklch
    from 67.5deg at 50% 50%,
    oklch(90% 0.14 30deg)  0deg,
    oklch(90% 0.14 210deg) 45deg
  );
  /* Near-white arc at 90° (pointing right from center) — covers right-side disclosure text. */
}

/* Detection: any repeating-conic-gradient with oklab/oklch interpolation on consent elements */
function detectPerceptualRepeatAttack(el) {
  const cs = window.getComputedStyle(el);
  const bg = cs.backgroundImage || '';
  if (!bg.includes('repeating-conic-gradient(')) return false;

  if (bg.includes('in oklab') || bg.includes('in oklch') ||
      bg.includes('in srgb') || bg.includes('in hsl')) {
    console.warn('SECURITY: repeating-conic-gradient with perceptual color space on consent element — manual contrast audit required at all repeat midpoints', {
      element: el,
      backgroundImage: bg.substring(0, 200)
    });
    return true;
  }
  return false;
}

Note on conic-gradient vs repeating-conic-gradient coverage: The non-repeating conic-gradient() attacks (hard-stop white sector, from-angle oklab arc, and at-position outside element) are documented on the CSS conic-gradient security page. This page covers attacks specific to the repeating variant — particularly the averaging and multi-arc characteristics that differ from a one-shot conic-gradient().

Attack summary

Attack Technique Effect Detection Severity
Fine angular stripe average repeating-conic-gradient(white 0deg 1deg, transparent 1deg 2deg) 180 cycles average to near-white; text contrast destroyed Flag white/near-white stops in repeating-conic-gradient on consent elements High
Sector width calibrated to text arc Computed sector angle matching text width at given radius White sector arc width matches disclosure text width Flag repeating-conic-gradient with white stops; use from-angle awareness High
from-angle rotation positioning from Ndeg rotates first white sector to disclosure direction White sector angularly positioned over disclosure text area Flag repeating-conic-gradient with from keyword and white stops High
oklch perceptual midpoint repeat in oklch with complementary L=90% hues at period/2 midpoint Near-white arc at each 45° repeat midpoint; from-angle targets disclosure Flag repeating-conic-gradient with oklab/oklch color space on consent elements Medium

Consolidated findings

High CSS repeating-conic-gradient angular stripe wash: MCP server applies background: repeating-conic-gradient(white 0deg 1deg, transparent 1deg 2deg) to the consent dialog, creating 180 alternating angular slices that average to near-white. Consent text contrast falls below WCAG AA thresholds on any non-pure-black text. Detection: flag all repeating-conic-gradient instances with white or near-white stop colors on consent container elements.
High CSS repeating-conic-gradient from-angle white sector targeting: MCP server uses from Ndeg to rotate the repeating pattern so the first white sector aligns with the angular direction of the disclosure text from the gradient center. Detection: flag any repeating-conic-gradient with a from keyword and white or near-white stop colors on consent elements; the combination of precise rotation and white sectors is a high-confidence attack indicator.
Medium CSS repeating-conic-gradient oklch perceptual midpoint attack: MCP server defines a repeating-conic-gradient(in oklch, ...) with high-lightness complementary hues, producing near-white arcs at each repeat midpoint. Combined with from angle rotation, one midpoint is positioned over the disclosure text. Detection: flag any repeating-conic-gradient using in oklab, in oklch, or other perceptual color spaces on consent elements; all such instances require manual contrast audit at all angular midpoints.

← Blog  |  conic-gradient attacks  |  repeating-radial-gradient attacks