From 17ad15358367658eccbd69df70d73fb0715b2bea Mon Sep 17 00:00:00 2001 From: Kamran Ahmed Date: Tue, 24 Jan 2023 18:36:14 +0400 Subject: [PATCH] Add forntend performance content --- bin/best-practice-content.cjs | 163 ++ bin/readme.md | 28 + package.json | 1 + .../best-practices/frontend-performance.json | 2596 ++++++++++++----- .../content/100-minimize-iframe-count.md | 1 + .../content/101-minify-css.md | 1 + .../102-make-css-files-non-blocking.md | 1 + .../content/103-inline-critical-css.md | 1 + .../content/104-avoid-inline-css.md | 1 + .../105-analyse-stylesheets-complexity.md | 1 + .../content/106-compress-your-images.md | 1 + .../107-choose-image-format-approprietly.md | 1 + .../content/108-minify-your-javascript.md | 1 + .../109-use-non-blocking-javascript.md | 1 + .../content/110-use-https-on-your-website.md | 1 + .../content/111-page-weight-below-1500.md | 1 + .../content/112-page-load-time-below-3s.md | 1 + .../content/113-keep-ttfb-less-1-3s.md | 1 + .../content/114-minimize-http-requests.md | 1 + .../content/115-use-same-protocol.md | 1 + .../content/116-avoid-404-files.md | 1 + .../content/117-use-http-cache-headers.md | 1 + .../content/118-enable-compression.md | 1 + .../content/119-minify-html.md | 1 + .../content/120-use-cdn.md | 1 + .../content/121-prefer-vector-images.md | 1 + .../content/122-set-width-height-images.md | 1 + .../content/123-avoid-base64-images.md | 1 + .../124-load-offscreen-images-lazily.md | 1 + .../content/125-serve-exact-size-images.md | 1 + .../126-avoid-multiple-inline-js-snippets.md | 1 + .../127-keep-dependencies-up-to-date.md | 1 + .../content/128-analyze-js-for-perf-issues.md | 1 + .../129-use-service-workers-for-caching.md | 1 + .../130-cookie-size-less-4096-bytes.md | 1 + .../content/131-keep-cookie-count-below-20.md | 1 + .../132-pre-load-urls-where-possible.md | 1 + .../133-concatenate-css-single-file.md | 1 + .../content/134-remove-unused-css.md | 1 + .../content/135-use-woff2-font-format.md | 1 + .../136-use-preconnect-to-load-fonts.md | 1 + .../content/137-keep-web-font-under-300k.md | 1 + .../content/138-prevent-flash-text.md | 1 + .../content/139-check-dependency-size.md | 1 + .../content/140-page-speed-insights.md | 1 + .../content/141-lighthouse.md | 1 + .../content/142-web-page-test.md | 1 + .../content/143-chrome-dev-tools.md | 1 + .../content/144-bundlephobia.md | 1 + .../content/145-squoosh-ap.md | 1 + .../content/146-framework-guides.md | 1 + .../content/147-recommended-guides.md | 1 + .../frontend-performance/content/index.md | 1 + .../content/minimize-iframes.md | 1 - 54 files changed, 2026 insertions(+), 812 deletions(-) create mode 100644 bin/best-practice-content.cjs create mode 100644 bin/readme.md create mode 100644 src/best-practices/frontend-performance/content/100-minimize-iframe-count.md create mode 100644 src/best-practices/frontend-performance/content/101-minify-css.md create mode 100644 src/best-practices/frontend-performance/content/102-make-css-files-non-blocking.md create mode 100644 src/best-practices/frontend-performance/content/103-inline-critical-css.md create mode 100644 src/best-practices/frontend-performance/content/104-avoid-inline-css.md create mode 100644 src/best-practices/frontend-performance/content/105-analyse-stylesheets-complexity.md create mode 100644 src/best-practices/frontend-performance/content/106-compress-your-images.md create mode 100644 src/best-practices/frontend-performance/content/107-choose-image-format-approprietly.md create mode 100644 src/best-practices/frontend-performance/content/108-minify-your-javascript.md create mode 100644 src/best-practices/frontend-performance/content/109-use-non-blocking-javascript.md create mode 100644 src/best-practices/frontend-performance/content/110-use-https-on-your-website.md create mode 100644 src/best-practices/frontend-performance/content/111-page-weight-below-1500.md create mode 100644 src/best-practices/frontend-performance/content/112-page-load-time-below-3s.md create mode 100644 src/best-practices/frontend-performance/content/113-keep-ttfb-less-1-3s.md create mode 100644 src/best-practices/frontend-performance/content/114-minimize-http-requests.md create mode 100644 src/best-practices/frontend-performance/content/115-use-same-protocol.md create mode 100644 src/best-practices/frontend-performance/content/116-avoid-404-files.md create mode 100644 src/best-practices/frontend-performance/content/117-use-http-cache-headers.md create mode 100644 src/best-practices/frontend-performance/content/118-enable-compression.md create mode 100644 src/best-practices/frontend-performance/content/119-minify-html.md create mode 100644 src/best-practices/frontend-performance/content/120-use-cdn.md create mode 100644 src/best-practices/frontend-performance/content/121-prefer-vector-images.md create mode 100644 src/best-practices/frontend-performance/content/122-set-width-height-images.md create mode 100644 src/best-practices/frontend-performance/content/123-avoid-base64-images.md create mode 100644 src/best-practices/frontend-performance/content/124-load-offscreen-images-lazily.md create mode 100644 src/best-practices/frontend-performance/content/125-serve-exact-size-images.md create mode 100644 src/best-practices/frontend-performance/content/126-avoid-multiple-inline-js-snippets.md create mode 100644 src/best-practices/frontend-performance/content/127-keep-dependencies-up-to-date.md create mode 100644 src/best-practices/frontend-performance/content/128-analyze-js-for-perf-issues.md create mode 100644 src/best-practices/frontend-performance/content/129-use-service-workers-for-caching.md create mode 100644 src/best-practices/frontend-performance/content/130-cookie-size-less-4096-bytes.md create mode 100644 src/best-practices/frontend-performance/content/131-keep-cookie-count-below-20.md create mode 100644 src/best-practices/frontend-performance/content/132-pre-load-urls-where-possible.md create mode 100644 src/best-practices/frontend-performance/content/133-concatenate-css-single-file.md create mode 100644 src/best-practices/frontend-performance/content/134-remove-unused-css.md create mode 100644 src/best-practices/frontend-performance/content/135-use-woff2-font-format.md create mode 100644 src/best-practices/frontend-performance/content/136-use-preconnect-to-load-fonts.md create mode 100644 src/best-practices/frontend-performance/content/137-keep-web-font-under-300k.md create mode 100644 src/best-practices/frontend-performance/content/138-prevent-flash-text.md create mode 100644 src/best-practices/frontend-performance/content/139-check-dependency-size.md create mode 100644 src/best-practices/frontend-performance/content/140-page-speed-insights.md create mode 100644 src/best-practices/frontend-performance/content/141-lighthouse.md create mode 100644 src/best-practices/frontend-performance/content/142-web-page-test.md create mode 100644 src/best-practices/frontend-performance/content/143-chrome-dev-tools.md create mode 100644 src/best-practices/frontend-performance/content/144-bundlephobia.md create mode 100644 src/best-practices/frontend-performance/content/145-squoosh-ap.md create mode 100644 src/best-practices/frontend-performance/content/146-framework-guides.md create mode 100644 src/best-practices/frontend-performance/content/147-recommended-guides.md create mode 100644 src/best-practices/frontend-performance/content/index.md delete mode 100644 src/best-practices/frontend-performance/content/minimize-iframes.md diff --git a/bin/best-practice-content.cjs b/bin/best-practice-content.cjs new file mode 100644 index 000000000..d7bf95b3d --- /dev/null +++ b/bin/best-practice-content.cjs @@ -0,0 +1,163 @@ +const fs = require('fs'); +const path = require('path'); + +const CONTENT_DIR = path.join(__dirname, '../content'); +// Directory containing the best-practices +const BEST_PRACTICE_CONTENT_DIR = path.join(__dirname, '../src/best-practices'); +const bestPracticeId = process.argv[2]; + +const allowedBestPracticeId = fs.readdirSync(BEST_PRACTICE_CONTENT_DIR); +if (!bestPracticeId) { + console.error('bestPractice is required'); + process.exit(1); +} + +if (!allowedBestPracticeId.includes(bestPracticeId)) { + console.error(`Invalid best practice key ${bestPracticeId}`); + console.error(`Allowed keys are ${allowedBestPracticeId.join(', ')}`); + process.exit(1); +} + +// Directory holding the best parctice content files +const bestPracticeDirName = fs + .readdirSync(BEST_PRACTICE_CONTENT_DIR) + .find((dirName) => dirName.replace(/\d+-/, '') === bestPracticeId); + +if (!bestPracticeDirName) { + console.error('Best practice directory not found'); + process.exit(1); +} + +const bestPracticeDirPath = path.join(BEST_PRACTICE_CONTENT_DIR, bestPracticeDirName); +const bestPracticeContentDirPath = path.join( + BEST_PRACTICE_CONTENT_DIR, + bestPracticeDirName, + 'content' +); + +// If best practice content already exists do not proceed as it would override the files +if (fs.existsSync(bestPracticeContentDirPath)) { + console.error(`Best Practice content already exists @ ${bestPracticeContentDirPath}`); + process.exit(1); +} + +function prepareDirTree(control, dirTree, dirSortOrders) { + // Directories are only created for groups + if (control.typeID !== '__group__') { + return; + } + + // e.g. 104-testing-your-apps:other-options + const controlName = control?.properties?.controlName || ''; + // e.g. 104 + const sortOrder = controlName.match(/^\d+/)?.[0]; + + // No directory for a group without control name + if (!controlName || !sortOrder) { + return; + } + + // e.g. testing-your-apps:other-options + const controlNameWithoutSortOrder = controlName.replace(/^\d+-/, ''); + // e.g. ['testing-your-apps', 'other-options'] + const dirParts = controlNameWithoutSortOrder.split(':'); + + // Nest the dir path in the dirTree + let currDirTree = dirTree; + dirParts.forEach((dirPart) => { + currDirTree[dirPart] = currDirTree[dirPart] || {}; + currDirTree = currDirTree[dirPart]; + }); + + dirSortOrders[controlNameWithoutSortOrder] = Number(sortOrder); + + const childrenControls = control.children.controls.control; + // No more children + if (childrenControls.length) { + childrenControls.forEach((childControl) => { + prepareDirTree(childControl, dirTree, dirSortOrders); + }); + } + + return { dirTree, dirSortOrders }; +} + +const bestPractice = require(path.join(__dirname, `../public/jsons/best-practices/${bestPracticeId}`)); +const controls = bestPractice.mockup.controls.control; + +// Prepare the dir tree that we will be creating and also calculate the sort orders +const dirTree = {}; +const dirSortOrders = {}; + +controls.forEach((control) => { + prepareDirTree(control, dirTree, dirSortOrders); +}); + +/** + * @param parentDir Parent directory in which directory is to be created + * @param dirTree Nested dir tree to be created + * @param sortOrders Mapping from groupName to sort order + * @param filePaths The mapping from groupName to file path + */ +function createDirTree(parentDir, dirTree, sortOrders, filePaths = {}) { + const childrenDirNames = Object.keys(dirTree); + const hasChildren = childrenDirNames.length !== 0; + + // @todo write test for this, yolo for now + const groupName = parentDir + .replace(bestPracticeContentDirPath, '') // Remove base dir path + .replace(/(^\/)|(\/$)/g, '') // Remove trailing slashes + .replace(/(^\d+?-)/g, '') // Remove sorting information + .replaceAll('/', ':') // Replace slashes with `:` + .replace(/:\d+-/, ':'); + + const humanizedGroupName = groupName + .split(':') + .pop() + ?.replaceAll('-', ' ') + .replace(/^\w/, ($0) => $0.toUpperCase()); + + const sortOrder = sortOrders[groupName] || ''; + + // Attach sorting information to dirname + // e.g. /best-practices/frontend-performance/content/internet + // ———> /best-practices/frontend-performance/content/103-internet + if (sortOrder) { + parentDir = parentDir.replace(/(.+?)([^\/]+)?$/, `$1${sortOrder}-$2`); + } + + // If no children, create a file for this under the parent directory + if (!hasChildren) { + let fileName = `${parentDir}.md`; + fs.writeFileSync(fileName, `# ${humanizedGroupName}`); + + filePaths[groupName || 'home'] = fileName.replace(CONTENT_DIR, ''); + return filePaths; + } + + // There *are* children, so create the parent as a directory + // and create `index.md` as the content file for this + fs.mkdirSync(parentDir); + + let readmeFilePath = path.join(parentDir, 'index.md'); + fs.writeFileSync(readmeFilePath, `# ${humanizedGroupName}`); + + filePaths[groupName || 'home'] = readmeFilePath.replace(CONTENT_DIR, ''); + + // For each of the directory names, create a + // directory inside the given directory + childrenDirNames.forEach((dirName) => { + createDirTree( + path.join(parentDir, dirName), + dirTree[dirName], + dirSortOrders, + filePaths + ); + }); + + return filePaths; +} + +// Create directories and get back the paths for created directories +createDirTree(bestPracticeContentDirPath, dirTree, dirSortOrders); +console.log('Created best practice content directory structure'); \ No newline at end of file diff --git a/bin/readme.md b/bin/readme.md new file mode 100644 index 000000000..5b38e692d --- /dev/null +++ b/bin/readme.md @@ -0,0 +1,28 @@ +## CLI Tools +> A bunch of CLI scripts to make the development easier + +## `roadmap-links.cjs` + +Generates a list of all the resources links in any roadmap file. + +## `compress-jsons.cjs` + +Compresses all the JSON files in the `public/jsons` folder + +## `roadmap-content.cjs` + +This command is used to create the content folders and files for the interactivity of the roadmap. You can use the below command to generate the roadmap skeletons inside a roadmap directory: + +```bash +npm run roadmap-content [frontend|backend|devops|...] +``` + +For the content skeleton to be generated, we should have proper grouping, and the group names in the project files. You can follow the steps listed below in order to add the meta information to the roadmap. + +- Remove all the groups from the roadmaps through the project editor. Select all and press `cmd+shift+g` +- Identify the boxes that should be clickable and group them together with `cmd+shift+g` +- Assign the name to the groups. + - Group names have the format of `[sort]-[slug]` e.g. `100-internet`. Each group name should start with a number starting from 100 which helps with sorting of the directories and the files. Groups at the same level have the sequential sorting information. + - Each groups children have a separate group and have the name similar to `[sort]-[parent-slug]:[child-slug]` where sort refers to the sorting of the `child-slug` and not the parent. Also parent-slug does not need to have the sorting information as a part of slug e.g. if parent was `100-internet` the children would be `100-internet:how-does-the-internet-work`, `101-internet:what-is-http`, `102-internet:browsers`. + + diff --git a/package.json b/package.json index d52a4bf78..03a61930a 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ "upgrade": "ncu -u", "roadmap-links": "node bin/roadmap-links.cjs", "roadmap-content": "node bin/roadmap-content.cjs", + "best-practice-content": "node bin/best-practice-content.cjs", "test:e2e": "playwright test" }, "dependencies": { diff --git a/public/jsons/best-practices/frontend-performance.json b/public/jsons/best-practices/frontend-performance.json index 9c8e73e2b..98dcaab38 100644 --- a/public/jsons/best-practices/frontend-performance.json +++ b/public/jsons/best-practices/frontend-performance.json @@ -5,7 +5,7 @@ { "ID": "14813", "typeID": "Label", - "zOrder": "9", + "zOrder": "8", "measuredW": "327", "measuredH": "40", "x": "676", @@ -18,7 +18,7 @@ { "ID": "14848", "typeID": "Canvas", - "zOrder": "10", + "zOrder": "9", "w": "361", "h": "150", "measuredW": "100", @@ -29,7 +29,7 @@ { "ID": "14849", "typeID": "Label", - "zOrder": "11", + "zOrder": "10", "measuredW": "332", "measuredH": "26", "x": "1105", @@ -42,7 +42,7 @@ { "ID": "14850", "typeID": "Label", - "zOrder": "12", + "zOrder": "11", "measuredW": "318", "measuredH": "26", "x": "1105", @@ -55,7 +55,7 @@ { "ID": "14853", "typeID": "Canvas", - "zOrder": "15", + "zOrder": "14", "w": "373", "h": "169", "measuredW": "100", @@ -66,7 +66,7 @@ { "ID": "14854", "typeID": "__group__", - "zOrder": "16", + "zOrder": "15", "measuredW": "191", "measuredH": "27", "w": "191", @@ -148,7 +148,7 @@ { "ID": "14855", "typeID": "__group__", - "zOrder": "17", + "zOrder": "16", "measuredW": "202", "measuredH": "27", "w": "202", @@ -230,7 +230,7 @@ { "ID": "14856", "typeID": "Label", - "zOrder": "18", + "zOrder": "17", "measuredW": "209", "measuredH": "32", "x": "256", @@ -243,7 +243,7 @@ { "ID": "14857", "typeID": "Arrow", - "zOrder": "19", + "zOrder": "18", "w": "1", "h": "76", "measuredW": "150", @@ -273,7 +273,7 @@ { "ID": "14882", "typeID": "TextArea", - "zOrder": "20", + "zOrder": "19", "w": "29", "h": "30", "measuredW": "200", @@ -281,23 +281,10 @@ "x": "849", "y": "461" }, - { - "ID": "14883", - "typeID": "Label", - "zOrder": "21", - "measuredW": "481", - "measuredH": "28", - "x": "897", - "y": "462", - "properties": { - "size": "20", - "text": "Minified HTML - Remove comments and whitespaces" - } - }, { "ID": "14915", "typeID": "TextArea", - "zOrder": "22", + "zOrder": "21", "w": "29", "h": "30", "measuredW": "200", @@ -305,23 +292,10 @@ "x": "849", "y": "500" }, - { - "ID": "14916", - "typeID": "Label", - "zOrder": "23", - "measuredW": "271", - "measuredH": "28", - "x": "897", - "y": "501", - "properties": { - "text": "Use Content Delivery Network", - "size": "20" - } - }, { "ID": "14920", "typeID": "TextArea", - "zOrder": "24", + "zOrder": "23", "w": "29", "h": "30", "measuredW": "200", @@ -329,23 +303,10 @@ "x": "850", "y": "1108" }, - { - "ID": "14921", - "typeID": "Label", - "zOrder": "25", - "measuredW": "274", - "measuredH": "28", - "x": "893", - "y": "1111", - "properties": { - "size": "20", - "text": "Pre-load URLs where possible" - } - }, { "ID": "14922", "typeID": "TextArea", - "zOrder": "26", + "zOrder": "25", "w": "29", "h": "30", "measuredW": "200", @@ -353,23 +314,10 @@ "x": "245", "y": "501" }, - { - "ID": "14923", - "typeID": "Label", - "zOrder": "27", - "measuredW": "461", - "measuredH": "28", - "x": "291", - "y": "504", - "properties": { - "size": "20", - "text": "Minified CSS - Remove comments, whitespaces etc" - } - }, { "ID": "14926", "typeID": "TextArea", - "zOrder": "28", + "zOrder": "27", "w": "29", "h": "30", "measuredW": "200", @@ -377,23 +325,10 @@ "x": "850", "y": "1147" }, - { - "ID": "14928", - "typeID": "Label", - "zOrder": "29", - "measuredW": "307", - "measuredH": "28", - "x": "893", - "y": "1148", - "properties": { - "size": "20", - "text": "Concatenate CSS into a single file" - } - }, { "ID": "14929", "typeID": "TextArea", - "zOrder": "30", + "zOrder": "29", "w": "29", "h": "30", "measuredW": "200", @@ -401,23 +336,10 @@ "x": "245", "y": "540" }, - { - "ID": "14930", - "typeID": "Label", - "zOrder": "31", - "measuredW": "237", - "measuredH": "28", - "x": "291", - "y": "543", - "properties": { - "size": "20", - "text": "CSS files are non-blocking" - } - }, { "ID": "14931", "typeID": "TextArea", - "zOrder": "32", + "zOrder": "31", "w": "29", "h": "30", "measuredW": "200", @@ -425,23 +347,10 @@ "x": "850", "y": "1190" }, - { - "ID": "14932", - "typeID": "Label", - "zOrder": "33", - "measuredW": "191", - "measuredH": "28", - "x": "894", - "y": "1191", - "properties": { - "size": "20", - "text": "Remove unused CSS" - } - }, { "ID": "14933", "typeID": "TextArea", - "zOrder": "34", + "zOrder": "33", "w": "29", "h": "30", "measuredW": "200", @@ -449,23 +358,10 @@ "x": "245", "y": "579" }, - { - "ID": "14934", - "typeID": "Label", - "zOrder": "35", - "measuredW": "389", - "measuredH": "28", - "x": "291", - "y": "582", - "properties": { - "size": "20", - "text": "Inline the Critical CSS (above the fold CSS)" - } - }, { "ID": "14935", "typeID": "TextArea", - "zOrder": "36", + "zOrder": "35", "w": "29", "h": "30", "measuredW": "200", @@ -473,23 +369,10 @@ "x": "245", "y": "619" }, - { - "ID": "14936", - "typeID": "Label", - "zOrder": "37", - "measuredW": "299", - "measuredH": "28", - "x": "291", - "y": "622", - "properties": { - "size": "20", - "text": "Avoid the embedded / inline CSS" - } - }, { "ID": "14937", "typeID": "TextArea", - "zOrder": "38", + "zOrder": "37", "w": "29", "h": "30", "measuredW": "200", @@ -497,23 +380,10 @@ "x": "245", "y": "658" }, - { - "ID": "14938", - "typeID": "Label", - "zOrder": "39", - "measuredW": "279", - "measuredH": "28", - "x": "291", - "y": "661", - "properties": { - "size": "20", - "text": "Analyse stylesheets complexity" - } - }, { "ID": "14939", "typeID": "TextArea", - "zOrder": "40", + "zOrder": "39", "w": "29", "h": "30", "measuredW": "200", @@ -521,23 +391,10 @@ "x": "850", "y": "1232" }, - { - "ID": "14940", - "typeID": "Label", - "zOrder": "41", - "measuredW": "213", - "measuredH": "28", - "x": "894", - "y": "1235", - "properties": { - "size": "20", - "text": "Use WOFF2 font format" - } - }, { "ID": "14941", "typeID": "TextArea", - "zOrder": "42", + "zOrder": "41", "w": "29", "h": "30", "measuredW": "200", @@ -545,23 +402,10 @@ "x": "850", "y": "1272" }, - { - "ID": "14942", - "typeID": "Label", - "zOrder": "43", - "measuredW": "362", - "measuredH": "28", - "x": "895", - "y": "1275", - "properties": { - "size": "20", - "text": "Use preconnect to load your fonts faster" - } - }, { "ID": "14943", "typeID": "TextArea", - "zOrder": "44", + "zOrder": "43", "w": "29", "h": "30", "measuredW": "200", @@ -569,23 +413,10 @@ "x": "850", "y": "1311" }, - { - "ID": "14944", - "typeID": "Label", - "zOrder": "45", - "measuredW": "326", - "measuredH": "28", - "x": "895", - "y": "1314", - "properties": { - "size": "20", - "text": "Keep the web font size under 300kb" - } - }, { "ID": "14945", "typeID": "TextArea", - "zOrder": "46", + "zOrder": "45", "w": "29", "h": "30", "measuredW": "200", @@ -593,23 +424,10 @@ "x": "850", "y": "1351" }, - { - "ID": "14946", - "typeID": "Label", - "zOrder": "47", - "measuredW": "269", - "measuredH": "28", - "x": "895", - "y": "1354", - "properties": { - "size": "20", - "text": "Prevent Flash or Invisible Text" - } - }, { "ID": "14947", "typeID": "TextArea", - "zOrder": "48", + "zOrder": "47", "w": "29", "h": "30", "measuredW": "200", @@ -617,23 +435,10 @@ "x": "245", "y": "696" }, - { - "ID": "14948", - "typeID": "Label", - "zOrder": "49", - "measuredW": "458", - "measuredH": "28", - "x": "291", - "y": "697", - "properties": { - "size": "20", - "text": "Compress your images / keep the image count low" - } - }, { "ID": "14949", "typeID": "TextArea", - "zOrder": "50", + "zOrder": "49", "w": "29", "h": "30", "measuredW": "200", @@ -641,23 +446,10 @@ "x": "245", "y": "736" }, - { - "ID": "14950", - "typeID": "Label", - "zOrder": "51", - "measuredW": "362", - "measuredH": "28", - "x": "291", - "y": "737", - "properties": { - "size": "20", - "text": "Choose your image format appropriately" - } - }, { "ID": "14951", "typeID": "TextArea", - "zOrder": "52", + "zOrder": "51", "w": "29", "h": "30", "measuredW": "200", @@ -665,23 +457,10 @@ "x": "849", "y": "540" }, - { - "ID": "14952", - "typeID": "Label", - "zOrder": "53", - "measuredW": "477", - "measuredH": "28", - "x": "897", - "y": "541", - "properties": { - "size": "20", - "text": "Prefer using vector image rather than bitmap images" - } - }, { "ID": "14953", "typeID": "TextArea", - "zOrder": "54", + "zOrder": "53", "w": "29", "h": "30", "measuredW": "200", @@ -689,23 +468,10 @@ "x": "849", "y": "580" }, - { - "ID": "14954", - "typeID": "Label", - "zOrder": "55", - "measuredW": "501", - "measuredH": "28", - "x": "897", - "y": "581", - "properties": { - "size": "20", - "text": "Set width and height attributes on images (aspect ratio)" - } - }, { "ID": "14955", "typeID": "TextArea", - "zOrder": "56", + "zOrder": "55", "w": "29", "h": "30", "measuredW": "200", @@ -713,23 +479,10 @@ "x": "849", "y": "620" }, - { - "ID": "14956", - "typeID": "Label", - "zOrder": "57", - "measuredW": "251", - "measuredH": "28", - "x": "897", - "y": "621", - "properties": { - "size": "20", - "text": "Avoid using Base64 images" - } - }, { "ID": "14957", "typeID": "TextArea", - "zOrder": "58", + "zOrder": "57", "w": "29", "h": "30", "measuredW": "200", @@ -737,23 +490,10 @@ "x": "849", "y": "660" }, - { - "ID": "14958", - "typeID": "Label", - "zOrder": "59", - "measuredW": "311", - "measuredH": "28", - "x": "897", - "y": "661", - "properties": { - "size": "20", - "text": "Offscreen images are loaded lazily" - } - }, { "ID": "14959", "typeID": "TextArea", - "zOrder": "60", + "zOrder": "59", "w": "29", "h": "30", "measuredW": "200", @@ -761,23 +501,10 @@ "x": "849", "y": "700" }, - { - "ID": "14960", - "typeID": "Label", - "zOrder": "61", - "measuredW": "516", - "measuredH": "28", - "x": "897", - "y": "701", - "properties": { - "size": "20", - "text": "Ensure to serve images that are close to your display size" - } - }, { "ID": "14961", "typeID": "TextArea", - "zOrder": "62", + "zOrder": "61", "w": "29", "h": "30", "measuredW": "200", @@ -785,23 +512,10 @@ "x": "245", "y": "777" }, - { - "ID": "14962", - "typeID": "Label", - "zOrder": "63", - "measuredW": "194", - "measuredH": "28", - "x": "291", - "y": "778", - "properties": { - "size": "20", - "text": "Minify your JavaScript" - } - }, { "ID": "14964", "typeID": "TextArea", - "zOrder": "64", + "zOrder": "63", "w": "29", "h": "30", "measuredW": "200", @@ -809,23 +523,10 @@ "x": "849", "y": "740" }, - { - "ID": "14965", - "typeID": "Label", - "zOrder": "65", - "measuredW": "450", - "measuredH": "28", - "x": "897", - "y": "741", - "properties": { - "size": "20", - "text": "Avoid multiple inline JavaScript snippets