diff --git a/.prettierrc b/.prettierrc index ca9ac21b..a72a84f5 100644 --- a/.prettierrc +++ b/.prettierrc @@ -1,17 +1,9 @@ { - "plugins": [ - "prettier-plugin-brace-style", - "prettier-plugin-space-before-function-paren", - "prettier-plugin-merge" - ], - "braceStyle": "stroustrup", - "arrowParens": "avoid", - "bracketSpacing": true, - "endOfLine": "auto", - "semi": true, - "singleQuote": true, - "tabWidth": 4, - "useTabs": true, - "trailingComma": "all", - "printWidth": 100 + "singleQuote": true, + "semi": true, + "tabWidth": 4, + "printWidth": 120, + "arrowParens": "avoid", + "trailingComma": "all", + "bracketSpacing": true } diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index 27b2b36c..00000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "editor.formatOnSave": true, - "editor.formatOnSaveMode": "file", - "editor.defaultFormatter": "esbenp.prettier-vscode", - "prettier.enable": true, - "debug.enableStatusBarColor": false -} diff --git a/_headers b/_headers deleted file mode 100644 index cb95f630..00000000 --- a/_headers +++ /dev/null @@ -1,2 +0,0 @@ -/* - Access-Control-Allow-Origin: * diff --git a/_redirects b/_redirects new file mode 100644 index 00000000..dce8970c --- /dev/null +++ b/_redirects @@ -0,0 +1 @@ +node_modules/:modulename/* https://cdn.jsdelivr.net/npm/:modulename@latest/:splat 200 diff --git a/favicon.ico b/favicon.ico new file mode 100644 index 00000000..7fda0a8a Binary files /dev/null and b/favicon.ico differ diff --git a/features/compat.js b/features/compat.js deleted file mode 100644 index f7181ffb..00000000 --- a/features/compat.js +++ /dev/null @@ -1,18 +0,0 @@ -export default { - id: 'compat', - title: 'Compatibility', - group: 'whatwg', - link: 'compat', - status: 'stable', - properties: { - 'touch-action': { - link: '#touch-action', - values: [ - 'pinch-zoom', - 'pan-x pinch-zoom', - 'pan-y pinch-zoom', - 'pan-x pan-y pinch-zoom', - ], - }, - }, -}; diff --git a/features/css-animation-worklet-1.js b/features/css-animation-worklet-1.js deleted file mode 100644 index f199fb3b..00000000 --- a/features/css-animation-worklet-1.js +++ /dev/null @@ -1,22 +0,0 @@ -export default { - id: 'css-animation-worklet-1', - title: 'CSS Animation Worklet Level 1', - group: 'houdini', - link: 'css-animation-worklet-1', - status: 'experimental', - globals: { - CSS: { - link: '#animation-worklet-desc', - properties: { - animationWorklet: { - instanceof: 'Worklet', - } - }, - }, - WorkletAnimation: { - link: '#worklet-animation-interface', - extends: 'Animation', - members: ['animatorName'], - }, - }, -}; diff --git a/features/css-borders-4.js b/features/css-borders-4.js deleted file mode 100644 index d78b1b9b..00000000 --- a/features/css-borders-4.js +++ /dev/null @@ -1,176 +0,0 @@ -const border_radius_tests = [ - '0', - '50%', - '250px 100px', - '10px 20px', - '50% 10%', - '250px / 50px', - '50% / 10%', - '250px 100px / 50px', - '250px / 50px 10px', - '250px 100px / 50px 10px', -]; -const corner_shape_tests = [ - 'round', - 'scoop', - 'bevel', - 'notch', - 'square', - 'squircle', - 'superellipse(4)', - 'superellipse(infinity)', -]; -const border_clip_tests = [ - 'normal', - '10px', - '10%', - '1fr', - '10px 20px', - '10% 20%', - '1fr 2fr', - '10px 20% 1fr', -]; - -export default { - id: 'css-borders-4', - title: 'CSS Borders and Box Decorations Module Level 4', - link: 'css-borders-4', - status: 'experimental', - values: { - 'stripes()': { - link: '#border-color', - property: 'border-color', - args: [ - 'red, yellow, green, blue', - 'red 1px, yellow 2px', - 'red 10%, yellow 20%', - 'red 1fr, yellow 2fr', - ], - }, - }, - properties: { - 'border--radius': { - link: '#corner-sizing-side-shorthands', - values: border_radius_tests, - children: [ - 'border-top-radius', 'border-right-radius', 'border-bottom-radius', 'border-left-radius', - 'border-block-start-radius', 'border-block-end-radius', 'border-inline-start-radius', 'border-inline-end-radius', - ], - }, - 'corner-shape': { - isGroup: true, - link: '#corner-shaping', - values: corner_shape_tests, - children: { - 'corner-shape': { - values: [ - ...corner_shape_tests, - 'round scoop', - 'round scoop bevel', - 'round scoop bevel notch', - ] - }, - 'corner--shape': { - link: '#corner-shape-shorthands', - values: corner_shape_tests, - children: [ - 'corner-top-shape', 'corner-right-shape', 'corner-bottom-shape', 'corner-left-shape', - 'corner-block-start-shape', 'corner-block-end-shape', 'corner-inline-start-shape', 'corner-inline-end-shape', - ], - }, - 'corner--shape': { - values: corner_shape_tests, - children: [ - 'corner-top-left-shape', 'corner-top-right-shape', 'corner-bottom-right-shape', 'corner-bottom-left-shape', - 'corner-start-start-shape', 'corner-start-end-shape', 'corner-end-end-shape', 'corner-end-start-shape', - ], - }, - }, - }, - 'border-limit': { - link: '#border-limit', - tests: [ - 'all', - 'sides', - 'corners', - 'sides 10px', - 'corners 10px', - 'sides 5%', - 'corners 5%', - 'top 10px', - 'right 10px', - 'bottom 10px', - 'left 10px', - 'top 5%', - 'right 5%', - 'bottom 5%', - 'left 5%', - ], - }, - 'border-clip': { - link: '#border-clip', - values: border_clip_tests, - children: [ - 'border-clip', - 'border-clip-top', 'border-clip-right', 'border-clip-bottom', 'border-clip-left', - 'border-clip-block-start', 'border-clip-block-end', 'border-clip-inline-start', 'border-clip-inline-end', - ], - }, - 'box-shadow-*': { - isGroup: true, - titleMd: '`box-shadow` longhands', - children: { - 'box-shadow-color': { - link: '#box-shadow-color', - }, - 'box-shadow-offset': { - link: '#box-shadow-offset', - }, - 'box-shadow-blur': { - link: '#box-shadow-blur', - }, - 'box-shadow-spread': { - link: '#box-shadow-spread', - }, - 'box-shadow-position': { - link: '#box-shadow-position', - }, - }, - }, - - 'border-shape': { - link: '#border-shape', - values: [ - 'none', - 'inset(10% round 10% 40% 10% 40%)', - 'ellipse(at top 50% left 20%)', - 'circle(at top left)', - 'polygon(100% 0, 100% 100%, 0 100%)', - "path('M 0 0')", - 'rect(10% 20px 30% 40px)', - 'xywh(10% 40% 100px 200px round 10% 40% 10% 40%)', - 'url(image.png)', - 'margin-box', - 'border-box', - 'padding-box', - 'content-box', - 'fill-box', - 'stroke-box', - 'view-box', - { - id: ' margin-box', - isGroup: false, - children: [ - 'inset(10% round 10% 40% 10% 40%) margin-box', - 'ellipse(at top 50% left 20%) margin-box', - 'circle(at top left) margin-box', - 'polygon(100% 0, 100% 100%, 0 100%) margin-box', - "path('M 0 0') margin-box", - 'rect(10% 20px 30% 40px) margin-box', - 'xywh(10% 40% 100px 200px round 10% 40% 10% 40%) margin-box', - ] - }, - ], - }, - }, -}; diff --git a/features/css-box-3.js b/features/css-box-3.js deleted file mode 100644 index 700c1428..00000000 --- a/features/css-box-3.js +++ /dev/null @@ -1,8 +0,0 @@ -export default { - id: 'css-box-3', - title: 'CSS Box Model Module Level 3', - link: 'css-box-3', - status: 'stable', - firstSnapshot: 2020, - // No new features since 2.2, see https://drafts.csswg.org/css-box-3/#changes-since-L2 -}; diff --git a/features/css-cascade-5.js b/features/css-cascade-5.js deleted file mode 100644 index 0547590a..00000000 --- a/features/css-cascade-5.js +++ /dev/null @@ -1,51 +0,0 @@ -export default { - id: 'css-cascade-5', - title: 'CSS Cascading and Inheritance Level 5', - link: 'css-cascade-5', - status: 'experimental', - values: { - properties: ['color', 'font-weight', 'background-image', 'all'], - 'revert-layer': { - link: '#revert-layer', - tests: 'revert-layer', - }, - }, - properties: { - all: { - link: '#revert-layer', - tests: 'revert-layer', - }, - }, - atrules: { - '@layer': { - link: '#at-layer', - prelude: 'foo', - children: [ - {/* block */}, - {prelude: 'foo, bar', contents: false}, - - ] - }, - }, - globals: { - CSSImportRule: { - link: '#extensions-to-cssimportrule-interface', - mdnGroup: 'DOM', - members: ['layerName'], - }, - - CSSLayerBlockRule: { - link: '#the-csslayerblockrule-interface', - mdnGroup: 'DOM', - extends: 'CSSGroupingRule', - members: ['name'], - }, - - CSSLayerStatementRule: { - link: '#the-csslayerstatementrule-interface', - mdnGroup: 'DOM', - extends: 'CSSRule', - members: ['nameList'], - }, - }, -}; diff --git a/features/css-cascade-6.js b/features/css-cascade-6.js deleted file mode 100644 index 35dc628e..00000000 --- a/features/css-cascade-6.js +++ /dev/null @@ -1,23 +0,0 @@ -export default { - id: 'css-cascade-6', - title: 'CSS Cascading and Inheritance Level 6', - link: 'css-cascade-6', - status: 'experimental', - atrules: { - '@scope': { - link: '#scope-atrule', - preludes: [ - '(.foo)', - '(.foo) to (.bar)', - ], - }, - }, - globals: { - CSSScopeRule: { - link: '#the-cssscoperule-interface', - mdnGroup: 'DOM', - extends: 'CSSGroupingRule', - members: ['start', 'end'], - } - } -} diff --git a/features/css-color-4.js b/features/css-color-4.js deleted file mode 100644 index 7888452d..00000000 --- a/features/css-color-4.js +++ /dev/null @@ -1,163 +0,0 @@ -function get_color_args ({prefix = '', hueIndex} = {}) { - let a = hueIndex === 0 ? 'deg' : '%'; - let b = hueIndex === 1 ? 'deg' : '%'; - let c = hueIndex === 2 ? 'deg' : '%'; - - return [ - '0 0 0', - '0 0 0 / .5', - '0 0 0 / 50%', - `0${a} 0 0`, - `0 0${b} 0`, - `0 0 0${c}`, - 'none none none', - '0 0 0 / none', - ].map(arg => `${prefix}${arg}`); -} - -export default { - id: 'css-color-4', - title: 'CSS Color Module Level 4', - link: 'css-color-4', - status: 'stable', - firstSnapshot: 2022, - values: { - 'rgb_hsl_extensions': { - titleMd: 'Extensions to `rgb()`, `rgba()`, `hsl()`, `hsla()`', - descriptionMd: 'Comma-less syntax, optional alpha, mixing types, `none`, `` for hue, `` for any component', - mdnGroup: 'CSS/color_value', - children: { - 'rgb()': { - link: '#funcdef-rgb', - dataType: 'color', - args: get_color_args(), - }, - 'rgba()': { - link: '#funcdef-rgba', - dataType: 'color', - args: get_color_args(), - }, - 'hsl()': { - link: '#funcdef-hsl', - dataType: 'color', - args: get_color_args({hueIndex: 0}), - }, - 'hsla()': { - link: '#funcdef-hsla', - dataType: 'color', - args: get_color_args({hueIndex: 0}), - }, - } - }, - - Hex: { - link: '#hex-notation', - dataType: 'color', - children: { - '#RGBA': { - value: '#0008', - }, - '#RRGGBBAA': { - value: '#00000088', - }, - }, - }, - rebeccapurple: { - link: '#named-colors', - mdn: 'color_value', - dataType: 'color', - }, - 'system colors': { - link: '#css-system-colors', - mdn: 'color_value', - dataType: 'color', - children: [ - 'Canvas', - 'CanvasText', - 'LinkText', - 'VisitedText', - 'ActiveText', - 'ButtonFace', - 'Field', - 'FieldText', - 'Highlight', - 'HighlightText', - 'GrayText', - ], - }, - 'hwb()': { - link: '#the-hwb-notation', - mdn: 'color_value/hwb', - dataType: 'color', - args: get_color_args({hueIndex: 0}), - }, - 'lab()': { - link: '#specifying-lab-lch', - mdn: 'color_value/lab', - dataType: 'color', - args: get_color_args(), - }, - 'oklab()': { - link: '#specifying-oklab-lch', - mdn: 'color_value/oklab', - dataType: 'color', - args: get_color_args(), - }, - 'lch()': { - link: '#specifying-lch-lch', - mdn: 'color_value/lch', - dataType: 'color', - args: get_color_args({hueIndex: 2}), - }, - 'oklch()': { - link: '#specifying-oklch-lch', - mdn: 'color_value/oklch', - dataType: 'color', - args: get_color_args({hueIndex: 2}), - }, - 'color()': { - link: '#color-function', - mdn: 'color_value/color', - dataType: 'color', - children: { - 'srgb': { - args: get_color_args({prefix: 'srgb '}), - }, - 'srgb-linear': { - args: get_color_args({prefix: 'srgb-linear '}), - }, - 'display-p3': { - args: get_color_args({prefix: 'display-p3 '}), - }, - 'display-p3-linear': { - args: get_color_args({prefix: 'display-p3-linear '}), - }, - 'a98-rgb': { - args: get_color_args({prefix: 'a98-rgb '}), - }, - 'prophoto-rgb': { - args: get_color_args({prefix: 'prophoto-rgb '}), - }, - 'rec2020': { - args: get_color_args({prefix: 'rec2020 '}), - }, - 'xyz': { - args: get_color_args({prefix: 'xyz '}), - }, - 'xyz-d50': { - args: get_color_args({prefix: 'xyz-d50 '}), - }, - 'xyz-d65': { - args: get_color_args({prefix: 'xyz-d65 '}), - } - }, - }, - }, - properties: { - opacity: { - link: '#transparency', - titleMd: 'Percentages in `opacity`', - value: '50%', - } - } -}; diff --git a/features/css-conditional-3.js b/features/css-conditional-3.js deleted file mode 100644 index 34a802b0..00000000 --- a/features/css-conditional-3.js +++ /dev/null @@ -1,52 +0,0 @@ -export default { - id: 'css-conditional-3', - title: 'CSS Conditional Rules Module Level 3', - link: 'css-conditional-3', - specLink: 'css3-conditional', - status: 'stable', - firstSnapshot: 2015, - atrules: { - '@supports': { - link: '#at-supports', - preludeRequired: true, - preludes: [ - '(color: green)', - 'not (color: green)', - '(color: green) or (color: red)', - '(color: green) and (color: red)', - '(color: green) and (not (foo: bar))', - '(color: green) or (not (foo: bar))', - ], - }, - }, - globals: { - CSSRule: { - link: '#extensions-to-cssrule-interface', - mdnGroup: 'DOM', - properties: ['SUPPORTS_RULE'], - }, - CSSConditionRule: { - link: '#the-cssconditionrule-interface', - mdnGroup: 'DOM', - extends: 'CSSGroupingRule', - members: ['conditionText'], - }, - CSSMediaRule: { - link: '#the-cssmediarule-interface', - mdnGroup: 'DOM', - extends: 'CSSConditionRule', - members: ['media', 'matches'], - }, - CSSSupportsRule: { - link: '#the-csssupportsrule-interface', - mdnGroup: 'DOM', - extends: 'CSSConditionRule', - members: ['matches'], - }, - CSS: { - link: '#the-css-namespace', - mdnGroup: 'DOM', - properties: ['supports'], - }, - }, -}; diff --git a/features/css-conditional-4.js b/features/css-conditional-4.js deleted file mode 100644 index 0745c772..00000000 --- a/features/css-conditional-4.js +++ /dev/null @@ -1,15 +0,0 @@ -export default { - id: 'css-conditional-4', - title: 'CSS Conditional Rules Module Level 4', - link: 'css-conditional-4', - status: 'experimental', - atrules: { - '@supports selector()': { - link: '#at-supports-ext', - args: [ - '.foo', - '::unknown-pseudo', - ], - }, - }, -}; diff --git a/features/css-conditional-5.js b/features/css-conditional-5.js deleted file mode 100644 index 61606ae6..00000000 --- a/features/css-conditional-5.js +++ /dev/null @@ -1,37 +0,0 @@ -export default { - id: 'css-conditional-5', - title: 'CSS Conditional Rules Module Level 5', - link: 'css-conditional-5', - status: 'experimental', - atrules: { - '@supports font-tech()': { - link: '#at-supports-ext', - arg: 'features-opentype', - }, - '@supports font-format()': { - link: '#at-supports-ext', - arg: 'woff2', - }, - '@when': { - link: '#when-rule', - preludeRequired: true, - preludes: [ - 'media(min-width: 0px)', - 'media(pointer)', - 'supports(color: red)', - ], - }, - '@else': { - link: '#else-rule', - preludeRequired: true, - contentBefore: '@when media(min-width: 0px) {} ', - preludes: [ - 'media(min-width: 200px)', - 'media(width >= 200px)', - 'media(pointer)', - 'supports(display: flex)', - ], - // TODO @else media(min-width: 0px) {} @else {} - }, - }, -}; diff --git a/features/css-contain-3.js b/features/css-contain-3.js deleted file mode 100644 index aa08aaf7..00000000 --- a/features/css-contain-3.js +++ /dev/null @@ -1,100 +0,0 @@ -export default { - id: 'css-contain-3', - title: 'CSS Containment Module Level 3', - link: 'css-contain-3', - status: 'experimental', - atrules: { - '@container': { - link: '#container-rule', - preludeRequired: true, - preludes: [ - '(min-width: 0px)', - '(max-width: 0px)', - '(width >= 0px)', - '(height >= 0px)', - '(inline-size >= 0px)', - '(block-size >= 0px)', - '(aspect-ratio >= 1 / 1)', - '(orientation = portrait)', - '(width >= 0px) and (orientation = portrait)', - '(width >= 0px) or (orientation: portrait)', - 'not (width < 0px)', - 'foo (width >= 0px)', - 'foo (inline-size > 0px) and style(--responsive = true)', - ], - }, - '@container style()': { - link: '#container-rule', - args: [ - '--foo', - '--foo: bar', - 'background-color', - 'background-color: red', - ], - }, - }, - values: { - properties: ['width'], - cqw: { - link: '#container-lengths', - mdn: 'length', - tests: '5cqw', - }, - cqh: { - link: '#container-lengths', - mdn: 'length', - tests: '5cqh', - }, - cqi: { - link: '#container-lengths', - mdn: 'length', - tests: '5cqi', - }, - cqb: { - link: '#container-lengths', - mdn: 'length', - tests: '5cqb', - }, - cqmin: { - link: '#container-lengths', - mdn: 'length', - tests: '5cqmin', - }, - cqmax: { - link: '#container-lengths', - mdn: 'length', - tests: '5cqmax', - }, - }, - properties: { - 'container-type': { - link: '#container-type', - tests: [ - 'normal', - 'size', - 'inline-size', - ], - }, - 'container-name': { - link: '#container-name', - tests: ['none', 'x', 'x y'], - }, - container: { - link: '#container-shorthand', - tests: [ - 'none', - 'x / normal', - 'x / size', - 'x / inline-size', - 'x y / size', - ], - }, - }, - globals: { - CSSContainerRule: { - link: '#the-csscontainerrule-interface', - mdnGroup: 'DOM', - extends: 'CSSConditionRule', - }, - }, -}; diff --git a/features/css-counter-styles-3.js b/features/css-counter-styles-3.js deleted file mode 100644 index ce690d26..00000000 --- a/features/css-counter-styles-3.js +++ /dev/null @@ -1,131 +0,0 @@ -export default { - id: 'css-counter-styles-3', - title: 'CSS Counter Styles Level 3', - link: 'css-counter-styles-3', - status: 'stable', - firstSnapshot: 2021, - atrules: { - '@counter-style': { - link: '#the-counter-style-rule', - isGroup: true, - preludeRequired: true, - prelude: 'foo', - descriptors: { - 'system': { - link: '#counter-style-system', - mdn: '@counter-style/system', - values: ['cyclic', 'numeric', 'alphabetic', 'symbolic', 'additive', 'fixed 3', 'extends decimal'], - }, - 'symbols': { - link: '#counter-style-symbols', - mdn: '@counter-style/symbols', - values: [ - 'A B C D E F', - "'\\24B6' '\\24B7' '\\24B8' D E F", - "'0' '1' '2' '4' '5' '6' '7'", - "'1' url('image.png') '2'", - "url('image1.png') url('image2.png') url('image3.png')", - 'custom-numbers', - ], - }, - 'additive-symbols': { - link: '#counter-style-additive-symbols', - mdn: '@counter-style/additive-symbols', - values: [ - '1000 M, 500 C', - '1000 M, 500 C, 100 L, 50 X', - ], - }, - 'negative': { - link: '#counter-style-negative', - mdn: '@counter-style/negative', - values: [ - '"--"', - '"(" ")"', - ], - }, - 'prefix': { - link: '#counter-style-prefix', - mdn: '@counter-style/prefix', - values: [ - 'a', // - '"a"', // - 'url(image.png)', // - ], - }, - 'suffix': { - link: '#counter-style-suffix', - mdn: '@counter-style/suffix', - values: [ - 'a', // - '"a"', // - 'url(image.png)', // - ], - }, - 'range': { - link: '#counter-style-range', - mdn: '@counter-style/range', - values: [ - 'auto', - '2 5', - 'infinite 10', - '10 infinite', - 'infinite infinite', - '2 5, 8 10', - 'infinite 8, 6 infinite', - ], - }, - 'pad': { - link: '#counter-style-pad', - mdn: '@counter-style/pad', - values: [ - '3 "0"', - '"0" 3', - ], - }, - 'speak-as': { - link: '#counter-style-speak-as', - mdn: '@counter-style/speak-as', - values: [ - 'auto', - 'bullets', - 'numbers', - 'words', - 'spell-out', - 'example-counter', - ], - }, - 'fallback': { - link: '#counter-style-fallback', - mdn: '@counter-style/fallback', - values: ['decimal'], - }, - }, - }, - }, - globals: { - CSSRule: { - link: '#extensions-to-cssrule-interface', - mdnGroup: 'DOM', - properties: ['COUNTER_STYLE_RULE'], - }, - CSSCounterStyleRule: { - link: '#the-csscounterstylerule-interface', - mdnGroup: 'DOM', - extends: 'CSSRule', - members: [ - 'name', - 'system', - 'symbols', - 'additiveSymbols', - 'negative', - 'prefix', - 'suffix', - 'range', - 'pad', - 'speakAs', - 'fallback', - ], - }, - }, -}; diff --git a/features/css-font-loading-3.js b/features/css-font-loading-3.js deleted file mode 100644 index d4cda1f6..00000000 --- a/features/css-font-loading-3.js +++ /dev/null @@ -1,67 +0,0 @@ -export default { - id: 'css-font-loading-3', - title: 'CSS Font Loading Module Level 3', - link: 'css-font-loading-3', - status: 'stable', - globals: { - FontFace: { - link: '#fontface-interface', - mdnGroup: 'DOM', - members: [ - 'family', - 'style', - 'weight', - 'stretch', - 'unicodeRange', - 'variant', - 'featureSettings', - 'variationSettings', - 'display', - 'ascentOverride', - 'descentOverride', - 'lineGapOverride', - 'status', - 'loaded', - 'features', - 'variations', - 'palettes', - ], - methods: ['load'], - }, - FontFaceFeatures: { - link: '#fontfacefeatures', - mdnGroup: 'DOM', - }, - FontFaceVariationAxis: { - link: '#fontfacevariationaxis', - mdnGroup: 'DOM', - members: ['name', 'axisTag', 'minimumValue', 'maximumValue', 'defaultValue'], - }, - FontFacePalettes: { - link: '#fontfacepalettes', - mdnGroup: 'DOM', - members: ['length'], - }, - FontFacePalette: { - link: '#fontfacepalette', - mdnGroup: 'DOM', - members: ['length', 'usableWithLightBackground', 'usableWithDarkBackground'], - }, - FontFaceSet: { - link: '#FontFaceSet-interface', - mdnGroup: 'DOM', - members: ['onloading', 'onloadingdone', 'onloadingerror', 'ready', 'status'], - methods: ['add', 'delete', 'clear', 'load', 'check'], - }, - FontFaceSetLoadEvent: { - link: '#fontfacesetloadevent', - mdnGroup: 'DOM', - members: ['fontfaces'], - }, - document: { - link: '#font-face-source', - mdnGroup: 'DOM', - properties: ['fonts'], - }, - }, -}; diff --git a/features/css-fonts-4.js b/features/css-fonts-4.js deleted file mode 100644 index 1f7a1aac..00000000 --- a/features/css-fonts-4.js +++ /dev/null @@ -1,273 +0,0 @@ -export default { - id: 'css-fonts-4', - title: 'CSS Fonts Module Level 4', - link: 'css-fonts-4', - status: 'stable', - properties: { - 'font-family': { - isGroup: true, - link: '#font-family-prop', - mdn: 'font-family', - tests: { - 'system-ui': {link: '#system-ui-def'}, - 'emoji': {link: '#emoji-def'}, - 'math': {link: '#math-def'}, - 'generic(fangsong)': {link: '#generic(fangsong)-def'}, - 'generic(kai)': {link: '#generic(kai)-def'}, - 'generic(khmer-mul)': {link: '#generic(khmer-mul)-def'}, - 'generic(nastaliq)': {link: '#generic(nastaliq)-def'}, - 'ui-serif': {link: '#ui-serif-def'}, - 'ui-sans-serif': {link: '#ui-sans-serif-def'}, - 'ui-monospace': {link: '#ui-monospace-def'}, - 'ui-rounded': {link: '#ui-rounded-def'}, - }, - }, - 'font-size': { - isGroup: true, - link: '#font-size-prop', - mdn: 'font-size', - tests: { - 'xxx-large': {link: '#xxx-large-def'}, - 'math': {link: '#math-in-font-size-def'}, - }, - }, - 'font-weight': { - title: 'Arbitrary font weights', - link: '#font-weight-prop', - mdn: 'font-weight', - tests: ['1', '90', '750', '1000'], - }, - 'font-style': { - titleMd: '`oblique `', - link: '#font-style-prop', - mdn: 'font-style', - tests: ['oblique 15deg', 'oblique -15deg', 'oblique 0deg'], - }, - 'font-variant-alternates': { - link: '#font-variant-alternates-prop', - tests: [ - 'normal', - 'stylistic(salt01)', - 'historical-forms', - 'styleset(ss01)', - 'styleset(stacked-g, geometric-m)', - 'character-variant(cv02)', - 'character-variant(beta-3, gamma)', - 'swash(flowing)', - 'ornaments(leaves)', - 'annotation(blocky)', - ], - }, - 'font-variant-emoji': { - link: '#font-variant-emoji-prop', - tests: [ - 'normal', - 'text', - 'emoji', - 'unicode', - ], - }, - 'font-variant': { - titleMd: '`font-variant` functions and keywords', - link: '#font-variant-prop', - mdn: 'font-variant', - tests: [ - // font-variant-alternates - 'stylistic(salt01)', - 'historical-forms', - 'styleset(ss01)', - 'styleset(stacked-g, geometric-m)', - 'character-variant(cv02)', - 'character-variant(beta-3, gamma)', - 'swash(flowing)', - 'ornaments(leaves)', - 'annotation(blocky)', - // font-variant-emoji - 'text', - 'emoji', - 'unicode', - 'discretionary-ligatures character-variant(leo-B, leo-M, leo-N, leo-T, leo-U)', - ] - }, - 'font-variation-settings': { - link: '#font-variation-settings-def', - tests: [ - 'normal', - '"wght" 2', - '"wght" 2, "ital" 1.2', - ], - }, - 'font-feature-settings': { - link: '#font-feature-settings-prop', - tests: ['normal', "'swsh' 2"], - }, - 'font-language-override': { - link: '#font-language-override', - tests: ['normal', "'SRB'"], - }, - 'font-synthesis-weight': { - link: '#font-synthesis-weight', - tests: ['auto', 'none'], - }, - 'font-synthesis-style': { - link: '#font-synthesis-style', - tests: ['auto', 'none', 'oblique-only'], - }, - 'font-synthesis-small-caps': { - link: '#font-synthesis-small-caps', - tests: ['auto', 'none'], - }, - 'font-synthesis': { - link: '#font-synthesis', - tests: [ - 'small-caps', - 'weight small-caps', - 'style small-caps', - 'style small-caps weight', - ], - }, - 'font-optical-sizing': { - link: '#font-optical-sizing-def', - tests: ['none', 'auto'], - }, - 'font-palette': { - link: '#font-palette-prop', - tests: ['normal', 'light', 'dark', '--custom-palette'], - }, - }, - atrules: { - '@font-face': { - isGroup: true, - descriptors: { - 'ascent-override': { - link: '#descdef-font-face-ascent-override', - tests: ['normal', '125%'], - }, - 'descent-override': { - link: '#descdef-font-face-descent-override', - tests: ['normal', '125%'], - }, - 'line-gap-override': { - link: '#descdef-font-face-line-gap-override', - tests: ['normal', '90%'], - }, - 'font-named-instance': { - link: '#font-named-instance', - tests: ['auto', "'Grotesque'"], - }, - 'font-display': { - link: '#descdef-font-face-font-display', - tests: ['auto', 'block', 'swap', 'fallback', 'optional'], - }, - 'font-stretch': { - link: '#descdef-font-face-font-stretch', - tests: [ - 'auto', - 'condensed normal', - ], - }, - 'font-style': { - link: '#descdef-font-face-font-style', - tests: [ - 'auto', - 'left', - 'right', - '10deg', - '10deg 5deg', - ], - }, - 'font-variation-settings': { - link: '#descdef-font-face-font-variation-settings', - tests: [ - 'normal', - '"wght" 2', - '"wght" 2, "ital" 1.2', - ], - }, - 'font-weight': { - link: '#descdef-font-face-font-weight', - tests: [ - 'auto', - '100 300', - ], - }, - 'src': { - code: 'tech()', - link: '#font-face-src-parsing', - tests: [ - 'url("foo") format("woff") tech(features-opentype)', - 'url("foo") format("woff") tech(features-graphite)', - 'url("foo") format("woff") tech(features-aat)', - 'url("foo") format("woff") tech(color-COLRv0)', - 'url("foo") format("woff") tech(color-COLRv1)', - 'url("foo") format("woff") tech(color-SVG)', - 'url("foo") format("woff") tech(color-sbix)', - 'url("foo") format("woff") tech(color-CBDT)', - 'url("foo") format("woff") tech(variations)', - 'url("foo") format("woff") tech(palettes)', - 'url("foo") format("woff") tech(incremental)', - 'url("foo") tech(color-COLRv1)', - 'url("foo") format("woff") tech(features-opentype, color-COLRv1)', - ], - }, - }, - }, - '@font-feature-values': { - link: '#font-feature-values', - preludeRequired: true, - prelude: 'Foo', - atrules: { - '@stylistic': { contents: 'a: 1' }, - '@historical-forms': { contents: 'a: 1' }, - '@styleset': { contents: 'a: 1' }, - '@character-variant': { contents: 'a: 1' }, - '@swash': { contents: 'a: 1' }, - '@ornaments': { contents: 'a: 1' }, - '@annotation': { contents: 'a: 1' }, - '@styleset': { contents: 'a: 1' }, - }, - }, - '@font-palette-values': { - link: '#font-palette-values', - prelude: '--custom-palette', - descriptors: ['font-family', 'base-palette', 'override-colors', 'font-display'], - }, - }, - globals: { - CSSRule: { - link: '#om-fontfeaturevalues', - mdnGroup: 'DOM', - properties: ['FONT_FEATURE_VALUES_RULE'], - }, - CSSFontFeatureValuesRule: { - link: '#om-fontfeaturevalues', - mdnGroup: 'DOM', - extends: 'CSSRule', - members: [ - 'fontFamily', - 'annotation', - 'ornaments', - 'stylistic', - 'swash', - 'characterVariant', - 'styleset', - ], - }, - CSSFontFeatureValuesMap: { - link: '#cssfontfeaturevaluesmap', - mdnGroup: 'DOM', - methods: ['set'], - }, - CSSFontPaletteValuesRule: { - link: '#om-fontpalettevalues', - mdnGroup: 'DOM', - extends: 'CSSRule', - members: [ - 'name', - 'fontFamily', - 'basePalette', - 'overrideColors', - ], - }, - }, -}; diff --git a/features/css-fonts-5.js b/features/css-fonts-5.js deleted file mode 100644 index d09c5101..00000000 --- a/features/css-fonts-5.js +++ /dev/null @@ -1,106 +0,0 @@ -export default { - id: 'css-fonts-5', - title: 'CSS Fonts Module Level 5', - link: 'css-fonts-5', - status: 'experimental', - properties: { - 'font-size-adjust': { - link: '#font-size-adjust-prop', - tests: [ - 'ex-height 0.545', - 'ex-height from-font', - 'cap-height 0.545', - 'cap-height from-font', - 'ch-width 0.545', - 'ch-width from-font', - 'ic-width 0.545', - 'ic-width from-font', - 'ic-height 0.545', - 'ic-height from-font', - 'from-font', - ], - }, - }, - atrules: { - '@font-face': { - titleMd: '`@font-face` descriptors', - link: '#at-font-face-rule', - isGroup: true, - descriptors: { - 'ascent-override': { - link: '#descdef-font-face-ascent-override', - values: ['normal 125%', '125% normal'], - }, - 'descent-override': { - link: '#descdef-font-face-descent-override', - values: ['normal 125%', '125% normal'], - }, - 'font-size': { - link: '#font-size-desc', - values: ['auto', '0.7', '0.7 0.9'], - }, - 'line-gap-override': { - link: '#descdef-font-face-line-gap-override', - values: ['normal 125%', '125% normal'], - }, - 'size-adjust': { - link: '#size-adjust-desc', - values: ['125%'], - }, - 'subscript-position-override': { - link: '#descdef-font-face-subscript-position-override', - values: [ - 'normal', - 'from-font', - '125%', - 'normal normal', - 'normal 125%', - 'normal from-font', - '125% normal', - 'from-font normal', - ], - }, - 'subscript-size-override': { - link: '#descdef-font-face-subscript-size-override', - values: [ - 'normal', - 'from-font', - '125%', - 'normal normal', - 'normal 125%', - 'normal from-font', - '125% normal', - 'from-font normal', - ], - }, - 'superscript-size-override': { - link: '#descdef-font-face-superscript-size-override', - values: [ - 'normal', - 'from-font', - '125%', - 'normal normal', - 'normal 125%', - 'normal from-font', - '125% normal', - 'from-font normal', - ], - }, - 'superscript-position-override': { - link: '#descdef-font-face-superscript-position-override', - values: [ - 'normal', - 'from-font', - '125%', - 'normal normal', - 'normal 125%', - 'normal from-font', - '125% normal', - 'from-font normal', - ], - }, - - } - }, - }, -}; diff --git a/features/css-images-3.js b/features/css-images-3.js deleted file mode 100644 index 0417bc0b..00000000 --- a/features/css-images-3.js +++ /dev/null @@ -1,81 +0,0 @@ -export default { - id: 'css-images-3', - title: 'CSS Images Module Level 3', - link: 'css-images-3', - specLink: 'css3-images', - status: 'stable', - firstSnapshot: 2015, - values: { - 'linear-gradient()': { - link: '#linear-gradients', - mdn : 'linear-gradient', - dataType: 'image', - args: [ - 'white, black', - 'to right, white, black', - '45deg, white, black', - 'white 50%, black 5px', - - // allow a single color stop with 0-1 positions - // https://github.com/w3c/csswg-drafts/issues/10092#issuecomment-2145860054 - 'red', - 'red 0', - 'red 50px', - '0, red', - ], - }, - 'radial-gradient()': { - link: '#radial-gradients', - mdn: 'radial-gradient', - dataType: 'image', - args: [ - 'white, black', - 'circle, white, black', - 'ellipse, white, black', - 'closest-corner, white, black', - 'circle farthest-side, white, black', - '60% 60%, white, black', - - // allow a single color stop with 0-1 positions - // https://github.com/w3c/csswg-drafts/issues/10092#issuecomment-2145860054 - 'red', - 'red 0%', - 'red 0% 100%', - ], - }, - 'repeating-linear-gradient()': { - link: '#repeating-gradients', - dataType: 'image', - args: [ - 'white, black', - 'red', - ], - }, - 'repeating-radial-gradient()': { - link: '#repeating-gradients', - dataType: 'image', - args: [ - 'white, black', - 'red', - ] - }, - }, - properties: { - 'object-fit': { - link: '#the-object-fit', - tests: ['fill', 'contain', 'cover', 'none', 'scale-down'], - }, - 'object-position': { - link: '#the-object-position', - tests: ['50% 50%', 'center', 'top right', 'bottom 10px right 20px'], - }, - 'image-orientation': { - link: '#the-image-orientation', - tests: ['from-image', '0deg', '90deg', '45deg', '45deg flip', '1turn', '100grad', '2rad'], - }, - 'image-rendering': { - link: '#the-image-rendering', - tests: ['auto', 'smooth', 'high-quality', 'crisp-edges', 'pixelated'], - }, - }, -}; diff --git a/features/css-mixins-1.js b/features/css-mixins-1.js deleted file mode 100644 index 96ddbeee..00000000 --- a/features/css-mixins-1.js +++ /dev/null @@ -1,37 +0,0 @@ -export default { - id: 'css-mixins-1', - title: 'CSS Functions and Mixins Module', - link: 'css-mixins-1', - status: 'experimental', - atrules: { - '@function': { - link: '#defining-custom-functions', - preludeRequired: true, - contents: 'result: var(--a, 1);', - preludes: [ - '--foo()', - '--foo(--a)', - '--foo(--a) returns ', - '--foo(--a )', - '--foo(--a type())', - '--foo(--a type( | ))', - ], - // TODO declarations and rules inside @function - }, - }, - globals: { - CSSFunctionRule: { - link: '#the-function-interface', - mdnGroup: 'DOM', - extends: 'CSSGroupingRule', - members: ['name', 'returnType'], - methods: ['getParameters'], - }, - CSSFunctionDeclarations: { - link: '#the-function-declarations-interface', - mdnGroup: 'DOM', - extends: 'CSSRule', - members: ['style'], - }, - }, -}; diff --git a/features/css-nesting-1.js b/features/css-nesting-1.js deleted file mode 100644 index e450c6c1..00000000 --- a/features/css-nesting-1.js +++ /dev/null @@ -1,24 +0,0 @@ -export default { - id: 'css-nesting-1', - title: 'CSS Nesting Module', - link: 'css-nesting-1', - status: 'experimental', - selectors: { - '&': { - link: '#nest-selector', - } - }, - globals: { - CSSStyleRule: { - link: '#cssom-style', - mdnGroup: 'DOM', - members: ['cssRules'], - methods: ['insertRule', 'deleteRule'], - }, - CSSNestedDeclarations: { - link: '#cssnesteddeclarations', - extends: 'CSSRule', - members: ['style'], - }, - }, -}; diff --git a/features/css-page-3.js b/features/css-page-3.js deleted file mode 100644 index a6488de7..00000000 --- a/features/css-page-3.js +++ /dev/null @@ -1,68 +0,0 @@ -export default { - id: 'css-page-3', - title: 'Paged Media Module Level 3', - link: 'css-page-3', - status: 'experimental', - properties: { - page: { - link: '#using-named-pages', - tests: ['auto', 'customName'], - }, - }, - atrules: { - '@page': { - link: '#at-page-rule', - isGroup: true, - preludes: { - ':blank': {}, - 'custom': { - isGroup: false, - children: [ - 'custom, custom2', - 'custom:left', - 'custom:right', - 'custom:first', - ] - } - }, - descriptors: { - size: { - link: '#page-size-prop', - values: [ - '4in 6in', - '4em 6em', - 'auto', - 'landscape', - 'portrait', - 'a3', - 'a4', - 'a5', - 'b4', - 'b5', - 'jis-b4', - 'jis-b5', - 'ledger', - 'legal', - 'letter', - 'a3 landscape', - 'a3 portrait', - 'landscape a3', - 'portrait a3', - ], - }, - 'page-orientation': { - link: '#page-orientation-prop', - values: ['upright', 'rotate-left', 'rotate-right'], - }, - marks: { - link: '#marks', - values: ['none', 'crop', 'cross', 'crop cross'], - }, - bleed: { - link: '#bleed', - values: ['auto', '6pt'], - }, - }, - }, - }, -}; diff --git a/features/css-properties-values-api-1.js b/features/css-properties-values-api-1.js deleted file mode 100644 index f72e4cad..00000000 --- a/features/css-properties-values-api-1.js +++ /dev/null @@ -1,28 +0,0 @@ -export default { - id: 'css-properties-values-api-1', - title: 'CSS Properties and Values API Level 1', - link: 'css-properties-values-api-1', - group: 'houdini', - status: 'experimental', - atrules: { - '@property': { - link: '#at-property-rule', - prelude: '--foo', - preludeRequired: true, - contents: 'syntax: "*"; inherits: true;' - }, - }, - globals: { - CSS: { - link: '#registering-custom-properties', - mdnGroup: 'DOM', - functions: ['registerProperty'], - }, - CSSPropertyRule: { - link: '#the-css-property-rule-interface', - mdnGroup: 'DOM', - extends: 'CSSRule', - members: ['name', 'syntax', 'inherits', 'initialValue'], - }, - }, -}; diff --git a/features/css-regions-1.js b/features/css-regions-1.js deleted file mode 100644 index 9d27ce8e..00000000 --- a/features/css-regions-1.js +++ /dev/null @@ -1,48 +0,0 @@ -export default { - id: 'css-regions-1', - title: 'CSS Regions Module Level 1', - link: 'css-regions-1', - status: 'experimental', - properties: { - 'flow-from': { - link: '#flow-from', - tests: ['none', 'named-flow'], - }, - 'flow-into': { - link: '#the-flow-into-property', - tests: ['none', 'named-flow', 'named-flow element', 'named-flow content'], - }, - 'region-fragment': { - link: '#the-region-fragment-property', - tests: ['auto', 'break'], - }, - }, - globals: { - document: { - link: '#the-namedflow-interface', - mdnGroup: 'DOM', - properties: ['namedFlows'], - }, - Element: { - link: '#the-region-interface', - mdnGroup: 'DOM', - members: ['regionOverset'], - methods: ['getRegionFlowRanges'], - }, - NamedFlowMap: { - link: '#namedflowmap', - mdnGroup: 'DOM', - }, - NamedFlow: { - link: '#namedflow', - mdnGroup: 'DOM', - extends: 'EventTarget', - members: [ - 'name', - 'overset', - 'firstEmptyRegionIndex', - ], - methods: ['getRegions', 'getContent', 'getRegionsByContent'], - }, - }, -}; diff --git a/features/css-scroll-snap-2.js b/features/css-scroll-snap-2.js deleted file mode 100644 index 0f86b23d..00000000 --- a/features/css-scroll-snap-2.js +++ /dev/null @@ -1,50 +0,0 @@ -export default { - id: 'css-scroll-snap-2', - title: 'CSS Scroll Snap Module Level 2', - link: 'css-scroll-snap-2', - status: 'experimental', - properties: { - 'scroll-start-target': { - link: '#scroll-start-target', - tests: [ - 'none', - 'auto', - ], - }, - }, - selectors: { - ':snapped': { - link: '#snapped', - tests: ':snapped', - }, - ':snapped-x': { - link: '#selectordef-snapped-x', - tests: ':snapped-x', - }, - ':snapped-y': { - link: '#selectordef-snapped-y', - tests: ':snapped-y', - }, - ':snapped-inline': { - link: '#selectordef-snapped-inline', - tests: ':snapped-inline', - }, - ':snapped-block': { - link: '#selectordef-snapped-block', - tests: ':snapped-block', - }, - }, - globals: { - SnapEvent: { - link: '#snap-events', - mdnGroup: 'DOM', - extends: 'Event', - members: ['snapTargetBlock', 'snapTargetInline'], - }, - Element: { - link: '#interface-globaleventhandlers', - mdnGroup: 'DOM', - members: ['onsnapchanged', 'onsnapchanging'], - } - }, -}; diff --git a/features/css-transitions-2.js b/features/css-transitions-2.js deleted file mode 100644 index cfae58c6..00000000 --- a/features/css-transitions-2.js +++ /dev/null @@ -1,30 +0,0 @@ -export default { - id: 'css-transitions-2', - title: 'CSS Transitions 2', - link: 'css-transitions-2', - status: 'experimental', - atrules: { - '@starting-style': { - link: '#defining-before-change-style-the-starting-style-rule', - }, - }, - properties: { - 'transition-behavior': { - link: '#transition-behavior-property', - tests: ['normal', 'allow-discrete'], - }, - }, - globals: { - CSSStartingStyleRule: { - link: '#the-cssstartingstylerule-interface', - mdnGroup: 'DOM', - extends: 'CSSGroupingRule', - }, - CSSTransition: { - link: '#the-CSSTransition-interface', - mdnGroup: 'DOM', - extends: 'Animation', - members: ['transitionProperty'], - }, - }, -}; diff --git a/features/css-typed-om-1.js b/features/css-typed-om-1.js deleted file mode 100644 index 88acfb60..00000000 --- a/features/css-typed-om-1.js +++ /dev/null @@ -1,339 +0,0 @@ -export default { - id: 'css-typed-om-1', - title: 'CSS Typed OM Level 1', - link: 'css-typed-om-1', - group: 'houdini', - status: 'experimental', - globals: { - Element: { - link: '#computed-stylepropertymapreadonly-objects', - mdnGroup: 'DOM', - members: [ - 'computedStyleMap', - ], - }, - StylePropertyMapReadOnly: { - link: '#the-stylepropertymap', - mdnGroup: 'DOM', - members: [ - 'get', - 'getAll', - 'has', - 'size', - ], - }, - StylePropertyMap: { - link: '#the-stylepropertymap', - mdnGroup: 'DOM', - members: [ - 'get', - 'getAll', - 'has', - 'size', - 'set', - 'append', - 'delete', - 'clear', - ], - }, - CSSStyleValue: { - link: '#stylevalue-objects', - mdnGroup: 'DOM', - functions: [ - 'parse', - 'parseAll', - ], - }, - CSSUnparsedValue: { - link: '#unparsedvalue-objects', - mdnGroup: 'DOM', - members: [ - 'length', - 'entries', - 'keys', - 'values', - 'forEach', - ], - }, - CSSVariableReferenceValue: { - link: '#unparsedvalue-objects', - mdnGroup: 'DOM', - members: [ - 'variable', - 'fallback', - ], - }, - CSSKeywordValue: { - link: '#keywordvalue-objects', - mdnGroup: 'DOM', - members: ['value'], - }, - CSSNumericValue: { - link: '#numeric-value', - mdnGroup: 'DOM', - methods: [ - 'add', - 'sub', - 'mul', - 'div', - 'min', - 'max', - 'equals', - 'to', - 'toSum', - 'type', - ], - }, - CSSUnitValue: { - link: '#simple-numeric', - mdnGroup: 'DOM', - members: [ - 'value', - 'unit', - ], - extends: 'CSSNumericValue', - }, - CSSMathValue: { - link: '#complex-numeric', - mdnGroup: 'DOM', - members: [ - 'operator', - ], - extends: 'CSSNumericValue', - }, - CSSMathSum: { - link: '#cssmathsum', - mdnGroup: 'DOM', - members: [ - 'values', - ], - extends: 'CSSMathValue', - }, - CSSMathProduct: { - link: '#cssmathproduct', - mdnGroup: 'DOM', - members: [ - 'values', - ], - extends: 'CSSMathValue', - }, - CSSMathNegate: { - link: '#cssmathnegate', - mdnGroup: 'DOM', - members: [ - 'value', - ], - extends: 'CSSMathValue', - }, - CSSMathInvert: { - link: '#cssmathinvert', - mdnGroup: 'DOM', - members: [ - 'value', - ], - extends: 'CSSMathValue', - }, - CSSMathMin: { - link: '#cssmathmin', - mdnGroup: 'DOM', - members: [ - 'values', - ], - extends: 'CSSMathValue', - }, - CSSMathMax: { - link: '#cssmathmax', - mdnGroup: 'DOM', - members: [ - 'values', - ], - extends: 'CSSMathValue', - }, - CSSMathClamp: { - link: '#cssmathclamp', - mdnGroup: 'DOM', - members: [ - 'lower', - 'value', - 'upper', - ], - extends: 'CSSMathValue', - }, - CSSNumericArray: { - link: '#cssnumericarray', - mdnGroup: 'DOM', - iterable: true, - members: ['length'], - }, - CSS: { - link: '#numeric-factory', - mdnGroup: 'DOM', - functions: [ - 'number', - 'percent', - 'em', - 'ex', - 'ch', - 'ic', - 'rem', - 'lh', - 'rlh', - 'vw', - 'vh', - 'vi', - 'vb', - 'vmin', - 'vmax', - 'svw', - 'svh', - 'svi', - 'svb', - 'svmin', - 'svmax', - 'lvw', - 'lvh', - 'lvi', - 'lvb', - 'lvmin', - 'lvmax', - 'dvw', - 'dvh', - 'dvi', - 'dvb', - 'dvmin', - 'dvmax', - 'cqw', - 'cqh', - 'cqi', - 'cqb', - 'cqmin', - 'cqmax', - 'cm', - 'mm', - 'Q', - 'in', - 'pt', - 'pc', - 'px', - 'deg', - 'grad', - 'rad', - 'turn', - 's', - 'ms', - 'Hz', - 'kHz', - 'dpi', - 'dpcm', - 'dppx', - 'fr', - ], - }, - CSSTransformValue: { - link: '#transformvalue-objects', - mdnGroup: 'DOM', - members: [ - 'length', - 'is2D', - 'toMatrix', - 'entries', - 'forEach', - 'keys', - 'values', - ], - }, - CSSTransformComponent: { - link: '#csstransformcomponent', - mdnGroup: 'DOM', - members: ['is2D', 'toMatrix'], - }, - CSSTranslate: { - link: '#csstranslate', - mdnGroup: 'DOM', - members: ['x', 'y', 'z'], - }, - CSSRotate: { - link: '#cssrotate', - mdnGroup: 'DOM', - members: ['x', 'y', 'z', 'angle'], - }, - CSSScale: { - link: '#cssscale', - mdnGroup: 'DOM', - members: ['x', 'y', 'z'], - }, - CSSSkew: { - link: '#cssskew', - mdnGroup: 'DOM', - members: ['ax', 'ay'], - }, - CSSSkewX: { - link: '#cssskewx', - mdnGroup: 'DOM', - members: ['ax'], - }, - CSSSkewY: { - link: '#cssskewy', - mdnGroup: 'DOM', - members: ['ay'], - }, - CSSPerspective: { - link: '#cssperspective', - mdnGroup: 'DOM', - members: ['length'], - }, - CSSMatrixComponent: { - link: '#cssmatrixcomponent', - mdnGroup: 'DOM', - members: ['matrix'], - }, - CSSImageValue: { - link: '#imagevalue-objects', - mdnGroup: 'DOM', - members: ['CSSImageValue'], - }, - CSSColorValue: { - link: '#colorvalue-objects', - mdnGroup: 'DOM', - members: ['CSSColorValue'], - }, - CSSRGB: { - link: '#cssrgb', - mdnGroup: 'DOM', - members: ['r', 'g', 'b', 'alpha'], - }, - CSSHSL: { - link: '#csshsl', - mdnGroup: 'DOM', - members: ['h', 's', 'l', 'alpha'], - }, - CSSHWB: { - link: '#csshwb', - mdnGroup: 'DOM', - members: ['h', 'w', 'b', 'alpha'], - }, - CSSLab: { - link: '#csslab', - members: ['l', 'a', 'b', 'alpha'], - }, - CSSLCH: { - link: '#csslch', - mdnGroup: 'DOM', - }, - CSSOKLab: { - link: '#cssoklab', - mdnGroup: 'DOM', - members: ['l', 'a', 'b', 'alpha'], - }, - CSSOKLCH: { - link: '#cssoklch', - mdnGroup: 'DOM', - members: ['l', 'c', 'h', 'alpha'], - }, - CSSColor: { - link: '#csscolor', - mdnGroup: 'DOM', - members: ['colorSpace', 'channels', 'alpha'], - }, - }, -}; diff --git a/features/css-values-3.js b/features/css-values-3.js deleted file mode 100644 index 96dfa18d..00000000 --- a/features/css-values-3.js +++ /dev/null @@ -1,79 +0,0 @@ -export default { - id: 'css-values-3', - title: 'CSS Values and Units Module Level 3', - link: 'css-values-3', - status: 'stable', - firstSnapshot: 2015, - units: { - rem: { - link: '#rem', - mdn: 'length', - dataType: 'length', - }, - ch: { - link: '#ch', - mdn: 'length', - dataType: 'length', - }, - vw: { - link: '#vw', - mdn: 'length', - dataType: 'length', - }, - vh: { - link: '#vh', - mdn: 'length', - dataType: 'length', - }, - vmin: { - link: '#vmin', - mdn: 'length', - dataType: 'length', - }, - vmax: { - link: '#vmax', - mdn: 'length', - dataType: 'length', - }, - Q: { - link: '#Q', - mdn: 'length', - dataType: 'length', - }, - }, - values: { - 'calc()': { - link: '#calc-notation', - dataTypes: { - length: { - args: ['1em - 1px', '2 * 1px', '1px + calc(1px)'], - }, - 'length-percentage': { - args: ['1em - 1%', '100%/3'], - }, - percentage: { - args: ['2% - 1%', '2 * 1%'], - }, - number: { - args: ['1.5 - 1', '3 / 2'], - }, - time: { - args: ['1s - 1ms'], - }, - angle: { - args: ['1deg + 2deg'], - }, - }, - values: { - 'translateX()': { - property: 'transform', - arg: 'calc(1px + 2px)', - }, - 'hsl()': { - property: 'color', - arg: 'calc(1 + 2), calc(1% + 2%), calc(1% + 2%)', - }, - } - }, - }, -}; diff --git a/features/css-variables-2.js b/features/css-variables-2.js deleted file mode 100644 index 9606b823..00000000 --- a/features/css-variables-2.js +++ /dev/null @@ -1,16 +0,0 @@ -export default { - id: 'css-variables-2', - title: 'CSS Custom Properties for Cascading Variables Module Level 2', - link: 'css-variables-2', - status: 'experimental', - values: { - '--*': { - title: 'Variable units', - link: '#variable-units', - properties: ['margin-block'], - tests: [ - '1.5--bs', - ], - }, - }, -}; diff --git a/features/css-viewport-1.js b/features/css-viewport-1.js deleted file mode 100644 index f02ddc44..00000000 --- a/features/css-viewport-1.js +++ /dev/null @@ -1,19 +0,0 @@ -export default { - id: 'css-viewport-1', - title: 'CSS Viewport Module Level 1', - link: 'css-viewport-1', - status: 'experimental', - properties: { - 'zoom': { - link: '#zoom-property', - tests: ['0', '1', '1.5', '110%'], - }, - }, - globals: { - Viewport: { - link: '#the-viewport-interface', - mdnGroup: 'DOM', - members: ['segments'], - }, - }, -}; diff --git a/features/css2-box.js b/features/css2-box.js deleted file mode 100644 index 9ceca6b8..00000000 --- a/features/css2-box.js +++ /dev/null @@ -1,94 +0,0 @@ -import { repeat, combine } from '../util.js'; - -const length = '1px'; -const percentage = '1%'; -const margin = [length, percentage, 'auto']; -const margin_1_4 = repeat(margin, {min: 1, max: 4}); - -const padding = [length, percentage]; -const padding_1_4 = repeat(padding, {min: 1, max: 4}); - -const borderColors = ['red', 'transparent']; -const borderStyles = ['solid', 'none', 'hidden', 'dotted', 'dashed', 'double', 'groove', 'ridge', 'inset', 'outset']; -const borderWidths = [length, 'thin', 'medium', 'thick']; - -const borderColors_1_4 = repeat(borderColors, {min: 1, max: 4}); -const borderStyles_1_4 = repeat(borderStyles.slice(0, 2), {min: 1, max: 4}); -const borderWidths_1_4 = repeat(borderWidths.slice(0, 2), {min: 1, max: 4}); - -const borderShorthands = combine([borderWidths[0], borderStyles[0], borderColors[0]], { combinator: '||' }); - -export default { - id: 'css2-box', - title: 'CSS 2 Box Model', - link: 'css2/', - specLink: 'CSS22/box.html', - status: 'stable', - version: 2.2, - properties: { - border: { - titleMd: '`border` properties', - isGroup: true, - children: { - border: { - link: '#border-shorthand-properties', - titleMd: '`border` and `border-` shorthands', - values: borderShorthands, - children: ['border', 'border-top', 'border-right', 'border-bottom', 'border-left'], - }, - 'border-color': { - link: '#border-color-properties', - titleMd: '`border-color` and `border--color`', - isGroup: true, - dataType: 'color', - values: borderColors, - children: [{id: 'border-color', values: borderColors_1_4}, 'border-top-color', 'border-right-color', 'border-bottom-color', 'border-left-color'], - }, - 'border-style': { - link: '#border-style-properties', - titleMd: '`border-style` and `border--style`', - values: borderStyles, - children: [{id: 'border-style', values: borderStyles_1_4}, 'border-top-style', 'border-right-style', 'border-bottom-style', 'border-left-style'], - }, - 'border-width': { - link: '#border-width-properties', - titleMd: '`border-width` and `border--width`', - values: borderWidths, - children: [{id: 'border-width', values: borderWidths_1_4}, 'border-top-width', 'border-right-width', 'border-bottom-width', 'border-left-width'], - }, - }, - }, - - margin: { - titleMd: '`margin` and `margin-`', - isGroup: true, - values: margin, - children: { - margin: { - link: '#propdef-margin', - values: margin_1_4, - }, - 'margin-right': { - link: '#propdef-margin-right', - }, - 'margin-left': { - link: '#propdef-margin-left', - }, - 'margin-top': { - link: '#propdef-margin-top', - }, - 'margin-bottom': { - link: '#propdef-margin-bottom', - }, - }, - }, - - padding: { - link: '#padding-properties', - titleMd: '`padding` and `padding-`', - isGroup: true, - values: padding, - children: [{id: 'padding', values: padding_1_4}, 'padding-top', 'padding-right', 'padding-bottom', 'padding-left'], - } - }, -}; diff --git a/features/css2-page.js b/features/css2-page.js deleted file mode 100644 index 95810073..00000000 --- a/features/css2-page.js +++ /dev/null @@ -1,39 +0,0 @@ -export default { - id: 'css2-page', - title: 'CSS 2 Paged Media', - link: 'css2/', - specLink: 'CSS22/page.html', - status: 'stable', - version: 2.2, - atrules: { - '@page': { - link: '#page-box', - preludes: [ - ':left', - ':right', - ':first', - ], - descriptors: ['margin', 'margin-top', 'margin-right', 'margin-bottom', 'margin-left'], - }, - }, - properties: { - orphans: { - link: '#break-inside', - }, - 'page-break-after': { - link: '#page-break-props', - tests: ['auto', 'always', 'avoid', 'left', 'right'], - }, - 'page-break-before': { - link: '#page-break-props', - tests: ['auto', 'always', 'avoid', 'left', 'right'], - }, - 'page-break-inside': { - link: '#page-break-props', - tests: ['auto', 'avoid'], - }, - widows: { - link: '#break-inside', - }, - }, -}; diff --git a/features/cssom-1.js b/features/cssom-1.js deleted file mode 100644 index 96d41848..00000000 --- a/features/cssom-1.js +++ /dev/null @@ -1,191 +0,0 @@ -export default { - id: 'cssom-1', - title: 'CSS Object Model (CSSOM)', - link: 'cssom-1', - status: 'experimental', - globals: { - CSS: { - link: '#namespacedef-css', - mdnGroup: 'DOM', - functions: ['escape'], - }, - StyleSheet: { - link: '#the-stylesheet-interface', - mdnGroup: 'DOM', - members: [ - 'type', - 'href', - 'ownerNode', - 'parentStyleSheet', - 'title', - 'media', - 'disabled', - ], - }, - CSSStyleSheet: { - link: '#the-cssstylesheet-interface', - mdnGroup: 'DOM', - members: [ - 'ownerRule', - 'cssRules', - ], - methods: [ - 'insertRule', - 'deleteRule', - 'replace', - 'replaceSync', - ], - extends: 'StyleSheet', - children: { - CSSStyleSheet: { - titleMd: 'Deprecated `CSSStyleSheet` members', - // FIXME this inherits the extends from above, but it shouldn't - members: ['rules'], - methods: ['addRule', 'removeRule'], - } - } - }, - StyleSheetList: { - link: '#the-stylesheetlist-interface', - mdnGroup: 'DOM', - members: ['length'], - methods: ['item'], - }, - document: { - link: '#extensions-to-the-document-or-shadow-root-interface', - mdnGroup: 'DOM', - properties: ['styleSheets', 'adoptedStyleSheets'], - }, - HTMLLinkElement: { - link: '#the-linkstyle-interface', - titleMd: 'The `LinkStyle` interface', - mdnGroup: 'DOM', - members: ['sheet', 'style'], - }, - window: { - link: '#extensions-to-the-window-interface', - mdnGroup: 'DOM', - functions: ['getComputedStyle'], - }, - MediaList: { - link: '#the-medialist-interface', - mdnGroup: 'DOM', - tests: ['mediaText', 'length'], - methods: ['item', 'appendMedium', 'deleteMedium'], - }, - CSSRuleList: { - link: '#the-cssrulelist-interface', - mdnGroup: 'DOM', - members: ['length'], - methods: ['item'], - }, - CSSRule: { - link: '#the-cssrule-interface', - mdnGroup: 'DOM', - members: [ - 'cssText', - 'parentRule', - 'parentStyleSheet', - {id: 'type', titleMd: 'Deprecated `type` attribute'}, - ], - properties: [ - 'STYLE_RULE', - 'CHARSET_RULE', - 'IMPORT_RULE', - 'MEDIA_RULE', - 'FONT_FACE_RULE', - 'PAGE_RULE', - 'MARGIN_RULE', - 'NAMESPACE_RULE', - ] - }, - CSSStyleRule: { - link: '#the-cssstylerule-interface', - mdnGroup: 'DOM', - members: [ - 'selectorText', - 'style', - ], - extends: 'CSSGroupingRule', - }, - CSSImportRule: { - link: '#the-cssimportrule-interface', - mdnGroup: 'DOM', - members: [ - 'href', - 'media', - 'styleSheet', - 'layerName', - 'supportsText', - ], - }, - CSSGroupingRule: { - link: '#the-cssgroupingrule-interface', - mdnGroup: 'DOM', - members: [ - 'cssRules', - ], - methods: ['insertRule', 'deleteRule'], - extends: 'CSSRule', - }, - CSSPageDescriptors: { - link: '#the-csspagerule-interface', - extends: 'CSSStyleDeclaration', - members: [ - 'margin', - 'marginTop', - 'marginRight', - 'marginBottom', - 'marginLeft', - 'margin-top', - 'margin-right', - 'margin-bottom', - 'margin-left', - 'size', - 'pageOrientation', - 'page-orientation', - 'marks', - 'bleed', - ], - }, - CSSPageRule: { - link: '#the-csspagerule-interface', - mdnGroup: 'DOM', - extends: 'CSSGroupingRule', - members: ['selectorText', 'style'], - }, - CSSMarginRule: { - link: '#the-cssmarginrule-interface', - mdnGroup: 'DOM', - extends: 'CSSRule', - members: ['name', 'style'], - }, - CSSNamespaceRule: { - link: '#the-cssnamespacerule-interface', - mdnGroup: 'DOM', - extends: 'CSSRule', - members: ['namespaceURI', 'prefix'], - }, - CSSStyleDeclaration: { - link: '#the-cssstyledeclaration-interface', - mdnGroup: 'DOM', - tests: [ - 'cssText', - 'length', - 'parentRule', - ], - methods: [ - 'item', - 'getPropertyValue', - 'getPropertyPriority', - 'setProperty', - 'removeProperty', - ] - }, - CSSStyleProperties: { - link: '#the-cssstyledeclaration-interface', - extends: 'CSSStyleDeclaration', - members: ['cssFloat'], - } - }, -}; diff --git a/features/html.js b/features/html.js deleted file mode 100644 index 69c6771a..00000000 --- a/features/html.js +++ /dev/null @@ -1,33 +0,0 @@ -export default { - id: 'html', - title: 'HTML Living Standard', - link: 'html', - group: 'whatwg', - status: 'experimental', - selectors: { - ':autofill': { - link: '#selector-autofill', - tests: ':autofill', - }, - ':popover-open': { - link: '#selector-popover-open', - tests: ':popover-open', - }, - ':state()': { - link: '#selector-custom', - tests: ':state(checked)', - }, - }, - globals: { - PageRevealEvent: { - link: '#the-pagerevealevent-interface', - mdnGroup: 'DOM', - extends: 'Event', - members: ['viewTransition'], - }, - Worklet: { - link: 'https://html.spec.whatwg.org/multipage/worklets.html#worklets-worklet', - methods: ['addModule'], - } - } -}; diff --git a/features/resize-observer-1.js b/features/resize-observer-1.js deleted file mode 100644 index 20a05e01..00000000 --- a/features/resize-observer-1.js +++ /dev/null @@ -1,23 +0,0 @@ -export default { - id: 'resize-observer-1', - title: 'Resize Observer', - link: 'resize-observer-1', - status: 'experimental', - globals: { - ResizeObserver: { - link: '#api', - mdnGroup: 'DOM', - methods: ['observe', 'unobserve', 'disconnect'], - }, - ResizeObserverEntry: { - link: '#resize-observer-entry-interface', - mdnGroup: 'DOM', - members: ['target', 'contentRect', 'borderBoxSize', 'contentBoxSize', 'devicePixelContentBoxSize'], - }, - ResizeObserverSize: { - link: '#resizeobserversize', - mdnGroup: 'DOM', - members: ['inlineSize', 'blockSize'], - }, - }, -}; diff --git a/features/web-animations-1.js b/features/web-animations-1.js deleted file mode 100644 index 676e7d6c..00000000 --- a/features/web-animations-1.js +++ /dev/null @@ -1,73 +0,0 @@ -export default { - id: 'web-animations-1', - title: 'Web Animations', - link: 'web-animations-1', - status: 'experimental', - globals: { - Animation: { - link: '#the-animation-interface', - mdnGroup: 'DOM', - extends: 'EventTarget', - members: [ - 'id', - 'effect', - 'timeline', - 'startTime', - 'currentTime', - 'playbackRate', - 'playState', - 'replaceState', - 'pending', - 'ready', - 'finished', - 'onfinish', - 'oncancel', - 'onremove', - ], - methods: ['cancel', 'finish', 'play', 'pause', 'updatePlaybackRate', 'reverse', 'persist', 'commitStyles'], - }, - AnimationTimeline: { - link: '#the-animationtimeline-interface', - mdnGroup: 'DOM', - members: ['currentTime'], - }, - AnimationEffect: { - link: '#animationeffect', - mdnGroup: 'DOM', - methods: ['getTiming', 'getComputedTiming', 'updateTiming'], - }, - KeyframeEffect: { - link: '#the-keyframeeffect-interface', - mdnGroup: 'DOM', - extends: 'AnimationEffect', - members: [ - 'target', - 'pseudoElement', - 'composite', - ], - methods: ['getKeyframes', 'setKeyframes'], - }, - Element: { - link: '#the-animatable-interface-mixin', - mdnGroup: 'DOM', - methods: ['animate', 'getAnimations'], - }, - document: { - link: '#extensions-to-the-documentorshadowroot-interface-mixin', - mdnGroup: 'DOM', - properties: ['timeline'], - functions: ['getAnimations'], - }, - DocumentTimeline: { - link: '#the-documenttimeline-interface', - mdnGroup: 'DOM', - extends: 'AnimationTimeline', - }, - AnimationPlaybackEvent: { - link: '#the-animationplaybackevent-interface', - mdnGroup: 'DOM', - extends: 'Event', - members: ['currentTime', 'timelineTime'], - }, - }, -}; diff --git a/features/web-animations-2.js b/features/web-animations-2.js deleted file mode 100644 index ed3b01db..00000000 --- a/features/web-animations-2.js +++ /dev/null @@ -1,50 +0,0 @@ -export default { - id: 'web-animations-2', - title: 'Web Animations Level 2', - link: 'web-animations-2', - status: 'experimental', - globals: { - AnimationTimeline: { - link: '#the-animationtimeline-interface', - mdnGroup: 'DOM', - members: ['currentTime', 'duration'], - methods: ['play'], - }, - AnimationEffect: { - link: '#the-animationeffect-interface', - mdnGroup: 'DOM', - members: [ - 'parent', - 'previousSibling', - 'nextSibling', - ], - methods: ['before', 'after', 'replace', 'remove'], - }, - GroupEffect: { - link: '#the-groupeffect-interface', - mdnGroup: 'DOM', - members: [ - 'children', - 'firstChild', - 'lastChild', - ], - methods: ['clone', 'prepend', 'append'], - }, - AnimationNodeList: { - link: '#the-animationnodelist-interface', - mdnGroup: 'DOM', - members: ['length', 'item'], - }, - SequenceEffect: { - link: '#the-sequenceeffect-interface', - mdnGroup: 'DOM', - extends: 'GroupEffect', - methods: ['clone'], - }, - KeyframeEffect: { - link: '#the-keyframeeffect-interface', - mdnGroup: 'DOM', - members: ['iteratonComposite'], - }, - }, -}; diff --git a/index.html b/index.html new file mode 100644 index 00000000..ff8a2460 --- /dev/null +++ b/index.html @@ -0,0 +1,152 @@ + + + + + +The CSS3 Test + + + + + + + + +

+ + How does my + Browser Score? + +

+ + + +
+
+
+

Your browser scores 0%

+ +

+ + Recognized + + 0 + out of 0 features + + in 0 ms +

+
+ +
+
+

+ + +

+
+

+ +
+
+
+
+ + +
+ + + diff --git a/package.json b/package.json index 73301269..5fb8fe25 100644 --- a/package.json +++ b/package.json @@ -18,10 +18,5 @@ "dependencies": { "ua-parser-js": "^2.0.4", "vue": "^3.5.19" - }, - "devDependencies": { - "prettier-plugin-brace-style": "latest", - "prettier-plugin-merge": "latest", - "prettier-plugin-space-before-function-paren": "latest" } } diff --git a/specs.js b/specs.js deleted file mode 100644 index 9cd6e975..00000000 --- a/specs.js +++ /dev/null @@ -1,155 +0,0 @@ -export { default as compat } from './features/compat.js'; -export { default as cssAlign3 } from './features/css-align-3.js'; -export { default as cssAnchorPosition1 } from './features/css-anchor-position-1.js'; -export { default as cssAnimationWorklet1 } from './features/css-animation-worklet-1.js'; -export { default as cssAnimations1 } from './features/css-animations-1.js'; -export { default as cssAnimations2 } from './features/css-animations-2.js'; -export { default as cssBackgrounds3 } from './features/css-backgrounds-3.js'; -export { default as cssBackgrounds4 } from './features/css-backgrounds-4.js'; -export { default as cssBorders4 } from './features/css-borders-4.js'; -export { default as cssBox3 } from './features/css-box-3.js'; -export { default as cssBox4 } from './features/css-box-4.js'; -export { default as cssBreak3 } from './features/css-break-3.js'; -export { default as cssBreak4 } from './features/css-break-4.js'; -export { default as cssCascade3 } from './features/css-cascade-3.js'; -export { default as cssCascade4 } from './features/css-cascade-4.js'; -export { default as cssCascade5 } from './features/css-cascade-5.js'; -export { default as cssCascade6 } from './features/css-cascade-6.js'; -export { default as cssColor3 } from './features/css-color-3.js'; -export { default as cssColor4 } from './features/css-color-4.js'; -export { default as cssColor5 } from './features/css-color-5.js'; -export { default as cssColorAdjust1 } from './features/css-color-adjust-1.js'; -export { default as cssColorHdr1 } from './features/css-color-hdr-1.js'; -export { default as cssComposition1 } from './features/css-composition-1.js'; -export { default as cssComposition2 } from './features/css-composition-2.js'; -export { default as cssConditional3 } from './features/css-conditional-3.js'; -export { default as cssConditional4 } from './features/css-conditional-4.js'; -export { default as cssConditional5 } from './features/css-conditional-5.js'; -export { default as cssContain1 } from './features/css-contain-1.js'; -export { default as cssContain2 } from './features/css-contain-2.js'; -export { default as cssContain3 } from './features/css-contain-3.js'; -export { default as cssContent3 } from './features/css-content-3.js'; -export { default as cssCounterStyles3 } from './features/css-counter-styles-3.js'; -export { default as cssDisplay3 } from './features/css-display-3.js'; -export { default as cssDisplay4 } from './features/css-display-4.js'; -export { default as cssEasing1 } from './features/css-easing-1.js'; -export { default as cssEasing2 } from './features/css-easing-2.js'; -export { default as cssEnv1 } from './features/css-env-1.js'; -export { default as cssExclusions1 } from './features/css-exclusions-1.js'; -export { default as cssFlexbox1 } from './features/css-flexbox-1.js'; -export { default as cssFontLoading3 } from './features/css-font-loading-3.js'; -export { default as cssFonts3 } from './features/css-fonts-3.js'; -export { default as cssFonts4 } from './features/css-fonts-4.js'; -export { default as cssFonts5 } from './features/css-fonts-5.js'; -export { default as cssForms1 } from './features/css-forms-1.js'; -export { default as cssGaps1 } from './features/css-gaps-1.js'; -export { default as cssGrid1 } from './features/css-grid-1.js'; -export { default as cssGrid2 } from './features/css-grid-2.js'; -export { default as cssGrid3 } from './features/css-grid-3.js'; -export { default as cssHighlightApi1 } from './features/css-highlight-api-1.js'; -export { default as cssImages3 } from './features/css-images-3.js'; -export { default as cssImages4 } from './features/css-images-4.js'; -export { default as cssImages5 } from './features/css-images-5.js'; -export { default as cssInline3 } from './features/css-inline-3.js'; -export { default as cssLayoutApi1 } from './features/css-layout-api-1.js'; -export { default as cssLineGrid1 } from './features/css-line-grid-1.js'; -export { default as cssLinkParams1 } from './features/css-link-params-1.js'; -export { default as cssLists3 } from './features/css-lists-3.js'; -export { default as cssLogical1 } from './features/css-logical-1.js'; -export { default as cssMasking1 } from './features/css-masking-1.js'; -export { default as cssMixins1 } from './features/css-mixins-1.js'; -export { default as cssMulticol1 } from './features/css-multicol-1.js'; -export { default as cssMulticol2 } from './features/css-multicol-2.js'; -export { default as cssNamespaces3 } from './features/css-namespaces-3.js'; -export { default as cssNav1 } from './features/css-nav-1.js'; -export { default as cssNesting1 } from './features/css-nesting-1.js'; -export { default as cssOverflow3 } from './features/css-overflow-3.js'; -export { default as cssOverflow4 } from './features/css-overflow-4.js'; -export { default as cssOverflow5 } from './features/css-overflow-5.js'; -export { default as cssOverscroll1 } from './features/css-overscroll-1.js'; -export { default as cssPage3 } from './features/css-page-3.js'; -export { default as cssPaintApi1 } from './features/css-paint-api-1.js'; -export { default as cssPosition3 } from './features/css-position-3.js'; -export { default as cssPropertiesValuesApi1 } from './features/css-properties-values-api-1.js'; -export { default as cssPseudo4 } from './features/css-pseudo-4.js'; -export { default as cssRegions1 } from './features/css-regions-1.js'; -export { default as cssRhythmic } from './features/css-rhythmic-1.js'; -export { default as cssRuby1 } from './features/css-ruby-1.js'; -export { default as cssScoping1 } from './features/css-scoping-1.js'; -export { default as cssScrollAnchoring1 } from './features/css-scroll-anchoring-1.js'; -export { default as cssScrollSnap1 } from './features/css-scroll-snap-1.js'; -export { default as cssScrollSnap2 } from './features/css-scroll-snap-2.js'; -export { default as cssScrollbars1 } from './features/css-scrollbars-1.js'; -export { default as cssShadowParts1 } from './features/css-shadow-parts-1.js'; -export { default as cssShapes1 } from './features/css-shapes-1.js'; -export { default as cssShapes2 } from './features/css-shapes-2.js'; -export { default as cssSizeAdjust1 } from './features/css-size-adjust-1.js'; -export { default as cssSizing3 } from './features/css-sizing-3.js'; -export { default as cssSizing4 } from './features/css-sizing-4.js'; -export { default as cssSpeech1 } from './features/css-speech-1.js'; -export { default as cssText3 } from './features/css-text-3.js'; -export { default as cssText4 } from './features/css-text-4.js'; -export { default as cssTextDecor3 } from './features/css-text-decor-3.js'; -export { default as cssTextDecor4 } from './features/css-text-decor-4.js'; -export { default as cssTransforms1 } from './features/css-transforms-1.js'; -export { default as cssTransforms2 } from './features/css-transforms-2.js'; -export { default as cssTransitions1 } from './features/css-transitions-1.js'; -export { default as cssTransitions2 } from './features/css-transitions-2.js'; -export { default as cssTypedOm1 } from './features/css-typed-om-1.js'; -export { default as cssUi3 } from './features/css-ui-3.js'; -export { default as cssUi4 } from './features/css-ui-4.js'; -export { default as cssValues3 } from './features/css-values-3.js'; -export { default as cssValues4 } from './features/css-values-4.js'; -export { default as cssValues5 } from './features/css-values-5.js'; -export { default as cssVariables1 } from './features/css-variables-1.js'; -export { default as cssVariables2 } from './features/css-variables-2.js'; -export { default as cssViewTransitions1 } from './features/css-view-transitions-1.js'; -export { default as cssViewTransitions2 } from './features/css-view-transitions-2.js'; -export { default as cssViewport1 } from './features/css-viewport-1.js'; -export { default as cssWillChange1 } from './features/css-will-change-1.js'; -export { default as cssWritingModes3 } from './features/css-writing-modes-3.js'; -export { default as cssWritingModes4 } from './features/css-writing-modes-4.js'; -export { default as css2Box } from './features/css2-box.js'; -export { default as css2Cascade } from './features/css2-cascade.js'; -export { default as css2Colors } from './features/css2-colors.js'; -export { default as css2Fonts } from './features/css2-fonts.js'; -export { default as css2Generate } from './features/css2-generate.js'; -export { default as css2Media } from './features/css2-media.js'; -export { default as css2Page } from './features/css2-page.js'; -export { default as css2Selectors } from './features/css2-selectors.js'; -export { default as css2Tables } from './features/css2-tables.js'; -export { default as css2Text } from './features/css2-text.js'; -export { default as css2Ui } from './features/css2-ui.js'; -export { default as css2VisuDet } from './features/css2-visudet.js'; -export { default as css2VisuFx } from './features/css2-visufx.js'; -export { default as css2VisuRen } from './features/css2-visuren.js'; -export { default as cssom1 } from './features/cssom-1.js'; -export { default as cssomView1 } from './features/cssom-view-1.js'; -export { default as fillStroke3 } from './features/fill-stroke-3.js'; -export { default as filterEffects1 } from './features/filter-effects-1.js'; -export { default as filterEffects2 } from './features/filter-effects-2.js'; -export { default as fullscreen } from './features/fullscreen.js'; -export { default as html } from './features/html.js'; -export { default as mathmlCore } from './features/mathml-core.js'; -export { default as mediaQueries3 } from './features/mediaqueries-3.js'; -export { default as mediaQueries4 } from './features/mediaqueries-4.js'; -export { default as mediaQueries5 } from './features/mediaqueries-5.js'; -export { default as motion1 } from './features/motion-1.js'; -export { default as pointerEvents1 } from './features/pointerevents1.js'; -export { default as pointerEvents3 } from './features/pointerevents3.js'; -export { default as resizeObserver1 } from './features/resize-observer-1.js'; -export { default as scrollAnimations1 } from './features/scroll-animations-1.js'; -export { default as selectors3 } from './features/selectors-3.js'; -export { default as selectors4 } from './features/selectors-4.js'; -export { default as selectors5 } from './features/selectors-5.js'; -export { default as svg2Coords } from './features/svg2-coords.js'; -export { default as svg2Geometry } from './features/svg2-geometry.js'; -export { default as svg2Interact } from './features/svg2-interact.js'; -export { default as svg2Painting } from './features/svg2-painting.js'; -export { default as svg2Paths } from './features/svg2-paths.js'; -export { default as svg2PServers } from './features/svg2-pservers.js'; -export { default as svg2Text } from './features/svg2-text.js'; -export { default as webAnimations1 } from './features/web-animations-1.js'; -export { default as webAnimations2 } from './features/web-animations-2.js'; -export { default as webVtt } from './features/webvtt.js'; -export { default as webxrDomOverlays1 } from './features/webxr-dom-overlays-1.js'; diff --git a/src/app.js b/src/app.js new file mode 100644 index 00000000..1fccac66 --- /dev/null +++ b/src/app.js @@ -0,0 +1,159 @@ +import { createApp } from '../node_modules/vue/dist/vue.esm-browser.js'; +import AbstractFeature from './classes/AbstractFeature.js'; +import Score from './classes/Score.js'; +import Specs from './tests.js'; +import Spec from './classes/Spec.js'; +import content from './vue/directives/content.js'; +import { passclass, round, percent } from './util.js'; + +// Vue components +import Feature from './vue/components/feature/feature.js'; +import CarbonAds from './vue/components/carbon-ads.js'; +import SupportStatus from './vue/components/support-status/support-status.js'; + +let allSpecs = {}; + +let root = new AbstractFeature(); + +for (let id in Specs) { + let spec = Specs[id]; + spec.id = id; + spec = new Spec(spec, root); + allSpecs[id] = spec; +} + +// Components available in every component +let globalComponents = { + "support-status": SupportStatus, + "bs-feature": Feature, +}; + +// Components only available in the top-level app instance +let localComponents = { + "carbon-ads": CarbonAds, +}; + +let appSpec = { + data() { + return { + root, + + /** + * All specs as dictionary + * @type {Record} + */ + allSpecs, + filter: new URLSearchParams(window.location.search).get('filter') ?? '', + // TODO move this to Score + testTime: 0, + favicon: '', + }; + }, + + computed: { + /** Sorted and filtered specs + * @type {Spec[]} + */ + specs () { + return this.allSpecsList.filter(spec => spec.matchesFilter(this.filter)).sort((a, b) => a.title.localeCompare(b.title)); + }, + + /** All specs as array + * @type {Spec[]} + */ + allSpecsList () { + return Object.values(this.allSpecs); + }, + + /** Score for filtered specs + * @type {Score} + */ + score () { + return this.root.score; + }, + }, + + mounted() { + this.updateFavicon(); + }, + + methods: { + passclass, + round, + percent, + + async updateFavicon() { + if (this.$refs.supportStatus) { + let favicon = await this.$refs.supportStatus.getDataUrl(); + this.favicon = favicon; + document.getElementById('favicon').href = favicon; + } + }, + }, + + watch: { + specs: { + handler() { + this.root.children = this.specs; + + for (let spec of this.specs) { + spec.test(); + } + + this.root.score.recalc(); + }, + immediate: true, + }, + + filter: { + handler() { + // Update address bar + let searchParams = new URLSearchParams(location.search); + if (this.filter) { + searchParams.set('filter', this.filter); + } + else { + searchParams.delete('filter'); + } + + let newUrl = location.pathname + '?' + searchParams + location.hash; + + history.replaceState({}, '', newUrl); + }, + }, + + "score.value": { + handler() { + this.$nextTick(() => { + this.updateFavicon(); + }); + }, + }, + }, + + directives: { + content, + }, + + components: localComponents, + + compilerOptions: { + isCustomElement: tag => !(tag in globalComponents || tag in localComponents), + }, +}; + +let createdApp = createApp(appSpec) + +// Global components +for (let [tag, component] of Object.entries(globalComponents)) { + createdApp.component(tag, component); +} + +let app = createdApp.mount("#content"); + +// Global exports +Object.assign(globalThis, { + app, + appSpec, + allSpecs, +}); diff --git a/src/classes/AbstractFeature.js b/src/classes/AbstractFeature.js new file mode 100644 index 00000000..c8e94fa2 --- /dev/null +++ b/src/classes/AbstractFeature.js @@ -0,0 +1,135 @@ +/** + * Base class for all features or feature groups (including specs) + */ + +import Score from './Score.js'; +import { IS_DEV } from '../util.js'; + +export default class AbstractFeature { + children = []; + tested = false; + + constructor (def = {}, parent) { + this.def = def; + + // For debugging + if (IS_DEV) { + // Expose all instances + this.constructor.all ??= []; + this.constructor.all.push(this); + + // Make class a global + globalThis[this.constructor.name] ??= this.constructor; + } + + if (parent) { + Object.defineProperty(this, 'parent', { + value: parent, + enumerable: false, + writable: true, + configurable: true, + }); + } + + this.id = def.id; + + if (def.title) { + Object.defineProperty(this, 'title', { + value: def.title, + enumerable: true, + writable: true, + configurable: true, + }); + } + + this.score = new Score(this, this.constructor.forceTotal); + } + + get link() { + return this.specLink ?? this.draftLink; + } + + get draftLink () { + let link = this.def.link ?? this.def.links?.dev ?? ''; + + if (link) { + return (this.parent?.draftLink ?? '') + link; + } + + return link; + } + + get specLink () { + let link = this.def.link ?? this.def.links?.tr ?? ''; + + if (link) { + return (this.parent?.specLink ?? '') + link; + } + + return link; + } + + get mdnLink () { + let ret = this.def.mdn ?? this.def.links?.mdn; + return ret ? 'https://developer.mozilla.org/en-US/docs/Web/' + ret : ''; + } + + /** + * Get a globally unique id for this feature. + */ + get uid () { + return this.getUid(); + } + + /** + * Same as uid, but uses hyphens instead of dots. + */ + get htmlId () { + return this.getUid('--'); + } + + /** + * Get a globally unique id for this feature, with a custom separator for different levels + */ + getUid (separator = '.', pathSuffix = '') { + let parentUid = this.parent?.getUid(separator) ?? ''; + if (parentUid) { + parentUid += separator; + } + + pathSuffix = pathSuffix ? separator + pathSuffix : ''; + let id = this.id ?? ''; + + return parentUid + id + pathSuffix; + } + + closest (fn) { + if (fn(this)) { + return this; + } + + return this.parent?.closest(fn) ?? null; + } + + closestValue (fn) { + return fn(this) ?? this.parent?.closestValue(fn); + } + + test () { + if (this.tested) { + return; + } + + this.tested = true; + + if (this.children?.length > 0) { + for (let child of this.children) { + child.test(); + } + + this.score.recalc(); + } + } +} + +globalThis.AbstractFeature = AbstractFeature; diff --git a/src/classes/CSSPropertyFeature.js b/src/classes/CSSPropertyFeature.js new file mode 100644 index 00000000..fe901d88 --- /dev/null +++ b/src/classes/CSSPropertyFeature.js @@ -0,0 +1,36 @@ +import Feature from './Feature.js'; +import CSSPropertyValueFeature from './CSSPropertyValueFeature.js'; +import supportsProperty from '../supports/property.js'; + + +export default class CSSPropertyFeature extends Feature { + /** + * @type {CSSPropertyValueFeature[]} + */ + children = []; + + constructor (def, parent, group) { + super(def, parent, group); + + if (this.tests?.length > 0) { + // Not a leaf node + for (let test of this.tests) { + let subFeature = new CSSPropertyValueFeature({id: test}, this); + this.children.push(subFeature); + } + } + } + + get property () { + return this.id; + } + + get code () { + return this.id; + } + + leafTest () { + // Has no values + return supportsProperty(this.id); + } +} diff --git a/src/classes/CSSPropertyValueFeature.js b/src/classes/CSSPropertyValueFeature.js new file mode 100644 index 00000000..0a5c7fd4 --- /dev/null +++ b/src/classes/CSSPropertyValueFeature.js @@ -0,0 +1,16 @@ +/** + * A feature that tests whether a specific value is supported for a specific property + * where the focus is the property + */ +import Feature from './Feature.js'; +import supportsValue from '../supports/value.js'; + +export default class CSSPropertyValueFeature extends Feature { + constructor (def, parent, group) { + super(def, parent, group); + } + + leafTest () { + return supportsValue(this.id, this.parent.id); + } +} diff --git a/src/classes/CSSValueFeature.js b/src/classes/CSSValueFeature.js new file mode 100644 index 00000000..47686184 --- /dev/null +++ b/src/classes/CSSValueFeature.js @@ -0,0 +1,59 @@ +/** + * A feature that tests whether a specific value is supported for a specific property + * where the focus is the value + */ +import Feature from './Feature.js'; +import CSSValuePropertyFeature from './CSSValuePropertyFeature.js'; +import supportsValue from '../supports/value.js'; + +export default class CSSValueFeature extends Feature { + /** + * @type {CSSValuePropertyFeature[]} + */ + children = []; + + constructor (def, parent, group) { + super(def, parent, group); + + if (def.property) { + this.property = def.property; + } + else { + let properties = def.properties ?? group?.properties ?? parent?.properties; + properties = Array.isArray(properties) ? properties : [properties]; + + if (properties.length === 1) { + this.property = properties[0]; + } + else { + this.properties = properties; + } + } + + if (this.tests) { + if (this.tests.length === 1 && (!this.id || this.id === this.tests[0])) { + // Testing a single value for a single property, no children to create + this.id = this.tests[0]; + delete this.tests; + } + else { + this._createChildren(); + } + } + + if (this.properties && this.children.length === 0) { + // We have multiple properties and have not yet created children + for (let property of this.properties) { + let subFeature = new CSSValuePropertyFeature({id: property}, this); + this.children.push(subFeature); + } + } + } + + leafTest () { + let value = this.id; + let property = this.closestValue(f => f.property); + if (this.id?.startsWith('currentColor')) console.log('value', value, property); + return supportsValue(value, property); + } +} diff --git a/src/classes/CSSValuePropertyFeature.js b/src/classes/CSSValuePropertyFeature.js new file mode 100644 index 00000000..9a67eaa2 --- /dev/null +++ b/src/classes/CSSValuePropertyFeature.js @@ -0,0 +1,16 @@ +import Feature from './Feature.js'; +import supportsValue from '../supports/value.js'; + +export default class CSSValuePropertyFeature extends Feature { + constructor (def, parent, group) { + super(def, parent, group); + } + + leafTest () { + let valueFeature = this.closest(f => f.constructor.name === 'CSSValueFeature'); + let value = valueFeature?.id; + let property = this.id; + return supportsValue(value, property); + } + +} diff --git a/src/classes/Feature.js b/src/classes/Feature.js new file mode 100644 index 00000000..2fc990f7 --- /dev/null +++ b/src/classes/Feature.js @@ -0,0 +1,129 @@ +/** + * A syntax feature (i.e. not a spec) + * May or may not have children + */ + +import featureTypes from '../features.js'; +import AbstractFeature from './AbstractFeature.js'; + +export default class Feature extends AbstractFeature { + static forceTotal = 1; + constructor (def, parent, group) { + super(def, parent); + + if (def.code) { + this.id ??= def.code; + } + + this.group = group; + this.type = def.type ?? parent?.type; + + if (def.tests) { + this.def = def; + this.tests = def.tests; + } + else if (Array.isArray(def) && typeof def[0] === 'string') { + // feature: [test1, test2, ...] + this.tests = def; + } + + if (this.tests && !Array.isArray(this.tests)) { + this.tests = [this.tests]; + } + + if (this.constructor === Feature && this.tests) { + // Subclasses need to call this on their own + this._createChildren(); + } + } + + _createChildren () { + if (this.tests.length > 0) { + for (let test of this.tests) { + let subFeature = new this.constructor({id: test}, this); + this.children.push(subFeature); + } + } + } + + get spec () { + return this.closest(f => f.constructor.name === 'Spec'); + } + + get code () { + return this.def.code ?? this.id; + } + + get mdnLink () { + let link = this.def.mdn ?? this.def.links?.mdn; + if (link) { + return getMdnLink(link, this.id, this.def.mdnGroup) + } + return ''; + } + + get uid () { + let parentUid = this.parent ? this.parent.uid + '.' : ''; + let typeUid = this.type ? this.type + '.' : ''; + return parentUid + typeUid + this.id; + } + + leafTest () { + let testCallback = featureTypes[this.type]; + + if (!testCallback) { + return null; + } + + let test = this.tests?.[0] ?? this.id; + test = test?.id ?? test; // test must be a string + return testCallback(test, this.id, this) ?? {}; + } + + test () { + if (this.children.length > 0) { + return super.test(); + } + + if (this.tested) { + return; + } + + this.tested = true; + + let startTime = performance.now(); + + // TODO run leafTest for parents that support it to save work on testing the children + this.result = this.leafTest(); + + this.score.set({ + passedTests: this.result.success, + totalTests: 1, + testTime: performance.now() - startTime, + }); + + this.score.recalc(); + } +} + +function getMdnLink (mdn, feature, mdnGroup) { + let mdnLink = 'https://developer.mozilla.org/en-US/docs/Web/'; + + switch (mdnGroup) { + case 'SVG': + mdnLink += 'SVG/Attribute/'; + break; + case 'DOM': + mdnLink += 'API/'; + break; + default: + mdnLink += 'CSS/'; + // add exception for Media Queries if no link define + // if (what === 'Media queries' && !links.mdn) { + // mdnLink += '@media/'; + // } + } + + mdnLink += mdn ?? feature.replace('()', '').replace(/(@[^ \/]+)[^\/]*(\/.*)/, '$1$2'); + return mdnLink; +} diff --git a/src/classes/InterfaceFeature.js b/src/classes/InterfaceFeature.js new file mode 100644 index 00000000..0855460d --- /dev/null +++ b/src/classes/InterfaceFeature.js @@ -0,0 +1,37 @@ +import Feature from './Feature.js'; +import supportsInterface from '../supports/interface.js'; +import InterfacePropertyFeature from './InterfacePropertyFeature.js'; + +export default class InterfaceFeature extends Feature { + /** + * @type {InterfacePropertyFeature[]} + */ + children = []; + + constructor (def, parent, group) { + super(def, parent, group); + + this.required = def.required ?? group?.required; + this.interface = def.interface ?? group?.interface; + + if (this.tests) { + if (this.tests.length === 1 && (!this.id || this.id === this.tests[0])) { + // Testing a single value for a single property, no children to create + this.id = this.tests[0]; + delete this.tests; + } + else { + if (this.tests.length > 0) { + for (let test of this.tests) { + let subFeature = new InterfacePropertyFeature({id: test}, this); + this.children.push(subFeature); + } + } + } + } + } + + leafTest () { + return supportsInterface(this.id); + } +} diff --git a/src/classes/InterfacePropertyFeature.js b/src/classes/InterfacePropertyFeature.js new file mode 100644 index 00000000..9ad30c0b --- /dev/null +++ b/src/classes/InterfacePropertyFeature.js @@ -0,0 +1,12 @@ +import Feature from './Feature.js'; +import supportsAttributeOrMethod from '../supports/attributeOrMethod.js'; + +export default class InterfacePropertyFeature extends Feature { + leafTest () { + let required = this.required ?? this.parent.required; + let interfaceObject = this.interface ?? this.parent.interface; + let interfaceName = this.parent.id; + + return supportsAttributeOrMethod(interfaceName, this.id, required, interfaceObject); + } +} diff --git a/src/classes/Score.js b/src/classes/Score.js new file mode 100644 index 00000000..4dad7884 --- /dev/null +++ b/src/classes/Score.js @@ -0,0 +1,131 @@ +export default class Score { + parent = null; + passed = 0; + total = 0; + passedTests = 0; + failedTests = 0; + totalTests = 0; + testTime = 0; + + /** + * @param {*} node - Score of parent object + * @param {*} forceTotal - By default, all tests count as individual features. Set this to 1 to count them as 1 feature. + */ + constructor(node, forceTotal) { + this.forceTotal = forceTotal; + if (node) { + this.node = node; + } + } + + get parent () { + return this.node?.parent?.score ?? null; + } + + get children () { + return this.node?.children?.map(child => child.score) ?? []; + } + + /** + * Percentage of passed tests + */ + get success () { + return this.passedTests / this.totalTests; + } + + get value () { + return this.valueOf(); + } + + /** + * Percentage of passed features + */ + valueOf () { + return this.passed / this.total; + } + + set (score) { + if (!score || (!score.totalTests && !this.totalTests)) { + return; + } + + for (let key in score) { + if (key in this) { + this[key] = score[key]; + } + } + + if ('totalTests' in score) { + this.total = this.forceTotal ?? this.totalTests; + } + + if ('passedTests' in score) { + this.failedTests = score.failedTests ?? (this.totalTests - this.passedTests); + this.passed = this.passedTests * this.total / this.totalTests; + } + + this.parent?.recalc(); + } + + /** + * Recalculate this and ancestor scores from children + * @returns + */ + recalc() { + if (!this.children?.length) { + // Nothing to do here + return; + } + + this.passed = 0; + this.total = 0; + this.passedTests = 0; + this.failedTests = 0; + this.totalTests = 0; + this.testTime = 0; + + let children = this.children; + + if (children.length === 1) { + this.set(children[0]); + } + else { + for (let child of children) { + this.passed += child.passed; + this.total += child.total; + this.passedTests += child.passedTests; + this.failedTests += child.failedTests; + this.totalTests += child.totalTests; + this.testTime += child.testTime; + } + } + + if (this.forceTotal) { + let actualTotal = this.total; + this.total = this.forceTotal; + let previousPassed = this.passed; + this.passed = previousPassed * this.forceTotal / actualTotal; + } + + this.parent?.recalc(); + } + + toString () { + return +(this.value * 100).toFixed(2) + '%'; + } + + /** + * Convert to JSON + * @returns {Object} + */ + toJSON () { + return { + passed: this.passed, + total: this.total, + passedTests: this.passedTests, + failedTests: this.failedTests, + totalTests: this.totalTests, + testTime: this.testTime, + }; + } +} diff --git a/src/classes/Spec.js b/src/classes/Spec.js new file mode 100644 index 00000000..fadbeec6 --- /dev/null +++ b/src/classes/Spec.js @@ -0,0 +1,141 @@ +import AbstractFeature from './AbstractFeature.js'; +import { groups, orgs } from '../data.js'; +import Feature from "./Feature.js"; +import CSSValueFeature from './CSSValueFeature.js'; +import CSSPropertyFeature from './CSSPropertyFeature.js'; +import {types as featureTypes} from '../features.js'; +import InterfaceFeature from './InterfaceFeature.js'; + +// Shorten the title by removing parentheticals, +// subheadings, CSS and Module words +const removedWords = RegExp(` *(?:\\b${['Level', 'Module'].join('|')}\\b)(?=\\s)`, 'g'); +const removedOther = / *(?:\([^)]*\)|:.*)( *)/g; + +const statuses = new Set(['stable', 'experimental']); + +export default class Spec extends AbstractFeature { + features = {}; + static statuses = statuses; + + constructor (def, parent) { + super(def, parent); + + if (def.title) { + this.title = this.title.replace(removedWords, ''); + this.title = this.title.replace(removedOther, '$1'); + this.title = this.title.trim(); + } + + for (let type of featureTypes) { + if (!(type in this.def)) { + continue; + } + + let {properties, required, interface: Interface, ...features} = this.def[type]; + + this.features[type] = []; + + for (let id in features) { + let feature = features[id]; + feature.id = id; + feature.type = type; + + if (type === 'values') { + feature = new CSSValueFeature(feature, this, this.def[type]); + } + else if (type === 'interfaces') { + feature = new InterfaceFeature(feature, this, this.def[type]); + } + else if (type === 'properties') { + feature = new CSSPropertyFeature(feature, this, this.def[type]); + } + else { + feature = new Feature(feature, this); + } + + this.features[type].push(feature); + + this.children.push(feature); + } + } + } + + get status () { + return this.def.status; + } + + get firstSnapshot () { + return this.def.firstSnapshot; + } + + get lastSnapshot () { + return this.def.lastSnapshot; + } + + get group () { + let group = this.def.group ?? 'csswg'; + return groups[group] ?? orgs[group] ?? groups.csswg; + } + + get org () { + let org = this.group.org ?? this.group.id; + return orgs[org]; + } + + get specLink () { + let ret = super.specLink; + + if (ret) { + let template = this.group.specs ?? this.org.specs; + return template.replace('{shortname}', ret).replace(/(\/|\.html)\/$/, '$1'); + } + + return ''; + } + + get draftLink () { + let ret = super.draftLink; + + if (ret) { + let template = this.group.drafts ?? this.org.drafts; + return template.replace('{shortname}', ret).replace(/(\/|\.html)\/$/, '$1'); + } + + return ''; + } + + matchesFilter (filter) { + if (!filter) { + return this.firstSnapshot !== 2.2; + } + + // Filter list of specifications + if (statuses.has(filter)) { + return this.status === filter; + } + + if (filter.match(/^css\d/)) { + if (!this.firstSnapshot) { + return false; + } + + const snapshot = Number(filter.substring(3)); + if (this.firstSnapshot > snapshot || this.lastSnapshot < snapshot) { + return false; + } + } + + // Group & org filters + if (filter === 'others') { + return this.group && this.group.id !== 'csswg'; + } + else if (filter in groups) { + return this.group && this.group.id === filter; + } + else if (filter in orgs) { + return this.org && this.org.id === filter; + } + + return true; + } +} diff --git a/src/data.js b/src/data.js new file mode 100644 index 00000000..968c8b66 --- /dev/null +++ b/src/data.js @@ -0,0 +1,64 @@ +export const orgs = { + w3c: { + title: 'W3C', + specs: 'https://www.w3.org/TR/{shortname}/', + drafts: 'https://w3c.github.io/{shortname}/', + groups: { + csswg: { + title: 'CSS Working Group', + drafts: 'https://drafts.csswg.org/{shortname}/', + }, + fxtf: { + title: 'FX Task Force', + drafts: 'https://drafts.fxtf.org/{shortname}/', + }, + houdini: { + title: 'CSS-TAG Houdini', + drafts: 'https://drafts.css-houdini.org/{shortname}/', + }, + iwwg: { + title: 'Immersive Web Working Group', + drafts: 'https://immersive-web.github.io/{shortname}/', + }, + svgwg: { + title: 'SVG Working Group Editor Drafts', + drafts: 'https://svgwg.org/{shortname}/', + }, + math: { + title: 'The MathML Refresh Community Group', + drafts: 'https://mathml-refresh.github.io/{shortname}/', + }, + }, + }, + whatwg: { + title: 'WHATWG', + specs: 'https://{shortname}.spec.whatwg.org/', + drafts: 'https://{shortname}.spec.whatwg.org/', + }, + ecma: { + title: 'ECMA', + specs: 'https://www.ecma-international.org/publications-and-standards/standards/ecma-{shortname}/', + groups: { + tc39: { + title: 'TC39', + drafts: 'https://tc39.es/proposal-{shortname}', + }, + }, + }, +}; + +export const groups = {}; + +for (let id in orgs) { + let org = orgs[id]; + org.id = id; + + if (org.groups) { + for (let groupId in org.groups) { + let group = org.groups[groupId]; + group.id = groupId; + group.org = id; + groups[groupId] = group; + } + } +} diff --git a/src/features.js b/src/features.js new file mode 100644 index 00000000..dafc281b --- /dev/null +++ b/src/features.js @@ -0,0 +1,73 @@ +import Supports from './supports.js'; + +export const types = new Set(['properties', 'values', 'interfaces', 'descriptors', 'selectors', 'declaration', '@rules', 'Media queries']); + +export default { + // values: function (test, name, feature) { + // let properties = feature[feature.type]?.properties || feature.properties; + + // if (!properties) { + // return { + // success: 0, + // note: 'No properties to test', + // }; + // } + + // let failed = []; + + // for (var j = 0, property; (property = properties[j++]); ) { + // if (!Supports.property(property).success) { + // // Property not supported, no point in using it to test values + // properties.splice(--j, 1); + // continue; + // } + + // if (!Supports.value(property, test).success) { + // failed.push(property); + // } + // } + + // var success = properties.length > 0 ? 1 - failed.length / properties.length : 0; + + // return { + // success: success, + // note: success > 0 && success < 1 ? 'Failed in: ' + failed.join(', ') : '', + // }; + // }, + + // properties: function (value, property) { + // return Supports.value(property, value); + // }, + + descriptors: function (value, name, feature) { + var required = undefined; + if (feature.required) { + if (feature.required[value]) { + required = feature.required[value]; + } else if (feature.required['*']) { + required = feature.required['*']; + } + } + return Supports.descriptorvalue(name, value, required); + }, + + selectors: function (test) { + return Supports.selector(test); + }, + + declaration: function (test) { + return Supports.declaration(test); + }, + + '@rules': function (test) { + return Supports.atrule(test); + }, + + // interfaces: function (value, name, feature) { + // return Supports.attributeOrMethod(name, value, feature.required, feature.interface); + // }, + + 'Media queries': function (test) { + return Supports.mq(test); + }, +}; diff --git a/src/supports.js b/src/supports.js new file mode 100644 index 00000000..69b04022 --- /dev/null +++ b/src/supports.js @@ -0,0 +1,40 @@ + +/** + * Setup dummy elements + */ +import { prefixes, domPrefixes } from './supports/shared.js'; +import { IS_DEV } from './util.js'; + +import property from './supports/property.js'; +import value from './supports/value.js'; +import descriptorvalue from './supports/descriptorvalue.js'; +import selector from './supports/selector.js'; +import atrule from './supports/atrule.js'; +import mq from './supports/mq.js'; +import variable from './supports/variable.js'; +import declaration from './supports/declaration.js'; +import Interface from './supports/interface.js'; +import attributeOrMethod from './supports/attributeOrMethod.js'; + +const Supports = { + prefixes, + domPrefixes, + property, + value, + descriptorvalue, + selector, + atrule, + mq, + variable, + declaration, + interface: Interface, + attributeOrMethod, +}; + +if (IS_DEV) { + window.Supports = Supports; +} + +export default Supports; +export { property, value, descriptorvalue, selector, atrule, mq, variable, declaration, Interface, attributeOrMethod }; + diff --git a/src/supports/atrule.js b/src/supports/atrule.js new file mode 100644 index 00000000..ac16df2c --- /dev/null +++ b/src/supports/atrule.js @@ -0,0 +1,30 @@ +import { prefixes, styleElement } from './shared.js'; + +let cached = {}; + +/** + * Low-level function to check if an at-rule is supported + * @param {string} atrule - The at-rule to check as a string (e.g. "@supports (display: flex)") + * @returns {boolean} + */ +export function isSupported (atrule) { + styleElement.textContent = atrule; // Safari 4 has issues with style.innerHTML + return styleElement.sheet.cssRules.length > 0; +} + +export default function (atrule) { + let cachedResult = cached[atrule]; + let success, prefix; + + if (cachedResult === undefined) { + prefix = prefixes.find(prefix => isSupported(atrule.replace(/^@/, '@' + prefix))); + success = prefix !== undefined; + cached[atrule] = prefix === '' ? true : (prefix ?? false); + } + else { + success = Boolean(cachedResult); + prefix = typeof cachedResult === "boolean" ? '' : cachedResult; + } + + return {success, prefix}; +} diff --git a/src/supports/attributeOrMethod.js b/src/supports/attributeOrMethod.js new file mode 100644 index 00000000..80759b8b --- /dev/null +++ b/src/supports/attributeOrMethod.js @@ -0,0 +1,103 @@ +// TODO refactor this +import { domPrefixes as prefixes, styleElement, prefixCamelCase as prefixName } from './shared.js'; + +import supportsInterface from './interface.js'; + +function getInterfaceFromRules(rules, interfaceName) { + for (let i = 0; i < rules.length; i++) { + if (rules[i].constructor.name === interfaceName) { + return rules[i]; + } + + if (rules[i].cssRules) { + let ret = getInterfaceFromRules(rules[i].cssRules, interfaceName); + if (ret) { + return ret; + } + } + } + + return null; +} + +let instances = {}; + +export function isSupported (interfaceNameOrProto, name) { + let prototype = typeof interfaceNameOrProto === 'string' ? window[interfaceNameOrProto]?.prototype : interfaceNameOrProto; + + if (!prototype) { + return false; + } + + if (name in prototype) { + return true; + } + + return isSupported(Object.getPrototypeOf(prototype), name); +} + +export function getInstance (name, testCss, fn) { + let instance = instances[name]; + + if (instance !== undefined) { + return instance; + } + + if (testCss) { + styleElement.textContent = testCss; + } + + try { + if (fn) { + instance = fn(styleElement); + } + else if (testCss) { + instance = getInterfaceFromRules(styleElement.sheet.cssRules, name); + } + } + catch (e) { + return null; + } + + if (instance) { + instances[name] = instance; + return instance; + } +} + +export default function attributeOrMethod (interfaceName, name, testCss, interfaceCallback) { + let interfaceSupported = supportsInterface(interfaceName); + + if (!interfaceSupported.success) { + return {success: false}; + } + + + + + let prefixedInterfaceName = interfaceSupported.name; + let prefix = prefixes.find(prefix => isSupported(prefixedInterfaceName, prefixName(prefix, name))); + + if (prefix !== undefined) { + return {success: true, prefix}; + } + + // Not found in prototype, try to get an instance (slow) + if (interfaceName.startsWith('CSSKeyframe')) { + console.log(interfaceName, name, interfaceSupported); + // debugger; + } + let instance = getInstance(prefixedInterfaceName, testCss, interfaceCallback); + + if (!instance) { + return {success: false}; + } + + prefix = prefixes.find(prefix => isSupported(instance, prefixName(prefix, name))); + let success = prefix !== undefined; + + return { + success, + prefix, + }; +} diff --git a/src/supports/declaration.js b/src/supports/declaration.js new file mode 100644 index 00000000..9831ab37 --- /dev/null +++ b/src/supports/declaration.js @@ -0,0 +1,11 @@ +import supportsValue from './value.js'; +import supportsVariable from './variable.js'; + +export default function declaration (intruction) { + var val = intruction.match(/\s*([^:]+)\s*:\s*(.+)\s*/); + return { + success: !val[1].match(/--.*/) + ? supportsValue(val[1], val[2]).success + : supportsVariable(val[1], val[2]).success, + }; +} diff --git a/src/supports/descriptorvalue.js b/src/supports/descriptorvalue.js new file mode 100644 index 00000000..89afffe8 --- /dev/null +++ b/src/supports/descriptorvalue.js @@ -0,0 +1,40 @@ +import { styleElement } from './shared.js'; +import { camelCase } from './util.js'; + +export default function descriptorValue (descriptor, value, required) { + /* doesn't handle prefixes for descriptor or value */ + var add = '', + pos = 0; + if (descriptor.match(/@.*\//)) { + var part = descriptor.split('/'); + var rule = part[0]; + descriptor = part[1]; + + if (required) { + if (required.rule) { + rule = required.rule + ' ' + rule; + pos = 1; + } + if (required.descriptor) { + add = required.descriptor + '; '; + } + } + } else { + var rule = '@font-face'; + } + + styleElement.textContent = rule + ' {' + add + descriptor + ':' + value + '}'; + try { + if (styleElement.sheet.cssRules.length) { + return { + success: + (styleElement.sheet.cssRules[pos].style && styleElement.sheet.cssRules[pos].style.length >= 1) || + styleElement.sheet.cssRules[pos][camelCase(descriptor)] !== undefined, + }; + } else { + return { success: false }; + } + } catch (e) { + return { success: false }; + } +} diff --git a/src/supports/interface.js b/src/supports/interface.js new file mode 100644 index 00000000..0d2d5467 --- /dev/null +++ b/src/supports/interface.js @@ -0,0 +1,46 @@ +import { domPrefixes as prefixes, prefixCamelCase as prefixName } from './shared.js'; + +let cached = {}; + +/** + * Low-level, no caching, no prefixes + * @param {string} name + */ +export function isSupported (name) { + return name in window; +} + +export default function (name) { + let cachedResult = cached[name]; + let success, prefix, prefixedName; + + if (cachedResult === undefined) { + prefix = prefixes.find(prefix => isSupported(prefixName(prefix, name))); + + if (prefix === undefined && name.indexOf('CSS') === 0) { + // Last ditch effort to find a prefix: try CSS[Prefix]Name + let nameWithoutCSS = name.slice(3); + prefix = prefixes.find(prefix => isSupported('CSS' + prefixName(prefix, nameWithoutCSS))); + + if (prefix !== undefined) { + prefixedName = 'CSS' + prefixName(prefix, nameWithoutCSS); + } + } + + prefixedName ??= prefixName(prefix, name) + cached[name] = success = prefix !== undefined; + + if (success && prefix) { + cached[name] = prefixedName; + } + } + else { + success = Boolean(cachedResult); + prefixedName = cachedResult === true ? name : (cachedResult || undefined); + } + + return { + success, + name: prefixedName, + }; +} diff --git a/src/supports/mq.js b/src/supports/mq.js new file mode 100644 index 00000000..b39d44d3 --- /dev/null +++ b/src/supports/mq.js @@ -0,0 +1,8 @@ +export default function mq (mq) { + return { + // We check whether the query does not include 'not all' because + // if it does, that means the query is ignored. + // See https://drafts.csswg.org/cssom/#parse-a-media-query-list + success: !matchMedia(mq).media.includes('not all'), + }; +} diff --git a/src/supports/property.js b/src/supports/property.js new file mode 100644 index 00000000..0e4fd942 --- /dev/null +++ b/src/supports/property.js @@ -0,0 +1,50 @@ +import { prefixes, inlineStyle } from './shared.js'; +import { camelCase } from './util.js'; + +let cached = {}; + +/** + * Low level support check, no caching, no prefixes + * @param {*} property + * @returns + */ +export function isSupported (property, value) { + if (globalThis.CSS && CSS.supports) { + return CSS.supports(property, value ?? 'initial'); + } + + // No CSS, fall back to the DOM + if (value === undefined || value === '') { + // No value, check if the property is present + return inlineStyle[property] !== undefined; + } + + // Set and check if it takes + inlineStyle.setProperty(property, ''); + inlineStyle.setProperty(property, value); + let result = inlineStyle.getPropertyValue(property) !== value; + inlineStyle.setProperty(property, ''); + return result; +} + +export default function (name) { + let cachedResult = cached[name]; + let success, prefix; + + if (cachedResult === undefined) { + prefix = prefixes.find(prefix => isSupported(prefix + name)); + success = prefix !== undefined; + cached[name] = prefix === '' ? true : (prefix ?? false); + } + else { + success = Boolean(cachedResult); + prefix = typeof cachedResult === "boolean" ? '' : cachedResult; + } + + return { + success, + property: prefix + name, + prefix, + }; +} + diff --git a/src/supports/selector.js b/src/supports/selector.js new file mode 100644 index 00000000..e5dbe070 --- /dev/null +++ b/src/supports/selector.js @@ -0,0 +1,26 @@ +import { prefixes } from './shared.js'; + +let cached = {}; + +export default function selector (selector) { + if (cached[selector]) { + return { + success: cached[selector], + }; + } + + for (let i = 0; i < prefixes.length; i++) { + let prefixed = selector.replace(/^(:+)/, '$1' + prefixes[i]); + + if (CSS.supports('selector(' + prefixed + ')')) { + cached[selector] = true; + return { + success: true, + prefix: prefixes[i], + }; + } + } + + cached[selector] = false; + return { success: false }; +} diff --git a/src/supports/shared.js b/src/supports/shared.js new file mode 100644 index 00000000..0cf73f82 --- /dev/null +++ b/src/supports/shared.js @@ -0,0 +1,22 @@ +export const prefixes = ['', '-moz-', '-webkit-', '-ms-', 'ms-', '-o-', '-khtml-']; +export const domPrefixes = ['', 'Webkit', 'WebKit', 'webkit', 'Moz', 'moz', 'ms', 'Ms']; + +const dummy = document.createElement('_'); +const inlineStyle = dummy.style; +const styleElement = document.createElement('style'); + +document.documentElement.appendChild(styleElement); +dummy.setAttribute('data-foo', 'bar'); +dummy.setAttribute('data-px', '1px'); +document.documentElement.appendChild(dummy); + +export {dummy, inlineStyle, styleElement} + +export function prefixCamelCase (prefix, name) { + if (!prefix) { + return name; + } + + let capitalizedName = name.charAt(0).toUpperCase() + name.slice(1); + return prefix + capitalizedName; +} diff --git a/src/supports/util.js b/src/supports/util.js new file mode 100644 index 00000000..f67af4b7 --- /dev/null +++ b/src/supports/util.js @@ -0,0 +1,7 @@ +export function camelCase(str) { + return str + .replace(/-([a-z])/g, function ($0, $1) { + return $1.toUpperCase(); + }) + .replace('-', ''); +} diff --git a/src/supports/value.js b/src/supports/value.js new file mode 100644 index 00000000..fb893f4a --- /dev/null +++ b/src/supports/value.js @@ -0,0 +1,24 @@ +import { prefixes } from './shared.js'; + +import supportsProperty, { isSupported } from './property.js'; + +export default function value (value, property = 'all') { + // First, check if the *property* is supported + property = supportsProperty(property); + + if (!property.success) { + return property; + } + + const propertyPrefix = property.prefix; + const prefixedProperty = propertyPrefix + property.property; + + const prefix = prefixes.find(prefix => isSupported(prefixedProperty, prefix + value)); + const success = prefix !== undefined; + + return { + success, + prefix, + propertyPrefix, + }; +} diff --git a/src/supports/variable.js b/src/supports/variable.js new file mode 100644 index 00000000..62659842 --- /dev/null +++ b/src/supports/variable.js @@ -0,0 +1,11 @@ +import { dummy, inlineStyle } from './shared.js'; + +export default function variable (name, value) { + inlineStyle.setProperty(name, value); + inlineStyle.setProperty('margin-right', 'var(' + name + ')'); + let styles = window.getComputedStyle(dummy); + + return { + success: styles.marginRight === value, + }; +} diff --git a/src/tests.js b/src/tests.js new file mode 100644 index 00000000..e5d97e3d --- /dev/null +++ b/src/tests.js @@ -0,0 +1,312 @@ +import compat from '../tests/compat.js'; +import cssAlign3 from '../tests/css-align-3.js'; +import cssAnchorPosition1 from '../tests/css-anchor-position-1.js'; +import cssAnimationWorklet1 from '../tests/css-animation-worklet-1.js'; +import cssAnimations1 from '../tests/css-animations-1.js'; +import cssAnimations2 from '../tests/css-animations-2.js'; +import cssBackgrounds3 from '../tests/css-backgrounds-3.js'; +import cssBackgrounds4 from '../tests/css-backgrounds-4.js'; +import cssBorders4 from '../tests/css-borders-4.js'; +import cssBox3 from '../tests/css-box-3.js'; +import cssBox4 from '../tests/css-box-4.js'; +import cssBreak3 from '../tests/css-break-3.js'; +import cssBreak4 from '../tests/css-break-4.js'; +import cssCascade3 from '../tests/css-cascade-3.js'; +import cssCascade4 from '../tests/css-cascade-4.js'; +import cssCascade5 from '../tests/css-cascade-5.js'; +import cssCascade6 from '../tests/css-cascade-6.js'; +import cssColor3 from '../tests/css-color-3.js'; +import cssColor4 from '../tests/css-color-4.js'; +import cssColor5 from '../tests/css-color-5.js'; +import cssColorAdjust1 from '../tests/css-color-adjust-1.js'; +import cssColorHdr1 from '../tests/css-color-hdr-1.js'; +import cssComposition1 from '../tests/css-composition-1.js'; +import cssComposition2 from '../tests/css-composition-2.js'; +import cssConditional3 from '../tests/css-conditional-3.js'; +import cssConditional4 from '../tests/css-conditional-4.js'; +import cssConditional5 from '../tests/css-conditional-5.js'; +import cssContainment1 from '../tests/css-containment-1.js'; +import cssContainment2 from '../tests/css-containment-2.js'; +import cssContainment3 from '../tests/css-containment-3.js'; +import cssContent3 from '../tests/css-content-3.js'; +import cssCounterStyles3 from '../tests/css-counter-styles-3.js'; +import cssDisplay3 from '../tests/css-display-3.js'; +import cssDisplay4 from '../tests/css-display-4.js'; +import cssEasing1 from '../tests/css-easing-1.js'; +import cssEasing2 from '../tests/css-easing-2.js'; +import cssEnv1 from '../tests/css-env-1.js'; +import cssExclusions1 from '../tests/css-exclusions-1.js'; +import cssFlexbox1 from '../tests/css-flexbox-1.js'; +import cssFontLoading3 from '../tests/css-font-loading-3.js'; +import cssFonts3 from '../tests/css-fonts-3.js'; +import cssFonts4 from '../tests/css-fonts-4.js'; +import cssFonts5 from '../tests/css-fonts-5.js'; +import cssForms1 from '../tests/css-forms-1.js'; +import cssGaps1 from '../tests/css-gaps-1.js'; +import cssGrid1 from '../tests/css-grid-1.js'; +import cssGrid2 from '../tests/css-grid-2.js'; +import cssGrid3 from '../tests/css-grid-3.js'; +import cssHighlightApi1 from '../tests/css-highlight-api-1.js'; +import cssImages3 from '../tests/css-images-3.js'; +import cssImages4 from '../tests/css-images-4.js'; +import cssImages5 from '../tests/css-images-5.js'; +import cssInline3 from '../tests/css-inline-3.js'; +import cssLayoutApi1 from '../tests/css-layout-api-1.js'; +import cssLineGrid1 from '../tests/css-line-grid-1.js'; +import cssLinkParams1 from '../tests/css-link-params-1.js'; +import cssLists3 from '../tests/css-lists-3.js'; +import cssLogical1 from '../tests/css-logical-1.js'; +import cssMasking1 from '../tests/css-masking-1.js'; +import cssMixins1 from '../tests/css-mixins-1.js'; +import cssMulticol1 from '../tests/css-multicol-1.js'; +import cssMulticol2 from '../tests/css-multicol-2.js'; +import cssNamespaces3 from '../tests/css-namespaces-3.js'; +import cssNav1 from '../tests/css-nav-1.js'; +import cssNesting1 from '../tests/css-nesting-1.js'; +import cssOverflow3 from '../tests/css-overflow-3.js'; +import cssOverflow4 from '../tests/css-overflow-4.js'; +import cssOverflow5 from '../tests/css-overflow-5.js'; +import cssOverscroll1 from '../tests/css-overscroll-1.js'; +import cssPage3 from '../tests/css-page-3.js'; +import cssPaintApi1 from '../tests/css-paint-api-1.js'; +import cssPosition3 from '../tests/css-position-3.js'; +import cssPropertiesValuesApi1 from '../tests/css-properties-values-api-1.js'; +import cssPseudo4 from '../tests/css-pseudo-4.js'; +import cssRegions1 from '../tests/css-regions-1.js'; +import cssRhythmic from '../tests/css-rhythmic-1.js'; +import cssRuby1 from '../tests/css-ruby-1.js'; +import cssScoping1 from '../tests/css-scoping-1.js'; +import cssScrollAnchoring1 from '../tests/css-scroll-anchoring-1.js'; +import cssScrollSnap1 from '../tests/css-scroll-snap-1.js'; +import cssScrollSnap2 from '../tests/css-scroll-snap-2.js'; +import cssScrollbars1 from '../tests/css-scrollbars-1.js'; +import cssShadowParts1 from '../tests/css-shadow-parts-1.js'; +import cssShapes1 from '../tests/css-shapes-1.js'; +import cssShapes2 from '../tests/css-shapes-2.js'; +import cssSizeAdjust1 from '../tests/css-size-adjust-1.js'; +import cssSizing3 from '../tests/css-sizing-3.js'; +import cssSizing4 from '../tests/css-sizing-4.js'; +import cssSpeech1 from '../tests/css-speech-1.js'; +import cssText3 from '../tests/css-text-3.js'; +import cssText4 from '../tests/css-text-4.js'; +import cssTextDecor3 from '../tests/css-text-decor-3.js'; +import cssTextDecor4 from '../tests/css-text-decor-4.js'; +import cssTransforms1 from '../tests/css-transforms-1.js'; +import cssTransforms2 from '../tests/css-transforms-2.js'; +import cssTransitions1 from '../tests/css-transitions-1.js'; +import cssTransitions2 from '../tests/css-transitions-2.js'; +import cssTypedOm1 from '../tests/css-typed-om-1.js'; +import cssUi3 from '../tests/css-ui-3.js'; +import cssUi4 from '../tests/css-ui-4.js'; +import cssValues3 from '../tests/css-values-3.js'; +import cssValues4 from '../tests/css-values-4.js'; +import cssValues5 from '../tests/css-values-5.js'; +import cssVariables1 from '../tests/css-variables-1.js'; +import cssVariables2 from '../tests/css-variables-2.js'; +import cssViewTransitions1 from '../tests/css-view-transitions-1.js'; +import cssViewTransitions2 from '../tests/css-view-transitions-2.js'; +import cssViewport1 from '../tests/css-viewport-1.js'; +import cssWillChange1 from '../tests/css-will-change-1.js'; +import cssWritingModes3 from '../tests/css-writing-modes-3.js'; +import cssWritingModes4 from '../tests/css-writing-modes-4.js'; +import css2Cascade from '../tests/css2-cascade.js'; +import css2Colors from '../tests/css2-colors.js'; +import css2Fonts from '../tests/css2-fonts.js'; +import css2Generate from '../tests/css2-generate.js'; +import css2Media from '../tests/css2-media.js'; +import css2Page from '../tests/css2-page.js'; +import css2Selectors from '../tests/css2-selectors.js'; +import css2Tables from '../tests/css2-tables.js'; +import css2Text from '../tests/css2-text.js'; +import css2Ui from '../tests/css2-ui.js'; +import css2VisuDet from '../tests/css2-visudet.js'; +import css2VisuFx from '../tests/css2-visufx.js'; +import css2VisuRen from '../tests/css2-visuren.js'; +import cssom1 from '../tests/cssom-1.js'; +import cssomView1 from '../tests/cssom-view-1.js'; +import fillStroke3 from '../tests/fill-stroke-3.js'; +import filterEffects1 from '../tests/filter-effects-1.js'; +import filterEffects2 from '../tests/filter-effects-2.js'; +import fullscreen from '../tests/fullscreen.js'; +import html from '../tests/html.js'; +import mathmlCore from '../tests/mathml-core.js'; +import mediaQueries3 from '../tests/mediaqueries-3.js'; +import mediaQueries4 from '../tests/mediaqueries-4.js'; +import mediaQueries5 from '../tests/mediaqueries-5.js'; +import motion1 from '../tests/motion-1.js'; +import pointerEvents1 from '../tests/pointerevents1.js'; +import pointerEvents3 from '../tests/pointerevents3.js'; +import resizeObserver1 from '../tests/resize-observer-1.js'; +import scrollAnimations1 from '../tests/scroll-animations-1.js'; +import selectors3 from '../tests/selectors-3.js'; +import selectors4 from '../tests/selectors-4.js'; +import selectors5 from '../tests/selectors-5.js'; +import svg2Coords from '../tests/svg2-coords.js'; +import svg2Geometry from '../tests/svg2-geometry.js'; +import svg2Interact from '../tests/svg2-interact.js'; +import svg2Painting from '../tests/svg2-painting.js'; +import svg2Paths from '../tests/svg2-paths.js'; +import svg2PServers from '../tests/svg2-pservers.js'; +import svg2Text from '../tests/svg2-text.js'; +import webAnimations1 from '../tests/web-animations-1.js'; +import webAnimations2 from '../tests/web-animations-2.js'; +import webVtt from '../tests/webvtt.js'; +import webxrDomOverlays1 from '../tests/webxr-dom-overlays-1.js'; + + +export default { + compat: compat, + 'css2-cascade': css2Cascade, + 'css2-colors': css2Colors, + 'css2-fonts': css2Fonts, + 'css2-generate': css2Generate, + 'css2-media': css2Media, + 'css2-page': css2Page, + 'css2-selectors': css2Selectors, + 'css2-tables': css2Tables, + 'css2-text': css2Text, + 'css2-ui': css2Ui, + 'css2-visudet': css2VisuDet, + 'css2-visufx': css2VisuFx, + 'css2-visuren': css2VisuRen, + 'css-align-3': cssAlign3, + 'css-anchor-position-1': cssAnchorPosition1, + 'css-animation-1': cssAnimations1, + 'css-animation-2': cssAnimations2, + 'css-animation-worklet-1': cssAnimationWorklet1, + 'css-backgrounds-3': cssBackgrounds3, + 'css-backgrounds-4': cssBackgrounds4, + 'css-borders-4': cssBorders4, + 'css-box-3': cssBox3, + 'css-box-4': cssBox4, + 'css-break-3': cssBreak3, + 'css-break-4': cssBreak4, + 'css-cascade-3': cssCascade3, + 'css-cascade-4': cssCascade4, + 'css-cascade-5': cssCascade5, + 'css-cascade-6': cssCascade6, + 'css-colors-3': cssColor3, + 'css-colors-4': cssColor4, + 'css-colors-5': cssColor5, + 'css-colors-adjust-1': cssColorAdjust1, + 'css-colors-hdr-1': cssColorHdr1, + 'css-composition-1': cssComposition1, + 'css-composition-2': cssComposition2, + 'css-conditional-3': cssConditional3, + 'css-conditional-4': cssConditional4, + 'css-conditional-5': cssConditional5, + 'css-containment-1': cssContainment1, + 'css-containment-2': cssContainment2, + 'css-containment-3': cssContainment3, + 'css-content-3': cssContent3, + 'css-counter-styles-3': cssCounterStyles3, + 'css-display-3': cssDisplay3, + 'css-display-4': cssDisplay4, + 'css-easing-1': cssEasing1, + 'css-easing-2': cssEasing2, + 'css-env-1': cssEnv1, + 'css-exclusions-1': cssExclusions1, + 'css-flexbox-1': cssFlexbox1, + 'css-font-loading-3': cssFontLoading3, + 'css-fonts-3': cssFonts3, + 'css-fonts-4': cssFonts4, + 'css-fonts-5': cssFonts5, + 'css-forms-1': cssForms1, + 'css-gaps-1': cssGaps1, + 'css-grid-1': cssGrid1, + 'css-grid-2': cssGrid2, + 'css-grid-3': cssGrid3, + 'css-highlight-api-1': cssHighlightApi1, + 'css-images-3': cssImages3, + 'css-images-4': cssImages4, + 'css-images-5': cssImages5, + 'css-inline-3': cssInline3, + 'css-layout-api-1': cssLayoutApi1, + 'css-line-grid-1': cssLineGrid1, + 'css-link-params-1': cssLinkParams1, + 'css-lists-3': cssLists3, + 'css-logical-1': cssLogical1, + 'css-masking-1': cssMasking1, + 'css-mixins-1': cssMixins1, + 'css-multicol-1': cssMulticol1, + 'css-multicol-2': cssMulticol2, + 'css-namespaces-3': cssNamespaces3, + 'css-nav-1': cssNav1, + 'css-nesting-1': cssNesting1, + 'css-overflow-3': cssOverflow3, + 'css-overflow-4': cssOverflow4, + 'css-overflow-5': cssOverflow5, + 'css-overscroll-1': cssOverscroll1, + 'css-page-3': cssPage3, + 'css-paint-api-1': cssPaintApi1, + 'css-position-3': cssPosition3, + 'css-properties-values-api-1': cssPropertiesValuesApi1, + 'css-pseudo-4': cssPseudo4, + 'css-regions-1': cssRegions1, + 'css-rhythmic': cssRhythmic, + 'css-ruby-1': cssRuby1, + 'css-scoping-1': cssScoping1, + 'css-scroll-anchoring-1': cssScrollAnchoring1, + 'css-scroll-snap-1': cssScrollSnap1, + 'css-scroll-snap-2': cssScrollSnap2, + 'css-scrollbars-1': cssScrollbars1, + 'css-shadow-parts-1': cssShadowParts1, + 'css-shapes-1': cssShapes1, + 'css-shapes-2': cssShapes2, + 'css-size-adjust-1': cssSizeAdjust1, + 'css-sizing-3': cssSizing3, + 'css-sizing-4': cssSizing4, + 'css-speech-1': cssSpeech1, + 'css-text-3': cssText3, + 'css-text-4': cssText4, + 'css-text-decor-3': cssTextDecor3, + 'css-text-decor-4': cssTextDecor4, + 'css-transforms-1': cssTransforms1, + 'css-transforms-2': cssTransforms2, + 'css-transitions-1': cssTransitions1, + 'css-transitions-2': cssTransitions2, + 'css-typed-om-1': cssTypedOm1, + 'css-ui-3': cssUi3, + 'css-ui-4': cssUi4, + 'css-values-3': cssValues3, + 'css-values-4': cssValues4, + 'css-values-5': cssValues5, + 'css-variables-1': cssVariables1, + 'css-variables-2': cssVariables2, + 'css-view-transitions-1': cssViewTransitions1, + 'css-view-transitions-2': cssViewTransitions2, + 'css-viewport-1': cssViewport1, + 'css-will-change-1': cssWillChange1, + 'css-writing-modes-3': cssWritingModes3, + 'css-writing-modes-4': cssWritingModes4, + 'cssom-1': cssom1, + 'cssom-view-1': cssomView1, + 'fill-stroke-3': fillStroke3, + 'filter-effects-1': filterEffects1, + 'filter-effects-2': filterEffects2, + fullscreen: fullscreen, + html: html, + 'mathml-core': mathmlCore, + 'mediaqueries-3': mediaQueries3, + 'mediaqueries-4': mediaQueries4, + 'mediaqueries-5': mediaQueries5, + 'motion-1': motion1, + 'pointerevents-1': pointerEvents1, + 'pointerevents-3': pointerEvents3, + 'resize-observer-1': resizeObserver1, + 'scroll-animations-1': scrollAnimations1, + 'selectors-3': selectors3, + 'selectors-4': selectors4, + 'selectors-5': selectors5, + 'svg2-coords': svg2Coords, + 'svg2-geometry': svg2Geometry, + 'svg2-interact': svg2Interact, + 'svg2-painting': svg2Painting, + 'svg2-paths': svg2Paths, + 'svg2-pServers': svg2PServers, + 'svg2-text': svg2Text, + 'web-animations-1': webAnimations1, + 'web-animations-2': webAnimations2, + webvtt: webVtt, + 'webxr-dom-overlays-1': webxrDomOverlays1, +}; diff --git a/src/util.js b/src/util.js new file mode 100644 index 00000000..19aa85fd --- /dev/null +++ b/src/util.js @@ -0,0 +1,40 @@ +export const IS_DEV = location.hostname === 'localhost' && !location.search.includes('prod'); +const classes = ['epic-fail', 'fail', 'very-buggy', 'buggy', 'slightly-buggy', 'almost-pass', 'pass']; + +export function passclass(info) { + if (info === undefined || info === null) { + return ''; + } + + let success; + + if (typeof info === 'boolean') { + success = +info; + } + else if (typeof info === 'number') { + success = info; + } + else if (typeof info === 'object' && 'passed' in info) { + success = info.passed / info.total; + } + + if (success >= 1) { + return classes.at(-1); + } + + let index = Math.round(success * (classes.length - 2)); + return classes[index]; +} + +export function round(value, maxDecimals = 0) { + return Math.round(value * 10 ** maxDecimals) / 10 ** maxDecimals; +} + +export function percent(value, maxDecimals = 0) { + value = +value; + return value.toLocaleString("en-US", { + style: "percent", + minimumFractionDigits: 0, + maximumFractionDigits: maxDecimals, + }); +} diff --git a/src/vue/components/carbon-ads.js b/src/vue/components/carbon-ads.js new file mode 100644 index 00000000..215df099 --- /dev/null +++ b/src/vue/components/carbon-ads.js @@ -0,0 +1,24 @@ +export default { + props: { + params: { + type: String, + required: true, + }, + }, + + template: `
`, + + mounted() { + if (this.initialized) { + return; + } + + this.initialized = true; + + let script = document.createElement('script'); + script.async = true; + script.id = '_carbonads_js'; + script.src = `https://cdn.carbonads.com/carbon.js?${this.params}`; + this.$refs.host.appendChild(script); + } +}; diff --git a/src/vue/components/feature/feature.js b/src/vue/components/feature/feature.js new file mode 100644 index 00000000..0570a288 --- /dev/null +++ b/src/vue/components/feature/feature.js @@ -0,0 +1,23 @@ +import { IS_DEV, passclass } from '../../../util.js'; + +export default { + props: { + feature: { + type: Object, + required: true, + }, + }, + + mounted () { + if (IS_DEV) { + // Expose feature object on container for debugging + this.$refs.container.feature = this.feature; + } + }, + + template: "#feature-component-template", + + methods: { + passclass, + }, +}; diff --git a/src/vue/components/support-status/support-status.css b/src/vue/components/support-status/support-status.css new file mode 100644 index 00000000..dd99e944 --- /dev/null +++ b/src/vue/components/support-status/support-status.css @@ -0,0 +1,43 @@ +.progress { + --_stroke-width: var(--stroke-width, 16); + --_faded-opacity: var(--faded-opacity, 0.2); + --_progress-color: var(--progress-color, var(--color)); + --_r: calc(50 - var(--_stroke-width) / 2); + --_perimeter: calc(2 * 3.1415926 * var(--_r)); + + width: 1lh; + height: 1lh; + margin: -0.1lh; + fill: none; + stroke-linecap: round; + stroke-linejoin: round; + flex-shrink: 0; + + .check { + stroke: var(--_progress-color); + stroke-width: 10; + } + + .x { + color: var(--_progress-color); + } + + circle { + fill: none; + stroke: var(--_progress-color); + stroke-width: var(--_stroke-width); + r: calc(var(--_r) * 1px); + stroke-dasharray: calc(var(--_perimeter) * 1px); + transform-origin: center; + transform-box: fill-box; + transform: rotate(-90deg); + } + + circle:not(.partial) { + stroke-opacity: var(--_faded-opacity); + } + + circle.partial { + stroke-dashoffset: calc(var(--_perimeter) * 1px * (1 - var(--progress, 0)/100)); + } +} diff --git a/src/vue/components/support-status/support-status.js b/src/vue/components/support-status/support-status.js new file mode 100644 index 00000000..b6c67811 --- /dev/null +++ b/src/vue/components/support-status/support-status.js @@ -0,0 +1,65 @@ +import { round, passclass } from '../../../util.js'; + +let css = fetch(new URL('./support-status.css', import.meta.url)).then(res => res.text()); + +const template = ` + + {{ tooltip }} + + + + + + +`; + +export default { + props: { + score: { + type: Object, + required: true + } + }, + + template, + + computed: { + tooltip () { + return `Scored ${this.score} by passing ${round(this.score.passed)} / ${round(this.score.total)} tests in ${round(this.score.testTime, 2)} ms` + } + }, + + methods: { + round, + passclass, + + async getSvg() { + let root = this.$refs.el; + if (!root) { + return ''; + } + + let clone = root.cloneNode(true); + + // Hardcode styles applied externally + let cs = getComputedStyle(root); + clone.style.setProperty('--color', cs.getPropertyValue('--color')); + clone.style.setProperty('--stroke-width', cs.getPropertyValue('--_stroke-width')); + + // Workaround to avoid clipping + // TODO debug why this is needed and why the stroke is not applied properly + let viewBox = clone.viewBox.baseVal; + viewBox.x = viewBox.y = -6; + viewBox.width = viewBox.height = 105; + + + clone.insertAdjacentHTML('afterbegin', ``) + + return clone.outerHTML; + }, + + async getDataUrl() { + return 'data:image/svg+xml,' + encodeURIComponent(await this.getSvg()); + } + } +} diff --git a/src/vue/directives/content.js b/src/vue/directives/content.js new file mode 100644 index 00000000..29784edb --- /dev/null +++ b/src/vue/directives/content.js @@ -0,0 +1,22 @@ +// Like v-text, but doesn't complain if the element has content, +// making it possible to use in a PE fashion, with the contents being the fallback +export default function content(el, { value, arg }) { + if (!el.dataset.fallback) { + // Store the original content as a fallback the first time + el.dataset.fallback = el.textContent; + } + + if (value === '') { + value = el.dataset.fallback; + } else { + if (arg === 'number') { + value = Number(value).toLocaleString(undefined, { maximumSignificantDigits: 2 }); + } + } + + // if (arg === 'html') { + // el.innerHTML = value; + // } else { + el.textContent = value; + // } +} diff --git a/features/README.md b/tests/README.md similarity index 83% rename from features/README.md rename to tests/README.md index 5ed7c007..e6abb267 100644 --- a/features/README.md +++ b/tests/README.md @@ -1,6 +1,6 @@ # CSS Test Data Schema -This document describes the data schema used in the CSS test files located in the `tests/` directory. Each test file exports a JavaScript object that defines tests for various CSS features, properties, values, and globals. +This document describes the data schema used in the CSS test files located in the `tests/` directory. Each test file exports a JavaScript object that defines tests for various CSS features, properties, values, and interfaces. ## Schema Summary @@ -40,6 +40,14 @@ This document describes the data schema used in the CSS test files located in th } }, + // Tests for CSS declarations (property-value pairs) + declaration?: { + "declaration-pattern": { + links: { /* documentation links */ }, + tests: string[] + } + }, + // Tests for CSS selectors selectors?: { "selector-name": { @@ -56,12 +64,13 @@ This document describes the data schema used in the CSS test files located in th } }, - // Tests for JavaScript/DOM globals - globals?: { + // Tests for JavaScript/DOM interfaces + interfaces?: { "InterfaceName": { links: { /* documentation links */ }, tests: string[], interface?: function, // Returns interface object to test + required?: string // CSS required for this test } } } @@ -122,7 +131,22 @@ values: { -### 3. [Selectors Tests](#selectors-tests) +### 3. [Declaration Tests](#declaration-tests) + +Tests for CSS declarations (property-value pairs). + +```javascript +declaration: { + 'declaration-pattern': { + links: { /* documentation links */ }, + tests: ['property: value1', 'property: value2'] + } +} +``` + + + +### 4. [Selectors Tests](#selectors-tests) Tests for CSS selectors. @@ -137,12 +161,12 @@ selectors: { -### 4. [At-Rules Tests](#at-rules-tests) +### 5. [At-Rules Tests](#at-rules-tests) Tests for CSS at-rules like `@media`, `@keyframes`, etc. ```javascript -atrules: { +'@rules': { '@rule-name': { links: { /* documentation links */ }, tests: ['@rule syntax1', '@rule syntax2'] @@ -152,20 +176,22 @@ atrules: { -### 5. [Globals Tests](#globals-tests) +### 6. [Interfaces Tests](#interfaces-tests) -Tests for JavaScript/DOM globals related to CSS. +Tests for JavaScript/DOM interfaces related to CSS. ```javascript -globals: { +interfaces: { 'InterfaceName': { links: { /* documentation links */ }, tests: ['method1', 'method2', 'property1'], + interface: function() { /* returns interface object */ }, + required: 'CSS required for this test' // Optional } } ``` - + ## Test Value Types @@ -174,6 +200,7 @@ Most test values are strings representing CSS syntax: - **CSS properties**: `'grid'`, `'inline-grid'` - **CSS values**: `'100px'`, `'1fr'`, `'auto'` - **CSS functions**: `'calc(100px + 1fr)'`, `'rgb(255, 0, 0)'` +- **CSS declarations**: `'width: var(--foo)'`, `'--foo: 2px'` ### Array Values Some tests use arrays for complex values: @@ -188,6 +215,9 @@ For interface tests, the `interface` property contains a function that returns t ### Links Object Each test category can have its own `links` object with the same structure as the top-level links. +### Required Property +Some tests have a `required` property that specifies CSS code that must be present for the test to work properly. + ### Properties Array In `values` tests, the `properties` array specifies which CSS properties the values apply to. @@ -237,21 +267,25 @@ export default { } ``` -### Global Test +### Interface Test ```javascript export default { title: 'CSS Typed OM Level 1', status: 'experimental', links: { dev: 'css-typed-om-1', + devtype: 'houdini' }, - globals: { + interfaces: { CSSStyleValue: { links: { dev: '#stylevalue-objects', mdnGroup: 'DOM' }, tests: ['parse', 'parseAll'], + interface: function() { + return CSSStyleValue + } } } } diff --git a/tests/compat.js b/tests/compat.js new file mode 100644 index 00000000..78ece486 --- /dev/null +++ b/tests/compat.js @@ -0,0 +1,16 @@ +export default { + title: 'Compatibility', + group: 'whatwg', + links: { + dev: 'compat', + }, + status: 'stable', + properties: { + 'touch-action': { + links: { + dev: '#touch-action', + }, + tests: ['pinch-zoom', 'pan-x pinch-zoom', 'pan-y pinch-zoom', 'pan-x pan-y pinch-zoom'], + }, + }, +}; diff --git a/features/css-align-3.js b/tests/css-align-3.js similarity index 99% rename from features/css-align-3.js rename to tests/css-align-3.js index a7967f69..e9090058 100644 --- a/features/css-align-3.js +++ b/tests/css-align-3.js @@ -1,5 +1,4 @@ export default { - id: 'css-align-3', title: 'CSS Box Alignment Module Level 3', link: 'css-align-3', status: 'stable', diff --git a/features/css-anchor-position-1.js b/tests/css-anchor-position-1.js similarity index 77% rename from features/css-anchor-position-1.js rename to tests/css-anchor-position-1.js index 79df7f78..13e8017b 100644 --- a/features/css-anchor-position-1.js +++ b/tests/css-anchor-position-1.js @@ -1,5 +1,4 @@ export default { - id: 'css-anchor-position-1', title: 'CSS Anchor Positioning', link: 'css-anchor-position-1', status: 'experimental', @@ -19,38 +18,40 @@ export default { 'inset-block', 'inset-inline', ], - args: [ - 'inside', - 'outside', - 'inside, 20px', - 'outside, 30%', - '--anchor inside', - '--anchor outside', - '--anchor inside, 20px', - '--anchor outside, 30%', - 'top', - 'left', - 'right', - 'bottom', - 'start', - 'end', - 'self-start', - 'self-end', - 'center', - '30%', - 'top, 20px', - 'start, 30%', - '30%, 20px', - '--anchor top', - '--anchor top, 20px', - '--anchor top, 5%', - 'top', - 'top, 20px', - 'top, 5%', + tests: [ + 'anchor(inside)', + 'anchor(outside)', + 'anchor(inside, 20px)', + 'anchor(outside, 30%)', + 'anchor(--anchor inside)', + 'anchor(--anchor outside)', + 'anchor(--anchor inside, 20px)', + 'anchor(--anchor outside, 30%)', + 'anchor(top)', + 'anchor(left)', + 'anchor(right)', + 'anchor(bottom)', + 'anchor(start)', + 'anchor(end)', + 'anchor(self-start)', + 'anchor(self-end)', + 'anchor(center)', + 'anchor(30%)', + 'anchor(top, 20px)', + 'anchor(start, 30%)', + 'anchor(30%, 20px)', + 'anchor(--anchor top)', + 'anchor(--anchor top, 20px)', + 'anchor(--anchor top, 5%)', + 'anchor(top)', + 'anchor(top, 20px)', + 'anchor(top, 5%)', ], }, 'anchor-center': { - link: '#anchor-center', + links: { + dev: '#anchor-center', + }, properties: [ 'justify-self', 'align-self', @@ -100,26 +101,26 @@ export default { 'margin-inline-start', 'margin-inline-end', ], - args: [ - 'width', - 'height', - 'block', - 'inline', - 'self-block', - 'self-inline', - 'width, 20px', - 'height, 5%', - '--anchor width', - '--anchor width, 20px', - '--anchor height, 5%', - '--anchor block', - '--anchor inline', - '--anchor block, 20px', - '--anchor inline, 5%', - '--anchor self-block', - '--anchor self-inline', - '--anchor self-block, 20px', - '--anchor self-inline, 5%', + tests: [ + 'anchor-size(width)', + 'anchor-size(height)', + 'anchor-size(block)', + 'anchor-size(inline)', + 'anchor-size(self-block)', + 'anchor-size(self-inline)', + 'anchor-size(width, 20px)', + 'anchor-size(height, 5%)', + 'anchor-size(--anchor width)', + 'anchor-size(--anchor width, 20px)', + 'anchor-size(--anchor height, 5%)', + 'anchor-size(--anchor block)', + 'anchor-size(--anchor inline)', + 'anchor-size(--anchor block, 20px)', + 'anchor-size(--anchor inline, 5%)', + 'anchor-size(--anchor self-block)', + 'anchor-size(--anchor self-inline)', + 'anchor-size(--anchor self-block, 20px)', + 'anchor-size(--anchor self-inline, 5%)', ], }, }, @@ -138,7 +139,9 @@ export default { ], }, 'position-area': { - link: '#position-area', + links: { + dev: '#position-area', + }, tests: [ 'left', 'center', @@ -394,17 +397,27 @@ export default { ], }, }, - atrules: { + '@rules': { '@position-try': { link: '#fallback-rule', - prelude: "--foo", - descriptors: ['top', 'left', 'bottom', 'right', 'position-area', 'margin'], + tests: [ + "@position-try --button-popover {\n top: anchor(--button bottom);\n left: anchor(--button left);\n}", + "@position-try --button-popover {\n bottom: anchor(--button top);\n right: anchor(--button right);\n margin: 1em;\n}", + "@position-try --position-try {\n position-area: top left;\n}", + ], }, }, - globals: { + interfaces: { CSSPositionTryRule: { - link: '#om-position-try', - mdnGroup: 'DOM', + links: { + dev: '#om-position-try', + mdnGroup: 'DOM', + }, + tests: ['name', 'style', 'cssText', 'parentRule', 'parentStyleSheet'], + required: '@position-try --button-popover {\n top: anchor(--button bottom);\n left: anchor(--button left);\n}', + interface: function(style) { + return style.sheet.cssRules[0].cssRules[0]; + } }, }, }; diff --git a/tests/css-animation-worklet-1.js b/tests/css-animation-worklet-1.js new file mode 100644 index 00000000..3dfb74a2 --- /dev/null +++ b/tests/css-animation-worklet-1.js @@ -0,0 +1,33 @@ +export default { + title: 'CSS Animation Worklet Level 1', + group: 'houdini', + link: 'css-animation-worklet-1', + status: 'experimental', + interfaces: { + CSS: { + link: '#animation-worklet-desc', + tests: ['animationWorklet'], + interface: function() { + return CSS; + }, + }, + Worklet: { + link: '#animation-worklet-desc', + tests: ['addModule'], + interface: function() { + return CSS.animationWorklet; + }, + }, + WorkletAnimation: { + link: '#worklet-animation-interface', + tests: ['animatorName'], + interface: function() { + return new WorkletAnimation('Animator', new KeyframeEffect( + document.body, + {transform: ['translateX(0)', 'translateX(50vw)']}, + {duration: 1000} + )); + }, + }, + }, +}; diff --git a/features/css-animations-1.js b/tests/css-animations-1.js similarity index 66% rename from features/css-animations-1.js rename to tests/css-animations-1.js index eeb28a24..51d637bc 100644 --- a/features/css-animations-1.js +++ b/tests/css-animations-1.js @@ -1,5 +1,4 @@ export default { - id: 'css-animations-1', title: 'CSS Animations Level 1', link: 'css-animations-1', status: 'stable', @@ -53,45 +52,55 @@ export default { tests: 'foo 1s 2s infinite linear alternate both', }, }, - atrules: { + '@rules': { '@keyframes': { link: '#keyframes', - prelude: 'foo', - // TODO from, to, + tests: [ + '@keyframes foo {\n from: {\n color: blue;\n }\n to: {\n color: red;\n }\n}', + '@keyframes foo {\n from: {\n color: blue;\n }\n 50%: {\n color: green;\n }\n to: {\n color: red;\n }\n}', + ], }, }, - globals: { + interfaces: { AnimationEvent: { link: '#interface-animationevent', mdnGroup: 'DOM', - extends: 'Event', - members: ['animationName', 'elapsedTime', 'pseudoElement'], + tests: ['animationName', 'elapsedTime', 'pseudoElement'], + interface: function() { + return new AnimationEvent('animationstart'); + } }, CSSRule: { link: '#interface-cssrule', mdnGroup: 'DOM', - properties: [ + tests: [ 'KEYFRAMES_RULE', 'KEYFRAME_RULE', ], + required: 'div { }', + interface: function(style) { + return style.sheet.cssRules[0]; + } }, CSSKeyframesRule: { link: '#interface-csskeyframesrule', mdnGroup: 'DOM', - extends: 'CSSRule', - members: ['name', 'cssRules', 'length'], - methods: ['appendRule', 'deleteRule', 'findRule'], + tests: ['name', 'cssRules', 'length', 'appendRule', 'deleteRule', 'findRule'], + required: '@keyframes foo { from {} to {} }', }, CSSKeyframeRule: { link: '#interface-csskeyframerule', mdnGroup: 'DOM', - extends: 'CSSRule', - members: ['keyText', 'style'], + tests: ['keyText', 'style'], + required: '@keyframes foo { from {} to {} }', }, - HTMLElement: { + Element: { link: '#interface-globaleventhandlers', mdnGroup: 'DOM', - members: ['onanimationstart', 'onanimationiteration', 'onanimationend', 'onanimationcancel'], + tests: ['onanimationstart', 'onanimationiteration', 'onanimationend', 'onanimationcancel'], + interface: function(style) { + return document.body; + }, } }, }; diff --git a/features/css-animations-2.js b/tests/css-animations-2.js similarity index 59% rename from features/css-animations-2.js rename to tests/css-animations-2.js index 521db4a9..881bee5d 100644 --- a/features/css-animations-2.js +++ b/tests/css-animations-2.js @@ -1,5 +1,4 @@ export default { - id: 'css-animations-2', title: 'CSS Animations Level 2', link: 'css-animations-2', status: 'experimental', @@ -29,11 +28,22 @@ export default { ], }, }, - globals: { + interfaces: { CSSAnimation: { link: '#the-CSSAnimation-interface', - mdnGroup: 'DOM', - members: ['animationName'], + links: { + mdnGroup: 'DOM', + }, + tests: ['animationName'], + required: '@keyframes slide-in { 0% { transform: translateY(-1000px); } 100% { transform: translateY(0); } } .animate { animation: slide-in 0.7s both; }', + interface: function(style) { + var div = document.createElement('div'); + div.className = 'animate'; + body.append(div); + var animations = div.getAnimations && div.getAnimations(); + div.remove(); + return animations.length > 0 && animations[0]; + } }, }, }; diff --git a/features/css-backgrounds-3.js b/tests/css-backgrounds-3.js similarity index 71% rename from features/css-backgrounds-3.js rename to tests/css-backgrounds-3.js index e40ba909..104def2a 100644 --- a/features/css-backgrounds-3.js +++ b/tests/css-backgrounds-3.js @@ -1,12 +1,14 @@ export default { - id: 'css-backgrounds-3', title: 'CSS Backgrounds and Borders Module Level 3', link: 'css-backgrounds-3', status: 'stable', firstSnapshot: 2015, properties: { 'background-repeat': { - link: '#background-repeat', + links: { + tr: '#the-background-repeat', + dev: '#background-repeat', + }, tests: [ 'space', 'round', @@ -29,27 +31,45 @@ export default { ], }, 'background-attachment': { - link: '#background-attachment', + links: { + tr: '#the-background-attachment', + dev: '#background-attachment', + }, tests: 'local', }, 'background-position': { - link: '#background-position', + links: { + tr: '#the-background-position', + dev: '#background-position', + }, tests: ['bottom 10px right 20px', 'bottom 10px right', 'top right 10px'], }, 'background-clip': { - link: '#background-clip', + links: { + tr: '#the-background-clip', + dev: '#background-clip', + }, tests: ['border-box', 'padding-box', 'content-box'], }, 'background-origin': { - link: '#background-origin', + links: { + tr: '#the-background-origin', + dev: '#background-origin', + }, tests: ['border-box', 'padding-box', 'content-box'], }, 'background-size': { - link: '#background-size', + links: { + tr: '#the-background-size', + dev: '#background-size', + }, tests: ['auto', 'cover', 'contain', '10px', '50%', '10px auto', 'auto 10%', '50em 50%'], }, background: { - link: '#background', + links: { + tr: '#the-background', + dev: '#background', + }, tests: [ 'url(foo.png), url(bar.svg)', 'top left / 50% 60%', @@ -59,32 +79,52 @@ export default { ], }, 'border-top-left-radius': { - link: '#border-radius', + links: { + tr: '#the-border-radius', + dev: '#border-radius', + }, tests: ['0', '50%', '250px 100px'], }, 'border-top-right-radius': { - link: '#border-radius', + links: { + tr: '#the-border-radius', + dev: '#border-radius', + }, tests: ['0', '50%', '250px 100px'], }, 'border-bottom-right-radius': { - link: '#border-radius', + links: { + tr: '#the-border-radius', + dev: '#border-radius', + }, tests: ['0', '50%', '250px 100px'], }, 'border-bottom-left-radius': { - link: '#border-radius', + links: { + tr: '#the-border-radius', + dev: '#border-radius', + }, tests: ['0', '50%', '250px 100px'], }, 'border-radius': { - link: '#border-radius', + links: { + tr: '#the-border-radius', + dev: '#border-radius', + }, tests: ['10px', '50%', '10px / 20px', '2px 4px 8px 16px', '2px 4px 8px 16px / 2px 4px 8px 16px'], }, 'border-image-source': { - link: '#border-image-source', - dataTypes: ['image'], + links: { + tr: '#the-border-image-source', + dev: '#border-image-source', + }, tests: ['none', 'url(foo.png)'], }, 'border-image-slice': { - link: '#border-image-slice', + links: { + tr: '#the-border-image-slice', + dev: '#border-image-slice', + }, tests: [ '10', '30%', @@ -125,7 +165,10 @@ export default { ], }, 'border-image-width': { - link: '#border-image-width', + links: { + tr: '#the-border-image-width', + dev: '#border-image-width', + }, tests: [ '10px', '5%', @@ -152,11 +195,17 @@ export default { ], }, 'border-image-outset': { - link: '#border-image-outset', + links: { + tr: '#the-border-image-outset', + dev: '#border-image-outset', + }, tests: ['10px', '20', '10px 20', '10px 20px', '20 30', '2px 3px 4', '1 2px 3px 4'], }, 'border-image-repeat': { - link: '#border-image-repeat', + links: { + tr: '#the-border-image-repeat', + dev: '#border-image-repeat', + }, tests: [ 'stretch', 'repeat', @@ -181,8 +230,10 @@ export default { ], }, 'border-image': { - link: '#border-image', - dataTypes: ['image'], + links: { + tr: '#the-border-image', + dev: '#border-image', + }, tests: [ 'url(foo.png) 10', 'url(foo.png) 10%', @@ -196,7 +247,10 @@ export default { ], }, 'box-shadow': { - link: '#box-shadow', + links: { + tr: '#the-box-shadow', + dev: '#box-shadow', + }, tests: [ '1px 1px', '0 0 black', diff --git a/features/css-backgrounds-4.js b/tests/css-backgrounds-4.js similarity index 86% rename from features/css-backgrounds-4.js rename to tests/css-backgrounds-4.js index c371b107..c1ebef8d 100644 --- a/features/css-backgrounds-4.js +++ b/tests/css-backgrounds-4.js @@ -1,11 +1,14 @@ export default { - id: 'css-backgrounds-4', title: 'CSS Backgrounds Module Level 4', - link: 'css-backgrounds-4', + links: { + dev: 'css-backgrounds-4', + }, status: 'experimental', properties: { 'background-position-x': { - link: '#background-position-longhands', + links: { + dev: '#background-position-longhands', + }, tests: [ 'right', 'center', @@ -34,7 +37,9 @@ export default { ], }, 'background-position-y': { - link: '#background-position-longhands', + links: { + dev: '#background-position-longhands', + }, tests: [ 'bottom', 'center', @@ -64,7 +69,9 @@ export default { ], }, 'background-position-block': { - link: '#background-position-longhands', + links: { + dev: '#background-position-longhands', + }, tests: [ 'center', 'start', @@ -95,7 +102,9 @@ export default { ], }, 'background-position-inline': { - link: '#background-position-longhands', + links: { + dev: '#background-position-longhands', + }, tests: [ 'center', 'start', @@ -126,8 +135,10 @@ export default { ], }, 'background-clip': { - link: '#background-clip', - specLink: '#the-background-clip', + links: { + tr: '#the-background-clip', + dev: '#background-clip', + }, tests: ['text', 'border-area'], }, }, diff --git a/tests/css-borders-4.js b/tests/css-borders-4.js new file mode 100644 index 00000000..cfee7b47 --- /dev/null +++ b/tests/css-borders-4.js @@ -0,0 +1,289 @@ +const border_radius_tests = [ + '0', + '50%', + '250px 100px', + '10px 20px', + '50% 10%', + '250px / 50px', + '50% / 10%', + '250px 100px / 50px', + '250px / 50px 10px', + '250px 100px / 50px 10px', +]; +const corner_shape_tests = [ + 'round', + 'scoop', + 'bevel', + 'notch', + 'square', + 'squircle', + 'superellipse(4)', + 'superellipse(infinity)', +]; +const border_clip_tests = [ + 'normal', + '10px', + '10%', + '1fr', + '10px 20px', + '10% 20%', + '1fr 2fr', + '10px 20% 1fr', +]; + +export default { + title: 'CSS Borders and Box Decorations Module Level 4', + link: 'css-borders-4', + status: 'experimental', + values: { + 'properties': ['border-top-color', 'border-block-start-color'], + 'stripes()': { + link: '#border-color', + tests: [ + 'stripes(red, yellow, green, blue)', + 'stripes(red 1px, yellow 2px, green 3px, blue 4px)', + 'stripes(red 10%, yellow 20%, green 30%, blue 40%)', + 'stripes(red 1fr, yellow 2fr, green 3fr, blue 4fr)', + 'stripes(red, yellow 2px, green 30%, blue 4fr)', + ], + }, + 'stripes() in border-color shorthand': { + link: '#border-color', + tests: [ + 'stripes(red, yellow, green, blue)', + 'stripes(red 1px, yellow 2px, green 3px, blue 4px)', + 'stripes(red 10%, yellow 20%, green 30%, blue 40%)', + 'stripes(red 1fr, yellow 2fr, green 3fr, blue 4fr)', + 'stripes(red, yellow 2px, green 30%, blue 4fr)', + 'stripes(red, yellow) stripes(green, blue)', + 'stripes(red 1px, yellow 2px) stripes(green 3px, blue 4px)', + 'stripes(red 10%, yellow 20%) stripes(green 30%, blue 40%)', + 'stripes(red 1fr, yellow 2fr) stripes(green 3fr, blue 4fr)', + 'stripes(red, yellow) stripes(green, blue) stripes(orange, purple)', + 'stripes(red 1px, yellow 2px) stripes(green 1fr, blue 2fr) stripes(orange 10%, purple 20%)', + 'stripes(red, yellow) stripes(green, blue) stripes(orange, purple) stripes(pink, brown)', + 'stripes(red 1px, yellow 2px) stripes(green 1fr, blue 2fr) stripes(orange 10%, purple 20%) stripes(pink 1fr, brown 2px)', + ], + }, + }, + properties: { + 'border-top-radius': { + link: '#corner-sizing-side-shorthands', + tests: border_radius_tests, + }, + 'border-right-radius': { + link: '#corner-sizing-side-shorthands', + tests: border_radius_tests, + }, + 'border-bottom-radius': { + link: '#corner-sizing-side-shorthands', + tests: border_radius_tests, + }, + 'border-left-radius': { + link: '#corner-sizing-side-shorthands', + tests: border_radius_tests, + }, + 'border-block-start-radius': { + link: '#corner-sizing-side-shorthands', + tests: border_radius_tests, + }, + 'border-block-end-radius': { + link: '#corner-sizing-side-shorthands', + tests: border_radius_tests, + }, + 'border-inline-start-radius': { + link: '#corner-sizing-side-shorthands', + tests: border_radius_tests, + }, + 'border-inline-end-radius': { + link: '#corner-sizing-side-shorthands', + tests: border_radius_tests, + }, + 'corner-shape': { + link: '#corner-shaping', + tests: [ + ...corner_shape_tests, + 'round scoop', + 'round scoop bevel', + 'round scoop bevel notch', + ], + }, + 'corner-top-left-shape': { + link: '#corner-shape-shorthands', + tests: corner_shape_tests, + }, + 'corner-top-right-shape': { + link: '#corner-shape-shorthands', + tests: corner_shape_tests, + }, + 'corner-bottom-right-shape': { + link: '#corner-shape-shorthands', + tests: corner_shape_tests, + }, + 'corner-bottom-left-shape': { + link: '#corner-shape-shorthands', + tests: corner_shape_tests, + }, + 'corner-start-start-shape': { + link: '#corner-shape-shorthands', + tests: corner_shape_tests, + }, + 'corner-start-end-shape': { + link: '#corner-shape-shorthands', + tests: corner_shape_tests, + }, + 'corner-end-end-shape': { + link: '#corner-shape-shorthands', + tests: corner_shape_tests, + }, + 'corner-end-start-shape': { + link: '#corner-shape-shorthands', + tests: corner_shape_tests, + }, + 'corner-top-shape': { + link: '#corner-shape-shorthands', + tests: corner_shape_tests, + }, + 'corner-bottom-shape': { + link: '#corner-shape-shorthands', + tests: corner_shape_tests, + }, + 'corner-left-shape': { + link: '#corner-shape-shorthands', + tests: corner_shape_tests, + }, + 'corner-right-shape': { + link: '#corner-shape-shorthands', + tests: corner_shape_tests, + }, + 'corner-block-start-shape': { + link: '#corner-shape-shorthands', + tests: corner_shape_tests, + }, + 'corner-block-end-shape': { + link: '#corner-shape-shorthands', + tests: corner_shape_tests, + }, + 'corner-inline-start-shape': { + link: '#corner-shape-shorthands', + tests: corner_shape_tests, + }, + 'corner-inline-end-shape': { + link: '#corner-shape-shorthands', + tests: corner_shape_tests, + }, + 'border-limit': { + link: '#border-limit', + tests: [ + 'all', + 'sides', + 'corners', + 'sides 10px', + 'corners 10px', + 'sides 5%', + 'corners 5%', + 'top 10px', + 'right 10px', + 'bottom 10px', + 'left 10px', + 'top 5%', + 'right 5%', + 'bottom 5%', + 'left 5%', + ], + }, + 'border-clip': { + link: '#border-clip', + tests: border_clip_tests, + }, + 'border-clip-top': { + link: '#border-clip', + tests: border_clip_tests, + }, + 'border-clip-right': { + link: '#border-clip', + tests: border_clip_tests, + }, + 'border-clip-bottom': { + link: '#border-clip', + tests: border_clip_tests, + }, + 'border-clip-left': { + link: '#border-clip', + tests: border_clip_tests, + }, + 'box-shadow-color': { + link: '#box-shadow-color', + tests: [ + 'green', + 'green, blue' + ], + }, + 'box-shadow-offset': { + link: '#box-shadow-offset', + tests: [ + 'none', + '0 0', + '10px 1em', + '-10px -1em', + 'none, 0 0, 10px 1em', + ], + }, + 'box-shadow-blur': { + link: '#box-shadow-blur', + tests: [ + '0', + '10px', + '1em', + '10px, 1em', + ], + }, + 'box-shadow-spread': { + link: '#box-shadow-spread', + tests: [ + '0', + '10px', + '1em', + '10px, 1em', + ], + }, + 'box-shadow-position': { + link: '#box-shadow-position', + tests: [ + 'outset', + 'inset', + 'outset, inset', + ], + }, + 'border-shape': { + link: '#border-shape', + tests: [ + 'none', + 'inset(10% round 10% 40% 10% 40%)', + 'ellipse(at top 50% left 20%)', + 'circle(at top left)', + 'polygon(100% 0, 100% 100%, 0 100%)', + "path('M 20 20 H 80 V 30')", + 'rect(10% 20px 30% 40px)', + 'xywh(10% 40% 100px 200px round 10% 40% 10% 40%)', + 'inset(10% round 10% 40% 10% 40%) margin-box', + 'ellipse(at top 50% left 20%) margin-box', + 'circle(at top left) margin-box', + 'polygon(100% 0, 100% 100%, 0 100%) margin-box', + "path('M 20 20 H 80 V 30') margin-box", + 'rect(10% 20px 30% 40px) margin-box', + 'xywh(10% 40% 100px 200px round 10% 40% 10% 40%) margin-box', + 'shape(from 30% 60px, curve to 180px 180px via 90px 190px, close)', + 'attr(src url)', + 'url(image.png)', + 'margin-box', + 'border-box', + 'padding-box', + 'content-box', + 'fill-box', + 'stroke-box', + 'view-box', + ], + }, + }, +}; diff --git a/tests/css-box-3.js b/tests/css-box-3.js new file mode 100644 index 00000000..d568185d --- /dev/null +++ b/tests/css-box-3.js @@ -0,0 +1,172 @@ +export default { + title: 'CSS 2 Box Model', + links: { + tr: 'CSS22/box.html', + dev: 'css2/', + }, + status: 'stable', + firstSnapshot: 2.2, + lastSnapshot: 2.2, + properties: { + 'border-color': { + link: '#border-color-properties', + tests: ['black', '#ff0000 rgb(0, 255, 0)', 'rgb(0%, 0%, 100%) #0f0 transparent', 'red green blue yellow'], + }, + 'border-style': { + link: '#border-style-properties', + tests: ['none', 'hidden', 'none dashed', 'none dashed dotted', 'none dashed dotted solid'], + }, + 'border-top': { + link: '#border-shorthand-properties', + tests: [ + 'black', + 'dotted', + '5px', + '#ff0000 dashed', + 'solid 0.2em', + 'rgb(0, 0, 255) 0.1ex', + '#0f0 double 0.8mm', + ], + }, + 'border-right': { + link: '#border-shorthand-properties', + tests: [ + 'black', + 'dotted', + '5px', + '#ff0000 dashed', + 'solid 0.2em', + 'rgb(0, 0, 255) 0.1ex', + '#0f0 double 0.8mm', + ], + }, + 'border-bottom': { + link: '#border-shorthand-properties', + tests: [ + 'black', + 'dotted', + '5px', + '#ff0000 dashed', + 'solid 0.2em', + 'rgb(0, 0, 255) 0.1ex', + '#0f0 double 0.8mm', + ], + }, + 'border-left': { + link: '#border-shorthand-properties', + tests: [ + 'black', + 'dotted', + '5px', + '#ff0000 dashed', + 'solid 0.2em', + 'rgb(0, 0, 255) 0.1ex', + '#0f0 double 0.8mm', + ], + }, + 'border-top-color': { + link: '#border-color-properties', + tests: ['black', '#00f', '#000000', 'rgb(255, 255, 255)', 'rgb(100%, 50%, 50%)', 'transparent'], + }, + 'border-right-color': { + link: '#border-color-properties', + tests: ['black', '#00f', '#000000', 'rgb(255, 255, 255)', 'rgb(100%, 50%, 50%)', 'transparent'], + }, + 'border-bottom-color': { + link: '#border-color-properties', + tests: ['black', '#00f', '#000000', 'rgb(255, 255, 255)', 'rgb(100%, 50%, 50%)', 'transparent'], + }, + 'border-left-color': { + link: '#border-color-properties', + tests: ['black', '#00f', '#000000', 'rgb(255, 255, 255)', 'rgb(100%, 50%, 50%)', 'transparent'], + }, + 'border-top-style': { + link: '#border-style-properties', + tests: ['none', 'hidden', 'dotted', 'dashed', 'solid', 'double', 'groove', 'ridge', 'inset', 'outset'], + }, + 'border-right-style': { + link: '#border-style-properties', + tests: ['none', 'hidden', 'dotted', 'dashed', 'solid', 'double', 'groove', 'ridge', 'inset', 'outset'], + }, + 'border-bottom-style': { + link: '#border-style-properties', + tests: ['none', 'hidden', 'dotted', 'dashed', 'solid', 'double', 'groove', 'ridge', 'inset', 'outset'], + }, + 'border-left-style': { + link: '#border-style-properties', + tests: ['none', 'hidden', 'dotted', 'dashed', 'solid', 'double', 'groove', 'ridge', 'inset', 'outset'], + }, + 'border-top-width': { + link: '#border-width-properties', + tests: ['thin', 'medium', 'thick', '5px'], + }, + 'border-right-width': { + link: '#border-width-properties', + tests: ['thin', 'medium', 'thick', '5px'], + }, + 'border-bottom-width': { + link: '#border-width-properties', + tests: ['thin', 'medium', 'thick', '5px'], + }, + 'border-left-width': { + link: '#border-width-properties', + tests: ['thin', 'medium', 'thick', '5px'], + }, + 'border-width': { + link: '#border-width-properties', + tests: ['thin', 'thin medium', 'thin medium thick', 'thin medium thick 5px'], + }, + border: { + link: '#border-shorthand-properties', + tests: [ + 'black', + 'dotted', + '5px', + '#ff0000 dashed', + 'solid 0.2em', + 'rgb(0, 0, 255) 0.1ex', + 'rgb(100%, 50%, 50%) double 0.8mm', + ], + }, + 'margin-right': { + link: '#propdef-margin-right', + tests: ['auto', '10px', '5%'], + }, + 'margin-left': { + link: '#propdef-margin-left', + tests: ['auto', '10px', '5%'], + }, + 'margin-top': { + link: '#propdef-margin-top', + tests: ['auto', '10px', '5%'], + }, + 'margin-bottom': { + link: '#propdef-margin-bottom', + tests: ['auto', '10px', '5%'], + }, + margin: { + link: '#propdef-margin', + tests: ['10px', '10px 5%', '10px 5px auto', '10px 5px auto 1em'], + }, + 'padding-top': { + link: '#padding-properties', + tests: ['10px', '5%'], + }, + 'padding-right': { + link: '#padding-properties', + tests: ['10px', '5%'], + }, + 'padding-bottom': { + link: '#padding-properties', + tests: ['10px', '5%'], + }, + 'padding-left': { + link: '#padding-properties', + tests: ['10px', '5%'], + }, + padding: { + link: '#padding-properties', + tests: ['10px', '10px 5%', '10px 5% 0.5em', '10px 5% 0.5em 0.8mm'], + }, + }, +}; diff --git a/features/css-box-4.js b/tests/css-box-4.js similarity index 97% rename from features/css-box-4.js rename to tests/css-box-4.js index 5e39d591..43d095ea 100644 --- a/features/css-box-4.js +++ b/tests/css-box-4.js @@ -1,5 +1,4 @@ export default { - id: 'css-box-4', title: 'CSS Box Model Module Level 4', link: 'css-box-4', status: 'experimental', diff --git a/features/css-break-3.js b/tests/css-break-3.js similarity index 97% rename from features/css-break-3.js rename to tests/css-break-3.js index d6bfffea..f9963eab 100644 --- a/features/css-break-3.js +++ b/tests/css-break-3.js @@ -1,5 +1,4 @@ export default { - id: 'css-break-3', title: 'CSS Fragmentation Module Level 3', link: 'css-break-3', status: 'stable', diff --git a/features/css-break-4.js b/tests/css-break-4.js similarity index 96% rename from features/css-break-4.js rename to tests/css-break-4.js index 83b28fe8..c51153dc 100644 --- a/features/css-break-4.js +++ b/tests/css-break-4.js @@ -1,5 +1,4 @@ export default { - id: 'css-break-4', title: 'CSS Fragmentation Module Level 4', link: 'css-break-4', status: 'experimental', diff --git a/features/css-cascade-3.js b/tests/css-cascade-3.js similarity index 94% rename from features/css-cascade-3.js rename to tests/css-cascade-3.js index 5dc50b5c..5cecb0d5 100644 --- a/features/css-cascade-3.js +++ b/tests/css-cascade-3.js @@ -1,5 +1,4 @@ export default { - id: 'css-cascade-3', title: 'CSS Cascading and Inheritance Level 3', link: 'css-cascade-3', status: 'stable', diff --git a/features/css-cascade-4.js b/tests/css-cascade-4.js similarity index 94% rename from features/css-cascade-4.js rename to tests/css-cascade-4.js index df2535bd..48fb4326 100644 --- a/features/css-cascade-4.js +++ b/tests/css-cascade-4.js @@ -1,5 +1,4 @@ export default { - id: 'css-cascade-4', title: 'CSS Cascading and Inheritance Level 4', link: 'css-cascade-4', status: 'stable', diff --git a/tests/css-cascade-5.js b/tests/css-cascade-5.js new file mode 100644 index 00000000..8612ee13 --- /dev/null +++ b/tests/css-cascade-5.js @@ -0,0 +1,57 @@ +export default { + title: 'CSS Cascading and Inheritance Level 5', + link: 'css-cascade-5', + status: 'experimental', + values: { + properties: ['color', 'font-weight', 'background-image', 'all'], + 'revert-layer': { + link: '#revert-layer', + tests: 'revert-layer', + }, + }, + properties: { + all: { + link: '#revert-layer', + tests: 'revert-layer', + }, + }, + '@rules': { + '@layer': { + link: '#at-layer', + tests: [ + '@layer framework {\n h1, h2 { color: maroon; background: white; }\n}', + '@layer framework {\n h1, h2 { color: maroon; background: white; }\n \n @media (prefers-color-scheme: dark) {\n h1, h2 { color: red; background: black; }\n }\n}', + '@layer framework;', + '@layer default, framework;', + ], + }, + }, + interfaces: { + /* Doesn't currently work because style sheet is only available once imported + CSSImportRule: { + link: '#extensions-to-cssimportrule-interface', + links: { + mdnGroup: 'DOM', + }, + tests: ['layerName'], + required: '@import url("foo.css") layer(mylayer);', + }, + */ + CSSLayerBlockRule: { + link: '#the-csslayerblockrule-interface', + links: { + mdnGroup: 'DOM', + }, + tests: ['name', 'cssRules', 'insertRule', 'deleteRule'], + required: '@layer mylayer { }', + }, + CSSLayerStatementRule: { + link: '#the-csslayerstatementrule-interface', + links: { + mdnGroup: 'DOM', + }, + tests: ['nameList', 'cssText', 'parentRule', 'parentStyleSheet'], + required: '@layer firstLayer, secondLayer;', + }, + }, +}; diff --git a/tests/css-cascade-6.js b/tests/css-cascade-6.js new file mode 100644 index 00000000..fff236d1 --- /dev/null +++ b/tests/css-cascade-6.js @@ -0,0 +1,31 @@ +export default { + title: 'CSS Cascading and Inheritance Level 6', + link: 'css-cascade-6', + status: 'experimental', + '@rules': { + '@scope': { + link: '#scope-atrule', + tests: [ + '@scope (#hero) {\n img {\n border-radius: 50%;\n }\n}', + '@scope ([data-scope="main-component"]) to ([data-scope]) {\n p {\n color: red;\n }\n\n section {\n background-color: snow;\n }\n}', + ], + }, + }, + selectors: { + 'scope-combinator': { + title: 'Scoped Descendant Combinator', + link: '#scope-combinator', + tests: 'foo >> bar', + }, + }, + interfaces: { + CSSScopeRule: { + links: { + dev: '#the-cssscoperule-interface', + mdnGroup: 'DOM', + }, + tests: ['start', 'end', 'cssRules', 'insertRule', 'deleteRule'], + required: '@scope (foo) to (bar) {}', + } + } +} diff --git a/features/css-color-3.js b/tests/css-color-3.js similarity index 97% rename from features/css-color-3.js rename to tests/css-color-3.js index 4c0a7d1e..a193e97c 100644 --- a/features/css-color-3.js +++ b/tests/css-color-3.js @@ -1,5 +1,4 @@ export default { - id: 'css-color-3', title: 'CSS Color Module Level 3', link: 'css-color-3', status: 'stable', diff --git a/tests/css-color-4.js b/tests/css-color-4.js new file mode 100644 index 00000000..0d6e9ed4 --- /dev/null +++ b/tests/css-color-4.js @@ -0,0 +1,133 @@ +export default { + title: 'CSS Color Module Level 4', + link: 'css-color-4', + status: 'stable', + firstSnapshot: 2022, + values: { + properties: ['color', 'background-color', 'border-color', 'text-decoration-color', 'column-rule-color'], + 'comma-less colors': { + link: '#funcdef-rgb', + mdn: 'color_value', + tests: ['rgb(0% 20% 70%)', 'rgb(0 64 185)', 'hsl(0 0% 0%)'], + }, + '/ alpha': { + link: '#funcdef-rgb', + mdn: 'color_value', + tests: [ + 'rgba(0% 20% 70% / 50%)', + 'rgba(0% 20% 70% / .5)', + 'rgba(0 64 185 / 50%)', + 'rgba(0 64 185 / .5)', + 'hsla(0 0% 0% /.5)', + ], + }, + 'optional alpha': { + link: '#funcdef-rgb', + mdn: 'color_value', + tests: [ + 'rgb(0% 20% 70% / 50%)', + 'rgb(0% 20% 70% / .5)', + 'rgb(0 64 185 / 50%)', + 'rgb(0 64 185 / .5)', + 'hsl(0 0% 0% / .5)', + ], + }, + 'Hex with alpha': { + link: '#hex-notation', + mdn: 'color_value', + tests: ['#000F', '#000000FF'], + }, + rebeccapurple: { + link: '#named-colors', + mdn: 'color_value', + tests: 'rebeccapurple', + }, + 'system colors': { + link: '#css-system-colors', + mdn: 'color_value', + tests: [ + 'Canvas', + 'CanvasText', + 'LinkText', + 'VisitedText', + 'ActiveText', + 'ButtonFace', + 'Field', + 'FieldText', + 'Highlight', + 'HighlightText', + 'GrayText', + ], + }, + 'hwb()': { + link: '#the-hwb-notation', + mdn: 'color_value/hwb', + tests: ['hwb(0 0% 0%)', 'hwb(0 0% 0% / .5)'], + }, + 'lab()': { + link: '#specifying-lab-lch', + mdn: 'color_value/lab', + tests: ['lab(0% 0 0)', 'lab(0% 0 0 /.5)'], + }, + 'oklab()': { + link: '#specifying-oklab-lch', + mdn: 'color_value/oklab', + tests: ['oklab(0% 0 0)', 'oklab(40.101% 0.1147 0.0453 / .5)'], + }, + 'lch()': { + link: '#specifying-lch-lch', + mdn: 'color_value/lch', + tests: ['lch(0% 0 0)', 'lch(none 0% none)', 'lch(0% 0 0 / .5)'], + }, + 'oklch()': { + link: '#specifying-oklch-lch', + mdn: 'color_value/oklch', + tests: ['oklch(0% 0 0)', 'oklch(40.101% 0.12332 21.555 / .5)'], + }, + 'color()': { + link: '#color-function', + mdn: 'color_value/color', + tests: [ + 'color(display-p3 1 0.5 0)', + 'color(display-p3 .2 .4 .6 / .5)', + 'color(display-p3 20% 40% 60%)', + 'color(srgb 1 0.5 0)', + 'color(srgb .2 .4 .6 / .5)', + 'color(srgb 20% 40% 60%)', + 'color(srgb-linear 1 0.5 0)', + 'color(srgb-linear .2 .4 .6 / .5)', + 'color(srgb-linear 20% 40% 60%)', + 'color(a98-rgb 1 0.5 0)', + 'color(a98-rgb .2 .4 .6 / .5)', + 'color(a98-rgb 20% 40% 60%)', + 'color(prophoto-rgb 1 0.5 0)', + 'color(prophoto-rgb .2 .4 .6 / .5)', + 'color(prophoto-rgb 20% 40% 60%)', + 'color(rec2020 1 0.5 0)', + 'color(rec2020 .2 .4 .6 / .5)', + 'color(rec2020 20% 40% 60%)', + 'color(xyz 1 0.5 0)', + 'color(xyz .2 .4 .6 / .5)', + 'color(xyz 20% 40% 60%)', + 'color(xyz-d50 1 0.5 0)', + 'color(xyz-d50 .2 .4 .6 / .5)', + 'color(xyz-d50 20% 40% 60%)', + 'color(xyz-d65 1 0.5 0)', + 'color(xyz-d65 .2 .4 .6 / .5)', + 'color(xyz-d65 20% 40% 60%)', + 'color(xyz-d65 .2 .4 .6 / none)', + 'color(xyz-d65 .2 .4 none / .5)', + 'color(xyz-d65 .2 none .4 / .5)', + 'color(xyz-d65 none .2 .4 / .5)', + 'color(xyz-d65 none none .4 / .5)', + 'color(xyz-d65 none none none)', + 'color(xyz-d65 none none none / none)', + ], + }, + 'percentages in opacity': { + link: '#transparency', + properties: ['opacity'], + tests: ['45%'], + }, + }, +}; diff --git a/features/css-color-5.js b/tests/css-color-5.js similarity index 63% rename from features/css-color-5.js rename to tests/css-color-5.js index d416139d..a20fe87a 100644 --- a/features/css-color-5.js +++ b/tests/css-color-5.js @@ -1,18 +1,15 @@ export default { - id: 'css-color-5', title: 'CSS Color Module Level 5', - link: 'css-color-5', + links: { + dev: 'css-color-5', + }, status: 'experimental', values: { - properties: [ - 'color', - 'background-color', - 'border-color', - 'text-decoration-color', - 'column-rule-color', - ], + properties: ['color', 'background-color', 'border-color', 'text-decoration-color', 'column-rule-color'], 'color-mix()': { - link: '#color-mix', + links: { + dev: '#color-mix', + }, mdn: 'color_value/color-mix', tests: [ 'color-mix(in srgb, teal 65%, olive)', @@ -27,17 +24,17 @@ export default { ], }, 'color-adjust()': { - link: '#coloradjust', + links: { + dev: '#coloradjust', + }, mdn: 'color_value', tests: ['color-adjust(peru lightness -20%)'], }, - 'contrast-color()': { - link: '#contrast-color', - mdn: 'color_value/contrast-color', - tests: ['contrast-color(red)', 'contrast-color(rgb(255, 0, 0, .2))'], - }, + 'relative color': { - link: '#relative-colors', + links: { + dev: '#relative-colors', + }, mdn: 'color_value', tests: [ 'rgb(from indianred 255 g b)', @@ -47,30 +44,32 @@ export default { 'oklab(from oklab(54.3% -22.5% -5%) calc(1.0 - l) calc(a * 0.8) b)', 'oklch(from oklch(52.6% 0.115 44.6deg) l c calc(h + 90))', 'color(from color(srgb 0 0 0 / 60%) srgb alpha 0.6 0.6 / 0.9)', - 'linear-gradient(in oklab to right, oklch(from hsl(none 3% 50%) calc(l * 0.8) c h), #4C3)', + 'linear-gradient(in oklab to right, oklch(from hsl(none 3% 50%) calc(l * 0.8) c h), #4C3)' ], }, + 'light-dark()': { - link: '#light-dark', - mdn: 'color_value/light-dark', + links: { + dev: '#light-dark', + }, + mdn: 'light-dark', tests: ['light-dark(green, red)'], }, + 'device-cmyk()': { link: '#cmyk-colors', mdn: 'color_value/device-cmyk', - tests: [ - 'device-cmyk(.2 .3 .4 .5)', - 'device-cmyk(.2 .3 .4 .5 / .5)', - 'device-cmyk(.2 .3 .4 .5 / 50%)', - ], + tests: ['device-cmyk(.2 .3 .4 .5)', 'device-cmyk(.2 .3 .4 .5 / .5)', 'device-cmyk(.2 .3 .4 .5 / 50%)'], }, }, - globals: { + interfaces: { CSSColorProfileRule: { - link: '#the-csscolorprofilerule-interface', - mdnGroup: 'DOM', - extends: 'CSSRule', - members: ['name', 'src', 'renderingIntent', 'components'], + links: { + dev: '#the-csscolorprofilerule-interface', + mdnGroup: 'DOM', + }, + tests: ['name', 'src', 'renderingIntent', 'components', 'cssText', 'parentRule', 'parentStyleSheet'], + required: '@color-profile { name: "sRGB"; src: url("sRGB.icc"); }', }, - }, + } }; diff --git a/features/css-color-adjust-1.js b/tests/css-color-adjust-1.js similarity index 96% rename from features/css-color-adjust-1.js rename to tests/css-color-adjust-1.js index 80ecd50d..90e420e0 100644 --- a/features/css-color-adjust-1.js +++ b/tests/css-color-adjust-1.js @@ -1,5 +1,4 @@ export default { - id: 'css-color-adjust-1', title: 'CSS Color Adjustment Module Level 1', link: 'css-color-adjust-1', status: 'stable', diff --git a/features/css-color-hdr-1.js b/tests/css-color-hdr-1.js similarity index 99% rename from features/css-color-hdr-1.js rename to tests/css-color-hdr-1.js index 04f7b351..08c528f8 100644 --- a/features/css-color-hdr-1.js +++ b/tests/css-color-hdr-1.js @@ -1,5 +1,4 @@ export default { - id: 'css-color-hdr-1', title: 'CSS Color HDR Module Level 1', link: 'css-color-hdr-1', status: 'experimental', diff --git a/features/css-composition-1.js b/tests/css-composition-1.js similarity index 97% rename from features/css-composition-1.js rename to tests/css-composition-1.js index f34ee526..3423630c 100644 --- a/features/css-composition-1.js +++ b/tests/css-composition-1.js @@ -1,5 +1,4 @@ export default { - id: 'css-composition-1', title: 'Compositing and Blending Level 1', link: 'compositing-1', group: 'fxtf', diff --git a/features/css-composition-2.js b/tests/css-composition-2.js similarity index 90% rename from features/css-composition-2.js rename to tests/css-composition-2.js index fdb0c641..d0c542c2 100644 --- a/features/css-composition-2.js +++ b/tests/css-composition-2.js @@ -1,5 +1,4 @@ export default { - id: 'css-composition-2', title: 'Compositing and Blending Level 2', link: 'compositing-2', group: 'fxtf', diff --git a/tests/css-conditional-3.js b/tests/css-conditional-3.js new file mode 100644 index 00000000..caf8661f --- /dev/null +++ b/tests/css-conditional-3.js @@ -0,0 +1,78 @@ +export default { + title: 'CSS Conditional Rules Module Level 3', + links: { + tr: 'css3-conditional', + dev: 'css-conditional-3', + }, + status: 'stable', + firstSnapshot: 2015, + '@rules': { + '@supports': { + link: '#at-supports', + tests: [ + '@supports (color: green) {}', + '@supports not (foo: bar) {}', + '@supports (color: green) or (color: red) {}', + '@supports (color: green) and (color: red) {}', + '@supports (color: green) and (not (foo: bar)) {}', + '@supports (color: green) or (not (foo: bar)) {}', + ], + }, + }, + interfaces: { + CSSRule: { + link: '#extensions-to-cssrule-interface', + links: { + mdnGroup: 'DOM', + }, + tests: ['SUPPORTS_RULE'], + required: 'div { }', + interface: function(style) { + return style.sheet.cssRules[0]; + } + }, + CSSConditionRule: { + link: '#the-cssconditionrule-interface', + links: { + mdnGroup: 'DOM', + }, + tests: ['conditionText', 'cssRules', 'insertRule', 'deleteRule'], + required: '@supports (color: green) { }', + interface: function(style) { + return style.sheet.cssRules[0]; + } + }, + CSSMediaRule: { + link: '#the-cssmediarule-interface', + links: { + mdnGroup: 'DOM', + }, + tests: ['media', 'matches', 'conditionText'], + required: '@media (min-width: 500px) { }', + interface: function(style) { + return style.sheet.cssRules[0]; + } + }, + CSSSupportsRule: { + link: '#the-csssupportsrule-interface', + links: { + mdnGroup: 'DOM', + }, + tests: ['matches', 'conditionText'], + required: '@supports (display: grid) { }', + interface: function(style) { + return style.sheet.cssRules[0]; + } + }, + CSS: { + link: '#the-css-namespace', + links: { + mdnGroup: 'DOM', + }, + tests: ['supports'], + interface: function() { + return CSS; + } + }, + }, +}; diff --git a/tests/css-conditional-4.js b/tests/css-conditional-4.js new file mode 100644 index 00000000..bf0b68b1 --- /dev/null +++ b/tests/css-conditional-4.js @@ -0,0 +1,15 @@ +export default { + title: 'CSS Conditional Rules Module Level 4', + link: 'css-conditional-4', + status: 'experimental', + '@rules': { + '@supports': { + link: '#at-supports-ext', + tests: [ + '@supports selector(::before) {}', + '@supports not selector(::-webkit-unknown-pseudo) {}', + '@supports selector(div, div) {}', + ], + }, + }, +}; diff --git a/tests/css-conditional-5.js b/tests/css-conditional-5.js new file mode 100644 index 00000000..77ba96a8 --- /dev/null +++ b/tests/css-conditional-5.js @@ -0,0 +1,30 @@ +export default { + title: 'CSS Conditional Rules Module Level 5', + link: 'css-conditional-5', + status: 'experimental', + '@rules': { + '@supports': { + link: '#at-supports-ext', + tests: ['@supports font-tech(features-opentype) {}', '@supports font-format(woff2) {}'], + }, + '@when': { + link: '#when-rule', + tests: [ + '@when media(min-width: 200px) {}', + '@when media(width >= 200px) {}', + '@when media(pointer) {}', + '@when supports(display: flex) {}', + ], + }, + '@else': { + link: '#else-rule', + tests: [ + '@when media(min-width: 200px) {} @else {}', + '@when media(min-width: 200px) {} @else media(min-width: 100px) {}', + '@when media(min-width: 200px) {} @else media(min-width >= 100px) {}', + '@when media(min-width: 200px) {} @else supports(display: flex) {}', + '@when media(min-width: 200px) {} @else media(min-width: 100px) {} @else {}', + ], + }, + }, +}; diff --git a/features/css-contain-1.js b/tests/css-containment-1.js similarity index 94% rename from features/css-contain-1.js rename to tests/css-containment-1.js index 07146605..4d03ba49 100644 --- a/features/css-contain-1.js +++ b/tests/css-containment-1.js @@ -1,5 +1,4 @@ export default { - id: 'css-contain-1', title: 'CSS Containment Module Level 1', link: 'css-contain-1', status: 'stable', diff --git a/features/css-contain-2.js b/tests/css-containment-2.js similarity index 62% rename from features/css-contain-2.js rename to tests/css-containment-2.js index c475bc0d..34dbb556 100644 --- a/features/css-contain-2.js +++ b/tests/css-containment-2.js @@ -1,5 +1,4 @@ export default { - id: 'css-contain-2', title: 'CSS Containment Module Level 2', link: 'css-contain-2', status: 'experimental', @@ -13,12 +12,16 @@ export default { tests: ['visible', 'auto', 'hidden'], }, }, - globals: { + interfaces: { ContentVisibilityAutoStateChangeEvent: { - link: 'https://drafts.csswg.org/css-contain/#content-visibility-auto-state-change', - mdnGroup: 'DOM', - extends: 'Event', - members: ['skipped'], + links: { + dev: '#content-visibility-auto-state-change', + mdnGroup: 'DOM', + }, + tests: ['skipped'], + interface: function() { + return new ContentVisibilityAutoStateChangeEvent('contentvisibilityautostatechange'); + } }, } }; diff --git a/tests/css-containment-3.js b/tests/css-containment-3.js new file mode 100644 index 00000000..57534c3a --- /dev/null +++ b/tests/css-containment-3.js @@ -0,0 +1,97 @@ +export default { + title: 'CSS Containment Module Level 3', + link: 'css-contain-3', + status: 'experimental', + '@rules': { + '@container': { + link: '#container-rule', + tests: [ + '@container (min-width: 150px) { div { margin: 10px } }', + '@container (max-width: 1000px) { div { margin: 10px } }', + '@container (width >= 150px) { div { margin: 10px } }', + '@container (height >= 150px) { div { margin: 10px } }', + '@container (inline-size >= 150px) { div { margin: 10px } }', + '@container (block-size >= 150px) { div { margin: 10px } }', + '@container (aspect-ratio >= 1 / 1) { div { margin: 10px } }', + '@container (orientation = portrait) { div { margin: 10px } }', + '@container (width >= 150px) and (orientation = portrait) { div { margin: 10px } }', + '@container (width >= 150px) or (orientation = portrait) { div { margin: 10px } }', + '@container not (width < 150px) { div { margin: 10px } }', + '@container style(pointer) { div { margin: 10px } }', + '@container style(pointer: fine) { div { margin: 10px } }', + '@container x (width >= 150px) { div { margin: 10px } }', + '@container card (inline-size > 30em) and style(--responsive = true) { div { margin: 10px } }', + ], + }, + }, + values: { + properties: ['width'], + cqw: { + link: '#container-lengths', + mdn: 'length', + tests: '5cqw', + }, + cqh: { + link: '#container-lengths', + mdn: 'length', + tests: '5cqh', + }, + cqi: { + link: '#container-lengths', + mdn: 'length', + tests: '5cqi', + }, + cqb: { + link: '#container-lengths', + mdn: 'length', + tests: '5cqb', + }, + cqmin: { + link: '#container-lengths', + mdn: 'length', + tests: '5cqmin', + }, + cqmax: { + link: '#container-lengths', + mdn: 'length', + tests: '5cqmax', + }, + }, + properties: { + 'container-type': { + link: '#container-type', + tests: [ + 'normal', + 'size', + 'inline-size', + ], + }, + 'container-name': { + link: '#container-name', + tests: ['none', 'x', 'x y'], + }, + container: { + link: '#container-shorthand', + tests: [ + 'none', + 'x / normal', + 'x / size', + 'x / inline-size', + 'x y / size', + ], + }, + }, + interfaces: { + CSSContainerRule: { + link: '#the-csscontainerrule-interface', + links: { + mdnGroup: 'DOM', + }, + tests: ['containerName', 'containerQuery', 'conditionText'], + required: '@container (min-width: 500px) { }', + interface: function(style) { + return style.sheet.cssRules[0]; + } + }, + }, +}; diff --git a/features/css-content-3.js b/tests/css-content-3.js similarity index 92% rename from features/css-content-3.js rename to tests/css-content-3.js index 71c771a0..e4f52c1b 100644 --- a/features/css-content-3.js +++ b/tests/css-content-3.js @@ -1,5 +1,4 @@ export default { - id: 'css-content-3', title: 'CSS Generated Content Module Level 3', link: 'css-content-3', status: 'experimental', diff --git a/tests/css-counter-styles-3.js b/tests/css-counter-styles-3.js new file mode 100644 index 00000000..3ef5080b --- /dev/null +++ b/tests/css-counter-styles-3.js @@ -0,0 +1,161 @@ +export default { + title: 'CSS Counter Styles Level 3', + link: 'css-counter-styles-3', + status: 'stable', + firstSnapshot: 2021, + '@rules': { + '@counter-style': { + link: '#the-counter-style-rule', + tests: "@counter-style foo {\n system: numeric;\n symbols: '0' '1' '2' '3' '4' '5' '6' '7' '8' '9';\n}", + }, + }, + descriptors: { + '@counter-style example/system': { + link: '#counter-style-system', + required: { + '*': { + descriptor: 'system: alphabetic; symbols: A B C D; additive-symbols: 1000 M, 500 C', + }, + 'extends decimal': {}, + }, + tests: ['cyclic', 'numeric', 'alphabetic', 'symbolic', 'additive', 'fixed 3', 'extends decimal'], + }, + '@counter-style example/negative': { + link: '#counter-style-negative', + required: { + '*': { + descriptor: 'system: alphabetic; symbols: A B C D; additive-symbols: 1000 M, 500 C', + }, + }, + tests: ["'-'", "'(' ')'"], + }, + '@counter-style example/prefix': { + link: '#counter-style-prefix', + required: { + '*': { + descriptor: 'system: alphabetic; symbols: A B C D; additive-symbols: 1000 M, 500 C', + }, + }, + tests: ['»', 'url(https://lea.verou.me/mark.svg)'], + }, + '@counter-style example/suffix': { + link: '#counter-style-suffix', + required: { + '*': { + descriptor: 'system: alphabetic; symbols: A B C D; additive-symbols: 1000 M, 500 C', + }, + }, + tests: ['»', 'url(https://lea.verou.me/mark.svg)'], + }, + '@counter-style example/range': { + link: '#counter-style-range', + required: { + '*': { + descriptor: 'system: alphabetic; symbols: A B C D; additive-symbols: 1000 M, 500 C', + }, + }, + tests: [ + 'auto', + '2 5', + 'infinite 10', + '10 infinite', + 'infinite infinite', + '2 5, 8 10', + 'infinite 8, 6 infinite', + ], + }, + '@counter-style example/symbols': { + link: '#counter-style-symbols', + required: { + '*': { descriptor: 'system: alphabetic' }, + 'custom-numbers': { + rule: '@counter-style custom-numbers { system: fixed; symbols: A B C D E;}', + }, + }, + tests: [ + 'A B C D E F', + "'\\24B6' '\\24B7' '\\24B8' D E F", + "'0' '1' '2' '4' '5' '6' '7'", + "'1' url('second.svg') '2'", + "url('first.svg') url('second.svg') url('third.svg')", + 'custom-numbers', + ], + }, + '@counter-style example/additive-symbols': { + links: { + tr: '#additive-system', + dev: '#descdef-counter-style-additive-symbols', + }, + required: { + '*': { descriptor: 'system: additive' }, + }, + tests: ["3 '0'", "3 '1', 2 '\\2E\\20'", "3 '1', 2 url(symbol.svg)"], + }, + '@counter-style example/pad': { + link: '#counter-style-pad', + required: { + '*': { + descriptor: 'system: alphabetic; symbols: A B C D; additive-symbols: 1000 M, 500 C', + }, + }, + tests: ["0 ''", "3 '0'", "'0' 3"], + }, + '@counter-style example/fallback': { + link: '#counter-style-fallback', + required: { + '*': { + descriptor: 'system: alphabetic; symbols: A B C D; additive-symbols: 1000 M, 500 C', + }, + }, + tests: ['decimal'], + }, + '@counter-style example/speak-as': { + link: '#counter-style-speak-as', + required: { + '*': { + descriptor: 'system: alphabetic; symbols: A B C D; additive-symbols: 1000 M, 500 C', + }, + }, + tests: ['auto', 'bullets', 'numbers', 'words', 'spell-out', 'example-counter'], + }, + }, + interfaces: { + CSSRule: { + link: '#extensions-to-cssrule-interface', + links: { + mdnGroup: 'DOM', + }, + tests: ['COUNTER_STYLE_RULE'], + required: 'div { }', + interface: function(style) { + return style.sheet.cssRules[0]; + } + }, + CSSCounterStyleRule: { + link: '#the-csscounterstylerule-interface', + links: { + mdnGroup: 'DOM', + }, + tests: [ + 'name', + 'system', + 'symbols', + 'additiveSymbols', + 'negative', + 'prefix', + 'suffix', + 'range', + 'pad', + 'speakAs', + 'fallback', + 'cssText', + 'parentRule', + 'parentStyleSheet', + ], + required: '@counter-style example { system: alphabetic; symbols: A B C D; additive-symbols: 1000 M, 500 C; }', + interface: function(style) { + return style.sheet.cssRules[0]; + } + }, + }, +}; diff --git a/features/css-display-3.js b/tests/css-display-3.js similarity index 96% rename from features/css-display-3.js rename to tests/css-display-3.js index 82c1e25b..a99cd0ab 100644 --- a/features/css-display-3.js +++ b/tests/css-display-3.js @@ -1,5 +1,4 @@ export default { - id: 'css-display-3', title: 'CSS Display Module Level 3', link: 'css-display-3', status: 'stable', diff --git a/features/css-display-4.js b/tests/css-display-4.js similarity index 95% rename from features/css-display-4.js rename to tests/css-display-4.js index da9536d1..27d84490 100644 --- a/features/css-display-4.js +++ b/tests/css-display-4.js @@ -1,5 +1,4 @@ export default { - id: 'css-display-4', title: 'CSS Display Module Level 4', link: 'css-display-4', status: 'experimental', diff --git a/features/css-easing-1.js b/tests/css-easing-1.js similarity index 93% rename from features/css-easing-1.js rename to tests/css-easing-1.js index e3ec83c1..bec87fe8 100644 --- a/features/css-easing-1.js +++ b/tests/css-easing-1.js @@ -1,5 +1,4 @@ export default { - id: 'css-easing-1', title: 'CSS Easing Functions Level 1', link: 'css-easing-1', status: 'stable', diff --git a/features/css-easing-2.js b/tests/css-easing-2.js similarity index 94% rename from features/css-easing-2.js rename to tests/css-easing-2.js index e66ef883..0360facb 100644 --- a/features/css-easing-2.js +++ b/tests/css-easing-2.js @@ -1,5 +1,4 @@ export default { - id: 'css-easing-2', title: 'CSS Easing Functions Level 2', link: 'css-easing-2', status: 'experimental', diff --git a/features/css-env-1.js b/tests/css-env-1.js similarity index 85% rename from features/css-env-1.js rename to tests/css-env-1.js index 30aa8377..2f71cf47 100644 --- a/features/css-env-1.js +++ b/tests/css-env-1.js @@ -1,12 +1,15 @@ export default { - id: 'css-env-1', title: 'Environment Variables Level 1', - link: 'css-env-1', + links: { + dev: 'css-env-1', + }, status: 'experimental', values: { properties: ['width', 'padding'], 'env()': { - link: '#env-function', + links: { + dev: '#env-function', + }, tests: [ 'env(safe-area-inset-top)', 'env(safe-area-inset-top, 12px)', diff --git a/features/css-exclusions-1.js b/tests/css-exclusions-1.js similarity index 79% rename from features/css-exclusions-1.js rename to tests/css-exclusions-1.js index ebaaa75c..eb9eb4bf 100644 --- a/features/css-exclusions-1.js +++ b/tests/css-exclusions-1.js @@ -1,8 +1,9 @@ export default { - id: 'css-exclusions-1', title: 'CSS Exclusions Module Level 1', - link: 'css-exclusions-1', - specLink: 'css3-exclusions', + links: { + tr: 'css3-exclusions', + dev: 'css-exclusions-1', + }, status: 'experimental', properties: { 'wrap-flow': { diff --git a/features/css-flexbox-1.js b/tests/css-flexbox-1.js similarity index 99% rename from features/css-flexbox-1.js rename to tests/css-flexbox-1.js index 4946b4de..2534627b 100644 --- a/features/css-flexbox-1.js +++ b/tests/css-flexbox-1.js @@ -1,5 +1,4 @@ export default { - id: 'css-flexbox-1', title: 'CSS Flexible Box Layout Module Level 1', link: 'css-flexbox-1', mdn: 'Glossary/Flexbox', diff --git a/tests/css-font-loading-3.js b/tests/css-font-loading-3.js new file mode 100644 index 00000000..d180bbc3 --- /dev/null +++ b/tests/css-font-loading-3.js @@ -0,0 +1,114 @@ +export default { + title: 'CSS Font Loading Module Level 3', + link: 'css-font-loading-3', + status: 'stable', + interfaces: { + FontFace: { + link: '#fontface-interface', + links: { + mdnGroup: 'DOM', + }, + tests: [ + 'family', + 'style', + 'weight', + 'stretch', + 'unicodeRange', + 'variant', + 'featureSettings', + 'variationSettings', + 'display', + 'ascenderOverride', + 'descenderOverride', + 'lineGapOverride', + 'status', + 'load', + 'loaded', + 'features', + 'variations', + 'palettes', + ], + interface: function() { + return new FontFace('test', 'url("test.woff")'); + }, + }, + FontFaceFeatures: { + links: { + dev: '#fontfacefeatures', + mdnGroup: 'DOM', + }, + tests: ['FontFaceFeatures'], + interface: function() { + return new FontFace('test', 'url("test.woff")').features; + }, + }, + FontFaceVariationAxis: { + links: { + dev: '#fontfacevariationaxis', + mdnGroup: 'DOM', + }, + tests: ['name', 'axisTag', 'minimumValue', 'maximumValue', 'defaultValue'], + interface: function() { + return new FontFace('test', 'url("test.woff")').variations; + }, + }, + FontFacePalettes: { + links: { + dev: '#fontfacepalettes', + mdnGroup: 'DOM', + }, + tests: ['length'], + interface: function() { + return new FontFace('test', 'url("test.woff")').palettes; + }, + }, + FontFacePalette: { + links: { + dev: '#fontfacepalette', + mdnGroup: 'DOM', + }, + tests: ['FontFacePalette'], + }, + FontFaceSet: { + link: '#FontFaceSet-interface', + links: { + mdnGroup: 'DOM', + }, + tests: [ + 'add', + 'delete', + 'clear', + 'onloading', + 'onloadingdone', + 'onloadingerror', + 'load', + 'check', + 'ready', + 'status', + ], + interface: function() { + return document.fonts; + } + }, + FontFaceSetLoadEvent: { + links: { + dev: '#fontfacesetloadevent', + mdnGroup: 'DOM', + }, + tests: ['fontfaces'], + interface: function() { + return new FontFaceSetLoadEvent('loadingdone'); + }, + }, + Document: { + link: '#font-face-source', + links: { + mdnGroup: 'DOM', + }, + tests: ['fonts'], + interface: function() { + return document; + }, + } + }, +}; diff --git a/features/css-fonts-3.js b/tests/css-fonts-3.js similarity index 67% rename from features/css-fonts-3.js rename to tests/css-fonts-3.js index 531bf27a..52062504 100644 --- a/features/css-fonts-3.js +++ b/tests/css-fonts-3.js @@ -1,5 +1,4 @@ export default { - id: 'css-fonts-3', title: 'CSS Fonts Module Level 3', link: 'css-fonts-3', status: 'stable', @@ -145,65 +144,68 @@ export default { tests: ['normal', "'c2sc'", "'smcp' on", "'liga' off", "'swsh' 2", "'smcp', 'liga' off, 'swsh' 2"], }, }, - - atrules: { + descriptors: { + '@font-face/src': { + link: '#descdef-src', + tests: [ + 'url(http://example.com/fonts/Gentium.woff)', + 'url(ideal-sans-serif.woff2) format("woff2"), url(good-sans-serif.woff) format("woff"), url(basic-sans-serif.ttf) format("opentype")', + 'local(Gentium), url(Gentium.woff)', + ], + }, + '@font-face/font-family': { + link: '#descdef-font-family', + tests: ['Gentium'], + }, + '@font-face/font-style': { + link: '#font-prop-desc', + tests: ['normal', 'italic', 'oblique '], + }, + '@font-face/font-weight': { + link: '#font-prop-desc', + tests: ['normal', 'bold', '100', '200', '300', '400', '500', '600', '700', '800', '900'], + }, + '@font-face/font-stretch': { + link: '#font-prop-desc', + tests: [ + 'normal', + 'ultra-condensed', + 'extra-condensed', + 'condensed', + 'semi-condensed', + 'semi-expanded', + 'expanded', + 'extra-expanded', + 'ultra-expanded ', + ], + }, + '@font-face/font-feature-settings': { + link: '#font-rend-desc', + tests: ['normal', "'c2sc'", "'smcp' on", "'liga' off", "'smcp', 'swsh' 2"], + }, + '@font-face/font-variation-settings': { + link: '#font-rend-desc', + tests: ['normal', "'swsh' 2"], + }, + '@font-face/unicode-range': { + link: '#unicode-range-desc', + tests: ['U+416', 'U+0-7F', 'U+A5, U+4E00-9FFF', 'U+30??'], + }, + }, + '@rules': { '@font-face': { - isGroup: true, link: '#font-face-rule', - descriptors: { - src: { - link: '#descdef-src', - values: [ - 'url(http://example.com/fonts/Gentium.woff)', - 'url(ideal-sans-serif.woff2) format("woff2"), url(good-sans-serif.woff) format("woff"), url(basic-sans-serif.ttf) format("opentype")', - 'local(Gentium), url(Gentium.woff)' - ], - }, - 'font-family': { - link: '#descdef-font-family', - value: 'Gentium' - }, - 'font-style': { - link: '#font-prop-desc', - values: ['normal', 'italic', 'oblique'], - }, - 'font-weight': { - link: '#font-prop-desc', - values: ['normal', 'bold', '100', '200', '300', '400', '500', '600', '700', '800', '900'], - }, - 'font-stretch': { - link: '#font-prop-desc', - values: [ - 'normal', - 'ultra-condensed', - 'extra-condensed', - 'condensed', - 'semi-condensed', - 'semi-expanded', - 'expanded', - 'extra-expanded', - 'ultra-expanded ', - ], - }, - 'font-feature-settings': { - link: '#font-rend-desc', - values: ['normal', "'c2sc'", "'smcp' on", "'liga' off", "'smcp', 'swsh' 2"], - }, - 'font-variation-settings': { - link: '#font-rend-desc', - values: ['normal', "'swsh' 2"], - }, - 'unicode-range': { - link: '#unicode-range-desc', - values: ['U+416', 'U+0-7F', 'U+A5, U+4E00-9FFF', 'U+30??'], - }, - } + tests: "@font-face {\n font-family: foo;\n src: local('Arial');\n}", }, }, - globals: { + interfaces: { CSSFontFaceRule: { link: '#om-fontface', - mdnGroup: 'DOM', + links: { + mdnGroup: 'DOM', + }, + tests: ['style', 'cssText', 'parentRule', 'parentStyleSheet'], + required: '@font-face { font-family: "Foo"; src: local("Foo") }', }, }, }; diff --git a/tests/css-fonts-4.js b/tests/css-fonts-4.js new file mode 100644 index 00000000..58ef3fd3 --- /dev/null +++ b/tests/css-fonts-4.js @@ -0,0 +1,358 @@ +export default { + title: 'CSS Fonts Module Level 4', + link: 'css-fonts-4', + status: 'stable', + values: { + properties: ['font-family'], + 'system-ui': { + link: '#system-ui-def', + mdn: 'font-family', + tests: 'system-ui', + }, + 'emoji': { + link: '#emoji-def', + mdn: 'font-family', + tests: 'emoji', + }, + 'math': { + link: '#math-def', + mdn: 'font-family', + tests: 'math', + }, + 'generic(fangsong)': { + links: { + tr: '#fangsong-def', + dev: '#generic(fangsong)-def', + }, + mdn: 'font-family', + tests: 'generic(fangsong)', + }, + 'generic(kai)': { + links: { + dev: '#generic(kai)-def', + }, + mdn: 'font-family', + tests: 'generic(kai)', + }, + 'generic(khmer-mul)': { + links: { + dev: '#generic(khmer-mul)-def', + }, + mdn: 'font-family', + tests: 'generic(khmer-mul)', + }, + 'generic(nastaliq)': { + links: { + dev: '#generic(nastaliq)-def', + }, + mdn: 'font-family', + tests: 'generic(nastaliq)', + }, + 'ui-serif': { + link: '#ui-serif-def', + mdn: 'font-family', + tests: 'ui-serif', + }, + 'ui-sans-serif': { + link: '#ui-sans-serif-def', + mdn: 'font-family', + tests: 'ui-sans-serif', + }, + 'ui-monospace': { + link: '#ui-monospace-def', + mdn: 'font-family', + tests: 'ui-monospace', + }, + 'ui-rounded': { + link: '#ui-rounded-def', + mdn: 'font-family', + tests: 'ui-rounded', + }, + 'xxx-large': { + link: '#font-size-prop', + properties: ['font-size'], + tests: ['xxx-large'], + }, + 'math in font-size': { + link: '#font-size-prop', + properties: ['font-size'], + tests: ['math'], + }, + 'arbitrary font weights': { + link: '#font-weight-prop', + properties: ['font-weight'], + tests: ['1', '90', '750', '1000'], + }, + 'angle for oblique': { + link: '#font-style-prop', + properties: ['font-style'], + tests: ['oblique 15deg', 'oblique -15deg', 'oblique 0deg'], + }, + 'font-variant functions and keywords': { + link: '#font-variant-prop', + properties: ['font-variant'], + tests: [ + 'stylistic(salt01)', + 'historical-forms', + 'styleset(ss01)', + 'styleset(stacked-g, geometric-m)', + 'character-variant(cv02)', + 'character-variant(beta-3, gamma)', + 'swash(flowing)', + 'ornaments(leaves)', + 'annotation(blocky)', + 'text', + 'emoji', + 'unicode', + 'discretionary-ligatures character-variant(leo-B, leo-M, leo-N, leo-T, leo-U)', + ], + }, + }, + properties: { + 'font-variant-alternates': { + link: '#font-variant-alternates-prop', + tests: [ + 'normal', + 'stylistic(salt01)', + 'historical-forms', + 'styleset(ss01)', + 'styleset(stacked-g, geometric-m)', + 'character-variant(cv02)', + 'character-variant(beta-3, gamma)', + 'swash(flowing)', + 'ornaments(leaves)', + 'annotation(blocky)', + ], + }, + 'font-variant-emoji': { + link: '#font-variant-emoji-prop', + tests: [ + 'normal', + 'text', + 'emoji', + 'unicode', + ], + }, + 'font-variation-settings': { + link: '#font-variation-settings-def', + tests: [ + 'normal', + '"wght" 2', + '"wght" 2, "ital" 1.2', + ], + }, + 'font-feature-settings': { + link: '#font-feature-settings-prop', + tests: ['normal', "'swsh' 2"], + }, + 'font-language-override': { + link: '#font-language-override', + tests: ['normal', "'SRB'"], + }, + 'font-synthesis-weight': { + link: '#font-synthesis-weight', + tests: ['auto', 'none'], + }, + 'font-synthesis-style': { + link: '#font-synthesis-style', + tests: ['auto', 'none', 'oblique-only'], + }, + 'font-synthesis-small-caps': { + link: '#font-synthesis-small-caps', + tests: ['auto', 'none'], + }, + 'font-synthesis': { + link: '#font-synthesis', + tests: [ + 'small-caps', + 'weight small-caps', + 'style small-caps', + 'style small-caps weight', + ], + }, + 'font-optical-sizing': { + link: '#font-optical-sizing-def', + tests: ['none', 'auto'], + }, + 'font-palette': { + link: '#font-palette-prop', + tests: ['normal', 'light', 'dark', '--custom-palette'], + }, + }, + '@rules': { + '@font-feature-values': { + link: '#font-feature-values', + tests: [ + '@font-feature-values Jupiter Sans {\n @stylistic {\n some-style: 1;\n }\n}', + '@font-feature-values Jupiter Sans {\n @historical-forms {\n some-style: 1;\n }\n}', + '@font-feature-values Jupiter Sans {\n @styleset {\n some-style: 1;\n }\n}', + '@font-feature-values Jupiter Sans {\n @character-variant {\n some-style: 1;\n }\n}', + '@font-feature-values Jupiter Sans {\n @swash {\n some-style: 1;\n }\n}', + '@font-feature-values Jupiter Sans {\n @ornaments {\n some-style: 1;\n }\n}', + '@font-feature-values Jupiter Sans {\n @annotation {\n some-style: 1;\n }\n}', + '@font-feature-values Jupiter Sans, Foo Bar {\n @styleset {\n some-style: 1;\n }\n}', + '@font-feature-values Jupiter Sans {\n @styleset {\n some-style: 1 2 3;\n }\n}', + '@font-feature-values Jupiter Sans {\n @styleset {\n some-style: 1;\n }\n@styleset {\n other-style: 2;\n }\n}', + ], + }, + '@font-palette-values': { + link: '#font-palette-values', + tests: [ + '@font-palette-values --custom-palette {\n font-family: Handover Sans;\n base-palette: 3;\n}', + '@font-palette-values --custom-palette {\n font-family: Handover Sans;\n override-colors: 0 #000, 1 red;\n}', + ], + }, + }, + descriptors: { + '@font-face/ascent-override': { + link: '#descdef-font-face-ascent-override', + tests: ['normal', '125%'], + }, + '@font-face/descent-override': { + link: '#descdef-font-face-descent-override', + tests: ['normal', '125%'], + }, + '@font-face/line-gap-override': { + link: '#descdef-font-face-line-gap-override', + tests: ['normal', '90%'], + }, + '@font-face/font-named-instance': { + link: '#font-named-instance', + tests: ['auto', "'Grotesque'"], + }, + '@font-face/font-display': { + link: '#descdef-font-face-font-display', + tests: ['auto', 'block', 'swap', 'fallback', 'optional'], + }, + '@font-face/font-stretch': { + link: '#descdef-font-face-font-stretch', + tests: [ + 'auto', + 'condensed normal', + ], + }, + '@font-face/font-style': { + link: '#descdef-font-face-font-style', + tests: [ + 'auto', + 'left', + 'right', + '10deg', + '10deg 5deg', + ], + }, + '@font-face/font-variation-settings': { + link: '#descdef-font-face-font-variation-settings', + tests: [ + 'normal', + '"wght" 2', + '"wght" 2, "ital" 1.2', + ], + }, + '@font-face/font-weight': { + link: '#descdef-font-face-font-weight', + tests: [ + 'auto', + '100 300', + ], + }, + '@font-face/src': { + link: '#font-face-src-parsing', + tests: [ + 'url("foo") format("woff") tech(features-opentype)', + 'url("foo") format("woff") tech(features-graphite)', + 'url("foo") format("woff") tech(features-aat)', + 'url("foo") format("woff") tech(color-COLRv0)', + 'url("foo") format("woff") tech(color-COLRv1)', + 'url("foo") format("woff") tech(color-SVG)', + 'url("foo") format("woff") tech(color-sbix)', + 'url("foo") format("woff") tech(color-CBDT)', + 'url("foo") format("woff") tech(variations)', + 'url("foo") format("woff") tech(palettes)', + 'url("foo") format("woff") tech(incremental)', + 'url("foo") tech(color-COLRv1)', + 'url("foo") format("woff") tech(features-opentype, color-COLRv1)', + ], + }, + '@font-feature-values/font-display': { + link: '#font-display-font-feature-values', + tests: ['auto', 'block', 'swap', 'fallback', 'optional'], + }, + }, + interfaces: { + CSSRule: { + link: '#om-fontfeaturevalues', + links: { + mdnGroup: 'DOM', + }, + tests: ['FONT_FEATURE_VALUES_RULE'], + required: 'div { }', + interface: function(style) { + return style.sheet.cssRules[0]; + } + }, + CSSFontFeatureValuesRule: { + link: '#om-fontfeaturevalues', + links: { + mdnGroup: 'DOM', + }, + tests: [ + 'fontFamily', + 'annotation', + 'ornaments', + 'stylistic', + 'swash', + 'characterVariant', + 'styleset', + 'cssText', + 'parentRule', + 'parentStyleSheet', + ], + required: '@font-feature-values Font One { @styleset { nice-style: 12; } }', + interface: function(style) { + return style.sheet.cssRules[0]; + } + }, + CSSFontFeatureValuesMap: { + link: '#cssfontfeaturevaluesmap', + links: { + mdnGroup: 'DOM', + }, + tests: [ + 'has', + 'get', + 'set', + 'keys', + 'values', + 'entries', + 'forEach', + 'clear', + 'delete', + 'size', + ], + required: '@font-feature-values Font One { @styleset { nice-style: 12; } }', + interface: function(style) { + return style.sheet.cssRules[0].styleset; + }, + }, + CSSFontPaletteValuesRule: { + link: '#om-fontpalettevalues', + links: { + mdnGroup: 'DOM', + }, + tests: [ + 'name', + 'fontFamily', + 'basePalette', + 'overrideColors', + 'cssText', + 'parentRule', + 'parentStyleSheet', + ], + required: '@font-palette-values --identifier { font-family: foo; override-colors: 0 #00ffbb, 1 #007744; }', + interface: function(style) { + return style.sheet.cssRules[0]; + } + }, + }, +}; diff --git a/tests/css-fonts-5.js b/tests/css-fonts-5.js new file mode 100644 index 00000000..242a7af2 --- /dev/null +++ b/tests/css-fonts-5.js @@ -0,0 +1,97 @@ +export default { + title: 'CSS Fonts Module Level 5', + link: 'css-fonts-5', + status: 'experimental', + properties: { + 'font-size-adjust': { + link: '#font-size-adjust-prop', + tests: [ + 'ex-height 0.545', + 'ex-height from-font', + 'cap-height 0.545', + 'cap-height from-font', + 'ch-width 0.545', + 'ch-width from-font', + 'ic-width 0.545', + 'ic-width from-font', + 'ic-height 0.545', + 'ic-height from-font', + 'from-font', + ], + }, + }, + descriptors: { + '@font-face/ascent-override': { + link: '#descdef-font-face-ascent-override', + tests: ['normal 125%', '125% normal'], + }, + '@font-face/descent-override': { + link: '#descdef-font-face-descent-override', + tests: ['normal 125%', '125% normal'], + }, + '@font-face/font-size': { + link: '#font-size-desc', + tests: ['auto', '0.7', '0.7 0.9'], + }, + '@font-face/line-gap-override': { + link: '#descdef-font-face-line-gap-override', + tests: ['normal 125%', '125% normal'], + }, + '@font-face/size-adjust': { + link: '#size-adjust-desc', + tests: ['125%'], + }, + '@font-face/subscript-position-override': { + link: '#descdef-font-face-subscript-position-override', + tests: [ + 'normal', + 'from-font', + '125%', + 'normal normal', + 'normal 125%', + 'normal from-font', + '125% normal', + 'from-font normal', + ], + }, + '@font-face/subscript-size-override': { + link: '#descdef-font-face-subscript-size-override', + tests: [ + 'normal', + 'from-font', + '125%', + 'normal normal', + 'normal 125%', + 'normal from-font', + '125% normal', + 'from-font normal', + ], + }, + '@font-face/superscript-size-override': { + link: '#descdef-font-face-superscript-size-override', + tests: [ + 'normal', + 'from-font', + '125%', + 'normal normal', + 'normal 125%', + 'normal from-font', + '125% normal', + 'from-font normal', + ], + }, + '@font-face/superscript-position-override': { + link: '#descdef-font-face-superscript-position-override', + tests: [ + 'normal', + 'from-font', + '125%', + 'normal normal', + 'normal 125%', + 'normal from-font', + '125% normal', + 'from-font normal', + ], + }, + }, +}; diff --git a/features/css-forms-1.js b/tests/css-forms-1.js similarity index 98% rename from features/css-forms-1.js rename to tests/css-forms-1.js index e8b4283f..e70389c2 100644 --- a/features/css-forms-1.js +++ b/tests/css-forms-1.js @@ -1,5 +1,4 @@ export default { - id: 'css-forms-1', title: 'CSS Form Control Styling Level 1', link: 'css-forms-1', status: 'experimental', diff --git a/features/css-gaps-1.js b/tests/css-gaps-1.js similarity index 85% rename from features/css-gaps-1.js rename to tests/css-gaps-1.js index 089dbed2..c8bae931 100644 --- a/features/css-gaps-1.js +++ b/tests/css-gaps-1.js @@ -1,11 +1,16 @@ export default { - id: 'css-gaps-1', title: 'CSS Gap Decorations Module Level 1', - link: 'css-gaps-1', + links: { + dev: 'css-gaps-1', + tr: 'css-gaps-1', + }, status: 'experimental', properties: { 'column-rule-break': { - link: '#propdef-column-rule-break', + links: { + dev: '#propdef-column-rule-break', + tr: '#propdef-column-rule-break', + }, tests: [ 'intersection', 'spanning-item', @@ -13,7 +18,10 @@ export default { ], }, 'row-rule-break': { - link: '#propdef-row-rule-break', + links: { + dev: '#propdef-row-rule-break', + tr: '#propdef-row-rule-break', + }, tests: [ 'intersection', 'spanning-item', @@ -21,7 +29,10 @@ export default { ], }, 'rule-break': { - link: '#propdef-rule-break', + links: { + dev: '#propdef-rule-break', + tr: '#propdef-rule-break', + }, tests: [ 'intersection', 'spanning-item', @@ -29,36 +40,50 @@ export default { ], }, 'column-rule-outset': { - link: '#propdef-column-rule-outset', + links: { + dev: '#propdef-column-rule-outset', + tr: '#propdef-column-rule-outset', + }, tests: [ '10px', '5%', ], }, 'row-rule-outset': { - link: '#propdef-row-rule-outset', + links: { + dev: '#propdef-row-rule-outset', + tr: '#propdef-row-rule-outset', + }, tests: [ '10px', '5%', ], }, 'rule-outset': { - link: '#propdef-rule-outset', + links: { + dev: '#propdef-rule-outset', + tr: '#propdef-rule-outset', + }, tests: [ '10px', '5%', ], }, 'rule-paint-order': { - link: '#propdef-rule-paint-order', + links: { + dev: '#propdef-rule-paint-order', + tr: '#propdef-rule-paint-order', + }, tests: [ 'row-over-column', 'column-over-row', ], }, 'column-rule-color': { - link: '#propdef-column-rule-color', - dataType: 'color', + links: { + dev: '#propdef-column-rule-color', + tr: '#propdef-column-rule-color', + }, tests: [ 'red', 'rgb(0 0 0 / 0.5)', @@ -75,7 +100,10 @@ export default { ], }, 'row-rule-color': { - link: '#propdef-row-rule-color', + links: { + dev: '#propdef-row-rule-color', + tr: '#propdef-row-rule-color', + }, tests: [ 'red', 'rgb(0 0 0 / 0.5)', @@ -92,7 +120,10 @@ export default { ], }, 'rule-color': { - link: '#propdef-rule-color', + links: { + dev: '#propdef-rule-color', + tr: '#propdef-rule-color', + }, tests: [ 'red', 'rgb(0 0 0 / 0.5)', @@ -109,7 +140,10 @@ export default { ], }, 'column-rule-style': { - link: '#propdef-column-rule-style', + links: { + dev: '#propdef-column-rule-style', + tr: '#propdef-column-rule-style', + }, tests: [ 'solid', 'dotted', @@ -127,7 +161,10 @@ export default { ], }, 'row-rule-style': { - link: '#propdef-row-rule-style', + links: { + dev: '#propdef-row-rule-style', + tr: '#propdef-row-rule-style', + }, tests: [ 'solid', 'dotted', @@ -145,7 +182,10 @@ export default { ], }, 'rule-style': { - link: '#propdef-rule-style', + links: { + dev: '#propdef-rule-style', + tr: '#propdef-rule-style', + }, tests: [ 'solid', 'dotted', @@ -163,7 +203,10 @@ export default { ], }, 'column-rule-width': { - link: '#propdef-column-rule-width', + links: { + dev: '#propdef-column-rule-width', + tr: '#propdef-column-rule-width', + }, tests: [ '10px', '5%', @@ -183,7 +226,10 @@ export default { ], }, 'row-rule-width': { - link: '#propdef-row-rule-width', + links: { + dev: '#propdef-row-rule-width', + tr: '#propdef-row-rule-width', + }, tests: [ '10px', '5%', @@ -203,7 +249,10 @@ export default { ], }, 'rule-width': { - link: '#propdef-rule-width', + links: { + dev: '#propdef-rule-width', + tr: '#propdef-rule-width', + }, tests: [ '10px', '5%', @@ -223,7 +272,10 @@ export default { ], }, 'column-rule': { - link: '#propdef-column-rule', + links: { + dev: '#propdef-column-rule', + tr: '#propdef-column-rule', + }, tests: [ '10px solid red', '5% dotted rgb(0 0 0 / 0.5)', @@ -240,7 +292,10 @@ export default { ], }, 'row-rule': { - link: '#propdef-row-rule', + links: { + dev: '#propdef-row-rule', + tr: '#propdef-row-rule', + }, tests: [ '10px solid red', '5% dotted rgb(0 0 0 / 0.5)', @@ -257,7 +312,10 @@ export default { ], }, 'rule': { - link: '#propdef-rule', + links: { + dev: '#propdef-rule', + tr: '#propdef-rule', + }, tests: [ '10px solid red', '5% dotted rgb(0 0 0 / 0.5)', diff --git a/features/css-grid-1.js b/tests/css-grid-1.js similarity index 99% rename from features/css-grid-1.js rename to tests/css-grid-1.js index d3507bf1..b339093b 100644 --- a/features/css-grid-1.js +++ b/tests/css-grid-1.js @@ -1,5 +1,4 @@ export default { - id: 'css-grid-1', title: 'CSS Grid Layout Module Level 1', link: 'css-grid-1', mdn: 'Glossary/Grid', diff --git a/features/css-grid-2.js b/tests/css-grid-2.js similarity index 97% rename from features/css-grid-2.js rename to tests/css-grid-2.js index 7423c077..e2348744 100644 --- a/features/css-grid-2.js +++ b/tests/css-grid-2.js @@ -1,5 +1,4 @@ export default { - id: 'css-grid-2', title: 'CSS Grid Layout Module Level 2', link: 'css-grid-2', mdn: 'Glossary/Grid', diff --git a/features/css-grid-3.js b/tests/css-grid-3.js similarity index 77% rename from features/css-grid-3.js rename to tests/css-grid-3.js index 4816b1d2..856674b8 100644 --- a/features/css-grid-3.js +++ b/tests/css-grid-3.js @@ -1,11 +1,14 @@ export default { - id: 'css-grid-3', title: 'CSS Grid Layout Module Level 3', - link: 'css-grid-3', + links: { + dev: 'css-grid-3', + }, status: 'experimental', properties: { grid: { - link: '#masonry-layout', + links: { + dev: '#masonry-layout', + }, tests: [ 'masonry / masonry', 'masonry / repeat(4, 2ch)', @@ -15,15 +18,21 @@ export default { ], }, 'grid-template-columns': { - link: '#masonry-layout', + links: { + dev: '#masonry-layout', + }, tests: ['masonry', 'repeat(auto-fit, minmax(25ch, 1fr))'], }, 'grid-template-rows': { - link: '#masonry-layout', + links: { + dev: '#masonry-layout', + }, tests: ['masonry ', 'repeat(auto-fit, minmax(25ch, 1fr))'], }, 'masonry-auto-flow': { - link: '#masonry-auto-flow', + links: { + dev: '#masonry-auto-flow', + }, tests: [ 'pack', 'next', diff --git a/features/css-highlight-api-1.js b/tests/css-highlight-api-1.js similarity index 57% rename from features/css-highlight-api-1.js rename to tests/css-highlight-api-1.js index ffe381bb..82905ee7 100644 --- a/features/css-highlight-api-1.js +++ b/tests/css-highlight-api-1.js @@ -1,5 +1,4 @@ export default { - id: 'css-highlight-api-1', title: 'CSS Custom Highlight API Module Level 1', link: 'css-highlight-api-1', status: 'experimental', @@ -9,20 +8,25 @@ export default { tests: ['::highlight(example-highlight)'], }, }, - globals: { + interfaces: { CSS: { link: '#registration', - mdnGroup: 'DOM', - properties: ['highlights'], + links: { + mdnGroup: 'DOM', + }, + tests: ['highlights'], + interface: function() { + return CSS; + } }, Highlight: { link: '#creation', - mdnGroup: 'DOM', - members: [ + links: { + mdnGroup: 'DOM', + }, + tests: [ 'priority', 'type', - ], - methods: [ 'has', 'add', 'delete', @@ -32,6 +36,12 @@ export default { 'entries', 'forEach', ], + interface: function(style) { + var range = new Range(); + range.setStart(document.body, 10); + range.setEnd(document.body, 20); + return new Highlight(range); + } }, } }; diff --git a/tests/css-images-3.js b/tests/css-images-3.js new file mode 100644 index 00000000..747889f4 --- /dev/null +++ b/tests/css-images-3.js @@ -0,0 +1,103 @@ +export default { + title: 'CSS Images Module Level 3', + links: { + tr: 'css3-images', + dev: 'css-images-3', + }, + status: 'stable', + firstSnapshot: 2015, + values: { + properties: ['background-image', 'list-style-image', 'border-image-source', 'mask-image', 'mask-border-source', 'shape-outside', 'content'], + 'linear-gradient()': { + link: '#linear-gradients', + tests: [ + 'linear-gradient(white, black)', + 'linear-gradient(to right, white, black)', + 'linear-gradient(0, white, black)', + 'linear-gradient(45deg, white, black)', + 'linear-gradient(white 50%, black)', + 'linear-gradient(white 5px, black)', + 'linear-gradient(white, #f06, black)', + 'linear-gradient(currentColor, black)', + 'linear-gradient(red -50px, white calc(-25px + 50%), blue 100%)', + + // allow a single color stop with 0-1 positions + // https://github.com/w3c/csswg-drafts/issues/10092#issuecomment-2145860054 + 'linear-gradient(red)', + 'linear-gradient(red 0)', + 'linear-gradient(red 50px)', + 'linear-gradient(0, red)', + ], + }, + 'radial-gradient()': { + link: '#radial-gradients', + tests: [ + 'radial-gradient(white, black)', + 'radial-gradient(circle, white, black)', + 'radial-gradient(ellipse, white, black)', + 'radial-gradient(closest-corner, white, black)', + 'radial-gradient(circle closest-corner, white, black)', + 'radial-gradient(farthest-side, white, black)', + 'radial-gradient(circle farthest-side, white, black)', + 'radial-gradient(0, white, black)', + 'radial-gradient(50%, white, black)', + 'radial-gradient(60% 60%, white, black)' /*, + "radial-gradient(at 60% 60%, white, black)", + "radial-gradient(30% 30% at 20% 20%, white, black)", + "radial-gradient(5em circle at top left, yellow, blue)", + "radial-gradient(circle farthest-side at top left, white, black)"*/, + + // allow a single color stop with 0-1 positions + // https://github.com/w3c/csswg-drafts/issues/10092#issuecomment-2145860054 + 'radial-gradient(red)', + 'radial-gradient(red 0)', + 'radial-gradient(red 50px)', + 'radial-gradient(0%, red)', + 'radial-gradient(50% 60%, red)', + 'radial-gradient(circle, red)', + 'radial-gradient(circle closest-corner, red)', + ], + }, + 'repeating-linear-gradient()': { + link: '#repeating-gradients', + tests: [ + 'repeating-linear-gradient(red)', + 'repeating-linear-gradient(white, black)', + ], + }, + 'repeating-radial-gradient()': { + link: '#repeating-gradients', + tests: [ + 'repeating-radial-gradient(red)', + 'repeating-radial-gradient(white, black)', + ] + }, + }, + properties: { + 'object-fit': { + links: { + tr: '#object-fit', + dev: '#the-object-fit', + }, + tests: ['fill', 'contain', 'cover', 'none', 'scale-down'], + }, + 'object-position': { + links: { + tr: '#object-position', + dev: '#the-object-position', + }, + tests: ['50% 50%', 'center', 'top right', 'bottom 10px right 20px'], + }, + 'image-orientation': { + links: { + tr: '#image-orientation', + dev: '#the-image-orientation', + }, + tests: ['from-image', '0deg', '90deg', '45deg', '45deg flip', '1turn', '100grad', '2rad'], + }, + 'image-rendering': { + link: '#the-image-rendering', + tests: ['auto', 'smooth', 'high-quality', 'crisp-edges', 'pixelated'], + }, + }, +}; diff --git a/features/css-images-4.js b/tests/css-images-4.js similarity index 91% rename from features/css-images-4.js rename to tests/css-images-4.js index 889aab03..01e5c6e3 100644 --- a/features/css-images-4.js +++ b/tests/css-images-4.js @@ -1,12 +1,18 @@ export default { - id: 'css-images-4', title: 'CSS Images Module Level 4', link: 'css-images-4', status: 'experimental', values: { + properties: ['background-image', 'list-style-image', 'border-image-source', 'mask-image', 'mask-border-source', 'shape-outside', 'content'], + 'linear-gradient()': { + link: '#linear-gradients', + tests: ['linear-gradient(45deg, #f06 25%, yellow 25% 50%, #f06 50% 75%, yellow 75%)'], + }, 'linear-gradient() color interpolation': { link: '#color-interpolation', - dataType: 'image', + links: { + mdn : 'linear-gradient' + }, tests: [ 'linear-gradient(to right in lch, #A37, #595)', 'linear-gradient(in lch to right, #A37, #595)', @@ -36,13 +42,13 @@ export default { }, 'radial-gradient()': { link: '#radial-gradients', - dataType: 'image', tests: ['radial-gradient(center, red 0% 25%, blue 25% 75%, red 75% 100%)'], }, 'radial-gradient() color interpolation': { link: '#radial-color-interpolation', - dataType: 'image', - mdn: 'radial-gradient', + links: { + mdn: 'radial-gradient' + }, tests: [ 'radial-gradient(farthest-side at left bottom in lab, color(display-p3 0.918 0.2 0.161), #081)', 'radial-gradient(in lab farthest-side at left bottom, color(display-p3 0.918 0.2 0.161), #081)', @@ -59,7 +65,6 @@ export default { }, 'conic-gradient()': { link: '#conic-gradients', - dataType: 'image', tests: [ 'conic-gradient(white, black)', 'conic-gradient(from 0, white, black)', @@ -82,8 +87,9 @@ export default { }, 'conic-gradient() color interpolation': { link: '#conic-gradients', - dataType: 'image', - mdn: 'conic-gradient', + links: { + mdn: 'conic-gradient' + }, tests: [ 'conic-gradient(in lab, #f06, gold)', 'conic-gradient(in lab, #f06 0deg, gold 1turn)', @@ -104,7 +110,6 @@ export default { }, 'repeating-conic-gradient()': { link: '#repeating-gradients', - dataType: 'image', tests: [ 'repeating-conic-gradient(white, black)', 'repeating-conic-gradient(hsla(0, 0%, 100%, .2) 0deg 15deg, hsla(0, 0%, 100%, 0) 0deg 30deg)', @@ -112,7 +117,6 @@ export default { }, 'image()': { link: '#image-notation', - dataType: 'image', tests: [ "image('sprites.png#xywh=10,30,60,20')", "image('wavy.svg', 'wavy.png' , 'wavy.gif')", @@ -122,7 +126,6 @@ export default { }, 'image-set()': { link: '#image-set-notation', - dataType: 'image', tests: [ "image-set('foo.png' 1x, 'foo-2x.png' 2x, 'foo-print.png' 600dpi)", 'image-set(linear-gradient(green, green) 1x, url(foobar.png) 2x)', @@ -143,12 +146,10 @@ export default { }, 'element()': { link: '#element-notation', - dataType: 'image', tests: 'element(#foo)', }, 'cross-fade()': { link: '#cross-fade-function', - dataType: 'image', tests: [ 'cross-fade(url(a.png), url(b.png))', 'cross-fade(url(a.png) 50%, url(b.png))', @@ -178,11 +179,16 @@ export default { ], }, }, - globals: { + interfaces: { CSS: { link: '#elementsources', - mdnGroup: 'DOM', - properties: ['elementSources'], + links: { + mdnGroup: 'DOM', + }, + tests: ['elementSources'], + interface: function () { + return CSS; + }, }, }, }; diff --git a/features/css-images-5.js b/tests/css-images-5.js similarity index 76% rename from features/css-images-5.js rename to tests/css-images-5.js index 9a9a5e16..e24ef4d5 100644 --- a/features/css-images-5.js +++ b/tests/css-images-5.js @@ -1,11 +1,14 @@ export default { - id: 'css-images-5', title: 'CSS Images Module Level 5', - link: 'css-images-5', + links: { + dev: 'css-images-5', + }, status: 'experimental', properties: { 'object-view-box': { - link: '#the-object-view-box', + links: { + dev: '#the-object-view-box', + }, tests: [ 'none', 'inset(10% round 10% 40% 10% 40%)', diff --git a/features/css-inline-3.js b/tests/css-inline-3.js similarity index 67% rename from features/css-inline-3.js rename to tests/css-inline-3.js index 48a8d93d..3caa10a9 100644 --- a/features/css-inline-3.js +++ b/tests/css-inline-3.js @@ -1,11 +1,16 @@ export default { - id: 'css-inline-3', title: 'CSS Inline Layout Module Level 3', - link: 'css-inline-3', + links: { + dev: 'css-inline-3', + tr: 'css-inline-3', + }, status: 'experimental', properties: { 'alignment-baseline': { - link: '#alignment-baseline-property', + links: { + dev: '#alignment-baseline-property', + tr: '#alignment-baseline-property', + }, tests: [ 'baseline', 'text-bottom', @@ -18,7 +23,10 @@ export default { ], }, 'baseline-shift': { - link: '#baseline-shift-property', + links: { + dev: '#baseline-shift-property', + tr: '#baseline-shift-property', + }, tests: [ '5px', '10%', @@ -30,7 +38,10 @@ export default { ], }, 'baseline-source': { - link: '#baseline-source', + links: { + dev: '#baseline-source', + tr: '#baseline-source', + }, tests: [ 'auto', 'first', @@ -38,7 +49,10 @@ export default { ], }, 'dominant-baseline': { - link: '#dominant-baseline-property', + links: { + dev: '#dominant-baseline-property', + tr: '#dominant-baseline-property', + }, tests: [ 'auto', 'text-bottom', @@ -52,7 +66,10 @@ export default { ], }, 'initial-letter': { - link: '#sizing-drop-initials', + links: { + dev: '#sizing-drop-initials', + tr: '#sizing-drop-initials', + }, tests: [ 'normal', '1.4 3', @@ -62,7 +79,10 @@ export default { ], }, 'initial-letter-align': { - link: '#aligning-initial-letter', + links: { + dev: '#aligning-initial-letter', + tr: '#aligning-initial-letter', + }, tests: [ 'border-box', 'alphabetic', @@ -73,7 +93,10 @@ export default { ], }, 'initial-letter-wrap': { - link: '#initial-letter-wrapping', + links: { + dev: '#initial-letter-wrapping', + tr: '#initial-letter-wrapping', + }, tests: [ 'none', 'first', @@ -84,14 +107,20 @@ export default { ], }, 'inline-sizing': { - link: '#line-fill', + links: { + dev: '#line-fill', + tr: '#line-fill', + }, tests: [ 'normal', 'stretch', ], }, 'line-fit-edge': { - link: '#text-edges', + links: { + dev: '#text-edges', + tr: '#text-edges', + }, tests: [ 'leading', 'text', @@ -106,7 +135,10 @@ export default { ], }, 'text-box': { - link: '#text-box-shorthand', + links: { + dev: '#text-box-shorthand', + tr: '#text-box-shorthand', + }, tests: [ 'normal', 'none', @@ -129,7 +161,10 @@ export default { ], }, 'text-box-edge': { - link: '#text-box-edge', + links: { + dev: '#text-box-edge', + tr: '#text-box-edge', + }, tests: [ 'auto', 'text', @@ -144,11 +179,17 @@ export default { ], }, 'text-box-trim': { - link: '#text-box-trim', + links: { + dev: '#text-box-trim', + tr: '#text-box-trim', + }, tests: ['none', 'trim-start', 'trim-end', 'trim-both'], }, 'vertical-align': { - link: '#transverse-alignment', + links: { + dev: '#transverse-alignment', + tr: '#transverse-alignment', + }, tests: [ 'first', 'last', diff --git a/features/css-layout-api-1.js b/tests/css-layout-api-1.js similarity index 55% rename from features/css-layout-api-1.js rename to tests/css-layout-api-1.js index 60ae8052..f4ee9022 100644 --- a/features/css-layout-api-1.js +++ b/tests/css-layout-api-1.js @@ -1,5 +1,4 @@ export default { - id: 'css-layout-api-1', title: 'CSS Layout API Level 1', link: 'css-layout-api-1', group: 'houdini', @@ -10,13 +9,19 @@ export default { tests: 'layout(foo)', }, }, - globals: { + interfaces: { CSS: { link: '#layout-worklet', - properties: { - layoutWorklet: { - instanceof: 'Worklet', - } + tests: ['layoutWorklet'], + interface: function() { + return CSS; + }, + }, + Worklet: { + link: '#layout-worklet', + tests: ['addModule'], + interface: function() { + return CSS.layoutWorklet; }, }, }, diff --git a/features/css-line-grid-1.js b/tests/css-line-grid-1.js similarity index 94% rename from features/css-line-grid-1.js rename to tests/css-line-grid-1.js index 47000681..6625f460 100644 --- a/features/css-line-grid-1.js +++ b/tests/css-line-grid-1.js @@ -1,5 +1,4 @@ export default { - id: 'css-line-grid-1', title: 'CSS Line Grid Module Level 1', link: 'css-line-grid-1', status: 'experimental', diff --git a/features/css-link-params-1.js b/tests/css-link-params-1.js similarity index 72% rename from features/css-link-params-1.js rename to tests/css-link-params-1.js index d68f4970..bd99b73b 100644 --- a/features/css-link-params-1.js +++ b/tests/css-link-params-1.js @@ -1,11 +1,14 @@ export default { - id: 'css-link-params-1', title: 'CSS Linked Parameters', - link: 'css-link-params-1', + links: { + dev: 'css-link-params-1', + }, status: 'experimental', properties: { 'link-parameters': { - link: '#link-param-prop', + links: { + dev: '#link-param-prop', + }, tests: [ 'none', 'param(--foo)', @@ -18,7 +21,10 @@ export default { values: { properties: ['background-image'], 'url() with param()': { - link: '#setting-url', + links: { + tr: '#setting-url', + dev: '#setting-url' + }, tests: 'url("http://example.com/image.svg" param(--bg-color white))', } } diff --git a/features/css-lists-3.js b/tests/css-lists-3.js similarity index 99% rename from features/css-lists-3.js rename to tests/css-lists-3.js index 3395fdf7..19a02c4f 100644 --- a/features/css-lists-3.js +++ b/tests/css-lists-3.js @@ -1,5 +1,4 @@ export default { - id: 'css-lists-3', title: 'CSS Lists Module Level 3', link: 'css-lists-3', status: 'stable', diff --git a/features/css-logical-1.js b/tests/css-logical-1.js similarity index 93% rename from features/css-logical-1.js rename to tests/css-logical-1.js index 1797d78a..f56ad29c 100644 --- a/features/css-logical-1.js +++ b/tests/css-logical-1.js @@ -1,5 +1,4 @@ export default { - id: 'css-logical-1', title: 'CSS Logical Properties and Values Level 1', link: 'css-logical-1', status: 'stable', @@ -221,23 +220,31 @@ export default { tests: ['navy blue'], }, 'border-start-start-radius': { - link: '#border-radius-properties', - specLink: '#border-radius-shorthands', + links: { + tr: '#border-radius-shorthands', + dev: '#border-radius-properties', + }, tests: ['0', '50%', '250px 100px'], }, 'border-start-end-radius': { - link: '#border-radius-properties', - specLink: '#border-radius-shorthands', + links: { + tr: '#border-radius-shorthands', + dev: '#border-radius-properties', + }, tests: ['0', '50%', '250px 100px'], }, 'border-end-start-radius': { - link: '#border-radius-properties', - specLink: '#border-radius-shorthands', + links: { + tr: '#border-radius-shorthands', + dev: '#border-radius-properties', + }, tests: ['0', '50%', '250px 100px'], }, 'border-end-end-radius': { - link: '#border-radius-properties', - specLink: '#border-radius-shorthands', + links: { + tr: '#border-radius-shorthands', + dev: '#border-radius-properties', + }, tests: ['0', '50%', '250px 100px'], }, margin: { diff --git a/features/css-masking-1.js b/tests/css-masking-1.js similarity index 98% rename from features/css-masking-1.js rename to tests/css-masking-1.js index be118aa7..58707c71 100644 --- a/features/css-masking-1.js +++ b/tests/css-masking-1.js @@ -1,5 +1,4 @@ export default { - id: 'css-masking-1', title: 'CSS Masking Module Level 1', link: 'css-masking-1', group: 'fxtf', @@ -31,7 +30,6 @@ export default { }, 'mask-image': { link: '#the-mask-image', - dataTypes: ['image'], tests: [ 'none', 'linear-gradient(black 0%, transparent 100%)', diff --git a/tests/css-mixins-1.js b/tests/css-mixins-1.js new file mode 100644 index 00000000..dd9d7185 --- /dev/null +++ b/tests/css-mixins-1.js @@ -0,0 +1,33 @@ +export default { + title: 'CSS Functions and Mixins Module', + link: 'css-mixins-1', + status: 'experimental', + '@rules': { + '@function': { + links: { + dev: '#defining-custom-functions', + }, + tests: [ + '@function --negative (--value) {\n result: calc(-1 * var(--value));\n}', + '@function --negative-length (--value) returns {\n result: calc(-1 * var(--value));\n}', + '@function --negative-length (--value ) returns {\n result: calc(-1 * var(--value));\n}', + '@function --negative-number-or-percentage(--value type( | )) {\n result: calc(-1 * var(--value));\n}', + '@function --circle-area (--r) {\n result: calc(3.1415 * var(--r2));\n --r2: calc(var(--r) * var(--r));\n}', + '@function --multiply (--value) using (--used) {\n result: calc(var(--used) * var(--value));\n}', + '@function --multiply-length (--value) using (--used ) {\n result: calc(var(--used) * var(--value));\n}', + '@function --multiply-length (--value) using (--used : 1em) {\n result: calc(var(--used) * var(--value));\n}', + '@function --suitable-font-size() {\n result: 16px;\n @media (width > 1000px) {\n result: 20px;\n }\n}', + ], + }, + }, + interfaces: { + CSSFunctionRule: { + links: { + dev: '#cssfunctionrule', + mdnGroup: 'DOM', + }, + tests: ['cssRules', 'insertRule', 'deleteRule'], + required: 'div { }', + }, + }, +}; diff --git a/features/css-multicol-1.js b/tests/css-multicol-1.js similarity index 90% rename from features/css-multicol-1.js rename to tests/css-multicol-1.js index f9319568..c9097963 100644 --- a/features/css-multicol-1.js +++ b/tests/css-multicol-1.js @@ -1,5 +1,4 @@ export default { - id: 'css-multicol-1', title: 'CSS Multi-column Layout Module Level 1', link: 'css-multicol-1', status: 'stable', @@ -19,7 +18,6 @@ export default { }, 'column-rule-color': { link: '#crc', - dataType: 'color', tests: 'red', }, 'column-rule-style': { @@ -31,8 +29,10 @@ export default { tests: '1px', }, 'column-rule': { - link: '#cr', - specLink: '#column-rule', + links: { + tr: '#column-rule', + dev: '#cr', + }, tests: ['transparent', '1px solid black'], }, 'column-span': { diff --git a/features/css-multicol-2.js b/tests/css-multicol-2.js similarity index 96% rename from features/css-multicol-2.js rename to tests/css-multicol-2.js index e0a10591..df7b9f9c 100644 --- a/features/css-multicol-2.js +++ b/tests/css-multicol-2.js @@ -1,5 +1,4 @@ export default { - id: 'css-multicol-2', title: 'CSS Multi-column Layout Module Level 2', link: 'css-multicol-2', status: 'experimental', diff --git a/features/css-namespaces-3.js b/tests/css-namespaces-3.js similarity index 51% rename from features/css-namespaces-3.js rename to tests/css-namespaces-3.js index d57092bf..d6c0ae4b 100644 --- a/features/css-namespaces-3.js +++ b/tests/css-namespaces-3.js @@ -1,17 +1,14 @@ export default { - id: 'css-namespaces-3', title: 'CSS Namespaces Module Level 3', link: 'css-namespaces-3', status: 'stable', firstSnapshot: 2007, - atrules: { + '@rules': { '@namespace': { link: '#declaration', - contents: false, - preludeRequired: true, - preludes: [ - '"http://www.w3.org/1999/xhtml"', - 'svg "http://www.w3.org/2000/svg"', + tests: [ + "@namespace \"http://www.w3.org/1999/xhtml\";", + "@namespace svg \"http://www.w3.org/2000/svg\";", ], }, }, diff --git a/features/css-nav-1.js b/tests/css-nav-1.js similarity index 55% rename from features/css-nav-1.js rename to tests/css-nav-1.js index c1b20d19..3a0cc473 100644 --- a/features/css-nav-1.js +++ b/tests/css-nav-1.js @@ -1,5 +1,4 @@ export default { - id: 'css-nav-1', title: 'CSS Spatial Navigation Level 1', link: 'css-nav-1', status: 'experimental', @@ -17,22 +16,36 @@ export default { tests: ['normal', 'grid'], }, }, - globals: { - window: { + interfaces: { + Window: { link: '#high-level-api', - mdnGroup: 'DOM', - functions: ['navigate'], + links: { + mdnGroup: 'DOM', + }, + tests: ['navigate'], + interface: function() { + return window; + } }, Element: { link: '#low-level-api', - mdnGroup: 'DOM', - methods: ['getSpatialNavigationContainer', 'focusableAreas', 'spatialNavigationSearch'], + links: { + mdnGroup: 'DOM', + }, + tests: ['getSpatialNavigationContainer', 'focusableAreas', 'spatialNavigationSearch'], + interface: function() { + return document.body; + } }, NavigationEvent: { link: '#events-navigationevent', - mdnGroup: 'DOM', - extends: 'UIEvent', - properties: ['dir', 'relatedTarget'], + links: { + mdnGroup: 'DOM', + }, + tests: ['dir', 'relatedTarget'], + interface: function() { + return new NavigationEvent('navbeforefocus'); + } }, }, }; diff --git a/tests/css-nesting-1.js b/tests/css-nesting-1.js new file mode 100644 index 00000000..ae08c478 --- /dev/null +++ b/tests/css-nesting-1.js @@ -0,0 +1,15 @@ +export default { + title: 'CSS Nesting Module', + link: 'css-nesting-1', + status: 'experimental', + interfaces: { + CSSStyleRule: { + link: '#cssom-style', + links: { + mdnGroup: 'DOM', + }, + tests: ['cssRules', 'insertRule', 'deleteRule'], + required: 'div { }', + }, + }, +}; diff --git a/features/css-overflow-3.js b/tests/css-overflow-3.js similarity index 93% rename from features/css-overflow-3.js rename to tests/css-overflow-3.js index 7b4e427a..1b6ce6b4 100644 --- a/features/css-overflow-3.js +++ b/tests/css-overflow-3.js @@ -1,5 +1,4 @@ export default { - id: 'css-overflow-3', title: 'CSS Overflow Module Level 3', link: 'css-overflow-3', status: 'experimental', @@ -49,7 +48,10 @@ export default { tests: ['auto', 'discard'], }, 'scrollbar-gutter': { - link: '#scrollbar-gutter-property', + links: { + tr: 'scrollbar-gutter-property', + dev: '#scrollbar-gutter-property', + }, tests: ['auto', 'stable', 'both-edges stable', 'stable both-edges'], }, }, diff --git a/features/css-overflow-4.js b/tests/css-overflow-4.js similarity index 98% rename from features/css-overflow-4.js rename to tests/css-overflow-4.js index e4f625e1..c607b0ce 100644 --- a/features/css-overflow-4.js +++ b/tests/css-overflow-4.js @@ -1,5 +1,4 @@ export default { - id: 'css-overflow-4', title: 'CSS Overflow Module Level 4', link: 'css-overflow-4', status: 'experimental', diff --git a/features/css-overflow-5.js b/tests/css-overflow-5.js similarity index 97% rename from features/css-overflow-5.js rename to tests/css-overflow-5.js index 9b3dbb51..1d2b461b 100644 --- a/features/css-overflow-5.js +++ b/tests/css-overflow-5.js @@ -1,5 +1,4 @@ export default { - id: 'css-overflow-5', title: 'CSS Overflow Module Level 5', link: 'css-overflow-5', status: 'experimental', diff --git a/features/css-overscroll-1.js b/tests/css-overscroll-1.js similarity index 67% rename from features/css-overscroll-1.js rename to tests/css-overscroll-1.js index 03751610..feb63f9b 100644 --- a/features/css-overscroll-1.js +++ b/tests/css-overscroll-1.js @@ -1,11 +1,12 @@ export default { - id: 'css-overscroll-1', title: 'CSS Overscroll Behavior Module Level 1', link: 'css-overscroll-1', status: 'experimental', properties: { 'overscroll-behavior': { - link: '#overscroll-behavior-properties', + links: { + dev: '#overscroll-behavior-properties', + }, tests: [ 'contain', 'none', @@ -22,19 +23,27 @@ export default { ], }, 'overscroll-behavior-x': { - link: '#overscroll-behavior-longhands-physical', + links: { + dev: '#overscroll-behavior-longhands-physical', + }, tests: ['contain', 'none', 'auto'], }, 'overscroll-behavior-y': { - link: '#overscroll-behavior-longhands-physical', + links: { + dev: '#overscroll-behavior-longhands-physical', + }, tests: ['contain', 'none', 'auto'], }, 'overscroll-behavior-inline': { - link: '#overscroll-behavior-longhands-logical', + links: { + dev: '#overscroll-behavior-longhands-logical', + }, tests: ['contain', 'none', 'auto'], }, 'overscroll-behavior-block': { - link: '#overscroll-behavior-longhands-logical', + links: { + dev: '#overscroll-behavior-longhands-logical', + }, tests: ['contain', 'none', 'auto'], }, }, diff --git a/tests/css-page-3.js b/tests/css-page-3.js new file mode 100644 index 00000000..e706a8a7 --- /dev/null +++ b/tests/css-page-3.js @@ -0,0 +1,58 @@ +export default { + title: 'Paged Media Module Level 3', + link: 'css-page-3', + status: 'experimental', + properties: { + page: { + links: { + tr: '#using-named-pages', + dev: '#using-named-pagest', + }, + tests: ['auto', 'customName'], + }, + }, + '@rules': { + '@page': { + link: '#at-page-rule', + tests: ['@page :blank { margin: 2cm }', '@page customName { margin: 2cm }'], + }, + }, + descriptors: { + '@page/size': { + link: '#page-size-prop', + tests: [ + '4in 6in', + '4em 6em', + 'auto', + 'landscape', + 'portrait', + 'a3', + 'a4', + 'a5', + 'b4', + 'b5', + 'jis-b4', + 'jis-b5', + 'ledger', + 'legal', + 'letter', + 'a3 landscape', + 'a3 portrait', + 'landscape a3', + 'portrait a3', + ], + }, + '@page/page-orientation': { + link: '#page-orientation-prop', + tests: ['upright', 'rotate-left', 'rotate-right'], + }, + '@page/marks': { + link: '#marks', + tests: ['none', 'crop', 'cross', 'crop cross'], + }, + '@page/bleed': { + link: '#bleed', + tests: ['auto', '6pt'], + }, + }, +}; diff --git a/features/css-paint-api-1.js b/tests/css-paint-api-1.js similarity index 64% rename from features/css-paint-api-1.js rename to tests/css-paint-api-1.js index b4beb207..597eff1c 100644 --- a/features/css-paint-api-1.js +++ b/tests/css-paint-api-1.js @@ -1,5 +1,4 @@ export default { - id: 'css-paint-api-1', title: 'CSS Painting API Level 1', link: 'css-paint-api-1', group: 'houdini', @@ -14,13 +13,19 @@ export default { ], }, }, - globals: { + interfaces: { CSS: { link: '#paint-worklet', - properties: { - paintWorklet: { - instanceof: 'Worklet', - } + tests: ['paintWorklet'], + interface: function() { + return CSS; + }, + }, + Worklet: { + link: '#paint-worklet', + tests: ['addModule'], + interface: function() { + return CSS.paintWorklet; }, }, } diff --git a/features/css-position-3.js b/tests/css-position-3.js similarity index 97% rename from features/css-position-3.js rename to tests/css-position-3.js index b96da5ae..123afcec 100644 --- a/features/css-position-3.js +++ b/tests/css-position-3.js @@ -1,5 +1,4 @@ export default { - id: 'css-position-3', title: 'CSS Positioned Layout Module Level 3', link: 'css-position-3', status: 'stable', diff --git a/tests/css-properties-values-api-1.js b/tests/css-properties-values-api-1.js new file mode 100644 index 00000000..7766ea83 --- /dev/null +++ b/tests/css-properties-values-api-1.js @@ -0,0 +1,74 @@ +export default { + title: 'CSS Properties and Values API Level 1', + link: 'css-properties-values-api-1', + group: 'houdini', + status: 'experimental', + descriptors: { + '@property --foo/syntax': { + link: '#the-syntax-descriptor', + required: { + "'x | y'": { + descriptor: "inherits: false; initial-value: x", + }, + "''": { + descriptor: "inherits: false; initial-value: 100px", + }, + "''": { + descriptor: "inherits: false; initial-value: red", + }, + }, + tests: [ + "'x | y'", + "''", + "''", + ], + }, + '@property --foo/inherits': { + link: '#inherits-descriptor', + required: { + '*': { + descriptor: "syntax: ''; initial-value: red", + }, + }, + tests: ['true', 'false'], + }, + '@property --foo/initial-value': { + link: '#initial-value-descriptor', + required: { + '*': { + descriptor: "syntax: ''; inherits: false", + }, + }, + tests: ['blue', '#00f', 'rgb(0, 0, 255)'], + }, + }, + '@rules': { + '@property': { + link: '#at-property-rule', + tests: "@property --cool-color {\n syntax: '';\n inherits: true;\n initial-value: red;\n}", + }, + }, + interfaces: { + CSS: { + link: '#registering-custom-properties', + links: { + mdnGroup: 'DOM', + }, + tests: ['registerProperty'], + interface: function() { + return CSS; + }, + }, + CSSPropertyRule: { + link: '#the-css-property-rule-interface', + links: { + mdnGroup: 'DOM', + }, + tests: ['name', 'syntax', 'inherits', 'initialValue'], + required: "@property --foo { syntax: ''; inherits: true; initial-value: blue; }", + interface: function(style) { + return style.sheet.cssRules[0]; + }, + }, + }, +}; diff --git a/features/css-pseudo-4.js b/tests/css-pseudo-4.js similarity index 84% rename from features/css-pseudo-4.js rename to tests/css-pseudo-4.js index 123042e6..92d56bf9 100644 --- a/features/css-pseudo-4.js +++ b/tests/css-pseudo-4.js @@ -1,5 +1,4 @@ export default { - id: 'css-pseudo-4', title: 'CSS Pseudo-Elements Module Level 4', link: 'css-pseudo-4', status: 'experimental', @@ -17,7 +16,9 @@ export default { tests: ['::selection'], }, '::search-text': { - link: '#selectordef-search-text', + links: { + dev: '#selectordef-search-text', + }, tests: [ '::search-text', '::search-text:current', @@ -54,7 +55,9 @@ export default { tests: ['::file-selector-button'], }, '::details-content': { - link: '#details-content-pseudo', + links: { + dev: '#details-content-pseudo', + }, tests: [ '::details-content', '::details-content::first-letter', @@ -80,18 +83,26 @@ export default { ], } }, - globals: { + interfaces: { Element: { link: '#window-interface', - mdnGroup: 'DOM', - methods: ['pseudo'], + links: { + mdnGroup: 'DOM', + }, + tests: ['pseudo'], + interface: function() { + return document.body; + }, }, CSSPseudoElement: { link: '#CSSPseudoElement-interface', - mdnGroup: 'DOM', - extends: 'EventTarget', - members: ['type', 'element', 'parent'], - methods: ['pseudo'], + links: { + mdnGroup: 'DOM', + }, + tests: ['type', 'element', 'parent', 'pseudo'], + interface: function() { + return document.body.pseudo('::selection'); + }, }, }, }; diff --git a/tests/css-regions-1.js b/tests/css-regions-1.js new file mode 100644 index 00000000..ccecdd7a --- /dev/null +++ b/tests/css-regions-1.js @@ -0,0 +1,77 @@ +export default { + title: 'CSS Regions Module Level 1', + link: 'css-regions-1', + status: 'experimental', + properties: { + 'flow-from': { + link: '#flow-from', + tests: ['none', 'named-flow'], + }, + 'flow-into': { + link: '#the-flow-into-property', + tests: ['none', 'named-flow', 'named-flow element', 'named-flow content'], + }, + 'region-fragment': { + link: '#the-region-fragment-property', + tests: ['auto', 'break'], + }, + }, + interfaces: { + Document: { + link: '#the-namedflow-interface', + links: { + mdnGroup: 'DOM', + }, + tests: ['namedFlows'], + interface: function() { + return document; + } + }, + Element: { + link: '#the-region-interface', + links: { + mdnGroup: 'DOM', + }, + tests: ['regionOverset', 'getRegionFlowRanges'], + interface: function() { + return document.body; + } + }, + NamedFlowMap: { + links: { + dev: '#namedflowmap', + mdnGroup: 'DOM', + }, + tests: [ + 'has', + 'get', + 'set', + 'keys', + 'values', + 'entries', + 'forEach', + ], + interface: function() { + return document.namedFlows; + } + }, + NamedFlow: { + links: { + dev: '#namedflow', + mdnGroup: 'DOM', + }, + tests: [ + 'name', + 'overset', + 'getRegions', + 'firstEmptyRegionIndex', + 'getContent', + 'getRegionsByContent', + ], + required: 'div { flow-from: --named-flow; }', + interface: function() { + return document.namedFlows.get('--named-flow'); + } + }, + }, +}; diff --git a/features/css-rhythmic-1.js b/tests/css-rhythmic-1.js similarity index 97% rename from features/css-rhythmic-1.js rename to tests/css-rhythmic-1.js index 2ac97821..be5db7a8 100644 --- a/features/css-rhythmic-1.js +++ b/tests/css-rhythmic-1.js @@ -1,5 +1,4 @@ export default { - id: 'css-rhythmic-1', title: 'CSS Rhythmic Sizing', link: 'css-rhythm-1', status: 'experimental', diff --git a/features/css-ruby-1.js b/tests/css-ruby-1.js similarity index 97% rename from features/css-ruby-1.js rename to tests/css-ruby-1.js index 86cd7c0d..8e10de28 100644 --- a/features/css-ruby-1.js +++ b/tests/css-ruby-1.js @@ -1,5 +1,4 @@ export default { - id: 'css-ruby-1', title: 'CSS Ruby Layout Module Level 1', link: 'css-ruby-1', status: 'experimental', diff --git a/features/css-scoping-1.js b/tests/css-scoping-1.js similarity index 84% rename from features/css-scoping-1.js rename to tests/css-scoping-1.js index ae7de525..cd715837 100644 --- a/features/css-scoping-1.js +++ b/tests/css-scoping-1.js @@ -1,5 +1,4 @@ export default { - id: 'css-scoping-1', title: 'CSS Scoping Module Level 1', link: 'css-scoping-1', status: 'experimental', @@ -19,11 +18,15 @@ export default { tests: [':host-context(*)', ':host-context(.foo)'], }, '::slotted()': { - link: '#slotted-pseudo', + links: { + dev: '#slotted-pseudo', + }, tests: ['::slotted(*)', '::slotted(.foo)'], }, ':has-slotted': { - link: '#the-has-slotted-pseudo', + links: { + dev: '#the-has-slotted-pseudo', + }, tests: ':has-slotted', }, }, diff --git a/features/css-scroll-anchoring-1.js b/tests/css-scroll-anchoring-1.js similarity index 77% rename from features/css-scroll-anchoring-1.js rename to tests/css-scroll-anchoring-1.js index 0efdee07..c17dbd1b 100644 --- a/features/css-scroll-anchoring-1.js +++ b/tests/css-scroll-anchoring-1.js @@ -1,11 +1,12 @@ export default { - id: 'css-scroll-anchoring-1', title: 'CSS Scroll Anchoring Module Level 1', link: 'css-scroll-anchoring-1', status: 'experimental', properties: { 'overflow-anchor': { - link: '#exclusion-api', + links: { + dev: '#exclusion-api', + }, tests: ['none', 'auto'], }, }, diff --git a/features/css-scroll-snap-1.js b/tests/css-scroll-snap-1.js similarity index 99% rename from features/css-scroll-snap-1.js rename to tests/css-scroll-snap-1.js index 17937557..7fd929c5 100644 --- a/features/css-scroll-snap-1.js +++ b/tests/css-scroll-snap-1.js @@ -1,5 +1,4 @@ export default { - id: 'css-scroll-snap-1', title: 'CSS Scroll Snap Module Level 1', link: 'css-scroll-snap-1', status: 'stable', diff --git a/tests/css-scroll-snap-2.js b/tests/css-scroll-snap-2.js new file mode 100644 index 00000000..763e579f --- /dev/null +++ b/tests/css-scroll-snap-2.js @@ -0,0 +1,72 @@ +export default { + title: 'CSS Scroll Snap Module Level 2', + links: { + dev: 'css-scroll-snap-2', + }, + status: 'experimental', + properties: { + 'scroll-start-target': { + links: { + dev: '#scroll-start-target', + }, + tests: [ + 'none', + 'auto', + ], + }, + }, + selectors: { + ':snapped': { + links: { + dev: '#snapped', + }, + tests: ':snapped', + }, + ':snapped-x': { + links: { + dev: '#selectordef-snapped-x', + }, + tests: ':snapped-x', + }, + ':snapped-y': { + links: { + dev: '#selectordef-snapped-y', + }, + tests: ':snapped-y', + }, + ':snapped-inline': { + links: { + dev: '#selectordef-snapped-inline', + }, + tests: ':snapped-inline', + }, + ':snapped-block': { + links: { + dev: '#selectordef-snapped-block', + }, + tests: ':snapped-block', + }, + }, + interfaces: { + SnapEvent: { + links: { + dev: '#snap-events', + mdnGroup: 'DOM', + }, + tests: ['snapTargetBlock', 'snapTargetInline'], + interface: function() { + return new SnapEvent('scrollsnapchange'); + }, + }, + Element: { + link: '#interface-globaleventhandlers', + links: { + mdnGroup: 'DOM', + }, + tests: ['onsnapchanged', 'onsnapchanging'], + interface: function(style) { + return document.body; + }, + } + }, +}; diff --git a/features/css-scrollbars-1.js b/tests/css-scrollbars-1.js similarity index 92% rename from features/css-scrollbars-1.js rename to tests/css-scrollbars-1.js index 9666140d..827e637b 100644 --- a/features/css-scrollbars-1.js +++ b/tests/css-scrollbars-1.js @@ -1,5 +1,4 @@ export default { - id: 'css-scrollbars-1', title: 'CSS Scrollbars Module Level 1', link: 'css-scrollbars-1', status: 'stable', diff --git a/features/css-shadow-parts-1.js b/tests/css-shadow-parts-1.js similarity index 93% rename from features/css-shadow-parts-1.js rename to tests/css-shadow-parts-1.js index 22fc5687..1744f9f8 100644 --- a/features/css-shadow-parts-1.js +++ b/tests/css-shadow-parts-1.js @@ -1,5 +1,4 @@ export default { - id: 'css-shadow-parts-1', title: 'CSS Shadow Parts', link: 'css-shadow-parts-1', status: 'experimental', @@ -66,11 +65,16 @@ export default { ], }, }, - globals: { + interfaces: { Element: { link: '#idl', - mdnGroup: 'DOM', - members: ['part'], + links: { + mdnGroup: 'DOM', + }, + tests: ['part'], + interface: function() { + return document.body; + }, }, }, }; diff --git a/features/css-shapes-1.js b/tests/css-shapes-1.js similarity index 95% rename from features/css-shapes-1.js rename to tests/css-shapes-1.js index 0b81a306..dd4c1204 100644 --- a/features/css-shapes-1.js +++ b/tests/css-shapes-1.js @@ -1,12 +1,10 @@ export default { - id: 'css-shapes-1', title: 'CSS Shapes Module Level 1', link: 'css-shapes-1', status: 'stable', properties: { 'shape-outside': { link: '#shape-outside-property', - dataTypes: ['image', 'shape'], tests: [ 'none', 'inset(10% round 10% 40% 10% 40%)', diff --git a/features/css-shapes-2.js b/tests/css-shapes-2.js similarity index 76% rename from features/css-shapes-2.js rename to tests/css-shapes-2.js index 9942e43a..f1f92856 100644 --- a/features/css-shapes-2.js +++ b/tests/css-shapes-2.js @@ -1,11 +1,14 @@ export default { - id: 'css-shapes-2', title: 'CSS Shapes Module Level 2', - link: 'css-shapes-2', + links: { + dev: 'css-shapes-2', + }, status: 'experimental', properties: { 'shape-inside': { - link: '#shape-inside-property', + links: { + dev: '#shape-inside-property', + }, tests: [ 'auto', 'outside-shape', @@ -20,7 +23,9 @@ export default { ], }, 'shape-padding': { - link: '#shape-padding-property', + links: { + dev: '#shape-padding-property', + }, tests: ['0', '10px', '50%'], }, }, diff --git a/features/css-size-adjust-1.js b/tests/css-size-adjust-1.js similarity index 67% rename from features/css-size-adjust-1.js rename to tests/css-size-adjust-1.js index 50f88809..52257a1e 100644 --- a/features/css-size-adjust-1.js +++ b/tests/css-size-adjust-1.js @@ -1,11 +1,14 @@ export default { - id: 'css-size-adjust-1', title: 'CSS Mobile Text Size Adjustment Module Level 1', - link: 'css-size-adjust-1', + links: { + dev: 'css-size-adjust-1', + }, status: 'experimental', properties: { 'text-size-adjust': { - link: '#adjustment-control', + links: { + dev: '#adjustment-control', + }, tests: ['auto', 'none', '110%'], }, }, diff --git a/features/css-sizing-3.js b/tests/css-sizing-3.js similarity index 98% rename from features/css-sizing-3.js rename to tests/css-sizing-3.js index 55de732a..d0a0fef6 100644 --- a/features/css-sizing-3.js +++ b/tests/css-sizing-3.js @@ -1,5 +1,4 @@ export default { - id: 'css-sizing-3', title: 'CSS Box Sizing Module Level 3', link: 'css-sizing-3', status: 'stable', diff --git a/features/css-sizing-4.js b/tests/css-sizing-4.js similarity index 99% rename from features/css-sizing-4.js rename to tests/css-sizing-4.js index 0cef7c2e..7e89fef3 100644 --- a/features/css-sizing-4.js +++ b/tests/css-sizing-4.js @@ -1,5 +1,4 @@ export default { - id: 'css-sizing-4', title: 'CSS Box Sizing Module Level 4', link: 'css-sizing-4', status: 'experimental', diff --git a/features/css-speech-1.js b/tests/css-speech-1.js similarity index 99% rename from features/css-speech-1.js rename to tests/css-speech-1.js index e46911a8..68fe8ad8 100644 --- a/features/css-speech-1.js +++ b/tests/css-speech-1.js @@ -1,5 +1,4 @@ export default { - id: 'css-speech-1', title: 'CSS Speech Module Level 1', link: 'css-speech-1', status: 'stable', diff --git a/features/css-text-3.js b/tests/css-text-3.js similarity index 95% rename from features/css-text-3.js rename to tests/css-text-3.js index a3072710..50c46c3b 100644 --- a/features/css-text-3.js +++ b/tests/css-text-3.js @@ -1,11 +1,13 @@ export default { - id: 'css-text-3', title: 'CSS Text Module Level 3', link: 'css-text-3', status: 'stable', properties: { 'text-transform': { - link: '#text-transform-property', + links: { + tr: '#text-transform', + dev: '#text-transform-property', + }, tests: ['full-width', 'full-size-kana', 'capitalize full-width', 'capitalize full-width full-size-kana'], }, 'tab-size': { diff --git a/features/css-text-4.js b/tests/css-text-4.js similarity index 99% rename from features/css-text-4.js rename to tests/css-text-4.js index ac217c44..2f7fd0bb 100644 --- a/features/css-text-4.js +++ b/tests/css-text-4.js @@ -1,5 +1,4 @@ export default { - id: 'css-text-4', title: 'CSS Text Module Level 4', link: 'css-text-4', status: 'experimental', diff --git a/features/css-text-decor-3.js b/tests/css-text-decor-3.js similarity index 95% rename from features/css-text-decor-3.js rename to tests/css-text-decor-3.js index 5584f38e..0ee4edb5 100644 --- a/features/css-text-decor-3.js +++ b/tests/css-text-decor-3.js @@ -1,5 +1,4 @@ export default { - id: 'css-text-decor-3', title: 'CSS Text Decoration Module Level 3', link: 'css-text-decor-3', status: 'stable', @@ -14,8 +13,7 @@ export default { }, 'text-decoration-color': { link: '#text-decoration-color-property', - dataType: 'color', - tests: 'red', + tests: 'white', }, 'text-decoration-style': { link: '#text-decoration-style-property', diff --git a/features/css-text-decor-4.js b/tests/css-text-decor-4.js similarity index 81% rename from features/css-text-decor-4.js rename to tests/css-text-decor-4.js index d62a893b..58575e26 100644 --- a/features/css-text-decor-4.js +++ b/tests/css-text-decor-4.js @@ -1,20 +1,11 @@ export default { - id: 'css-text-decor-4', title: 'CSS Text Decoration Module Level 4', link: 'css-text-decor-4', status: 'experimental', properties: { 'text-decoration': { link: '#text-decoration-property', - tests: [ - 'underline solid blue 1px', - 'underline 0.3em limegreen', - 'underline wavy 2px blue', - ], - }, - 'text-decoration-inset': { - link: '#text-decoration-property', - tests: ['auto', '20px', '20px 1em'], + tests: ['underline solid blue 1px'], }, 'text-decoration-skip': { link: '#text-decoration-skip-property', @@ -65,11 +56,21 @@ export default { }, 'text-decoration-skip-spaces': { link: '#text-decoration-skip-spaces-property', - tests: ['none', 'all', 'start', 'end', 'start end'], + tests: [ + 'none', + 'all', + 'start', + 'end', + 'start end', + ], }, 'text-decoration-trim': { link: '#text-decoration-skip-inset-property', - tests: ['auto', '10px', '5px 10px'], + tests: [ + 'auto', + '10px', + '5px 10px', + ], }, 'text-underline-offset': { link: '#underline-offset', @@ -77,10 +78,18 @@ export default { }, 'text-underline-position': { link: '#text-underline-position-property', - tests: ['from-font', 'from-font left', 'from-font right', 'right from-font'], + tests: [ + 'from-font', + 'from-font left', + 'from-font right', + 'right from-font', + ], }, 'text-decoration-thickness': { - link: '#text-decoration-thickness-property', + links: { + tr: '#underline-offset', + dev: '#text-decoration-width-property', + }, tests: ['auto', 'from-font', '3px', '10%'], }, 'text-shadow': { diff --git a/features/css-transforms-1.js b/tests/css-transforms-1.js similarity index 97% rename from features/css-transforms-1.js rename to tests/css-transforms-1.js index 35c71005..811ae0c8 100644 --- a/features/css-transforms-1.js +++ b/tests/css-transforms-1.js @@ -1,5 +1,4 @@ export default { - id: 'css-transforms-1', title: 'CSS Transforms Module Level 1', link: 'css-transforms-1', status: 'stable', diff --git a/features/css-transforms-2.js b/tests/css-transforms-2.js similarity index 98% rename from features/css-transforms-2.js rename to tests/css-transforms-2.js index 698ac173..a7a500cd 100644 --- a/features/css-transforms-2.js +++ b/tests/css-transforms-2.js @@ -1,5 +1,4 @@ export default { - id: 'css-transforms-2', title: 'CSS Transforms Module Level 2', link: 'css-transforms-2', status: 'stable', diff --git a/features/css-transitions-1.js b/tests/css-transitions-1.js similarity index 75% rename from features/css-transitions-1.js rename to tests/css-transitions-1.js index c1bdf1d3..cb721b5d 100644 --- a/features/css-transitions-1.js +++ b/tests/css-transitions-1.js @@ -1,5 +1,4 @@ export default { - id: 'css-transitions-1', title: 'CSS Transitions', link: 'css-transitions-1', status: 'stable', @@ -37,17 +36,22 @@ export default { tests: '1s 2s width linear', }, }, - globals: { + interfaces: { TransitionEvent: { link: '#interface-transitionevent', mdnGroup: 'DOM', - extends: 'Event', - members: ['propertyName', 'elapsedTime', 'pseudoElement'], + tests: ['propertyName', 'elapsedTime', 'pseudoElement'], + interface: function() { + return new TransitionEvent('transitionend'); + }, }, - HTMLElement: { + Element: { link: '#interface-dom', mdnGroup: 'DOM', - members: ['ontransitionstart', 'ontransitionrun', 'ontransitionend', 'ontransitioncancel'], + tests: ['ontransitionstart', 'ontransitionrun', 'ontransitionend', 'ontransitioncancel'], + interface: function() { + return document.body; + }, }, }, }; diff --git a/tests/css-transitions-2.js b/tests/css-transitions-2.js new file mode 100644 index 00000000..b2a254fe --- /dev/null +++ b/tests/css-transitions-2.js @@ -0,0 +1,75 @@ +export default { + title: 'CSS Transitions 2', + links: { + dev: 'css-transitions-2', + }, + status: 'experimental', + '@rules': { + '@starting-style': { + links: { + dev: '#defining-before-change-style-the-starting-style-rule', + }, + tests: "@starting-style {\n h1 {\n background-color: red;\n }\n}", + }, + }, + properties: { + 'transition-behavior': { + link: '#transition-behavior-property', + tests: ['normal', 'allow-discrete'], + }, + }, + interfaces: { + CSSStartingStyleRule: { + links: { + dev: '#the-cssstartingstylerule-interface', + mdnGroup: 'DOM', + }, + tests: [ + 'cssRules', + 'addRule', + 'deleteRule', + ], + required: '@starting-style { h1 { background-color: red; } }', + }, + CSSTransition: { + links: { + dev: '#the-CSSTransition-interface', + mdnGroup: 'DOM', + }, + tests: [ + 'transitionProperty', + 'id', + 'effect', + 'timeline', + 'startTime', + 'currentTime', + 'playbackRate', + 'playState', + 'replaceState', + 'pending', + 'ready', + 'finished', + 'onfinish', + 'oncancel', + 'onremove', + 'cancel', + 'finish', + 'play', + 'pause', + 'updatePlaybackRate', + 'reverse', + 'persist', + 'commitStyles', + ], + interface: function() { + var div = document.createElement('div'); + document.body.append(div); + div.style.transition = 'opacity 100s'; + div.style.opacity = '0'; + getComputedStyle(div).opacity; + div.style.opacity = '1'; + return div.getAnimations()[0]; + } + }, + }, +}; diff --git a/tests/css-typed-om-1.js b/tests/css-typed-om-1.js new file mode 100644 index 00000000..43596ae2 --- /dev/null +++ b/tests/css-typed-om-1.js @@ -0,0 +1,638 @@ +export default { + title: 'CSS Typed OM Level 1', + links: { + dev: 'css-typed-om-1', + }, + group: 'houdini', + status: 'experimental', + interfaces: { + Element: { + links: { + dev: '#computed-stylepropertymapreadonly-objects', + mdnGroup: 'DOM', + }, + tests: ['computedStyleMap'], + interface: function() { + return document.body; + }, + }, + StylePropertyMapReadOnly: { + links: { + dev: '#the-stylepropertymap', + mdnGroup: 'DOM', + }, + tests: [ + 'get', + 'getAll', + 'has', + 'size', + ], + interface: function() { + return document.body.computedStyleMap(); + }, + }, + StylePropertyMap: { + links: { + dev: '#the-stylepropertymap', + mdnGroup: 'DOM', + }, + tests: [ + 'get', + 'getAll', + 'has', + 'size', + 'set', + 'append', + 'delete', + 'clear', + ], + interface: function() { + return document.body.attributeStyleMap; + }, + }, + CSSStyleValue: { + links: { + dev: '#stylevalue-objects', + mdnGroup: 'DOM', + }, + tests: ['parse', 'parseAll'], + interface: function() { + return CSSStyleValue + }, + }, + CSSUnparsedValue: { + links: { + dev: '#unparsedvalue-objects', + mdnGroup: 'DOM', + }, + tests: [ + 'length', + 'entries', + 'keys', + 'values', + 'forEach', + ], + required: ':root { --foo: 1px; }', + interface: function(style) { + return style.sheet.cssRules[0].styleMap.get('--foo'); + }, + }, + CSSVariableReferenceValue: { + links: { + dev: '#unparsedvalue-objects', + mdnGroup: 'DOM', + }, + tests: ['variable', 'fallback'], + required: ':root { --foo: 1px; margin-top: var(--foo, 2px); }', + interface: function(style) { + return style.sheet.cssRules[0].styleMap.get('margin-top')[0]; + }, + }, + CSSKeywordValue: { + links: { + dev: '#keywordvalue-objects', + mdnGroup: 'DOM', + }, + tests: ['value'], + required: ':root { display: grid; }', + interface: function(style) { + return style.sheet.cssRules[0].styleMap.get('display'); + }, + }, + CSSNumericValue: { + links: { + dev: '#numeric-value', + mdnGroup: 'DOM', + }, + tests: [ + 'add', + 'sub', + 'mul', + 'div', + 'min', + 'max', + 'equals', + 'to', + 'toSum', + 'type', + ], + required: ':root { opacity: 1; }', + interface: function(style) { + return style.sheet.cssRules[0].styleMap.get('opacity'); + }, + }, + CSSUnitValue: { + links: { + dev: '#simple-numeric', + mdnGroup: 'DOM', + }, + tests: [ + 'value', + 'unit', + 'add', + 'sub', + 'mul', + 'div', + 'min', + 'max', + 'equals', + 'to', + 'toSum', + 'type', + ], + required: ':root { margin-top: 1px; }', + interface: function(style) { + return style.sheet.cssRules[0].styleMap.get('margin-top'); + }, + }, + CSSMathValue: { + links: { + dev: '#complex-numeric', + mdnGroup: 'DOM', + }, + tests: [ + 'operator', + 'add', + 'sub', + 'mul', + 'div', + 'min', + 'max', + 'equals', + 'to', + 'toSum', + 'type', + ], + required: ':root { margin-top: calc(1vh + 10px); }', + interface: function(style) { + return style.sheet.cssRules[0].styleMap.get('margin-top'); + }, + }, + CSSMathSum: { + links: { + dev: '#cssmathsum', + mdnGroup: 'DOM', + }, + tests: [ + 'values', + 'operator', + 'add', + 'sub', + 'mul', + 'div', + 'min', + 'max', + 'equals', + 'to', + 'toSum', + 'type', + ], + required: ':root { margin-top: calc(1vh + 10px); }', + interface: function(style) { + return style.sheet.cssRules[0].styleMap.get('margin-top'); + }, + }, + CSSMathProduct: { + links: { + dev: '#cssmathproduct', + mdnGroup: 'DOM', + }, + tests: [ + 'values', + 'operator', + 'add', + 'sub', + 'mul', + 'div', + 'min', + 'max', + 'equals', + 'to', + 'toSum', + 'type', + ], + interface: function(style) { + return new CSSMathProduct(2, 3); + }, + }, + CSSMathNegate: { + links: { + dev: '#cssmathnegate', + mdnGroup: 'DOM', + }, + tests: [ + 'value', + 'operator', + 'add', + 'sub', + 'mul', + 'div', + 'min', + 'max', + 'equals', + 'to', + 'toSum', + 'type', + ], + interface: function(style) { + return new CSSMathNegate(3); + }, + }, + CSSMathInvert: { + links: { + dev: '#cssmathinvert', + mdnGroup: 'DOM', + }, + tests: [ + 'value', + 'operator', + 'add', + 'sub', + 'mul', + 'div', + 'min', + 'max', + 'equals', + 'to', + 'toSum', + 'type', + ], + interface: function(style) { + return new CSSMathInvert(3); + }, + }, + CSSMathMin: { + links: { + dev: '#cssmathmin', + mdnGroup: 'DOM', + }, + tests: [ + 'values', + 'operator', + 'add', + 'sub', + 'mul', + 'div', + 'min', + 'max', + 'equals', + 'to', + 'toSum', + 'type', + ], + required: ':root { margin-top: min(20vh, 100px); }', + interface: function(style) { + return style.sheet.cssRules[0].styleMap.get('margin-top'); + }, + }, + CSSMathMax: { + links: { + dev: '#cssmathmax', + mdnGroup: 'DOM', + }, + tests: [ + 'values', + 'operator', + 'add', + 'sub', + 'mul', + 'div', + 'min', + 'max', + 'equals', + 'to', + 'toSum', + 'type', + ], + required: ':root { margin-top: max(20vh, 400px); }', + interface: function(style) { + return style.sheet.cssRules[0].styleMap.get('margin-top'); + }, + }, + CSSMathClamp: { + links: { + dev: '#cssmathclamp', + mdnGroup: 'DOM', + }, + tests: [ + 'lower', + 'value', + 'upper', + 'operator', + 'add', + 'sub', + 'mul', + 'div', + 'min', + 'max', + 'equals', + 'to', + 'toSum', + 'type', + ], + required: ':root { margin-top: clamp(100px, 20vh, 400px); }', + interface: function(style) { + return style.sheet.cssRules[0].styleMap.get('margin-top'); + }, + }, + CSSNumericArray: { + links: { + dev: '##cssnumericarray', + mdnGroup: 'DOM', + }, + tests: ['length'], + required: ':root { margin-top: calc(1% + 10px); }', + interface: function(style) { + return style.sheet.cssRules[0].styleMap.get('margin-top').values; + }, + }, + CSS: { + links: { + dev: '#numeric-factory', + mdnGroup: 'DOM', + }, + tests: [ + 'number', + 'percent', + 'em', + 'ex', + 'ch', + 'ic', + 'rem', + 'lh', + 'rlh', + 'vw', + 'vh', + 'vi', + 'vb', + 'vmin', + 'vmax', + 'svw', + 'svh', + 'svi', + 'svb', + 'svmin', + 'svmax', + 'lvw', + 'lvh', + 'lvi', + 'lvb', + 'lvmin', + 'lvmax', + 'dvw', + 'dvh', + 'dvi', + 'dvb', + 'dvmin', + 'dvmax', + 'cqw', + 'cqh', + 'cqi', + 'cqb', + 'cqmin', + 'cqmax', + 'cm', + 'mm', + 'Q', + 'in', + 'pt', + 'pc', + 'px', + 'deg', + 'grad', + 'rad', + 'turn', + 's', + 'ms', + 'Hz', + 'kHz', + 'dpi', + 'dpcm', + 'dppx', + 'fr', + ], + interface: function() { + return CSS; + }, + }, + CSSTransformValue: { + links: { + dev: '#transformvalue-objects', + mdnGroup: 'DOM', + }, + tests: [ + 'length', + 'is2D', + 'toMatrix', + 'entries', + 'forEach', + 'keys', + 'values', + ], + required: ':root { transform: translate(10px, 20px); }', + interface: function(style) { + return style.sheet.cssRules[0].styleMap.get('transform'); + }, + }, + CSSTransformComponent: { + links: { + dev: '#csstransformcomponent', + mdnGroup: 'DOM', + }, + tests: ['is2D', 'toMatrix'], + required: ':root { transform: translate(10px, 20px); }', + interface: function(style) { + return style.sheet.cssRules[0].styleMap.get('transform')[0]; + }, + }, + CSSTranslate: { + links: { + dev: '#csstranslate', + mdnGroup: 'DOM', + }, + tests: ['x', 'y', 'z'], + required: ':root { transform: translate(10px, 20px); }', + interface: function(style) { + return style.sheet.cssRules[0].styleMap.get('transform')[0]; + }, + }, + CSSRotate: { + links: { + dev: '#cssrotate', + mdnGroup: 'DOM', + }, + tests: ['x', 'y', 'z', 'angle'], + required: ':root { transform: rotate(10deg); }', + interface: function(style) { + return style.sheet.cssRules[0].styleMap.get('transform')[0]; + }, + }, + CSSScale: { + links: { + dev: '#cssscale', + mdnGroup: 'DOM', + }, + tests: ['x', 'y', 'z'], + required: ':root { transform: scale(2); }', + interface: function(style) { + return style.sheet.cssRules[0].styleMap.get('transform')[0]; + }, + }, + CSSSkew: { + links: { + dev: '#cssskew', + mdnGroup: 'DOM', + }, + tests: ['ax', 'ay'], + required: ':root { transform: skew(10deg, 20deg); }', + interface: function(style) { + return style.sheet.cssRules[0].styleMap.get('transform')[0]; + }, + }, + CSSSkewX: { + links: { + dev: '#cssskewx', + mdnGroup: 'DOM', + }, + tests: ['ax'], + required: ':root { transform: skewX(10deg); }', + interface: function(style) { + return style.sheet.cssRules[0].styleMap.get('transform')[0]; + }, + }, + CSSSkewY: { + links: { + dev: '#cssskewy', + mdnGroup: 'DOM', + }, + tests: ['ay'], + required: ':root { transform: skewY(10deg); }', + interface: function(style) { + return style.sheet.cssRules[0].styleMap.get('transform')[0]; + }, + }, + CSSPerspective: { + links: { + dev: '#cssperspective', + mdnGroup: 'DOM', + }, + tests: ['length'], + required: ':root { transform: perspective(10px); }', + interface: function(style) { + return style.sheet.cssRules[0].styleMap.get('transform')[0]; + }, + }, + CSSMatrixComponent: { + links: { + dev: '#cssmatrixcomponent', + mdnGroup: 'DOM', + }, + tests: ['matrix'], + required: ':root { transform: matrix(1, 0, 0, 1, 0, 0); }', + interface: function(style) { + return style.sheet.cssRules[0].styleMap.get('transform')[0]; + }, + }, + CSSImageValue: { + links: { + dev: '#imagevalue-objects', + mdnGroup: 'DOM', + }, + tests: ['CSSImageValue'], + }, + CSSColorValue: { + links: { + dev: '#colorvalue-objects', + mdnGroup: 'DOM', + }, + }, + CSSRGB: { + links: { + dev: '#cssrgb', + mdnGroup: 'DOM', + }, + tests: ['r', 'g', 'b', 'alpha'], + required: ':root { color: rgb(255, 0, 0); }', + interface: function(style) { + return style.sheet.cssRules[0].styleMap.get('color'); + }, + }, + CSSHSL: { + links: { + dev: '#csshsl', + mdnGroup: 'DOM', + }, + tests: ['h', 's', 'l', 'alpha'], + required: ':root { color: hsl(0, 100%, 50%); }', + interface: function(style) { + return style.sheet.cssRules[0].styleMap.get('color'); + }, + }, + CSSHWB: { + links: { + dev: '#csshwb', + mdnGroup: 'DOM', + }, + tests: ['h', 'w', 'b', 'alpha'], + required: ':root { color: hwb(0, 0%, 0%); }', + interface: function(style) { + return style.sheet.cssRules[0].styleMap.get('color'); + }, + }, + CSSLab: { + links: { + dev: '#csslab', + mdnGroup: 'DOM', + }, + tests: ['l', 'a', 'b', 'alpha'], + required: ':root { color: lab(0%, 0%, 0%); }', + interface: function(style) { + return style.sheet.cssRules[0].styleMap.get('color'); + }, + }, + CSSLCH: { + links: { + dev: '#csslch', + mdnGroup: 'DOM', + }, + tests: ['l', 'c', 'h', 'alpha'], + required: ':root { color: lch(0%, 0%, 0deg); }', + interface: function(style) { + return style.sheet.cssRules[0].styleMap.get('color'); + }, + }, + CSSOKLab: { + links: { + dev: '#cssoklab', + mdnGroup: 'DOM', + }, + tests: ['l', 'a', 'b', 'alpha'], + required: ':root { color: oklab(0%, 0%, 0%); }', + interface: function(style) { + return style.sheet.cssRules[0].styleMap.get('color'); + }, + }, + CSSOKLCH: { + links: { + dev: '#cssoklch', + mdnGroup: 'DOM', + }, + tests: ['l', 'c', 'h', 'alpha'], + required: ':root { color: oklch(0%, 0%, 0deg); }', + interface: function(style) { + return style.sheet.cssRules[0].styleMap.get('color'); + }, + }, + CSSColor: { + links: { + dev: '#csscolor', + mdnGroup: 'DOM', + }, + tests: ['colorSpace', 'channels', 'alpha'], + required: ':root { color: color(display-p3 1 0 0); }', + interface: function(style) { + return style.sheet.cssRules[0].styleMap.get('color'); + }, + }, + }, +}; diff --git a/features/css-ui-3.js b/tests/css-ui-3.js similarity index 98% rename from features/css-ui-3.js rename to tests/css-ui-3.js index 2691cbc2..a8bf35e3 100644 --- a/features/css-ui-3.js +++ b/tests/css-ui-3.js @@ -1,5 +1,4 @@ export default { - id: 'css-ui-3', title: 'CSS Basic User Interface Module Level 3 (CSS3 UI)', link: 'css-ui-3', status: 'stable', diff --git a/features/css-ui-4.js b/tests/css-ui-4.js similarity index 99% rename from features/css-ui-4.js rename to tests/css-ui-4.js index 05787a53..a09cefbb 100644 --- a/features/css-ui-4.js +++ b/tests/css-ui-4.js @@ -1,5 +1,4 @@ export default { - id: 'css-ui-4', title: 'CSS Basic User Interface Module Level 4', link: 'css-ui-4', status: 'experimental', diff --git a/tests/css-values-3.js b/tests/css-values-3.js new file mode 100644 index 00000000..ed8fd337 --- /dev/null +++ b/tests/css-values-3.js @@ -0,0 +1,64 @@ +export default { + title: 'CSS Values and Units Module Level 3', + link: 'css-values-3', + status: 'stable', + firstSnapshot: 2015, + values: { + properties: ['width', 'padding'], + rem: { + link: '#rem', + mdn: 'length', + tests: '5rem', + }, + ch: { + link: '#ch', + mdn: 'length', + tests: '5ch', + }, + vw: { + link: '#vw', + mdn: 'length', + tests: '5vw', + }, + vh: { + link: '#vh', + mdn: 'length', + tests: '5vh', + }, + vmin: { + link: '#vmin', + mdn: 'length', + tests: '5vmin', + }, + vmax: { + link: '#vmax', + mdn: 'length', + tests: '5vmax', + }, + Q: { + link: '#Q', + mdn: 'length', + tests: '5Q', + }, + 'calc()': { + link: '#calc-notation', + tests: [ + 'calc(1px + 2px)', + 'calc(5px*2)', + 'calc(5px/2)', + 'calc(100%/3 - 2*1em - 2*1px)', + 'calc(5px - 10px)', + 'calc(1vw - 1px)', + 'calc(calc(100%))', + ], + }, + 'calc() in other functions': { + link: '#calc-notation', + properties: ['transform'], + tests: ['translateX(calc(1px + 2px))'], + } + }, + properties: { + transform: ['rotate(calc(15deg + 30deg))'], + }, +}; diff --git a/features/css-values-4.js b/tests/css-values-4.js similarity index 65% rename from features/css-values-4.js rename to tests/css-values-4.js index ae007c69..e6ac9f8d 100644 --- a/features/css-values-4.js +++ b/tests/css-values-4.js @@ -1,205 +1,182 @@ export default { - id: 'css-values-4', title: 'CSS Values and Units Module Level 4', link: 'css-values-4', status: 'experimental', - units: { + values: { + properties: ['width', 'padding'], ex: { link: '#ex', mdn: 'length', - dataType: 'length', + tests: '5ex', }, rex: { link: '#rex', mdn: 'length', - dataType: 'length', + tests: '5rex', }, cap: { link: '#cap', mdn: 'length', - dataType: 'length', + tests: '5cap', }, rcap: { link: '#rcap', mdn: 'length', - dataType: 'length', + tests: '5rcap', }, rch: { - link: '#rch', + links: { + tr: '#rch', + dev: '#rcap', + }, mdn: 'length', - dataType: 'length', + tests: '5rch', + }, + rch: { + links: { + tr: '#rch', + dev: '#rcap', + }, + mdn: 'length', + tests: '5rch', }, ic: { link: '#ic', mdn: 'length', - dataType: 'length', + tests: '5ic', }, ric: { link: '#ric', mdn: 'length', - dataType: 'length', + tests: '5ric', }, lh: { link: '#lh', mdn: 'length', - dataType: 'length', + tests: '5lh', }, rlh: { link: '#rlh', mdn: 'length', - dataType: 'length', - }, - vw: { - link: '#viewport-relative-lengths', - mdn: 'length', - dataType: 'length', - }, - svw: { - link: '#viewport-relative-lengths', - mdn: 'length', - dataType: 'length', - }, - lvw: { - link: '#viewport-relative-lengths', - mdn: 'length', - dataType: 'length', - }, - dvw: { - link: '#viewport-relative-lengths', - mdn: 'length', - dataType: 'length', - }, - vh: { - link: '#viewport-relative-lengths', - mdn: 'length', - dataType: 'length', + tests: '5rlh', }, svh: { link: '#viewport-relative-lengths', mdn: 'length', - dataType: 'length', + tests: '5svh', }, lvh: { link: '#viewport-relative-lengths', mdn: 'length', - dataType: 'length', + tests: '5lvh', }, dvh: { link: '#viewport-relative-lengths', mdn: 'length', - dataType: 'length', + tests: '5dvh', }, - vi: { - link: '#viewport-relative-lengths', - mdn: 'length', - dataType: 'length', - }, - svi: { - link: '#viewport-relative-lengths', - mdn: 'length', - dataType: 'length', - }, - lvi: { - link: '#viewport-relative-lengths', - mdn: 'length', - dataType: 'length', - }, - dvi: { + svw: { link: '#viewport-relative-lengths', mdn: 'length', - dataType: 'length', + tests: '5svw', }, - vb: { + lvw: { link: '#viewport-relative-lengths', mdn: 'length', - dataType: 'length', + tests: '5lvw', }, - svb: { + dvw: { link: '#viewport-relative-lengths', mdn: 'length', - dataType: 'length', + tests: '5dvw', }, - lvb: { + dvmin: { link: '#viewport-relative-lengths', mdn: 'length', - dataType: 'length', + tests: '5dvmin', }, - dvb: { + dvmax: { link: '#viewport-relative-lengths', mdn: 'length', - dataType: 'length', + tests: '5dvmax', }, - vmin: { + vb: { link: '#viewport-relative-lengths', mdn: 'length', - dataType: 'length', + tests: '5vb', }, - svmin: { + vi: { link: '#viewport-relative-lengths', mdn: 'length', - dataType: 'length', + tests: '5vi', }, - lvmin: { + svb: { link: '#viewport-relative-lengths', mdn: 'length', - dataType: 'length', + tests: '5dvb', }, - dvmin: { + dvi: { link: '#viewport-relative-lengths', mdn: 'length', - dataType: 'length', + tests: '5dvi', }, - vmax: { + lvd: { link: '#viewport-relative-lengths', mdn: 'length', - dataType: 'length', + tests: '5lvb', }, - svmax: { + lvi: { link: '#viewport-relative-lengths', mdn: 'length', - dataType: 'length', + tests: '5lvi', }, - lvmax: { + svb: { link: '#viewport-relative-lengths', mdn: 'length', - dataType: 'length', + tests: '5svb', }, - dvmax: { + svi: { link: '#viewport-relative-lengths', mdn: 'length', - dataType: 'length', + tests: '5svi', }, - }, - values: { - properties: ['width', 'padding'], 'min()': { - link: '#comp-func', - args: ['10 * (1vw + 1vh) / 2, 12px'], + links: { + tr: '#calc-notation', + dev: '#comp-func', + }, + tests: ['min(10 * (1vw + 1vh) / 2, 12px)'], }, 'max()': { - link: '#comp-func', - args: ['10 * (1vw + 1vh) / 2, 12px'], + links: { + tr: '#calc-notation', + dev: '#comp-func', + }, + tests: ['max(10 * (1vw + 1vh) / 2, 12px)'], }, 'clamp()': { - link: '#comp-func', - args: ['12px, 10 * (1vw + 1vh) / 2, 100px'], + links: { + tr: '#calc-notation', + dev: '#comp-func', + }, + tests: ['clamp(12px, 10 * (1vw + 1vh) / 2, 100px)'], }, 'calc()': { link: '#calc-func', - args: [ - '1rem * pow(1.5, -1)', - '1rem * (pow(e, pi) - pi)', - '-18px - sign(5px)*round(down, -18px*sign(5px), 5px)', - '-18px - round(to-zero, -18px, 5px)', + tests: [ + 'calc(1rem * pow(1.5, -1))', + 'calc(1rem * (pow(e, pi) - pi))', + 'calc(-18px - sign(5px)*round(down, -18px*sign(5px), 5px))', + 'calc(-18px - round(to-zero, -18px, 5px))', ], }, 'round()': { link: '#round-func', - args: [ - 'down, 5.5px, 5px', - 'up, 5.5px, 5px', - 'nearest, 5.5px, 5px', - 'to-zero, 5.5px, 5px', + tests: [ + 'round(down, 5.5px, 5px)', + 'round(up, 5.5px, 5px)', + 'round(nearest, 5.5px, 5px)', + 'round(to-zero, 5.5px, 5px)', ], }, 'mod()': { @@ -275,15 +252,24 @@ export default { tests: ['calc(1px * calc(pi))'], }, infinity: { - link: '#calc-error-constants', + links: { + tr: '#calc-error-constants', + dev: '#ccalc-error-constants', + }, tests: ['calc(1px * sin(tan(atan(infinity))))'], }, '-infinity': { - link: '#calc-error-constants', + links: { + tr: '#calc-error-constants', + dev: '#ccalc-error-constants', + }, tests: ['calc(1px * sin(tan(atan(-infinity))))'], }, NaN: { - link: '#calc-error-constants', + links: { + tr: '#calc-error-constants', + dev: '#ccalc-error-constants', + }, tests: ['calc(1px * sin(tan(atan(NaN))))'], }, }, diff --git a/features/css-values-5.js b/tests/css-values-5.js similarity index 99% rename from features/css-values-5.js rename to tests/css-values-5.js index d4431c90..ef0516d4 100644 --- a/features/css-values-5.js +++ b/tests/css-values-5.js @@ -38,7 +38,6 @@ const calc_size_default_tests = [ ] export default { - id: 'css-values-5', title: 'CSS Values and Units Module Level 5', link: 'css-values-5', status: 'experimental', diff --git a/features/css-variables-1.js b/tests/css-variables-1.js similarity index 62% rename from features/css-variables-1.js rename to tests/css-variables-1.js index 0438309a..2ac7b6df 100644 --- a/features/css-variables-1.js +++ b/tests/css-variables-1.js @@ -1,23 +1,21 @@ export default { - id: 'css-variables-1', title: 'CSS Custom Properties for Cascading Variables Module Level 1', link: 'css-variables-1', status: 'stable', firstSnapshot: 2018, - properties: { + declaration: { '--*': { link: '#defining-variables', - mdn: '--*', + tests: ['--foo: 2px'], }, - }, - values: { 'var(--*)': { link: '#using-variables', - mdn: 'var', - properties: ['width'], + mdn: '--*', tests: [ - 'var(--foo)', - 'var(--foo, fallback)' + 'width: var(--foo)', + 'width: var(--FOO)', + 'width: var(--foo, 4px)', + 'color: rgba(255, 255, 255, var(--foo, .2) )', ], }, }, diff --git a/tests/css-variables-2.js b/tests/css-variables-2.js new file mode 100644 index 00000000..de1ca62b --- /dev/null +++ b/tests/css-variables-2.js @@ -0,0 +1,18 @@ +export default { + title: 'CSS Custom Properties for Cascading Variables Module Level 2', + links: { + dev: 'css-variables-2', + }, + status: 'experimental', + declaration: { + 'Variable units': { + links: { + dev: '#variable-units', + mdn: '--*', + }, + tests: [ + 'margin-block: 1.5--bs', + ], + }, + }, +}; diff --git a/features/css-view-transitions-1.js b/tests/css-view-transitions-1.js similarity index 56% rename from features/css-view-transitions-1.js rename to tests/css-view-transitions-1.js index 46481864..66e3a4d2 100644 --- a/features/css-view-transitions-1.js +++ b/tests/css-view-transitions-1.js @@ -1,5 +1,4 @@ export default { - id: 'css-view-transitions-1', title: 'CSS View Transitions Module Level 1', link: 'css-view-transitions-1', status: 'stable', @@ -16,32 +15,53 @@ export default { }, '::view-transition-group()': { link: '#selectordef-view-transition-group-pt-name-selector', - tests: ['::view-transition-group(*)', '::view-transition-group(--foo)'], + tests: [ + '::view-transition-group(*)', + '::view-transition-group(--foo)', + ], }, '::view-transition-image-pair()': { link: '#selectordef-view-transition-image-pair-pt-name-selector', - tests: ['::view-transition-image-pair(*)', '::view-transition-image-pair(--foo)'], + tests: [ + '::view-transition-image-pair(*)', + '::view-transition-image-pair(--foo)', + ], }, '::view-transition-new()': { link: '#selectordef-view-transition-new-pt-name-selector', - tests: ['::view-transition-new(*)', '::view-transition-new(--foo)'], + tests: [ + '::view-transition-new(*)', + '::view-transition-new(--foo)', + ], }, '::view-transition-old()': { link: '#selectordef-view-transition-old-pt-name-selector', - tests: ['::view-transition-old(*)', '::view-transition-old(--foo)'], + tests: [ + '::view-transition-old(*)', + '::view-transition-old(--foo)', + ], }, }, - globals: { - document: { + interfaces: { + Document: { link: '#additions-to-document-api', - mdnGroup: 'DOM', - functions: ['startViewTransition'], + links: { + mdnGroup: 'DOM', + }, + tests: ['startViewTransition'], + interface: function() { + return document; + }, }, ViewTransition: { link: '#the-domtransition-interface', - mdnGroup: 'DOM', - members: ['updateCallbackDone', 'ready', 'finished'], - methods: ['skipTransition'], + links: { + mdnGroup: 'DOM', + }, + tests: ['updateCallbackDone', 'ready', 'finished', 'skipTransition'], + interface: function() { + return ViewTransition.prototype; + }, }, }, }; diff --git a/features/css-view-transitions-2.js b/tests/css-view-transitions-2.js similarity index 88% rename from features/css-view-transitions-2.js rename to tests/css-view-transitions-2.js index a50928fb..d3bed075 100644 --- a/features/css-view-transitions-2.js +++ b/tests/css-view-transitions-2.js @@ -1,5 +1,4 @@ export default { - id: 'css-view-transitions-2', title: 'CSS View Transitions Module Level 2', link: 'css-view-transitions-2', status: 'experimental', @@ -100,7 +99,7 @@ export default { ], }, }, - atrules: { + '@rules': { '@view-transition': { link: '#view-transition-rule', tests: [ @@ -112,17 +111,33 @@ export default { ], }, }, - globals: { + interfaces: { CSSViewTransitionRule: { link: '#navgation-behavior-rule-interface', - mdnGroup: 'DOM', - extends: 'CSSRule', - members: ['navigation', 'types'], + links: { + mdnGroup: 'DOM', + }, + tests: [ + 'navigation', + 'types', + 'cssText', + 'parentRule', + 'parentStyleSheet', + ], + required: '@view-transition { navigation: auto; }', + interface: function(style) { + return style.sheet.cssRules[0]; + }, }, ViewTransition: { link: '#view-transitions-extension-types', - mdnGroup: 'DOM', - members: ['types'], + links: { + mdnGroup: 'DOM', + }, + tests: ['types'], + interface: function() { + return ViewTransition.prototype; + }, }, }, }; diff --git a/tests/css-viewport-1.js b/tests/css-viewport-1.js new file mode 100644 index 00000000..bece98ab --- /dev/null +++ b/tests/css-viewport-1.js @@ -0,0 +1,26 @@ +export default { + title: 'CSS Viewport Module Level 1', + links: { + tr: 'css-viewport-1', + dev: 'css-viewport', + }, + status: 'experimental', + properties: { + 'zoom': { + link: '#zoom-property', + tests: ['0', '1', '1.5', '110%'], + }, + }, + interfaces: { + Viewport: { + links: { + dev: '#the-viewport-interface', + mdnGroup: 'DOM', + }, + tests: ['segments'], + interface: function() { + return window.viewport; + }, + }, + }, +}; diff --git a/features/css-will-change-1.js b/tests/css-will-change-1.js similarity index 90% rename from features/css-will-change-1.js rename to tests/css-will-change-1.js index d31a7c4a..bb59c20f 100644 --- a/features/css-will-change-1.js +++ b/tests/css-will-change-1.js @@ -1,5 +1,4 @@ export default { - id: 'css-will-change-1', title: 'CSS Will Change Module Level 1', link: 'css-will-change-1', status: 'stable', diff --git a/features/css-writing-modes-3.js b/tests/css-writing-modes-3.js similarity index 95% rename from features/css-writing-modes-3.js rename to tests/css-writing-modes-3.js index 149472f5..6bc6ef50 100644 --- a/features/css-writing-modes-3.js +++ b/tests/css-writing-modes-3.js @@ -1,5 +1,4 @@ export default { - id: 'css-writing-modes-3', title: 'CSS Writing Modes Level 3', link: 'css-writing-modes-3', status: 'stable', diff --git a/features/css-writing-modes-4.js b/tests/css-writing-modes-4.js similarity index 91% rename from features/css-writing-modes-4.js rename to tests/css-writing-modes-4.js index fc3261f1..d8522795 100644 --- a/features/css-writing-modes-4.js +++ b/tests/css-writing-modes-4.js @@ -1,5 +1,4 @@ export default { - id: 'css-writing-modes-4', title: 'CSS Writing Modes Level 4', link: 'css-writing-modes-4', status: 'experimental', diff --git a/features/css2-cascade.js b/tests/css2-cascade.js similarity index 64% rename from features/css2-cascade.js rename to tests/css2-cascade.js index c0fd386c..1ee77814 100644 --- a/features/css2-cascade.js +++ b/tests/css2-cascade.js @@ -1,14 +1,17 @@ export default { - id: 'css2-cascade', title: 'CSS 2 Assigning property values, Cascading, and Inheritance', - link: 'css2/', - specLink: 'CSS22/cascade.html', + links: { + tr: 'CSS22/cascade.html', + dev: 'css2/', + }, status: 'stable', - version: 2.2, + firstSnapshot: 2.2, + lastSnapshot: 2.2, values: { properties: ['color', 'border-color'], inherit: { link: '#value-def-inherit', + tests: 'inherit', }, }, }; diff --git a/features/css2-colors.js b/tests/css2-colors.js similarity index 87% rename from features/css2-colors.js rename to tests/css2-colors.js index 0d2ca952..55541aab 100644 --- a/features/css2-colors.js +++ b/tests/css2-colors.js @@ -1,10 +1,12 @@ export default { - id: 'css2-colors', title: 'CSS 2 Colors and Backgrounds', - link: 'css2/', - specLink: 'CSS22/colors.html', + links: { + tr: 'CSS22/colors.html', + dev: 'css2/', + }, status: 'stable', - version: 2.2, + firstSnapshot: 2.2, + lastSnapshot: 2.2, properties: { 'background-attachment': { link: '#propdef-background-attachment', @@ -12,13 +14,11 @@ export default { }, 'background-color': { link: '#propdef-background-color', - dataType: 'color', tests: ['black', '#00f', '#000000', 'rgb(255, 255, 255)', 'rgb(100%, 50%, 50%)', 'transparent'], }, 'background-image': { link: '#propdef-background-image', - dataType: 'image', - tests: ['none', "url('image.png')"], + tests: ['none', "url('image.png')", 'url(image.png)'], }, 'background-position': { link: '#propdef-background-position', diff --git a/features/css2-fonts.js b/tests/css2-fonts.js similarity index 93% rename from features/css2-fonts.js rename to tests/css2-fonts.js index 57831762..ac9d3dcb 100644 --- a/features/css2-fonts.js +++ b/tests/css2-fonts.js @@ -1,10 +1,12 @@ export default { - id: 'css2-fonts', title: 'CSS 2 Fonts', - link: 'css2/', - specLink: 'CSS22/fonts.html', + links: { + tr: 'CSS22/fonts.html', + dev: 'css2/', + }, status: 'stable', - version: 2.2, + firstSnapshot: 2.2, + lastSnapshot: 2.2, properties: { 'font-family': { link: '#font-family-prop', diff --git a/features/css2-generate.js b/tests/css2-generate.js similarity index 89% rename from features/css2-generate.js rename to tests/css2-generate.js index 9aeccbe5..c1d8ef42 100644 --- a/features/css2-generate.js +++ b/tests/css2-generate.js @@ -1,15 +1,18 @@ export default { - id: 'css2-generate', title: 'CSS 2 Generated Content, Automatic Numbering, and Lists', - link: 'css2/', - specLink: 'CSS22/generate.html', + links: { + tr: 'CSS22/generate.html', + dev: 'css2/', + }, status: 'stable', - version: 2.2, + firstSnapshot: 2.2, + lastSnapshot: 2.2, properties: { content: { - link: '#content①', - specLink: '#content', - dataTypes: ['image', 'string'], + links: { + tr: '#content', + dev: '#content①', + }, tests: [ 'normal', 'none', @@ -35,7 +38,6 @@ export default { }, 'list-style-image': { link: '#propdef-list-style-image', - dataTypes: ['image'], tests: ['none', 'url(image.png)'], }, 'list-style-position': { @@ -81,9 +83,11 @@ export default { selectors: { ':before': { link: '#before-after-content', + tests: ':before', }, ':after': { link: '#before-after-content', + tests: ':after', }, }, }; diff --git a/features/css2-media.js b/tests/css2-media.js similarity index 59% rename from features/css2-media.js rename to tests/css2-media.js index 075262ed..f7d50f67 100644 --- a/features/css2-media.js +++ b/tests/css2-media.js @@ -1,11 +1,13 @@ export default { - id: 'css2-media', title: 'CSS 2 Media types', - link: 'css2/', - specLink: 'CSS22/media.html', + links: { + tr: 'CSS22/media.html', + dev: 'css2/', + }, status: 'stable', - version: 2.2, - mediaqueries: { + firstSnapshot: 2.2, + lastSnapshot: 2.2, + 'Media queries': { Syntax: { link: '#at-media-rule', tests: ['all', 'print', 'screen', 'print, screen'], diff --git a/tests/css2-page.js b/tests/css2-page.js new file mode 100644 index 00000000..ffeed1aa --- /dev/null +++ b/tests/css2-page.js @@ -0,0 +1,65 @@ +export default { + title: 'CSS 2 Paged Media', + links: { + tr: 'CSS22/page.html', + dev: 'css2/', + }, + status: 'stable', + firstSnapshot: 2.2, + lastSnapshot: 2.2, + '@rules': { + '@page': { + link: '#page-box', + tests: [ + '@page { margin: 2cm; }', + '@page :left { margin: 2cm; }', + '@page :right { margin: 2cm; }', + '@page :first { margin: 2cm; }', + ], + }, + }, + descriptors: { + '@page/margin': { + link: '#page-box', + tests: ['2cm', '4%', 'auto'], + }, + '@page/margin-top': { + link: '#page-box', + tests: ['2cm', '4%', 'auto'], + }, + '@page/margin-right': { + link: '#page-box', + tests: ['2cm', '4%', 'auto'], + }, + '@page/margin-bottom': { + link: '#page-box', + tests: ['2cm', '4%', 'auto'], + }, + '@page/margin-left': { + link: '#page-box', + tests: ['2cm', '4%', 'auto'], + }, + }, + properties: { + orphans: { + link: '#break-inside', + tests: ['1', '2'], + }, + 'page-break-after': { + link: '#page-break-props', + tests: ['auto', 'always', 'avoid', 'left', 'right'], + }, + 'page-break-before': { + link: '#page-break-props', + tests: ['auto', 'always', 'avoid', 'left', 'right'], + }, + 'page-break-inside': { + link: '#page-break-props', + tests: ['auto', 'avoid'], + }, + widows: { + link: '#break-inside', + tests: ['1', '2'], + }, + }, +}; diff --git a/features/css2-selectors.js b/tests/css2-selectors.js similarity index 91% rename from features/css2-selectors.js rename to tests/css2-selectors.js index c5eb9d13..eb261811 100644 --- a/features/css2-selectors.js +++ b/tests/css2-selectors.js @@ -1,10 +1,12 @@ export default { - id: 'css2-selectors', title: 'CSS 2 Selectors', - link: 'css2/', - specLink: 'CSS22/selector.html', + links: { + tr: 'CSS22/selector.html', + dev: 'css2/', + }, status: 'stable', - version: 2.2, + firstSnapshot: 2.2, + lastSnapshot: 2.2, selectors: { 'Universal selector': { link: '#universal-selector', @@ -22,8 +24,7 @@ export default { link: '#child-selectors', tests: 'div > p', }, - 'e + f': { - title: 'Adjacent sibling combinator', + 'Adjacent sibling selector': { link: '#adjacent-selectors', tests: 'div + p', }, diff --git a/features/css2-tables.js b/tests/css2-tables.js similarity index 84% rename from features/css2-tables.js rename to tests/css2-tables.js index 20e02272..e0bc97e6 100644 --- a/features/css2-tables.js +++ b/tests/css2-tables.js @@ -1,10 +1,12 @@ export default { - id: 'css2-tables', title: 'CSS 2 Tables', - link: 'css2/', - specLink: 'CSS22/tables.html', + links: { + tr: 'CSS22/tables.html', + dev: 'css2/', + }, status: 'stable', - version: 2.2, + firstSnapshot: 2.2, + lastSnapshot: 2.2, properties: { 'border-collapse': { link: '#propdef-border-collapse', diff --git a/features/css2-text.js b/tests/css2-text.js similarity index 90% rename from features/css2-text.js rename to tests/css2-text.js index 14e189af..eff68939 100644 --- a/features/css2-text.js +++ b/tests/css2-text.js @@ -1,10 +1,12 @@ export default { - id: 'css2-text', title: 'CSS 2 Text', - link: 'css2/', - specLink: 'CSS22/text.html', + links: { + tr: 'CSS22/text.html', + dev: 'css2/', + }, status: 'stable', - version: 2.2, + firstSnapshot: 2.2, + lastSnapshot: 2.2, properties: { 'letter-spacing': { link: '#propdef-letter-spacing', diff --git a/features/css2-ui.js b/tests/css2-ui.js similarity index 91% rename from features/css2-ui.js rename to tests/css2-ui.js index 4ee3d035..e6318557 100644 --- a/features/css2-ui.js +++ b/tests/css2-ui.js @@ -1,10 +1,12 @@ export default { - id: 'css2-ui', title: 'CSS 2 User Interface', - link: 'css2/', - specLink: 'CSS22/ui.html', + links: { + tr: 'CSS22/ui.html', + dev: 'css2/', + }, status: 'stable', - version: 2.2, + firstSnapshot: 2.2, + lastSnapshot: 2.2, properties: { cursor: { link: '#cursor-props', diff --git a/features/css2-visudet.js b/tests/css2-visudet.js similarity index 63% rename from features/css2-visudet.js rename to tests/css2-visudet.js index 9fc3f08a..acd2df2c 100644 --- a/features/css2-visudet.js +++ b/tests/css2-visudet.js @@ -1,39 +1,35 @@ export default { - id: 'css2-visudet', title: 'CSS 2 Visual Formatting Model Details', - link: 'css2/', - specLink: 'CSS22/visudet.html', + links: { + tr: 'CSS22/visudet.html', + dev: 'css2/', + }, status: 'stable', - version: 2.2, + firstSnapshot: 2.2, + lastSnapshot: 2.2, properties: { height: { link: '#the-height-property', - dataTypes: ['length', 'percentage', 'length-percentage'], tests: ['auto', '100px', '10%'], }, 'line-height': { link: '#propdef-line-height', - dataTypes: ['number', 'length', 'percentage'], tests: ['normal', '2', '2em', '150%'], }, 'max-height': { link: '#min-max-heights', - dataTypes: ['length', 'percentage', 'length-percentage'], tests: ['none', '100px', '80%'], }, 'max-width': { link: '#min-max-widths', - dataTypes: ['length', 'percentage', 'length-percentage'], tests: ['none', '100px', '80%'], }, 'min-height': { link: '#min-max-heights', - dataTypes: ['length', 'percentage', 'length-percentage'], tests: ['100px', '10%'], }, 'min-width': { link: '#min-max-widths', - dataTypes: ['length', 'percentage', 'length-percentage'], tests: ['100px', '10%'], }, 'vertical-align': { @@ -42,7 +38,6 @@ export default { }, width: { link: '#the-width-property', - dataTypes: ['length', 'percentage', 'length-percentage'], tests: ['auto', '100px', '10%'], }, }, diff --git a/features/css2-visufx.js b/tests/css2-visufx.js similarity index 67% rename from features/css2-visufx.js rename to tests/css2-visufx.js index 7ff892c9..8771b86f 100644 --- a/features/css2-visufx.js +++ b/tests/css2-visufx.js @@ -1,18 +1,22 @@ export default { - id: 'css2-visufx', title: 'CSS 2 Visual Effects', - link: 'css2/', - specLink: 'CSS22/visufx.html', + links: { + tr: 'CSS22/visufx.html', + dev: 'css2/', + }, status: 'stable', - version: 2.2, + firstSnapshot: 2.2, + lastSnapshot: 2.2, properties: { clip: { link: '#clipping', tests: ['auto', 'rect(1px, 10em, 3ex, 0.2mm)'], }, overflow: { - link: '#overflow①', - specLink: '#overflow', + links: { + tr: '#overflow', + dev: '#overflow①', + }, tests: ['auto', 'visible', 'hidden', 'scroll'], }, visibility: { diff --git a/features/css2-visuren.js b/tests/css2-visuren.js similarity index 92% rename from features/css2-visuren.js rename to tests/css2-visuren.js index b3bfef2a..88873db5 100644 --- a/features/css2-visuren.js +++ b/tests/css2-visuren.js @@ -1,10 +1,12 @@ export default { - id: 'css2-visuren', title: 'CSS 2 Visual Formatting Model', - link: 'css2/', - specLink: 'CSS22/visuren.html', + links: { + tr: 'CSS22/visuren.html', + dev: 'css2/', + }, status: 'stable', - version: 2.2, + firstSnapshot: 2.2, + lastSnapshot: 2.2, properties: { bottom: { link: '#position-props', diff --git a/tests/cssom-1.js b/tests/cssom-1.js new file mode 100644 index 00000000..c1989ae1 --- /dev/null +++ b/tests/cssom-1.js @@ -0,0 +1,238 @@ +export default { + title: 'CSS Object Model (CSSOM)', + link: 'cssom-1', + status: 'experimental', + interfaces: { + CSS: { + link: '#namespacedef-css', + links: { + mdnGroup: 'DOM', + }, + tests: ['escape'], + interface: function() { + return CSS; + } + }, + StyleSheet: { + link: '#the-stylesheet-interface', + links: { + mdnGroup: 'DOM', + }, + tests: [ + 'type', + 'href', + 'ownerNode', + 'parentStyleSheet', + 'title', + 'media', + 'disabled', + ], + interface: function(style) { + return style.sheet; + } + }, + CSSStyleSheet: { + link: '#the-cssstylesheet-interface', + links: { + mdnGroup: 'DOM', + }, + tests: [ + 'type', + 'href', + 'title', + 'media', + 'ownerNode', + 'parentStyleSheet', + 'title', + 'media', + 'disabled', + 'ownerRule', + 'cssRules', + 'insertRule', + 'deleteRule', + 'rules', + 'addRule', + 'removeRule', + 'replace', + 'replaceSync', + ], + interface: function(style) { + return style.sheet; + } + }, + StyleSheetList: { + link: '#the-stylesheetlist-interface', + links: { + mdnGroup: 'DOM', + }, + tests: ['item', 'length'], + interface: function() { + return document.styleSheets; + } + }, + Document: { + link: '#extensions-to-the-document-or-shadow-root-interface', + links: { + mdnGroup: 'DOM', + }, + tests: ['styleSheets', 'adoptedStyleSheets'], + interface: function() { + return document; + }, + }, + Element: { + link: '#the-linkstyle-interface', + links: { + mdnGroup: 'DOM', + }, + tests: ['sheet', 'style'], + interface: function(style) { + return style; + }, + }, + Window: { + link: '#extensions-to-the-window-interface', + links: { + mdnGroup: 'DOM', + }, + tests: ['getComputedStyle'], + interface: function() { + return window; + }, + }, + MediaList: { + link: '#the-medialist-interface', + links: { + mdnGroup: 'DOM', + }, + tests: ['mediaText', 'length', 'item', 'appendMedium', 'deleteMedium'], + interface: function(style) { + return style.sheet.media; + } + }, + CSSRuleList: { + link: '#the-cssrulelist-interface', + links: { + mdnGroup: 'DOM', + }, + tests: ['item', 'length'], + interface: function(style) { + return style.sheet.cssRules; + } + }, + CSSRule: { + link: '#the-cssrule-interface', + links: { + mdnGroup: 'DOM', + }, + tests: [ + 'cssText', + 'parentRule', + 'parentStyleSheet', + 'type', + 'STYLE_RULE', + 'CHARSET_RULE', + 'IMPORT_RULE', + 'MEDIA_RULE', + 'FONT_FACE_RULE', + 'PAGE_RULE', + 'MARGIN_RULE', + 'NAMESPACE_RULE', + ], + required: 'div { }', + interface: function(style) { + return style.sheet.cssRules[0]; + } + }, + CSSStyleRule: { + link: '#the-cssstylerule-interface', + links: { + mdnGroup: 'DOM', + }, + tests: [ + 'selectorText', + 'style', + 'cssRules', + 'insertRule', + 'deleteRule', + 'cssText', + 'parentRule', + 'parentStyleSheet', + ], + required: 'div { }', + }, + /* Doesn't currently work because style sheet is only available once imported + CSSImportRule: { + link: '#the-cssimportrule-interface', + links: { + mdnGroup: 'DOM', + }, + tests: ['href', 'media', 'styleSheet'], + required: '@import url("foo.css");', + }, + */ + CSSGroupingRule: { + link: '#the-cssgroupingrule-interface', + links: { + mdnGroup: 'DOM', + }, + tests: [ + 'cssRules', + 'insertRule', + 'deleteRule', + 'cssText', + 'parentRule', + 'parentStyleSheet', + ], + required: '@media { }', + interface: function(style) { + return style.sheet.cssRules[0]; + }, + }, + CSSPageRule: { + link: '#the-csspagerule-interface', + links: { + mdnGroup: 'DOM', + }, + tests: ['selectorText', 'style', 'cssRules', 'insertRule', 'deleteRule'], + required: '@page { }', + }, + CSSMarginRule: { + link: '#the-cssmarginrule-interface', + links: { + mdnGroup: 'DOM', + }, + tests: ['selectorText', 'style', 'cssText', 'parentRule', 'parentStyleSheet'], + required: '@page { @top-left { content: "foo"; } }', + }, + CSSNamespaceRule: { + link: '#the-cssnamespacerule-interface', + links: { + mdnGroup: 'DOM', + }, + tests: ['namespaceURI', 'prefix', 'cssText', 'parentRule', 'parentStyleSheet'], + required: '@namespace svg url("http://www.w3.org/2000/svg");', + }, + CSSStyleDeclaration: { + link: '#the-cssstyledeclaration-interface', + links: { + mdnGroup: 'DOM', + }, + tests: [ + 'cssText', + 'length', + 'item', + 'getPropertyValue', + 'getPropertyPriority', + 'setProperty', + 'removeProperty', + 'parentRule', + 'cssFloat' + ], + required: 'div { color: red; }', + interface: function(style) { + return style.sheet.cssRules[0].style; + } + }, + }, +}; diff --git a/features/cssom-view-1.js b/tests/cssom-view-1.js similarity index 50% rename from features/cssom-view-1.js rename to tests/cssom-view-1.js index 693c1bdd..f13dce08 100644 --- a/features/cssom-view-1.js +++ b/tests/cssom-view-1.js @@ -1,5 +1,4 @@ export default { - id: 'cssom-view-1', title: 'CSSOM View Module', link: 'cssom-view-1', status: 'experimental', @@ -9,52 +8,67 @@ export default { tests: ['auto', 'smooth '], }, }, - globals: { - window: { + interfaces: { + Window: { link: '#extensions-to-the-window-interface', - mdnGroup: 'DOM', - properties: [ + links: { + mdnGroup: 'DOM', + }, + tests: [ + 'matchMedia', 'screen', + 'visualViewport', + 'moveTo', + 'moveBy', + 'resizeTo', + 'resizeBy', 'innerWidth', 'innerHeight', 'scrollX', 'pageXOffset', 'scrollY', 'pageYOffset', + 'scroll', + 'scrollTo', + 'scrollBy', 'screenX', + 'screenLeft', 'screenY', + 'screenTop', 'outerWidth', 'outerHeight', 'devicePixelRatio', ], - functions: [ - 'matchMedia', - 'moveTo', - 'moveBy', - 'resizeTo', - 'resizeBy', - 'scroll', - 'scrollTo', - 'scrollBy', - ], + interface: function() { + return window; + }, }, MediaQueryList: { link: '#the-mediaquerylist-interface', - mdnGroup: 'DOM', - extends: 'EventTarget', - members: ['media', 'matches', 'onchange'], - methods: ['addListener', 'removeListener'], + links: { + mdnGroup: 'DOM', + }, + tests: ['media', 'matches', 'addListener', 'removeListener', 'onchange'], + interface: function() { + return window.matchMedia(''); + }, }, MediaQueryListEvent: { link: '#mediaquerylistevent', - mdnGroup: 'DOM', - extends: 'Event', - members: ['media', 'matches'], + links: { + mdnGroup: 'DOM', + }, + tests: ['matches'], + interface: function() { + return new MediaQueryListEvent('change', {matches: true}); + }, }, Screen: { link: '#the-screen-interface', - mdnGroup: 'DOM', - members: [ + links: { + mdnGroup: 'DOM', + }, + tests: [ 'availWidth', 'availHeight', 'width', @@ -62,27 +76,52 @@ export default { 'colorDepth', 'pixelDepth', ], + interface: function() { + return window.screen; + }, }, - document: { + Document: { link: '#extensions-to-the-document-interface', - mdnGroup: 'DOM', - members: ['scrollingElement'], - methods: [ + links: { + mdnGroup: 'DOM', + }, + tests: [ 'elementFromPoint', 'elementsFromPoint', 'caretPositionFromPoint', + 'scrollingElement', + 'getBoxQuads', + 'convertQuadFromNode', + 'convertRectFromNode', + 'convertPointFromNode', ], + interface: function() { + return document; + }, }, CaretPosition: { link: '#caretposition', - mdnGroup: 'DOM', - members: ['offsetNode', 'offset'], - methods: ['getClientRect'], + links: { + mdnGroup: 'DOM', + }, + tests: ['offsetNode', 'offset', 'getClientRect'], + interface: function() { + return document.caretPositionFromPoint(0, 0); + }, }, Element: { link: '#extension-to-the-element-interface', - mdnGroup: 'DOM', - members: [ + links: { + mdnGroup: 'DOM', + }, + tests: [ + 'getClientRects', + 'getBoundingClientRect', + 'checkVisibility', + 'scrollIntoView', + 'scroll', + 'scrollTo', + 'scrollBy', 'scrollTop', 'scrollLeft', 'scrollWidth', @@ -91,41 +130,57 @@ export default { 'clientLeft', 'clientWidth', 'clientHeight', + 'getBoxQuads', + 'convertQuadFromNode', + 'convertRectFromNode', + 'convertPointFromNode', ], - methods: [ - 'getClientRects', - 'getBoundingClientRect', - 'scrollIntoView', - 'scroll', - 'scrollTo', - 'scrollBy', - ], + interface: function() { + return document.createElement('div'); + }, }, HTMLElement: { link: '#extensions-to-the-htmlelement-interface', - mdnGroup: 'DOM', - members: [ + links: { + mdnGroup: 'DOM', + }, + tests: [ 'offsetParent', 'offsetTop', 'offsetLeft', 'offsetWidth', 'offsetHeight', ], + interface: function() { + return document.createElement('div'); + }, }, HTMLImageElement: { link: '#extensions-to-the-htmlimageelement-interface', - mdnGroup: 'DOM', - members: ['x', 'y'], + links: { + mdnGroup: 'DOM', + }, + tests: ['x', 'y'], + interface: function() { + return document.createElement('img'); + }, }, Range: { link: '#extensions-to-the-range-interface', - mdnGroup: 'DOM', - methods: ['getClientRects', 'getBoundingClientRect'], + links: { + mdnGroup: 'DOM', + }, + tests: ['getClientRects', 'getBoundingClientRect'], + interface: function() { + return document.createRange(); + }, }, MouseEvent: { link: '#extensions-to-the-mouseevent-interface', - mdnGroup: 'DOM', - members: [ + links: { + mdnGroup: 'DOM', + }, + tests: [ 'screenX', 'screenY', 'pageX', @@ -137,32 +192,46 @@ export default { 'offsetX', 'offsetY', ], + interface: function() { + return new MouseEvent('click', {screenX: 0, screenY: 0, clientX: 0, clientY: 0}); + }, }, Text: { link: '#geometryutils', - mdnGroup: 'DOM', - methods: [ + links: { + mdnGroup: 'DOM', + }, + tests: [ 'getBoxQuads', 'convertQuadFromNode', 'convertRectFromNode', 'convertPointFromNode', ], + interface: function() { + return document.createTextNode(''); + }, }, CSSPseudoElement: { link: '#geometryutils', - mdnGroup: 'DOM', - methods: [ + links: { + mdnGroup: 'DOM', + }, + tests: [ 'getBoxQuads', 'convertQuadFromNode', 'convertRectFromNode', 'convertPointFromNode', ], + interface: function() { + return document.createTextNode(''); + }, }, VisualViewport: { - link: '#the-visualviewport-interface', - mdnGroup: 'DOM', - extends: 'EventTarget', - members: [ + links: { + dev: '#the-visualviewport-interface', + mdnGroup: 'DOM', + }, + tests: [ 'offsetLeft', 'offsetTop', 'pageLeft', @@ -173,7 +242,13 @@ export default { 'onresize', 'onscroll', 'onscrollend', + 'addEventListener', + 'removeEventListener', + 'dispatchEvent', ], + interface: function() { + return window.visualViewport; + }, }, }, }; diff --git a/features/fill-stroke-3.js b/tests/fill-stroke-3.js similarity index 98% rename from features/fill-stroke-3.js rename to tests/fill-stroke-3.js index 530caa09..896dd95a 100644 --- a/features/fill-stroke-3.js +++ b/tests/fill-stroke-3.js @@ -1,5 +1,4 @@ export default { - id: 'fill-stroke-3', title: 'CSS Fill and Stroke Module Level 3', link: 'fill-stroke-3', group: 'fxtf', @@ -29,7 +28,6 @@ export default { }, 'fill-image': { link: '#fill-image', - dataTypes: ['image'], tests: [ 'url(foo.png)', "image('sprites.png#xywh=10,30,60,20')", @@ -160,7 +158,6 @@ export default { }, 'stroke-image': { link: '#stroke-image', - dataTypes: ['image'], tests: [ 'url(foo.png)', "image('sprites.png#xywh=10,30,60,20')", diff --git a/features/filter-effects-1.js b/tests/filter-effects-1.js similarity index 97% rename from features/filter-effects-1.js rename to tests/filter-effects-1.js index 1258bba7..cc2fa74f 100644 --- a/features/filter-effects-1.js +++ b/tests/filter-effects-1.js @@ -1,5 +1,4 @@ export default { - id: 'filter-effects-1', title: 'Filter Effects Module Level 1', link: 'filter-effects-1', group: 'fxtf', diff --git a/features/filter-effects-2.js b/tests/filter-effects-2.js similarity index 83% rename from features/filter-effects-2.js rename to tests/filter-effects-2.js index 1e4eaa1e..838ec804 100644 --- a/features/filter-effects-2.js +++ b/tests/filter-effects-2.js @@ -1,12 +1,15 @@ export default { - id: 'filter-effects-2', title: 'Filter Effects Module Level 2', - link: 'filter-effects-2', + links: { + dev: 'filter-effects-2', + }, group: 'fxtf', status: 'experimental', properties: { 'backdrop-filter': { - link: '#BackdropFilterProperty', + links: { + dev: '#BackdropFilterProperty', + }, tests: [ 'none', 'url(#id)', diff --git a/features/fullscreen.js b/tests/fullscreen.js similarity index 62% rename from features/fullscreen.js rename to tests/fullscreen.js index ad5e11d9..8d4cfb71 100644 --- a/features/fullscreen.js +++ b/tests/fullscreen.js @@ -1,12 +1,15 @@ export default { - id: 'fullscreen', title: 'Fullscreen API', - link: 'fullscreen', + links: { + dev: 'fullscreen', + }, group: 'whatwg', status: 'experimental', selectors: { '::backdrop': { - link: '#::backdrop-pseudo-element', + links: { + dev: '#::backdrop-pseudo-element', + }, tests: '::backdrop', }, }, diff --git a/tests/html.js b/tests/html.js new file mode 100644 index 00000000..16afde0f --- /dev/null +++ b/tests/html.js @@ -0,0 +1,40 @@ +export default { + title: 'HTML Living Standard', + links: { + dev: 'html', + }, + group: 'whatwg', + status: 'experimental', + selectors: { + ':autofill': { + links: { + dev: '#selector-autofill', + }, + tests: ':autofill', + }, + ':popover-open': { + links: { + dev: '#selector-popover-open', + }, + tests: ':popover-open', + }, + ':state()': { + links: { + dev: '#selector-custom', + }, + tests: ':state(checked)', + }, + }, + interfaces: { + PageRevealEvent: { + links: { + dev: '#the-pagerevealevent-interface', + mdnGroup: 'DOM', + }, + tests: ['viewTransition'], + interface: function() { + return new PageRevealEvent('reveal'); + }, + }, + } +}; diff --git a/features/mathml-core.js b/tests/mathml-core.js similarity index 67% rename from features/mathml-core.js rename to tests/mathml-core.js index 36167030..c719f49a 100644 --- a/features/mathml-core.js +++ b/tests/mathml-core.js @@ -1,16 +1,21 @@ export default { - id: 'mathml-core', title: 'MathML Core', - link: 'mathml-core/#css-extensions-for-math-layout', + links: { + dev: 'mathml-core/#css-extensions-for-math-layout', + }, group: 'math', status: 'experimental', properties: { display: { - link: '#new-display-math-value', + links: { + dev: 'new-display-math-value', + }, tests: ['math', 'block math', 'inline math'], }, 'text-transform': { - link: '#new-text-transform-values', + links: { + dev: '#new-text-transform-values', + }, tests: [ 'math-auto', 'math-bold', @@ -33,19 +38,27 @@ export default { ], }, 'font-size': { - link: '#the-math-script-level-property', + links: { + dev: '#the-math-script-level-property', + }, tests: ['math'], }, 'math-style': { - link: '#the-math-style-property', + links: { + dev: '#the-math-style-property', + }, tests: ['normal', 'compact'], }, 'math-shift': { - link: '#the-math-shift', + links: { + dev: '#the-math-shift', + }, tests: ['normal', 'compact'], }, 'math-depth': { - link: '#the-math-script-level-property', + links: { + dev: '#the-math-script-level-property', + }, tests: ['auto-add', 'add(0)', 'add(1)', '0', '1'], }, }, diff --git a/features/mediaqueries-3.js b/tests/mediaqueries-3.js similarity index 94% rename from features/mediaqueries-3.js rename to tests/mediaqueries-3.js index efee6ff3..43789e7b 100644 --- a/features/mediaqueries-3.js +++ b/tests/mediaqueries-3.js @@ -1,10 +1,12 @@ export default { - id: 'mediaqueries-3', title: 'Media Queries Level 3', - link: 'mediaqueries-3', + links: { + tr: 'css3-mediaqueries', + dev: 'mediaqueries-3', + }, status: 'stable', firstSnapshot: 2010, - mediaqueries: { + 'Media queries': { Syntax: { link: '#syntax', tests: [ @@ -20,12 +22,12 @@ export default { width: { link: '#width', mdn: 'Media_Queries/Using_media_queries', - tests: ['(width)', '(width:1280px)', '(min-width:1px)', '(max-width: 1px)'], + tests: ['(width)', '(width:1280px)', '(min-width:1px)', '(max-width:1000000px)'], }, height: { link: '#height', mdn: 'Media_Queries/Using_media_queries', - tests: ['(height)', '(height:720px)', '(min-height:1px)', '(max-height: 1px)'], + tests: ['(height)', '(height:720px)', '(min-height:1px)', '(max-height:1000000px)'], }, 'device-width': { link: '#device-width', diff --git a/features/mediaqueries-4.js b/tests/mediaqueries-4.js similarity index 98% rename from features/mediaqueries-4.js rename to tests/mediaqueries-4.js index 8efaaed7..ac8142e8 100644 --- a/features/mediaqueries-4.js +++ b/tests/mediaqueries-4.js @@ -1,9 +1,8 @@ export default { - id: 'mediaqueries-4', title: 'Media Queries Level 4', link: 'mediaqueries-4', status: 'stable', - mediaqueries: { + 'Media queries': { Syntax: { link: '#mq-syntax', tests: [ diff --git a/features/mediaqueries-5.js b/tests/mediaqueries-5.js similarity index 94% rename from features/mediaqueries-5.js rename to tests/mediaqueries-5.js index ea11331e..81d13a73 100644 --- a/features/mediaqueries-5.js +++ b/tests/mediaqueries-5.js @@ -1,9 +1,8 @@ export default { - id: 'mediaqueries-5', title: 'Media Queries Level 5', link: 'mediaqueries-5', status: 'experimental', - mediaqueries: { + 'Media queries': { 'display-mode': { link: '#display-modes', tests: [ @@ -86,7 +85,10 @@ export default { tests: ['(nav-controls)', '(nav-controls: none)', '(nav-controls: back)'], }, 'video-color-gamut': { - link: '#video-color-gamut', + links: { + dev: '#video-color-gamut', + tr: '#video-color-gamut', + }, tests: [ '(video-color-gamut)', '(video-color-gamut: srgb)', @@ -95,7 +97,10 @@ export default { ], }, 'video-dynamic-range': { - link: '#video-dynamic-range', + links: { + dev: '#video-dynamic-range', + tr: '#video-dynamic-range', + }, tests: ['(video-dynamic-range)', '(video-dynamic-range: standard)', '(video-dynamic-range: high)'], }, }, diff --git a/features/motion-1.js b/tests/motion-1.js similarity index 72% rename from features/motion-1.js rename to tests/motion-1.js index 7ae8450c..2c00629a 100644 --- a/features/motion-1.js +++ b/tests/motion-1.js @@ -1,20 +1,31 @@ export default { - id: 'motion-1', title: 'Motion Path Module Level 1', - link: 'motion-1', + links: { + dev: 'motion-1', + tr: 'motion-1', + }, group: 'fxtf', status: 'experimental', properties: { 'offset-anchor': { - link: '#offset-anchor-property', + links: { + dev: '#offset-anchor-property', + tr: '#offset-anchor-property', + }, tests: ['auto', 'center', 'top left', '10px 20px', '30% 40%'], }, 'offset-distance': { - link: '#offset-distance-property', + links: { + dev: '#offset-distance-property', + tr: '#offset-distance-property', + }, tests: ['20px', '30%'], }, 'offset-path': { - link: '#offset-path-property', + links: { + dev: '#offset-path-property', + tr: '#offset-path-property', + }, tests: [ 'none', 'ray(45deg closest-side)', @@ -40,15 +51,24 @@ export default { ], }, 'offset-position': { - link: '#offset-position-property', + links: { + dev: '#offset-position-property', + tr: '#offset-position-property', + }, tests: ['auto', 'center', 'top left', '10px 20px', '30% 40%'], }, 'offset-rotate': { - link: '#offset-rotate-property', + links: { + dev: '#offset-rotate-property', + tr: '#offset-rotate-property', + }, tests: ['auto', 'reverse', '90deg', 'auto 90deg', 'reverse 90deg'], }, offset: { - link: '#offset-shorthand', + links: { + dev: '#offset-shorthand', + tr: '#offset-shorthand', + }, tests: [ 'none', 'ray(45deg closest-side)', diff --git a/features/pointerevents1.js b/tests/pointerevents1.js similarity index 65% rename from features/pointerevents1.js rename to tests/pointerevents1.js index 8792873b..d88ae890 100644 --- a/features/pointerevents1.js +++ b/tests/pointerevents1.js @@ -1,11 +1,14 @@ export default { - id: 'pointerevents-1', title: 'Pointer Events Level 1', - specLink: 'pointerevents1', + links: { + tr: 'pointerevents1', + }, status: 'stable', properties: { 'touch-action': { - specLink: '#the-touch-action-css-property', + links: { + tr: '#the-touch-action-css-property', + }, tests: ['auto', 'none', 'pan-x', 'pan-y', 'pan-x pan-y', 'manipulation'], }, }, diff --git a/features/pointerevents3.js b/tests/pointerevents3.js similarity index 64% rename from features/pointerevents3.js rename to tests/pointerevents3.js index 9c1e7eb5..1809cbd5 100644 --- a/features/pointerevents3.js +++ b/tests/pointerevents3.js @@ -1,12 +1,16 @@ export default { - id: 'pointerevents-3', title: 'Pointer Events Level 3', - specLink: 'pointerevents3', + links: { + tr: 'pointerevents3', + dev: 'pointerevents', + }, group: 'github', status: 'experimental', properties: { 'touch-action': { - link: '#the-touch-action-css-property', + links: { + dev: '#the-touch-action-css-property', + }, tests: ['pan-left', 'pan-right', 'pan-up', 'pan-down', 'pan-left pan-up'], }, }, diff --git a/tests/resize-observer-1.js b/tests/resize-observer-1.js new file mode 100644 index 00000000..afaa66a2 --- /dev/null +++ b/tests/resize-observer-1.js @@ -0,0 +1,31 @@ +export default { + title: 'Resize Observer', + link: 'resize-observer-1', + status: 'experimental', + interfaces: { + ResizeObserver: { + link: '#api', + links: { + mdnGroup: 'DOM', + }, + tests: ['observe', 'unobserve', 'disconnect'], + interface: function() { + return new ResizeObserver(function() {}); + } + }, + ResizeObserverEntry: { + link: '#resize-observer-entry-interface', + links: { + mdnGroup: 'DOM', + }, + tests: ['ResizeObserverEntry'], + }, + ResizeObserverSize: { + link: '#resizeobserversize', + links: { + mdnGroup: 'DOM', + }, + tests: ['ResizeObserverEntry'], + }, + }, +}; diff --git a/features/scroll-animations-1.js b/tests/scroll-animations-1.js similarity index 80% rename from features/scroll-animations-1.js rename to tests/scroll-animations-1.js index f6ce99b9..348756dc 100644 --- a/features/scroll-animations-1.js +++ b/tests/scroll-animations-1.js @@ -1,10 +1,9 @@ export default { - id: 'scroll-animations-1', title: 'Scroll-driven Animations', link: 'scroll-animations-1', status: 'experimental', - values: { - properties: ['animation-timeline'], + 'values': { + 'properties': ['animation-timeline'], 'scroll()': { link: '#scroll-notation', tests: [ @@ -118,7 +117,13 @@ export default { }, 'scroll-timeline-axis': { link: '#scroll-timeline-axis', - tests: ['block', 'inline', 'x', 'y', 'block, inline'], + tests: [ + 'block', + 'inline', + 'x', + 'y', + 'block, inline', + ], }, 'scroll-timeline-name': { link: '#scroll-timeline-name', @@ -146,11 +151,24 @@ export default { }, 'view-timeline-axis': { link: '#view-timeline-axis', - tests: ['block', 'inline', 'x', 'y', 'block, inline'], + tests: [ + 'block', + 'inline', + 'x', + 'y', + 'block, inline', + ], }, 'view-timeline-inset': { link: '#view-timeline-inset', - tests: ['auto', '100px', '5%', '100px 200px', '5% 10%', 'auto, 100px'], + tests: [ + 'auto', + '100px', + '5%', + '100px 200px', + '5% 10%', + 'auto, 100px', + ], }, 'view-timeline-name': { link: '#view-timeline-name', @@ -163,21 +181,37 @@ export default { }, 'timeline-scope': { link: '#timeline-scope', - tests: ['none', '--timeline-scope', '--some-timeline-scope, --other-timeline-scope'], - }, + tests: [ + 'none', + '--timeline-scope', + '--some-timeline-scope, --other-timeline-scope', + ], + } }, - globals: { + interfaces: { ScrollTimeline: { link: '#scrolltimeline-interface', - mdnGroup: 'DOM', - extends: 'AnimationTimeline', - members: ['source', 'axis'], + links: { + mdnGroup: 'DOM', + }, + tests: ['source', 'axis', 'currentTime'], + interface: function() { + return new ScrollTimeline({ + source: document.scrollingElement, + }); + } }, ViewTimeline: { link: '#viewtimeline-interface', - mdnGroup: 'DOM', - extends: 'ScrollTimeline', - members: ['subject', 'startOffset', 'endOffset'], + links: { + mdnGroup: 'DOM', + }, + tests: ['subject', 'startOffset', 'endOffset', 'currentTime'], + interface: function() { + return new ViewTimeline({ + source: document.scrollingElement, + }); + } }, }, }; diff --git a/features/selectors-3.js b/tests/selectors-3.js similarity index 52% rename from features/selectors-3.js rename to tests/selectors-3.js index 34461ee2..211be418 100644 --- a/features/selectors-3.js +++ b/tests/selectors-3.js @@ -1,17 +1,4 @@ -const An_plus_B = [ - 'even', - 'odd', - 'n', - '-n', - '0n', - '1', - '-1', - '0', - '3n + 1', -] - export default { - id: 'selectors-3', title: 'Selectors Level 3', link: 'selectors-3', mdn: 'Glossary/CSS_Selector', @@ -25,15 +12,19 @@ export default { }, '::before': { link: '#gen-content', + tests: '::before', }, '::after': { link: '#gen-content', + tests: '::after', }, '::first-letter': { link: '#first-letter', + tests: '::first-letter', }, '::first-line': { link: '#first-line', + tests: '::first-line', }, '[att^=val]': { link: '#attribute-substrings', @@ -57,55 +48,123 @@ export default { }, ':target': { link: '#target-pseudo', + tests: ':target', }, ':enabled': { link: '#enableddisabled', + tests: ':enabled', }, ':disabled': { link: '#enableddisabled', + tests: ':disabled', }, ':checked': { link: '#checked', + tests: ':checked', }, ':indeterminate': { link: '#indeterminate', + tests: ':indeterminate', }, ':root': { link: '#root-pseudo', + tests: ':root', }, ':nth-child()': { link: '#nth-child-pseudo', - args: An_plus_B, + tests: [ + ':nth-child(even)', + ':nth-child(odd)', + ':nth-child(n)', + ':nth-child(-n)', + ':nth-child(0n)', + ':nth-child(1)', + ':nth-child(-1)', + ':nth-child(0)', + ':nth-child(n+1)', + ':nth-child(3n+1)', + ':nth-child(3n + 1)', + ':nth-child(-n+1)', + ':nth-child(3n-1)', + ], }, ':nth-last-child()': { link: '#nth-last-child-pseudo', - args: An_plus_B, + tests: [ + ':nth-last-child(even)', + ':nth-last-child(odd)', + ':nth-last-child(n)', + ':nth-last-child(-n)', + ':nth-last-child(0n)', + ':nth-last-child(1)', + ':nth-last-child(-1)', + ':nth-last-child(0)', + ':nth-last-child(n+1)', + ':nth-last-child(3n+1)', + ':nth-last-child(3n + 1)', + ':nth-last-child(-n+1)', + ':nth-last-child(3n-1)', + ], }, ':nth-of-type()': { link: '#nth-of-type-pseudo', - args: An_plus_B, + tests: [ + ':nth-of-type(even)', + ':nth-of-type(odd)', + ':nth-of-type(n)', + ':nth-of-type(-n)', + ':nth-of-type(0n)', + ':nth-of-type(1)', + ':nth-of-type(-1)', + ':nth-of-type(0)', + ':nth-of-type(n+1)', + ':nth-of-type(3n+1)', + ':nth-of-type(3n + 1)', + ':nth-of-type(-n+1)', + ':nth-of-type(3n-1)', + ], }, ':nth-last-of-type()': { link: '#nth-last-of-type-pseudo', - args: An_plus_B, + tests: [ + ':nth-last-of-type(even)', + ':nth-last-of-type(odd)', + ':nth-last-of-type(n)', + ':nth-last-of-type(-n)', + ':nth-last-of-type(0n)', + ':nth-last-of-type(1)', + ':nth-last-of-type(-1)', + ':nth-last-of-type(0)', + ':nth-last-of-type(n+1)', + ':nth-last-of-type(3n+1)', + ':nth-last-of-type(3n + 1)', + ':nth-last-of-type(-n+1)', + ':nth-last-of-type(3n-1)', + ], }, ':last-child': { link: '#last-child-pseudo', + tests: ':last-child', }, ':only-child': { link: '#only-child-pseudo', + tests: ':only-child', }, ':first-of-type': { link: '#first-of-type-pseudo', + tests: ':first-of-type', }, ':last-of-type': { link: '#last-of-type-pseudo', + tests: ':last-of-type', }, ':only-of-type': { link: '#only-of-type-pseudo', + tests: ':only-of-type', }, ':empty': { link: '#empty-pseudo', + tests: ':empty', }, ':not()': { link: '#negation', diff --git a/features/selectors-4.js b/tests/selectors-4.js similarity index 98% rename from features/selectors-4.js rename to tests/selectors-4.js index dddc881c..c5af85bc 100644 --- a/features/selectors-4.js +++ b/tests/selectors-4.js @@ -1,5 +1,4 @@ export default { - id: 'selectors-4', title: 'Selectors Level 4', link: 'selectors-4', status: 'experimental', @@ -174,7 +173,10 @@ export default { ], }, ':defined': { - link: '#the-defined-pseudo', + links: { + tr: '#the-defined-pseudo', + dev: 'the-defined-pseudo', + }, tests: [':defined'], }, ':nth-child()': { diff --git a/features/selectors-5.js b/tests/selectors-5.js similarity index 61% rename from features/selectors-5.js rename to tests/selectors-5.js index c83bde73..116abfb9 100644 --- a/features/selectors-5.js +++ b/tests/selectors-5.js @@ -1,21 +1,28 @@ export default { - id: 'selectors-5', title: 'Selectors Level 5', - link: 'selectors-5', + links: { + dev: 'selectors-5', + }, status: 'experimental', selectors: { ':local-link()': { - link: '#local-pseudo', + links: { + dev: '#local-pseudo', + }, tests: [ ':local-link(1)', ], }, ':state()': { - link: '#custom-state', + links: { + dev: '#custom-state', + }, tests: [':state(stuck)'], }, 'Reference selector': { - link: '#idref-combinators', + links: { + dev: '#idref-combinators', + }, tests: [ 'label /for/ input', ], diff --git a/features/svg2-coords.js b/tests/svg2-coords.js similarity index 79% rename from features/svg2-coords.js rename to tests/svg2-coords.js index a9775dbd..b1270e54 100644 --- a/features/svg2-coords.js +++ b/tests/svg2-coords.js @@ -1,14 +1,17 @@ export default { - id: 'svg2-coords', title: 'SVG 2 Coordinate Systems, Transformations and Units', - link: 'svg2-draft/coords.html', - specLink: 'svg2/coords.html', + links: { + tr: 'svg2/coords.html', + dev: 'svg2-draft/coords.html', + }, group: 'svgwg', status: 'experimental', properties: { 'vector-effect': { link: '#VectorEffects', - mdnGroup: 'SVG', + links: { + mdnGroup: 'SVG', + }, tests: [ 'none', 'non-scaling-stroke', diff --git a/features/svg2-geometry.js b/tests/svg2-geometry.js similarity index 62% rename from features/svg2-geometry.js rename to tests/svg2-geometry.js index 4e05ee98..d87173ea 100644 --- a/features/svg2-geometry.js +++ b/tests/svg2-geometry.js @@ -1,44 +1,59 @@ export default { - id: 'svg2-geometry', title: 'SVG 2 Geometry Properties', - link: 'svg2-draft/geometry.html', - specLink: 'svg2/geometry.html', + links: { + tr: 'svg2/geometry.html', + dev: 'svg2-draft/geometry.html', + }, group: 'svgwg', status: 'experimental', properties: { cx: { link: '#CX', - mdnGroup: 'SVG', + links: { + mdnGroup: 'SVG', + }, tests: ['0', '1px', '-5px', '25%'], }, cy: { link: '#CY', - mdnGroup: 'SVG', + links: { + mdnGroup: 'SVG', + }, tests: ['0', '1px', '-5px', '25%'], }, r: { link: '#R', - mdnGroup: 'SVG', + links: { + mdnGroup: 'SVG', + }, tests: ['0', '1px', '25%'], }, rx: { link: '#RX', - mdnGroup: 'SVG', + links: { + mdnGroup: 'SVG', + }, tests: ['auto', '0', '1px', '25%'], }, ry: { link: '#RY', - mdnGroup: 'SVG', + links: { + mdnGroup: 'SVG', + }, tests: ['auto', '0', '1px', '25%'], }, x: { link: '#X', - mdnGroup: 'SVG', + links: { + mdnGroup: 'SVG', + }, tests: ['0', '1px', '-5px', '25%'], }, y: { link: '#Y', - mdnGroup: 'SVG', + links: { + mdnGroup: 'SVG', + }, tests: ['0', '1px', '-5px', '25%'], }, }, diff --git a/features/svg2-interact.js b/tests/svg2-interact.js similarity index 80% rename from features/svg2-interact.js rename to tests/svg2-interact.js index 66e446e2..e07c95c8 100644 --- a/features/svg2-interact.js +++ b/tests/svg2-interact.js @@ -1,8 +1,9 @@ export default { - id: 'svg2-interact', title: 'SVG 2 Scripting and Interactivity', - link: 'svg2-draft/interact.html', - specLink: 'svg2/interact.html', + links: { + tr: 'svg2/interact.html', + dev: 'svg2-draft/interact.html', + }, group: 'svgwg', status: 'experimental', properties: { diff --git a/features/svg2-painting.js b/tests/svg2-painting.js similarity index 77% rename from features/svg2-painting.js rename to tests/svg2-painting.js index 8094aac4..109d0c30 100644 --- a/features/svg2-painting.js +++ b/tests/svg2-painting.js @@ -1,18 +1,23 @@ export default { - id: 'svg2-painting', title: 'SVG 2 Painting: Filling, Stroking and Marker Symbols', - link: 'svg2-draft/painting.html', - specLink: 'svg2/painting.html', + links: { + tr: 'svg2/painting.html', + dev: 'svg2-draft/painting.html', + }, group: 'svgwg', status: 'experimental', properties: { 'color-interpolation': { link: '#ColorInterpolation', - mdnGroup: 'SVG', + links: { + mdnGroup: 'SVG', + }, tests: ['auto', 'sRGB', 'linearRGB'], }, 'color-rendering': { - link: '#ColorRendering', + links: { + tr: '#ColorRendering', + }, tests: ['auto', 'optimizeSpeed', 'optimizeQuality'], }, marker: { @@ -21,17 +26,23 @@ export default { }, 'marker-end': { link: '#VertexMarkerProperties', - mdnGroup: 'SVG', + links: { + mdnGroup: 'SVG', + }, tests: ['none', 'url(#marker)'], }, 'marker-mid': { link: '#VertexMarkerProperties', - mdnGroup: 'SVG', + links: { + mdnGroup: 'SVG', + }, tests: ['none', 'url(#marker)'], }, 'marker-start': { link: '#VertexMarkerProperties', - mdnGroup: 'SVG', + links: { + mdnGroup: 'SVG', + }, tests: ['none', 'url(#marker)'], }, 'paint-order': { @@ -40,7 +51,9 @@ export default { }, 'shape-rendering': { link: '#ShapeRendering', - mdnGroup: 'SVG', + links: { + mdnGroup: 'SVG', + }, tests: ['auto', 'optimizeSpeed', 'crispEdges', 'geometricPrecision'], }, 'text-rendering': { diff --git a/features/svg2-paths.js b/tests/svg2-paths.js similarity index 62% rename from features/svg2-paths.js rename to tests/svg2-paths.js index acd8a57d..958b50a3 100644 --- a/features/svg2-paths.js +++ b/tests/svg2-paths.js @@ -1,14 +1,17 @@ export default { - id: 'svg2-paths', title: 'SVG 2 Paths', - link: 'svg2-draft/paths.html', - specLink: 'svg2/paths.html', + links: { + tr: 'svg2/paths.html', + dev: 'svg2-draft/paths.html', + }, group: 'svgwg', status: 'experimental', properties: { d: { link: '#TheDProperty', - mdnGroup: 'SVG', + links: { + mdnGroup: 'SVG', + }, tests: ['none', "'M 20 20 H 80 V 30'"], }, }, diff --git a/features/svg2-pservers.js b/tests/svg2-pservers.js similarity index 51% rename from features/svg2-pservers.js rename to tests/svg2-pservers.js index 3efe5fc9..b7a651d6 100644 --- a/features/svg2-pservers.js +++ b/tests/svg2-pservers.js @@ -1,19 +1,24 @@ export default { - id: 'svg2-pServers', title: 'SVG 2 Paint Servers: Gradients and Patterns', - link: 'svg2-draft/pservers.html', - specLink: 'svg2/pservers.html', + links: { + tr: 'svg2/pservers.html', + dev: 'svg2-draft/pservers.html', + }, group: 'svgwg', status: 'experimental', properties: { 'stop-color': { - link: '#StopColorProperty', - mdnGroup: 'SVG', + links: { + dev: '#StopColorProperty', + mdnGroup: 'SVG', + }, tests: ['green'], }, 'stop-opacity': { - link: '#StopOpacityProperty', - mdnGroup: 'SVG', + links: { + dev: '#StopOpacityProperty', + mdnGroup: 'SVG', + }, tests: ['.5', '45%'], }, }, diff --git a/features/svg2-text.js b/tests/svg2-text.js similarity index 79% rename from features/svg2-text.js rename to tests/svg2-text.js index d363d43b..1f31f553 100644 --- a/features/svg2-text.js +++ b/tests/svg2-text.js @@ -1,8 +1,9 @@ export default { - id: 'svg2-text', title: 'SVG 2 Text', - link: 'svg2-draft/text.html', - specLink: 'svg2/text.html', + links: { + tr: 'svg2/text.html', + dev: 'svg2-draft/text.html', + }, group: 'svgwg', status: 'experimental', properties: { @@ -21,11 +22,15 @@ export default { }, 'text-anchor': { link: '#TextAnchoringProperties', - mdnGroup: 'SVG', + links: { + mdnGroup: 'SVG', + }, tests: ['start', 'middle', 'end'], }, 'text-decoration-fill': { - link: '#TextDecorationFillStroke', + links: { + tr: '#TextDecorationFillStroke', + }, tests: [ 'none', 'green', @@ -37,7 +42,9 @@ export default { ], }, 'text-decoration-stroke': { - link: '#TextDecorationFillStroke', + links: { + tr: '#TextDecorationFillStroke', + }, tests: [ 'none', 'green', diff --git a/tests/web-animations-1.js b/tests/web-animations-1.js new file mode 100644 index 00000000..9ff3f96c --- /dev/null +++ b/tests/web-animations-1.js @@ -0,0 +1,147 @@ +export default { + title: 'Web Animations', + link: 'web-animations-1', + status: 'experimental', + interfaces: { + Animation: { + link: '#the-animation-interface', + links: { + mdnGroup: 'DOM', + }, + tests: [ + 'id', + 'effect', + 'timeline', + 'startTime', + 'currentTime', + 'playbackRate', + 'playState', + 'replaceState', + 'pending', + 'ready', + 'finished', + 'onfinish', + 'oncancel', + 'onremove', + 'cancel', + 'finish', + 'play', + 'pause', + 'updatePlaybackRate', + 'reverse', + 'persist', + 'commitStyles', + 'addEventListener', + 'removeEventListener', + 'dispatchEvent', + ], + interface: function() { + var div = document.createElement('div'); + var keyFrames = new KeyframeEffect( + div, + [ + { transform: 'translateY(0%)' }, + { transform: 'translateY(100%)' } + ], + { duration: 3000, fill: 'forwards' } + ) + return new Animation(keyFrames, document.timeline); + } + }, + AnimationTimeline: { + link: '#the-animationtimeline-interface', + links: { + mdnGroup: 'DOM', + }, + tests: ['currentTime'], + interface: function() { + return document.timeline; + } + }, + AnimationEffect: { + link: '#animationeffect', + links: { + mdnGroup: 'DOM', + }, + tests: ['getTiming', 'getComputedTiming', 'updateTiming'], + interface: function() { + var div = document.createElement('div'); + return new KeyframeEffect( + div, + [ + { transform: "translateY(0%)" }, + { transform: "translateY(100%)" }, + ], + { duration: 3000, fill: "forwards" } + ); + } + }, + KeyframeEffect: { + link: '#the-keyframeeffect-interface', + links: { + mdnGroup: 'DOM', + }, + tests: [ + 'target', + 'pseudoElement', + 'composite', + 'getKeyframes', + 'setKeyframes', + 'getTiming', + 'getComputedTiming', + 'updateTiming', + ], + interface: function() { + var div = document.createElement('div'); + return new KeyframeEffect( + div, + [ + { transform: "translateY(0%)" }, + { transform: "translateY(100%)" }, + ], + { duration: 3000, fill: "forwards" } + ); + } + }, + Element: { + link: '#the-animatable-interface-mixin', + links: { + mdnGroup: 'DOM', + }, + tests: ['animate', 'getAnimations'], + interface: function() { + return document.body; + } + }, + Document: { + link: '#extensions-to-the-documentorshadowroot-interface-mixin', + links: { + mdnGroup: 'DOM', + }, + tests: ['timeline', 'getAnimations'], + interface: function() { + return document; + } + }, + DocumentTimeline: { + link: '#the-documenttimeline-interface', + links: { + mdnGroup: 'DOM', + }, + tests: ['currentTime'], + interface: function() { + return document.timeline; + } + }, + AnimationPlaybackEvent: { + link: '#the-animationplaybackevent-interface', + links: { + mdnGroup: 'DOM', + }, + tests: ['currentTime', 'timelineTime'], + interface: function() { + return new AnimationPlaybackEvent('finish'); + }, + }, + }, +}; diff --git a/tests/web-animations-2.js b/tests/web-animations-2.js new file mode 100644 index 00000000..35fca480 --- /dev/null +++ b/tests/web-animations-2.js @@ -0,0 +1,159 @@ +export default { + title: 'Web Animations Level 2', + link: 'web-animations-2', + status: 'experimental', + interfaces: { + AnimationTimeline: { + link: '#the-animationtimeline-interface', + links: { + mdnGroup: 'DOM', + }, + tests: ['duration', 'play'], + interface: function() { + return document.timeline; + } + }, + AnimationEffect: { + link: '#the-animationeffect-interface', + links: { + mdnGroup: 'DOM', + }, + tests: [ + 'parent', + 'previousSibling', + 'nextSibling', + 'before', + 'after', + 'replace', + 'remove' + ], + interface: function() { + var div = document.createElement('div'); + return new KeyframeEffect( + div, + [ + { transform: "translateY(0%)" }, + { transform: "translateY(100%)" }, + ], + { duration: 3000, fill: "forwards" } + ); + } + }, + GroupEffect: { + link: '#the-groupeffect-interface', + links: { + mdnGroup: 'DOM', + }, + tests: [ + 'children', + 'firstChild', + 'lastChild', + 'clone', + 'prepend', + 'append', + ], + interface: function() { + var div = document.createElement('div'); + return new GroupEffect( + new KeyframeEffect( + div, + [ + { transform: "translateY(0%)" }, + { transform: "translateY(100%)" }, + ], + { duration: 3000, fill: "forwards" } + ), + new KeyframeEffect( + div, + [ + { opacity: 0 }, + { opacity: 1 }, + ], + { duration: 3000, fill: "forwards" } + ), + ); + } + }, + AnimationNodeList: { + link: '#the-animationnodelist-interface', + links: { + mdnGroup: 'DOM', + }, + tests: ['length', 'item'], + interface: function() { + var div = document.createElement('div'); + return new GroupEffect( + new KeyframeEffect( + div, + [ + { transform: "translateY(0%)" }, + { transform: "translateY(100%)" }, + ], + { duration: 3000, fill: "forwards" } + ), + new KeyframeEffect( + div, + [ + { opacity: 0 }, + { opacity: 1 }, + ], + { duration: 3000, fill: "forwards" } + ), + ).children; + } + }, + SequenceEffect: { + link: '#the-sequenceeffect-interface', + links: { + mdnGroup: 'DOM', + }, + tests: [ + 'children', + 'firstChild', + 'lastChild', + 'clone', + 'prepend', + 'append', + ], + interface: function() { + var div = document.createElement('div'); + return new SequenceEffect( + new KeyframeEffect( + div, + [ + { transform: "translateY(0%)" }, + { transform: "translateY(100%)" }, + ], + { duration: 3000, fill: "forwards" } + ), + new KeyframeEffect( + div, + [ + { opacity: 0 }, + { opacity: 1 }, + ], + { duration: 3000, fill: "forwards" } + ), + ); + } + }, + KeyframeEffect: { + link: '#the-keyframeeffect-interface', + links: { + mdnGroup: 'DOM', + }, + tests: ['iteratonComposite'], + interface: function() { + var div = document.createElement('div'); + return new KeyframeEffect( + div, + [ + { transform: "translateY(0%)" }, + { transform: "translateY(100%)" }, + ], + { duration: 3000, fill: "forwards" } + ); + } + }, + }, +}; diff --git a/features/webvtt.js b/tests/webvtt.js similarity index 91% rename from features/webvtt.js rename to tests/webvtt.js index af4c854b..ac3c60f0 100644 --- a/features/webvtt.js +++ b/tests/webvtt.js @@ -1,7 +1,9 @@ export default { - id: 'webvtt', title: 'WebVTT: The Web Video Text Tracks Format', - link: 'webvtt', + links: { + tr: 'webvtt1', + dev: 'webvtt', + }, group: 'github', status: 'experimental', selectors: { diff --git a/features/webxr-dom-overlays-1.js b/tests/webxr-dom-overlays-1.js similarity index 69% rename from features/webxr-dom-overlays-1.js rename to tests/webxr-dom-overlays-1.js index 0b9c6a93..ca26cb2d 100644 --- a/features/webxr-dom-overlays-1.js +++ b/tests/webxr-dom-overlays-1.js @@ -1,8 +1,9 @@ export default { - id: 'webxr-dom-overlays-1', title: 'WebXR DOM Overlays Module', - link: 'dom-overlays', - specLink: 'webxr-dom-overlays-1', + links: { + tr: 'webxr-dom-overlays-1', + dev: 'dom-overlays', + }, group: 'iwwg', status: 'experimental', selectors: { diff --git a/ui/avatar.png b/ui/avatar.png new file mode 100644 index 00000000..0b6e66f4 Binary files /dev/null and b/ui/avatar.png differ diff --git a/ui/components/tooltip/tooltip.css b/ui/components/tooltip/tooltip.css new file mode 100644 index 00000000..b151936d --- /dev/null +++ b/ui/components/tooltip/tooltip.css @@ -0,0 +1,36 @@ +/* Light DOM CSS for */ + +ui-tooltip { + display: inline-block; + position: relative; +} + +/* Trigger */ +ui-tooltip > :not(.tooltip) { + display: inline-flex; + align-items: center; + cursor: pointer; + anchor-name: --trigger; + + &:not(:hover, :focus-within) { + opacity: .5; + } +} + +ui-tooltip > .tooltip { + margin: 0; + position: absolute; + z-index: 2; + /* Anchor positioning as PE (currently not working) */ + position-anchor: --trigger; + position-area: top span-all; + position-try: normal flip-block flip-inline; + inset: auto; + /* Fallback */ + inset-block-end: 100%; + min-width: 15em; +} + +ui-tooltip:not(:hover, :focus-within) > .tooltip { + display: none; +} diff --git a/ui/css/carbon.css b/ui/css/carbon.css new file mode 100644 index 00000000..2e32d502 --- /dev/null +++ b/ui/css/carbon.css @@ -0,0 +1,46 @@ +/* Carbon Ads */ +#carbonads { + display: block; + overflow: hidden; + padding: 1em; + border: solid 1px var(--aside-border-color); + background-color: var(--aside-background-color); + font-size: 12px; + line-height: 1.5; + + a { + font-weight: 400; + color: var(--aside-foreground-color); + text-shadow: none; + } + + span { + position: relative; + display: block; + overflow: hidden; + } +} + +.carbon-img { + float: left; + margin-right: 1em; + + img { + display: block; + } +} + +.carbon-text { + display: block; + float: left; + max-width: calc(100% - 130px - 1em); + text-align: left; +} + +.carbon-poweredby { + position: absolute; + right: 0; + bottom: 0; + display: block; + font-size: 10px; +} diff --git a/ui/css/icons.css b/ui/css/icons.css new file mode 100644 index 00000000..1cb092be --- /dev/null +++ b/ui/css/icons.css @@ -0,0 +1,9 @@ +:root { + --icon-check: url('../icons/check.svg'); + --icon-caret: url('../icons/caret.svg'); + --icon-x: url('../icons/x.svg'); + --icon-warning: url('../icons/warning.svg'); + --icon-w3c: url('../icons/w3c.svg'); + --icon-mdn: url('../icons/mdn.svg'); + --icon-whatwg: url('../icons/whatwg.svg'); +} diff --git a/ui/css/style.css b/ui/css/style.css new file mode 100644 index 00000000..52c0d46e --- /dev/null +++ b/ui/css/style.css @@ -0,0 +1,431 @@ +@import "tokens.css"; +@import "icons.css"; +@import "carbon.css"; +@import "../components/tooltip/tooltip.css"; +@import "../../src/vue/components/support-status/support-status.css"; + +@media (prefers-color-scheme: dark) { + :root { + color-scheme: dark; + } +} + +:focus { + outline: var(--focus-ring); + outline-offset: var(--focus-ring-offset); +} + +@supports selector(:focus-visible) { + :focus { + outline: none; + } +} + +:focus-visible { + outline: var(--focus-ring); + outline-offset: var(--focus-ring-offset); +} + +html { + container-type: scroll-state; + container-name: document; +} + +body { + max-width: 60em; + padding: 1em; + margin: auto; + background: var(--color-bg); + color: var(--color-text); + font: 100%/1.5 var(--font-body); +} + +a { + color: var(--color-text-soft); + font-weight: bold; + text-decoration: none; +} + +code { + font-family: var(--font-mono); +} + +h1, +h2 { + margin: 1em 0 .5em; + font-family: var(--font-heading); + font-weight: normal; + line-height: 1.1; +} + +#content > section section section { + h1 { + display: flex; + align-items: center; + font-size: 180%; + gap: 0.5em; + } + + section h1 { + color: var(--color-text-soft); + font-size: 120%; + font-weight: bold; + text-transform: capitalize; + } +} + +h1 > .score { + margin-left: auto; + font-weight: bold; +} + +.icon, +.icon-before::before, +.spec-link::before { + content:''; + display: inline-block; + vertical-align: -.1em; + font-size: max(1em, 1rem); + width: 1em; + height: 1em; + + background: var(--icon-color, currentColor); + -webkit-mask: var(--icon) no-repeat center / contain; + mask: var(--icon) no-repeat center / contain; +} + +.icon-before::before, +.spec-link::before { + margin-inline-end: .2em; +} + +.spec-links { + display: flex; + gap: 0.3em; +} + +.spec-link { + display: flex; + align-items: center; + padding: .3em .4em; + margin: -.5em 0; + font-family: var(--font-sans); + font-size: 0.85rem; + background-color: var(--spec-link-background-color); + color: var(--spec-link-foreground-color); + --icon-color: light-dark(color-mix(in oklab, + var(--brand-color, currentColor), var(--spec-link-foreground-color) 75%), + var(--brand-color, currentColor)); + border-radius: .25em; + vertical-align: middle; + + &:hover, + &:focus { + outline: none; + --background: var(--brand-color, color-mix(in-oklab, var(--spec-link-background-color), var(--color-neutral-0-100) 25%)); + background: var(--background); + color: oklch(from var(--background) var(--text)); + + &::before, + &::after { + --icon-color: oklch(from var(--background) var(--text)); + } + } +} + +.w3c-link { + --icon: var(--icon-w3c); + --brand-color: #005a9c; +} + +.mdn-link { + --icon: var(--icon-mdn); + --brand-color: black; +} + +.whatwg-link { + --icon: var(--icon-whatwg); + --brand-color: #3c790a; +} + +details summary { + > .spec-link { + display: none; + vertical-align: inherit; + + &::before { + height: 18px; + width: 18px; + background-size: 18px 18px; + vertical-align: -4px; + } + } + + &:hover, + &:focus-within { + > .spec-link { + display: inline-block; + } + } +} + +/* Logo */ +body > h1 { + position: absolute; + inset-inline-start: 1em; + inset-block-start: 0; + z-index: 1; + padding-block: .5em; + padding-inline: .8em .6em; + margin: 0; + background: var(--title-background-color); + color: var(--title-foreground-color); + font-size: 150%; + font-weight: 900; + text-transform: uppercase; + transition: .1s; + box-shadow: 0 .4em .7em -.3em var(--color-shade); + + a { + color: inherit; + } + + small { + display: block; + text-transform: none; + font-size: 75%; + opacity: 0.8; + font-weight: 600; + letter-spacing: normal; + word-spacing: .1em; + } + + @container document scroll-state(scrollable: top) { + position: fixed; + inset-inline-start: 0; + inset-block-start: 1em; + transform: rotate(-90deg) translateX(-100%); + transform-origin: top left; + box-shadow: -.4em 0 .7em -.3em var(--color-shade); + } +} + +#content { + display: flex; + gap: 2em; +} + +#tests { + flex: 2; +} + +.pass { + --color: var(--color-pass); +} + +.almost-pass { + --color: var(--color-almost-pass); +} + +.slightly-buggy { + --color: var(--color-slightly-buggy); +} + +.buggy { + --color: var(--color-buggy); +} + +.very-buggy { + --color: var(--color-very-buggy); +} + +.fail { + --color: var(--color-fail); +} + +.epic-fail { + --color: var(--color-epic-fail); +} + +#tests > header { + text-align: center; + + > h1 { + font-size: 250%; + } + + > h1 > strong { + display: block; + margin-top: .1em; + font-size: 500%; + line-height: .7; + } + + > p { + font-weight: 500; + + .progress { + display: inline-block; + vertical-align: -.05em; + height: 1.2em; + --stroke-width: 20; + } + } +} + +details { + margin: .3em 0; + font: 100% var(--font-mono); +} + +.feature { + display: flex; + gap: .5em; + align-items: center; + padding: .5em .6em; + background: var(--color, var(--color-gray)); + color: oklch(from var(--color) var(--text)); + border-radius: .3em; + position: relative; + --progress-color: oklch(from var(--color) calc(l + 0.4) 0 0); + + .title { + flex: 1; + } + + .progress { + height: 1.2lh; + + .x { + opacity: .5; + } + } +} + +summary.feature { + list-style: none; + cursor: pointer; + --icon: var(--icon-caret); + + &::before { + content:''; + display: inline-block; + vertical-align: -.1em; + font-size: max(1em, 1rem); + width: 1em; + height: 1em; + + background: var(--icon-color, currentColor); + -webkit-mask: var(--icon) no-repeat center / contain; + mask: var(--icon) no-repeat center / contain; + + opacity: 0.6; + transition: transform 0.2s; + } + + &:is([open] > *)::before { + transform: rotate(90deg); + } +} + +.feature { + white-space: pre; + white-space: break-spaces; +} + +.feature-group, +.feature:not(.feature-group > *) { + margin-block: .3em; +} + +.feature-group > :not(summary), +li.feature { + margin-block: .3em; + margin-inline-start: 2em; + font-size: 85%; +} + +.feature small { + display: block; + opacity: .8; +} + +.prefix { + display: inline-block; + padding: .3em .4em; + margin: -.5em 0 -.5em .3em; + font-family: sans-serif; + font-size: 0.7rem; + background: var(--prefix-background-color); + color: var(--prefix-foreground-color); + border-radius: .3em; + vertical-align: middle; +} + +aside { + flex: 1; + font-size: 85%; + align-self: flex-start; + position: sticky; + top: 0; + + .caution { + padding: 1em; + background: var(--color-bg-loud); + color: var(--color-text-loud); + font-size: .85rem; + line-height: 1.5; + + p { + margin: 0; + } + } + + h1 { + font-size: 150%; + } + + ul { + max-height: calc(100vh - 18em); + overflow: auto; + margin: .5em 0; + padding: 0; + } + + li { + list-style: none; + padding: .3em 0; + border-bottom: 1px dotted var(--color-border); + + &:first-child { + border-top: none; + } + + &:last-child { + border-bottom: none; + } + } +} + +#specsTested li { + position: relative; + display: flex; + align-items: center; + gap: .5em; + padding-inline-start: .2em; + + .progress { + --stroke-width: 20; + } +} + +footer { + word-spacing: -1px; + + > p { + padding: 1em 0; + border-top: 1px solid var(--footer-border-color); + text-align: center; + } +} + diff --git a/ui/css/tokens.css b/ui/css/tokens.css new file mode 100644 index 00000000..9b561122 --- /dev/null +++ b/ui/css/tokens.css @@ -0,0 +1,70 @@ +:root { + --font-sans: system-ui, sans-serif; + --font-serif: ui-serif,serif; + --font-mono: Monaco, Consolas, monospace; + --font-body: var(--font-sans); + --font-heading: var(--font-body); + + --color-gray: hsl(200 10% 50%); + --color-magenta: hsl(336 100% 50%); + --color-green: light-dark(hsl(93 100% 30%), hsl(93 100% 25%)); + --color-lime: light-dark(hsl(80 100% 40%), hsl(80 100% 35%)); + --color-yellow: oklch(85% 0.2 91); + --color-orange: light-dark(hsl(34 100% 47%), hsl(34 100% 42%)); + --color-red: light-dark(hsl(9 100% 47%), hsl(9 100% 42%)); + --color-red-dark: light-dark(hsl(0 100% 37%), hsl(0 100% 32%)); + --color-blue: light-dark(oklch(65% 0.15 220), oklch(60% 0.15 220)); + + --color-pass: var(--color-green); + --color-almost-pass: var(--color-lime); + --color-slightly-buggy: var(--color-yellow); + --color-buggy: var(--color-orange); + --color-very-buggy: color-mix(in hsl, var(--color-orange), var(--color-red) 60%); + --color-fail: var(--color-red); + --color-epic-fail: var(--color-red-dark); + + --color-neutral: var(--color-gray); + --color-accent: var(--color-magenta); + --color-accent-text: white; + + --color-neutral-0-100: light-dark(white, black); + --color-neutral-10-100: light-dark(hsl(from var(--color-neutral) h s 10%), hsl(from var(--color-neutral) h s 100%)); + --color-neutral-20-90: light-dark(hsl(from var(--color-neutral) h s 20%), hsl(from var(--color-neutral) h s 90%)); + --color-neutral-30-80: light-dark(hsl(from var(--color-neutral) h s 30%), hsl(from var(--color-neutral) h s 80%)); + --color-neutral-80-30: light-dark(hsl(from var(--color-neutral) h s 80%), hsl(from var(--color-neutral) h s 30%)); + --color-neutral-90-20: light-dark(hsl(from var(--color-neutral) h s 20%), hsl(from var(--color-neutral) h s 90%)); + --color-neutral-100-10: light-dark(hsl(from var(--color-neutral) h s 100%), hsl(from var(--color-neutral) h s 10%)); + --color-neutral-100-0: light-dark(white, black); + --color-transparent: oklab(none none none / 0); + + --color-bg: canvas; + --color-text: var(--color-neutral-10-100); + --color-border: var(--color-neutral-80-30); + --color-shade: light-dark(oklab(none none none / .4), black); + --color-bg-soft: var(--color-neutral-90-20); + --color-text-soft: var(--color-neutral-20-90); + --color-bg-loud: var(--color-neutral-20-90); + --color-text-loud: var(--color-neutral-100-10); + + --footer-border-color: var(--color-border); + --focus-border-color: color-mix(in oklab, var(--color-blue), var(--color-transparent) 60%); + --focus-ring: .15em solid var(--focus-border-color, red); + --focus-ring-offset: .1em; + + --title-background-color: var(--color-bg-loud); + --title-foreground-color: var(--color-text-loud); + --spec-link-background-color: var(--color-bg-loud); + --spec-link-foreground-color: var(--color-text-loud); + + --prefix-background-color: var(--color-shade); + --prefix-foreground-color: var(--spec-link-foreground-color); + + --aside-border-color: light-dark(hsl(0 0% 80% / .8), hsl(0 0% 20% / .8)); + --aside-background-color: light-dark(hsl(0 0% 98% / .6), hsl(0 0% 2% / .6)); + --aside-foreground-color: light-dark(hsl(0 0% 0%), hsl(0 0% 100%)); + + --l-threshold: 0.73; + --is-light: clamp(0, (l - var(--l-threshold)) * infinity, 1); + --is-dark: (1 - var(--is-light)); + --text: calc(1 * var(--is-dark) + .1 * var(--is-light)) calc(c * var(--is-light)) h; +} diff --git a/ui/icons/caret.svg b/ui/icons/caret.svg new file mode 100644 index 00000000..438c3d32 --- /dev/null +++ b/ui/icons/caret.svg @@ -0,0 +1,5 @@ + + + + diff --git a/ui/icons/check.svg b/ui/icons/check.svg new file mode 100644 index 00000000..2398f98c --- /dev/null +++ b/ui/icons/check.svg @@ -0,0 +1,3 @@ + + + diff --git a/ui/icons/mdn.svg b/ui/icons/mdn.svg new file mode 100644 index 00000000..7f96efa8 --- /dev/null +++ b/ui/icons/mdn.svg @@ -0,0 +1,4 @@ + + + diff --git a/ui/icons/w3c.svg b/ui/icons/w3c.svg new file mode 100644 index 00000000..adebfd23 --- /dev/null +++ b/ui/icons/w3c.svg @@ -0,0 +1,5 @@ + + + diff --git a/ui/icons/warning.svg b/ui/icons/warning.svg new file mode 100644 index 00000000..bdc55648 --- /dev/null +++ b/ui/icons/warning.svg @@ -0,0 +1 @@ + diff --git a/ui/icons/whatwg.svg b/ui/icons/whatwg.svg new file mode 100644 index 00000000..5616ef85 --- /dev/null +++ b/ui/icons/whatwg.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/ui/icons/x.svg b/ui/icons/x.svg new file mode 100644 index 00000000..a026d847 --- /dev/null +++ b/ui/icons/x.svg @@ -0,0 +1,7 @@ + + + + + diff --git a/ui/logo.svg b/ui/logo.svg new file mode 100644 index 00000000..e28c4cc8 --- /dev/null +++ b/ui/logo.svg @@ -0,0 +1,86 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/util.js b/util.js deleted file mode 100644 index e929bcfe..00000000 --- a/util.js +++ /dev/null @@ -1,152 +0,0 @@ -// https://www.w3.org/TR/css-values/#component-combinators -export const combinators = { - '||': {anyOrder: true, min: 0, max: 1}, - '|': {anyOrder: true, min: 1, max: 1}, - '&&': {anyOrder: false, min: 1, max: 1}, -}; - -// https://www.w3.org/TR/css-values/#component-multipliers -export const multipliers = { - '?': {min: 0, max: 1}, - '*': {min: 0, max: Infinity}, - '+': {min: 1, max: Infinity}, - '#': {min: 1, max: 1, joiner: ', '}, -}; - -/** - * Combine multiple string arrays into a single array by recursively joining them. - * undefined is handled by not joining, allowing you to emulate things like {m,n} - * @param {...(string | undefined)[]} arrays - The arrays to combine - * @param {Object} [options] - The options for the combination - * @param {string} [options.joiner=' '] - The joiner to use between the arrays - * @returns {string[]} The combined array - */ -export function combine(...arrays) { - let options = {}; - if (!Array.isArray(arrays.at(-1))) { - options = arrays.pop(); - } - - if (options.combinator && combinators[options.combinator]) { - // Resolve combinator into actual options - options = {...combinators[options.combinator], ...options}; - - delete options.combinator; - } - - const { separator = " ", joiner, min = 1, anyOrder = false } = options; - let ret; - - if (anyOrder && arrays.length === 1) { - // TODO support multiple arrays - arrays = permutations(arrays[0]); - ret = arrays.map(array => combine(...array, {...options, anyOrder: false})).flat(1); - } - else { - arrays = arrays.map(arg => Array.isArray(arg) ? arg : [arg]); - - if (min === 0) { - arrays = arrays.map(array => array.indexOf(undefined) === -1 ? [undefined, ...array] : array); - } - - ret = arrays[0]; - - for (let i = 1; i arrays[i].map(b => joiner ? joiner(a, b) : defaultJoiner(a, b, separator))); - } - } - - // Drop duplicates - let set = new Set(ret); - if (set.has('')) { - set.delete(''); - } - ret = Array.from(set); - - return ret; -} - -/** - * Default joiner for combine() - * @param {*} a - * @param {*} b - * @param {*} separator - * @returns - */ -export function defaultJoiner (a, b, separator) { - let hasA = a || a === 0; - let hasB = b || b === 0; - - if (hasA && hasB) { - return a + separator + b; - } - else if (hasA) { - return a; - } - else if (hasB) { - return b; - } else { - return ''; // both undefined - } -} - -/** - * Repeat a value or array of values a given number of times. - * @param {any[] | any} values The value or array of values to repeat - * @param {Object} [options] The options for the repetition - * @param {number} [options.min=1] The minimum number of times to repeat the value - * @param {number} [options.max=min] The maximum number of times to repeat the value - * @param {string} [options.separator=' '] The separator to use between the values - * @returns {any[]} The repeated values - */ -export function repeat (values, {min, max = min, separator = ' '} = {}) { - values = Array.isArray(values) ? values : [values]; - let valuesAfterMin = min < max ? [undefined, ...values] : values; - - let args = [ - ...Array(min).fill(values), - ...Array(max - min).fill(valuesAfterMin), - ]; - - return combine(...args, {separator}); -} - -/** - * Generate all permutations of an array of values - * Uses Heap's algorithm https://en.wikipedia.org/wiki/Heap%27s_algorithm - * with a tweak to permute the *tail* first. - * E.g. for [1,2,3] => [[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]] - * @param {any[]} array The array to permute - * @param {number} [k=0] Start from index k - * @param {any[][]} permutations - */ -export function permutations (array, k = 0, ret = []) { - if (!Array.isArray(array)) { - return [[]]; - } - - const A = array.slice(); - - if (array.length < 2) { - return [A]; - } - - const n = A.length; - - if (k === n - 1) { - ret.push(A.slice()); - return; - } - - for (let i = k; i < n; i++) { - // Swap A[k] with A[i] - [A[k], A[i]] = [A[i], A[k]]; - - permutations(A, k + 1, ret); - - // Swap A[k] with A[i] back - [A[k], A[i]] = [A[i], A[k]]; - } - - return ret; -}