Skip to content

[scss][less] Accept keyframe selectors in nested rule set declarations - #502

Open
Rusty Raven (kakiuwang-ui) wants to merge 1 commit into
microsoft:mainfrom
kakiuwang-ui:fix/mixin-keyframe-selectors
Open

[scss][less] Accept keyframe selectors in nested rule set declarations#502
Rusty Raven (kakiuwang-ui) wants to merge 1 commit into
microsoft:mainfrom
kakiuwang-ui:fix/mixin-keyframe-selectors

Conversation

@kakiuwang-ui

@kakiuwang-ui Rusty Raven (kakiuwang-ui) commented Jul 25, 2026

Copy link
Copy Markdown

Fixes microsoft/vscode#227452

Problem

The SCSS parser reports false errors (css-rcurlyexpected, css-ruleorselectorexpected) for mixins that contain keyframe selectors, even though this is valid Sass that compiles fine:

@mixin slideX($from, $to) {
  0% { transform: translateX($from); }
  100% { transform: translateX($to); }
}

@keyframes slide-left { @include slideX(0, -100%); }

The same class of bug also reproduces one nesting level deeper (@mixin a { @if $b { 0% { } } }), inside @include content blocks wrapped in control statements, and in LESS parametric mixins (.m() { 0% { } } — cf. microsoft/vscode#28091, which fixed only detached rulesets in 2017).

Fix

Rather than adding another per-construct special case (there were already two: _parseMixinReferenceBodyStatement for scss @include bodies and _parseDetachedRuleSetBody for less detached rulesets, both added for vscode#28091), accept keyframe selectors in the shared nested fallback: the non-at-keyword branch of the scss/less _parseRuleSetDeclaration now tries _tryParseKeyframeSelector:

  • after _tryParseRuleset, so plain nested rulesets like e1 { } keep their RuleSet node type (the existing SCSS - Selector Printing › nested selector test guards this), and
  • before _parseDeclaration, which would otherwise consume from, 50% and emit a colon-expected error.

Because every nested construct (@mixin bodies, @include content blocks, @if/@each/@for bodies, less mixins and detached rulesets) funnels through _parseRuleSetDeclaration, this one change covers them all, and the two existing special-case body parsers are removed (net −9 lines in the parsers). _tryParseKeyframeSelector fully backtracks, so declarations, variable declarations and error recovery are unaffected.

Plain CSS is unchanged: .a { 0% { } } in a .css file still errors, since the base parser's _parseRuleSetDeclaration is untouched.

This matches dart-sass/less semantics, which parse rule bodies permissively and validate selectors at evaluation time (a mixin declaration cannot know its inclusion site).

Tests

Added parser tests for scss (0%/100% in @mixin, mixed from, 50% lists, keyframe selectors under @if/@else inside a mixin) and less (parametric mixins with 0%/100% and mixed lists). Full suite passes: 710 pass, 0 fail.

@kakiuwang-ui

Copy link
Copy Markdown
Author

@microsoft-github-policy-service agree

Keyframe selectors such as `0% { ... }` are valid inside mixin bodies
(and any content that may be included into a @Keyframes rule), but the
SCSS and LESS parsers only accepted them via two per-construct special
cases (scss @include content blocks, less detached rulesets), so @mixin
declaration bodies raised false 'css-rcurlyexpected' errors — and the
same bug survived one nesting level deeper inside @if/@each/@for and in
less parametric mixins.

Fix it in the shared fallback instead: try _tryParseKeyframeSelector in
the non-at-keyword branch of the scss/less _parseRuleSetDeclaration,
after _tryParseRuleset (so plain rulesets like `e1 { }` keep their
RuleSet node type, as the selector-printing tests require) and before
_parseDeclaration (which would otherwise consume `from, 50%` and emit
a colon-expected error). This subsumes the two existing special-case
body parsers, which are removed. Plain CSS is unchanged and still
rejects keyframe selectors outside @Keyframes.

Matches dart-sass, which parses mixin bodies permissively and validates
selectors at evaluation time.

Fixes microsoft/vscode#227452
@kakiuwang-ui Rusty Raven (kakiuwang-ui) changed the title [scss] Allow keyframe selectors in @mixin declaration bodies [scss][less] Accept keyframe selectors in nested rule set declarations Jul 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[scss] inbuild sass/scss extension shows error that is none

1 participant