UNPKG

doiuse

Version:

Lint CSS for browser support against caniuse database

2,006 lines (1,691 loc) 77.8 kB
'use strict'; var multimatch = require('multimatch'); var browserslist = require('browserslist'); var caniuse = require('caniuse-lite'); var postcss = require('postcss'); function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; } function _interopNamespace(e) { if (e && e.__esModule) return e; var n = Object.create(null); if (e) { Object.keys(e).forEach(function (k) { if (k !== 'default') { var d = Object.getOwnPropertyDescriptor(e, k); Object.defineProperty(n, k, d.get ? d : { enumerable: true, get: function () { return e[k]; } }); } }); } n["default"] = e; return Object.freeze(n); } var multimatch__default = /*#__PURE__*/_interopDefaultLegacy(multimatch); var browserslist__default = /*#__PURE__*/_interopDefaultLegacy(browserslist); var caniuse__namespace = /*#__PURE__*/_interopNamespace(caniuse); /** * No description available. * * See: This feature comes from MDN: https://developer.mozilla.org/en-US/search?q=Alternate+stylesheet */ // This feature is only in HTML, so we don't lint it /** * @type {import('../features').Feature} */ var alternateStylesheet = {}; /** * Method of defining how a background image is attached to a scrollable element. Values include `scroll` (default), `fixed` and `local`. * * See: https://caniuse.com/background-attachment */ /** * @type {import('../features').Feature} */ var backgroundAttachment = { 'background-attachment': [ 'fixed', 'local', ], }; /** * Non-standard method of clipping a background image to the foreground text. * * See: https://caniuse.com/background-clip-text */ /** * @type {import('../features').Feature} */ var backgroundClipText = { 'background-clip': 'text', '-webkit-background-clip': 'text', }; var backgroundImgOpts = { 'background-clip': true, 'background-origin': true, 'background-size': true, }; var backgroundPositionXY = { 'background-position-x': true, 'background-position-y': true, }; /** * Allows CSS background images to be repeated without clipping. * * See: https://caniuse.com/background-repeat-round-space */ /** * @type {import('../features').Feature} */ var backgroundRepeatRoundSpace = { 'background-repeat': ['round', 'space'], }; var borderImage = { 'border-image': true, 'border-image-outset': true, 'border-image-repeat': true, 'border-image-slice': true, 'border-image-source': true, 'border-image-width': true, }; var borderRadius = { 'border-radius': true, 'border-top-left-radius': true, 'border-top-right-radius': true, 'border-bottom-right-radius': true, 'border-bottom-left-radius': true, }; var calc = { '': 'calc(', }; /** @typedef {RegExp|string|((value:string) => boolean)} FeatureCheck */ /** * Strip the contents of url literals so they aren't matched * by our naive substring matching. * @param {string} input * @return {string} */ function stripUrls(input) { return input.replaceAll(/url\([^)]*\)/g, 'url()'); } /** * @param {string} browserKey * @param {string[]} [versions] * @return {string} */ function formatBrowserName(browserKey, versions) { const entry = caniuse.agents[browserKey]; const browserName = entry ? entry.browser : null; if (!versions) { return browserName || ''; } const ranges = []; let rangeStart = -1; let rangeEnd = -1; for (const [index, versionString] of versions.entries()) { const current = +versionString; const next = +versions[index + 1]; if (Number.isInteger(current)) { if (rangeStart === -1) { rangeStart = current; rangeEnd = current; } else if (current === rangeEnd + 1) { rangeEnd = current; } else { ranges.push(rangeStart === rangeEnd ? [rangeStart] : [rangeStart, rangeEnd]); rangeStart = current; rangeEnd = current; } if (!Number.isInteger(next) || current + 1 !== next) { ranges.push(rangeStart === rangeEnd ? [rangeStart] : [rangeStart, rangeEnd]); rangeStart = -1; rangeEnd = -1; } } else { if (rangeStart !== -1) { ranges.push(rangeStart === rangeEnd ? [rangeStart] : [rangeStart, rangeEnd]); rangeStart = -1; rangeEnd = -1; } ranges.push([versionString]); } } const versionString = ranges.map((range) => range.join('-')).join(','); return `${browserName} (${versionString})`; } /** * * @param {FeatureCheck|FeatureCheck[]} check * @param {?string|undefined} candidate */ function performFeatureCheck(check, candidate) { if (check == null || candidate == null) return false; if (check instanceof RegExp) { return check.test(candidate); } switch (typeof check) { case 'string': return candidate.includes(check); case 'function': return check(candidate); case 'boolean': return check; case 'object': if (Array.isArray(check)) { return check.some((c) => performFeatureCheck(c, candidate)); } // Fallthrough default: throw new TypeError(`Unexpected feature check: ${check}`); } } /** * @param {FeatureCheck|FeatureCheck[]} selector * @return {(rule:import('postcss').ChildNode) => boolean} */ function checkSelector(selector) { return (rule) => rule.type === 'rule' && performFeatureCheck(selector, rule.selector); } /** * @param {FeatureCheck|FeatureCheck[]} [name] * @param {FeatureCheck|FeatureCheck[]} [parameters] * @return {(rule:import('postcss').ChildNode) => boolean} */ function checkAtRule(name, parameters) { return (rule) => rule.type === 'atrule' && performFeatureCheck(name, rule.name) && (!parameters || performFeatureCheck(parameters, rule.params)); } /** * @see https://drafts.csswg.org/css-values/#lengths * @see https://drafts.csswg.org/css-values/#number-value * @param {string[]} units * @return {(rule:import('postcss').ChildNode) => boolean} */ function checkCSSLengthUnits(...units) { const regexp = new RegExp(`(\\+-)?[\\d.]*\\.?\\d+(e(\\+-)?\\d+)?(${units.join('|')})`, 'i'); return (rule) => ( (rule.type === 'decl') ? performFeatureCheck(regexp, stripUrls(rule.value)) : ((rule.type === 'atrule') ? performFeatureCheck(regexp, rule.params) : false)); } /** * Unit representing the width of the character "0" in the current font, of particular use in combination with monospace fonts. * @see https://caniuse.com/ch-unit * @type {import('../features').Feature} */ var chUnit = checkCSSLengthUnits('ch'); var cssAll = { 'all': true, }; var cssAnimation = { 'animation': true, 'animation-name': true, 'animation-duration': true, 'animation-timing-function': true, 'animation-iteration-count': true, 'animation-direction': true, 'animation-play-state': true, 'animation-delay': true, 'animation-fill-mode': true, }; // @keyframes isn't checked for, but if you try to use it, // you'll still get a warning anyway because of the 'animation' property var cssAnyLink = checkSelector([':any-link', ':-webkit-any-link']); var cssAppearance = { 'appearance': true, '-webkit-appearance': true, '-moz-appearance': true, }; var cssAtCounterStyle = checkAtRule('counter-style'); var cssAutofill = checkSelector([':autofill', ':-webkit-autofill']); var cssBackdropFilter = { 'backdrop-filter': true, }; /** * @param {import('postcss').ChildNode} rule * @return {boolean} */ var cssBackgroundOffsets = (rule) => { if (rule.type !== 'decl') return false; if (rule.prop !== 'background-position') return false; return postcss.list.space(rule.value).length > 2; }; var cssBackgroundblendmode = { 'background-blend-mode': true, }; /** * Controls whether the box's margins, borders, padding, and other decorations wrap the broken edges of the box fragments (when the box is split by a break (page/column/region/line). * * See: https://caniuse.com/css-boxdecorationbreak */ /** * @type {import('../features').Feature} */ var cssBoxdecorationbreak = { 'box-decoration-break': true, '-webkit-box-decoration-break': true, }; var cssBoxshadow = { 'box-shadow': true, }; /** * @param {import('postcss').ChildNode} rule * @return {boolean} */ var cssCanvas = (rule) => { if (rule.type !== 'decl') return false; if (!rule.prop.startsWith('background')) return false; return rule.value.startsWith('-webkit-canvas'); }; /** * The `caret-color` property allows the color to be set of the caret (blinking text insertion pointer) in an editable text area. * * See: https://caniuse.com/css-caret-color */ /** * @type {import('../features').Feature} */ var cssCaretColor = { 'caret-color': true, }; /** * The `@layer` at-rule allows authors to explicitly layer their styles in the cascade, before specificity and order of appearance are considered. * * See: https://caniuse.com/css-cascade-layers */ /** * @type {import('../features').Feature} */ var cssCascadeLayers = checkAtRule('layer'); /** * Including an `i` before the `]` in a CSS attribute selector causes the attribute value to be matched in an ASCII-case-insensitive manner. For example, `[b="xyz" i]` would match both `<a b="xyz">` and `<a b="XYZ">`. * * See: https://caniuse.com/css-case-insensitive */ /** * @type {import('../features').Feature} */ var cssCaseInsensitive = (rule) => { if (rule.type !== 'rule') { return false; } if (/\[.*i]/.test(rule.selector)) { return true; } return false; }; var cssClipPath = { 'clip-path': true, }; /** * The `print-color-adjust` (or `-webkit-print-color-adjust` as prefixed in WebKit/Blink browsers) property is a CSS extension that can be used to force printing of background colors and images. * * See: https://caniuse.com/css-color-adjust */ /** * @type {import('../features').Feature} */ var cssColorAdjust = { 'print-color-adjust': true, '-webkit-print-color-adjust': true, }; /** * The CSS `color()` function allows the browser to display colors in any color space, such as the P3 color space which can display colors outside of the default sRGB color space. * * See: https://caniuse.com/css-color-function */ /** * @type {import('../features').Feature} */ var cssColorFunction = { '': 'color(', }; const REGEX$2 = /(^|[^-])(repeating-)?conic-gradient/; var cssConicGradients = { 'background': REGEX$2, 'background-image': REGEX$2, 'border-image': REGEX$2, 'border-image-source': REGEX$2, 'content': REGEX$2, 'cursor': REGEX$2, 'list-style': REGEX$2, 'list-style-image': REGEX$2, }; /** * Size queries in Container Queries provide a way to query the size of a container, and conditionally apply CSS to the content of that container. * * See: https://caniuse.com/css-container-queries */ /** * @type {import('../features').Feature} */ var cssContainerQueries = checkAtRule('container'); var cssContainerQueriesStyle = checkAtRule('container', 'style('); var cssContainerQueryUnits = checkCSSLengthUnits( 'cqw', 'cqh', 'cqi', 'cqb', 'cqmin', 'cqmax', ); /** * The CSS `contain` property lets developers limit the scope of the browser's styles, layout and paint work for faster and more efficient rendering. * * See: https://caniuse.com/css-containment */ /** * @type {import('../features').Feature} */ var cssContainment = { 'contain': true, }; /** * Provides control over when elements are rendered, so rendering can be skipped for elements not yet in the user's viewport. * * See: https://caniuse.com/css-content-visibility */ /** * @type {import('../features').Feature} */ var cssContentVisibility = { 'content-visibility': true, }; var cssCounters = { 'counter-reset': true, 'counter-increment': true, '': 'counter(', }; /** * Scales images with an algorithm that preserves edges and contrast, without smoothing colors or introducing blur. This is intended for images such as pixel art. Official values that accomplish this for the `image-rendering` property are `crisp-edges` and `pixelated`. * * See: https://caniuse.com/css-crisp-edges */ /** * @type {import('../features').Feature} */ var cssCrispEdges = { 'image-rendering': ['crisp-edges', 'pixelated'], }; /** * Image function to create a "crossfade" between images. This allows one image to transition (fade) into another based on a percentage value. * * See: https://caniuse.com/css-cross-fade */ /** * @type {import('../features').Feature} */ var cssCrossFade = { '': [ 'cross-fade(', '-webkit-cross-fade(', ], }; /** * The `:default` pseudo-class matches checkboxes and radio buttons which are checked by default, `<option>`s with the `selected` attribute, and the default submit button (if any) of a form. * * See: https://caniuse.com/css-default-pseudo */ /** * @type {import('../features').Feature} */ var cssDefaultPseudo = checkSelector(':default'); /** * An explicit, non-whitespace spelling of the descendant combinator. `A >> B` is equivalent to `A B`. * * See: https://caniuse.com/css-descendant-gtgt */ /** * @type {import('../features').Feature} */ var cssDescendantGtgt = checkSelector('>>'); /** * Method of overriding the size of viewport in web page using the `@viewport` rule, replacing Apple's own popular `<meta>` viewport implementation. Includes the `extend-to-zoom` width value. * * See: https://caniuse.com/css-deviceadaptation */ /** * @type {import('../features').Feature} */ var cssDeviceadaptation = checkAtRule('viewport'); /** * Matches elements based on their directionality. `:dir(ltr)` matches elements which are Left-to-Right. `:dir(rtl)` matches elements which are Right-to-Left. * * See: https://caniuse.com/css-dir-pseudo */ /** * @type {import('../features').Feature} */ var cssDirPseudo = checkSelector(':dir('); /** * `display: contents` causes an element's children to appear as if they were direct children of the element's parent, ignoring the element itself. This can be useful when a wrapper element should be ignored when using CSS grid or similar layout techniques. * * See: https://caniuse.com/css-display-contents */ /** * @type {import('../features').Feature} */ var cssDisplayContents = { 'display': 'contents', }; /** * This function renders a live image generated from an arbitrary HTML element * * See: https://caniuse.com/css-element-function */ /** * @type {import('../features').Feature} */ var cssElementFunction = { '': 'element(', }; var cssEnvFunction = { '': 'env(', }; /** * Exclusions defines how inline content flows around elements. It extends the content wrapping ability of floats to any block-level element. * * See: https://caniuse.com/css-exclusions */ /** * @type {import('../features').Feature} */ var cssExclusions = { 'wrap-flow': true, '-ms-wrap-flow': true, }; var cssFeaturequeries = checkAtRule(['supports']); /** * No description available. * * See: This feature comes from MDN: https://developer.mozilla.org/en-US/search?q=::file-selector-button+CSS+pseudo-element */ /** * @type {import('../features').Feature} */ var cssFileSelectorButton = checkSelector(':file-selector-button'); /** * This function filters a CSS input image with a set of filter functions (like blur, grayscale or hue) * * See: https://caniuse.com/css-filter-function */ /** * @type {import('../features').Feature} */ var cssFilterFunction = { '': 'filter(', }; var cssFilters = { 'filter': true, }; /** * CSS pseudo-element that allows styling only the first "letter" of text within an element. Useful for implementing initial caps or drop caps styling. * * See: https://caniuse.com/css-first-letter */ /** * @type {import('../features').Feature} */ var cssFirstLetter = checkSelector(':first-letter'); /** * Allows styling specifically for the first line of text using the `::first-line` pseudo-element. Note that only a limited set of properties can be applied. * * See: https://caniuse.com/css-first-line */ /** * @type {import('../features').Feature} */ var cssFirstLine = checkSelector(':first-line'); var cssFixed = { 'position': 'fixed', }; /** * The `:focus-visible` pseudo-class applies while an element matches the `:focus` pseudo-class, and the UA determines via heuristics that the focus should be specially indicated on the element (typically via a “focus ring”). * * See: https://caniuse.com/css-focus-visible */ /** * @type {import('../features').Feature} */ var cssFocusVisible = checkSelector(':focus-visible'); /** * The `:focus-within` pseudo-class matches elements that either themselves match `:focus` or that have descendants which match `:focus`. * * See: https://caniuse.com/css-focus-within */ /** * @type {import('../features').Feature} */ var cssFocusWithin = checkSelector(':focus-within'); var cssFontPalette = [ (rule) => performFeatureCheck('font-palette', rule.name), checkAtRule('font-palette-values'), ]; /** * `@font-face` descriptor `font-display` that allows control over how a downloadable font renders before it is fully loaded. * * See: https://caniuse.com/css-font-rendering-controls */ /** * @type {import('../features').Feature} */ var cssFontRenderingControls = { 'font-display': true, }; var cssFontStretch = { 'font-stretch': true, }; var cssGencontent = checkSelector([':before', ':after']); const REGEX$1 = /(^|[^-])((linear)|(radial))-gradient/; var cssGradients = { 'background': REGEX$1, 'background-image': REGEX$1, 'border-image': REGEX$1, 'border-image-source': REGEX$1, 'content': REGEX$1, 'cursor': REGEX$1, 'list-style': REGEX$1, 'list-style-image': REGEX$1, }; /** @see https://drafts.csswg.org/css-grid/ */ var cssGrid = { 'display': ['grid', 'inline-grid', '-ms-grid'], 'grid': true, 'grid-area': true, 'grid-auto-columns': true, 'grid-auto-flow': true, 'grid-auto-rows': true, 'grid-column': true, 'grid-column-end': true, 'grid-column-start': true, 'grid-row': true, 'grid-row-end': true, 'grid-row-start': true, 'grid-template': true, 'grid-template-areas': true, 'grid-template-columns': true, 'grid-template-rows': true, }; const REGEX = /grid/; var cssGridAnimation = { 'transition': REGEX, 'transition-property': REGEX, }; /** * Allows some punctuation characters from start (or the end) of text elements to be placed "outside" of the box in order to preserve the reading flow. * @see https://caniuse.com/css-hanging-punctuation */ /** * @type {import('../features').Feature} */ var cssHangingPunctuation = { 'hanging-punctuation': true, }; /** * Select elements containing specific content. For example, `a:has(img)` selects all `<a>` elements that contain an `<img>` child. * @see https://caniuse.com/css-has */ /** * @type {import('../features').Feature} */ var cssHas = checkSelector(':has'); var cssHyphens = { 'hyphens': true, }; var cssImageOrientation = { 'image-orientation': true, }; /** * Method of letting the browser pick the most appropriate CSS image from a given set. * @see https://caniuse.com/css-image-set */ /** * @type {import('../features').Feature} */ var cssImageSet = { '': 'image-set(', }; /** * If a temporal or number `<input>` has `max` and/or `min` attributes, then `:in-range` matches when the value is within the specified range and `:out-of-range` matches when the value is outside the specified range. If there are no range constraints, then neither pseudo-class matches. * @see https://caniuse.com/css-in-out-of-range */ /** * @type {import('../features').Feature} */ var cssInOutOfRange = checkSelector([ ':in-range', ':out-of-range', ]); /** * The `:indeterminate` pseudo-class matches indeterminate checkboxes, indeterminate `<progress>` bars, and radio buttons with no checked button in their radio button group. * @see https://caniuse.com/css-indeterminate-pseudo */ /** * @type {import('../features').Feature} */ var cssIndeterminatePseudo = checkSelector(':indeterminate'); /** * Method of creating an enlarged cap, including a drop or raised cap, in a robust way. * @see https://caniuse.com/css-initial-letter */ /** * @type {import('../features').Feature} */ var cssInitialLetter = { 'initial-letter': true, }; var cssInitialValue = { '': 'initial', }; /** * The `lch()` and `lab()` color functions are based on the CIE LAB color space, representing colors in a way that closely matches human perception and provides access to a wider spectrum of colors than offered by the usual RGB color space. * @see https://caniuse.com/css-lch-lab */ /** * @type {import('../features').Feature} */ var cssLchLab = { '': [ 'lch(', 'lab(', ], }; /** * Controls spacing between characters of text (i.e. "tracking" in typographical terms). Not to be confused with kerning. * @see https://caniuse.com/css-letter-spacing */ /** * @type {import('../features').Feature} */ var cssLetterSpacing = { 'letter-spacing': true, }; /** * CSS property that will contain text to a given amount of lines when used in combination with `display: -webkit-box`. It will end with ellipsis when `text-overflow: ellipsis` is included. * @see https://caniuse.com/css-line-clamp */ /** * @type {import('../features').Feature} */ var cssLineClamp = { 'line-clamp': true, '-webkit-line-clamp': true, }; var cssLogicalProps = { 'block-size': true, 'inline-size': true, 'min-block-size': true, 'min-inline-size': true, 'max-block-size': true, 'max-inline-size': true, 'margin-block-start': true, 'margin-block-end': true, 'margin-inline-start': true, 'margin-inline-end': true, 'margin-block': true, 'margin-inline': true, 'inset-block-start': true, 'inset-block-end': true, 'inset-inline-start': true, 'inset-inline-end': true, 'inset-block': true, 'inset-inline': true, 'inset': true, 'padding-block-start': true, 'padding-block-end': true, 'padding-inline-start': true, 'padding-inline-end': true, 'padding-block': true, 'padding-inline': true, 'border-block-start-width': true, 'border-block-end-width': true, 'border-inline-start-width': true, 'border-inline-end-width': true, 'border-block-width': true, 'border-inline-width': true, 'border-block-start-style': true, 'border-block-end-style': true, 'border-inline-start-style': true, 'border-inline-end-style': true, 'border-block-style': true, 'border-inline-style': true, 'border-block-start-color': true, 'border-block-end-color': true, 'border-inline-start-color': true, 'border-inline-end-color': true, 'border-block-color': true, 'border-inline-color': true, 'border-block-start': true, 'border-block-end': true, 'border-inline-start': true, 'border-inline-end': true, 'border-block': true, 'border-inline': true, 'border-start-start-radius': true, 'border-start-end-radius': true, 'border-end-start-radius': true, 'border-end-end-radius': true, 'text-align': ['start', 'end'], 'float': ['inline-start', 'inline-end'], 'resize': ['block', 'inline'], 'overflow-block': true, 'overflow-inline': true, }; /** * The `::marker` pseudo-element allows list item markers to be styled or have their content value customized. * @see https://caniuse.com/css-marker-pseudo */ /** * @type {import('../features').Feature} */ var cssMarkerPseudo = checkSelector(':marker'); var cssMasks = { 'clip-path': true, 'mask': true, 'mask-border': true, 'mask-border-mode': true, 'mask-border-outset': true, 'mask-border-repeat': true, 'mask-border-slice': true, 'mask-border-source': true, 'mask-border-width': true, 'mask-clip': true, 'mask-composite': true, 'mask-image': true, 'mask-mode': true, 'mask-origin': true, 'mask-position': true, 'mask-repeat': true, 'mask-size': true, 'mask-type': true, // and webkit versions '-webkit-clip-path': true, '-webkit-mask': true, '-webkit-mask-border': true, '-webkit-mask-border-mode': true, '-webkit-mask-border-outset': true, '-webkit-mask-border-repeat': true, '-webkit-mask-border-slice': true, '-webkit-mask-border-source': true, '-webkit-mask-border-width': true, '-webkit-mask-clip': true, '-webkit-mask-composite': true, '-webkit-mask-image': true, '-webkit-mask-mode': true, '-webkit-mask-origin': true, '-webkit-mask-position': true, '-webkit-mask-repeat': true, '-webkit-mask-size': true, '-webkit-mask-type': true, }; var cssMatchesPseudo = checkSelector([ ':is', ':where', ':matches', ':any', ':-webkit-any', ':-webkit-is', ':-webkit-where', ':-webkit-matches', ]); var cssMathFunctions = { '': ['min(', 'max(', 'clamp('], }; /** * Allows a media query to be set based on the presence and accuracy of the user's pointing device, and whether they have the ability to hover over elements on the page. This includes the `pointer`, `any-pointer`, `hover`, and `any-hover` media features. * @see https://caniuse.com/css-media-interaction */ /** * @type {import('../features').Feature} */ var cssMediaInteraction = checkAtRule('media', [ 'pointer', 'any-pointer', 'hover', 'any-hover', ]); var cssMediaRangeSyntax = checkAtRule('media', [' > ', ' < ', ' <= ', ' >= ']); var cssMediaResolution = checkAtRule('media', ['min-resolution', 'max-resolution', 'resolution', 'device-pixel-ratio']); /** * No description available. * @see This feature comes from MDN: https://developer.mozilla.org/en-US/search?q=Media+Queries:+scripting+media+feature */ /** * @type {import('../features').Feature} */ var cssMediaScripting = checkAtRule('media', ['scripting']); var cssMediaqueries = checkAtRule(['media']); var cssMixblendmode = { 'mix-blend-mode': true, }; var cssMotionPaths = { 'offset-path': true, }; /** * Using the `@namespace` at-rule, elements of other namespaces (e.g. SVG) can be targeted using the pipe (`|`) selector. * @see https://caniuse.com/css-namespaces */ /** * @type {import('../features').Feature} */ var cssNamespaces = checkAtRule('namespace'); /** * CSS nesting provides the ability to nest one style rule inside another, with the selector of the child rule relative to the selector of the parent rule. Similar behavior previously required a CSS pre-processor. * @see https://caniuse.com/css-nesting */ /** * @type {import('../features').Feature} */ var cssNesting = (rule) => { if (rule.type === 'rule' && rule.parent.type === 'rule') { return true; } if (rule.type === 'atrule' && rule.parent.type === 'rule') { return true; } return false; }; /** * Selectors Level 3 only allowed `:not()` pseudo-class to accept a single simple selector, which the element must not match any of. Thus, `:not(a, .b, [c])` or `:not(a.b[c])` did not work. Selectors Level 4 allows `:not()` to accept a list of selectors. Thus, `:not(a):not(.b):not([c])` can instead be written as `:not(a, .b, [c])` and `:not(a.b[c])` works as intended. * @see https://caniuse.com/css-not-sel-list */ /** * @type {import('../features').Feature} */ var cssNotSelList = checkSelector( (selector) => selector.includes(':not(') && !/:not\(\s*.?#?-?[\w[\]-]+\s*\)/.test(selector), ); /** * The newest versions of `:nth-child()` and `:nth-last-child()` accept an optional `of S` clause which filters the children to only those which match the selector list `S`. For example, `:nth-child(1 of .foo)` selects the first child among the children that have the `foo` class (ignoring any non-`foo` children which precede that child). Similar to `:nth-of-type`, but for arbitrary selectors instead of only type selectors. * @see https://caniuse.com/css-nth-child-of */ /** * @type {import('../features').Feature} */ var cssNthChildOf = checkSelector( (selector) => /:nth-(child|last-child)\(\s*\d+\s*of/.test(selector), ); var cssOpacity = { 'opacity': true, }; /** * The `:optional` pseudo-class matches form inputs (`<input>`, `<textarea>`, `<select>`) which are not `:required`. * @see https://caniuse.com/css-optional-pseudo */ /** * @type {import('../features').Feature} */ var cssOptionalPseudo = checkSelector(':optional'); /** * Originally a single property for controlling overflowing content in both horizontal & vertical directions, the `overflow` property is now a shorthand for `overflow-x` & `overflow-y`. The latest version of the specification also introduces the `clip` value that blocks programmatic scrolling. * @see https://caniuse.com/css-overflow */ /** * @type {import('../features').Feature} */ var cssOverflow = { 'overflow': true, 'overflow-x': true, 'overflow-y': true, }; /** * Changes in DOM elements above the visible region of a scrolling box can result in the page moving while the user is in the middle of consuming the content. By default, the value of `overflow-anchor` is `auto`, it can mitigate this jarring user experience by keeping track of the position of an anchor node and adjusting the scroll offset accordingly * @see https://caniuse.com/css-overflow-anchor */ /** * @type {import('../features').Feature} */ var cssOverflowAnchor = { 'overflow-anchor': true, }; var cssOverflowOverlay = { 'overflow': 'overlay', }; var cssOverscrollBehavior = { 'overscroll-behavior': true, 'overscroll-behavior-x': true, 'overscroll-behavior-y': true, }; /** * Properties to control the way elements are broken across (printed) pages. * @see https://caniuse.com/css-page-break */ /** * @type {import('../features').Feature} */ var cssPageBreak = { 'page-break-after': true, 'page-break-before': true, 'page-break-inside': true, }; /** * CSS at-rule (`@page`) to define page-specific rules when printing web pages, such as margin per page and page dimensions. * @see https://caniuse.com/css-paged-media */ /** * @type {import('../features').Feature} */ var cssPagedMedia = checkAtRule('page'); /** * Allows programmatic generation of images used by CSS * @see https://caniuse.com/css-paint-api */ /** * @type {import('../features').Feature} */ var cssPaintApi = { '': 'paint(' }; var cssPlaceholder = [ checkSelector(':placeholder'), ]; /** * :placeholder-shown CSS pseudo-class * Input elements can sometimes show placeholder text as a hint to the user on what to type in. See, for example, the placeholder attribute in HTML5. The :placeholder-shown pseudo-class matches an input element that is showing such placeholder text. * @see https://caniuse.com/css-placeholder-shown */ /** * @type {import('../features').Feature} */ var cssPlaceholderShown = checkSelector(':placeholder-shown'); var cssPrintColorAdjust = { 'print-color-adjust': true, }; /** * CSS :read-only and :read-write selectors * :read-only and :read-write pseudo-classes to match elements which are considered user-alterable * @see https://caniuse.com/css-read-only-write */ /** * @type {import('../features').Feature} */ var cssReadOnlyWrite = checkSelector([':read-only', ':read-write']); /** * Rebeccapurple color * The new color added in CSS Color Module Level 4 * @see https://caniuse.com/css-rebeccapurple */ /** * @type {import('../features').Feature} */ var cssRebeccapurple = { '': 'rebeccapurple', }; var cssReflections = { 'box-reflect': true, '-webkit-box-reflect': true, }; /** * CSS Regions * Method of flowing content into multiple elements, allowing magazine-like layouts. While once supported in WebKit-based browsers and Internet Explorer, implementing the feature is no longer being pursued by any browser. * @see https://caniuse.com/css-regions */ /** * @type {import('../features').Feature} */ var cssRegions = { 'flow-into': true, 'flow-from': true, '-webkit-flow-into': true, '-webkit-flow-from': true, }; /** * CSS Relative colors * The CSS Relative Color syntax allows a color to be defined relative to another color using the `from` keyword and optionally `calc()` for any of the color values. * @see https://caniuse.com/css-relative-colors */ /** * @type {import('../features').Feature} */ var cssRelativeColors = { '': /((rgb)|(rgba)|(hsl)|(hsla)|(hwb))\(\s*from/, }; var cssRepeatingGradients = { '': [ 'repeating-linear-gradient', 'repeating-radial-gradient', ], }; var cssResize = { 'resize': true, }; var cssRevertValue = { '': 'revert', }; /** * #rrggbbaa hex color notation * The CSS Color Module Level 4 defines new 4 & 8 character hex notation for color to include the opacity level. * @see https://caniuse.com/css-rrggbbaa */ /** * @type {import('../features').Feature} */ var cssRrggbbaa = { '': [ /#[\da-f]{8}/i, /#[\da-f]{4}(\W|$)/i, ], }; /** * CSS Scroll-behavior * Method of specifying the scrolling behavior for a scrolling box, when scrolling happens due to navigation or CSSOM scrolling APIs. * @see https://caniuse.com/css-scroll-behavior */ /** * @type {import('../features').Feature} */ var cssScrollBehavior = { 'scroll-behavior': true, }; var cssScrollbar = { 'scrollbar-color': true, 'scrollbar-width': true, }; const ATTRIBUTE_IDENTIFIER$1 = '[^\\~|^$*\\]]*'; // Export for testing const REGEXES$1 = { 'HAS_ATTRIBUTE': new RegExp(`\\[${ATTRIBUTE_IDENTIFIER$1}\\]`), 'MATCH_ATTRIBUTE': new RegExp(`\\[${ATTRIBUTE_IDENTIFIER$1}=${ATTRIBUTE_IDENTIFIER$1}\\]`), 'WORD_ATTRIBUTE': new RegExp(`\\[${ATTRIBUTE_IDENTIFIER$1}\\~=${ATTRIBUTE_IDENTIFIER$1}\\]`), 'SUBCODE_ATTRIBUTE': new RegExp(`\\[${ATTRIBUTE_IDENTIFIER$1}\\|=${ATTRIBUTE_IDENTIFIER$1}\\]`), 'BRACKET_PARENS': /(\[[^=\]]*(=("[^"]*")|[^\]]+|)])|(\([^)]*\))/g, }; /** * @param {RegExp} pattern * @return {(str:string) => boolean} */ function matchOutsideOfBrackets(pattern) { if (!(pattern instanceof RegExp)) { throw new TypeError('matchOutsideOfBrackets expects a RegExp'); } return (string) => pattern.test(string.replaceAll(REGEXES$1.BRACKET_PARENS, '')); } var cssSel2 = checkSelector([ ':first-child', ':link', ':visited', ':active', ':hover', ':focus', ':lang', REGEXES$1.HAS_ATTRIBUTE, REGEXES$1.MATCH_ATTRIBUTE, REGEXES$1.WORD_ATTRIBUTE, REGEXES$1.SUBCODE_ATTRIBUTE, matchOutsideOfBrackets(/\*/), matchOutsideOfBrackets(/>/), matchOutsideOfBrackets(/\+/), matchOutsideOfBrackets(/\./), matchOutsideOfBrackets(/#/), ]); const ATTRIBUTE_IDENTIFIER = '[^\\~|^$*\\]]*'; // Export for testing const REGEXES = { 'SIBLING_SELECTOR': /^[^[]*~/, 'STARTSWITH_ATTRIBUTE': new RegExp(`\\[${ATTRIBUTE_IDENTIFIER}\\^=${ATTRIBUTE_IDENTIFIER}\\]`), 'ENDSWITH_ATTRIBUTE': new RegExp(`\\[${ATTRIBUTE_IDENTIFIER}\\$=${ATTRIBUTE_IDENTIFIER}\\]`), 'INSENSITIVE_ATTRIBUTE': new RegExp(`\\[${ATTRIBUTE_IDENTIFIER}\\*=${ATTRIBUTE_IDENTIFIER}\\]`), }; var cssSel3 = checkSelector([ ':root', ':nth-child', ':nth-last-child', 'nth-of-type', 'nth-last-of-type', ':last-child', ':first-of-type', ':last-of-type', ':only-child', ':only-of-type', ':empty', ':target', ':enabled', ':disabled', ':checked', ':not', REGEXES.SIBLING_SELECTOR, REGEXES.STARTSWITH_ATTRIBUTE, REGEXES.ENDSWITH_ATTRIBUTE, REGEXES.INSENSITIVE_ATTRIBUTE, ]); var cssSelection = checkSelector(':selection'); var cssShapes = { 'shape-outside': true, 'shape-image-threshold': true, 'shape-margin': true, }; /** * CSS Scroll Snap * CSS technique that allows customizable scrolling experiences like pagination of carousels by setting defined snap positions. * @see https://caniuse.com/css-snappoints */ /** * @type {import('../features').Feature} */ var cssSnappoints = { 'scroll-snap-type': true, 'scroll-padding': true, 'scroll-padding-top': true, 'scroll-padding-right': true, 'scroll-padding-bottom': true, 'scroll-padding-left': true, 'scroll-padding-inline': true, 'scroll-padding-inline-start': true, 'scroll-padding-inline-end': true, 'scroll-padding-block': true, 'scroll-padding-block-start': true, 'scroll-padding-block-end': true, 'scroll-snap-align': true, 'scroll-margin': true, 'scroll-margin-top': true, 'scroll-margin-right': true, 'scroll-margin-bottom': true, 'scroll-margin-left': true, 'scroll-margin-inline': true, 'scroll-margin-inline-start': true, 'scroll-margin-inline-end': true, 'scroll-margin-block': true, 'scroll-margin-block-start': true, 'scroll-margin-block-end': true, 'scroll-snap-stop': true, }; var cssSticky = { 'position': 'sticky', }; /** * CSS Subgrid * Feature of the CSS Grid Layout Module Level 2 that allows a grid-item with its own grid to align in one or both dimensions with its parent grid. * @see https://caniuse.com/css-subgrid */ /** * @type {import('../features').Feature} */ var cssSubgrid = { '': 'subgrid', }; /** * CSS.supports() API * The CSS.supports() static methods returns a Boolean value indicating if the browser supports a given CSS feature, or not. * @see https://caniuse.com/css-supports-api */ /** * @type {import('../features').Feature} */ var cssSupportsApi = {}; /* this feature appears in JS, so it's not linted by this tool */ var cssTable = { 'display': [ 'table', 'table-', 'inline-table', ], }; /** * CSS3 text-align-last * CSS property to describe how the last line of a block or a line right before a forced line break when `text-align` is `justify`. * @see https://caniuse.com/css-text-align-last */ /** * @type {import('../features').Feature} */ var cssTextAlignLast = { 'text-align-last': true, }; /** * CSS text-box-trim & text-box-edge * Provides the ability to remove the vertical space appearing above and below text glyphs, allowing more precise positioning and alignment. Previously specified as the `leading-trim` & `text-edge` properties. * @see https://caniuse.com/css-text-box-trim */ /** * @type {import('../features').Feature} */ var cssTextBoxTrim = { 'text-box-trim': true, 'text-box-edge': true, }; /** * CSS text-indent * The `text-indent` property applies indentation to lines of inline content in a block. * @see https://caniuse.com/css-text-indent */ /** * @type {import('../features').Feature} */ var cssTextIndent = { 'text-indent': true, }; /** * CSS text-justify * CSS property to define how text should be justified when `text-align: justify` is set. * @see https://caniuse.com/css-text-justify */ /** * @type {import('../features').Feature} */ var cssTextJustify = { 'text-justify': true, }; /** * CSS text-orientation * The CSS `text-orientation` property specifies the orientation of text within a line. Current values only have an effect in vertical typographic modes (defined with the `writing-mode` property) * @see https://caniuse.com/css-text-orientation */ /** * @type {import('../features').Feature} */ var cssTextOrientation = { 'text-orientation': true, }; /** * CSS Text 4 text-spacing * No description available. * @see This feature comes from MDN: https://developer.mozilla.org/en-US/search?q=CSS+Text+4+text-spacing */ /** * @type {import('../features').Feature} */ var cssTextSpacing = { 'text-spacing': true, }; var cssTextshadow = { 'text-shadow': true, }; var cssTouchAction = { 'touch-action': true, }; var cssTransitions = { 'transition': true, 'transition-property': true, 'transition-duration': true, 'transition-delay': true, 'transition-timing-function': true, }; /** * CSS unicode-bidi property * No description available. * @see This feature comes from MDN: https://developer.mozilla.org/en-US/search?q=CSS+unicode-bidi+property */ /** * @type {import('../features').Feature} */ var cssUnicodeBidi = { 'unicode-bidi': true, }; var cssUnsetValue = { '': 'unset', }; /** * @param {import('postcss').ChildNode} rule * @return {boolean} */ var cssVariables = (rule) => { if (rule.type !== 'decl') return false; return (rule.prop.startsWith('--') || rule.value.includes('var(')); }; /** * CSS @when / @else conditional rules * Syntax allowing CSS conditions (like media and support queries) to be written more simply, as well as making it possibly to write mutually exclusive rules using `@else` statements. * @see https://caniuse.com/css-when-else */ /** * @type {import('../features').Feature} */ var cssWhenElse = checkAtRule(['when', 'else']); /** * CSS widows & orphans * CSS properties to control when lines break across pages or columns by defining the amount of lines that must be left before or after the break. * @see https://caniuse.com/css-widows-orphans */ /** * @type {import('../features').Feature} */ var cssWidowsOrphans = { 'widows': true, 'orphans': true, }; /** * width: stretch property * No description available. * @see This feature comes from MDN: https://developer.mozilla.org/en-US/search?q=width:+stretch+property */ /** * @type {import('../features').Feature} */ var cssWidthStretch = { 'width': 'stretch', }; /** * CSS writing-mode property * Property to define whether lines of text are laid out horizontally or vertically and the direction in which blocks progress. * @see https://caniuse.com/css-writing-mode */ /** * @type {import('../features').Feature} */ var cssWritingMode = { 'writing-mode': true, }; /** * CSS zoom * Non-standard method of scaling content. * @see https://caniuse.com/css-zoom */ /** * @type {import('../features').Feature} */ var cssZoom = { 'zoom': true, }; /** * CSS3 attr() function for all properties * While `attr()` is supported for effectively all browsers for the `content` property, CSS Values and Units Level 5 adds the ability to use `attr()` on **any** CSS property, and to use it for non-string values (e.g. numbers, colors). * @see https://caniuse.com/css3-attr */ /** * @type {import('../features').Feature} */ var css3Attr = (rule) => { if (rule.type !== 'decl') return false; if (rule.prop === 'content') { return false; } return rule.toString().includes('attr('); }; var css3Boxsizing = { 'box-sizing': true, }; var css3Colors = { '': [ 'rgba', 'hsl', 'hsla', ], }; var css3Cursors = { 'cursor': [ 'none', 'context-menu', 'cell', 'vertical-text', 'alias', 'copy', 'no-drop', 'not-allowed', 'nesw-resize', 'nwse-resize', 'col-resize', 'row-resize', 'all-scroll', ], }; var css3CursorsGrab = { 'cursor': ['grab', 'grabbing'], }; var css3CursorsNewer = { 'cursor': ['zoom-in', 'zoom-out'], }; var css3Tabsize = { 'tab-size': true, }; /** * CSS currentColor value * A CSS value that will apply the existing `color` value to other properties like `background-color`, etc. * @see https://caniuse.com/currentcolor */ /** * @type {import('../features').Feature} */ var currentcolor = { '': 'currentColor', }; /** * Window.devicePixelRatio * Read-only property that returns the ratio of the (vertical) size of one physical pixel on the current display device to the size of one CSS pixel. * @see https://caniuse.com/devicepixelratio */ /** * @type {import('../features').Feature} */ var devicepixelratio = {}; /* this feature is found in JS and is not linted by this tool */ /** * ui-serif, ui-sans-serif, ui-monospace and ui-rounded values for font-family * Allows more control when choosing system interface fonts * @see https://caniuse.com/extended-system-fonts */ /** * @type {import('../features').Feature} */ var extendedSystemFonts = { 'font-family': ['ui-serif', 'ui-sans-serif', 'ui-monospace', 'ui-rounded'], }; var flexbox = { 'display': [ 'flex', 'inline-flex', ], 'flex': true, 'flex-grow': true, 'flex-shrink': true, 'flex-basis': true, 'flex-direction': true, 'flex-wrap': true, 'flex-flow': true, }; /** * gap property for Flexbox * `gap` for flexbox containers to create gaps/gutters between flex items * @see https://caniuse.com/flexbox-gap */ /** * @type {import('../features').Feature} */ var flexboxGap = (rule) => { if (!('some' in rule) || !rule.nodes) return false; let hasFlexbox = false; let hasGap = false; return rule.some((decl) => { if (decl.type !== 'decl') return false; hasFlexbox ||= decl.prop === 'display' && (decl.value === 'flex' || decl.value === 'inline-flex'); hasGap ||= decl.prop === 'gap' || decl.prop === 'column-gap' || decl.prop === 'row-gap'; return hasFlexbox && hasGap; }); }; /** * display: flow-root * The element generates a block container box, and lays out its contents using flow layout. It always establishes a new block formatting context for its contents. It provides a better solution to the most use cases of the "clearfix" hack. * @see https://caniuse.com/flow-root */ /** * @type {import('../features').Feature} */ var flowRoot = { 'display': 'flow-root', }; /** * system-ui value for font-family * Value for `font-family` that represents the default user interface font. * @see https://caniuse.com/font-family-system-ui */ /** * @type {import('../features').Feature} */ var fontFamilySystemUi = { 'font-family': 'system-ui', }; var fontFeature = { 'font-feature-settings': true, 'font-variant-ligatures': true, 'font-language-override': true, 'font-kerning': true, }; /** * CSS3 font-kerning * Controls the usage of the kerning information (spacing between letters) stored in the font. Note that this only affects OpenType fonts with kerning information, it has no effect on other fonts. * @see https://caniuse.com/font-kerning */ /** * @type {import('../features').Feature} */ var fontKerning = { 'font-kerning': true, }; /** * CSS Font Loading * This CSS module defines a scripting interface to font faces in CSS, allowing font faces to be easily created and loaded from script. It also provides methods to track the loading status of an individual font, or of all the fonts on an entire page. * @see https://caniuse.com/font-loading */ /** * @type {import('../features').Feature} */ var fontLoading = {}; /* this feature appears in JS and as such is not linted */ var fontSizeAdjust = { 'font-size-adjust': true, }; /** * CSS font-smooth * Controls the application of anti-aliasing when fonts are rendered. * @see https://caniuse.com/font-smooth */ /** * @type {import('../features').Feature} */ var fontSmooth = { 'font-smooth': true, }; var fontUnicodeRange = { 'unicode-range': true, }; /** * CSS font-variant-alternates * Controls the usage of alternate glyphs associated to alternative names defined in @font-feature-values for certain types of OpenType fonts. * @see https://caniuse.com/font-variant-alternates */ /** * @type {import('../features').Feature} */ var fontVariantAlternates = { 'font-variant-alternates': true, }; /** * CSS font-variant-numeric * CSS property that provides different ways of displaying numbers, fractions, and ordinal markers. * @see https://caniuse.com/font-variant-numeric */ /** * @type {import('../features').Feature} */ var fontVariantNumeric = { 'font-variant-numeric': true, }; var fontface = [ checkAtRule('font-face'), ]; var fullscreen = checkSelector(':fullscreen'); /** * getComputedStyle * API to get the current computed CSS styles applied to an element. This may be the current value applied by an animation or as set by a stylesheet. * @see https://caniuse.com/getcomputedstyle */ /** * @type {import('../features').Feature} */ var getcomputedstyle = {}; /* this is a js api, so we don't lint it */ var inlineBlock = { 'display': 'inline-block', }; const VALUES = ['max-content', 'min-content', 'fit-content', 'fill-available', 'stretch', 'fill']; var intrinsicWidth = { 'width': VALUES, 'min-width': VALUES, 'max-width': VALUES, 'height': VALUES, 'min-height': VALUES, 'max-height': VALUES, }; /** * CSS justify-content: space-evenly * The "space-evenly" value for the `justify-content` property distributes the space between items evenly. It is similar to space-around but provides equal instead of half-sized space on the edges. Can be used in both CSS flexbox & grid. * @see https://caniuse.com/justify-content-space-evenly */ /** * @type {import('../features').Feature} */ var justifyContentSpaceEvenly = { 'justify-content': 'space-evenly', }; var kerningPairsLigatures = { 'text-rendering': 'optimizeLegibility', }; var mdnCssUnicodeBidiIsolate = { 'unicode-bidi': 'isolate', }; var mdnCssUnicodeBidiIsolateOverride = { 'unicode-bidi': 'isolate-override', }; var mdnCssUnicodeBidiPlaintext = { 'unicode-bidi': 'plaintext', }; var mdnTextDecorationColor = { 'text-decoration-color': true, }; var mdnTextDecorationLine = { 'text-decoration-line': true, }; var mdnTextDecorationShorthand = { 'text-decoration': true, }; var mdnTextDecorationStyle = { 'text-decoration-style': true, }; var minmaxwh = { 'min-width': true, 'max-width': true, 'min-height': true, 'max-height': true, }; /** * @param {import('postcss').ChildNode} rule * @return {boolean} */ var multibackgrounds = (rule) => { if (rule.type !== 'decl') return false; if (!/^background-?/.test(rule.prop)) return false; return postcss.list.comma(rule.value).length > 1; }; var multicolumn = { 'columns': true, 'column-width': true, 'column-gap': true, 'column-rule': true, 'column-rule-color': true, 'column-rule-width': true, 'column-count': true, 'column-rule-style': true, 'column-span': true, 'column-fill': true, 'break-before': true, 'break-after': true, 'break-inside': true, }; var objectFit = { 'object-fit': true, 'object-position': true, }; var outline = { 'outline': true, 'outline-style': true, 'outline-width': true, 'outline-color': true, }; var pointer = { 'touch-action': true, }; var pointerEvents = { 'pointer-events': true, }; /** * prefers-color-scheme media query * Media query to detect if the user has set their system to use a light or dark color theme. * @see https://caniuse.com/prefers-color-scheme */ /** * @type {import('../features