From 6ab54a0955bcda77fb4742b2cb1ce688a959846b Mon Sep 17 00:00:00 2001 From: Josh Ribakoff Date: Wed, 21 Jan 2026 22:26:17 -0800 Subject: [PATCH] Fix video flicker by using selective CSS transitions The animation was experiencing subtle flickering/dimming at step boundaries. Root cause: The clearState() function removes all CSS classes before applying new ones. With 'transition: all', backgrounds would visibly transition back to default (#0f3460) before the new state colors were applied. Fix: Change CSS transitions to only animate specific properties: - .array-cell: transition transform and box-shadow only (not background/border) - .pointer: transition opacity only - .message-display: remove transition entirely This allows background/border colors to change instantly while preserving smooth scale and glow animations. Also adds regression tests that verify templates don't use 'transition: all' on elements that have clearState() applied to them. Co-Authored-By: Claude Opus 4.5 --- templates/array_animation.html | 11 ++++-- tests/test_animation_adapters.py | 60 ++++++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+), 3 deletions(-) diff --git a/templates/array_animation.html b/templates/array_animation.html index 736ab1a..dfd53d4 100644 --- a/templates/array_animation.html +++ b/templates/array_animation.html @@ -66,7 +66,10 @@ border: 3px solid #16c79a; border-radius: 12px; position: relative; - transition: all 0.3s ease; + /* Only transition transform/box-shadow, NOT background/border. + This prevents the flicker caused by clearing all classes + before applying new ones - background now changes instantly. */ + transition: transform 0.3s ease, box-shadow 0.3s ease; } .array-cell.highlight-left { @@ -113,7 +116,9 @@ padding: 4px 10px; border-radius: 6px; opacity: 0; - transition: all 0.4s ease; + /* Only transition opacity, not position-related properties. + Prevents flicker when pointers move between cells. */ + transition: opacity 0.3s ease; } .pointer.left-pointer { @@ -135,7 +140,7 @@ color: #f7d716; text-align: center; min-height: 36px; - transition: opacity 0.3s ease; + /* No transition - instant text updates prevent flicker */ } .step-indicator { diff --git a/tests/test_animation_adapters.py b/tests/test_animation_adapters.py index 4d6ca1a..45cc65b 100644 --- a/tests/test_animation_adapters.py +++ b/tests/test_animation_adapters.py @@ -325,6 +325,66 @@ def test_record_respects_duration(self, simple_html: str): assert result.stat().st_size > 1000 # Should have meaningful content +class TestTemplateCSS: + """Tests for CSS in animation templates to prevent regressions.""" + + @pytest.fixture + def template_path(self) -> Path: + """Path to the array animation template.""" + return Path(__file__).parent.parent / "templates" / "array_animation.html" + + def test_no_transition_all_on_array_cell(self, template_path: Path): + """Array cells should not use 'transition: all' to prevent flicker. + + When using 'transition: all', the clearState() function causes a visible + flicker because background colors transition back to default before the + new state is applied. Instead, only specific properties should be animated. + """ + content = template_path.read_text() + + # Extract the .array-cell CSS rule + import re + array_cell_match = re.search( + r'\.array-cell\s*\{[^}]+\}', + content, + re.DOTALL + ) + assert array_cell_match, ".array-cell CSS rule not found" + + array_cell_css = array_cell_match.group(0) + + # Should NOT have 'transition: all' + assert 'transition: all' not in array_cell_css, ( + "Found 'transition: all' in .array-cell CSS. " + "This causes flicker during state changes. " + "Use specific properties like 'transition: transform, box-shadow' instead." + ) + + # SHOULD have some transition for smooth animations + assert 'transition:' in array_cell_css or 'transition :' in array_cell_css, ( + ".array-cell should have transition for transform/box-shadow" + ) + + def test_no_transition_all_on_pointer(self, template_path: Path): + """Pointer elements should not use 'transition: all' to prevent flicker.""" + content = template_path.read_text() + + import re + pointer_match = re.search( + r'\.pointer\s*\{[^}]+\}', + content, + re.DOTALL + ) + assert pointer_match, ".pointer CSS rule not found" + + pointer_css = pointer_match.group(0) + + assert 'transition: all' not in pointer_css, ( + "Found 'transition: all' in .pointer CSS. " + "This can cause flicker when pointers move between cells." + ) + + # Mark slow tests so they can be skipped with: pytest -m "not slow" def pytest_configure(config): config.addinivalue_line("markers", "slow: marks tests as slow (deselect with '-m \"not slow\"')")