diff --git a/.eslintignore b/.eslintignore index 112136a2..2c0895e1 100644 --- a/.eslintignore +++ b/.eslintignore @@ -13,3 +13,4 @@ utils/doclint/generate_types/test/test.ts vendor/ web-test-runner.config.mjs test-ts-types/ +website/ diff --git a/.gitignore b/.gitignore index 64c5ce08..7db3ffd3 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,4 @@ new-docs/ puppeteer.tgz docs-api-json/ docs-dist/ +website/docs diff --git a/.prettierignore b/.prettierignore index 48855903..32785bfe 100644 --- a/.prettierignore +++ b/.prettierignore @@ -7,3 +7,4 @@ package-lock.json yarn.lock package.json docs-dist/ +website/ diff --git a/package.json b/package.json index 414a99d8..de3967bb 100644 --- a/package.json +++ b/package.json @@ -37,7 +37,7 @@ "test-install": "scripts/test-install.sh", "clean-docs": "rimraf new-docs && rimraf docs-api-json", "generate-d-ts": "npm run clean-docs && api-extractor run --local --verbose", - "generate-docs": "npm run generate-d-ts && api-documenter markdown -i docs-api-json -o new-docs && node utils/remove-tag.js", + "generate-docs": "npm run generate-d-ts && api-documenter markdown -i docs-api-json -o website/docs && node utils/remove-tag.js", "ensure-correct-devtools-protocol-revision": "ts-node -s scripts/ensure-correct-devtools-protocol-package", "ensure-pinned-deps": "ts-node -s scripts/ensure-pinned-deps", "test-types-file": "ts-node -s scripts/test-ts-definition-files.ts", diff --git a/utils/remove-tag.js b/utils/remove-tag.js index 8e29117b..8cc59237 100644 --- a/utils/remove-tag.js +++ b/utils/remove-tag.js @@ -15,15 +15,15 @@ * limitations under the License. */ /** - * Docusaurus gets confused by empty HTML comment tags in our Markdown. - These are generated by API Extractor but do not affect the final output, + * Docusaurus gets confused by empty HTML comment tags in our Markdown. + These are generated by API Extractor but do not affect the final output, so to avoid Docusaurus errors we strip them out before generating the website. **/ const glob = require('glob'); const fs = require('fs'); // look for all .md files in the given location. -const files = glob.sync(`./new-docs/*.md`); +const files = glob.sync(`./website/docs/*.md`); for (const file of files) { const content = fs.readFileSync(file, 'utf8'); diff --git a/website/blog/CONTRIBUTING.md b/website/blog/CONTRIBUTING.md new file mode 100644 index 00000000..ba0b0936 --- /dev/null +++ b/website/blog/CONTRIBUTING.md @@ -0,0 +1,3 @@ + + +Head to GitHub to view our CONTRIBUTING.md document diff --git a/website/docs/intro.md b/website/docs/intro.md deleted file mode 100644 index 03b6b8b1..00000000 --- a/website/docs/intro.md +++ /dev/null @@ -1,35 +0,0 @@ ---- -sidebar_position: 1 ---- - -# Tutorial Intro - -Let's discover **Docusaurus in less than 5 minutes**. - -## Getting Started - -Get started by **creating a new site**. - -Or **try Docusaurus immediately** with **[new.docusaurus.io](https://new.docusaurus.io)**. - -## Generate a new site - -Generate a new Docusaurus site using the **classic template**: - -```shell -npx @docusaurus/init@latest init my-website classic -``` - -## Start your site - -Run the development server: - -```shell -cd my-website - -npx docusaurus start -``` - -Your site starts at `http://localhost:3000`. - -Open `docs/intro.md` and edit some lines: the site **reloads automatically** and display your changes. diff --git a/website/docusaurus.config.js b/website/docusaurus.config.js index e95a131f..85cd2470 100644 --- a/website/docusaurus.config.js +++ b/website/docusaurus.config.js @@ -22,30 +22,47 @@ module.exports = { tagline: 'Note: this documentation is WIP. Please use https://pptr.dev.', url: 'https://puppeteer.github.io/', baseUrl: '/puppeteer/', - onBrokenLinks: 'throw', - onBrokenMarkdownLinks: 'warn', + onBrokenLinks: 'ignore', + onBrokenMarkdownLinks: 'ignore', favicon: 'img/favicon.ico', organizationName: 'puppeteer', // Usually your GitHub org/user name. projectName: 'puppeteer', // Usually your repo name. themeConfig: { + hideableSidebar: true, navbar: { + style: "primary", title: 'Puppeteer', logo: { alt: 'My Site Logo', src: 'img/logo.svg', }, + hideOnScroll: true, items: [ { - type: 'doc', - docId: 'intro', + to: 'docs/puppeteer.puppeteer', + label: 'APIs', position: 'left', - label: 'Tutorial', }, { + to: 'blog/contributing', + label: 'Contribute', + position: 'left', + }, + { + label: 'Github', href: 'https://github.com/puppeteer/puppeteer', - label: 'GitHub', position: 'right', }, + { + label: 'Stack', + href: 'https://stackoverflow.com/questions/tagged/puppeteer', + position: 'right' + }, + { + label: 'Version 1.0', + href: 'https://github.com/puppeteer/puppeteer/blob/main/docs/troubleshooting.md', + position: 'right' + } ], }, footer: { @@ -66,6 +83,9 @@ module.exports = { // Please change this to your repo. editUrl: 'https://github.com/facebook/puppeteer/edit/main/website/', }, + theme: { + customCss: require.resolve("./src/css/custom.css"), + }, }, ], ], diff --git a/website/sidebars.js b/website/sidebars.js index b934a5ac..2c65f401 100644 --- a/website/sidebars.js +++ b/website/sidebars.js @@ -1,41 +1,853 @@ -/** - * Copyright 2021 Google Inc. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/** - * Creating a sidebar enables you to: - - create an ordered group of docs - - render a sidebar for each doc of that group - - provide next/previous navigation - - The sidebars can be generated from the filesystem, or explicitly defined here. - - Create as many sidebars as you want. - */ - module.exports = { - // By default, Docusaurus generates a sidebar from the docs folder structure - tutorialSidebar: [{ type: 'autogenerated', dirName: '.' }], - - // But you can create a sidebar manually - /* - tutorialSidebar: [ - { - type: 'category', - label: 'Tutorial', - items: ['hello'], + docs: { + Puppeteer: [ + { + type: 'doc', + id: 'puppeteer.puppeteer', + label: 'Puppeteer', + }, + { + Methods: [ + { + type: 'doc', + id: 'puppeteer.puppeteer.clearcustomqueryhandlers', + label: 'clearcustomqueryhandlers', + }, + { + type: 'doc', + id: 'puppeteer.puppeteer.connect', + label: 'connect', + }, + { + type: 'doc', + id: 'puppeteer.puppeteer.customqueryhandlernames', + label: 'customqueryhandlersnames', + }, + { + type: 'doc', + id: 'puppeteer.puppeteer.devices', + label: 'devices', + }, + { + type: 'doc', + id: 'puppeteer.puppeteer.errors', + label: 'errors', + }, + { + type: 'doc', + id: 'puppeteer.puppeteer.networkconditions', + label: 'networkconditions', + }, + { + type: 'doc', + id: 'puppeteer.puppeteer.registercustomqueryhandler', + label: 'registercustomqueryhandler', + }, + { + type: 'doc', + id: 'puppeteer.puppeteer.unregistercustomqueryhandler', + label: 'unregistercustomqueryhandler', + }, + ] + } + ], + "BrowserFetcher": [ + { + type: 'doc', + id: 'puppeteer.browserfetcher', + label: 'BrowserFetcher', + }, + { + Methods: [ + { + type: 'doc', + id: 'puppeteer.browserfetcher.candownload', + label: 'candownload', + }, + { + type: 'doc', + id: 'puppeteer.browserfetcher.download', + label: 'download', + }, + { + type: 'doc', + id: 'puppeteer.browserfetcher.host', + label: 'host', + }, + { + type: 'doc', + id: 'puppeteer.browserfetcher.localrevisions', + label: 'localrevisions', + }, + { + type: 'doc', + id: 'puppeteer.browserfetcher.platform', + label: 'platform', + }, + { + type: 'doc', + id: 'puppeteer.browserfetcher.product', + label: 'product', + }, + { + type: 'doc', + id: 'puppeteer.browserfetcher.remove', + label: 'remove', + }, + { + type: 'doc', + id: 'puppeteer.browserfetcher.revisioninfo', + label: 'revisioninfo', + }, + ] + }, + ], + "Browser": [ + { + type: 'doc', + id: 'puppeteer.browser', + label: 'Browser', + }, + { + Methods: [ + { + type: 'doc', + id: 'puppeteer.browser.browsercontexts', + label: 'browsercontexts', + }, + { + type: 'doc', + id: 'puppeteer.browser.close', + label: 'close', + }, + { + type: 'doc', + id: 'puppeteer.browser.createincognitobrowsercontext', + label: 'createincognitobrowsercontext', + }, + { + type: 'doc', + id: 'puppeteer.browser.defaultbrowsercontext', + label: 'defaultbrowsercontext', + }, + { + type: 'doc', + id: 'puppeteer.browser.disconnect', + label: 'disconnect', + }, + { + type: 'doc', + id: 'puppeteer.browser.isconnected', + label: 'isconnected', + }, + { + type: 'doc', + id: 'puppeteer.browser.newpage', + label: 'newpage', + }, + { + type: 'doc', + id: 'puppeteer.browser.pages', + label: 'pages', + }, + { + type: 'doc', + id: 'puppeteer.browser.process', + label: 'process', + }, + { + type: 'doc', + id: 'puppeteer.browser.target', + label: 'target', + }, + { + type: 'doc', + id: 'puppeteer.browser.useragent', + label: 'useragent', + }, + { + type: 'doc', + id: 'puppeteer.browser.waitfortarget', + label: 'waitfortarget', + }, + { + type: 'doc', + id: 'puppeteer.browser.wsendpoint', + label: 'wsendpoint', + }, + ] + }, + ], + "BrowserContext": [ + { + type: 'doc', + id: 'puppeteer.browsercontext', + label: 'BrowserContext', + }, + { + Methods: [ + { + type: 'doc', + id: 'puppeteer.browsercontext.browser', + label: 'browser', + }, + { + type: 'doc', + id: 'puppeteer.browsercontext.overridepermissions', + label: 'overridepermissions', + }, + { + type: 'doc', + id: 'puppeteer.browsercontext.close', + label: 'close', + }, + { + type: 'doc', + id: 'puppeteer.browsercontext.isincognito', + label: 'isincognito', + }, + { + type: 'doc', + id: 'puppeteer.browsercontext.newpage', + label: 'newpage', + }, + { + type: 'doc', + id: 'puppeteer.browsercontext.overridepermissions', + label: 'overridepermissions', + }, + { + type: 'doc', + id: 'puppeteer.browsercontext.pages', + label: 'pages', + }, + { + type: 'doc', + id: 'puppeteer.browsercontext.targets', + label: 'targets', + }, + { + type: 'doc', + id: 'puppeteer.browsercontext.waitfortarget', + label: 'waitfortarget', + }, + ] + }, + ], + "Page": [ + { + type: 'doc', + id: 'puppeteer.page', + label: 'Page', + }, + { + Namespaces: [ + { + type: 'doc', + id: 'puppeteer.page.accessibility', + label: 'accessibility', + }, + { + type: 'doc', + id: 'puppeteer.page.coverage', + label: 'coverage', + }, + { + type: 'doc', + id: 'puppeteer.page.isdraginterceptionenabled', + label: 'isDragInterceptionEnabled', + }, + { + type: 'doc', + id: 'puppeteer.page.keyboard', + label: 'keyboard', + }, + { + type: 'doc', + id: 'puppeteer.page.mouse', + label: 'mouse', + }, + { + type: 'doc', + id: 'puppeteer.page.touchscreen', + label: 'touchScreen', + }, + { + type: 'doc', + id: 'puppeteer.page.tracing', + label: 'tracing', + }, + ] + }, + { + Methods: [ + { + type: 'doc', + id: 'puppeteer.page._', + label: '$', + }, + { + type: 'doc', + id: 'puppeteer.page.__', + label: '$$', + }, + { + type: 'doc', + id: 'puppeteer.page.__eval', + label: '$$eval', + }, + { + type: 'doc', + id: 'puppeteer.page._eval', + label: '$eval', + }, + { + type: 'doc', + id: 'puppeteer.page._x', + label: '$x', + }, + { + type: 'doc', + id: 'puppeteer.page.addscripttag', + label: 'addScriptTag', + }, + { + type: 'doc', + id: 'puppeteer.page.addstyletag', + label: 'addStyleTag', + }, + { + type: 'doc', + id: 'puppeteer.page.authenticate', + label: 'authenticate', + }, + { + type: 'doc', + id: 'puppeteer.page.bringtofront', + label: 'bringToFront', + }, + { + type: 'doc', + id: 'puppeteer.page.browser', + label: 'browser', + }, + { + type: 'doc', + id: 'puppeteer.page.browsercontext', + label: 'browserContext', + }, + { + type: 'doc', + id: 'puppeteer.page.click', + label: 'click', + }, + { + type: 'doc', + id: 'puppeteer.page.close', + label: 'close', + }, + { + type: 'doc', + id: 'puppeteer.page.content', + label: 'content()', + }, + { + type: 'doc', + id: 'puppeteer.page.cookies', + label: 'cookies', + }, + { + type: 'doc', + id: 'puppeteer.page.createpdfstream', + label: 'createPDFStream', + }, + { + type: 'doc', + id: 'puppeteer.page.deletecookie', + label: 'deleteCookie', + }, + { + type: 'doc', + id: 'puppeteer.page.emulate', + label: 'emulate', + }, + { + type: 'doc', + id: 'puppeteer.page.emulatecputhrottling', + label: 'emulateCPUThrottling', + }, + { + type: 'doc', + id: 'puppeteer.page.emulateidlestate', + label: 'emulateIdleState', + }, + { + type: 'doc', + id: 'puppeteer.page.emulatemediafeatures', + label: 'emulateMediaFeatures', + }, + { + type: 'doc', + id: 'puppeteer.page.emulatenetworkconditions', + label: 'emulateNetworkConditions', + }, + { + type: 'doc', + id: 'puppeteer.page.emulatetimezone', + label: 'emulateTimeZone', + }, + { + type: 'doc', + id: 'puppeteer.page.emulatevisiondeficiency', + label: 'emulateVisionDefinciency', + }, + { + type: 'doc', + id: 'puppeteer.page.evaluate', + label: 'evaluate', + }, + { + type: 'doc', + id: 'puppeteer.page.evaluatehandle', + label: 'evaluateHandle', + }, + { + type: 'doc', + id: 'puppeteer.page.evaluateonnewdocument', + label: 'evaluateOnNewDocument', + }, + { + type: 'doc', + id: 'puppeteer.page.exposefunction', + label: 'exposeFunction', + }, + { + type: 'doc', + id: 'puppeteer.page.focus', + label: 'focus', + }, + { + type: 'doc', + id: 'puppeteer.page.frames', + label: 'frames', + }, + { + type: 'doc', + id: 'puppeteer.page.goback', + label: 'goBack', + }, + { + type: 'doc', + id: 'puppeteer.page.goforward', + label: 'goForward', + }, + { + type: 'doc', + id: 'puppeteer.page.goto', + label: 'goTo', + }, + { + type: 'doc', + id: 'puppeteer.page.hover', + label: 'hover', + }, + { + type: 'doc', + id: 'puppeteer.page.isclosed', + label: 'isClosed', + }, + { + type: 'doc', + id: 'puppeteer.page.isjavascriptenabled', + label: 'isJavaScriptEnbled', + }, + { + type: 'doc', + id: 'puppeteer.page.mainframe', + label: 'mainFrame', + }, + { + type: 'doc', + id: 'puppeteer.page.metrics', + label: 'metrics', + }, + { + type: 'doc', + id: 'puppeteer.page.pdf', + label: 'PDF', + }, + { + type: 'doc', + id: 'puppeteer.page.queryobjects', + label: 'queryObjects', + }, + { + type: 'doc', + id: 'puppeteer.page.reload', + label: 'reload', + }, + { + type: 'doc', + id: 'puppeteer.page.screenshot', + label: 'screenshot', + }, + { + type: 'doc', + id: 'puppeteer.page.select', + label: 'select', + }, + { + type: 'doc', + id: 'puppeteer.page.setbypasscsp', + label: 'setByPassCSP', + }, + { + type: 'doc', + id: 'puppeteer.page.setcacheenabled', + label: 'setCacheEnaled', + }, + { + type: 'doc', + id: 'puppeteer.page.setcontent', + label: 'setContent', + }, + { + type: 'doc', + id: 'puppeteer.page.setcookie', + label: 'setCookie', + }, + { + type: 'doc', + id: 'puppeteer.page.setdefaultnavigationtimeout', + label: 'setDefaultNavigationTimeOut', + }, + { + type: 'doc', + id: 'puppeteer.page.setdefaulttimeout', + label: 'setDefaultTimeOut', + }, + { + type: 'doc', + id: 'puppeteer.page.setdraginterception', + label: 'setDragInterception', + }, + { + type: 'doc', + id: 'puppeteer.page.setextrahttpheaders', + label: 'setExtraHTTPHeader', + }, + { + type: 'doc', + id: 'puppeteer.page.setgeolocation', + label: 'setGeoLocation', + }, + { + type: 'doc', + id: 'puppeteer.page.setjavascriptenabled', + label: 'setJavaScriptEnabled', + }, + { + type: 'doc', + id: 'puppeteer.page.setofflinemode', + label: 'setOfflineMode', + }, + { + type: 'doc', + id: 'puppeteer.page.setrequestinterception', + label: 'setRequestInterception', + }, + { + type: 'doc', + id: 'puppeteer.page.setuseragent', + label: 'setUserAgent', + }, + { + type: 'doc', + id: 'puppeteer.page.setviewport', + label: 'setViewPort', + }, + { + type: 'doc', + id: 'puppeteer.page.tap', + label: 'tap', + }, + { + type: 'doc', + id: 'puppeteer.page.target', + label: 'target', + }, + { + type: 'doc', + id: 'puppeteer.page.title', + label: 'title', + }, + { + type: 'doc', + id: 'puppeteer.page.type', + label: 'type', + }, + { + type: 'doc', + id: 'puppeteer.page.url', + label: 'url', + }, + { + type: 'doc', + id: 'puppeteer.page.viewport', + label: 'viewport', + }, + { + type: 'doc', + id: 'puppeteer.page.waitfor', + label: 'waitFor', + }, + { + type: 'doc', + id: 'puppeteer.page.waitforfilechooser', + label: 'waitForFileChooser', + }, + { + type: 'doc', + id: 'puppeteer.page.waitforfunction', + label: 'waitForFunction', + }, + { + type: 'doc', + id: 'puppeteer.page.waitfornavigation', + label: 'waitForNavigation', + }, + { + type: 'doc', + id: 'puppeteer.page.waitforrequest', + label: 'waitForRequest', + }, + { + type: 'doc', + id: 'puppeteer.page.waitforresponse', + label: 'waitForResponse', + }, + { + type: 'doc', + id: 'puppeteer.page.waitforselector', + label: 'waitForSelector', + }, + { + type: 'doc', + id: 'puppeteer.page.waitfortimeout', + label: 'waittimeout', + }, + { + type: 'doc', + id: 'puppeteer.page.waitforxpath', + label: 'waitForXPath', + }, + { + type: 'doc', + id: 'puppeteer.page.workers', + label: 'workers', + }, + ] + }, + ], + "WebWorker": [ + { + type: 'doc', + id: 'puppeteer.webworker', + label: 'WebWorker', + }, + { + Methods: [ + { + type: 'doc', + id: 'puppeteer.webworker.evaluate', + label: 'evaluate', + }, + { + type: 'doc', + id: 'puppeteer.webworker.evaluatehandle', + label: 'evaluatehandle', + }, + { + type: 'doc', + id: 'puppeteer.webworker.executioncontext', + label: 'executioncontext', + }, + { + type: 'doc', + id: 'puppeteer.webworker.url', + label: 'url', + }, + ] + }, + ], + "Accessibility": [ + { + type: 'doc', + id: 'puppeteer.accessibility', + label: 'Accessibility', + }, + { + Methods: [ + { + type: 'doc', + id: 'puppeteer.accessibility.snapshot', + label: 'snapshot', + }, + ] + }, + ], + "FileChooser": [ + { + type: 'doc', + id: 'puppeteer.filechooser', + label: 'FileChooser', + }, + { + Methods: [ + { + type: 'doc', + id: 'puppeteer.filechooser.accept', + label: 'accept', + }, + { + type: 'doc', + id: 'puppeteer.filechooser.cancel', + label: 'cancel', + }, + { + type: 'doc', + id: 'puppeteer.filechooser.ismultiple', + label: 'ismultiple', + }, + ] + }, + ], + "ExecutionContext": [ + 'puppeteer.executioncontext', + 'puppeteer.executioncontext.evaluate', + 'puppeteer.executioncontext.evaluatehandle', + 'puppeteer.executioncontext.frame', + 'puppeteer.executioncontext.queryobjects', + ], + "JSHandle": [ + 'puppeteer.jshandle', + 'puppeteer.jshandle.aselement', + 'puppeteer.jshandle.dispose', + 'puppeteer.jshandle.evaluate', + 'puppeteer.jshandle.evaluatehandle', + 'puppeteer.jshandle.executioncontext', + 'puppeteer.jshandle.getproperties', + 'puppeteer.jshandle.getproperty', + 'puppeteer.jshandle.jsonvalue', + ], + "ElementHandle": [ + 'puppeteer.elementhandle', + 'puppeteer.elementhandle._', + 'puppeteer.elementhandle.__', + 'puppeteer.elementhandle.__eval', + 'puppeteer.elementhandle._eval', + 'puppeteer.elementhandle._x', + 'puppeteer.elementhandle.aselement', + 'puppeteer.elementhandle.boundingbox', + 'puppeteer.elementhandle.boxmodel', + 'puppeteer.elementhandle.click', + 'puppeteer.elementhandle.contentframe', + 'puppeteer.elementhandle.focus', + 'puppeteer.elementhandle.hover', + 'puppeteer.elementhandle.clickablepoint', + 'puppeteer.elementhandle.drag', + 'puppeteer.elementhandle.draganddrop', + 'puppeteer.elementhandle.dragenter', + 'puppeteer.elementhandle.dragover', + 'puppeteer.elementhandle.drop', + 'puppeteer.elementhandle.isintersectingviewport', + 'puppeteer.elementhandle.press', + 'puppeteer.elementhandle.screenshot', + 'puppeteer.elementhandle.select', + 'puppeteer.elementhandle.tap', + 'puppeteer.elementhandle.type', + 'puppeteer.elementhandle.uploadfile', + ], + "HTTPRequest": [ + 'puppeteer.httprequest', + 'puppeteer.httprequest.abort', + 'puppeteer.httprequest.continue', + 'puppeteer.httprequest.failure', + 'puppeteer.httprequest.frame', + 'puppeteer.httprequest.headers', + 'puppeteer.httprequest.isnavigationrequest', + 'puppeteer.httprequest.method', + 'puppeteer.httprequest.postdata', + 'puppeteer.httprequest.redirectchain', + 'puppeteer.httprequest.resourcetype', + 'puppeteer.httprequest.respond', + 'puppeteer.httprequest.response', + 'puppeteer.httprequest.url', + ], + "HTTPRespose": [ + 'puppeteer.httpresponse', + 'puppeteer.httpresponse.buffer', + 'puppeteer.httpresponse.frame', + 'puppeteer.httpresponse.fromcache', + 'puppeteer.httpresponse.fromserviceworker', + 'puppeteer.httpresponse.headers', + 'puppeteer.httpresponse.json', + 'puppeteer.httpresponse.ok', + 'puppeteer.httpresponse.remoteaddress', + 'puppeteer.httpresponse.request', + 'puppeteer.httpresponse.securitydetails', + 'puppeteer.httpresponse.status', + 'puppeteer.httpresponse.statustext', + 'puppeteer.httpresponse.text', + 'puppeteer.httpresponse.url', + ], + "SecurityDetails": [ + 'puppeteer.securitydetails', + 'puppeteer.securitydetails.issuer', + 'puppeteer.securitydetails.protocol', + 'puppeteer.securitydetails.subjectalternativenames', + 'puppeteer.securitydetails.subjectname', + 'puppeteer.securitydetails.validfrom', + 'puppeteer.securitydetails.validto', + ], + "Target": [ + 'puppeteer.target', + 'puppeteer.target.browser', + 'puppeteer.target.browsercontext', + 'puppeteer.target.createcdpsession', + 'puppeteer.target.opener', + 'puppeteer.target.page', + 'puppeteer.target.type', + 'puppeteer.target.url', + 'puppeteer.target.worker', + ], + "CDPSession": [ + 'puppeteer.cdpsession', + 'puppeteer.cdpsession.connection', + 'puppeteer.cdpsession.detach', + 'puppeteer.cdpsession.send', + ], + "Coverage": [ + 'puppeteer.coverage', + 'puppeteer.coverage.startcsscoverage', + 'puppeteer.coverage.startjscoverage', + 'puppeteer.coverage.stopcsscoverage', + 'puppeteer.coverage.stopjscoverage', + ], + "TimeOutError": [ + 'puppeteer.timeouterror' + ], + "EventEmitter": [ + 'puppeteer.eventemitter', + 'puppeteer.eventemitter.addlistener', + 'puppeteer.eventemitter.emit', + 'puppeteer.eventemitter.addlistener', + 'puppeteer.eventemitter.listenercount', + 'puppeteer.eventemitter.off', + 'puppeteer.eventemitter.on', + 'puppeteer.eventemitter.once', + 'puppeteer.eventemitter.removelistener', + 'puppeteer.eventemitter.removealllisteners', + ], }, - ], - */ }; diff --git a/website/src/css/custom.css b/website/src/css/custom.css index 62b33e51..ae8906ba 100644 --- a/website/src/css/custom.css +++ b/website/src/css/custom.css @@ -23,7 +23,7 @@ /* You can override the default Infima variables here. */ :root { - --ifm-color-primary: #25c2a0; + --ifm-color-primary: rgb(64 181 164); --ifm-color-primary-dark: rgb(33, 175, 144); --ifm-color-primary-darker: rgb(31, 165, 136); --ifm-color-primary-darkest: rgb(26, 136, 112); diff --git a/website/src/pages/index.js b/website/src/pages/index.js deleted file mode 100644 index 305c6b8f..00000000 --- a/website/src/pages/index.js +++ /dev/null @@ -1,44 +0,0 @@ -/** - * Copyright 2021 Google Inc. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -import React from 'react'; -import clsx from 'clsx'; -import Layout from '@theme/Layout'; -import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; -import styles from './index.module.css'; - -function HomepageHeader() { - const { siteConfig } = useDocusaurusContext(); - return ( -
-
-

