Skip to content

Commit a8c4d29

Browse files
Add media query support (#118)
* Add descriptors support * format * Fix a few descriptions manually * `node css/generateData.mjs` * Add status support
1 parent ec47dd3 commit a8c4d29

File tree

9 files changed

+1905
-158
lines changed

9 files changed

+1905
-158
lines changed
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import { listAll } from '@webref/css'
2+
3+
export async function addAtRuleDescriptors(atDirectives) {
4+
await addMediaQueryAtRuleDescriptors(atDirectives.find((directive) => directive.name === '@media'))
5+
return atDirectives
6+
}
7+
8+
/**
9+
* @typedef {{name: string, href: string, value: string, type: string, values?: SpecDescriptorValue[]}} SpecDescriptor
10+
* @typedef {{name: string, prose: string, href: string, type: string, value: string}} SpecDescriptorValue
11+
* @typedef {{name: string, description?: string, references: IReference[], syntax: string, type: string, values: IValueData[]}} Descriptor
12+
* @typedef {{name: string, description?: string, references: IReference[]}} IValueData
13+
* @typedef {{name: string, url: string}} IReference
14+
*/
15+
16+
async function addMediaQueryAtRuleDescriptors(atDirective) {
17+
const mediaQueries = (await listAll())['mediaqueries-5']
18+
/** @type {SpecDescriptor[]} */
19+
const specDescriptors = mediaQueries.atrules.find((obj) => obj.name === '@media').descriptors
20+
/** @type {Descriptor[]} */
21+
const outDescriptors = []
22+
for (const descriptor of specDescriptors) {
23+
outDescriptors.push({
24+
name: descriptor.name,
25+
references: [{ name: 'W3C Reference', url: descriptor.href }],
26+
type: descriptor.type,
27+
syntax: descriptor.value,
28+
values: descriptor.values?.map((value) => ({
29+
name: value.name,
30+
description: value.prose,
31+
references: [{ name: 'W3C Reference', url: value.href }],
32+
})),
33+
})
34+
}
35+
atDirective.descriptors = outDescriptors
36+
}

web-data/css/generateData.mjs

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { addMDNAtDirectives, addMDNPseudoElements, addMDNPseudoSelectors } from
1212
import { addBrowserCompatDataToProperties, addMDNReferences, browserNames } from './mdn/mdn-browser-compat-data-importer.mjs';
1313
import { applyRelevance } from './chromestatus/applyRelevance.mjs';
1414
import { computeBaseline } from 'compute-baseline';
15+
import { addAtRuleDescriptors } from './add-atrule-descriptors.mjs';
1516

1617
const { readFile, writeFile } = fs.promises;
1718

@@ -242,14 +243,14 @@ function getValues(valArr, restriction, ruleName) {
242243
}
243244
}
244245
var vals = valArr
245-
.map(function (v) {
246+
.map(function(v) {
246247
return {
247248
name: v.$.name,
248249
desc: v.desc,
249250
browsers: v.$.browsers !== 'all' ? v.$.browsers : void 0
250251
}
251252
})
252-
.filter(function (v) {
253+
.filter(function(v) {
253254
if (v.browsers === 'none') {
254255
return false
255256
}
@@ -261,7 +262,7 @@ function getValues(valArr, restriction, ruleName) {
261262

262263
var moreColors = {}
263264

264-
vals = vals.filter(function (v) {
265+
vals = vals.filter(function(v) {
265266
if (typeof colorsCopy[v.name] === 'string') {
266267
delete colorsCopy[v.name]
267268
return false
@@ -307,7 +308,7 @@ function toSource(object, keyName) {
307308
}
308309
var result = []
309310
var entryArr = object.css[keyName].entry
310-
entryArr.forEach(function (e) {
311+
entryArr.forEach(function(e) {
311312
if (e.$.browsers === 'none') {
312313
return
313314
}
@@ -336,6 +337,7 @@ async function process() {
336337
const data = await readFile(path.resolve(__dirname, schemaFileName));
337338
const result = JSON.parse(data.toString());
338339
let atDirectives = toSource(result, 'atDirectives');
340+
atDirectives = await addAtRuleDescriptors(atDirectives)
339341
atDirectives = await addMDNAtDirectives(atDirectives);
340342

341343
let pseudoClasses = toSource(result, 'pseudoClasses');
@@ -360,7 +362,12 @@ async function process() {
360362
}
361363

362364
customDataObject.properties.forEach(processEntry)
363-
customDataObject.atDirectives.forEach(processEntry)
365+
for (const directive of customDataObject.atDirectives) {
366+
processEntry(directive)
367+
for (const descriptor of directive.descriptors || []) {
368+
processEntry(descriptor)
369+
}
370+
}
364371
customDataObject.pseudoClasses.forEach(processEntry)
365372
customDataObject.pseudoElements.forEach(processEntry)
366373

@@ -419,13 +426,17 @@ function processEntry(entry) {
419426
* Todo@Pine: Change MDN data generation so this yields new entry
420427
*/
421428
function convertEntry(entry) {
422-
entry.description = entry.desc
423-
delete entry.desc
429+
if ('desc' in entry) {
430+
entry.description = entry.desc
431+
delete entry.desc
432+
}
424433

425434
if (entry.values) {
426435
entry.values.forEach(v => {
427-
v.description = v.desc
428-
delete v.desc
436+
if ('desc' in v) {
437+
v.description = v.desc
438+
delete v.desc
439+
}
429440

430441
if (v.browsers) {
431442
if (v.browsers === 'all') {

web-data/css/mdn/mdn-browser-compat-data-importer.mjs

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ import bcd from '@mdn/browser-compat-data' with { type: 'json' };
88
export function addBrowserCompatDataToProperties(atdirectives, pseudoclasses, pseudoelements, properties) {
99
atdirectives.forEach(item => {
1010
addCompatData(item, 'at-rules', item.name.slice(1));
11+
for (const descriptor of item.descriptors || []) {
12+
addCompatData(descriptor, 'at-rules', item.name.slice(1), descriptor.name);
13+
}
1114
});
1215

1316
pseudoclasses.forEach(item => {
@@ -33,19 +36,31 @@ export function addBrowserCompatDataToProperties(atdirectives, pseudoclasses, ps
3336
});
3437
}
3538

36-
function addCompatData(item, namespace, featureName) {
39+
function addCompatData(item, namespace, featureName, subField) {
3740
if (!(featureName in bcd.css[namespace])) {
3841
return;
3942
}
4043

41-
const matchingBCDItem = bcd.css[namespace][featureName];
42-
item.bcdKey = `css.${namespace}.${featureName}`;
44+
let matchingBCDItem = bcd.css[namespace][featureName];
45+
if (subField) {
46+
matchingBCDItem = matchingBCDItem[subField]
47+
}
48+
49+
if (!(matchingBCDItem)) {
50+
return;
51+
}
52+
53+
if (subField) {
54+
item.bcdKey = `css.${namespace}.${featureName}.${subField}`;
55+
} else {
56+
item.bcdKey = `css.${namespace}.${featureName}`;
57+
}
4358
addBCDToBrowsers(item, matchingBCDItem);
4459
}
4560

4661
export function addMDNReferences(atdirectives, pseudoclasses, pseudoelements, properties) {
4762
const addReference = (item, matchingItem) => {
48-
if (matchingItem.__compat && matchingItem.__compat.mdn_url) {
63+
if (matchingItem?.__compat?.mdn_url) {
4964
if (!item.references) {
5065
item.references = [];
5166
}
@@ -60,6 +75,9 @@ export function addMDNReferences(atdirectives, pseudoclasses, pseudoelements, pr
6075
if (bcd.css['at-rules'][item.name.slice(1)]) {
6176
const matchingBCDItem = bcd.css['at-rules'][item.name.slice(1)];
6277
addReference(item, matchingBCDItem);
78+
for (const descriptor of item.descriptors || []) {
79+
addReference(descriptor, matchingBCDItem[descriptor.name]);
80+
}
6381
}
6482
});
6583

@@ -159,7 +177,7 @@ export function supportToShortCompatString(support, browserAbbrev) {
159177
}
160178

161179
if (version_added) {
162-
if (typeof(version_added) === 'string') {
180+
if (typeof (version_added) === 'string') {
163181
if (version_added.startsWith('≤')) {
164182
version_added = version_added.substring(1);
165183
}
@@ -180,10 +198,10 @@ function isSupported(support) {
180198
}
181199

182200
if (version_added) {
183-
if (typeof(version_added) === 'boolean') {
201+
if (typeof (version_added) === 'boolean') {
184202
return version_added;
185-
} else if (typeof(version_added) === 'string') {
186-
if (typeof(parseInt(version_added)) === 'number') {
203+
} else if (typeof (version_added) === 'string') {
204+
if (typeof (parseInt(version_added)) === 'number') {
187205
return true;
188206
}
189207
}

web-data/css/mdn/mdn-data-importer.mjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ export async function addMDNProperties(vscProperties) {
105105

106106
if (missingDocumentation.length) {
107107
const fetchedDocs = ['{'];
108-
console.log('add to mdn-documentation.ts (propertyDescriptions):');
108+
console.log('add to mdn-documentation.mjs (propertyDescriptions):');
109109
for (let prop of missingDocumentation) {
110110
const doc = await fetchDocFromMDN(prop, propertyMap[prop]?.atRule);
111111
fetchedDocs.push(` '${prop}': \`${doc ?? ''}\`,`);

web-data/css/mdn/mdn-data-selector-importer.mjs

Lines changed: 40 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
import mdnData from 'mdn-data';
99
import mdnCompatData from '@mdn/browser-compat-data' with { type: 'json' };
1010
import { abbreviateStatus } from './mdn-data-importer.mjs';
11-
import { pseudoSelectorDescriptions, pseudoElementDescriptions, fetchDocFromMDN, atDirectiveDescriptions } from './mdn-documentation.mjs';
11+
import { pseudoSelectorDescriptions, pseudoElementDescriptions, fetchDocFromMDN, atDirectiveDescriptions, atDirectiveDescriptorDescriptions } from './mdn-documentation.mjs';
1212

1313
export async function addMDNPseudoElements(vscPseudoElements) {
1414
const mdnSelectors = mdnData.css.selectors;
@@ -26,21 +26,21 @@ export async function addMDNPseudoElements(vscPseudoElements) {
2626
!allPseudoElementNames.includes(selectorName) &&
2727
!allPseudoElementNames.includes(selectorName + '()')
2828
) {
29-
const desc = pseudoElementDescriptions[selectorName] || '';
30-
if (!desc) {
29+
const description = pseudoElementDescriptions[selectorName] || '';
30+
if (!description) {
3131
missingDocumentation.push(selectorName);
3232
}
3333
allPseudoElements.push({
3434
name: selectorName,
35-
desc,
35+
description,
3636
status: abbreviateStatus(selector, mdnCompatProperties[selectorName])
3737
});
3838
}
3939
}
4040
}
4141
if (missingDocumentation.length) {
4242
const fetchedDocs = ['{'];
43-
console.log('add to mdn-documentation.ts (pseudoElementDescriptions):');
43+
console.log('add to mdn-documentation.mjs (pseudoElementDescriptions):');
4444
for (let prop of missingDocumentation) {
4545
const doc = await fetchDocFromMDN(prop.replace(/::/, '_doublecolon_'), undefined);
4646
fetchedDocs.push(` '${prop}': \`${doc ?? ''}\`,`);
@@ -77,21 +77,21 @@ export async function addMDNPseudoSelectors(vscPseudoClasses) {
7777
!allPseudoSelectorNames.includes(selectorName) &&
7878
!allPseudoSelectorNames.includes(selectorName + '()')
7979
) {
80-
const desc = pseudoSelectorDescriptions[selectorName] || '';
81-
if (!desc) {
80+
const description = pseudoSelectorDescriptions[selectorName] || '';
81+
if (!description) {
8282
missingDocumentation.push(selectorName);
8383
}
8484

8585
allPseudoSelectors.push({
8686
name: selectorName,
87-
desc,
87+
description,
8888
status: abbreviateStatus(selector, mdnCompatProperties[selectorName])
8989
});
9090
}
9191
}
9292
}
9393
if (missingDocumentation.length) {
94-
console.log('add to mdn-documentation.ts (pseudoSelectorDescriptions):');
94+
console.log('add to mdn-documentation.mjs (pseudoSelectorDescriptions):');
9595
const fetchedDocs = ['{'];
9696
for (let prop of missingDocumentation) {
9797
const doc = await fetchDocFromMDN(prop.replace(/:/, '_colon_'), undefined);
@@ -112,21 +112,21 @@ export async function addMDNAtDirectives(atDirectives) {
112112

113113
for (const name of Object.keys(mdnAtRules)) {
114114
if (!allAtDirectiveNames.includes(name)) {
115-
const desc = atDirectiveDescriptions[name] || '';
116-
if (!desc) {
115+
const description = atDirectiveDescriptions[name] || '';
116+
if (!description) {
117117
missingDocumentation.push(name);
118118
}
119119

120120
allAtDirectives.push({
121121
name: name,
122-
desc: desc,
122+
description,
123123
browsers: undefined
124124
});
125125
}
126126
}
127127

128128
if (missingDocumentation.length) {
129-
console.log('add to mdn-documentation.ts (atDirectiveDescriptions):');
129+
console.log('add to mdn-documentation.mjs (atDirectiveDescriptions):');
130130
const fetchedDocs = ['{'];
131131
for (let prop of missingDocumentation) {
132132
const doc = await fetchDocFromMDN(prop.replace(/:/, '_colon_'), undefined);
@@ -136,5 +136,32 @@ export async function addMDNAtDirectives(atDirectives) {
136136
console.log(fetchedDocs.join('\n'));
137137
}
138138

139+
for (const directive of allAtDirectives) {
140+
const missingDocumentation = [];
141+
if (directive.descriptors) {
142+
const descriptorDescriptions = atDirectiveDescriptorDescriptions[directive.name] || {}
143+
for (const descriptor of directive.descriptors) {
144+
const description = descriptorDescriptions[descriptor.name]
145+
if (description !== undefined) {
146+
descriptor.description = description
147+
descriptor.status = abbreviateStatus(descriptor.name, mdnCompatData.css['at-rules'][directive.name.slice(1)]?.[descriptor.name])
148+
} else {
149+
missingDocumentation.push(descriptor.name)
150+
}
151+
}
152+
}
153+
154+
if (missingDocumentation.length) {
155+
console.log(`add to mdn-documentation.mjs (atDirectiveDescriptorDescriptions['${directive.name}']):`);
156+
const fetchedDocs = ['{'];
157+
for (const descriptor of missingDocumentation) {
158+
const doc = await fetchDocFromMDN(descriptor, directive.name);
159+
fetchedDocs.push(` '${descriptor}': \`${doc ?? ''}\`,`);
160+
}
161+
fetchedDocs.push('}');
162+
console.log(fetchedDocs.join('\n'));
163+
}
164+
}
165+
139166
return allAtDirectives;
140167
}

web-data/css/mdn/mdn-documentation.mjs

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -398,4 +398,49 @@ export const atDirectiveDescriptions = {
398398
'@view-transition': `The @view-transition CSS at-rule is used to opt in the current and destination documents to undergo a view transition, in the case of a cross-document navigation.`,
399399
};
400400

401-
401+
export const atDirectiveDescriptorDescriptions = {
402+
'@media': {
403+
'width': `The width CSS media feature can be used to test the width of the viewport (or the page box, for paged media).`,
404+
'height': `The height CSS media feature can be used to apply styles based on the height of the viewport (or the page box, for paged media).`,
405+
'aspect-ratio': `The aspect-ratio CSS media feature can be used to test the aspect ratio of the viewport.`,
406+
'orientation': `The orientation CSS media feature can be used to test the orientation of the viewport (or the page box, for paged media).`,
407+
'overflow-block': `The overflow-block CSS media feature can be used to test how the output device handles content that overflows the initial containing block along the block axis.`,
408+
'overflow-inline': `The overflow-inline CSS media feature can be used to test how the output device handles content that overflows the initial containing block along the inline axis.`,
409+
'horizontal-viewport-segments': ``,
410+
'vertical-viewport-segments': ``,
411+
'display-mode': `The display-mode CSS media feature can be used to test whether a web app is being displayed in a normal browser tab or in some alternative way, such as a standalone app or fullscreen mode.`,
412+
'resolution': `The resolution CSS media feature can be used to test the pixel density of the output device.`,
413+
'scan': `The scan CSS media feature is used to apply CSS styles based on the scanning process of the output device.`,
414+
'grid': `The grid CSS media feature can be used to test whether the output device uses a grid-based screen.`,
415+
'update': `The update CSS media feature can be used to test how frequently (if at all) the output device is able to modify the appearance of content once rendered.`,
416+
'environment-blending': ``,
417+
'color': `The color CSS media feature can be used to test the number of bits per color component (red, green, blue) of the output device.`,
418+
'color-index': `The color-index CSS media feature can be used to test the number of entries in the output device's color lookup table.`,
419+
'monochrome': `The monochrome CSS media feature can be used to test the number of bits per pixel in the monochrome frame buffer of the output device.`,
420+
'color-gamut': `The color-gamut CSS media feature is used to apply CSS styles based on the approximate range of color gamut supported by the user agent and the output device.`,
421+
'dynamic-range': `The dynamic-range CSS media feature can be used to test the combination of brightness, contrast ratio, and color depth that are supported by the user agent and the output device.`,
422+
'inverted-colors': `The inverted-colors CSS media feature is used to test if the user agent or the underlying operating system has inverted all colors.`,
423+
'pointer': `The pointer CSS media feature tests whether the user has a pointing device (such as a mouse), and if so, how accurate the primary pointing device is.`,
424+
'hover': `The hover CSS media feature can be used to test whether the user's primary input mechanism can hover over elements.`,
425+
'any-pointer': `The any-pointer CSS media feature tests whether the user has any pointing device (such as a mouse), and if so, how accurate it is.`,
426+
'any-hover': `The any-hover CSS media feature can be used to test whether any available input mechanism can hover over elements.`,
427+
'nav-controls': ``,
428+
'video-color-gamut': ``,
429+
'video-dynamic-range': `The video-dynamic-range CSS media feature can be used to test the combination of brightness, contrast ratio, and color depth that are supported by the video plane of the user agent and the output device.`,
430+
'scripting': `The scripting CSS media feature can be used to test whether scripting (such as JavaScript) is available.`,
431+
'prefers-reduced-motion': `The prefers-reduced-motion CSS media feature is used to detect if a user has enabled a setting on their device to minimize the amount of non-essential motion. The setting is used to convey to the browser on the device that the user prefers an interface that removes, reduces, or replaces motion-based animations.`,
432+
'prefers-reduced-transparency': `The prefers-reduced-transparency CSS media feature is used to detect if a user has enabled a setting on their device to reduce the transparent or translucent layer effects used on the device. Switching on such a setting can help improve contrast and readability for some users.`,
433+
'prefers-contrast': `The prefers-contrast CSS media feature is used to detect whether the user has requested the web content to be presented with a lower or higher contrast.`,
434+
'forced-colors': `The forced-colors CSS media feature is used to detect if the user agent has enabled a forced colors mode where it enforces a user-chosen limited color palette on the page. An example of a forced colors mode is Windows High Contrast mode.`,
435+
'prefers-color-scheme': `The prefers-color-scheme CSS media feature is used to detect if a user has requested light or dark color themes.`,
436+
'prefers-reduced-data': `The prefers-reduced-data CSS media feature is used to detect if the user has requested the web content that consumes less internet traffic.`,
437+
'device-width': `The device-width CSS media feature can be used to test the width of an output device's rendering surface.`,
438+
'device-height': `The device-height CSS media feature can be used to test the height of an output device's rendering surface.`,
439+
'device-aspect-ratio': `The device-aspect-ratio CSS media feature can be used to test the width-to-height aspect ratio of an output device.`,
440+
'horizontal-viewport-segments': ``,
441+
'vertical-viewport-segments': ``,
442+
'environment-blending': ``,
443+
'nav-controls': ``,
444+
'video-color-gamut': ``,
445+
},
446+
}

0 commit comments

Comments
 (0)