Skip to content

[Feature]: Document CSS-only disclosure + click-to-pin and hover tooltips #162

Description

@aaahariri

What are you trying to show?

Diagrams where a second layer of detail exists but does not belong in the default view.

Two concrete cases:

  1. Progressive detail. One diagram serving two audiences — the reader sees nine boxes, and can open one box to see its ports, retry policy, or SLA. This is the pressure valve for the §7 complexity budget: instead of splitting into overview + detail as two files, the detail lives in the same file, closed by default.
  2. Secondary annotation. A metric, a threshold, or a topology sketch that would push the static diagram past 4/10 density if drawn inline, but that a reader may want on demand.

Today the skill has no documented answer for either. The only interaction it describes is step mode playback in references/animation.md, which is for ordered explanation — a different job from on-demand detail.

Proposed solution

A new optional primitive, references/primitive-disclosure.md, alongside the existing primitive-annotation.md / primitive-sketchy.md / primitive-terminal.md, plus one row in SKILL.md §6's optional-primitives list.

It would already be legal. I tested four approaches against the skill's own skills/diagram-design/scripts/self_check.py (v2.6.7):

Approach Result
onclick attribute on a node group FAIL — executable attribute onclick on <g>
Extra <script> with a click listener FAIL — script 1 must carry only the canonical data-diagram-controls attribute
<a href="#id"> around a node + .detail:target { opacity: 1 } OK
CSS :hover revealing an SVG overlay group OK
Nested <title> inside the node group (native tooltip) OK

So the last three pass with zero JavaScript and zero remote references. The gap is documentation, not capability — nothing in SKILL.md or the 53 reference files mentions :target, hover, or disclosure, so an agent will never produce these patterns unless a user asks for them by name.

Click — :target

<a href="#detail-ci" aria-label="Show detail for CI checks">
  <rect class="box" .../><text ...>CI checks</text>
</a>
...
<g class="detail" id="detail-ci"> ...detail lines... </g>
.detail { opacity: 0; }
.detail:target { opacity: 1; }

Hover — scoped :hover

<g class="hot">
  <rect class="box" .../><text ...>Deploy</text>
  <g class="tip" aria-hidden="true"> ...panel... </g>
</g>
.tip { opacity: 0; pointer-events: none; }
.hot:hover .tip { opacity: 1; }

The hover panel can hold anything SVG can hold, including a small nested diagram (I built one showing a load balancer fanning out to three pods).

Why this fits the philosophy — and where it does not

I want to be straight that this is in tension with the project's stance, and I am raising it rather than assuming the answer.

Where it fits:

  • Zero JavaScript, so the one-script / canonical-controller rule is untouched.
  • No remote references, so the single-file guarantee holds.
  • :target state lives in the URL, so a specific open panel is linkable and reproducible — the same determinism property animation.md requires of ?motion=step&step=N.

Where it does not, and what a contract could require:

  • Hover has no touch equivalent and disappears from PNG/SVG export and print. Any hover content would have to be strictly redundant — an elaboration of something already labeled, never the only place a fact appears.
  • Click hides content by default, which cuts against "the source is complete." The mitigation is the same contract animation.md already uses: the closed state must be meaningful on its own, and a static capture must expose every panel.
  • It could be abused to defeat the 9-node budget by hiding a 30-node diagram inside tooltips. A stated cap (say ≤4 disclosure targets, ≤3 lines each, no nested disclosure) would keep it honest.

If the verdict is that on-demand detail simply is not what this skill is for, that is a completely reasonable answer, and documenting the rejection — a line in the anti-patterns table saying disclosure is deliberately out of scope — would be almost as useful as adding it. Right now it is neither blessed nor forbidden, and it passes the linter, which means people will build it inconsistently.

I have working examples of all three techniques (click-to-pin, hover text tooltip, hover tooltip with a nested diagram) that pass self_check.py, and I'm glad to open a PR against a shape you specify. Wanted your call on the philosophy before writing the reference file.

Scope

New or changed primitive

Design-system fit

  • I've checked the anti-patterns and 'When not to use' — this still seems worth drawing
  • I'm willing to contribute the implementation or pair with a maintainer

Activity

  1. cathrynlavery commented on Sep 1, 2026

    @cathrynlavery
    Owner

    This is one of the best-argued issues this repo has received — you tested against self_check.py, cited the exact failure strings, and named the tension honestly. I reproduced your table locally and it's exactly right: :target, scoped :hover, and nested <title> all pass; onclick and a second script fail. You're also right that 'passes the linter but undocumented' is the worst of the three possible states.

    The call: disclosure is out of scope, and we'll document that rather than leave it ambiguous.

    The deciding principle isn't the script rule — you're correct that CSS-only techniques honor it — it's static-frame completeness. ADR 0003's contract is that without JavaScript and under reduced motion, the file shows the complete static frame; PNG export rasterizes the default state, and these diagrams live on in decks, posts, and print where hover and :target simply don't exist. Content that exists only behind interaction silently disappears from every one of those surfaces, and 'strictly redundant only' is a contract we could state but not verify — this repo's stance (ADR 0005) is that placement rules get verified, not reviewed, and there's no verifier for redundancy. The §7 pressure valve stays what SKILL.md already prescribes: split into overview + detail as two diagrams.

    One carve-out: a nested <title> inside a node group (the native tooltip) carrying a short elaboration of something already labeled is fine — it degrades to nothing rather than to missing meaning.

    So the PR I'd welcome from you, if you're still game: an anti-pattern row in SKILL.md §4 (hidden-by-default disclosure content — hover/:target panels — because exports, print, and touch lose it), a matching one-line note in references/animation.md distinguishing on-demand detail from ordered explanation, and the <title> allowance stated next to it. Plus the synchronized manifest bump every PR needs. Documenting the rejection, as you said, is almost as useful as the feature — and it makes your linter finding permanent policy instead of an accident.

  2. cathrynlavery commented on Oct 5, 2026

    @cathrynlavery
    Owner

    One correction to my September 1 comment: PRs no longer bump the manifest version. Since ADR 0009, a workflow bumps versions on main after each merge, and the version gate fails any PR that changes them. Leave the plugin.json versions alone. The rest of the scope above stands.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions