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>Overlay Positioning Edge Cases</title>
7 <style>
8 body {
9 font-family: system-ui, sans-serif;
10 background: #f9fafb;
11 padding: 2rem;
12 max-width: 900px;
13 margin: 0 auto;
14 }
15 h1 { font-size: 2rem; margin-bottom: 0.5rem; }
16 h2 { font-size: 1.125rem; margin: 2.5rem 0 0.75rem; color: #6b7280; border-bottom: 1px solid #e5e7eb; padding-bottom: 0.5rem; }
17 .scenario { margin-bottom: 2rem; padding: 1rem; border: 1px dashed #d1d5db; border-radius: 8px; }
18 .scenario-label { font-size: 0.75rem; color: #6b7280; margin-bottom: 0.5rem; text-transform: uppercase; letter-spacing: 0.05em; }
19
20 /* ============================================
21 1. TRANSFORM ANCESTOR
22 position:absolute overlays on body use document coords,
23 but getBoundingClientRect is viewport-relative.
24 Transforms on ancestors create new stacking contexts.
25 ============================================ */
26 .transform-container {
27 transform: translateY(0);
28 padding: 1rem;
29 background: #f3f4f6;
30 border-radius: 8px;
31 }
32 .transform-rotate {
33 transform: rotate(0deg);
34 padding: 1rem;
35 background: #f3f4f6;
36 border-radius: 8px;
37 }
38 .transform-scale {
39 transform: scale(1);
40 padding: 1rem;
41 background: #f3f4f6;
42 border-radius: 8px;
43 }
44 .transform-nested-outer {
45 transform: translateX(0);
46 padding: 1rem;
47 background: #e5e7eb;
48 border-radius: 8px;
49 }
50 .transform-nested-inner {
51 transform: translateY(0);
52 padding: 1rem;
53 background: #d1d5db;
54 border-radius: 8px;
55 }
56
57 /* ============================================
58 2. CLOSED <details>
59 Elements inside collapsed details may report
60 a bounding rect but aren't visually present.
61 ============================================ */
62 details { margin-bottom: 1rem; }
63 summary { cursor: pointer; font-weight: 600; }
64
65 /* ============================================
66 3. STICKY / FIXED POSITIONING
67 Sticky elements change their containing block
68 behavior during scroll.
69 ============================================ */
70 .sticky-container {
71 height: 200px;
72 overflow-y: auto;
73 border: 1px solid #d1d5db;
74 border-radius: 8px;
75 }
76 .sticky-header {
77 position: sticky;
78 top: 0;
79 background: #1f2937;
80 color: white;
81 padding: 0.5rem 1rem;
82 z-index: 10;
83 }
84 .sticky-content {
85 padding: 1rem;
86 }
87
88 /* ============================================
89 4. OVERFLOW HIDDEN
90 Elements that extend beyond an overflow:hidden
91 parent are visually clipped but still in the DOM.
92 ============================================ */
93 .overflow-hidden-container {
94 overflow: hidden;
95 height: 60px;
96 border: 1px solid #d1d5db;
97 border-radius: 8px;
98 padding: 0.5rem 1rem;
99 }
100
101 /* ============================================
102 5. ABSOLUTE / RELATIVE POSITIONING
103 Elements that are offset from their normal flow
104 position via top/left.
105 ============================================ */
106 .relative-offset {
107 position: relative;
108 top: 20px;
109 left: 40px;
110 padding: 1rem;
111 background: #f3f4f6;
112 border-radius: 8px;
113 }
114 .absolute-container {
115 position: relative;
116 height: 120px;
117 background: #f3f4f6;
118 border-radius: 8px;
119 }
120 .absolute-child {
121 position: absolute;
122 bottom: 10px;
123 right: 10px;
124 }
125
126 /* ============================================
127 6. FLEXBOX / GRID CENTERING
128 Elements centered via flex/grid may have
129 unexpected positions vs. document flow.
130 ============================================ */
131 .flex-center {
132 display: flex;
133 justify-content: center;
134 align-items: center;
135 height: 120px;
136 background: #f3f4f6;
137 border-radius: 8px;
138 }
139 .grid-center {
140 display: grid;
141 place-items: center;
142 height: 120px;
143 background: #f3f4f6;
144 border-radius: 8px;
145 }
146
147 /* ============================================
148 7. WILL-CHANGE / CONTAIN / FILTER
149 These CSS properties create new containing
150 blocks, similar to transforms.
151 ============================================ */
152 .will-change-container {
153 will-change: transform;
154 padding: 1rem;
155 background: #f3f4f6;
156 border-radius: 8px;
157 }
158 .contain-container {
159 contain: layout;
160 padding: 1rem;
161 background: #f3f4f6;
162 border-radius: 8px;
163 }
164 .filter-container {
165 filter: brightness(1);
166 padding: 1rem;
167 background: #f3f4f6;
168 border-radius: 8px;
169 }
170 .backdrop-filter-container {
171 backdrop-filter: blur(0px);
172 padding: 1rem;
173 background: #f3f4f6;
174 border-radius: 8px;
175 }
176
177 /* ============================================
178 8. MARGIN COLLAPSE / NEGATIVE MARGINS
179 Elements with collapsed or negative margins
180 can shift from expected position.
181 ============================================ */
182 .negative-margin-container {
183 padding: 2rem;
184 background: #f3f4f6;
185 border-radius: 8px;
186 }
187 .negative-margin-child {
188 margin-top: -1rem;
189 margin-left: -1rem;
190 }
191
192 /* ============================================
193 Anti-pattern triggers (tiny-text, cramped, ai-color)
194 used across scenarios.
195 ============================================ */
196 .tiny { font-size: 10px; color: #374151; line-height: 1.4; }
197 .cramped-box { border: 1px solid #d1d5db; padding: 3px; font-size: 14px; border-radius: 4px; }
198 .ai-btn { background: linear-gradient(135deg, #8b5cf6, #6366f1); color: white; padding: 8px 16px; border: none; border-radius: 6px; font-size: 14px; cursor: pointer; }
199 .side-tab-card { border-left: 4px solid #8b5cf6; padding: 1rem; background: white; border-radius: 0 8px 8px 0; }
200 </style>
201</head>
202<body>
203
204<h1>Overlay Positioning Edge Cases</h1>
205<p>Each scenario places a detectable anti-pattern inside a layout context that can cause overlay misalignment.</p>
206
207<!-- ═══════════════════════════════════════════
208 1. TRANSFORM ANCESTORS
209 ═══════════════════════════════════════════ -->
210<h2>1. Transform Ancestors</h2>
211
212<div class="scenario">
213 <div class="scenario-label">1a. translateY(0) container</div>
214 <div class="transform-container">
215 <span class="tiny">This tiny text is inside a transform: translateY(0) container. The overlay should frame this text precisely.</span>
216 </div>
217</div>
218
219<div class="scenario">
220 <div class="scenario-label">1b. rotate(0deg) container</div>
221 <div class="transform-rotate">
222 <span class="tiny">Tiny text inside a transform: rotate(0deg) container.</span>
223 </div>
224</div>
225
226<div class="scenario">
227 <div class="scenario-label">1c. scale(1) container</div>
228 <div class="transform-scale">
229 <div class="cramped-box">Cramped text inside a transform: scale(1) container.</div>
230 </div>
231</div>
232
233<div class="scenario">
234 <div class="scenario-label">1d. Nested transforms</div>
235 <div class="transform-nested-outer">
236 <div class="transform-nested-inner">
237 <span class="tiny">Tiny text nested inside two levels of transformed parents.</span>
238 </div>
239 </div>
240</div>
241
242<div class="scenario">
243 <div class="scenario-label">1e. Transform + absolute child</div>
244 <div class="transform-container" style="position: relative; height: 100px;">
245 <div style="position: absolute; bottom: 8px; right: 8px;">
246 <span class="tiny">Absolutely positioned tiny text inside transformed parent.</span>
247 </div>
248 </div>
249</div>
250
251<!-- ═══════════════════════════════════════════
252 2. CLOSED <details>
253 ═══════════════════════════════════════════ -->
254<h2>2. Closed Details</h2>
255
256<div class="scenario">
257 <div class="scenario-label">2a. Closed details with anti-pattern inside</div>
258 <details>
259 <summary>Click to expand (closed by default)</summary>
260 <span class="tiny">This tiny text is hidden inside a closed details element. No overlay should appear for it.</span>
261 <div class="cramped-box">Cramped text also hidden inside closed details.</div>
262 </details>
263</div>
264
265<div class="scenario">
266 <div class="scenario-label">2b. Open details with anti-pattern inside</div>
267 <details open>
268 <summary>This details is open</summary>
269 <span class="tiny">This tiny text IS visible because details is open. Overlay should frame it correctly.</span>
270 </details>
271</div>
272
273<div class="scenario">
274 <div class="scenario-label">2c. Nested details (outer open, inner closed)</div>
275 <details open>
276 <summary>Outer details (open)</summary>
277 <p>Some visible content.</p>
278 <details>
279 <summary>Inner details (closed)</summary>
280 <span class="tiny">Hidden tiny text inside nested closed details.</span>
281 </details>
282 </details>
283</div>
284
285<div class="scenario">
286 <div class="scenario-label">2d. Transform inside closed details</div>
287 <details>
288 <summary>Closed with transform inside</summary>
289 <div class="transform-container">
290 <span class="tiny">Tiny text inside transform inside closed details. Double trouble.</span>
291 </div>
292 </details>
293</div>
294
295<!-- ═══════════════════════════════════════════
296 3. STICKY / FIXED
297 ═══════════════════════════════════════════ -->
298<h2>3. Sticky and Fixed Positioning</h2>
299
300<div class="scenario">
301 <div class="scenario-label">3a. Sticky header inside scrollable container</div>
302 <div class="sticky-container">
303 <div class="sticky-header">
304 <span class="tiny">Tiny text in a sticky header.</span>
305 </div>
306 <div class="sticky-content">
307 <p>Scroll this container to test sticky behavior.</p>
308 <p>More content to enable scrolling.</p>
309 <p>Even more content here.</p>
310 <div class="cramped-box">Cramped text below the sticky header.</div>
311 <p>Additional content.</p>
312 <p>More filler text.</p>
313 </div>
314 </div>
315</div>
316
317<div class="scenario">
318 <div class="scenario-label">3b. Fixed footer (stays at bottom of viewport)</div>
319 <p>The fixed footer at the bottom of the page has tiny text. Its overlay should stay fixed too.</p>
320</div>
321
322<!-- Actual fixed footer -- outside scenario container so it's truly fixed -->
323<div style="position: fixed; bottom: 0; left: 0; right: 0; background: #1f2937; color: white; padding: 0.5rem 1rem; z-index: 50;">
324 <span class="tiny">Tiny text in a fixed footer -- overlay should track this on scroll.</span>
325</div>
326
327<!-- ═══════════════════════════════════════════
328 4. OVERFLOW HIDDEN
329 ═══════════════════════════════════════════ -->
330<h2>4. Overflow Hidden</h2>
331
332<div class="scenario">
333 <div class="scenario-label">4a. Content clipped by overflow:hidden</div>
334 <div class="overflow-hidden-container">
335 <p>This paragraph is visible.</p>
336 <span class="tiny">This tiny text is below the overflow cutoff, clipped but still in DOM.</span>
337 <p>This content is also clipped away.</p>
338 </div>
339</div>
340
341<div class="scenario">
342 <div class="scenario-label">4b. overflow:hidden + transform ancestor</div>
343 <div class="transform-container">
344 <div class="overflow-hidden-container">
345 <p>Visible inside transform + overflow.</p>
346 <span class="tiny">Clipped tiny text inside a transformed overflow:hidden container.</span>
347 </div>
348 </div>
349</div>
350
351<!-- ═══════════════════════════════════════════
352 5. ABSOLUTE / RELATIVE OFFSETS
353 ═══════════════════════════════════════════ -->
354<h2>5. Position Offsets</h2>
355
356<div class="scenario">
357 <div class="scenario-label">5a. Relative position with top/left offset</div>
358 <div class="relative-offset">
359 <span class="tiny">This tiny text is shifted via position:relative + top/left offset.</span>
360 </div>
361 <div style="height: 30px;"></div><!-- spacer for the offset -->
362</div>
363
364<div class="scenario">
365 <div class="scenario-label">5b. Absolute child in relative parent</div>
366 <div class="absolute-container">
367 <p>Normal flow content at top.</p>
368 <div class="absolute-child">
369 <span class="tiny">Absolutely positioned tiny text at bottom-right.</span>
370 </div>
371 </div>
372</div>
373
374<div class="scenario">
375 <div class="scenario-label">5c. Negative top offset</div>
376 <div style="padding-top: 2rem;">
377 <div style="position: relative; top: -1rem;">
378 <span class="tiny">Tiny text pulled upward via negative top offset.</span>
379 </div>
380 </div>
381</div>
382
383<!-- ═══════════════════════════════════════════
384 6. FLEX / GRID CENTERING
385 ═══════════════════════════════════════════ -->
386<h2>6. Flex and Grid Centering</h2>
387
388<div class="scenario">
389 <div class="scenario-label">6a. Flex-centered anti-pattern</div>
390 <div class="flex-center">
391 <span class="tiny">Centered tiny text inside a flex container.</span>
392 </div>
393</div>
394
395<div class="scenario">
396 <div class="scenario-label">6b. Grid-centered anti-pattern</div>
397 <div class="grid-center">
398 <div class="cramped-box">Cramped text inside a grid-centered container.</div>
399 </div>
400</div>
401
402<div class="scenario">
403 <div class="scenario-label">6c. Flex with transform</div>
404 <div class="flex-center transform-container">
405 <button class="ai-btn">AI Gradient Button in Flex + Transform</button>
406 </div>
407</div>
408
409<!-- ═══════════════════════════════════════════
410 7. WILL-CHANGE / CONTAIN / FILTER
411 ═══════════════════════════════════════════ -->
412<h2>7. Containing Block Creators</h2>
413
414<div class="scenario">
415 <div class="scenario-label">7a. will-change: transform</div>
416 <div class="will-change-container">
417 <span class="tiny">Tiny text inside a will-change: transform container.</span>
418 </div>
419</div>
420
421<div class="scenario">
422 <div class="scenario-label">7b. contain: layout</div>
423 <div class="contain-container">
424 <span class="tiny">Tiny text inside a contain: layout container.</span>
425 </div>
426</div>
427
428<div class="scenario">
429 <div class="scenario-label">7c. filter: brightness(1)</div>
430 <div class="filter-container">
431 <span class="tiny">Tiny text inside a filter container (creates containing block).</span>
432 </div>
433</div>
434
435<div class="scenario">
436 <div class="scenario-label">7d. backdrop-filter</div>
437 <div class="backdrop-filter-container">
438 <span class="tiny">Tiny text inside a backdrop-filter container.</span>
439 </div>
440</div>
441
442<!-- ═══════════════════════════════════════════
443 8. NEGATIVE MARGINS
444 ═══════════════════════════════════════════ -->
445<h2>8. Margin Collapse and Negative Margins</h2>
446
447<div class="scenario">
448 <div class="scenario-label">8a. Negative margin shifting element position</div>
449 <div class="negative-margin-container">
450 <div class="negative-margin-child">
451 <span class="tiny">Tiny text pulled out of its parent via negative margins.</span>
452 </div>
453 </div>
454</div>
455
456<!-- ═══════════════════════════════════════════
457 9. COMBINATION SCENARIOS
458 ═══════════════════════════════════════════ -->
459<h2>9. Combined Edge Cases</h2>
460
461<div class="scenario">
462 <div class="scenario-label">9a. Transform + sticky + overflow</div>
463 <div class="transform-container">
464 <div class="sticky-container">
465 <div class="sticky-header">
466 <span class="tiny">Tiny text in sticky header inside transform inside scrollable overflow.</span>
467 </div>
468 <div class="sticky-content">
469 <p>Scroll content line 1.</p>
470 <p>Scroll content line 2.</p>
471 <p>Scroll content line 3.</p>
472 <div class="cramped-box">Cramped box scrolled below sticky.</div>
473 <p>Scroll content line 4.</p>
474 <p>Scroll content line 5.</p>
475 </div>
476 </div>
477 </div>
478</div>
479
480<div class="scenario">
481 <div class="scenario-label">9b. Closed details + transform + flex</div>
482 <details>
483 <summary>Closed combo scenario</summary>
484 <div class="flex-center transform-container">
485 <span class="tiny">Triple-nested: closed details > flex > transform > tiny text.</span>
486 </div>
487 </details>
488</div>
489
490<div class="scenario">
491 <div class="scenario-label">9c. Grid + absolute + transform</div>
492 <div class="grid-center" style="position: relative; height: 150px;">
493 <div class="transform-container" style="position: absolute; top: 10px; left: 10px; right: 10px;">
494 <span class="tiny">Absolute inside grid inside transform: overlays must track all three contexts.</span>
495 </div>
496 <div style="position: absolute; bottom: 10px; right: 10px;">
497 <div class="cramped-box">Absolute cramped box at bottom-right of grid.</div>
498 </div>
499 </div>
500</div>
501
502<div class="scenario">
503 <div class="scenario-label">9d. Overflow hidden + absolute breakout</div>
504 <div style="position: relative; overflow: hidden; height: 80px; background: #f3f4f6; border-radius: 8px; padding: 1rem;">
505 <p>Visible content.</p>
506 <div style="position: absolute; bottom: -20px; left: 0; right: 0;">
507 <span class="tiny">Absolute-positioned tiny text that breaks out below overflow:hidden.</span>
508 </div>
509 </div>
510</div>
511
512<div class="scenario">
513 <div class="scenario-label">9e. Side-tab card inside transform + relative offset</div>
514 <div class="transform-container">
515 <div style="position: relative; left: 50px;">
516 <div class="side-tab-card">
517 This card has a side-tab border accent and is offset inside a transform container.
518 </div>
519 </div>
520 </div>
521</div>
522
523<script src="/js/detect-antipatterns-browser.js"></script>
524</body>
525</html>