Skip to content

lint: gsap_css_transform_conflict doubles the CSS transform text when one declaration has both translate and scale #3263

Description

@pillowsoft

Summary

In @hyperframes/lint, the gsap_css_transform_conflict rule duplicates the CSS transform text in both message and fixHint whenever a single CSS declaration contains both a translate and a scale function.

Reproduced on 0.7.107 (also present in 0.7.94).

Repro

import { lintHyperframeHtml } from '@hyperframes/lint/browser';

const html = `<!doctype html><html><head>
<script src="https://cdn.jsdelivr.net/npm/gsap@3/dist/gsap.min.js"></script>
<style>.scene-1 { transform: scale(1.08) translate3d(1.5%, 0, 0); }</style>
</head>
<body data-composition-id="repro" data-width="1920" data-height="1080" data-start="0" data-duration="5">
<div class="scene-1" data-start="0" data-duration="5">hi</div>
<script>
window.__timelines = window.__timelines || [];
const tl = gsap.timeline();
tl.to(".scene-1", { duration: 1, x: 100, scale: 1.2 });
window.__timelines.push(tl);
</script>
</body></html>`;

const res = await lintHyperframeHtml(html);
console.log(res.findings.filter(f => f.code === 'gsap_css_transform_conflict'));

Actual

".scene-1" has CSS `transform: scale(1.08) translate3d(1.5%, 0, 0) scale(1.08) translate3d(1.5%, 0, 0)`
and a GSAP tween animates x/scale. ...

The same doubling appears in fixHint ("Remove transform: scale(1.08) translate3d(1.5%, 0, 0) scale(1.08) translate3d(1.5%, 0, 0) from CSS...").

Expected

".scene-1" has CSS `transform: scale(1.08) translate3d(1.5%, 0, 0)` and a GSAP tween animates x/scale. ...

Cause

In the gsap_css_transform_conflict rule, cssTransform is built as:

const cssFromTranslate = translateProps.length > 0 ? matchCssTransform(sel, cssTranslateSelectors) : undefined;
const cssFromScale     = scaleProps.length     > 0 ? matchCssTransform(sel, cssScaleSelectors)     : undefined;
// ...
cssTransform: [cssFromTranslate, cssFromScale].filter(Boolean).join(' ')

When one declaration (transform: scale(...) translate3d(...)) matches both the translate and the scale selector maps, cssFromTranslate === cssFromScale, and the join(' ') concatenates the identical string with itself.

Suggested fix

Deduplicate before joining, e.g.:

cssTransform: [...new Set([cssFromTranslate, cssFromScale].filter(Boolean))].join(' ')

Impact

Cosmetic but user-visible: the doubled text appears in linter output and in the fixHint we surface to authors, and the suggested "Remove <doubled text> from CSS" instruction does not match any string actually present in the stylesheet.

Environment

  • @hyperframes/lint 0.7.107 (browser entry, esbuild IIFE bundle)
  • Node 22

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions