Skip to content

feat(css): add support for scroll-driven animation keyframe selectors#9788

Merged
ematipico merged 2 commits intobiomejs:mainfrom
MeGaNeKoS:fix/css-keyframes-timeline-range-7760
Apr 3, 2026
Merged

feat(css): add support for scroll-driven animation keyframe selectors#9788
ematipico merged 2 commits intobiomejs:mainfrom
MeGaNeKoS:fix/css-keyframes-timeline-range-7760

Conversation

@MeGaNeKoS
Copy link
Copy Markdown
Contributor

Summary

Fixed #7760: CSS keyframes with timeline-range-name not supported.

Problem

The CSS parser only recognized from, to, and percentage values (e.g. 50%) as valid keyframe selectors. The CSS Scroll-driven Animations spec adds <timeline-range-name> <percentage> selectors, which Biome rejected with a parse error.

/* This was rejected by Biome */
@keyframes reveal-up {
    entry 0%   { opacity: 0; }
    entry 60%  { opacity: 1; }
    exit 60%   { opacity: 1; }
    exit 100%  { opacity: 0; }
}

Approach

  • Added a new CssKeyframesRangeSelector node type to the CSS grammar (css.ungram) containing a name token and a percentage
  • Updated the keyframe selector parser to recognize timeline-range-name identifiers (cover, contain, entry, exit, entry-crossing, exit-crossing) followed by a percentage
  • Added formatter support for the new node
  • Updated error messages to include timeline-range-names as valid options

Test plan

  • New parser test (at_rule_keyframe_range.css) covering all 6 timeline-range-names, mixed selectors, and various combinations
  • Existing keyframe tests still pass (error snapshots updated for new error message)
  • Format and lint pass

AI disclosure

Claude Code was used to understand the problem, research the CSS Scroll-driven Animations spec, write unit tests, and run the PR requirement pipeline.

@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented Apr 3, 2026

🦋 Changeset detected

Latest commit: dd0180d

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 13 packages
Name Type
@biomejs/biome Patch
@biomejs/cli-win32-x64 Patch
@biomejs/cli-win32-arm64 Patch
@biomejs/cli-darwin-x64 Patch
@biomejs/cli-darwin-arm64 Patch
@biomejs/cli-linux-x64 Patch
@biomejs/cli-linux-arm64 Patch
@biomejs/cli-linux-x64-musl Patch
@biomejs/cli-linux-arm64-musl Patch
@biomejs/wasm-web Patch
@biomejs/wasm-bundler Patch
@biomejs/wasm-nodejs Patch
@biomejs/backend-jsonrpc Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@github-actions github-actions Bot added A-Parser Area: parser A-Formatter Area: formatter A-Tooling Area: internal tools L-CSS Language: CSS and super languages L-Grit Language: GritQL labels Apr 3, 2026
@codspeed-hq
Copy link
Copy Markdown

codspeed-hq Bot commented Apr 3, 2026

Merging this PR will not alter performance

✅ 29 untouched benchmarks
⏩ 227 skipped benchmarks1


Comparing MeGaNeKoS:fix/css-keyframes-timeline-range-7760 (dd0180d) with main (94fdf1a)

Open in CodSpeed

Footnotes

  1. 227 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

@MeGaNeKoS MeGaNeKoS force-pushed the fix/css-keyframes-timeline-range-7760 branch from 4bbdcbd to d103b68 Compare April 3, 2026 05:44
@MeGaNeKoS MeGaNeKoS marked this pull request as ready for review April 3, 2026 06:27
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 3, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 4ad72503-a1f5-4a6f-bddd-f8eb7aeef93d

📥 Commits

Reviewing files that changed from the base of the PR and between 62b44e2 and dd0180d.

⛔ Files ignored due to path filters (10)
  • crates/biome_css_factory/src/generated/node_factory.rs is excluded by !**/generated/**, !**/generated/** and included by **
  • crates/biome_css_factory/src/generated/syntax_factory.rs is excluded by !**/generated/**, !**/generated/** and included by **
  • crates/biome_css_parser/tests/css_test_suite/error/at_rule/at_rule_keyframes/at_rule_keyframes.css.snap is excluded by !**/*.snap and included by **
  • crates/biome_css_parser/tests/css_test_suite/error/at_rule/at_rule_keyframes/at_rule_keyframes_1.css.snap is excluded by !**/*.snap and included by **
  • crates/biome_css_parser/tests/css_test_suite/error/at_rule/at_rule_keyframes/at_rule_keyframes_range_error.css.snap is excluded by !**/*.snap and included by **
  • crates/biome_css_parser/tests/css_test_suite/ok/at_rule/at_rule_keyframe_range.css.snap is excluded by !**/*.snap and included by **
  • crates/biome_css_syntax/src/generated/kind.rs is excluded by !**/generated/**, !**/generated/** and included by **
  • crates/biome_css_syntax/src/generated/macros.rs is excluded by !**/generated/**, !**/generated/** and included by **
  • crates/biome_css_syntax/src/generated/nodes.rs is excluded by !**/generated/**, !**/generated/** and included by **
  • crates/biome_css_syntax/src/generated/nodes_mut.rs is excluded by !**/generated/**, !**/generated/** and included by **
📒 Files selected for processing (13)
  • .changeset/feat-css-keyframes-timeline-range.md
  • crates/biome_css_formatter/src/css/any/keyframes_selector.rs
  • crates/biome_css_formatter/src/css/selectors/keyframes_range_selector.rs
  • crates/biome_css_formatter/src/css/selectors/mod.rs
  • crates/biome_css_formatter/src/generated.rs
  • crates/biome_css_parser/src/lexer/mod.rs
  • crates/biome_css_parser/src/syntax/at_rule/keyframes.rs
  • crates/biome_css_parser/src/syntax/at_rule/parse_error.rs
  • crates/biome_css_parser/tests/css_test_suite/error/at_rule/at_rule_keyframes/at_rule_keyframes_range_error.css
  • crates/biome_css_parser/tests/css_test_suite/ok/at_rule/at_rule_keyframe_range.css
  • crates/biome_grit_patterns/src/grit_target_language/css_target_language/generated_mappings.rs
  • xtask/codegen/css.ungram
  • xtask/codegen/src/css_kinds_src.rs
✅ Files skipped from review due to trivial changes (4)
  • crates/biome_css_formatter/src/css/selectors/mod.rs
  • crates/biome_css_formatter/src/css/selectors/keyframes_range_selector.rs
  • crates/biome_grit_patterns/src/grit_target_language/css_target_language/generated_mappings.rs
  • crates/biome_css_formatter/src/generated.rs
🚧 Files skipped from review as they are similar to previous changes (5)
  • crates/biome_css_formatter/src/css/any/keyframes_selector.rs
  • crates/biome_css_parser/src/lexer/mod.rs
  • xtask/codegen/src/css_kinds_src.rs
  • crates/biome_css_parser/src/syntax/at_rule/parse_error.rs
  • xtask/codegen/css.ungram

Walkthrough

Adds support for CSS scroll‑driven animation timeline‑range‑name keyframe selectors across parser, lexer, grammar, formatter and tests. Introduces a new CssKeyframesRangeSelector non‑terminal and syntax kind, recognises cover, contain, entry, exit, entry‑crossing, exit‑crossing in the lexer, parses <timeline-range-name> <percentage> in @keyframes, emits a specific diagnostic when the percentage is missing, formats the new node, updates codegen mappings, and adds OK and error test fixtures.

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarises the main change: adding support for scroll-driven animation keyframe selectors as per the CSS spec.
Description check ✅ Passed The description clearly explains the problem, approach, and test plan, directly relating to the changeset and linked issue #7760.
Linked Issues check ✅ Passed The PR fully implements the requirements from #7760: adds support for timeline-range-name keyframe selectors (cover, contain, entry, exit, entry-crossing, exit-crossing), updates parser and formatter, and provides comprehensive tests.
Out of Scope Changes check ✅ Passed All changes are scoped to CSS parser, formatter, and grammar updates directly supporting the timeline-range-name keyframe selector feature. No unrelated modifications detected.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@crates/biome_css_parser/src/syntax/at_rule/keyframes.rs`:
- Around line 366-370: The code makes the percentage after a timeline-range-name
optional by calling parse_percentage_dimension(p).ok(); change this so the
percentage is required: after bump_ts(TIMELINE_RANGE_NAME_SET) call
parse_percentage_dimension(p) and if it returns Err/None emit a diagnostic on
the parser (or mark the node as bogus) instead of silently continuing, and only
set kind to CSS_KEYFRAMES_RANGE_SELECTOR when parse_percentage_dimension
succeeds; reference functions/symbols: is_at_timeline_range_name,
parse_percentage_dimension, TIMELINE_RANGE_NAME_SET,
CSS_KEYFRAMES_RANGE_SELECTOR to locate and update the branch to handle missing
percentage as an error/recovery path.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 6f5f9f02-d8e3-4aa8-a67c-ff886b5fe6e8

📥 Commits

Reviewing files that changed from the base of the PR and between 94fdf1a and 2f50f45.

⛔ Files ignored due to path filters (9)
  • crates/biome_css_factory/src/generated/node_factory.rs is excluded by !**/generated/**, !**/generated/** and included by **
  • crates/biome_css_factory/src/generated/syntax_factory.rs is excluded by !**/generated/**, !**/generated/** and included by **
  • crates/biome_css_parser/tests/css_test_suite/error/at_rule/at_rule_keyframes/at_rule_keyframes.css.snap is excluded by !**/*.snap and included by **
  • crates/biome_css_parser/tests/css_test_suite/error/at_rule/at_rule_keyframes/at_rule_keyframes_1.css.snap is excluded by !**/*.snap and included by **
  • crates/biome_css_parser/tests/css_test_suite/ok/at_rule/at_rule_keyframe_range.css.snap is excluded by !**/*.snap and included by **
  • crates/biome_css_syntax/src/generated/kind.rs is excluded by !**/generated/**, !**/generated/** and included by **
  • crates/biome_css_syntax/src/generated/macros.rs is excluded by !**/generated/**, !**/generated/** and included by **
  • crates/biome_css_syntax/src/generated/nodes.rs is excluded by !**/generated/**, !**/generated/** and included by **
  • crates/biome_css_syntax/src/generated/nodes_mut.rs is excluded by !**/generated/**, !**/generated/** and included by **
📒 Files selected for processing (12)
  • .changeset/feat-css-keyframes-timeline-range.md
  • crates/biome_css_formatter/src/css/any/keyframes_selector.rs
  • crates/biome_css_formatter/src/css/selectors/keyframes_range_selector.rs
  • crates/biome_css_formatter/src/css/selectors/mod.rs
  • crates/biome_css_formatter/src/generated.rs
  • crates/biome_css_parser/src/lexer/mod.rs
  • crates/biome_css_parser/src/syntax/at_rule/keyframes.rs
  • crates/biome_css_parser/src/syntax/at_rule/parse_error.rs
  • crates/biome_css_parser/tests/css_test_suite/ok/at_rule/at_rule_keyframe_range.css
  • crates/biome_grit_patterns/src/grit_target_language/css_target_language/generated_mappings.rs
  • xtask/codegen/css.ungram
  • xtask/codegen/src/css_kinds_src.rs

Comment thread crates/biome_css_parser/src/syntax/at_rule/keyframes.rs
@MeGaNeKoS MeGaNeKoS force-pushed the fix/css-keyframes-timeline-range-7760 branch from 2f50f45 to 62b44e2 Compare April 3, 2026 07:10
@MeGaNeKoS MeGaNeKoS force-pushed the fix/css-keyframes-timeline-range-7760 branch from 62b44e2 to dd0180d Compare April 3, 2026 07:24
@ematipico ematipico assigned denbezrukov and unassigned denbezrukov Apr 3, 2026
@ematipico ematipico requested review from a team and denbezrukov April 3, 2026 08:43
Copy link
Copy Markdown
Contributor

@denbezrukov denbezrukov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good!
Thank you so much!

@ematipico ematipico merged commit 53b8e57 into biomejs:main Apr 3, 2026
19 checks passed
@github-actions github-actions Bot mentioned this pull request Apr 3, 2026
@MeGaNeKoS MeGaNeKoS deleted the fix/css-keyframes-timeline-range-7760 branch April 3, 2026 10:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-Formatter Area: formatter A-Parser Area: parser A-Tooling Area: internal tools L-CSS Language: CSS and super languages L-Grit Language: GritQL

Projects

None yet

Development

Successfully merging this pull request may close these issues.

CSS keyframes with timeline-range-name not supported

3 participants