From c2d47202c63ed7b4161a36656a621a0a033f575c Mon Sep 17 00:00:00 2001 From: ll-nick <68419636+ll-nick@users.noreply.github.com> Date: Thu, 17 Sep 2026 10:04:08 +0200 Subject: [PATCH] Remove stale CSS classes to prevent cascade breaking styling --- src/inkflow/colors.py | 15 +++++++++++++-- tests/test_colors.py | 15 +++++++++++++++ 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/src/inkflow/colors.py b/src/inkflow/colors.py index de788970..e3afc48b 100644 --- a/src/inkflow/colors.py +++ b/src/inkflow/colors.py @@ -264,7 +264,20 @@ def colorize_element( changed = False existing_classes: list[str] = str(el.get("class") or "").split() + style_attr = str(el.get("style") or "") + decls = parse_style(style_attr) if style_attr else [] + # An inline style declaration overrides a same-named presentation attribute + # per the CSS cascade, so a fill/stroke attribute shadowed by one is dead and + # must not be classified — otherwise both the stale attribute color and the + # actual style color get classes, and cascade order picks the wrong one. + styled_props = {prop for prop, _ in decls if prop in ("fill", "stroke")} + for prop in ("fill", "stroke"): + if prop in styled_props: + if prop in el.attrib: + del el.attrib[prop] + changed = True + continue val = str(el.get(prop) or "").lower().strip() if not val or val in ("none", "inherit", "currentcolor"): continue @@ -276,9 +289,7 @@ def colorize_element( del el.attrib[prop] changed = True - style_attr = str(el.get("style") or "") if style_attr: - decls = parse_style(style_attr) remaining: list[tuple[str, str]] = [] for prop, val in decls: if prop in ("fill", "stroke"): diff --git a/tests/test_colors.py b/tests/test_colors.py index e947bba2..8a8c445d 100644 --- a/tests/test_colors.py +++ b/tests/test_colors.py @@ -163,3 +163,18 @@ def test_colorize_svg_handles_uppercase_hex() -> None: result, changed = colorize_svg(svg, _HEX_MAP) assert changed assert "inkflow-fill-accent" in result + + +def test_colorize_svg_style_fill_overrides_stale_attribute() -> None: + """A ``style`` fill wins the CSS cascade over a ``fill`` attribute, so a + stale attribute left over from a copy-paste must not get its own class.""" + hex_map = hex_to_class_map({"accent": "#cba6f7", "teal": "#cdd6f4"}) + svg = ( + '' + '' + ) + result, changed = colorize_svg(svg, hex_map) + assert changed + assert "inkflow-fill-accent" in result + assert "inkflow-fill-teal" not in result + assert 'fill="#cdd6f4"' not in result