{siteConfig.title}

-

{siteConfig.tagline}

-
-
- ); -} - -export default function Home() { - const { siteConfig } = useDocusaurusContext(); - return ( - - - - ); -} diff --git a/website/src/pages/index.md b/website/src/pages/index.md new file mode 100644 index 00000000..e3a2ad65 --- /dev/null +++ b/website/src/pages/index.md @@ -0,0 +1,462 @@ +# Puppeteer + + + +[![Build status](https://github.com/puppeteer/puppeteer/workflows/run-checks/badge.svg)](https://github.com/puppeteer/puppeteer/actions?query=workflow%3Arun-checks) [![npm puppeteer package](https://img.shields.io/npm/v/puppeteer.svg)](https://npmjs.org/package/puppeteer) + + + + + +###### [API](https://github.com/puppeteer/puppeteer/blob/v10.0.0/docs/api.md) | [FAQ](#faq) | [Contributing](https://github.com/puppeteer/puppeteer/blob/main/CONTRIBUTING.md) | [Troubleshooting](https://github.com/puppeteer/puppeteer/blob/main/docs/troubleshooting.md) + +> Puppeteer is a Node library which provides a high-level API to control Chrome or Chromium over the [DevTools Protocol](https://chromedevtools.github.io/devtools-protocol/). Puppeteer runs [headless](https://developers.google.com/web/updates/2017/04/headless-chrome) by default, but can be configured to run full (non-headless) Chrome or Chromium. + + + +###### What can I do? + +Most things that you can do manually in the browser can be done using Puppeteer! Here are a few examples to get you started: + +- Generate screenshots and PDFs of pages. +- Crawl a SPA (Single-Page Application) and generate pre-rendered content (i.e. "SSR" (Server-Side Rendering)). +- Automate form submission, UI testing, keyboard input, etc. +- Create an up-to-date, automated testing environment. Run your tests directly in the latest version of Chrome using the latest JavaScript and browser features. +- Capture a [timeline trace](https://developers.google.com/web/tools/chrome-devtools/evaluate-performance/reference) of your site to help diagnose performance issues. +- Test Chrome Extensions. + + +Give it a spin: https://try-puppeteer.appspot.com/ + + + +## Getting Started + +### Installation + +To use Puppeteer in your project, run: + +```bash +npm i puppeteer +# or "yarn add puppeteer" +``` + +Note: When you install Puppeteer, it downloads a recent version of Chromium (~170MB Mac, ~282MB Linux, ~280MB Win) that is guaranteed to work with the API. To skip the download, or to download a different browser, see [Environment variables](https://github.com/puppeteer/puppeteer/blob/v10.0.0/docs/api.md#environment-variables). + +### puppeteer-core + +Since version 1.7.0 we publish the [`puppeteer-core`](https://www.npmjs.com/package/puppeteer-core) package, +a version of Puppeteer that doesn't download any browser by default. + +```bash +npm i puppeteer-core +# or "yarn add puppeteer-core" +``` + +`puppeteer-core` is intended to be a lightweight version of Puppeteer for launching an existing browser installation or for connecting to a remote one. Be sure that the version of puppeteer-core you install is compatible with the +browser you intend to connect to. + +See [puppeteer vs puppeteer-core](https://github.com/puppeteer/puppeteer/blob/main/docs/api.md#puppeteer-vs-puppeteer-core). + +### Usage + +Puppeteer follows the latest [maintenance LTS](https://github.com/nodejs/Release#release-schedule) version of Node. + +Note: Prior to v1.18.1, Puppeteer required at least Node v6.4.0. Versions from v1.18.1 to v2.1.0 rely on +Node 8.9.0+. Starting from v3.0.0 Puppeteer starts to rely on Node 10.18.1+. All examples below use async/await which is only supported in Node v7.6.0 or greater. + +Puppeteer will be familiar to people using other browser testing frameworks. You create an instance +of `Browser`, open pages, and then manipulate them with [Puppeteer's API](https://github.com/puppeteer/puppeteer/blob/v10.0.0/docs/api.md#). + +**Example** - navigating to https://example.com and saving a screenshot as _example.png_: + +Save file as **example.js** + +```js +const puppeteer = require('puppeteer'); + +(async () => { + const browser = await puppeteer.launch(); + const page = await browser.newPage(); + await page.goto('https://example.com'); + await page.screenshot({ path: 'example.png' }); + + await browser.close(); +})(); +``` + +Execute script on the command line + +```bash +node example.js +``` + +Puppeteer sets an initial page size to 800×600px, which defines the screenshot size. The page size can be customized with [`Page.setViewport()`](https://github.com/puppeteer/puppeteer/blob/v10.0.0/docs/api.md#pagesetviewportviewport). + +**Example** - create a PDF. + +Save file as **hn.js** + +```js +const puppeteer = require('puppeteer'); + +(async () => { + const browser = await puppeteer.launch(); + const page = await browser.newPage(); + await page.goto('https://news.ycombinator.com', { + waitUntil: 'networkidle2', + }); + await page.pdf({ path: 'hn.pdf', format: 'a4' }); + + await browser.close(); +})(); +``` + +Execute script on the command line + +```bash +node hn.js +``` + +See [`Page.pdf()`](https://github.com/puppeteer/puppeteer/blob/v10.0.0/docs/api.md#pagepdfoptions) for more information about creating pdfs. + +**Example** - evaluate script in the context of the page + +Save file as **get-dimensions.js** + +```js +const puppeteer = require('puppeteer'); + +(async () => { + const browser = await puppeteer.launch(); + const page = await browser.newPage(); + await page.goto('https://example.com'); + + // Get the "viewport" of the page, as reported by the page. + const dimensions = await page.evaluate(() => { + return { + width: document.documentElement.clientWidth, + height: document.documentElement.clientHeight, + deviceScaleFactor: window.devicePixelRatio, + }; + }); + + console.log('Dimensions:', dimensions); + + await browser.close(); +})(); +``` + +Execute script on the command line + +```bash +node get-dimensions.js +``` + +See [`Page.evaluate()`](https://github.com/puppeteer/puppeteer/blob/v10.0.0/docs/api.md#pageevaluatepagefunction-args) for more information on `evaluate` and related methods like `evaluateOnNewDocument` and `exposeFunction`. + + + + + +## Default runtime settings + +**1. Uses Headless mode** + +Puppeteer launches Chromium in [headless mode](https://developers.google.com/web/updates/2017/04/headless-chrome). To launch a full version of Chromium, set the [`headless` option](https://github.com/puppeteer/puppeteer/blob/v10.0.0/docs/api.md#puppeteerlaunchoptions) when launching a browser: + +```js +const browser = await puppeteer.launch({ headless: false }); // default is true +``` + +**2. Runs a bundled version of Chromium** + +By default, Puppeteer downloads and uses a specific version of Chromium so its API +is guaranteed to work out of the box. To use Puppeteer with a different version of Chrome or Chromium, +pass in the executable's path when creating a `Browser` instance: + +```js +const browser = await puppeteer.launch({ executablePath: '/path/to/Chrome' }); +``` + +You can also use Puppeteer with Firefox Nightly (experimental support). See [`Puppeteer.launch()`](https://github.com/puppeteer/puppeteer/blob/v10.0.0/docs/api.md#puppeteerlaunchoptions) for more information. + +See [`this article`](https://www.howtogeek.com/202825/what%E2%80%99s-the-difference-between-chromium-and-chrome/) for a description of the differences between Chromium and Chrome. [`This article`](https://chromium.googlesource.com/chromium/src/+/master/docs/chromium_browser_vs_google_chrome.md) describes some differences for Linux users. + +**3. Creates a fresh user profile** + +Puppeteer creates its own browser user profile which it **cleans up on every run**. + + + +## Resources + +- [API Documentation](https://github.com/puppeteer/puppeteer/blob/v10.0.0/docs/api.md) +- [Examples](https://github.com/puppeteer/puppeteer/tree/main/examples/) +- [Community list of Puppeteer resources](https://github.com/transitive-bullshit/awesome-puppeteer) + + + +## Debugging tips + +1. Turn off headless mode - sometimes it's useful to see what the browser is + displaying. Instead of launching in headless mode, launch a full version of + the browser using `headless: false`: + + ```js + const browser = await puppeteer.launch({ headless: false }); + ``` + +2. Slow it down - the `slowMo` option slows down Puppeteer operations by the + specified amount of milliseconds. It's another way to help see what's going on. + + ```js + const browser = await puppeteer.launch({ + headless: false, + slowMo: 250, // slow down by 250ms + }); + ``` + +3. Capture console output - You can listen for the `console` event. + This is also handy when debugging code in `page.evaluate()`: + + ```js + page.on('console', (msg) => console.log('PAGE LOG:', msg.text())); + + await page.evaluate(() => console.log(`url is ${location.href}`)); + ``` + +4. Use debugger in application code browser + + There are two execution context: node.js that is running test code, and the browser + running application code being tested. This lets you debug code in the + application code browser; ie code inside `evaluate()`. + + - Use `{devtools: true}` when launching Puppeteer: + + ```js + const browser = await puppeteer.launch({ devtools: true }); + ``` + + - Change default test timeout: + + jest: `jest.setTimeout(100000);` + + jasmine: `jasmine.DEFAULT_TIMEOUT_INTERVAL = 100000;` + + mocha: `this.timeout(100000);` (don't forget to change test to use [function and not '=>'](https://stackoverflow.com/a/23492442)) + + - Add an evaluate statement with `debugger` inside / add `debugger` to an existing evaluate statement: + + ```js + await page.evaluate(() => { + debugger; + }); + ``` + + The test will now stop executing in the above evaluate statement, and chromium will stop in debug mode. + +5. Use debugger in node.js + + This will let you debug test code. For example, you can step over `await page.click()` in the node.js script and see the click happen in the application code browser. + + Note that you won't be able to run `await page.click()` in + DevTools console due to this [Chromium bug](https://bugs.chromium.org/p/chromium/issues/detail?id=833928). So if + you want to try something out, you have to add it to your test file. + + - Add `debugger;` to your test, eg: + + ```js + debugger; + await page.click('a[target=_blank]'); + ``` + + - Set `headless` to `false` + - Run `node --inspect-brk`, eg `node --inspect-brk node_modules/.bin/jest tests` + - In Chrome open `chrome://inspect/#devices` and click `inspect` + - In the newly opened test browser, type `F8` to resume test execution + - Now your `debugger` will be hit and you can debug in the test browser + +6. Enable verbose logging - internal DevTools protocol traffic + will be logged via the [`debug`](https://github.com/visionmedia/debug) module under the `puppeteer` namespace. + + # Basic verbose logging + env DEBUG="puppeteer:*" node script.js + + # Protocol traffic can be rather noisy. This example filters out all Network domain messages + env DEBUG="puppeteer:*" env DEBUG_COLORS=true node script.js 2>&1 | grep -v '"Network' + +7. Debug your Puppeteer (node) code easily, using [ndb](https://github.com/GoogleChromeLabs/ndb) + +- `npm install -g ndb` (or even better, use [npx](https://github.com/zkat/npx)!) + +- add a `debugger` to your Puppeteer (node) code + +- add `ndb` (or `npx ndb`) before your test command. For example: + + `ndb jest` or `ndb mocha` (or `npx ndb jest` / `npx ndb mocha`) + +- debug your test inside chromium like a boss! + + + + + +## Usage with TypeScript + +We have recently completed a migration to move the Puppeteer source code from JavaScript to TypeScript and as of version 7.0.1 we ship our own built-in type definitions. + +If you are on a version older than 7, we recommend installing the Puppeteer type definitions from the [DefinitelyTyped](https://definitelytyped.org/) repository: + +```bash +npm install --save-dev @types/puppeteer +``` + +The types that you'll see appearing in the Puppeteer source code are based off the great work of those who have contributed to the `@types/puppeteer` package. We really appreciate the hard work those people put in to providing high quality TypeScript definitions for Puppeteer's users. + + + +## Contributing to Puppeteer + +Check out [contributing guide](https://github.com/puppeteer/puppeteer/blob/main/CONTRIBUTING.md) to get an overview of Puppeteer development. + + + +# FAQ + +#### Q: Who maintains Puppeteer? + +The Chrome DevTools team maintains the library, but we'd love your help and expertise on the project! +See [Contributing](https://github.com/puppeteer/puppeteer/blob/main/CONTRIBUTING.md). + +#### Q: What is the status of cross-browser support? + +Official Firefox support is currently experimental. The ongoing collaboration with Mozilla aims to support common end-to-end testing use cases, for which developers expect cross-browser coverage. The Puppeteer team needs input from users to stabilize Firefox support and to bring missing APIs to our attention. + +From Puppeteer v2.1.0 onwards you can specify [`puppeteer.launch({product: 'firefox'})`](https://github.com/puppeteer/puppeteer/blob/v10.0.0/docs/api.md#puppeteerlaunchoptions) to run your Puppeteer scripts in Firefox Nightly, without any additional custom patches. While [an older experiment](https://www.npmjs.com/package/puppeteer-firefox) required a patched version of Firefox, [the current approach](https://wiki.mozilla.org/Remote) works with “stock” Firefox. + +We will continue to collaborate with other browser vendors to bring Puppeteer support to browsers such as Safari. +This effort includes exploration of a standard for executing cross-browser commands (instead of relying on the non-standard DevTools Protocol used by Chrome). + +#### Q: What are Puppeteer’s goals and principles? + +The goals of the project are: + +- Provide a slim, canonical library that highlights the capabilities of the [DevTools Protocol](https://chromedevtools.github.io/devtools-protocol/). +- Provide a reference implementation for similar testing libraries. Eventually, these other frameworks could adopt Puppeteer as their foundational layer. +- Grow the adoption of headless/automated browser testing. +- Help dogfood new DevTools Protocol features...and catch bugs! +- Learn more about the pain points of automated browser testing and help fill those gaps. + +We adapt [Chromium principles](https://www.chromium.org/developers/core-principles) to help us drive product decisions: + +- **Speed**: Puppeteer has almost zero performance overhead over an automated page. +- **Security**: Puppeteer operates off-process with respect to Chromium, making it safe to automate potentially malicious pages. +- **Stability**: Puppeteer should not be flaky and should not leak memory. +- **Simplicity**: Puppeteer provides a high-level API that’s easy to use, understand, and debug. + +#### Q: Is Puppeteer replacing Selenium/WebDriver? + +**No**. Both projects are valuable for very different reasons: + +- Selenium/WebDriver focuses on cross-browser automation; its value proposition is a single standard API that works across all major browsers. +- Puppeteer focuses on Chromium; its value proposition is richer functionality and higher reliability. + +That said, you **can** use Puppeteer to run tests against Chromium, e.g. using the community-driven [jest-puppeteer](https://github.com/smooth-code/jest-puppeteer). While this probably shouldn’t be your only testing solution, it does have a few good points compared to WebDriver: + +- Puppeteer requires zero setup and comes bundled with the Chromium version it works best with, making it [very easy to start with](https://github.com/puppeteer/puppeteer/#getting-started). At the end of the day, it’s better to have a few tests running chromium-only, than no tests at all. +- Puppeteer has event-driven architecture, which removes a lot of potential flakiness. There’s no need for evil “sleep(1000)” calls in puppeteer scripts. +- Puppeteer runs headless by default, which makes it fast to run. Puppeteer v1.5.0 also exposes browser contexts, making it possible to efficiently parallelize test execution. +- Puppeteer shines when it comes to debugging: flip the “headless” bit to false, add “slowMo”, and you’ll see what the browser is doing. You can even open Chrome DevTools to inspect the test environment. + +#### Q: Why doesn’t Puppeteer v.XXX work with Chromium v.YYY? + +We see Puppeteer as an **indivisible entity** with Chromium. Each version of Puppeteer bundles a specific version of Chromium – **the only** version it is guaranteed to work with. + +This is not an artificial constraint: A lot of work on Puppeteer is actually taking place in the Chromium repository. Here’s a typical story: + +- A Puppeteer bug is reported: https://github.com/puppeteer/puppeteer/issues/2709 +- It turned out this is an issue with the DevTools protocol, so we’re fixing it in Chromium: https://chromium-review.googlesource.com/c/chromium/src/+/1102154 +- Once the upstream fix is landed, we roll updated Chromium into Puppeteer: https://github.com/puppeteer/puppeteer/pull/2769 + +However, oftentimes it is desirable to use Puppeteer with the official Google Chrome rather than Chromium. For this to work, you should install a `puppeteer-core` version that corresponds to the Chrome version. + +For example, in order to drive Chrome 71 with puppeteer-core, use `chrome-71` npm tag: + +```bash +npm install puppeteer-core@chrome-71 +``` + +#### Q: Which Chromium version does Puppeteer use? + +Look for the `chromium` entry in [revisions.ts](https://github.com/puppeteer/puppeteer/blob/main/src/revisions.ts). To find the corresponding Chromium commit and version number, search for the revision prefixed by an `r` in [OmahaProxy](https://omahaproxy.appspot.com/)'s "Find Releases" section. + +#### Q: Which Firefox version does Puppeteer use? + +Since Firefox support is experimental, Puppeteer downloads the latest [Firefox Nightly](https://wiki.mozilla.org/Nightly) when the `PUPPETEER_PRODUCT` environment variable is set to `firefox`. That's also why the value of `firefox` in [revisions.ts](https://github.com/puppeteer/puppeteer/blob/main/src/revisions.ts) is `latest` -- Puppeteer isn't tied to a particular Firefox version. + +To fetch Firefox Nightly as part of Puppeteer installation: + +```bash +PUPPETEER_PRODUCT=firefox npm i puppeteer +# or "yarn add puppeteer" +``` + +#### Q: What’s considered a “Navigation”? + +From Puppeteer’s standpoint, **“navigation” is anything that changes a page’s URL**. +Aside from regular navigation where the browser hits the network to fetch a new document from the web server, this includes [anchor navigations](https://www.w3.org/TR/html5/single-page.html#scroll-to-fragid) and [History API](https://developer.mozilla.org/en-US/docs/Web/API/History_API) usage. + +With this definition of “navigation,” **Puppeteer works seamlessly with single-page applications.** + +#### Q: What’s the difference between a “trusted" and "untrusted" input event? + +In browsers, input events could be divided into two big groups: trusted vs. untrusted. + +- **Trusted events**: events generated by users interacting with the page, e.g. using a mouse or keyboard. +- **Untrusted event**: events generated by Web APIs, e.g. `document.createEvent` or `element.click()` methods. + +Websites can distinguish between these two groups: + +- using an [`Event.isTrusted`](https://developer.mozilla.org/en-US/docs/Web/API/Event/isTrusted) event flag +- sniffing for accompanying events. For example, every trusted `'click'` event is preceded by `'mousedown'` and `'mouseup'` events. + +For automation purposes it’s important to generate trusted events. **All input events generated with Puppeteer are trusted and fire proper accompanying events.** If, for some reason, one needs an untrusted event, it’s always possible to hop into a page context with `page.evaluate` and generate a fake event: + +```js +await page.evaluate(() => { + document.querySelector('button[type=submit]').click(); +}); +``` + +#### Q: What features does Puppeteer not support? + +You may find that Puppeteer does not behave as expected when controlling pages that incorporate audio and video. (For example, [video playback/screenshots is likely to fail](https://github.com/puppeteer/puppeteer/issues/291).) There are two reasons for this: + +- Puppeteer is bundled with Chromium — not Chrome — and so by default, it inherits all of [Chromium's media-related limitations](https://www.chromium.org/audio-video). This means that Puppeteer does not support licensed formats such as AAC or H.264. (However, it is possible to force Puppeteer to use a separately-installed version Chrome instead of Chromium via the [`executablePath` option to `puppeteer.launch`](https://github.com/puppeteer/puppeteer/blob/v10.0.0/docs/api.md#puppeteerlaunchoptions). You should only use this configuration if you need an official release of Chrome that supports these media formats.) +- Since Puppeteer (in all configurations) controls a desktop version of Chromium/Chrome, features that are only supported by the mobile version of Chrome are not supported. This means that Puppeteer [does not support HTTP Live Streaming (HLS)](https://caniuse.com/#feat=http-live-streaming). + +#### Q: I am having trouble installing / running Puppeteer in my test environment. Where should I look for help? + +We have a [troubleshooting](https://github.com/puppeteer/puppeteer/blob/main/docs/troubleshooting.md) guide for various operating systems that lists the required dependencies. + +#### Q: How do I try/test a prerelease version of Puppeteer? + +You can check out this repo or install the latest prerelease from npm: + +```bash +npm i --save puppeteer@next +``` + +Please note that prerelease may be unstable and contain bugs. + +#### Q: I have more questions! Where do I ask? + +There are many ways to get help on Puppeteer: + +- [bugtracker](https://github.com/puppeteer/puppeteer/issues) +- [Stack Overflow](https://stackoverflow.com/questions/tagged/puppeteer) + +Make sure to search these channels before posting your question. + + diff --git a/website/src/pages/index.module.css b/website/src/pages/index.module.css deleted file mode 100644 index 0f5c6456..00000000 --- a/website/src/pages/index.module.css +++ /dev/null @@ -1,27 +0,0 @@ -/** - * Copyright 2021 Google Inc. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -.heroBanner { - padding: 4rem 0; - text-align: center; - position: relative; - overflow: hidden; -} - -@media screen and (max-width: 966px) { - .heroBanner { - padding: 2rem; - } -}