@@ -190,6 +190,45 @@ def test_graph_growth_labels_are_conservative_for_exact_signed_formulas(self):
190190 expect (self .page .locator ('.path' )).to_have_count (2 )
191191 expect (self .page .get_by_role ('button' , name = 'Hide other routes' , exact = True )).to_be_visible ()
192192
193+ def test_typst_math_keeps_vector_glyphs_and_colors_in_both_themes (self ):
194+ self .visit ('problem/KSatisfiability?variant=k=K3' )
195+ expect (self .page .locator ('article.typst-detail' )).to_be_visible ()
196+ formula = self .page .locator ('.typst-math > svg' ).filter (has = self .page .locator ('g + g use' )).first
197+ expect (formula ).to_be_visible ()
198+ self .page .evaluate ('document.fonts.ready' )
199+ # Include an intentional color and a fraction-like stroke alongside the real Typst glyphs.
200+ formula .locator ('use' ).first .evaluate ("node => node.setAttribute('fill', '#e15759')" )
201+ formula .evaluate ('''svg => {
202+ const line = document.createElementNS('http://www.w3.org/2000/svg', 'path');
203+ line.setAttribute('d', 'M0 .1H4'); line.setAttribute('stroke', '#000000');
204+ line.setAttribute('stroke-width', '.1'); svg.append(line);
205+ }''' )
206+ for theme , ink , paper in [('dark' , '#eef3e9' , '#0b110d' ), ('light' , '#112011' , '#ffffff' )]:
207+ with self .subTest (theme = theme ):
208+ self .page .evaluate ('''({ink, paper}) => {
209+ document.documentElement.style.setProperty('--ink', ink);
210+ document.documentElement.style.setProperty('--paper', paper);
211+ }''' , {'ink' : ink , 'paper' : paper })
212+ expect (formula .locator ('use' ).first ).to_have_css ('fill' , 'rgb(225, 87, 89)' )
213+ actual = formula .screenshot ()
214+ # Reference the same vectors with explicit theme paint and no rasterizing filter.
215+ saved = formula .evaluate ('''(svg, ink) => {
216+ const painted = [...svg.querySelectorAll('[fill="#000000"], [stroke="#000000"]')];
217+ const saved = [svg.getAttribute('style'), painted.map(node => node.getAttribute('style'))];
218+ svg.style.filter = 'none';
219+ for (const node of painted) {
220+ if (node.getAttribute('fill') === '#000000') node.style.fill = ink;
221+ if (node.getAttribute('stroke') === '#000000') node.style.stroke = ink;
222+ }
223+ return saved;
224+ }''' , ink )
225+ self .assertEqual (actual , formula .screenshot (), 'Theme styling changed the rendered math vectors' )
226+ formula .evaluate ('''(svg, [style, paints]) => {
227+ const restore = (node, value) => value === null ? node.removeAttribute('style') : node.setAttribute('style', value);
228+ restore(svg, style);
229+ [...svg.querySelectorAll('[fill="#000000"], [stroke="#000000"]')].forEach((node, i) => restore(node, paints[i]));
230+ }''' , saved )
231+
193232 def test_accessibility_atlas (self ):
194233 self .visit ('atlas' )
195234 expect (self .page .locator ('.atlas-row' ).first ).to_be_visible ()
0 commit comments