├── .editorconfig ├── .eslintrc ├── .github └── workflows │ └── gh-pages.yml ├── .gitignore ├── README.md ├── data └── .gitignore ├── package-lock.json ├── package.json ├── scripts ├── download-css.js ├── download-sites.js ├── sync-usage-dict.js ├── test.js ├── usage │ ├── Atrule.json │ ├── Declaration.json │ ├── Dimension.json │ ├── Function.json │ ├── MediaFeature.json │ ├── PseudoClassSelector.json │ ├── PseudoElementSelector.json │ └── index.js └── utils.js └── ui ├── data.js ├── index.js ├── page ├── default.css ├── default.js ├── usage.css └── usage.js ├── prepare.js └── view ├── chart.css ├── chart.js └── sidebar.js /.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 4 8 | end_of_line = lf 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "node": true, 4 | "mocha": true, 5 | "es6": true 6 | }, 7 | "parserOptions": { 8 | "ecmaVersion": 2022, 9 | "sourceType": "module" 10 | }, 11 | "globals": { 12 | "discovery": "readonly" 13 | }, 14 | "rules": { 15 | "no-duplicate-case": 2, 16 | "no-undef": 2, 17 | "no-unused-vars": [ 18 | 2, 19 | { 20 | "vars": "all", 21 | "args": "after-used" 22 | } 23 | ], 24 | "no-empty": [ 25 | 2, 26 | { 27 | "allowEmptyCatch": true 28 | } 29 | ], 30 | "no-implicit-coercion": [ 31 | 2, 32 | { 33 | "boolean": true, 34 | "string": true, 35 | "number": true 36 | } 37 | ], 38 | "no-with": 2, 39 | "brace-style": 2, 40 | "no-mixed-spaces-and-tabs": 2, 41 | "no-multiple-empty-lines": 2, 42 | "no-multi-str": 2, 43 | "dot-location": [ 44 | 2, 45 | "property" 46 | ], 47 | "operator-linebreak": [ 48 | 2, 49 | "after", 50 | { 51 | "overrides": { 52 | "?": "before", 53 | ":": "before" 54 | } 55 | } 56 | ], 57 | "key-spacing": [ 58 | 2, 59 | { 60 | "beforeColon": false, 61 | "afterColon": true 62 | } 63 | ], 64 | "space-unary-ops": [ 65 | 2, 66 | { 67 | "words": false, 68 | "nonwords": false 69 | } 70 | ], 71 | "no-spaced-func": 2, 72 | "space-before-function-paren": [ 73 | 2, 74 | { 75 | "anonymous": "ignore", 76 | "named": "never" 77 | } 78 | ], 79 | "array-bracket-spacing": [ 80 | 2, 81 | "never" 82 | ], 83 | "space-in-parens": [ 84 | 2, 85 | "never" 86 | ], 87 | "comma-dangle": [ 88 | 2, 89 | "never" 90 | ], 91 | "no-trailing-spaces": 2, 92 | "yoda": [ 93 | 2, 94 | "never" 95 | ], 96 | "camelcase": [ 97 | 2, 98 | { 99 | "properties": "never" 100 | } 101 | ], 102 | "comma-style": [ 103 | 2, 104 | "last" 105 | ], 106 | "curly": [ 107 | 2, 108 | "all" 109 | ], 110 | "dot-notation": 2, 111 | "eol-last": 2, 112 | "one-var": [ 113 | 2, 114 | "never" 115 | ], 116 | "wrap-iife": 2, 117 | "space-infix-ops": 2, 118 | "keyword-spacing": [ 119 | 2, 120 | { 121 | "overrides": { 122 | "else": { 123 | "before": true 124 | }, 125 | "while": { 126 | "before": true 127 | }, 128 | "catch": { 129 | "before": true 130 | }, 131 | "finally": { 132 | "before": true 133 | } 134 | } 135 | } 136 | ], 137 | "spaced-comment": [ 138 | 2, 139 | "always" 140 | ], 141 | "space-before-blocks": [ 142 | 2, 143 | "always" 144 | ], 145 | "semi": [ 146 | 2, 147 | "always" 148 | ], 149 | "indent": [ 150 | 2, 151 | 4, 152 | { 153 | "SwitchCase": 1 154 | } 155 | ], 156 | "linebreak-style": [ 157 | 2, 158 | "unix" 159 | ], 160 | "quotes": [ 161 | 2, 162 | "single", 163 | { 164 | "avoidEscape": true 165 | } 166 | ] 167 | } 168 | } 169 | -------------------------------------------------------------------------------- /.github/workflows/gh-pages.yml: -------------------------------------------------------------------------------- 1 | name: Deploy to GH-pages 2 | 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | count: 7 | description: 'Site count' 8 | default: '25' 9 | 10 | # A workflow run is made up of one or more jobs that can run sequentially or in parallel 11 | jobs: 12 | # This workflow contains a single job called "build" 13 | build: 14 | # The type of runner that the job will run on 15 | runs-on: ubuntu-latest 16 | 17 | # Steps represent a sequence of tasks that will be executed as part of the job 18 | steps: 19 | # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it 20 | - uses: actions/checkout@v2 21 | - name: Use Node.js 22 | uses: actions/setup-node@v1 23 | with: 24 | node-version: '14.x' 25 | - run: npm ci 26 | - run: npm run sync:sites 27 | - run: npm run download:css ${{ github.event.inputs.count }} 28 | - run: npm test 29 | - run: npm run build 30 | - name: Deploy 31 | uses: peaceiris/actions-gh-pages@v3 32 | with: 33 | github_token: ${{ secrets.GITHUB_TOKEN }} 34 | publish_dir: ./build 35 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules/ 2 | /.vscode/ 3 | /build/ 4 | npm-debug.log 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Real Web CSS 2 | 3 | Real site's CSS usage data analysis. Powered by [CSSTree](https://github.com/csstree/csstree) & [Discoveryjs](https://github.com/discoveryjs/discovery) 4 | 5 | [Real Web CSS](https://csstree.github.io/real-web-css/) 6 | 7 | ## How to use locally 8 | 9 | 1. Download [The Majestic Million](https://majestic.com/reports/majestic-million)'s site list and make a top of site (output `data/sites.csv`): 10 | 11 | ``` 12 | npm run sync:sites 13 | ``` 14 | 15 | 1. Download a top site's CSS (output `data/css/*.css`): 16 | 17 | ``` 18 | npm run download:css 19 | ``` 20 | 21 | > If something goes wrong on CSS downloading (freezing, network issue or errors on console), you can abort the process. Downloading will continue from the last successful processed site. You can set the start site by editing `data/idx.txt` file (if file doesn't exist downloading is start from the first one), which contains just a single number – site index. 22 | 23 | You can specify a number of sites to be used for CSS downloading (25 by default): 24 | 25 | ``` 26 | npm run download:css 100 27 | ``` 28 | 29 | 1. Extract data from downloaded CSS (output `data/test-results.json`): 30 | 31 | ``` 32 | npm test 33 | ``` 34 | 35 | 1. Discover data 36 | 37 | There are two options: 38 | 39 | - Run a server that provides user interface: 40 | 41 | ``` 42 | npm start 43 | ``` 44 | 45 | - Build static version (no server is required, see report in `build` folder): 46 | 47 | ``` 48 | npm run build 49 | ``` 50 | -------------------------------------------------------------------------------- /data/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | !.npmignore 4 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "real-web-css", 3 | "version": "1.0.0", 4 | "description": "Using CSSTree for real site CSS", 5 | "author": "Roman Dvornov ", 6 | "license": "CC-BY-SA 3.0", 7 | "repository": "csstree/real-web-css", 8 | "keywords": [], 9 | "discovery": "ui", 10 | "scripts": { 11 | "start": "discovery", 12 | "test": "node scripts/test", 13 | "build": "discovery-build", 14 | "download:css": "node scripts/download-css", 15 | "sync:sites": "node scripts/download-sites", 16 | "sync:usage": "node scripts/sync-usage-dict" 17 | }, 18 | "dependencies": { 19 | "@discoveryjs/cli": "^2.2.1", 20 | "@discoveryjs/discovery": "^1.0.0-beta.70", 21 | "css-tree": "^2.3.1", 22 | "csstree-validator": "^3.0.0", 23 | "eslint": "^8.22.0", 24 | "fixed-width-string": "^1.0.0", 25 | "known-css-properties": "^0.25.0", 26 | "puppeteer": "^16.1.1" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /scripts/download-css.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const fs = require('fs'); 3 | const puppeteer = require('puppeteer'); 4 | const sites = require('./utils').getSiteList(process.argv[2]); 5 | const seedFile = path.join(__dirname, '../data/idx.txt'); 6 | const outputDir = path.join(__dirname, '../data/css'); 7 | 8 | if (!fs.existsSync(outputDir)) { 9 | fs.mkdirSync(outputDir, { recursive: true }); 10 | } 11 | 12 | async function downloadSiteCSS(browser, siteIdx, siteUrl) { 13 | const page = await browser.newPage(); 14 | const styleSheetResponses = []; 15 | const requests = new Set(); 16 | const awaitRequests = new Map(); 17 | 18 | // set up page listeners 19 | await page.setRequestInterception(true); 20 | page.on('request', request => { 21 | // console.info('Requesting', request.url) 22 | 23 | if (['image', 'font', 'other'].includes(request.resourceType())) { 24 | return request.abort(); 25 | } 26 | 27 | request.continue(); 28 | requests.add(request); 29 | awaitRequests.set(request, { 30 | id: requests.size - 1, 31 | startTime: Date.now() 32 | }); 33 | }); 34 | page.on('requestfinished', request => { 35 | // console.info('requestfinished', request.url) 36 | awaitRequests.delete(request); 37 | }); 38 | page.on('requestfailed', request => { 39 | // console.info('requestfailed', request.url) 40 | awaitRequests.delete(request); 41 | }); 42 | page.on('response', response => { 43 | if (response.request().resourceType() === 'stylesheet') { 44 | const url = response.url(); 45 | const isDataUrl = /^data:/i.test(url); 46 | 47 | console.log(' ✅ ' + (isDataUrl ? '' : url)); 48 | styleSheetResponses.push(response.text() 49 | .then( 50 | content => ({ content }), 51 | error => ({ error: error.message }) 52 | ) 53 | .then(payload => { 54 | if (isDataUrl) { 55 | return { 56 | type: 'dataurl', 57 | ...payload 58 | }; 59 | } 60 | 61 | return { 62 | type: 'external', 63 | url, 64 | ...payload 65 | }; 66 | }) 67 | ); 68 | } 69 | }); 70 | 71 | // timer to track long requests 72 | const trackRequestsIntervalTimer = setInterval(() => { 73 | console.log(` ⏳ Await ${awaitRequests.size} / ${requests.size} requests`); 74 | 75 | for (const [request, { id, startTime }] of awaitRequests) { 76 | if (Date.now() - startTime > 5000) { 77 | console.log(` 🐢 #${id} ${request.url()} ${((Date.now() - startTime) / 1000).toFixed(1)}s`); 78 | } 79 | } 80 | }, 1000); 81 | 82 | // load page 83 | let gotoOk = true; 84 | try { 85 | await Promise.race([ 86 | page.goto(siteUrl), 87 | new Promise((_, reject) => setTimeout(() => { 88 | if (requests.size <= 1) { 89 | reject(new Error('No response from site in 5000 ms')); 90 | } 91 | }, 5000)) 92 | ]); 93 | console.log(' 🏁 Page loaded'); 94 | 95 | // some requests may occur after page.goto(), await such requests but 15 sec max 96 | const maxAwaitTime = Date.now() + 15000; 97 | do { 98 | await new Promise(r => setTimeout(r, 200)); 99 | } while (awaitRequests.size > 0 && Date.now() < maxAwaitTime); 100 | } catch (e) { 101 | gotoOk = false; 102 | console.error(' ❌ Page.goto failed', e.message); 103 | } finally { 104 | clearInterval(trackRequestsIntervalTimer); 105 | } 106 | 107 | // extract CSS content 108 | const externalStyleSheets = await Promise.all(styleSheetResponses); 109 | const inlineStyleSheets = gotoOk ? await page.evaluate(() => 110 | // eslint-disable-next-line no-undef 111 | [...document.styleSheets] 112 | .map(sheet => sheet.ownerNode.textContent) 113 | .filter(Boolean) 114 | .map(content => ({ 115 | type: 'inline', 116 | content 117 | })) 118 | ) : []; 119 | 120 | if (externalStyleSheets.length || inlineStyleSheets.length) { 121 | console.log(` 🔸 Stylesheets found: ${[ 122 | externalStyleSheets.length ? `${externalStyleSheets.length} external` : '', 123 | inlineStyleSheets.length ? `${inlineStyleSheets.length} inline` : '' 124 | ].filter(Boolean).join(', ') || 'none'}`); 125 | } else { 126 | console.log(' ❌ No CSS found'); 127 | } 128 | 129 | // close page to free resources 130 | await page.close(); 131 | 132 | return externalStyleSheets.concat(inlineStyleSheets); 133 | } 134 | 135 | async function main() { 136 | // where are we in the list of URLs 137 | let siteIdx = fs.existsSync(seedFile) ? parseInt(fs.readFileSync(seedFile)) : 0; 138 | if (siteIdx >= sites.length) { 139 | siteIdx = 0; 140 | } 141 | 142 | console.log(`Download CSS for sites #${siteIdx}...${sites.length - 1}`); 143 | console.log(); 144 | 145 | // create a browser 146 | const browser = await puppeteer.launch({ 147 | ignoreHTTPSErrors: true 148 | }); 149 | 150 | process.on('exit', async () => await browser.close()); 151 | 152 | for (const siteUrl of sites.slice(siteIdx)) { 153 | console.log('Visit site #' + siteIdx + ' 🚀 ' + siteUrl); 154 | 155 | try { 156 | const outputFilename = path.join(outputDir, String(siteIdx).padStart(3, '0') + '.json'); 157 | const stylesheets = await downloadSiteCSS(browser, siteIdx, 'http://' + siteUrl); 158 | 159 | // write data to file 160 | console.log(' 💾 Write data to ' + path.relative(process.cwd(), outputFilename)); 161 | fs.writeFileSync(outputFilename, JSON.stringify({ 162 | id: siteIdx, 163 | url: siteUrl, 164 | datetime: new Date(), 165 | stylesheets 166 | }), 'utf8'); 167 | 168 | console.log(' 🎉 DONE'); 169 | console.log(); 170 | } catch (e) { 171 | console.error(' ❌ Failed ', e); 172 | console.error(); 173 | } 174 | 175 | // remember the place in the likely scenario that 176 | fs.writeFileSync(seedFile, String(++siteIdx)); 177 | } 178 | 179 | // we're done! 180 | console.log('DONE 🎉'); 181 | process.exit(); 182 | } 183 | 184 | main(); 185 | -------------------------------------------------------------------------------- /scripts/download-sites.js: -------------------------------------------------------------------------------- 1 | const { get } = require('https'); 2 | const path = require('path'); 3 | const fs = require('fs'); 4 | const url = 'https://downloads.majestic.com/majestic_million.csv'; 5 | const outputRawFile = path.join(__dirname, '../data/sites-raw.csv'); 6 | const outputFile = path.join(__dirname, '../data/sites.csv'); 7 | 8 | function bytes(n) { 9 | const units = ['bytes', 'Kb', 'MB']; 10 | 11 | while (n > 1000 && units.length > 1) { 12 | n /= 1000; 13 | units.shift(); 14 | } 15 | 16 | return `${Number.isInteger(n) ? n : n.toFixed(1)}${units[0]}`; 17 | } 18 | 19 | console.log('Download ' + url + ' ...'); 20 | get(url, function(response) { 21 | const size = response.headers['content-length']; 22 | let lastDownload = 0; 23 | let downloaded = 0; 24 | 25 | const timer = setInterval(function() { 26 | if (lastDownload === downloaded) { 27 | return; 28 | } 29 | 30 | lastDownload = downloaded; 31 | console.log(`${(100 * downloaded / size).toFixed(1).padStart(4)}% (${bytes(downloaded)})`); 32 | }, 200); 33 | 34 | response 35 | .pipe(fs.createWriteStream(outputRawFile)); 36 | 37 | response 38 | .on('data', function(chunk) { 39 | downloaded += chunk.length; 40 | }) 41 | .on('end', function() { 42 | clearInterval(timer); 43 | 44 | console.log(' 100% (' + bytes(downloaded) + ')'); 45 | console.log('DONE'); 46 | console.log(''); 47 | 48 | console.log('Write to ' + outputFile); 49 | fs.writeFileSync( 50 | outputFile, 51 | fs.readFileSync(outputRawFile, 'utf8') 52 | .split(/\r\n?|\n/) 53 | .slice(1) 54 | .map((line) => line.split(',')[2]) 55 | .join('\n'), 56 | 'utf8' 57 | ); 58 | }); 59 | }); 60 | -------------------------------------------------------------------------------- /scripts/sync-usage-dict.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const { lexer } = require('css-tree'); 3 | const knownCssProperties = require('known-css-properties').all; 4 | 5 | const csstreeDict = lexer.dump(); 6 | const usageFilenameDir = __dirname + '/usage/'; 7 | 8 | function sync(type, syncValid, ignoreCurrent) { 9 | const current = JSON.parse(fs.readFileSync(`${usageFilenameDir}${type}.json`)); 10 | const invalid = new Set(current.invalid); 11 | const valid = new Set([ 12 | ...ignoreCurrent ? [] : current.valid, 13 | ...syncValid 14 | ]); 15 | 16 | for (const name of valid) { 17 | invalid.delete(name); 18 | } 19 | 20 | for (const name of current.valid) { 21 | if (!valid.has(name)) { 22 | invalid.add(name); 23 | } 24 | } 25 | 26 | fs.writeFileSync(`${usageFilenameDir}${type}.json`, JSON.stringify({ 27 | ...current, 28 | valid: [...valid].sort(), 29 | invalid: [...invalid].sort() 30 | }, null, 4) + '\n'); 31 | 32 | } 33 | 34 | // declaration 35 | sync('Declaration', [ 36 | ...knownCssProperties, 37 | ...Object.keys(csstreeDict.properties).filter(name => name !== '--*') 38 | ]); 39 | 40 | // atrule 41 | sync('Atrule', Object.keys(csstreeDict.atrules)); 42 | 43 | // demension units 44 | sync('Dimension', [].concat(...Object.values(csstreeDict.units)), true); 45 | -------------------------------------------------------------------------------- /scripts/test.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const fs = require('fs'); 3 | const { URL } = require('url'); 4 | const csstree = require('css-tree'); 5 | const { validate } = require('csstree-validator'); 6 | const { dictionaries } = require('./usage/'); 7 | const cssDir = path.join(__dirname, '../data/css'); 8 | const resultFile = path.join(__dirname, '../data/test-result.json'); 9 | const startTime = Date.now(); 10 | 11 | const dataFiles = fs.readdirSync(cssDir) 12 | .filter(filename => path.extname(filename) === '.json') 13 | .sort(); 14 | 15 | const reports = dataFiles.map(filename => { 16 | const { id, url: siteUrl, datetime, stylesheets } = require(path.join(cssDir, filename)); 17 | const startTime = Date.now(); 18 | 19 | console.log(''); 20 | console.log('Site #' + id + ' ' + siteUrl); 21 | console.log( 22 | ' ' + 23 | stylesheets.length + ' stylesheet(s), ' + 24 | stylesheets 25 | .reduce((size, { content }) => size + (content || '').length, 0) + 26 | 'bytes' 27 | ); 28 | 29 | const report = { 30 | id, 31 | domain: siteUrl, 32 | fetchedAt: datetime, 33 | processedAt: new Date(), 34 | processingTime: 0, 35 | stylesheets: stylesheets.map(({ type, url, content = '', error }) => { 36 | // console.log(` * ${type}${url ? ' ' + url : ''}`); 37 | const errors = []; 38 | 39 | if (error) { 40 | errors.push({ name: 'FetchError', message: error }); 41 | } 42 | 43 | const beforeParse = Date.now(); 44 | const ast = csstree.parse(content, { 45 | filename: url || '', 46 | positions: true, 47 | onParseError: function(e) { 48 | // console.log(' [ERROR] Parsing: ' + e.message); 49 | errors.push({ 50 | name: 'ParseError', 51 | message: e.message, 52 | details: typeof e.sourceFragment === 'function' 53 | ? e.message + '\n' + e.sourceFragment(0) 54 | : e.formattedMessage || e.message 55 | }); 56 | } 57 | }); 58 | const parseTime = Date.now() - beforeParse; 59 | const validateErrors = validate(ast); 60 | const validateTime = Date.now() - parseTime - beforeParse; 61 | const usage = Object.create(null); 62 | const addUsageEntry = (type, name) => { 63 | if (type in usage === false) { 64 | usage[type] = { 65 | [name]: 1 66 | }; 67 | } else { 68 | if (name in usage[type] === false) { 69 | usage[type][name] = 1; 70 | } else { 71 | usage[type][name] += 1; 72 | } 73 | } 74 | }; 75 | 76 | csstree.walk(ast, (node) => { 77 | if (node.type in dictionaries === false) { 78 | return; 79 | } 80 | 81 | let name; 82 | 83 | if (node.type === 'Declaration') { 84 | name = csstree.property(node.property); 85 | 86 | if (name.hack) { 87 | addUsageEntry('Declaration hacks', name.hack, node.loc.start.offset); 88 | } 89 | 90 | if (name.custom) { 91 | return; 92 | } 93 | 94 | name = name.name;// + (name.hack ? ' (with hack: ' + name.hack + ')' : ''); 95 | } else if (node.type === 'Dimension') { 96 | name = node.unit.toLowerCase(); 97 | } else { 98 | name = node.name.toLowerCase(); 99 | if (node.type === 'PseudoClassSelector' || 100 | node.type === 'PseudoElementSelector' || 101 | node.type === 'Function') { 102 | if (node.children !== null) { 103 | name += '()'; 104 | } 105 | } 106 | } 107 | 108 | addUsageEntry(node.type, name, node.loc.start.offset); 109 | }); 110 | 111 | return { 112 | type, 113 | url, 114 | domain: url && new URL(url).hostname, 115 | size: content.length, 116 | usage, 117 | parseTime, 118 | validateTime, 119 | errors: [...errors, ...validateErrors].map(error => ({ 120 | name: error.name, 121 | message: error.message, 122 | ...error, 123 | stack: undefined 124 | })) 125 | }; 126 | }) 127 | }; 128 | 129 | report.processingTime = Date.now() - startTime; 130 | 131 | const errors = report.stylesheets.reduce((res, stylesheet) => { 132 | for (const error of stylesheet.errors) { 133 | if (error.name === 'ParseError') { 134 | res.parse++; 135 | } else { 136 | res.syntax++; 137 | } 138 | } 139 | return res; 140 | }, { parse: 0, syntax: 0 }); 141 | 142 | console.log( 143 | errors.parse 144 | ? ' 😢 Parse errors: ' + errors.parse 145 | : ' ✅ No parse errors' 146 | ); 147 | console.log( 148 | errors.syntax 149 | ? ' 😢 Syntax errors: ' + errors.syntax 150 | : ' ✅ No syntax errors' 151 | ); 152 | 153 | console.log(' ⏳ Finished in ' + report.processingTime + 'ms'); 154 | 155 | return report; 156 | }); 157 | 158 | fs.writeFileSync(resultFile, JSON.stringify(reports, null, 0), 'utf8'); 159 | 160 | console.log(); 161 | console.log('Sites:', reports.length); 162 | console.log('DONE in ' + (Date.now() - startTime) + 'ms'); 163 | -------------------------------------------------------------------------------- /scripts/usage/Atrule.json: -------------------------------------------------------------------------------- 1 | { 2 | "sources": [ 3 | { 4 | "url": "https://developer.mozilla.org/en-US/docs/Web/CSS/At-rule" 5 | }, 6 | { 7 | "url": "https://github.com/tabatkins/parse-css", 8 | "comment": "Standards-based CSS Parser by @tabatkins" 9 | }, 10 | { 11 | "url": "https://drafts.csswg.org/css-page-3/#at-page-rule", 12 | "comment": "https://drafts.csswg.org/css-page-3/#at-page-rule" 13 | } 14 | ], 15 | "valid": [ 16 | "-moz-document", 17 | "-moz-keyframes", 18 | "-ms-viewport", 19 | "-o-keyframes", 20 | "-o-viewport", 21 | "-webkit-keyframes", 22 | "-webkit-viewport", 23 | "annotation", 24 | "bottom-center", 25 | "bottom-left", 26 | "bottom-left-corner", 27 | "bottom-right", 28 | "bottom-right-corner", 29 | "character-variants", 30 | "charset", 31 | "counter-style", 32 | "document", 33 | "font-face", 34 | "font-feature-values", 35 | "import", 36 | "keyframes", 37 | "layer", 38 | "left-bottom", 39 | "left-middle", 40 | "left-top", 41 | "media", 42 | "namespace", 43 | "nest", 44 | "ornaments", 45 | "page", 46 | "property", 47 | "right-bottom", 48 | "right-middle", 49 | "right-top", 50 | "scroll-timeline", 51 | "styleset", 52 | "stylistic", 53 | "supports", 54 | "swash", 55 | "top-center", 56 | "top-left", 57 | "top-left-corner", 58 | "top-right", 59 | "top-right-corner", 60 | "viewport" 61 | ], 62 | "invalid": [ 63 | "-keyframes", 64 | "-moz-viewport", 65 | "-ms-keyframes", 66 | "classic-demo-cdn", 67 | "font", 68 | "galleria-classic-min", 69 | "galleria-flickr-min", 70 | "galleria-history-min", 71 | "galleria-picasa-min", 72 | "inlcude", 73 | "me", 74 | "rotation" 75 | ] 76 | } 77 | -------------------------------------------------------------------------------- /scripts/usage/Declaration.json: -------------------------------------------------------------------------------- 1 | { 2 | "sources": [ 3 | { 4 | "url": "https://github.com/betit/known-css-properties/blob/master/data/all.json", 5 | "comment": "`known-css-properties` package" 6 | }, 7 | { 8 | "url": "https://github.com/Microsoft/vscode-css-languageservice/blob/master/build/css-schema.xml", 9 | "comment": "VS Code CSS language service dictionary" 10 | }, 11 | { 12 | "url": "https://github.com/mdn/data", 13 | "comment": "MDN data" 14 | } 15 | ], 16 | "valid": [ 17 | "-apple-color-filter", 18 | "-apple-dashboard-region", 19 | "-apple-pay-button-style", 20 | "-apple-pay-button-type", 21 | "-apple-trailing-word", 22 | "-epub-caption-side", 23 | "-epub-hyphens", 24 | "-epub-text-combine", 25 | "-epub-text-emphasis", 26 | "-epub-text-emphasis-color", 27 | "-epub-text-emphasis-style", 28 | "-epub-text-orientation", 29 | "-epub-text-transform", 30 | "-epub-word-break", 31 | "-epub-writing-mode", 32 | "-internal-text-autosizing-status", 33 | "-moz-animation", 34 | "-moz-animation-delay", 35 | "-moz-animation-direction", 36 | "-moz-animation-duration", 37 | "-moz-animation-fill-mode", 38 | "-moz-animation-iteration-count", 39 | "-moz-animation-name", 40 | "-moz-animation-play-state", 41 | "-moz-animation-timing-function", 42 | "-moz-appearance", 43 | "-moz-backface-visibility", 44 | "-moz-background-clip", 45 | "-moz-background-inline-policy", 46 | "-moz-background-origin", 47 | "-moz-background-size", 48 | "-moz-binding", 49 | "-moz-border-bottom-colors", 50 | "-moz-border-end", 51 | "-moz-border-end-color", 52 | "-moz-border-end-style", 53 | "-moz-border-end-width", 54 | "-moz-border-image", 55 | "-moz-border-left-colors", 56 | "-moz-border-radius", 57 | "-moz-border-radius-bottomleft", 58 | "-moz-border-radius-bottomright", 59 | "-moz-border-radius-topleft", 60 | "-moz-border-radius-topright", 61 | "-moz-border-right-colors", 62 | "-moz-border-start", 63 | "-moz-border-start-color", 64 | "-moz-border-start-style", 65 | "-moz-border-start-width", 66 | "-moz-border-top-colors", 67 | "-moz-box-align", 68 | "-moz-box-direction", 69 | "-moz-box-flex", 70 | "-moz-box-flexgroup", 71 | "-moz-box-ordinal-group", 72 | "-moz-box-orient", 73 | "-moz-box-pack", 74 | "-moz-box-shadow", 75 | "-moz-box-sizing", 76 | "-moz-column-count", 77 | "-moz-column-fill", 78 | "-moz-column-gap", 79 | "-moz-column-rule", 80 | "-moz-column-rule-color", 81 | "-moz-column-rule-style", 82 | "-moz-column-rule-width", 83 | "-moz-column-span", 84 | "-moz-column-width", 85 | "-moz-columns", 86 | "-moz-context-properties", 87 | "-moz-control-character-visibility", 88 | "-moz-float-edge", 89 | "-moz-font-feature-settings", 90 | "-moz-font-language-override", 91 | "-moz-force-broken-image-icon", 92 | "-moz-hyphens", 93 | "-moz-image-region", 94 | "-moz-margin-end", 95 | "-moz-margin-start", 96 | "-moz-opacity", 97 | "-moz-orient", 98 | "-moz-osx-font-smoothing", 99 | "-moz-outline", 100 | "-moz-outline-color", 101 | "-moz-outline-offset", 102 | "-moz-outline-radius", 103 | "-moz-outline-radius-bottomleft", 104 | "-moz-outline-radius-bottomright", 105 | "-moz-outline-radius-topleft", 106 | "-moz-outline-radius-topright", 107 | "-moz-outline-style", 108 | "-moz-outline-width", 109 | "-moz-padding-end", 110 | "-moz-padding-start", 111 | "-moz-perspective", 112 | "-moz-perspective-origin", 113 | "-moz-stack-sizing", 114 | "-moz-tab-size", 115 | "-moz-text-align-last", 116 | "-moz-text-blink", 117 | "-moz-text-decoration-color", 118 | "-moz-text-decoration-line", 119 | "-moz-text-decoration-style", 120 | "-moz-text-size-adjust", 121 | "-moz-transform", 122 | "-moz-transform-origin", 123 | "-moz-transform-style", 124 | "-moz-transition", 125 | "-moz-transition-delay", 126 | "-moz-transition-duration", 127 | "-moz-transition-property", 128 | "-moz-transition-timing-function", 129 | "-moz-user-focus", 130 | "-moz-user-input", 131 | "-moz-user-modify", 132 | "-moz-user-select", 133 | "-moz-window-dragging", 134 | "-moz-window-shadow", 135 | "-ms-accelerator", 136 | "-ms-animation", 137 | "-ms-animation-delay", 138 | "-ms-animation-direction", 139 | "-ms-animation-duration", 140 | "-ms-animation-fill-mode", 141 | "-ms-animation-iteration-count", 142 | "-ms-animation-name", 143 | "-ms-animation-play-state", 144 | "-ms-animation-timing-function", 145 | "-ms-backface-visibility", 146 | "-ms-behavior", 147 | "-ms-block-progression", 148 | "-ms-content-zoom-chaining", 149 | "-ms-content-zoom-limit", 150 | "-ms-content-zoom-limit-max", 151 | "-ms-content-zoom-limit-min", 152 | "-ms-content-zoom-snap", 153 | "-ms-content-zoom-snap-points", 154 | "-ms-content-zoom-snap-type", 155 | "-ms-content-zooming", 156 | "-ms-filter", 157 | "-ms-flex", 158 | "-ms-flex-align", 159 | "-ms-flex-direction", 160 | "-ms-flex-flow", 161 | "-ms-flex-item-align", 162 | "-ms-flex-line-pack", 163 | "-ms-flex-negative", 164 | "-ms-flex-order", 165 | "-ms-flex-pack", 166 | "-ms-flex-positive", 167 | "-ms-flex-preferred-size", 168 | "-ms-flex-wrap", 169 | "-ms-flow-from", 170 | "-ms-flow-into", 171 | "-ms-font-feature-settings", 172 | "-ms-grid-column", 173 | "-ms-grid-column-align", 174 | "-ms-grid-column-span", 175 | "-ms-grid-columns", 176 | "-ms-grid-layer", 177 | "-ms-grid-row", 178 | "-ms-grid-row-align", 179 | "-ms-grid-row-span", 180 | "-ms-grid-rows", 181 | "-ms-high-contrast-adjust", 182 | "-ms-hyphenate-limit-chars", 183 | "-ms-hyphenate-limit-last", 184 | "-ms-hyphenate-limit-lines", 185 | "-ms-hyphenate-limit-zone", 186 | "-ms-hyphens", 187 | "-ms-ime-align", 188 | "-ms-ime-mode", 189 | "-ms-interpolation-mode", 190 | "-ms-layout-grid", 191 | "-ms-layout-grid-char", 192 | "-ms-layout-grid-line", 193 | "-ms-layout-grid-mode", 194 | "-ms-layout-grid-type", 195 | "-ms-line-break", 196 | "-ms-overflow-style", 197 | "-ms-perspective", 198 | "-ms-perspective-origin", 199 | "-ms-perspective-origin-x", 200 | "-ms-perspective-origin-y", 201 | "-ms-progress-appearance", 202 | "-ms-scroll-chaining", 203 | "-ms-scroll-limit", 204 | "-ms-scroll-limit-x-max", 205 | "-ms-scroll-limit-x-min", 206 | "-ms-scroll-limit-y-max", 207 | "-ms-scroll-limit-y-min", 208 | "-ms-scroll-rails", 209 | "-ms-scroll-snap-points-x", 210 | "-ms-scroll-snap-points-y", 211 | "-ms-scroll-snap-type", 212 | "-ms-scroll-snap-x", 213 | "-ms-scroll-snap-y", 214 | "-ms-scroll-translation", 215 | "-ms-scrollbar-3dlight-color", 216 | "-ms-scrollbar-arrow-color", 217 | "-ms-scrollbar-base-color", 218 | "-ms-scrollbar-darkshadow-color", 219 | "-ms-scrollbar-face-color", 220 | "-ms-scrollbar-highlight-color", 221 | "-ms-scrollbar-shadow-color", 222 | "-ms-scrollbar-track-color", 223 | "-ms-text-align-last", 224 | "-ms-text-autospace", 225 | "-ms-text-combine-horizontal", 226 | "-ms-text-justify", 227 | "-ms-text-kashida-space", 228 | "-ms-text-overflow", 229 | "-ms-text-size-adjust", 230 | "-ms-text-underline-position", 231 | "-ms-touch-action", 232 | "-ms-touch-select", 233 | "-ms-transform", 234 | "-ms-transform-origin", 235 | "-ms-transform-origin-x", 236 | "-ms-transform-origin-y", 237 | "-ms-transform-origin-z", 238 | "-ms-transform-style", 239 | "-ms-transition", 240 | "-ms-transition-delay", 241 | "-ms-transition-duration", 242 | "-ms-transition-property", 243 | "-ms-transition-timing-function", 244 | "-ms-user-select", 245 | "-ms-word-break", 246 | "-ms-word-wrap", 247 | "-ms-wrap-flow", 248 | "-ms-wrap-margin", 249 | "-ms-wrap-through", 250 | "-ms-writing-mode", 251 | "-ms-zoom", 252 | "-ms-zoom-animation", 253 | "-o-animation", 254 | "-o-animation-delay", 255 | "-o-animation-direction", 256 | "-o-animation-duration", 257 | "-o-animation-fill-mode", 258 | "-o-animation-iteration-count", 259 | "-o-animation-name", 260 | "-o-animation-play-state", 261 | "-o-animation-timing-function", 262 | "-o-border-image", 263 | "-o-link", 264 | "-o-link-source", 265 | "-o-object-fit", 266 | "-o-object-position", 267 | "-o-tab-size", 268 | "-o-table-baseline", 269 | "-o-text-overflow", 270 | "-o-transform", 271 | "-o-transform-origin", 272 | "-o-transition", 273 | "-o-transition-delay", 274 | "-o-transition-duration", 275 | "-o-transition-property", 276 | "-o-transition-timing-function", 277 | "-wap-accesskey", 278 | "-wap-input-format", 279 | "-wap-input-required", 280 | "-wap-marquee-dir", 281 | "-wap-marquee-loop", 282 | "-wap-marquee-speed", 283 | "-wap-marquee-style", 284 | "-webkit-align-content", 285 | "-webkit-align-items", 286 | "-webkit-align-self", 287 | "-webkit-alt", 288 | "-webkit-animation", 289 | "-webkit-animation-delay", 290 | "-webkit-animation-direction", 291 | "-webkit-animation-duration", 292 | "-webkit-animation-fill-mode", 293 | "-webkit-animation-iteration-count", 294 | "-webkit-animation-name", 295 | "-webkit-animation-play-state", 296 | "-webkit-animation-timing-function", 297 | "-webkit-animation-trigger", 298 | "-webkit-app-region", 299 | "-webkit-appearance", 300 | "-webkit-aspect-ratio", 301 | "-webkit-backdrop-filter", 302 | "-webkit-backface-visibility", 303 | "-webkit-background", 304 | "-webkit-background-attachment", 305 | "-webkit-background-clip", 306 | "-webkit-background-color", 307 | "-webkit-background-composite", 308 | "-webkit-background-image", 309 | "-webkit-background-origin", 310 | "-webkit-background-position", 311 | "-webkit-background-position-x", 312 | "-webkit-background-position-y", 313 | "-webkit-background-repeat", 314 | "-webkit-background-size", 315 | "-webkit-border-after", 316 | "-webkit-border-after-color", 317 | "-webkit-border-after-style", 318 | "-webkit-border-after-width", 319 | "-webkit-border-before", 320 | "-webkit-border-before-color", 321 | "-webkit-border-before-style", 322 | "-webkit-border-before-width", 323 | "-webkit-border-bottom-left-radius", 324 | "-webkit-border-bottom-right-radius", 325 | "-webkit-border-end", 326 | "-webkit-border-end-color", 327 | "-webkit-border-end-style", 328 | "-webkit-border-end-width", 329 | "-webkit-border-fit", 330 | "-webkit-border-horizontal-spacing", 331 | "-webkit-border-image", 332 | "-webkit-border-image-outset", 333 | "-webkit-border-image-repeat", 334 | "-webkit-border-image-slice", 335 | "-webkit-border-image-source", 336 | "-webkit-border-image-width", 337 | "-webkit-border-radius", 338 | "-webkit-border-start", 339 | "-webkit-border-start-color", 340 | "-webkit-border-start-style", 341 | "-webkit-border-start-width", 342 | "-webkit-border-top-left-radius", 343 | "-webkit-border-top-right-radius", 344 | "-webkit-border-vertical-spacing", 345 | "-webkit-box-align", 346 | "-webkit-box-decoration-break", 347 | "-webkit-box-direction", 348 | "-webkit-box-flex", 349 | "-webkit-box-flex-group", 350 | "-webkit-box-lines", 351 | "-webkit-box-ordinal-group", 352 | "-webkit-box-orient", 353 | "-webkit-box-pack", 354 | "-webkit-box-reflect", 355 | "-webkit-box-shadow", 356 | "-webkit-box-sizing", 357 | "-webkit-break-after", 358 | "-webkit-break-before", 359 | "-webkit-break-inside", 360 | "-webkit-clip-path", 361 | "-webkit-color-correction", 362 | "-webkit-column-axis", 363 | "-webkit-column-break-after", 364 | "-webkit-column-break-before", 365 | "-webkit-column-break-inside", 366 | "-webkit-column-count", 367 | "-webkit-column-fill", 368 | "-webkit-column-gap", 369 | "-webkit-column-progression", 370 | "-webkit-column-rule", 371 | "-webkit-column-rule-color", 372 | "-webkit-column-rule-style", 373 | "-webkit-column-rule-width", 374 | "-webkit-column-span", 375 | "-webkit-column-width", 376 | "-webkit-columns", 377 | "-webkit-composition-fill-color", 378 | "-webkit-composition-frame-color", 379 | "-webkit-cursor-visibility", 380 | "-webkit-dashboard-region", 381 | "-webkit-filter", 382 | "-webkit-flex", 383 | "-webkit-flex-align", 384 | "-webkit-flex-basis", 385 | "-webkit-flex-direction", 386 | "-webkit-flex-flow", 387 | "-webkit-flex-grow", 388 | "-webkit-flex-item-align", 389 | "-webkit-flex-line-pack", 390 | "-webkit-flex-order", 391 | "-webkit-flex-pack", 392 | "-webkit-flex-shrink", 393 | "-webkit-flex-wrap", 394 | "-webkit-flow-from", 395 | "-webkit-flow-into", 396 | "-webkit-font-feature-settings", 397 | "-webkit-font-kerning", 398 | "-webkit-font-size-delta", 399 | "-webkit-font-smoothing", 400 | "-webkit-font-variant-ligatures", 401 | "-webkit-grid-after", 402 | "-webkit-grid-auto-columns", 403 | "-webkit-grid-auto-flow", 404 | "-webkit-grid-auto-rows", 405 | "-webkit-grid-before", 406 | "-webkit-grid-column", 407 | "-webkit-grid-columns", 408 | "-webkit-grid-end", 409 | "-webkit-grid-row", 410 | "-webkit-grid-rows", 411 | "-webkit-grid-start", 412 | "-webkit-highlight", 413 | "-webkit-hyphenate-character", 414 | "-webkit-hyphenate-limit-after", 415 | "-webkit-hyphenate-limit-before", 416 | "-webkit-hyphenate-limit-lines", 417 | "-webkit-hyphens", 418 | "-webkit-initial-letter", 419 | "-webkit-justify-content", 420 | "-webkit-justify-items", 421 | "-webkit-justify-self", 422 | "-webkit-line-align", 423 | "-webkit-line-box-contain", 424 | "-webkit-line-break", 425 | "-webkit-line-clamp", 426 | "-webkit-line-grid", 427 | "-webkit-line-grid-snap", 428 | "-webkit-line-snap", 429 | "-webkit-locale", 430 | "-webkit-logical-height", 431 | "-webkit-logical-width", 432 | "-webkit-margin-after", 433 | "-webkit-margin-after-collapse", 434 | "-webkit-margin-before", 435 | "-webkit-margin-before-collapse", 436 | "-webkit-margin-bottom-collapse", 437 | "-webkit-margin-collapse", 438 | "-webkit-margin-end", 439 | "-webkit-margin-start", 440 | "-webkit-margin-top-collapse", 441 | "-webkit-marquee", 442 | "-webkit-marquee-direction", 443 | "-webkit-marquee-increment", 444 | "-webkit-marquee-repetition", 445 | "-webkit-marquee-speed", 446 | "-webkit-marquee-style", 447 | "-webkit-mask", 448 | "-webkit-mask-attachment", 449 | "-webkit-mask-box-image", 450 | "-webkit-mask-box-image-outset", 451 | "-webkit-mask-box-image-repeat", 452 | "-webkit-mask-box-image-slice", 453 | "-webkit-mask-box-image-source", 454 | "-webkit-mask-box-image-width", 455 | "-webkit-mask-clip", 456 | "-webkit-mask-composite", 457 | "-webkit-mask-image", 458 | "-webkit-mask-origin", 459 | "-webkit-mask-position", 460 | "-webkit-mask-position-x", 461 | "-webkit-mask-position-y", 462 | "-webkit-mask-repeat", 463 | "-webkit-mask-repeat-x", 464 | "-webkit-mask-repeat-y", 465 | "-webkit-mask-size", 466 | "-webkit-mask-source-type", 467 | "-webkit-match-nearest-mail-blockquote-color", 468 | "-webkit-max-logical-height", 469 | "-webkit-max-logical-width", 470 | "-webkit-min-logical-height", 471 | "-webkit-min-logical-width", 472 | "-webkit-nbsp-mode", 473 | "-webkit-opacity", 474 | "-webkit-order", 475 | "-webkit-overflow-scrolling", 476 | "-webkit-padding-after", 477 | "-webkit-padding-before", 478 | "-webkit-padding-end", 479 | "-webkit-padding-start", 480 | "-webkit-perspective", 481 | "-webkit-perspective-origin", 482 | "-webkit-perspective-origin-x", 483 | "-webkit-perspective-origin-y", 484 | "-webkit-print-color-adjust", 485 | "-webkit-region-break-after", 486 | "-webkit-region-break-before", 487 | "-webkit-region-break-inside", 488 | "-webkit-region-fragment", 489 | "-webkit-region-overflow", 490 | "-webkit-rtl-ordering", 491 | "-webkit-ruby-position", 492 | "-webkit-scroll-snap-coordinate", 493 | "-webkit-scroll-snap-destination", 494 | "-webkit-scroll-snap-points-x", 495 | "-webkit-scroll-snap-points-y", 496 | "-webkit-scroll-snap-type", 497 | "-webkit-shape-image-threshold", 498 | "-webkit-shape-inside", 499 | "-webkit-shape-margin", 500 | "-webkit-shape-outside", 501 | "-webkit-shape-padding", 502 | "-webkit-svg-shadow", 503 | "-webkit-tap-highlight-color", 504 | "-webkit-text-combine", 505 | "-webkit-text-decoration", 506 | "-webkit-text-decoration-color", 507 | "-webkit-text-decoration-line", 508 | "-webkit-text-decoration-skip", 509 | "-webkit-text-decoration-style", 510 | "-webkit-text-decorations-in-effect", 511 | "-webkit-text-emphasis", 512 | "-webkit-text-emphasis-color", 513 | "-webkit-text-emphasis-position", 514 | "-webkit-text-emphasis-style", 515 | "-webkit-text-fill-color", 516 | "-webkit-text-orientation", 517 | "-webkit-text-security", 518 | "-webkit-text-size-adjust", 519 | "-webkit-text-stroke", 520 | "-webkit-text-stroke-color", 521 | "-webkit-text-stroke-width", 522 | "-webkit-text-underline-position", 523 | "-webkit-text-zoom", 524 | "-webkit-touch-callout", 525 | "-webkit-transform", 526 | "-webkit-transform-origin", 527 | "-webkit-transform-origin-x", 528 | "-webkit-transform-origin-y", 529 | "-webkit-transform-origin-z", 530 | "-webkit-transform-style", 531 | "-webkit-transition", 532 | "-webkit-transition-delay", 533 | "-webkit-transition-duration", 534 | "-webkit-transition-property", 535 | "-webkit-transition-timing-function", 536 | "-webkit-user-drag", 537 | "-webkit-user-modify", 538 | "-webkit-user-select", 539 | "-webkit-widget-region", 540 | "-webkit-wrap", 541 | "-webkit-wrap-flow", 542 | "-webkit-wrap-margin", 543 | "-webkit-wrap-padding", 544 | "-webkit-wrap-shape-inside", 545 | "-webkit-wrap-shape-outside", 546 | "-webkit-wrap-through", 547 | "-webkit-writing-mode", 548 | "accelerator", 549 | "accent-color", 550 | "additive-symbols", 551 | "align-content", 552 | "align-items", 553 | "align-self", 554 | "align-tracks", 555 | "alignment-baseline", 556 | "all", 557 | "alt", 558 | "animation", 559 | "animation-composition", 560 | "animation-delay", 561 | "animation-direction", 562 | "animation-duration", 563 | "animation-fill-mode", 564 | "animation-iteration-count", 565 | "animation-name", 566 | "animation-play-state", 567 | "animation-timeline", 568 | "animation-timing-function", 569 | "app-region", 570 | "appearance", 571 | "ascent-override", 572 | "aspect-ratio", 573 | "audio-level", 574 | "azimuth", 575 | "backdrop-filter", 576 | "backface-visibility", 577 | "background", 578 | "background-attachment", 579 | "background-blend-mode", 580 | "background-clip", 581 | "background-color", 582 | "background-image", 583 | "background-image-transform", 584 | "background-origin", 585 | "background-position", 586 | "background-position-x", 587 | "background-position-y", 588 | "background-repeat", 589 | "background-repeat-x", 590 | "background-repeat-y", 591 | "background-size", 592 | "base-palette", 593 | "baseline-shift", 594 | "baseline-source", 595 | "behavior", 596 | "block-ellipsis", 597 | "block-overflow", 598 | "block-size", 599 | "block-step", 600 | "block-step-align", 601 | "block-step-insert", 602 | "block-step-round", 603 | "block-step-size", 604 | "bookmark-label", 605 | "bookmark-level", 606 | "bookmark-state", 607 | "border", 608 | "border-block", 609 | "border-block-color", 610 | "border-block-end", 611 | "border-block-end-color", 612 | "border-block-end-style", 613 | "border-block-end-width", 614 | "border-block-start", 615 | "border-block-start-color", 616 | "border-block-start-style", 617 | "border-block-start-width", 618 | "border-block-style", 619 | "border-block-width", 620 | "border-bottom", 621 | "border-bottom-color", 622 | "border-bottom-left-radius", 623 | "border-bottom-right-radius", 624 | "border-bottom-style", 625 | "border-bottom-width", 626 | "border-boundary", 627 | "border-collapse", 628 | "border-color", 629 | "border-end-end-radius", 630 | "border-end-start-radius", 631 | "border-image", 632 | "border-image-outset", 633 | "border-image-repeat", 634 | "border-image-slice", 635 | "border-image-source", 636 | "border-image-transform", 637 | "border-image-width", 638 | "border-inline", 639 | "border-inline-color", 640 | "border-inline-end", 641 | "border-inline-end-color", 642 | "border-inline-end-style", 643 | "border-inline-end-width", 644 | "border-inline-start", 645 | "border-inline-start-color", 646 | "border-inline-start-style", 647 | "border-inline-start-width", 648 | "border-inline-style", 649 | "border-inline-width", 650 | "border-left", 651 | "border-left-color", 652 | "border-left-style", 653 | "border-left-width", 654 | "border-radius", 655 | "border-right", 656 | "border-right-color", 657 | "border-right-style", 658 | "border-right-width", 659 | "border-spacing", 660 | "border-start-end-radius", 661 | "border-start-start-radius", 662 | "border-style", 663 | "border-top", 664 | "border-top-color", 665 | "border-top-left-radius", 666 | "border-top-right-radius", 667 | "border-top-style", 668 | "border-top-width", 669 | "border-width", 670 | "bottom", 671 | "box-align", 672 | "box-decoration-break", 673 | "box-direction", 674 | "box-flex", 675 | "box-flex-group", 676 | "box-lines", 677 | "box-ordinal-group", 678 | "box-orient", 679 | "box-pack", 680 | "box-shadow", 681 | "box-sizing", 682 | "box-snap", 683 | "box-suppress", 684 | "break-after", 685 | "break-before", 686 | "break-inside", 687 | "buffered-rendering", 688 | "caption-side", 689 | "caret", 690 | "caret-animation", 691 | "caret-color", 692 | "caret-shape", 693 | "chains", 694 | "clear", 695 | "clip", 696 | "clip-path", 697 | "clip-rule", 698 | "color", 699 | "color-adjust", 700 | "color-interpolation", 701 | "color-interpolation-filters", 702 | "color-profile", 703 | "color-rendering", 704 | "color-scheme", 705 | "column-count", 706 | "column-fill", 707 | "column-gap", 708 | "column-progression", 709 | "column-rule", 710 | "column-rule-color", 711 | "column-rule-style", 712 | "column-rule-width", 713 | "column-span", 714 | "column-width", 715 | "columns", 716 | "contain", 717 | "contain-intrinsic-block-size", 718 | "contain-intrinsic-height", 719 | "contain-intrinsic-inline-size", 720 | "contain-intrinsic-size", 721 | "contain-intrinsic-width", 722 | "container", 723 | "container-name", 724 | "container-type", 725 | "content", 726 | "content-visibility", 727 | "continue", 728 | "counter-increment", 729 | "counter-reset", 730 | "counter-set", 731 | "crop", 732 | "cue", 733 | "cue-after", 734 | "cue-before", 735 | "cursor", 736 | "cx", 737 | "cy", 738 | "d", 739 | "descent-override", 740 | "direction", 741 | "display", 742 | "display-align", 743 | "dominant-baseline", 744 | "elevation", 745 | "empty-cells", 746 | "enable-background", 747 | "epub-caption-side", 748 | "epub-hyphens", 749 | "epub-text-combine", 750 | "epub-text-emphasis", 751 | "epub-text-emphasis-color", 752 | "epub-text-emphasis-style", 753 | "epub-text-orientation", 754 | "epub-text-transform", 755 | "epub-word-break", 756 | "epub-writing-mode", 757 | "fallback", 758 | "fill", 759 | "fill-break", 760 | "fill-color", 761 | "fill-image", 762 | "fill-opacity", 763 | "fill-origin", 764 | "fill-position", 765 | "fill-repeat", 766 | "fill-rule", 767 | "fill-size", 768 | "filter", 769 | "flex", 770 | "flex-basis", 771 | "flex-direction", 772 | "flex-flow", 773 | "flex-grow", 774 | "flex-shrink", 775 | "flex-wrap", 776 | "float", 777 | "float-defer", 778 | "float-offset", 779 | "float-reference", 780 | "flood-color", 781 | "flood-opacity", 782 | "flow", 783 | "flow-from", 784 | "flow-into", 785 | "font", 786 | "font-display", 787 | "font-family", 788 | "font-feature-settings", 789 | "font-kerning", 790 | "font-language-override", 791 | "font-max-size", 792 | "font-min-size", 793 | "font-optical-sizing", 794 | "font-palette", 795 | "font-presentation", 796 | "font-size", 797 | "font-size-adjust", 798 | "font-smooth", 799 | "font-stretch", 800 | "font-style", 801 | "font-synthesis", 802 | "font-synthesis-small-caps", 803 | "font-synthesis-style", 804 | "font-synthesis-weight", 805 | "font-variant", 806 | "font-variant-alternates", 807 | "font-variant-caps", 808 | "font-variant-east-asian", 809 | "font-variant-emoji", 810 | "font-variant-ligatures", 811 | "font-variant-numeric", 812 | "font-variant-position", 813 | "font-variation-settings", 814 | "font-weight", 815 | "footnote-display", 816 | "footnote-policy", 817 | "forced-color-adjust", 818 | "gap", 819 | "glyph-orientation-horizontal", 820 | "glyph-orientation-vertical", 821 | "grid", 822 | "grid-area", 823 | "grid-auto-columns", 824 | "grid-auto-flow", 825 | "grid-auto-rows", 826 | "grid-column", 827 | "grid-column-end", 828 | "grid-column-gap", 829 | "grid-column-start", 830 | "grid-gap", 831 | "grid-row", 832 | "grid-row-end", 833 | "grid-row-gap", 834 | "grid-row-start", 835 | "grid-template", 836 | "grid-template-areas", 837 | "grid-template-columns", 838 | "grid-template-rows", 839 | "hanging-punctuation", 840 | "height", 841 | "hyphenate-character", 842 | "hyphenate-limit-chars", 843 | "hyphenate-limit-last", 844 | "hyphenate-limit-lines", 845 | "hyphenate-limit-zone", 846 | "hyphens", 847 | "image-orientation", 848 | "image-rendering", 849 | "image-resolution", 850 | "ime-mode", 851 | "inherits", 852 | "initial-letter", 853 | "initial-letter-align", 854 | "initial-letter-wrap", 855 | "initial-value", 856 | "inline-size", 857 | "inline-sizing", 858 | "input-format", 859 | "input-security", 860 | "inset", 861 | "inset-block", 862 | "inset-block-end", 863 | "inset-block-start", 864 | "inset-inline", 865 | "inset-inline-end", 866 | "inset-inline-start", 867 | "isolation", 868 | "justify-content", 869 | "justify-items", 870 | "justify-self", 871 | "justify-tracks", 872 | "kerning", 873 | "layout-flow", 874 | "layout-grid", 875 | "layout-grid-char", 876 | "layout-grid-line", 877 | "layout-grid-mode", 878 | "layout-grid-type", 879 | "leading-trim", 880 | "left", 881 | "letter-spacing", 882 | "lighting-color", 883 | "line-break", 884 | "line-clamp", 885 | "line-gap-override", 886 | "line-grid", 887 | "line-height", 888 | "line-height-step", 889 | "line-increment", 890 | "line-padding", 891 | "line-snap", 892 | "list-style", 893 | "list-style-image", 894 | "list-style-position", 895 | "list-style-type", 896 | "margin", 897 | "margin-block", 898 | "margin-block-end", 899 | "margin-block-start", 900 | "margin-bottom", 901 | "margin-break", 902 | "margin-inline", 903 | "margin-inline-end", 904 | "margin-inline-start", 905 | "margin-left", 906 | "margin-right", 907 | "margin-top", 908 | "margin-trim", 909 | "marker", 910 | "marker-end", 911 | "marker-knockout-left", 912 | "marker-knockout-right", 913 | "marker-mid", 914 | "marker-offset", 915 | "marker-pattern", 916 | "marker-segment", 917 | "marker-side", 918 | "marker-start", 919 | "marks", 920 | "marquee-direction", 921 | "marquee-loop", 922 | "marquee-speed", 923 | "marquee-style", 924 | "mask", 925 | "mask-border", 926 | "mask-border-mode", 927 | "mask-border-outset", 928 | "mask-border-repeat", 929 | "mask-border-slice", 930 | "mask-border-source", 931 | "mask-border-width", 932 | "mask-clip", 933 | "mask-composite", 934 | "mask-image", 935 | "mask-mode", 936 | "mask-origin", 937 | "mask-position", 938 | "mask-position-x", 939 | "mask-position-y", 940 | "mask-repeat", 941 | "mask-size", 942 | "mask-source-type", 943 | "mask-type", 944 | "masonry-auto-flow", 945 | "math-depth", 946 | "math-shift", 947 | "math-style", 948 | "max-block-size", 949 | "max-height", 950 | "max-inline-size", 951 | "max-lines", 952 | "max-width", 953 | "max-zoom", 954 | "min-block-size", 955 | "min-height", 956 | "min-inline-size", 957 | "min-intrinsic-sizing", 958 | "min-width", 959 | "min-zoom", 960 | "mix-blend-mode", 961 | "motion", 962 | "motion-offset", 963 | "motion-path", 964 | "motion-rotation", 965 | "move-to", 966 | "nav-down", 967 | "nav-index", 968 | "nav-left", 969 | "nav-right", 970 | "nav-up", 971 | "negative", 972 | "object-fit", 973 | "object-overflow", 974 | "object-position", 975 | "object-view-box", 976 | "offset", 977 | "offset-after", 978 | "offset-anchor", 979 | "offset-before", 980 | "offset-block-end", 981 | "offset-block-start", 982 | "offset-distance", 983 | "offset-end", 984 | "offset-inline-end", 985 | "offset-inline-start", 986 | "offset-path", 987 | "offset-position", 988 | "offset-rotate", 989 | "offset-rotation", 990 | "offset-start", 991 | "opacity", 992 | "order", 993 | "orientation", 994 | "orphans", 995 | "outline", 996 | "outline-color", 997 | "outline-offset", 998 | "outline-style", 999 | "outline-width", 1000 | "overflow", 1001 | "overflow-anchor", 1002 | "overflow-block", 1003 | "overflow-clip-box", 1004 | "overflow-clip-margin", 1005 | "overflow-inline", 1006 | "overflow-style", 1007 | "overflow-wrap", 1008 | "overflow-x", 1009 | "overflow-y", 1010 | "override-colors", 1011 | "overscroll-behavior", 1012 | "overscroll-behavior-block", 1013 | "overscroll-behavior-inline", 1014 | "overscroll-behavior-x", 1015 | "overscroll-behavior-y", 1016 | "pad", 1017 | "padding", 1018 | "padding-block", 1019 | "padding-block-end", 1020 | "padding-block-start", 1021 | "padding-bottom", 1022 | "padding-inline", 1023 | "padding-inline-end", 1024 | "padding-inline-start", 1025 | "padding-left", 1026 | "padding-right", 1027 | "padding-top", 1028 | "page", 1029 | "page-break-after", 1030 | "page-break-before", 1031 | "page-break-inside", 1032 | "page-orientation", 1033 | "page-policy", 1034 | "paint-order", 1035 | "pause", 1036 | "pause-after", 1037 | "pause-before", 1038 | "pen-action", 1039 | "perspective", 1040 | "perspective-origin", 1041 | "perspective-origin-x", 1042 | "perspective-origin-y", 1043 | "pitch", 1044 | "pitch-range", 1045 | "place-content", 1046 | "place-items", 1047 | "place-self", 1048 | "play-during", 1049 | "pointer-events", 1050 | "position", 1051 | "prefix", 1052 | "print-color-adjust", 1053 | "property-name", 1054 | "quotes", 1055 | "r", 1056 | "range", 1057 | "region-fragment", 1058 | "resize", 1059 | "rest", 1060 | "rest-after", 1061 | "rest-before", 1062 | "richness", 1063 | "right", 1064 | "rotate", 1065 | "rotation", 1066 | "rotation-point", 1067 | "row-gap", 1068 | "ruby-align", 1069 | "ruby-merge", 1070 | "ruby-overhang", 1071 | "ruby-position", 1072 | "ruby-span", 1073 | "running", 1074 | "rx", 1075 | "ry", 1076 | "scale", 1077 | "scroll-behavior", 1078 | "scroll-margin", 1079 | "scroll-margin-block", 1080 | "scroll-margin-block-end", 1081 | "scroll-margin-block-start", 1082 | "scroll-margin-bottom", 1083 | "scroll-margin-inline", 1084 | "scroll-margin-inline-end", 1085 | "scroll-margin-inline-start", 1086 | "scroll-margin-left", 1087 | "scroll-margin-right", 1088 | "scroll-margin-top", 1089 | "scroll-padding", 1090 | "scroll-padding-block", 1091 | "scroll-padding-block-end", 1092 | "scroll-padding-block-start", 1093 | "scroll-padding-bottom", 1094 | "scroll-padding-inline", 1095 | "scroll-padding-inline-end", 1096 | "scroll-padding-inline-start", 1097 | "scroll-padding-left", 1098 | "scroll-padding-right", 1099 | "scroll-padding-top", 1100 | "scroll-snap-align", 1101 | "scroll-snap-coordinate", 1102 | "scroll-snap-destination", 1103 | "scroll-snap-margin", 1104 | "scroll-snap-margin-block", 1105 | "scroll-snap-margin-block-end", 1106 | "scroll-snap-margin-block-start", 1107 | "scroll-snap-margin-bottom", 1108 | "scroll-snap-margin-inline", 1109 | "scroll-snap-margin-inline-end", 1110 | "scroll-snap-margin-inline-start", 1111 | "scroll-snap-margin-left", 1112 | "scroll-snap-margin-right", 1113 | "scroll-snap-margin-top", 1114 | "scroll-snap-points-x", 1115 | "scroll-snap-points-y", 1116 | "scroll-snap-stop", 1117 | "scroll-snap-type", 1118 | "scroll-snap-type-x", 1119 | "scroll-snap-type-y", 1120 | "scroll-timeline", 1121 | "scroll-timeline-axis", 1122 | "scroll-timeline-name", 1123 | "scrollbar-3dlight-color", 1124 | "scrollbar-arrow-color", 1125 | "scrollbar-base-color", 1126 | "scrollbar-color", 1127 | "scrollbar-dark-shadow-color", 1128 | "scrollbar-darkshadow-color", 1129 | "scrollbar-face-color", 1130 | "scrollbar-gutter", 1131 | "scrollbar-highlight-color", 1132 | "scrollbar-shadow-color", 1133 | "scrollbar-track-color", 1134 | "scrollbar-width", 1135 | "scrollbar3d-light-color", 1136 | "scrollbar3dlight-color", 1137 | "shape-image-threshold", 1138 | "shape-inside", 1139 | "shape-margin", 1140 | "shape-outside", 1141 | "shape-padding", 1142 | "shape-rendering", 1143 | "size", 1144 | "size-adjust", 1145 | "snap-height", 1146 | "solid-color", 1147 | "solid-opacity", 1148 | "spatial-navigation-action", 1149 | "spatial-navigation-contain", 1150 | "spatial-navigation-function", 1151 | "speak", 1152 | "speak-as", 1153 | "speak-header", 1154 | "speak-numeral", 1155 | "speak-punctuation", 1156 | "speech-rate", 1157 | "src", 1158 | "stop-color", 1159 | "stop-opacity", 1160 | "stress", 1161 | "string-set", 1162 | "stroke", 1163 | "stroke-align", 1164 | "stroke-alignment", 1165 | "stroke-break", 1166 | "stroke-color", 1167 | "stroke-dash-corner", 1168 | "stroke-dash-justify", 1169 | "stroke-dashadjust", 1170 | "stroke-dasharray", 1171 | "stroke-dashcorner", 1172 | "stroke-dashoffset", 1173 | "stroke-image", 1174 | "stroke-linecap", 1175 | "stroke-linejoin", 1176 | "stroke-miterlimit", 1177 | "stroke-opacity", 1178 | "stroke-origin", 1179 | "stroke-position", 1180 | "stroke-repeat", 1181 | "stroke-size", 1182 | "stroke-width", 1183 | "suffix", 1184 | "supported-color-schemes", 1185 | "symbols", 1186 | "syntax", 1187 | "system", 1188 | "tab-size", 1189 | "table-layout", 1190 | "text-align", 1191 | "text-align-all", 1192 | "text-align-last", 1193 | "text-anchor", 1194 | "text-autospace", 1195 | "text-combine-upright", 1196 | "text-decoration", 1197 | "text-decoration-blink", 1198 | "text-decoration-color", 1199 | "text-decoration-line", 1200 | "text-decoration-line-through", 1201 | "text-decoration-none", 1202 | "text-decoration-overline", 1203 | "text-decoration-skip", 1204 | "text-decoration-skip-box", 1205 | "text-decoration-skip-ink", 1206 | "text-decoration-skip-inset", 1207 | "text-decoration-skip-self", 1208 | "text-decoration-skip-spaces", 1209 | "text-decoration-style", 1210 | "text-decoration-thickness", 1211 | "text-decoration-underline", 1212 | "text-edge", 1213 | "text-emphasis", 1214 | "text-emphasis-color", 1215 | "text-emphasis-position", 1216 | "text-emphasis-skip", 1217 | "text-emphasis-style", 1218 | "text-group-align", 1219 | "text-indent", 1220 | "text-justify", 1221 | "text-justify-trim", 1222 | "text-kashida", 1223 | "text-kashida-space", 1224 | "text-line-through", 1225 | "text-line-through-color", 1226 | "text-line-through-mode", 1227 | "text-line-through-style", 1228 | "text-line-through-width", 1229 | "text-orientation", 1230 | "text-overflow", 1231 | "text-overline", 1232 | "text-overline-color", 1233 | "text-overline-mode", 1234 | "text-overline-style", 1235 | "text-overline-width", 1236 | "text-rendering", 1237 | "text-shadow", 1238 | "text-size-adjust", 1239 | "text-space-collapse", 1240 | "text-space-trim", 1241 | "text-spacing", 1242 | "text-transform", 1243 | "text-underline", 1244 | "text-underline-color", 1245 | "text-underline-mode", 1246 | "text-underline-offset", 1247 | "text-underline-position", 1248 | "text-underline-style", 1249 | "text-underline-width", 1250 | "text-wrap", 1251 | "top", 1252 | "touch-action", 1253 | "touch-action-delay", 1254 | "transform", 1255 | "transform-box", 1256 | "transform-origin", 1257 | "transform-origin-x", 1258 | "transform-origin-y", 1259 | "transform-origin-z", 1260 | "transform-style", 1261 | "transition", 1262 | "transition-delay", 1263 | "transition-duration", 1264 | "transition-property", 1265 | "transition-timing-function", 1266 | "translate", 1267 | "uc-alt-skin", 1268 | "uc-skin", 1269 | "unicode-bidi", 1270 | "unicode-range", 1271 | "user-select", 1272 | "user-zoom", 1273 | "vector-effect", 1274 | "vertical-align", 1275 | "viewport-fill", 1276 | "viewport-fill-opacity", 1277 | "viewport-fit", 1278 | "visibility", 1279 | "voice-balance", 1280 | "voice-duration", 1281 | "voice-family", 1282 | "voice-pitch", 1283 | "voice-range", 1284 | "voice-rate", 1285 | "voice-stress", 1286 | "voice-volume", 1287 | "volume", 1288 | "white-space", 1289 | "widows", 1290 | "width", 1291 | "will-change", 1292 | "word-boundary-detection", 1293 | "word-boundary-expansion", 1294 | "word-break", 1295 | "word-spacing", 1296 | "word-wrap", 1297 | "wrap-after", 1298 | "wrap-before", 1299 | "wrap-flow", 1300 | "wrap-inside", 1301 | "wrap-through", 1302 | "writing-mode", 1303 | "x", 1304 | "y", 1305 | "z-index", 1306 | "zoom" 1307 | ], 1308 | "invalid": [ 1309 | "-background", 1310 | "-background-color", 1311 | "-clear", 1312 | "-color", 1313 | "-content", 1314 | "-display", 1315 | "-height", 1316 | "-html-opacity", 1317 | "-icab-text-overflow", 1318 | "-ie-background-size", 1319 | "-khtml-border-bottom-left-radius", 1320 | "-khtml-border-bottom-right-radius", 1321 | "-khtml-border-radius", 1322 | "-khtml-border-top-left-radius", 1323 | "-khtml-border-top-right-radius", 1324 | "-khtml-box-shadow", 1325 | "-khtml-opacity", 1326 | "-khtml-text-overflow", 1327 | "-khtml-user-drag", 1328 | "-khtml-user-select", 1329 | "-margin-bottom", 1330 | "-margin-left", 1331 | "-margin-right", 1332 | "-margin-top", 1333 | "-min-height", 1334 | "-mos-osx-font-smoothing", 1335 | "-mox-background-size", 1336 | "-moz--webkit-border-radius", 1337 | "-moz-align-items", 1338 | "-moz-align-self", 1339 | "-moz-background", 1340 | "-moz-border-bottom-color", 1341 | "-moz-border-bottom-left-radius", 1342 | "-moz-border-bottom-right-radius", 1343 | "-moz-border-left-color", 1344 | "-moz-border-radius-toprleft", 1345 | "-moz-border-right-color", 1346 | "-moz-border-top-color", 1347 | "-moz-border-top-left-radius", 1348 | "-moz-border-top-right-radius", 1349 | "-moz-box-lines", 1350 | "-moz-box-model", 1351 | "-moz-box-shfavow", 1352 | "-moz-clip-path", 1353 | "-moz-column-break-inside", 1354 | "-moz-filter", 1355 | "-moz-flex", 1356 | "-moz-flex-direction", 1357 | "-moz-flex-flow", 1358 | "-moz-flex-grow", 1359 | "-moz-flex-shrink", 1360 | "-moz-flex-wrap", 1361 | "-moz-font-kerning", 1362 | "-moz-font-smoothing", 1363 | "-moz-font-variant-ligatures", 1364 | "-moz-justify-content", 1365 | "-moz-order", 1366 | "-moz-padding-bottom", 1367 | "-moz-pointer-events", 1368 | "-moz-scrollbars", 1369 | "-moz-text-overflow", 1370 | "-moz-text-rendering", 1371 | "-moz-text-shadow", 1372 | "-moz-transform-origin-y", 1373 | "-moz-user-drag", 1374 | "-moz-width", 1375 | "-ms-align-content", 1376 | "-ms-align-items", 1377 | "-ms-align-self", 1378 | "-ms-animations", 1379 | "-ms-animations-delay", 1380 | "-ms-appearance", 1381 | "-ms-background", 1382 | "-ms-border-radius", 1383 | "-ms-box-shadow", 1384 | "-ms-box-sizing", 1385 | "-ms-clip-path", 1386 | "-ms-display", 1387 | "-ms-fles-wrap", 1388 | "-ms-flex-grow", 1389 | "-ms-flex-shrink", 1390 | "-ms-font-smoothing", 1391 | "-ms-justify-content", 1392 | "-ms-opacity", 1393 | "-ms-order", 1394 | "-ms-overflow-x", 1395 | "-ms-overflow-y", 1396 | "-ms-pointer-events", 1397 | "-ms-position", 1398 | "-ms-text-rendering", 1399 | "-ms-top", 1400 | "-ms-user-drag", 1401 | "-mz-transition-duration", 1402 | "-mz-transition-property", 1403 | "-o-align-items", 1404 | "-o-appearance", 1405 | "-o-backface-visibility", 1406 | "-o-background", 1407 | "-o-background-size", 1408 | "-o-border-radius", 1409 | "-o-box-align", 1410 | "-o-box-lines", 1411 | "-o-box-orient", 1412 | "-o-box-pack", 1413 | "-o-box-shadow", 1414 | "-o-box-sizing", 1415 | "-o-filter", 1416 | "-o-flex", 1417 | "-o-font-family", 1418 | "-o-font-feature-settings", 1419 | "-o-font-smoothing", 1420 | "-o-hyphens", 1421 | "-o-justify-content", 1422 | "-o-opacity", 1423 | "-o-perspective", 1424 | "-o-text-rendering", 1425 | "-o-transform-style", 1426 | "-o-user-drag", 1427 | "-o-user-select", 1428 | "-os-transform", 1429 | "-padding", 1430 | "-padding-bottom", 1431 | "-padding-top", 1432 | "-pie-background", 1433 | "-text-transform", 1434 | "-web-kit-animation-delay", 1435 | "-webbox-shadow", 1436 | "-webkit--moz-box-pack", 1437 | "-webkit--webkit-border-radius", 1438 | "-webkit-align-item", 1439 | "-webkit-bottom-left-radius", 1440 | "-webkit-bottom-right-radius", 1441 | "-webkit-box-model", 1442 | "-webkit-box-shfavow", 1443 | "-webkit-focus-ring-color", 1444 | "-webkit-font-language-override", 1445 | "-webkit-font-smooth", 1446 | "-webkit-outline", 1447 | "-webkit-overflow-scroll", 1448 | "-webkit-padding-bottom", 1449 | "-webkit-pointer-events", 1450 | "-webkit-tab-size", 1451 | "-webkit-text-align-last", 1452 | "-webkit-text-overflow", 1453 | "-webkit-text-rendering", 1454 | "-webkit-text-shadow", 1455 | "-webkit-top-left-radius", 1456 | "-webkit-top-right-radius", 1457 | "-webkit-word-break", 1458 | "-webkitz-box-shadow", 1459 | "-webskit-transition", 1460 | "a", 1461 | "align", 1462 | "align-item", 1463 | "align-text", 1464 | "bacground-color", 1465 | "backgorund-size", 1466 | "backgrond-color", 1467 | "backgroud-repeat", 1468 | "background-color-color", 1469 | "bb", 1470 | "bblr", 1471 | "blr", 1472 | "border-radius-bottomleft", 1473 | "border-radius-bottomright", 1474 | "border-radius-top-left", 1475 | "border-rfavius", 1476 | "border-rigth", 1477 | "border-top-radius", 1478 | "border-weight", 1479 | "box-model", 1480 | "box-shfavow", 1481 | "center", 1482 | "column-break-inside", 1483 | "composes", 1484 | "context", 1485 | "diplay", 1486 | "dispaly", 1487 | "font-color", 1488 | "font-smoothing", 1489 | "font-weigth", 1490 | "font-wieght", 1491 | "fontweight", 1492 | "fot-size", 1493 | "geight", 1494 | "height-transition", 1495 | "hiehgt", 1496 | "ie-dummy", 1497 | "interpolation-mode", 1498 | "justifycontent", 1499 | "last-child-padding", 1500 | "leftp", 1501 | "line-heightght", 1502 | "line-heught", 1503 | "line-hight", 1504 | "list-style-display", 1505 | "lne-height", 1506 | "magrin", 1507 | "margim-bottom", 1508 | "margin-botton", 1509 | "margn", 1510 | "marigin-bottom", 1511 | "max0width", 1512 | "min-font-size", 1513 | "mmargin-top", 1514 | "moz-border-radius", 1515 | "moz-box-shadow", 1516 | "moz-box-sizing", 1517 | "moz-transform-style", 1518 | "moz-transition", 1519 | "ms-display", 1520 | "ms-transform", 1521 | "nofocusline", 1522 | "null", 1523 | "olor", 1524 | "opactiy", 1525 | "oveflow", 1526 | "overflow-", 1527 | "overflow-scrolling", 1528 | "padding-start", 1529 | "pading-right", 1530 | "png", 1531 | "pointer-event", 1532 | "poistion", 1533 | "pposition", 1534 | "qheight", 1535 | "repeat-x", 1536 | "star", 1537 | "stroke-dash-array", 1538 | "tab-index", 1539 | "tap-highlight-color", 1540 | "taxt-align", 1541 | "texd-decoration", 1542 | "text-color", 1543 | "text-tranform", 1544 | "textshadow", 1545 | "top-left-border-radius", 1546 | "tranform", 1547 | "transition-time-function", 1548 | "transtion", 1549 | "user-drag", 1550 | "vertical", 1551 | "verticle-align", 1552 | "viewbox", 1553 | "viibility", 1554 | "visiility", 1555 | "webkit-border-radius", 1556 | "webkit-box-shadow", 1557 | "webkit-font-smoothing", 1558 | "webkit-transition", 1559 | "weight", 1560 | "widht", 1561 | "widtht", 1562 | "with", 1563 | "x-content", 1564 | "xopacity", 1565 | "xpadding" 1566 | ] 1567 | } 1568 | -------------------------------------------------------------------------------- /scripts/usage/Dimension.json: -------------------------------------------------------------------------------- 1 | { 2 | "sources": [ 3 | { 4 | "url": "https://raw.githubusercontent.com/mdn/data/master/css/units.json", 5 | "comment": "mdn/data units" 6 | } 7 | ], 8 | "comments": [ 9 | "db & st from https://www.w3.org/TR/css3-speech" 10 | ], 11 | "valid": [ 12 | "cap", 13 | "ch", 14 | "cm", 15 | "cqb", 16 | "cqh", 17 | "cqi", 18 | "cqmax", 19 | "cqmin", 20 | "cqw", 21 | "db", 22 | "deg", 23 | "dpcm", 24 | "dpi", 25 | "dppx", 26 | "dvb", 27 | "dvh", 28 | "dvi", 29 | "dvmax", 30 | "dvmin", 31 | "dvw", 32 | "em", 33 | "ex", 34 | "fr", 35 | "grad", 36 | "hz", 37 | "ic", 38 | "in", 39 | "khz", 40 | "lh", 41 | "lvb", 42 | "lvh", 43 | "lvi", 44 | "lvmax", 45 | "lvmin", 46 | "lvw", 47 | "mm", 48 | "ms", 49 | "pc", 50 | "pt", 51 | "px", 52 | "q", 53 | "rad", 54 | "rcap", 55 | "rch", 56 | "rem", 57 | "rex", 58 | "ric", 59 | "rlh", 60 | "s", 61 | "st", 62 | "svb", 63 | "svh", 64 | "svi", 65 | "svmax", 66 | "svmin", 67 | "svw", 68 | "turn", 69 | "vb", 70 | "vh", 71 | "vi", 72 | "vmax", 73 | "vmin", 74 | "vw", 75 | "x" 76 | ], 77 | "invalid": [ 78 | "\\0", 79 | "bb", 80 | "d2", 81 | "dp", 82 | "dpis", 83 | "dpx", 84 | "e", 85 | "f", 86 | "p", 87 | "px05px", 88 | "px9", 89 | "pxauto", 90 | "pxw", 91 | "spx", 92 | "vm", 93 | "xl" 94 | ] 95 | } 96 | -------------------------------------------------------------------------------- /scripts/usage/Function.json: -------------------------------------------------------------------------------- 1 | { 2 | "sources": [ 3 | { "url": "https://github.com/Microsoft/vscode-css-languageservice/blob/master/build/css-schema.xml", "comment": "VS Code CSS language service dictionary" } 4 | ], 5 | "comments": [ 6 | "snapInterval() & snapList() - https://msdn.microsoft.com/library/hh772036", 7 | "-ms- for gradients – preview releases of IE10 used a \"-ms\" prefixed syntax, and IE10 onwards use the standard syntax (https://stackoverflow.com/questions/15957320/css3-linear-gradient-on-ie10)", 8 | "constant() was used for Safari TP and then renamed into standarded env() in release" 9 | ], 10 | "valid": [ 11 | "-moz-calc()", 12 | "-moz-image-set()", 13 | "-moz-linear-gradient()", 14 | "-moz-radial-gradient()", 15 | "-moz-repeating-linear-gradient()", 16 | "-moz-repeating-radial-gradient()", 17 | "-o-linear-gradient()", 18 | "-o-radial-gradient()", 19 | "-o-repeating-linear-gradient()", 20 | "-o-repeating-radial-gradient()", 21 | "-webkit-calc()", 22 | "-webkit-gradient()", 23 | "-webkit-image-set()", 24 | "-webkit-linear-gradient()", 25 | "-webkit-radial-gradient()", 26 | "-webkit-repeating-linear-gradient()", 27 | "-webkit-repeating-radial-gradient()", 28 | "annotation()", 29 | "attr()", 30 | "blur()", 31 | "brightness()", 32 | "calc()", 33 | "character-variant()", 34 | "circle()", 35 | "color-stop()", 36 | "contrast()", 37 | "counter()", 38 | "counters()", 39 | "cross-fade()", 40 | "cubic-bezier()", 41 | "domain()", 42 | "drop-shadow()", 43 | "element()", 44 | "ellipse()", 45 | "env()", 46 | "expression()", 47 | "fit-content()", 48 | "flip()", 49 | "format()", 50 | "frames()", 51 | "from()", 52 | "grayscale()", 53 | "hsl()", 54 | "hsla()", 55 | "hue-rotate()", 56 | "image()", 57 | "image-set()", 58 | "inset()", 59 | "invert()", 60 | "leader()", 61 | "linear-gradient()", 62 | "local()", 63 | "matrix()", 64 | "matrix3d()", 65 | "min()", 66 | "minmax()", 67 | "max()", 68 | "opacity()", 69 | "ornaments()", 70 | "path()", 71 | "perspective()", 72 | "polygon()", 73 | "radial-gradient()", 74 | "ray()", 75 | "rect()", 76 | "regexp()", 77 | "repeat()", 78 | "repeating-linear-gradient()", 79 | "repeating-radial-gradient()", 80 | "rgb()", 81 | "rgba()", 82 | "rotate()", 83 | "rotate3d()", 84 | "rotatex()", 85 | "rotatey()", 86 | "rotatez()", 87 | "saturate()", 88 | "scale()", 89 | "scale3d()", 90 | "scalex()", 91 | "scaley()", 92 | "scalez()", 93 | "sepia()", 94 | "shade()", 95 | "skew()", 96 | "skewx()", 97 | "skewy()", 98 | "snapinterval()", 99 | "snaplist()", 100 | "steps()", 101 | "styleset()", 102 | "stylistic()", 103 | "swash()", 104 | "symbols()", 105 | "target-counter()", 106 | "target-counters()", 107 | "target-text()", 108 | "tint()", 109 | "to()", 110 | "translate()", 111 | "translate3d()", 112 | "translatex()", 113 | "translatey()", 114 | "translatez()", 115 | "url-prefix()", 116 | "var()" 117 | ], 118 | "invalid": [ 119 | "-moz--image-set()", 120 | "-moz-translatey()", 121 | "-ms-image-set()", 122 | "-ms-linear-gradient()", 123 | "-ms-radial-gradient()", 124 | "-ms-repeating-linear-gradient()", 125 | "-ms-repeating-radial-gradient()", 126 | "-ms-translatey()", 127 | "-o-image-set()", 128 | "-o-translatey()", 129 | "-webkit-translatey()", 130 | "constant()", 131 | "a_nullable_with_important()", 132 | "col()", 133 | "data-uri()", 134 | "e6e6e6linear-gradient()", 135 | "g-fontsize()", 136 | "ligthen()", 137 | "mask()", 138 | "not()", 139 | "obsidian-spacer-redux()", 140 | "or()", 141 | "rbga()", 142 | "rec-calc()", 143 | "rem-calc()", 144 | "set-text-color()", 145 | "trasnlate3d()", 146 | "uniquite()", 147 | " -moz-linear-gradient()", 148 | " -o-linear-gradient()" 149 | ] 150 | } 151 | -------------------------------------------------------------------------------- /scripts/usage/MediaFeature.json: -------------------------------------------------------------------------------- 1 | { 2 | "sources": [ 3 | { "url": "https://msdn.microsoft.com/library/Hh771830", "comment": "-ms-high-contrast" }, 4 | { "url": "https://msdn.microsoft.com/library/hh771863", "comment": "-ms-high-contrast-adjust" }, 5 | { "url": "https://www.w3.org/TR/2017/CR-mediaqueries-4-20170905/#media-descriptor-table", "comment": "Media Queries Level 4 - All the media features" }, 6 | { "url": "https://developer.mozilla.org/en-US/docs/Web/CSS/Media_Queries/Using_media_queries", "comment": "some -moz- and other extensions" } 7 | ], 8 | "comments": [ 9 | "bootstrap 3 uses `@media all and (transform-3d),(-webkit-transform-3d)` for , but `(transform-3d)`", 10 | "no confirmation found that unprefixed min/max-device-pixel-ratio is supported by any browser" 11 | ], 12 | "valid": [ 13 | "-moz-mac-graphite-theme", 14 | "-moz-maemo-classic", 15 | "-moz-device-pixel-ratio", 16 | "-moz-os-version", 17 | "-moz-scrollbar-end-backward", 18 | "-moz-scrollbar-end-forward", 19 | "-moz-scrollbar-start-backward", 20 | "-moz-scrollbar-start-forward", 21 | "-moz-scrollbar-thumb-proportional", 22 | "-moz-touch-enabled", 23 | "-moz-windows-accent-color-in-titlebar", 24 | "-moz-windows-classic", 25 | "-moz-windows-compositor", 26 | "-moz-windows-default-theme", 27 | "-moz-windows-glass", 28 | "-moz-windows-theme", 29 | "-ms-high-contrast", 30 | "-ms-high-contrast-adjust", 31 | "-o-min-device-pixel-ratio", 32 | "-o-max-device-pixel-ratio", 33 | "-webkit-animation", 34 | "-webkit-device-pixel-ratio", 35 | "-webkit-min-device-pixel-ratio", 36 | "-webkit-max-device-pixel-ratio", 37 | "-webkit-transform-3d", 38 | "-webkit-transform-2d", 39 | "-webkit-transition", 40 | "any-hover", 41 | "any-pointer", 42 | "aspect-ratio", 43 | "color", 44 | "color-gamut", 45 | "color-index", 46 | "device-aspect-ratio", 47 | "device-height", 48 | "device-width", 49 | "display-mode", 50 | "grid", 51 | "height", 52 | "hover", 53 | "inverted-colors", 54 | "max--moz-device-pixel-ratio", 55 | "max-aspect-ratio", 56 | "max-color", 57 | "max-color-index", 58 | "max-device-aspect-ratio", 59 | "max-device-height", 60 | "max-device-width", 61 | "max-height", 62 | "max-resolution", 63 | "max-width", 64 | "min--moz-device-pixel-ratio", 65 | "min-aspect-ratio", 66 | "min-color", 67 | "min-color-index", 68 | "min-device-aspect-ratio", 69 | "min-device-height", 70 | "min-device-width", 71 | "min-height", 72 | "min-resolution", 73 | "min-width", 74 | "monochrome", 75 | "orientation", 76 | "overflow-block", 77 | "overflow-inline", 78 | "pointer", 79 | "resolution", 80 | "scan", 81 | "update", 82 | "width" 83 | ], 84 | "invalid": [ 85 | "-moz-min-device-pixel-ratio", 86 | "-ms-hight-contrast", 87 | "max-widwth", 88 | "max-device-pixel-ratio", 89 | "min-device-pixel-ratio", 90 | "transform-3d" 91 | ] 92 | } 93 | -------------------------------------------------------------------------------- /scripts/usage/PseudoClassSelector.json: -------------------------------------------------------------------------------- 1 | { 2 | "sources": [ 3 | { "url": "https://developer.mozilla.org/en-US/docs/Web/CSS/Mozilla_Extensions#Pseudo-elements_and_pseudo-classes", "comment": "all -moz- extensions" }, 4 | { "url": "https://msdn.microsoft.com/en-us/library/windows/apps/cc848867.aspx", "comment": "-ms-lang()" }, 5 | { "url": "https://css-tricks.com/custom-scrollbars-in-webkit/", "comment": "pseudo-classes for scrollbars in webkit" }, 6 | { "url": "https://github.com/Microsoft/vscode-css-languageservice/blob/master/build/css-schema.xml", "comment": "VS Code CSS language service dictionary" } 7 | ], 8 | "comments": [ 9 | "horizontal, vertical, decrement, increment, start, end, double-button, single-button, no-button, corner-present and window-inactive are all about scrollbar states", 10 | ":host, :host(), :host-context() – CSS Scoping Module Level 1" 11 | ], 12 | "valid": [ 13 | "-moz-any-link", 14 | "-moz-any()", 15 | "-moz-broken", 16 | "-moz-drag-over", 17 | "-moz-first-node", 18 | "-moz-focusring", 19 | "-moz-full-screen-ancestor", 20 | "-moz-full-screen", 21 | "-moz-handler-blocked", 22 | "-moz-handler-crashed", 23 | "-moz-handler-disabled", 24 | "-moz-last-node", 25 | "-moz-list-bullet", 26 | "-moz-list-number", 27 | "-moz-loading", 28 | "-moz-locale-dir()", 29 | "-moz-lwtheme-brighttext", 30 | "-moz-lwtheme-darktext", 31 | "-moz-lwtheme", 32 | "-moz-native-anonymous", 33 | "-moz-only-whitespace", 34 | "-moz-placeholder", 35 | "-moz-read-only", 36 | "-moz-read-write", 37 | "-moz-submit-invalid", 38 | "-moz-suppressed", 39 | "-moz-system-metric()", 40 | "-moz-tree-cell-text", 41 | "-moz-tree-cell-text()", 42 | "-moz-tree-cell", 43 | "-moz-tree-checkbox", 44 | "-moz-tree-column", 45 | "-moz-tree-drop-feedback", 46 | "-moz-tree-image", 47 | "-moz-tree-indentation", 48 | "-moz-tree-line", 49 | "-moz-tree-progressmeter", 50 | "-moz-tree-row", 51 | "-moz-tree-row()", 52 | "-moz-tree-separator", 53 | "-moz-tree-twisty", 54 | "-moz-ui-invalid", 55 | "-moz-ui-valid", 56 | "-moz-user-disabled", 57 | "-moz-window-inactive", 58 | "-ms-fullscreen", 59 | "-ms-input-placeholder", 60 | "-ms-keyboard-active", 61 | "-ms-lang()", 62 | "-o-prefocus", 63 | "-webkit-any-link", 64 | "-webkit-any()", 65 | "-webkit-autofill", 66 | "-webkit-full-screen-ancestor", 67 | "-webkit-full-screen", 68 | "active", 69 | "after", 70 | "any-link", 71 | "any", 72 | "before", 73 | "blank", 74 | "checked", 75 | "corner-present", 76 | "current", 77 | "current()", 78 | "decrement", 79 | "default", 80 | "defined", 81 | "dir", 82 | "dir()", 83 | "disabled", 84 | "double-button", 85 | "empty", 86 | "enabled", 87 | "end", 88 | "first-child", 89 | "first-letter", 90 | "first-line", 91 | "first-of-type", 92 | "first", 93 | "focus-visible", 94 | "focus-within", 95 | "focus", 96 | "fullscreen", 97 | "future", 98 | "has", 99 | "horizontal", 100 | "host-context()", 101 | "host", 102 | "host()", 103 | "hover", 104 | "in-range", 105 | "increment", 106 | "indeterminate", 107 | "invalid", 108 | "is()", 109 | "lang()", 110 | "last-child", 111 | "last-of-type", 112 | "left", 113 | "link", 114 | "local-link", 115 | "matches()", 116 | "no-button", 117 | "not()", 118 | "nth-child()", 119 | "nth-col()", 120 | "nth-last-child()", 121 | "nth-last-col()", 122 | "nth-last-match()", 123 | "nth-last-of-type()", 124 | "nth-match()", 125 | "nth-of-type()", 126 | "only-child", 127 | "only-of-type", 128 | "optional", 129 | "out-of-range", 130 | "past", 131 | "paused", 132 | "picture-in-picture", 133 | "placeholder-shown", 134 | "playing", 135 | "read-only", 136 | "read-write", 137 | "required", 138 | "right", 139 | "root", 140 | "scope", 141 | "single-button", 142 | "start", 143 | "target-within", 144 | "target", 145 | "user-invalid", 146 | "user-valid", 147 | "valid", 148 | "vertical", 149 | "visited", 150 | "where()", 151 | "window-inactive" 152 | ], 153 | "invalid": [ 154 | "-moz-focus-inner", 155 | "-moz-input-placeholder", 156 | "-ms-expand", 157 | "-ms-full-screen", 158 | "-ms-fullscreen--sm", 159 | "-ms-fullscreen-ancestor", 160 | "-o-full-screen", 161 | "-o-fullscreen", 162 | "-safarifarifari", 163 | "-webkit-input-placeholder", 164 | "active__icon", 165 | "active__label_apps", 166 | "active__label_text", 167 | "before--bold", 168 | "btn-pending", 169 | "checked--theme-dark", 170 | "export", 171 | "firs-child", 172 | "first-child()", 173 | "first-child\u00a0", 174 | "focus__icon", 175 | "focus__label_apps", 176 | "focus__label_text", 177 | "focus-icon", 178 | "focus-text", 179 | "full-screen", 180 | "fullscreen-ancestor", 181 | "global()", 182 | "gobla()", 183 | "hello-its-safari", 184 | "hove", 185 | "hover__background--badge-inverted", 186 | "hover__background--badge", 187 | "hover__background--body-inverted", 188 | "hover__background--body", 189 | "hover__background--dropdown", 190 | "hover__background--footer-secondary", 191 | "hover__background--footer", 192 | "hover__background--header-inverted", 193 | "hover__background--header", 194 | "hover__background--input-inverted", 195 | "hover__background--input", 196 | "hover__background--light", 197 | "hover__background--modal", 198 | "hover__background--notification", 199 | "hover__background--overlay", 200 | "hover__background--popover", 201 | "hover__background--popular-tags", 202 | "hover__background--secondary", 203 | "hover__background--showcase-item-inverted", 204 | "hover__background--showcase-item", 205 | "hover__background--showcase", 206 | "hover__background--slider", 207 | "hover__background--switch", 208 | "hover__background--table-head", 209 | "hover__background--table-odd", 210 | "hover__background--table", 211 | "hover__background--tabs-active", 212 | "hover__background--tabs", 213 | "hover__background--tags-inverted", 214 | "hover__background--tags", 215 | "hover__background--tooltip", 216 | "hover__blue--1", 217 | "hover__blue--2", 218 | "hover__blue--3", 219 | "hover__blue--4", 220 | "hover__blue--5", 221 | "hover__blue--6", 222 | "hover__blue--7", 223 | "hover__blue--8", 224 | "hover__blue--campu", 225 | "hover__blue--congress", 226 | "hover__blue--endeavour", 227 | "hover__blue--freepik", 228 | "hover__blue--malibu", 229 | "hover__blue--onahau", 230 | "hover__blue--picton", 231 | "hover__blue--science", 232 | "hover__dark-blue--1", 233 | "hover__dark-blue--2", 234 | "hover__dark-blue--3", 235 | "hover__dark-blue--4", 236 | "hover__dark-blue--5", 237 | "hover__dark-blue--6", 238 | "hover__dark-blue--7", 239 | "hover__dark-blue--bay", 240 | "hover__dark-blue--biscay", 241 | "hover__dark-blue--blackale", 242 | "hover__dark-blue--blumine", 243 | "hover__dark-blue--chathams", 244 | "hover__dark-blue--fc", 245 | "hover__dark-blue--sea", 246 | "hover__dark-blue--zodiac", 247 | "hover__general--accordion", 248 | "hover__general--border-inverted", 249 | "hover__general--border", 250 | "hover__general--button-inverted", 251 | "hover__general--button", 252 | "hover__general--footer", 253 | "hover__general--form-border", 254 | "hover__general--gray-1", 255 | "hover__general--gray-2", 256 | "hover__general--gray-3", 257 | "hover__general--gray-4", 258 | "hover__general--heading-inverted", 259 | "hover__general--heading", 260 | "hover__general--icon-inverted", 261 | "hover__general--icon", 262 | "hover__general--link-inverted", 263 | "hover__general--link", 264 | "hover__general--shadow", 265 | "hover__general--tabs-active", 266 | "hover__general--tabs-border", 267 | "hover__general--tabs-triangle-active", 268 | "hover__general--tabs-triangle", 269 | "hover__general--tabs", 270 | "hover__general--tags-inverted", 271 | "hover__general--tags", 272 | "hover__general--text-dark", 273 | "hover__general--text-inverted", 274 | "hover__general--text-secondary", 275 | "hover__general--text", 276 | "hover__general--triangle-active", 277 | "hover__general--triangle", 278 | "hover__gray--1", 279 | "hover__gray--2", 280 | "hover__gray--3", 281 | "hover__gray--4", 282 | "hover__gray--5", 283 | "hover__gray--6", 284 | "hover__gray--7", 285 | "hover__gray--8", 286 | "hover__gray--9", 287 | "hover__gray--aqua", 288 | "hover__gray--bali", 289 | "hover__gray--bayoux", 290 | "hover__gray--cadet", 291 | "hover__gray--ebony-clay", 292 | "hover__gray--ebony", 293 | "hover__gray--gaysir", 294 | "hover__gray--hades", 295 | "hover__gray--heather", 296 | "hover__gray--hoki", 297 | "hover__gray--mystic", 298 | "hover__gray--oxford", 299 | "hover__green--1", 300 | "hover__green--2", 301 | "hover__green--3", 302 | "hover__green--4", 303 | "hover__green--5", 304 | "hover__green--6", 305 | "hover__green--7", 306 | "hover__green--8", 307 | "hover__green--flaticon", 308 | "hover__green--hint", 309 | "hover__green--jade", 310 | "hover__green--jungle", 311 | "hover__green--pepinen", 312 | "hover__green--shamrock", 313 | "hover__green--snowy", 314 | "hover__green--sugar", 315 | "hover__icon", 316 | "hover__label_apps", 317 | "hover__label_text", 318 | "hover__project--flaticon", 319 | "hover__project--main-dark", 320 | "hover__project--main-inverted", 321 | "hover__project--main-light", 322 | "hover__project--main", 323 | "hover__project--premium", 324 | "hover__project--tutpad", 325 | "hover__scale-gray--1", 326 | "hover__scale-gray--2", 327 | "hover__scale-gray--3", 328 | "hover__scale-gray--4", 329 | "hover__scale-gray--5", 330 | "hover__scale-gray--6", 331 | "hover__scale-gray--7", 332 | "hover__scale-gray--8", 333 | "hover__state--blue", 334 | "hover__state--gray", 335 | "hover__state--green", 336 | "hover__state--premium", 337 | "hover__state--purple", 338 | "hover__state--red", 339 | "hover__state--yellow", 340 | "hover__yellow--1", 341 | "hover__yellow--2", 342 | "hover__yellow--3", 343 | "hover__yellow--4", 344 | "hover__yellow--5", 345 | "hover__yellow--6", 346 | "hover__yellow--7", 347 | "hover__yellow--8", 348 | "hover__yellow--bea", 349 | "hover__yellow--buttermilk", 350 | "hover__yellow--dandelion", 351 | "hover__yellow--paco", 352 | "hover__yellow--poppy", 353 | "hover__yellow--premium", 354 | "hover__yellow--sin", 355 | "hover__yellow--tango", 356 | "hover--group", 357 | "hover--mobile-btn__dropdown-icon", 358 | "hover--private", 359 | "hover-icon", 360 | "hover-text", 361 | "hvoer", 362 | "input-placeholder", 363 | "last-first", 364 | "last-item", 365 | "last", 366 | "moz-placeholder", 367 | "not", 368 | "notsafari", 369 | "nth-first-of-type()", 370 | "nth-last-child", 371 | "nth-of-type", 372 | "nth()", 373 | "placeholder", 374 | "safarionlyhack", 375 | "second-child", 376 | "selected", 377 | "thisisasafarionlyhack", 378 | "visted" 379 | ] 380 | } 381 | -------------------------------------------------------------------------------- /scripts/usage/PseudoElementSelector.json: -------------------------------------------------------------------------------- 1 | { 2 | "sources": [ 3 | { "url": "https://developer.mozilla.org/en-US/docs/Web/CSS/Mozilla_Extensions#Pseudo-elements_and_pseudo-classes", "comment": "-moz-" }, 4 | { "url": "https://msdn.microsoft.com/en-us/library/hh869604(v=vs.85).aspx", "comment": "-ms-" }, 5 | { "url": "https://msdn.microsoft.com/en-us/library/dn312072(v=vs.85).aspx", "comment": "-ms-backdrop" }, 6 | { "url": "https://gist.github.com/afabbro/3759334", "comment": "-webkit- pseudo-elements gist" }, 7 | { "url": "https://gist.github.com/webtobesocial/aefd6e25064c08e0cc9a", "comment": "additional details to -webkit- pseudos" }, 8 | { "url": "https://github.com/philsinatra/docs/blob/master/html5-video.md", "comment": "-webkit-media-text-track-*" }, 9 | { "url": "https://chromium.googlesource.com/chromium/blink/+/master/Source/core/css/mediaControls.css", "comment": "Blink default media controls styles" }, 10 | { "url": "https://stackoverflow.com/a/46092077/685672", "comment": "-webkit-caps-lock-indicator/-webkit-credentials-auto-fill-button" }, 11 | { "url": "https://drafts.csswg.org/css-pseudo-4/#cssom", "comment": "CSS Pseudo-Elements Module Level 4" }, 12 | { "url": "https://github.com/Microsoft/vscode-css-languageservice/blob/master/build/css-schema.xml", "comment": "VS Code CSS language service dictionary" } 13 | ], 14 | "comments": [ 15 | "::shadow & ::content – Shadow DOM v0", 16 | "::slotted – Shadow DOM v1", 17 | "::-webkit-validation-bubble & ::-webkit-validation-bubble-* – Chrome 28 removed support", 18 | "CSS Pseudo-Elements Module Level 4 have additional pseudo-elements, not added since in ED stage and no browser support yet" 19 | ], 20 | "valid": [ 21 | "-internal-media-controls-cast-button", 22 | "-moz-anonymous-block", 23 | "-moz-anonymous-positioned-block", 24 | "-moz-canvas", 25 | "-moz-cell-content", 26 | "-moz-focus-inner", 27 | "-moz-focus-outer", 28 | "-moz-inline-table", 29 | "-moz-list-bullet", 30 | "-moz-list-number", 31 | "-moz-page-sequence", 32 | "-moz-page", 33 | "-moz-pagebreak", 34 | "-moz-pagecontent", 35 | "-moz-placeholder", 36 | "-moz-progress-bar", 37 | "-moz-range-progress", 38 | "-moz-range-thumb", 39 | "-moz-range-track", 40 | "-moz-scrolled-canvas", 41 | "-moz-scrolled-content", 42 | "-moz-scrolled-page-sequence", 43 | "-moz-selection", 44 | "-moz-svg-foreign-content", 45 | "-moz-table-cell", 46 | "-moz-table-column-group", 47 | "-moz-table-column", 48 | "-moz-table-outer", 49 | "-moz-table-row-group", 50 | "-moz-table-row", 51 | "-moz-table", 52 | "-moz-viewport-scroll", 53 | "-moz-viewport", 54 | "-moz-xul-anonymous-block", 55 | "-ms-backdrop", 56 | "-ms-browse", 57 | "-ms-check", 58 | "-ms-clear", 59 | "-ms-expand", 60 | "-ms-fill-lower", 61 | "-ms-fill-upper", 62 | "-ms-fill", 63 | "-ms-reveal", 64 | "-ms-thumb", 65 | "-ms-ticks-after", 66 | "-ms-ticks-before", 67 | "-ms-tooltip", 68 | "-ms-track", 69 | "-ms-value", 70 | "-webkit-calendar-picker-indicator", 71 | "-webkit-caps-lock-indicator", 72 | "-webkit-color-swatch-wrapper", 73 | "-webkit-color-swatch", 74 | "-webkit-contacts-auto-fill-button", 75 | "-webkit-credentials-auto-fill-button", 76 | "-webkit-datetime-edit-day-field", 77 | "-webkit-datetime-edit-fields-wrapper", 78 | "-webkit-datetime-edit-month-field", 79 | "-webkit-datetime-edit-text", 80 | "-webkit-datetime-edit-year-field", 81 | "-webkit-datetime-edit", 82 | "-webkit-details-marker", 83 | "-webkit-file-upload-button", 84 | "-webkit-full-page-media", 85 | "-webkit-image-inner-element", 86 | "-webkit-inner-spin-button", 87 | "-webkit-input-placeholder", 88 | "-webkit-input-speech-button", 89 | "-webkit-keygen-select", 90 | "-webkit-media-controls-current-time-display", 91 | "-webkit-media-controls-enclosure", 92 | "-webkit-media-controls-fullscreen-button", 93 | "-webkit-media-controls-fullscreen-volume-max-button", 94 | "-webkit-media-controls-fullscreen-volume-min-button", 95 | "-webkit-media-controls-fullscreen-volume-slider", 96 | "-webkit-media-controls-mute-button", 97 | "-webkit-media-controls-overlay-enclosure", 98 | "-webkit-media-controls-overlay-play-button", 99 | "-webkit-media-controls-panel-container", 100 | "-webkit-media-controls-panel", 101 | "-webkit-media-controls-play-button", 102 | "-webkit-media-controls-return-to-realtime-button", 103 | "-webkit-media-controls-rewind-button", 104 | "-webkit-media-controls-seek-back-button", 105 | "-webkit-media-controls-seek-forward-button", 106 | "-webkit-media-controls-start-playback-button", 107 | "-webkit-media-controls-time-remaining-display", 108 | "-webkit-media-controls-timeline-container", 109 | "-webkit-media-controls-timeline", 110 | "-webkit-media-controls-toggle-closed-captions-button", 111 | "-webkit-media-controls-volume-slider-container", 112 | "-webkit-media-controls-volume-slider", 113 | "-webkit-media-controls", 114 | "-webkit-media-slider-container", 115 | "-webkit-media-slider-thumb", 116 | "-webkit-media-text-track-background", 117 | "-webkit-media-text-track-container", 118 | "-webkit-media-text-track-display", 119 | "-webkit-media-text-track-region-container", 120 | "-webkit-media-text-track-region", 121 | "-webkit-meter-bar", 122 | "-webkit-meter-even-less-good-value", 123 | "-webkit-meter-optimum-value", 124 | "-webkit-meter-suboptimal-value", 125 | "-webkit-meter-suboptimum-value", 126 | "-webkit-outer-spin-button", 127 | "-webkit-progress-bar-value", 128 | "-webkit-progress-bar", 129 | "-webkit-progress-inner-element", 130 | "-webkit-progress-value", 131 | "-webkit-resizer", 132 | "-webkit-scrollbar-button", 133 | "-webkit-scrollbar-corner", 134 | "-webkit-scrollbar-thumb", 135 | "-webkit-scrollbar-track-piece", 136 | "-webkit-scrollbar-track", 137 | "-webkit-scrollbar", 138 | "-webkit-search-cancel-button", 139 | "-webkit-search-decoration", 140 | "-webkit-search-results-button", 141 | "-webkit-search-results-decoration", 142 | "-webkit-selection", 143 | "-webkit-slider-container", 144 | "-webkit-slider-runnable-track", 145 | "-webkit-slider-thumb", 146 | "-webkit-textfield-decoration-container", 147 | "-webkit-validation-bubble-arrow-clipper", 148 | "-webkit-validation-bubble-arrow", 149 | "-webkit-validation-bubble-heading", 150 | "-webkit-validation-bubble-message", 151 | "-webkit-validation-bubble-text-block", 152 | "-webkit-validation-bubble", 153 | "after", 154 | "backdrop", 155 | "before", 156 | "content", 157 | "cue-region", 158 | "cue-region()", 159 | "cue", 160 | "cue()", 161 | "first-letter", 162 | "first-line", 163 | "grammar-error", 164 | "marker", 165 | "part", 166 | "placeholder", 167 | "selection", 168 | "shadow", 169 | "slotted", 170 | "spelling-error", 171 | "target-text" 172 | ], 173 | "invalid": [ 174 | "-moz-input-placeholder", 175 | "-ms-input-placeholder", 176 | "-ms-placeholder", 177 | "-webkit-", 178 | "first-child", 179 | "full-page-media", 180 | "i-block-chrome", 181 | "last-child", 182 | "scrollbar-thumb", 183 | "scrollbar-track", 184 | "scrollbar", 185 | "webkit-input-placeholder" 186 | ] 187 | } 188 | -------------------------------------------------------------------------------- /scripts/usage/index.js: -------------------------------------------------------------------------------- 1 | const types = [ 2 | 'Atrule', 3 | 'MediaFeature', 4 | 'PseudoClassSelector', 5 | 'PseudoElementSelector', 6 | 'Function', 7 | 'Declaration', 8 | 'Declaration hacks', 9 | 'Dimension' 10 | ]; 11 | const statusOrder = ['❔', '🆗', '⚠']; 12 | const dictionaries = types.reduce(function(res, type) { 13 | res[type] = !/hacks/.test(type) 14 | ? require('./' + type + '.json') 15 | : { invalid: [], valid: [] }; 16 | return res; 17 | }, Object.create(null)); 18 | const status = types.reduce(function(res, type) { 19 | const data = dictionaries[type]; 20 | const status = Object.create(null); 21 | data.invalid.forEach(function(name) { 22 | status[name] = '⚠'; // 🚫❌❗⛔️ 23 | }); 24 | data.valid.forEach(function(name) { 25 | if (name in status) { 26 | throw new Error('Duplicate status for `' + type + '/' + name + '`'); 27 | } 28 | status[name] = '🆗'; 29 | }); 30 | res[type] = status; 31 | return res; 32 | }, Object.create(null)); 33 | 34 | module.exports = { 35 | types, 36 | dictionaries, 37 | statusOrder, 38 | status 39 | }; 40 | -------------------------------------------------------------------------------- /scripts/utils.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | getSiteList(count = 25) { 3 | return require('fs') 4 | .readFileSync(__dirname + '/../data/sites.csv', 'utf8') 5 | .trim() 6 | .split(/\r\n?|\n/) 7 | .slice(0, count); 8 | } 9 | }; 10 | -------------------------------------------------------------------------------- /ui/data.js: -------------------------------------------------------------------------------- 1 | module.exports = function() { 2 | const sites = require('../data/test-result.json'); 3 | const usageDict = require('../scripts/usage').dictionaries; 4 | 5 | return { 6 | sites, 7 | usageDict 8 | }; 9 | }; 10 | -------------------------------------------------------------------------------- /ui/index.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | name: 'Real Web CSS', 3 | basedir: __dirname, 4 | data: './data', 5 | prepare: './prepare', 6 | view: { 7 | assets: [ 8 | './page/default.css', 9 | './page/default.js', 10 | './page/usage.css', 11 | './page/usage.js', 12 | './view/chart.css', 13 | './view/chart.js' 14 | // 'view/sidebar.js' 15 | ] 16 | } 17 | }; 18 | -------------------------------------------------------------------------------- /ui/page/default.css: -------------------------------------------------------------------------------- 1 | .page-default .credits { 2 | font-size: 12px; 3 | opacity: .8; 4 | } 5 | 6 | .page-default .view-chart { 7 | max-width: 1000px; 8 | } 9 | .page-default .view-expand > .header .view-source { 10 | background: none; 11 | } 12 | -------------------------------------------------------------------------------- /ui/page/default.js: -------------------------------------------------------------------------------- 1 | const stylesheetTable = { 2 | view: 'table', 3 | cols: { 4 | url: { content: { 5 | view: 'switch', 6 | content: [ 7 | { when: 'url', content: 'link:{ href: url, external: true, text: url | size() > 50 ? $[:50] + "..." : $ }' }, 8 | { content: [] } 9 | ] 10 | } } 11 | } 12 | }; 13 | 14 | const usageDictTable = { 15 | view: 'table', 16 | cols: [ 17 | 'fullName', 18 | { header: 'Sites', data: 'sites', content: 'text:size()', details: 'table' }, 19 | { header: 'Stylesheets', data: 'stylesheets', content: 'text:size()', details: 'table' } 20 | ] 21 | }; 22 | 23 | discovery.page.define('default', [ 24 | { 25 | view: 'page-header', 26 | content: [ 27 | 'h1:#.name', 28 | { 29 | view: 'block', 30 | className: 'credits', 31 | content: [ 32 | 'text:"Powered by "', 33 | 'link:{ text: "CSSTree", href: "https://github.com/csstree/csstree" }', 34 | 'text:" and "', 35 | 'link:{ text: "Discovery.js", href: "https://github.com/discoveryjs/discovery" }' 36 | ] 37 | } 38 | ] 39 | }, 40 | { 41 | view: 'chart', 42 | data: 'sites.stylesheets.errors.group(=>name).({ x: key, y: value.size() }).sort(y desc)', 43 | sort: false, 44 | vertical: true 45 | }, 46 | { 47 | view: 'hstack', 48 | content: [ 49 | { 50 | view: 'section', 51 | header: 'h2:"Sites"', 52 | content: { 53 | view: 'table', 54 | data: 'sites.[stylesheets]', 55 | cols: [ 56 | { header: '#', data: 'id + 1' }, 57 | { header: 'Site', data: 'domain' }, 58 | { header: 'Count', className: 'number', data: 'stylesheets', content: 'text-numeric:size()', details: stylesheetTable }, 59 | { header: 'Size', className: 'number', data: 'stylesheets', content: 'text-numeric:reduce(=>$$ + size, 0)', details: stylesheetTable }, 60 | { header: 'Errors', className: 'number', data: 'stylesheets.errors', content: 'text-numeric:size()', details: [ 61 | { 62 | view: 'list', 63 | item: { 64 | view: 'expand', 65 | header: [ 66 | 'badge:name', 67 | 'text:message', 68 | { view: 'source', when: 'details', data: '{ lineNum: false, content: details }' } 69 | ], 70 | content: { view: 'struct', expanded: 1 } 71 | } 72 | }, 73 | 'table' 74 | ] } 75 | ] 76 | } 77 | }, 78 | { 79 | view: 'section', 80 | header: 'h2:"Usage"', 81 | content: [ 82 | { 83 | view: 'table', 84 | data: 'usage.entries().({ type: key, stat: value })', 85 | cols: [ 86 | { header: 'Type', data: 'type' }, 87 | { header: 'All', data: 'stat.count' }, 88 | { header: 'Valid', className: 'number', data: 'stat.valid', content: 'text:size()', details: usageDictTable }, 89 | { header: 'Invalid', className: 'number', data: 'stat.invalid', content: 'text:size()', details: usageDictTable }, 90 | { header: 'Unknown', className: 'number', data: 'stat.unknown', content: 'text:size()', details: usageDictTable }, 91 | { header: 'Known', className: 'number', data: '#.data.usageDict[type].valid', content: 'text:size()', details: { 92 | view: 'table', 93 | data: '.({ fullName: $ })' 94 | } }, 95 | { header: 'Unused known', className: 'number', data: 'stat.unusedKnowns', content: 'text:size()', details: { 96 | view: 'table', 97 | data: '.({ fullName: $ })' 98 | } } 99 | ] 100 | }, 101 | 'link:{ href: "#usage", text: "Usage page" }' 102 | ] 103 | } 104 | ] 105 | }, 106 | { view: 'struct', expanded: 2 } 107 | ]); 108 | -------------------------------------------------------------------------------- /ui/page/usage.css: -------------------------------------------------------------------------------- 1 | .page-usage .name { 2 | font-family: var(--discovery-monospace-font-family); 3 | margin: 0 .6ex; 4 | } 5 | 6 | .page-usage .view-button { 7 | padding: 1px 6px; 8 | margin: 0 4px 0 0; 9 | opacity: .8; 10 | } 11 | .page-usage .view-button:hover { 12 | opacity: 1; 13 | } 14 | -------------------------------------------------------------------------------- /ui/page/usage.js: -------------------------------------------------------------------------------- 1 | /* eslint-env browser */ 2 | 3 | const statBadge = { 4 | view: 'badge', 5 | data: `{ 6 | $name; 7 | $type; 8 | $siteCount: sites.size(); 9 | $stylesheetCount: stylesheets.size(); 10 | $entryCount: stylesheets.stylesheet.usage.reduce(=> $$ + $[$type][$name], 0); 11 | 12 | sites, 13 | $siteCount, 14 | $stylesheetCount, 15 | $entryCount, 16 | text: [ 17 | $entryCount, 18 | $stylesheetCount, 19 | $siteCount 20 | ].join(' / ') 21 | }`, 22 | tooltip: [ 23 | { view: 'block', content: 'text:"Entries: " + entryCount' }, 24 | { view: 'block', content: 'text:"StyleSheets: " + stylesheetCount' }, 25 | { view: 'block', content: 'text:"Sites: " + siteCount' }, 26 | { view: 'ol', data: 'sites', item: 'text:domain', limit: 10 } 27 | ] 28 | }; 29 | 30 | function setStateHandler(toSet) { 31 | return function (_, data, context) { 32 | const stat = context.data.usage[data.type]; 33 | 34 | for (const fromSet of ['valid', 'invalid', 'unknown']) { 35 | const fromIdx = stat[fromSet].indexOf(data); 36 | 37 | if (fromIdx !== -1) { 38 | if (fromSet === toSet) { 39 | console.warn('From and to sets are equal'); 40 | return; 41 | } 42 | 43 | const toIdx = stat[toSet].findIndex(item => item.name > data.name); 44 | 45 | stat[fromSet].splice(fromIdx, 1); 46 | 47 | if (toIdx === -1) { 48 | stat[toSet].push(data); 49 | } else { 50 | stat[toSet].splice(toIdx, 0, data); 51 | } 52 | 53 | discovery.renderPage(); 54 | break; 55 | } 56 | } 57 | }; 58 | } 59 | 60 | discovery.page.define('usage', [ 61 | { 62 | view: 'page-header', 63 | content: 'h1:"Usage across sites"' 64 | }, 65 | { 66 | view: 'list', 67 | data: 'usage.entries().({ name: key, ... value })', 68 | item: [ 69 | 'h2:name', 70 | { 71 | view: 'hstack', 72 | content: [ 73 | { 74 | view: 'block', 75 | data: 'valid', 76 | content: [ 77 | 'h4:`✅ Valid (${size()})`', 78 | { view: 'ul', item: [ 79 | 'html:`${fullName}`', 80 | statBadge 81 | ] } 82 | ] 83 | }, 84 | { 85 | view: 'block', 86 | data: 'invalid', 87 | content: [ 88 | 'h4:`❌ Invalid (${size()})`', 89 | { view: 'ul', item: [ 90 | 'html:`${fullName}`', 91 | statBadge 92 | ] } 93 | ] 94 | }, 95 | { 96 | view: 'block', 97 | data: 'unknown', 98 | whenData: true, 99 | content: [ 100 | 'h4:`⚠️ Unknown (${size()})`', 101 | { view: 'ul', item: [ 102 | ...location.hostname === 'localhost' 103 | ? [ 104 | { view: 'button', content: 'text:"✅"', onClick: setStateHandler('valid') }, 105 | { view: 'button', content: 'text:"❌"', onClick: setStateHandler('invalid') } 106 | ] 107 | : [], 108 | 'html:`${fullName}`', 109 | statBadge 110 | ] } 111 | ] 112 | } 113 | ] 114 | } 115 | ] 116 | } 117 | ]); 118 | -------------------------------------------------------------------------------- /ui/prepare.js: -------------------------------------------------------------------------------- 1 | export default function(data, { defineObjectMarker }) { 2 | const siteMarker = defineObjectMarker('site', { ref: 'domain' }); 3 | const stylesheetMarker = defineObjectMarker('stylesheet'); 4 | const usageStat = Object.create(null); 5 | 6 | data.usage = usageStat; 7 | 8 | for (const type of Object.keys(data.usageDict)) { 9 | usageStat[type] = Object.create(null); 10 | } 11 | 12 | for (const site of data.sites) { 13 | siteMarker(site); 14 | 15 | for (const stylesheet of site.stylesheets) { 16 | stylesheetMarker(stylesheet); 17 | stylesheet.site = site; 18 | 19 | for (const error of stylesheet.errors) { 20 | error.stylesheet = stylesheet; 21 | } 22 | 23 | for (const [type, stat] of Object.entries(stylesheet.usage)) { 24 | const map = usageStat[type]; 25 | 26 | for (const [key, count] of Object.entries(stat)) { 27 | if (!map[key]) { 28 | map[key] = []; 29 | } 30 | 31 | map[key].push({ 32 | domain: site.domain, 33 | stylesheet, 34 | count 35 | }); 36 | } 37 | } 38 | } 39 | } 40 | 41 | for (const [type, stat] of Object.entries(usageStat)) { 42 | const usageDict = data.usageDict[type]; 43 | const neverUsedValid = new Set(usageDict.valid); 44 | const newStat = { 45 | count: 0, 46 | unusedKnowns: null, 47 | valid: [], 48 | invalid: [], 49 | unknown: [] 50 | }; 51 | 52 | usageStat[type] = newStat; 53 | 54 | for (const [name, stylesheets] of Object.entries(stat)) { 55 | const validity = usageDict.valid.includes(name) 56 | ? 'valid' 57 | : usageDict.invalid.includes(name) 58 | ? 'invalid' 59 | : 'unknown'; 60 | let fullName = name; 61 | 62 | switch (type) { 63 | case 'Atrule': 64 | fullName = `@${name}`; 65 | break; 66 | 67 | case 'PseudoClassSelector': 68 | fullName = `:${name}`; 69 | break; 70 | 71 | case 'PseudoElementSelector': 72 | fullName = `::${name}`; 73 | break; 74 | } 75 | 76 | neverUsedValid.delete(name); 77 | newStat.count++; 78 | newStat[validity].push({ 79 | name, 80 | status: validity, 81 | type, 82 | fullName, 83 | sites: [...new Set(stylesheets.map(s => s.stylesheet.site))], 84 | stylesheets 85 | }); 86 | } 87 | 88 | newStat.unusedKnowns = [...neverUsedValid]; 89 | newStat.valid.sort((a, b) => a.name < b.name ? -1 : 1); 90 | newStat.invalid.sort((a, b) => a.name < b.name ? -1 : 1); 91 | newStat.unknown.sort((a, b) => a.name < b.name ? -1 : 1); 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /ui/view/chart.css: -------------------------------------------------------------------------------- 1 | .view-chart { 2 | --values-padding: 0 3px; 3 | --fill-bar-direction: top; 4 | --value-line-direction: bottom; 5 | --x-direction: row; 6 | --x-labels-border: 1px 0 0 0; 7 | --x-labels-padding: 4px 3px; 8 | --y-direction: column-reverse; 9 | --min-height: 200px; 10 | 11 | display: grid; 12 | grid-template-areas: 13 | "y-labels canvas" 14 | "- x-labels"; 15 | grid-template-columns: min-content 1fr; 16 | padding-top: 5px; 17 | } 18 | .view-chart.vertical { 19 | --values-padding: 5px 0; 20 | --fill-bar-direction: right; 21 | --value-line-direction: left; 22 | --x-direction: column; 23 | --x-labels-border: 0 1px 0 0; 24 | --x-labels-padding: 5px 4px; 25 | --y-direction: row; 26 | --min-height: auto; 27 | 28 | grid-template-areas: 29 | "x-labels canvas" 30 | "- y-labels"; 31 | } 32 | .view-chart .canvas { 33 | grid-area: canvas; 34 | display: flex; 35 | flex-direction: var(--x-direction); 36 | align-items: stretch; 37 | gap: 1px; 38 | min-height: var(--min-height); 39 | padding: var(--values-padding); 40 | background-size: calc(100% / (var(--y-label-count) - 1)) 41 | calc(100% / (var(--y-label-count) - 1)); 42 | background-image: linear-gradient( 43 | to var(--value-line-direction), 44 | #8883 1px, 45 | transparent 0 46 | ); 47 | } 48 | .view-chart .canvas .bar { 49 | flex: 1; 50 | } 51 | .view-chart.vertical .canvas .bar { 52 | position: relative; 53 | min-height: 13px; 54 | } 55 | @keyframes chart-bar-grow { 56 | from, 57 | 30% { 58 | --size: 0; 59 | } 60 | } 61 | .view-chart .canvas .bar::before { 62 | content: ""; 63 | display: block; 64 | height: 100%; 65 | /* animation: 0.9s ease chart-bar-grow; */ 66 | background-image: linear-gradient( 67 | to var(--fill-bar-direction), 68 | var(--color, #54a6eea6) 69 | max(min(1px, var(--size) * 1000000px), calc(var(--size) * 100%)), 70 | transparent 0 71 | ); 72 | } 73 | .view-chart .canvas .bar:hover { 74 | background: #fff2; 75 | } 76 | 77 | .view-chart .y-labels { 78 | grid-area: y-labels; 79 | display: flex; 80 | flex-direction: var(--y-direction); 81 | justify-content: stretch; 82 | position: relative; 83 | } 84 | .view-chart .y-label { 85 | flex: 1; 86 | min-width: 4ex; 87 | opacity: 0.75; 88 | line-height: 1; 89 | font-size: 10px; 90 | text-align: right; 91 | } 92 | .view-chart:not(.vertical) .y-label { 93 | margin-top: -5px; 94 | margin-right: 6px; 95 | } 96 | .view-chart.vertical .y-label { 97 | writing-mode: vertical-rl; 98 | transform: translate(-10px) rotate(215deg); 99 | transform-origin: right center; 100 | } 101 | .view-chart:not(.vertical) .y-label:first-child { 102 | position: absolute; 103 | right: 0; 104 | margin-bottom: -5px; 105 | } 106 | .view-chart.vertical .y-label:first-child { 107 | position: absolute; 108 | left: 0; 109 | margin-left: -15px; 110 | } 111 | 112 | .view-chart .x-labels { 113 | grid-area: x-labels; 114 | position: relative; 115 | display: flex; 116 | flex-direction: var(--x-direction); 117 | justify-content: space-around; 118 | gap: 1px; 119 | border: solid #8883; 120 | border-width: var(--x-labels-border); 121 | padding: var(--x-labels-padding); 122 | cursor: default; 123 | } 124 | .view-chart .x-label { 125 | white-space: nowrap; 126 | line-height: 1; 127 | font-size: 10px; 128 | text-align: right; 129 | opacity: 0.75; 130 | } 131 | .view-chart:not(.vertical) .x-label { 132 | writing-mode: vertical-rl; 133 | transform: translate(calc(-50% - 15px), -5px) rotate(215deg); 134 | transform-origin: right center; 135 | } 136 | .view-chart.vertical .x-label { 137 | padding: 1px 0 2px; 138 | } 139 | 140 | .chart-tooltip { 141 | padding: 5px 10px; 142 | min-width: 120px; 143 | border: 0.5px solid #fff5; 144 | border-radius: 3px; 145 | font-size: 12px; 146 | background: rgba(255, 255, 255, 0.75); 147 | -webkit-backdrop-filter: blur(4px); 148 | backdrop-filter: blur(4px); 149 | } 150 | .discovery-root-darkmode .chart-tooltip { 151 | background: rgba(36, 36, 36, 0.8); 152 | } 153 | .chart-tooltip .view-badge { 154 | display: inline-block; 155 | margin: 0 0 3px -5px; 156 | padding-top: 0; 157 | padding-bottom: 1px; 158 | line-height: 19px; 159 | } 160 | -------------------------------------------------------------------------------- /ui/view/chart.js: -------------------------------------------------------------------------------- 1 | /* eslint-env browser */ 2 | /* global discovery */ 3 | 4 | const dataByEl = new WeakMap(); 5 | 6 | new discovery.view.Popup({ 7 | className: 'chart-tooltip', 8 | hoverTriggers: '.view-chart .bar', 9 | position: 'pointer', 10 | render(el, triggerEl) { 11 | const { data, context } = dataByEl.get(triggerEl); 12 | 13 | discovery.view.render( 14 | el, 15 | [ 16 | { view: 'block', content: 'text:labelText' }, 17 | { 18 | view: 'block', 19 | content: 'text-numeric:value = undefined ? "–" : value' 20 | } 21 | ], 22 | data, 23 | context 24 | ); 25 | } 26 | }); 27 | 28 | // CSS.registerProperty({ 29 | // name: '--size', 30 | // initialValue: 0, 31 | // syntax: '', 32 | // inherits: true, 33 | // }); 34 | 35 | discovery.view.define('chart', (el, config, data, context) => { 36 | function addBar(x, y) { 37 | const barEl = canvasEl.appendChild(document.createElement('div')); 38 | const labelEl = xLabelsEl.appendChild(document.createElement('div')); 39 | const num = xLabelsEl.children.length - 1; 40 | const labelText = labelX(x, num); 41 | 42 | barEl.className = y === undefined ? 'bar empty' : 'bar'; 43 | barEl.style.setProperty('--size', (y || 0) / roundedMaxY); 44 | labelEl.className = 'x-label'; 45 | labelEl.textContent = labelText; 46 | labelEl.style.setProperty('--num', num); 47 | 48 | dataByEl.set(barEl, { 49 | context, 50 | data: { 51 | value: y, 52 | labelNum: num, 53 | labelRaw: x, 54 | labelText 55 | } 56 | }); 57 | } 58 | 59 | const { vertical, sort = true } = config; 60 | let { nextX, labelX } = config; 61 | const sortedData = sort ? data.slice().sort((a, b) => (a.x < b.x ? -1 : 1)) : data; 62 | const maxY = sortedData.reduce((maxY, { y }) => Math.max(maxY, y), 0); 63 | let roundedMaxY = maxY; 64 | const minY = sortedData.reduce((minY, { y }) => Math.min(minY, y), Infinity); 65 | 66 | if (typeof nextX !== 'function') { 67 | nextX = (_, current) => current; 68 | } 69 | 70 | if (typeof labelX !== 'function') { 71 | labelX = value => value; 72 | } 73 | 74 | // find better split 75 | 76 | const canvasEl = el.appendChild(document.createElement('div')); 77 | const xLabelsEl = el.appendChild(document.createElement('div')); 78 | const yLabelsEl = el.appendChild(document.createElement('div')); 79 | 80 | canvasEl.className = 'canvas'; 81 | xLabelsEl.className = 'x-labels'; 82 | yLabelsEl.className = 'y-labels'; 83 | 84 | // console.log({ minY, maxY }); 85 | // round maxY 86 | if (maxY - minY > 5) { 87 | const allowedGrowth = maxY - minY < 20 ? 1.8 : 1.15; 88 | let base = maxY - minY < 100 ? 5 : 10; 89 | 90 | while (true) { 91 | const add = base - (roundedMaxY % base); 92 | 93 | if ((roundedMaxY + add) / maxY > allowedGrowth) { 94 | break; 95 | } 96 | 97 | roundedMaxY += add; 98 | // console.log({ normMaxY }); 99 | base *= 10; 100 | } 101 | } else { 102 | roundedMaxY = 5; 103 | } 104 | 105 | for ( 106 | let i = 0, n = roundedMaxY % 5 ? 5 : 6, step = roundedMaxY / (n - 1); 107 | i < n; 108 | i++ 109 | ) { 110 | const labelEl = yLabelsEl.appendChild(document.createElement('div')); 111 | labelEl.textContent = step * i; 112 | labelEl.className = 'y-label'; 113 | } 114 | 115 | let prev = sortedData[0].x; 116 | for (const { x, y } of sortedData) { 117 | while (prev !== x) { 118 | prev = nextX(prev, x); 119 | if (prev !== x) { 120 | addBar(prev); 121 | } 122 | } 123 | 124 | addBar(x, y); 125 | prev = x; 126 | } 127 | 128 | el.style.setProperty('--y-label-count', yLabelsEl.children.length); 129 | el.style.setProperty('--x-label-count', xLabelsEl.children.length); 130 | 131 | el.classList.toggle('vertical', Boolean(vertical)); 132 | el.append(yLabelsEl, canvasEl, xLabelsEl); 133 | }); 134 | -------------------------------------------------------------------------------- /ui/view/sidebar.js: -------------------------------------------------------------------------------- 1 | discovery.view.define('sidebar', { 2 | view: 'content-filter', 3 | content: { 4 | view: 'list' 5 | } 6 | }, { 7 | tag: false 8 | }); 9 | --------------------------------------------------------------------------------