Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion docs/rules/use-baseline.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ While using only Baseline widely available features can help ensure the greatest
This rule warns when it finds any of the following:

- A CSS property that isn't widely available or otherwise isn't enclosed in a `@supports` block.
- An at-rule that isn't widely available.
- An at-rule that isn't widely available or isn't guarded by a matching `@supports at-rule()` query.
- A media condition inside `@media` that isn't widely available.
- A CSS property value that isn't widely available or otherwise isn't enclosed in a `@supports` block (currently limited to identifiers only).
- A CSS property function that isn't widely available.
Expand Down Expand Up @@ -100,6 +100,15 @@ Examples of **correct** code:
accent-color: auto;
}
}

/* valid - @supports indicates you're choosing a limited availability at-rule */
@supports at-rule(@scope) {
@scope (.card) {
a {
color: red;
}
}
}
```

**Important:** While the `cursor` property is not considered baseline, it has wide support and will likely be considered baseline once the WebDX Community Group adds [an editorial step](https://github.com/web-platform-dx/web-features/issues/1038). In the meantime, this rule does not warn when `cursor` is used.
Expand Down
57 changes: 53 additions & 4 deletions src/rules/use-baseline.js
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,12 @@ class SupportsRule {
*/
#selectors = new Set();

/**
* The at-rules supported by this rule.
* @type {Set<string>}
*/
#atRules = new Set();

/**
* Adds a property to the rule.
* @param {string} property The name of the property.
Expand Down Expand Up @@ -320,6 +326,24 @@ class SupportsRule {
hasSelector(selector) {
return this.#selectors.has(selector);
}

/**
* Adds an at-rule to the rule.
* @param {string} atRule The name of the at-rule.
* @returns {void}
*/
addAtRule(atRule) {
this.#atRules.add(atRule);
}

/**
* Determines if the rule supports an at-rule.
* @param {string} atRule The name of the at-rule.
* @returns {boolean} `true` if the at-rule is supported, `false` if not.
*/
hasAtRule(atRule) {
return this.#atRules.has(atRule);
}
}

/**
Expand Down Expand Up @@ -424,6 +448,15 @@ class SupportsRules {
hasSelector(selector) {
return this.#rules.some(rule => rule.hasSelector(selector));
}

/**
* Determines if any rule supports an at-rule.
* @param {string} atRule The name of the at-rule.
* @returns {boolean} `true` if any rule supports the at-rule, `false` if not.
*/
hasAtRule(atRule) {
return this.#rules.some(rule => rule.hasAtRule(atRule));
}
}

/**
Expand Down Expand Up @@ -775,14 +808,26 @@ export default /** @satisfies {UseBaselineRuleDefinition} */ ({
continue;
}

if (
conditionChild.type === "FeatureFunction" &&
conditionChild.feature === "selector"
) {
if (conditionChild.type !== "FeatureFunction") {
continue;
}

const feature = conditionChild.feature.toLowerCase();

if (feature === "selector") {
for (const selectorChild of conditionChild.value
.children) {
supportsRule.addSelector(selectorChild.name);
}

continue;
}

if (feature === "at-rule") {
const atRule = conditionChild.value.value
.slice(1)
.toLowerCase();
supportsRule.addAtRule(atRule);
}
}
},
Expand Down Expand Up @@ -950,6 +995,10 @@ export default /** @satisfies {UseBaselineRuleDefinition} */ ({
return;
}

if (supportsRules.hasAtRule(atRuleName)) {
return;
}

const featureStatus = atRules.get(atRuleName);

if (!baselineAvailability.isSupported(featureStatus)) {
Expand Down
42 changes: 42 additions & 0 deletions tests/rules/use-baseline.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,16 @@ ruleTester.run("use-baseline", rule, {
`@supports selector(:fullscreen) {
h1:fullscreen { color: red; }
}`,
`@supports at-rule(@scope) {
@scope (.card) {
a { color: red; }
}
}`,
`@SUPPORTS AT-RULE(@SCOPE) {
@scope (.card) {
a { color: red; }
}
}`,
"div { cursor: pointer; }",
"pre { overflow: auto; }",
".highlight, #highlight, highlight { color: red }",
Expand Down Expand Up @@ -549,6 +559,38 @@ ruleTester.run("use-baseline", rule, {
},
],
},
{
Comment thread
Pixel998 marked this conversation as resolved.
code: "@supports at-rule(@scope) {}\n@supports (color: red) {\n@scope (.card) { a { color: red; } }\n}",
errors: [
{
messageId: "notBaselineAtRule",
data: {
atRule: "scope",
availability: "widely",
},
line: 3,
column: 1,
endLine: 3,
endColumn: 7,
},
],
},
{
code: "@supports at-rule(@scope) {\n@view-transition { navigation: auto; }\n}",
errors: [
{
messageId: "notBaselineAtRule",
data: {
atRule: "view-transition",
availability: "widely",
},
line: 2,
column: 1,
endLine: 2,
endColumn: 17,
},
],
},
{
code: "details::details-content { background-color: #a29bfe; }",
errors: [
Expand Down
Loading