Skip to content

Tag icon CSS: default icon overrides the identifier sorting directly before it in icons.html #206

Description

@Dumbliidore

Context

I'm using Zensical tags with per-tag brand icons on a personal documentation site: most identifiers get their own icon (simple/html5, simple/javascript, simple/css, simple/rust, ...), and unmapped tags should fall back to the default icon (lucide/hash). Setup follows https://zensical.org/docs/setup/tags/#tag-icons-and-identifiers.

Reproduced on Zensical 0.0.50 and 0.0.57 (latest at time of reporting).

Bug description

When [project.theme.icon.tag] contains both default and an identifier that sorts alphabetically directly before "default" (e.g. css, but also api, bug, ...), the generated tag-icon CSS contains the default icon under the previous identifier's selector, overriding that identifier's own icon. The affected tag then renders with the wrong (default) icon.

Concretely, the build output contains two rules for the same selector, where the second one wins:

.md-tag.md-tag--css { --md-tag-icon: url('... simple/css ...'); }   /* correct */
.md-tag.md-tag--css { --md-tag-icon: url('... lucide/hash ...'); }  /* leaked "default", overrides the above */

Root cause: in zensical/ui, src/partials/icons.html (same code in dist/partials/icons.html), modifier is only assigned when type != "default":

{% if type != "default" %}
  {% set modifier = ".md-tag--" ~ type %}
{% endif %}

In Jinja2, {% set %} inside a {% for %} body persists across iterations, so when the loop reaches "default" it reuses the previous iteration's selector. Since config.theme.icon.tag is iterated in alphabetical order, exactly the identifier preceding "default" is clobbered. If default sorts first, the bug does not manifest.

Workaround (what I use for now): override the partial via custom_dir with a fixed template that resets modifier = "" at the start of each iteration — or omit the default entry entirely (unmapped tags then show no icon).

Related links

Reproduction

Minimal reproduction attached as bug-report-repro.zip (2 files, well below 1 MB):

zensical.toml

[project]
site_name = "Tag icon default bug repro"

[project.theme.icon.tag]
default = "lucide/hash"
css = "simple/css"

[project.extra.tags]
CSS = "css"

docs/index.md

---
tags:
  - CSS
---

# Tag icon bug reproduction

No customizations are involved (no custom_dir, extra_css, or extra_javascript).

bug-report-repro.zip

Steps to reproduce

  1. Unzip the reproduction and open the folder in a terminal.
  2. Install Zensical and run zensical build.
  3. Open the generated site/index.html and locate the <style>:root{...} block near the top.
  4. Note that there are two .md-tag.md-tag--css{...} rules: the first embeds the CSS logo (simple/css), the second embeds lucide/hash.
  5. Open the page in a browser (or just look at the CSS cascade): because both rules share the same specificity, the second rule wins and the CSS tag shows the hash icon instead of the CSS logo.

Expected: the default entry emits the plain .md-tag{...} selector, so only tags without an associated identifier get the default icon.

Browser

No response

Before submitting

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugIssue reports a bugresolvedIssue is resolved, yet unreleased if open

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions