color.html

  1<!DOCTYPE html>
  2<html lang="en">
  3<head>
  4  <meta charset="UTF-8">
  5  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  6  <title>Color Anti-Patterns — Should Flag vs Should Pass</title>
  7  <style>
  8    body { font-family: system-ui, sans-serif; background: #fafafa; padding: 24px; margin: 0; color: #1a1a1a; }
  9    .grid { display: grid; grid-template-columns: 1fr 1fr; gap: 32px; max-width: 1200px; margin: 0 auto; }
 10    .col h2 { font-size: 14px; text-transform: uppercase; letter-spacing: 0.05em; margin: 0 0 16px; color: #475569; }
 11    .col h3 { font-size: 11px; text-transform: uppercase; letter-spacing: 0.05em; margin: 24px 0 8px; color: #64748b; }
 12    .card { padding: 14px 16px; border-radius: 10px; margin-bottom: 10px; }
 13    /* Styled button color cases (declared via stylesheet so jsdom resolves them
 14       reliably — inline color doesn't always win over user-agent rules in jsdom
 15       for <a> and <button>). */
 16    .styled-pill-low { background-color: rgb(31, 26, 21); color: rgb(91, 79, 68); display: inline-block; padding: 9px 18px; border-radius: 999px; font-weight: 500; font-size: 14px; text-decoration: none; }
 17    .low-contrast-button { background-color: rgb(55, 65, 81); color: rgb(108, 114, 128); display: inline-block; padding: 10px 20px; border-radius: 8px; border: 0; font-size: 14px; }
 18    .good-pill-high { background-color: rgb(20, 20, 25); color: rgb(245, 240, 232); display: inline-block; padding: 9px 18px; border-radius: 999px; font-weight: 500; font-size: 14px; text-decoration: none; }
 19    .inline-link-low { color: rgb(170, 170, 170); }
 20  </style>
 21</head>
 22<body>
 23  <div class="grid">
 24
 25    <!-- ════════════════════════════════════════════════════════════
 26         LEFT COLUMN: should flag
 27         ═══════════════════════════════════════════════════════════ -->
 28    <div class="col" data-col="flag">
 29      <h2>Should flag</h2>
 30
 31      <h3>Pure black surfaces</h3>
 32      <div class="card" style="background: #000000; color: white;">
 33        <p>Pure #000 background</p>
 34      </div>
 35
 36      <h3>Gray on color</h3>
 37      <div class="card" style="background: rgb(59, 130, 246);">
 38        <p style="color: rgb(150, 150, 150);">Gray text on blue background</p>
 39      </div>
 40      <div class="card" style="background: rgb(16, 185, 129);">
 41        <p style="color: rgb(180, 180, 180);">Gray text on green background</p>
 42      </div>
 43
 44      <h3>Low contrast</h3>
 45      <div class="card" style="background: white;">
 46        <p style="color: rgb(200, 200, 200);">Light gray text on white — very low contrast</p>
 47      </div>
 48      <div class="card" style="background: rgb(30, 30, 30);">
 49        <p style="color: rgb(80, 80, 80);">Dark gray text on near-black — low contrast</p>
 50      </div>
 51      <!-- Gradient background — gray heading is unreadable across every stop -->
 52      <div class="card" style="background: linear-gradient(180deg, #3b82f6 0%, #8b5cf6 100%); padding: 16px;">
 53        <h1 style="color: #808080; font-size: 32px; font-weight: 800; margin: 0;">Welcome to Our Platform</h1>
 54        <p style="color: #666666; font-size: 14px;">The best solution for modern teams and businesses</p>
 55      </div>
 56
 57      <h3>Gradient text</h3>
 58      <h3 style="background: linear-gradient(135deg, #667eea, #764ba2); -webkit-background-clip: text; background-clip: text; -webkit-text-fill-color: transparent; font-size: 1.5rem;">
 59        Gradient Heading
 60      </h3>
 61
 62      <h3>AI color palette</h3>
 63      <h1 style="color: rgb(139, 92, 246); font-size: 1.5rem; margin: 0;">Purple heading text</h1>
 64
 65      <h3>Styled &lt;a&gt; and &lt;button&gt; with low contrast</h3>
 66      <!-- A pill-style <a> with its own opaque background and low-contrast text.
 67           Currently SAFE_TAGS skips <a> entirely; this case asserts the contrast
 68           check still runs when the element is styled as a button. Mirrors a real
 69           bug from the landing-demo: warm-charcoal text on near-black bg. -->
 70      <a href="#" class="styled-pill-low" data-test="styled-pill">Get started</a>
 71      <!-- A <button> element with its own opaque background and low-contrast text.
 72           Same SAFE_TAGS exception — buttons styled with their own background should
 73           still pass through the contrast check. -->
 74      <button class="low-contrast-button" data-test="low-contrast-button">Submit</button>
 75
 76      <h3>Tailwind color anti-patterns</h3>
 77      <div class="bg-black text-white p-4 rounded card" style="background: black; color: white;">
 78        <p>bg-black — pure black bg</p>
 79      </div>
 80      <div class="bg-blue-500 text-gray-400 p-4 rounded card" style="background: rgb(59, 130, 246);">
 81        <p style="color: rgb(156, 163, 175);">text-gray-400 on bg-blue-500 — gray on color</p>
 82      </div>
 83      <h3 class="text-purple-500 text-2xl font-bold" style="color: rgb(168, 85, 247); font-size: 1.5rem; font-weight: 700;">text-purple-500 heading</h3>
 84      <div class="bg-gradient-to-r from-purple-500 to-indigo-500 text-white p-4 rounded card" style="background: linear-gradient(to right, rgb(168, 85, 247), rgb(99, 102, 241)); color: white;">
 85        <p>Purple-to-indigo gradient</p>
 86      </div>
 87    </div>
 88
 89    <!-- ════════════════════════════════════════════════════════════
 90         RIGHT COLUMN: should pass
 91         ═══════════════════════════════════════════════════════════ -->
 92    <div class="col" data-col="pass">
 93      <h2>Should pass</h2>
 94
 95      <h3>Tinted neutrals</h3>
 96      <div class="card" style="background: rgb(26, 26, 30);">
 97        <p style="color: rgb(240, 240, 245);">Near-black bg, near-white text — tinted, not pure</p>
 98      </div>
 99      <div class="card" style="background: rgb(250, 250, 252);">
100        <p style="color: rgb(20, 20, 25);">Near-white bg, near-black text — good contrast, tinted</p>
101      </div>
102
103      <h3>Pure white surface is acceptable</h3>
104      <div class="card" style="background: #ffffff;">
105        <p style="color: #111827;">Pure #fff background with dark text — acceptable contrast</p>
106      </div>
107
108      <h3>Good contrast</h3>
109      <div class="card" style="background: rgb(30, 64, 175);">
110        <p style="color: rgb(240, 240, 245);">Near-white text on dark blue — high contrast, not pure white</p>
111      </div>
112      <div class="card" style="background: rgb(16, 185, 129);">
113        <p style="color: rgb(5, 46, 32);">Dark green text on green bg — same hue family</p>
114      </div>
115
116      <h3>Distinctive accents (not AI purple)</h3>
117      <h3 style="color: rgb(220, 38, 38); font-size: 1.25rem; margin: 0;">Red heading — not AI purple</h3>
118      <h3 style="color: rgb(180, 83, 9); font-size: 1.25rem; margin: 8px 0 0;">Amber heading — distinctive</h3>
119
120      <h3>Background-image url() ancestor (not low contrast)</h3>
121      <!-- White text on transparent bg where ancestor has a background-image.
122           The detector cannot determine the image color, so it must NOT assume
123           white and report a false low-contrast finding. -->
124      <div data-test="bg-image-ancestor" style="background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPj/HwADBwIAMCbHYQAAAABJRU5ErkJggg=='); background-size: cover; padding: 20px;">
125        <p style="color: rgb(255, 255, 255); font-size: 16px;">White text on image background — should not flag low-contrast</p>
126      </div>
127
128      <h3>Tailwind opacity-modified bg-black (not pure black)</h3>
129      <!-- bg-black/N classes are NOT pure black — they apply an alpha modifier.
130           The detector must not match these as pure-black-white. -->
131      <div class="bg-black/3 p-4 rounded card" data-test="bg-black-opacity" style="background: rgba(0,0,0,0.03);">
132        <p>bg-black/3 — 3% opacity, not pure black</p>
133      </div>
134      <div class="hover:bg-black/5 p-4 rounded card" data-test="bg-black-hover-opacity" style="background: rgba(0,0,0,0);">
135        <p>hover:bg-black/5 — hover state, not pure black</p>
136      </div>
137      <div class="bg-black/50 p-4 rounded card" data-test="bg-black-half-opacity" style="background: rgba(0,0,0,0.5);">
138        <p style="color: white;">bg-black/50 — 50% opacity, not pure black</p>
139      </div>
140
141      <h3>Inline &lt;a&gt; without own background (must remain skipped)</h3>
142      <!-- A regular text link inside a paragraph. No own background. SAFE_TAGS
143           must continue to skip these — flagging them would create noise on
144           every text link on the web. The detector exception for styled
145           buttons must not regress to flagging plain inline links. -->
146      <p>Read more about this <a href="#" class="inline-link-low" data-test="inline-link">here</a> if you want to learn more about the topic.</p>
147
148      <h3>Styled &lt;a&gt; with good contrast (must not flag)</h3>
149      <!-- A pill-style <a> styled correctly: cream text on near-black bg,
150           high contrast. The detector exception for styled buttons must
151           let the contrast check run, but this case must clearly pass. -->
152      <a href="#" class="good-pill-high" data-test="good-pill">High contrast pill</a>
153
154      <h3>Emoji on light backgrounds</h3>
155      <!-- Emojis render as multicolor glyphs regardless of CSS color, so the
156           CSS color is irrelevant for contrast. These should NOT be flagged. -->
157      <div class="card emoji-test" data-test="emoji-light" style="background: #ffe4e6;">
158        <p style="color: #ffe4e6; font-size: 24px;">⚠️ 🚨 ✨ 🎨</p>
159      </div>
160      <div class="card emoji-test" data-test="emoji-dark" style="background: #1a1a1a;">
161        <p style="color: #1a1a1a; font-size: 24px;">⚠️ 🚨 ✨ 🎨</p>
162      </div>
163    </div>
164  </div>
165  <script src="/js/detect-antipatterns-browser.js"></script>
166</body>
167</html>