From 28a9ced8f541ba3442248b5fb50ca87772d4f4a2 Mon Sep 17 00:00:00 2001 From: Adam Averay Date: Wed, 1 Oct 2025 09:36:43 +0100 Subject: [PATCH] Allow ignoring longhand background image property --- README.md | 27 ++++++++ src/rules/use-defensive-css/index.js | 26 +++++++- src/rules/use-defensive-css/index.test.js | 75 +++++++++++++++++++++++ 3 files changed, 125 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index d25bcbd..f03ce0e 100644 --- a/README.md +++ b/README.md @@ -159,6 +159,33 @@ div { } ``` +| Option | Description | +| --------- | ------------------------------------------------------------------- | +| shorthand | Provide false to ignore the shorthand property (`background-image`) | +| longhand | Provide false to ignore the longhand property (`background`) | + +```json +{ + "rules": { + "plugin/use-defensive-css": [ + true, + { "custom-property-fallbacks": [true, { "longhand": false }] } + ] + } +} +``` + +By default both shorthand and longhand properties are validated. Ignoring either +will allow using that property type without raising issues. + +#### ✅ Passing Examples + +```css +div { + background-image: url('some-image.jpg'); +} +``` + ### Custom Property Fallbacks > [Read more about this pattern in Defensive CSS](https://defensivecss.dev/tip/css-variable-fallback/) diff --git a/src/rules/use-defensive-css/index.js b/src/rules/use-defensive-css/index.js index 07b2436..784fb09 100644 --- a/src/rules/use-defensive-css/index.js +++ b/src/rules/use-defensive-css/index.js @@ -94,15 +94,35 @@ const ruleFunction = (_, options) => { } /* BACKGROUND REPEAT */ - if (options?.['background-repeat']) { - if (decl.prop === 'background' && decl.value.includes('url(')) { + if ( + options?.['background-repeat'] && + (!Array.isArray(options?.['background-repeat']) || + options['background-repeat'][0]) + ) { + const backgroundRepeatOptions = { + shorthand: true, + longhand: true, + ...(Array.isArray(options['background-repeat']) + ? options['background-repeat'][1] + : {}), + }; + + if ( + decl.prop === 'background' && + backgroundRepeatOptions.shorthand && + decl.value.includes('url(') + ) { backgroundRepeatProps.hasBackgroundImage = true; backgroundRepeatProps.isMissingBackgroundRepeat = !findShorthandBackgroundRepeat(decl.value); backgroundRepeatProps.nodeToReport = decl; } - if (decl.prop === 'background-image' && decl.value.includes('url(')) { + if ( + decl.prop === 'background-image' && + backgroundRepeatOptions.longhand && + decl.value.includes('url(') + ) { backgroundRepeatProps.hasBackgroundImage = true; backgroundRepeatProps.nodeToReport = decl; } diff --git a/src/rules/use-defensive-css/index.test.js b/src/rules/use-defensive-css/index.test.js index 1d44ac8..f62afbc 100644 --- a/src/rules/use-defensive-css/index.test.js +++ b/src/rules/use-defensive-css/index.test.js @@ -169,6 +169,81 @@ testRule({ ], }); +/* eslint-disable-next-line no-undef */ +testRule({ + ruleName, + config: [true, { 'background-repeat': [true, { longhand: false }] }], + plugins: ['./index.js'], + accept: [ + { + code: `div { background: url('some-image.jpg') repeat black top center; }`, + description: "Shorthand background property with 'repeat' value.", + }, + { + code: `div { background: url('some-image.jpg') repeat-x black top center; }`, + description: "Shorthand background property with 'repeat-x' value.", + }, + { + code: `div { background: url('some-image.jpg') repeat-y black top center; }`, + description: "Shorthand background property with 'repeat-y' value.", + }, + { + code: `div { background: url('some-image.jpg') no-repeat black top center; }`, + description: "Shorthand background property with 'no-repeat' value.", + }, + { + code: `div { background: url('some-image.jpg') round black top center; }`, + description: "Shorthand background property with 'round' value.", + }, + { + code: `div { background: url('some-image.jpg') space black top center; }`, + description: "Shorthand background property with 'space' value.", + }, + { + code: `div { background: url('some-image.jpg') space round black top center; }`, + description: "Shorthand background property with 'space round' value.", + }, + { + code: `div { background: url('some-image.jpg') black top center; background-repeat: no-repeat; }`, + description: + 'Shorthand background property with background-repeat property.', + }, + { + code: `div { background-image: url('some-image.jpg'); background-repeat: no-repeat; }`, + description: 'Using background-image with background-repeat properties.', + }, + { + code: `div { background-image: linear-gradient(#e66465, #9198e5); }`, + description: + 'Using a linear-gradient background image without background repeat is okay.', + }, + { + code: `div { background-image: linear-gradient(#e66465, #9198e5), url('some-image.jpg'); background-repeat: no-repeat; }`, + description: + 'Using background-image with gradient and url with background-repeat property is okay.', + }, + { + code: `div { background-image: url('some-image.jpg'); }`, + description: + 'A background-image property without a background-repeat property.', + }, + { + code: `div { background-image: linear-gradient(#e66465, #9198e5), url('some-image.jpg'); }`, + description: + 'A background-image property with both a gradient and url() but no background-repeat property.', + message: messages.backgroundRepeat(), + }, + ], + + reject: [ + { + code: `div { background: url('some-image.jpg') black top center; }`, + description: 'A shorthand background property without a repeat property.', + message: messages.backgroundRepeat(), + }, + ], +}); + /* eslint-disable-next-line no-undef */ testRule({ ruleName,