├── CONTRIBUTING.md ├── LICENSE ├── README.md └── examples ├── labeling-shadow-dom ├── element-with-aria-label.js ├── element-with-label.js ├── element-without-label.js └── index.html ├── managing-focus-spa ├── app.js ├── index.html └── styles.css ├── modals ├── images │ ├── architecture.jpg │ ├── arrow-up.png │ ├── art.jpg │ ├── bag.png │ ├── card-guide.jpg │ ├── cloud-ad.png │ ├── coffee-cups.jpg │ ├── decoration.jpg │ ├── down-arrow.png │ ├── fashion.jpg │ ├── food.jpg │ ├── landscape.jpg │ ├── music.jpg │ ├── nature.jpg │ ├── photography.jpg │ ├── sport.jpg │ ├── star-off.png │ ├── star-on.png │ ├── travelling.jpg │ ├── vacation.jpg │ ├── wallabies.jpg │ ├── woodfire.jpg │ └── workspace.jpg ├── index.html ├── main.css ├── placeholder.html ├── scripts │ ├── app.js │ ├── modal.js │ ├── number.js │ ├── shopping-bag.js │ └── stars.js └── third_party │ ├── handlebars-grouped-each.js │ └── handlebars-v4.0.5.js └── reordering-content ├── index.html └── styles.css /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # For Contributors 2 | 3 | We'd love your help! This doc covers how to become a contributor and submit code to the project. 4 | 5 | ## Follow the coding style 6 | 7 | The `.eslintrc` defines all. We use [JSDoc](http://usejsdoc.org/) along with [closure annotations](https://developers.google.com/closure/compiler/docs/js-for-compiler). Annotations encouraged for all contributions. 8 | 9 | ## Learn about the architecture 10 | 11 | See [Lighthouse Architecture](./docs/architecture.md), our overview and tour of the codebase. 12 | 13 | ## Sign the Contributor License Agreement 14 | 15 | We'd love to accept your sample apps and patches! Before we can take them, we have to jump a couple of legal hurdles. 16 | 17 | Please fill out either the individual or corporate Contributor License Agreement (CLA). 18 | 19 | * If you are an individual writing original source code and you're sure you own the intellectual property, then you'll need to sign an [individual CLA](https://developers.google.com/open-source/cla/individual). 20 | * If you work for a company that wants to allow you to contribute your work, then you'll need to sign a [corporate CLA](https://developers.google.com/open-source/cla/corporate). 21 | 22 | Follow either of the two links above to access the appropriate CLA and instructions for how to sign and return it. Once we receive it, we'll be able to 23 | accept your pull requests. 24 | 25 | ## Contributing a patch 26 | 27 | If you have a contribution for our [documentation](https://developers.google.com/web/tools/lighthouse/), please submit it in the [WebFundamentals repo](https://github.com/google/WebFundamentals/tree/master/src/content/en/tools/lighthouse). 28 | 29 | 1. Submit an issue describing your proposed change to the repo in question. 30 | 1. The repo owner will respond to your issue promptly. 31 | 1. If your proposed change is accepted, and you haven't already done so, sign a Contributor License Agreement (see details above). 32 | 1. Fork the repo, develop and test your code changes. 33 | 1. Ensure that your code adheres to the existing style in the sample to which you are contributing. 34 | 1. Submit a pull request. 35 | 36 | ## helpText guidelines 37 | 38 | Keep the `helpText` of an audit as short as possible. When a reference doc for the audit exists on 39 | developers.google.com/web, the `helpText` should only explain *why* the user should care 40 | about the audit, not *how* to fix it. 41 | 42 | Do: 43 | 44 | Serve images that are smaller than the user's viewport to save cellular data and 45 | improve load time. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/oversized-images). 46 | 47 | Don't: 48 | 49 | Serve images that are smaller than the user's viewport to save cellular data and 50 | improve load time. Consider using responsive images and client hints. 51 | 52 | If no reference doc exists yet, then you can use the `helpText` as a stopgap for explaining 53 | both why the audit is important and how to fix it. 54 | 55 | # For Maintainers 56 | 57 | ## Release guide 58 | 59 | ```sh 60 | # * Install the latest. This also builds the cli, extension, and viewer * 61 | yarn 62 | yarn install-all 63 | yarn build-all 64 | 65 | # * Bump it * 66 | echo "Bump the versions in extension/app/manifest.json and package.json" 67 | 68 | # * Test err'thing * 69 | echo "Test the CLI." 70 | lighthouse --perf "chrome://version" 71 | yarn smoke 72 | echo "Test the extension" 73 | 74 | echo "Test a fresh local install" 75 | # (starting from lighthouse root...) 76 | # cd ..; mkdir tmp; cd tmp 77 | # npm install ../lighthouse 78 | # npm explore lighthouse -- npm run smoke 79 | # npm explore lighthouse -- npm run smokehouse 80 | # npm explore lighthouse -- npm run chrome # try the manual launcher 81 | # npm explore lighthouse -- npm run fast -- http://example.com 82 | # cd ..; rm -rf ./tmp; 83 | 84 | echo "Test the lighthouse-viewer build" 85 | # Manual test for now: 86 | # Start a server in lighthouse-viewer/dist/ and open the page in a tab. You should see the viewer. 87 | # Drop in a results.json or paste an existing gist url (e.g. https://gist.github.com/ebidel/b9fd478b5f40bf5fab174439dc18f83a). 88 | # Check for errors! 89 | 90 | # * Put up the PR * 91 | echo "Branch and commit the version bump." 92 | echo "Generate a PR and get it merged." 93 | 94 | # * Deploy-time * 95 | cd lighthouse-extension; yarn build; gulp package; cd .. 96 | echo "Upload the package zip to CWS dev dashboard" 97 | 98 | echo "Verify the npm package won't include unncessary files" 99 | yarn global add irish-pub pkgfiles 100 | irish-pub; pkgfiles; 101 | 102 | echo "ship it" 103 | npm publish 104 | yarn deploy-viewer 105 | 106 | echo "Use the GitHub web interface to tag the release" 107 | echo "Generate the release notes, and update the release page" 108 | 109 | # * Tell the world!!! * 110 | echo "Inform various peoples" 111 | ``` 112 | 113 | ### Releasing chrome launcher. 114 | 115 | ```sh 116 | cd chrome-launcher 117 | echo "build the launcher source code" 118 | yarn build 119 | yarn version 120 | echo "version bump will be committed" 121 | npm publish 122 | ``` 123 | 124 | ### Canary release 125 | 126 | ```sh 127 | # Pull latest in a clean non-dev clone. 128 | 129 | yarn install-all 130 | 131 | # Update manifest_canary.json w/ version bumps. 132 | 133 | # branch and commit 134 | git commmit -m "bump extension canary to 2.0.0.X" 135 | 136 | npm version prerelease # this will commit 137 | 138 | 139 | # overwrite extension's manifest w/ manifest_canary. 140 | 141 | yarn build-all 142 | 143 | cd lighthouse-extension/ 144 | gulp package 145 | # upload zip to CWS and publish 146 | 147 | # verify you build-all'd for the typescript compile 148 | # ... 149 | 150 | # publish to canary tag! 151 | npm publish --tag canary 152 | ``` 153 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2017 Google, Inc. 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # A11ycasts 2 | 3 | ## The Show 4 | 5 | [A11ycasts](https://www.youtube.com/playlist?list=PLNYkxOF6rcICWx0C9LVWWVqvHlYJyqw7g) 6 | is a fortnightly YouTube series covering web accessibility. 7 | 8 | ## Examples 9 | 10 | - [Accessible modal dialogs](https://github.com/GoogleChrome/a11ycasts/tree/master/examples/modals) 11 | - [How to label Shadow DOM](https://github.com/GoogleChrome/a11ycasts/tree/master/examples/labeling-shadow-dom) 12 | - [Reordering content](https://github.com/GoogleChrome/a11ycasts/tree/master/examples/reordering-content) 13 | - [Managing focus in a web app](https://github.com/GoogleChrome/a11ycasts/tree/master/examples/managing-focus-spa) 14 | 15 | ## License 16 | 17 | Copyright 2017 Google, Inc. 18 | 19 | Licensed under the [Apache License, Version 2.0](LICENSE) (the "License"); 20 | you may not use this file except in compliance with the License. You may 21 | obtain a copy of the License at 22 | 23 | http://www.apache.org/licenses/LICENSE-2.0 24 | 25 | Unless required by applicable law or agreed to in writing, software 26 | distributed under the License is distributed on an "AS IS" BASIS, 27 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 28 | See the License for the specific language governing permissions and 29 | limitations under the License. 30 | -------------------------------------------------------------------------------- /examples/labeling-shadow-dom/element-with-aria-label.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license Copyright 2017 Google Inc. All Rights Reserved. 3 | * 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 4 | * 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. 5 | */ 6 | 7 | class ElementWithAriaLabel extends HTMLElement { 8 | 9 | static get observedAttributes() { 10 | return ['label']; 11 | } 12 | 13 | constructor() { 14 | super(); 15 | this.attachShadow({ mode: 'open' }); 16 | this.button = document.createElement('button'); 17 | this.button.textContent = "❤️"; 18 | this.shadowRoot.appendChild(this.button); 19 | } 20 | 21 | attributeChangedCallback(name, oldVal, newVal) { 22 | if (name === 'label' && newVal) { 23 | this.button.setAttribute('aria-label', newVal); 24 | } 25 | } 26 | 27 | } 28 | 29 | customElements.define('element-with-aria-label', ElementWithAriaLabel); 30 | -------------------------------------------------------------------------------- /examples/labeling-shadow-dom/element-with-label.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license Copyright 2017 Google Inc. All Rights Reserved. 3 | * 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 4 | * 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. 5 | */ 6 | 7 | class ElementWithLabel extends HTMLElement { 8 | 9 | static get observedAttributes() { 10 | return ['label']; 11 | } 12 | 13 | constructor() { 14 | super(); 15 | this.attachShadow({ mode: 'open' }); 16 | this.label = document.createElement('label'); 17 | this.input = document.createElement('input'); 18 | this.input.id = 'internal-input'; 19 | this.label.htmlFor = this.input.id; 20 | this.shadowRoot.appendChild(this.label); 21 | this.shadowRoot.appendChild(this.input); 22 | } 23 | 24 | attributeChangedCallback(name, oldVal, newVal) { 25 | if (name === 'label' && newVal) { 26 | this.label.textContent = newVal; 27 | } 28 | } 29 | 30 | } 31 | 32 | customElements.define('element-with-label', ElementWithLabel); 33 | -------------------------------------------------------------------------------- /examples/labeling-shadow-dom/element-without-label.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license Copyright 2017 Google Inc. All Rights Reserved. 3 | * 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 4 | * 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. 5 | */ 6 | 7 | class ElementWithoutLabel extends HTMLElement { 8 | 9 | constructor() { 10 | super(); 11 | this.attachShadow({ mode: 'open' }); 12 | this.input = document.createElement('input'); 13 | this.shadowRoot.appendChild(this.input); 14 | } 15 | 16 | } 17 | 18 | customElements.define('element-without-label', ElementWithoutLabel); 19 | -------------------------------------------------------------------------------- /examples/labeling-shadow-dom/index.html: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 9 | 10 | Labeling Shadow DOM 11 | 12 | 13 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 |
26 | 27 |
28 | 29 |
30 | 31 |
32 | 33 |
34 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /examples/managing-focus-spa/app.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license Copyright 2017 Google Inc. All Rights Reserved. 3 | * 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 4 | * 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. 5 | */ 6 | 7 | function addPageForPath(path) { 8 | let template = document.getElementById(`${path}-template`); 9 | let main = document.querySelector('main'); 10 | main.innerHTML = ''; 11 | main.appendChild(template.content.cloneNode(true)); 12 | return main.firstElementChild; 13 | } 14 | 15 | // For any route, check the path and 16 | // load the corresponding template. 17 | // Then focus the primary heading. 18 | page('*', function(ctx) { 19 | let path = ctx.path.split('/')[1]; 20 | let page = addPageForPath(path); 21 | let heading = page.querySelector('h1'); 22 | heading.focus(); 23 | }); 24 | // Start on the home page 25 | page('/zombies'); 26 | // Initialize router 27 | page(); 28 | -------------------------------------------------------------------------------- /examples/managing-focus-spa/index.html: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | Managing Focus SPA 14 | 15 | 16 | 17 | 18 | 19 | 20 |
21 | 27 |
28 |
29 | 30 | 31 | 45 | 59 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | -------------------------------------------------------------------------------- /examples/managing-focus-spa/styles.css: -------------------------------------------------------------------------------- 1 | /** 2 | * @license Copyright 2017 Google Inc. All Rights Reserved. 3 | * 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 4 | * 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. 5 | */ 6 | 7 | *, *::after, *::before { 8 | box-sizing: border-box; 9 | } 10 | 11 | html, body { 12 | height: 100%; 13 | } 14 | 15 | body { 16 | margin: 0; 17 | font-size: 24px; 18 | font-family: Helvetica, Arial, sans-serif; 19 | } 20 | 21 | .wrapper { 22 | height: 100%; 23 | display: flex; 24 | } 25 | 26 | #side-nav-label { 27 | display: block; 28 | border-bottom: 1px solid #e0e0e0; 29 | padding: 15px 0 15px 22px; 30 | font-weight: 700; 31 | } 32 | 33 | nav { 34 | background: white; 35 | min-width: 200px; 36 | border-right: 1px solid #e0e0e0; 37 | } 38 | 39 | nav a { 40 | display: block; 41 | padding: 15px 0 15px 22px; 42 | text-decoration: none; 43 | color: black; 44 | font-size: 16px; 45 | font-weight: 700; 46 | } 47 | 48 | nav a:hover { 49 | background: black; 50 | color: white; 51 | } 52 | 53 | main { 54 | flex: 1; 55 | } 56 | 57 | .contain { 58 | width: 760px; 59 | margin: 0 auto; 60 | } 61 | 62 | header { 63 | height: 256px; 64 | display: flex; 65 | align-items: center; 66 | } 67 | 68 | .zombies header { 69 | background-color: #33691E; 70 | } 71 | 72 | .hipsters header { 73 | background-color: #00796B; 74 | } 75 | 76 | .bacon header { 77 | background-color: #C62828; 78 | } 79 | 80 | h1 { 81 | margin: 0; 82 | color: white; 83 | font-size: 56px; 84 | font-weight: normal; 85 | } 86 | 87 | h1:focus { 88 | outline: none; 89 | } 90 | 91 | section { 92 | margin-top: 56px; 93 | } 94 | -------------------------------------------------------------------------------- /examples/modals/images/architecture.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleChromeLabs/a11ycasts/ce55044bf88b4c1725faddd9b88eace61876b726/examples/modals/images/architecture.jpg -------------------------------------------------------------------------------- /examples/modals/images/arrow-up.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleChromeLabs/a11ycasts/ce55044bf88b4c1725faddd9b88eace61876b726/examples/modals/images/arrow-up.png -------------------------------------------------------------------------------- /examples/modals/images/art.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleChromeLabs/a11ycasts/ce55044bf88b4c1725faddd9b88eace61876b726/examples/modals/images/art.jpg -------------------------------------------------------------------------------- /examples/modals/images/bag.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleChromeLabs/a11ycasts/ce55044bf88b4c1725faddd9b88eace61876b726/examples/modals/images/bag.png -------------------------------------------------------------------------------- /examples/modals/images/card-guide.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleChromeLabs/a11ycasts/ce55044bf88b4c1725faddd9b88eace61876b726/examples/modals/images/card-guide.jpg -------------------------------------------------------------------------------- /examples/modals/images/cloud-ad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleChromeLabs/a11ycasts/ce55044bf88b4c1725faddd9b88eace61876b726/examples/modals/images/cloud-ad.png -------------------------------------------------------------------------------- /examples/modals/images/coffee-cups.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleChromeLabs/a11ycasts/ce55044bf88b4c1725faddd9b88eace61876b726/examples/modals/images/coffee-cups.jpg -------------------------------------------------------------------------------- /examples/modals/images/decoration.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleChromeLabs/a11ycasts/ce55044bf88b4c1725faddd9b88eace61876b726/examples/modals/images/decoration.jpg -------------------------------------------------------------------------------- /examples/modals/images/down-arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleChromeLabs/a11ycasts/ce55044bf88b4c1725faddd9b88eace61876b726/examples/modals/images/down-arrow.png -------------------------------------------------------------------------------- /examples/modals/images/fashion.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleChromeLabs/a11ycasts/ce55044bf88b4c1725faddd9b88eace61876b726/examples/modals/images/fashion.jpg -------------------------------------------------------------------------------- /examples/modals/images/food.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleChromeLabs/a11ycasts/ce55044bf88b4c1725faddd9b88eace61876b726/examples/modals/images/food.jpg -------------------------------------------------------------------------------- /examples/modals/images/landscape.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleChromeLabs/a11ycasts/ce55044bf88b4c1725faddd9b88eace61876b726/examples/modals/images/landscape.jpg -------------------------------------------------------------------------------- /examples/modals/images/music.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleChromeLabs/a11ycasts/ce55044bf88b4c1725faddd9b88eace61876b726/examples/modals/images/music.jpg -------------------------------------------------------------------------------- /examples/modals/images/nature.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleChromeLabs/a11ycasts/ce55044bf88b4c1725faddd9b88eace61876b726/examples/modals/images/nature.jpg -------------------------------------------------------------------------------- /examples/modals/images/photography.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleChromeLabs/a11ycasts/ce55044bf88b4c1725faddd9b88eace61876b726/examples/modals/images/photography.jpg -------------------------------------------------------------------------------- /examples/modals/images/sport.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleChromeLabs/a11ycasts/ce55044bf88b4c1725faddd9b88eace61876b726/examples/modals/images/sport.jpg -------------------------------------------------------------------------------- /examples/modals/images/star-off.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleChromeLabs/a11ycasts/ce55044bf88b4c1725faddd9b88eace61876b726/examples/modals/images/star-off.png -------------------------------------------------------------------------------- /examples/modals/images/star-on.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleChromeLabs/a11ycasts/ce55044bf88b4c1725faddd9b88eace61876b726/examples/modals/images/star-on.png -------------------------------------------------------------------------------- /examples/modals/images/travelling.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleChromeLabs/a11ycasts/ce55044bf88b4c1725faddd9b88eace61876b726/examples/modals/images/travelling.jpg -------------------------------------------------------------------------------- /examples/modals/images/vacation.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleChromeLabs/a11ycasts/ce55044bf88b4c1725faddd9b88eace61876b726/examples/modals/images/vacation.jpg -------------------------------------------------------------------------------- /examples/modals/images/wallabies.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleChromeLabs/a11ycasts/ce55044bf88b4c1725faddd9b88eace61876b726/examples/modals/images/wallabies.jpg -------------------------------------------------------------------------------- /examples/modals/images/woodfire.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleChromeLabs/a11ycasts/ce55044bf88b4c1725faddd9b88eace61876b726/examples/modals/images/woodfire.jpg -------------------------------------------------------------------------------- /examples/modals/images/workspace.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleChromeLabs/a11ycasts/ce55044bf88b4c1725faddd9b88eace61876b726/examples/modals/images/workspace.jpg -------------------------------------------------------------------------------- /examples/modals/index.html: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | Lifestyle 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 42 | 43 | 94 | 95 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 128 |
129 |
130 | 132 |

Today's Posts

133 |
134 |
135 |
136 |
137 | 165 |
166 | 167 | 169 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | -------------------------------------------------------------------------------- /examples/modals/main.css: -------------------------------------------------------------------------------- 1 | /** 2 | * @license Copyright 2016 Google Inc. All Rights Reserved. 3 | * 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 4 | * 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. 5 | */ 6 | 7 | *, *::before, *::after { 8 | box-sizing: border-box; 9 | } 10 | 11 | body { 12 | margin: 0; 13 | background-color: #F0F0F0; 14 | } 15 | 16 | /* Cheap trick to control flash of unstyled content as templates load in */ 17 | body.unresolved { 18 | display: none; 19 | } 20 | 21 | h1, 22 | h2, 23 | h3 { 24 | margin: 0; 25 | } 26 | 27 | h1, 28 | h2, 29 | h3 { 30 | font-family: 'Playfair Display', serif; 31 | font-size: 30px; 32 | font-weight: lighter; 33 | } 34 | 35 | body, 36 | span, 37 | p { 38 | font-family: 'PT Sans', sans-serif; 39 | font-size: 16px; 40 | } 41 | 42 | p { 43 | margin: 0; 44 | line-height: 1.8; 45 | color: #757575; 46 | } 47 | 48 | a { 49 | text-decoration: none; 50 | color: initial; 51 | } 52 | 53 | form { 54 | width: 100%; 55 | display: flex; 56 | flex-direction: column; 57 | align-items: center; 58 | } 59 | 60 | /* Helpers */ 61 | 62 | .offscreen { 63 | position:absolute; 64 | left:-10000px; 65 | top:auto; 66 | width:1px; 67 | height:1px; 68 | overflow:hidden; 69 | } 70 | 71 | /* Buttons & Links */ 72 | 73 | .link, 74 | .btn { 75 | display: inline-block; 76 | text-transform: uppercase; 77 | color: #00838f; 78 | letter-spacing: 3px; 79 | font-size: 14px; 80 | border: none; 81 | background: #FFF; 82 | padding: 16px 16px; 83 | height: 45px; 84 | line-height: 1; 85 | text-align: center; 86 | } 87 | 88 | .link_framed, 89 | .btn_framed { 90 | box-shadow: inset 0px 0px 0px 3px #00838f; 91 | } 92 | 93 | .link_full, 94 | .btn_full { 95 | color: #FFF; 96 | background: #00838f; 97 | } 98 | 99 | .btn-row { 100 | display: flex; 101 | } 102 | 103 | .btn-row_centered { 104 | justify-content: center; 105 | } 106 | 107 | .btn-row_spaced { 108 | justify-content: space-around; 109 | } 110 | 111 | /* Container */ 112 | 113 | .container { 114 | width: 380px; 115 | margin: 0 auto; 116 | } 117 | 118 | /* Nav */ 119 | 120 | nav { 121 | height: 84px; 122 | background: #FFF; 123 | margin-bottom: 40px; 124 | box-shadow: 0 0 30px rgba(0, 0, 0, 0.15); 125 | } 126 | 127 | nav .container { 128 | height: 100%; 129 | display: flex; 130 | justify-content: space-between; 131 | align-items: center; 132 | } 133 | 134 | /* Logo */ 135 | 136 | .logo { 137 | color: #000; 138 | } 139 | 140 | /* Shopping Bag */ 141 | 142 | .shopping-bag { 143 | display: block; 144 | width: 27px; 145 | height: 33px; 146 | background-image: url('images/bag.png'); 147 | background-color: transparent; 148 | border: none; 149 | position: relative; 150 | } 151 | 152 | .shopping-bag::before { 153 | content: attr(data-count); 154 | display: block; 155 | line-height: 1; 156 | padding: 5px; 157 | color: #FFF; 158 | background: #00838f; 159 | width: 25px; 160 | height: 25px; 161 | border-radius: 50%; 162 | font-size: 16px; 163 | position: absolute; 164 | left: -10px; 165 | top: 18px; 166 | text-align: center; 167 | } 168 | 169 | /* Content Wrapper */ 170 | 171 | .content-wrapper { 172 | display: flex; 173 | flex-direction: column; 174 | } 175 | 176 | /* Row */ 177 | 178 | .row { 179 | display: flex; 180 | flex-direction: column; 181 | align-items: center; 182 | } 183 | 184 | /* Tag */ 185 | 186 | .tag { 187 | text-transform: uppercase; 188 | letter-spacing: 3px; 189 | font-size: 14px; 190 | color: #757575; 191 | } 192 | 193 | /* Card */ 194 | 195 | .card { 196 | position: relative; 197 | width: 380px; 198 | height: 480px; 199 | margin: 0 0 43px; 200 | box-shadow: 0 0 30px rgba(0, 0, 0, 0.15); 201 | background-color: #FFF; 202 | } 203 | 204 | .card__tag-wrapper { 205 | position: absolute; 206 | top: -22px; 207 | display: block; 208 | width: 100%; 209 | text-align: center; 210 | } 211 | 212 | .card__tag { 213 | display: inline-block; 214 | padding: 10px 32px; 215 | background-color: #FFF; 216 | } 217 | 218 | .card__img { 219 | display: block; 220 | } 221 | 222 | .card__content-wrapper { 223 | display: flex; 224 | flex-direction: column; 225 | align-items: center; 226 | padding: 0 50px; 227 | } 228 | 229 | .card__title { 230 | font-size: 25px; 231 | margin: 16px 0; 232 | text-align: center; 233 | } 234 | 235 | .card__description { 236 | text-align: center; 237 | margin: 12px 0; 238 | } 239 | 240 | /* Product Card */ 241 | 242 | .product-card { 243 | display: flex; 244 | flex-direction: column; 245 | align-items: center; 246 | justify-content: center; 247 | height: auto; 248 | background-color: #FBFBFB; 249 | } 250 | 251 | .product-card__img { 252 | flex: 0.43; 253 | } 254 | 255 | .product-card__content { 256 | padding: 48px 30px 32px 30px; 257 | flex: 0.57; 258 | } 259 | 260 | .product-card__tag { 261 | margin-bottom: 25px; 262 | } 263 | 264 | .product-card__title { 265 | margin-bottom: 30px; 266 | } 267 | 268 | .product-card__rating { 269 | margin-bottom: 55px; 270 | } 271 | 272 | .product-card__purchase-info { 273 | display: flex; 274 | justify-content: space-between; 275 | padding-bottom: 60px; 276 | margin-bottom: 30px; 277 | border-bottom: 1px solid #E3E3E3; 278 | } 279 | 280 | .product-card__price { 281 | font-size: 30px; 282 | color: #00838f; 283 | vertical-align: middle; 284 | } 285 | 286 | .product-card__shipping { 287 | display: block; 288 | font-size: 17px; 289 | color: #757575; 290 | margin-top: 10px; 291 | } 292 | 293 | .product-card__buy-btn { 294 | width: 240px; 295 | height: 53px; 296 | } 297 | 298 | /* Spinbutton */ 299 | 300 | .spinbutton { 301 | border-radius: 2px; 302 | border: 1px solid #E2E2E2; 303 | margin: 2px; 304 | display: flex; 305 | background: white; 306 | width: 100px; 307 | } 308 | 309 | .spinbutton .number { 310 | padding: 6px 0 6px 6px; 311 | flex: 1; 312 | color: #727272; 313 | border: none; 314 | width: 100%; 315 | } 316 | 317 | .spinbutton .buttons { 318 | display: flex; 319 | flex-direction: column; 320 | width: 20px; 321 | } 322 | 323 | .spinbutton button { 324 | background-color: white; 325 | border: none; 326 | border-left: 1px solid #E2E2E2; 327 | width: 100%; 328 | background-repeat: no-repeat; 329 | background-position: center; 330 | margin: 0; 331 | padding: 0; 332 | flex: 1; 333 | } 334 | 335 | .spinbutton .add { 336 | border-bottom: 1px solid #E2E2E2; 337 | background-image: url(images/arrow-up.png); 338 | } 339 | 340 | .spinbutton .subtract { 341 | background-image: url(images/down-arrow.png); 342 | } 343 | 344 | /* Stars */ 345 | 346 | .stars { 347 | display: flex; 348 | width: 100px; 349 | } 350 | 351 | .star { 352 | width: 20px; 353 | } 354 | 355 | 356 | /* Aside */ 357 | 358 | aside { 359 | display: flex; 360 | flex-direction: column; 361 | align-items: center; 362 | width: 380px; 363 | margin: 0 auto; 364 | } 365 | 366 | aside > *:not(:last-child) { 367 | margin-bottom: 30px; 368 | } 369 | 370 | /* Hot List */ 371 | 372 | .hot-list, 373 | .social-cards, 374 | .subscribe-card { 375 | width: 100%; 376 | } 377 | 378 | .hot-list { 379 | margin: 0 auto; 380 | padding: 25px 20px; 381 | box-shadow: 0 0 30px rgba(0, 0, 0, 0.15); 382 | background-color: #FFF; 383 | } 384 | 385 | .hot-list__header { 386 | margin-bottom: 15px; 387 | } 388 | 389 | /* List Post Wrapper */ 390 | 391 | .list-post-wrapper { 392 | display: flex; 393 | justify-content: space-between; 394 | flex-wrap: wrap; 395 | margin-bottom: 21px; 396 | } 397 | 398 | /* List Post */ 399 | 400 | .list-post { 401 | width: 350px; 402 | padding: 33px 0; 403 | border-bottom: 1px solid #D9D9D9; 404 | } 405 | 406 | .list-post__tag, 407 | .list-post__title, 408 | .list-post__img { 409 | margin-bottom: 13px; 410 | } 411 | 412 | .list-post__title { 413 | font-size: 23px; 414 | } 415 | 416 | .list-post__img { 417 | width: 100%; 418 | } 419 | 420 | /* Social Cards */ 421 | 422 | .social-cards { 423 | display: flex; 424 | flex-wrap: wrap; 425 | justify-content: space-between; 426 | } 427 | 428 | /* Social Card */ 429 | 430 | .social-card { 431 | width: 170px; 432 | height: 130px; 433 | box-shadow: 0 0 30px rgba(0, 0, 0, 0.15); 434 | background: #FFF; 435 | display: inline-block; 436 | margin-bottom: 17px; 437 | } 438 | 439 | .social-card:nth-of-type(1n+3) { 440 | margin-bottom: 0; 441 | } 442 | 443 | /* Subscribe Card */ 444 | 445 | .subscribe-card { 446 | display: flex; 447 | flex-direction: column; 448 | align-items: center; 449 | padding: 25px 20px; 450 | background: #FFF; 451 | box-shadow: 0 0 30px rgba(0, 0, 0, 0.15); 452 | } 453 | 454 | .subscribe-card__heading { 455 | text-align: center; 456 | margin-bottom: 30px; 457 | } 458 | 459 | .subscribe-card__field { 460 | display: block; 461 | width: 100%; 462 | border: none; 463 | border-bottom: 2px solid #D9D9D9; 464 | font-size: 16px; 465 | margin-bottom: 5px; 466 | line-height: 30px; 467 | } 468 | 469 | .subscribe-card__label { 470 | color: #757575; 471 | width: 100%; 472 | margin-bottom: 10px; 473 | } 474 | 475 | .subscribe-card__label:last-of-type { 476 | margin-bottom: 20px; 477 | } 478 | 479 | /* Dialog */ 480 | 481 | .dialog { 482 | display: none; 483 | } 484 | 485 | .dialog.opened { 486 | display: block; 487 | } 488 | 489 | .dialog__window { 490 | display: inline-block; 491 | position: fixed; 492 | top: 50%; 493 | left: 50%; 494 | transform: translate(-50%, -50%); 495 | background: white; 496 | border: 2px solid black; 497 | padding: 18px; 498 | z-index: 101; 499 | } 500 | 501 | .dialog__mask { 502 | position: fixed; 503 | top: 0; 504 | left: 0; 505 | height: 100%; 506 | width: 100%; 507 | background: #000000; 508 | opacity: 0.6; 509 | overflow: hidden; 510 | z-index: 100; 511 | } 512 | 513 | /* Breakpoints */ 514 | 515 | /* Tablet */ 516 | 517 | @media (min-width: 800px) { 518 | 519 | /* Container */ 520 | .container { 521 | width: 780px; 522 | } 523 | 524 | /* Row */ 525 | 526 | .row { 527 | flex-direction: row; 528 | justify-content: center; 529 | align-items: stretch; 530 | margin-bottom: 48px; 531 | } 532 | 533 | .row .card:not(:last-of-type) { 534 | margin-right: 20px; 535 | } 536 | 537 | /* Card */ 538 | 539 | .card { 540 | margin-bottom: 0; 541 | } 542 | 543 | /* Product Card */ 544 | 545 | .product-card { 546 | flex-direction: row; 547 | height: 480px; 548 | width: 780px; 549 | } 550 | 551 | .product-card__content { 552 | padding: 48px 30px 32px 0; 553 | } 554 | 555 | /* Aside */ 556 | 557 | aside { 558 | width: 780px; 559 | } 560 | 561 | /* Hot List, Social Cards, Subscribe Card */ 562 | 563 | .hot-list, 564 | .social-cards, 565 | .subscribe-card { 566 | margin: 0 auto; 567 | } 568 | 569 | /* Social Cards */ 570 | 571 | .social-cards { 572 | flex-wrap: no-wrap; 573 | } 574 | 575 | } 576 | 577 | /* Desktop */ 578 | 579 | @media (min-width: 1190px) { 580 | 581 | /* Container */ 582 | .container { 583 | width: 1170px; 584 | } 585 | 586 | /* Content Wrapper */ 587 | 588 | .content-wrapper { 589 | flex-direction: row; 590 | justify-content: center; 591 | margin-bottom: 50px; 592 | } 593 | 594 | /* Main */ 595 | 596 | main { 597 | margin-right: 35px; 598 | } 599 | 600 | /* Product Card */ 601 | 602 | .product-card { 603 | width: 780px; 604 | } 605 | 606 | /* Aside */ 607 | 608 | aside { 609 | width: 357px; 610 | margin: 0; 611 | } 612 | 613 | /* List Post Wrapper */ 614 | .list-post-wrapper { 615 | display: block; 616 | } 617 | 618 | /* List Post */ 619 | 620 | .list-post { 621 | width: 100%; 622 | } 623 | 624 | /* Social Cards */ 625 | 626 | .social-cards { 627 | flex-wrap: wrap; 628 | } 629 | 630 | } 631 | -------------------------------------------------------------------------------- /examples/modals/placeholder.html: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | Udacity Lifestyle 13 | 14 | 15 | 16 | 27 | 28 | 29 | 30 | 41 |
42 |
43 |

Oh hi!

44 | A wallaby with a baby in its pouch 45 |

46 | This is a placeholder page. If you feel like building out the rest of 47 | the site that is extra credit ;) 48 |

49 | Take me back to the main page! 50 |
51 |
52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /examples/modals/scripts/app.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license Copyright 2017 Google Inc. All Rights Reserved. 3 | * 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 4 | * 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. 5 | */ 6 | 7 | class App { 8 | 9 | constructor() { 10 | this.data = appData; 11 | this.templatize('#tmpl-main-posts', '#top-posts', this.data, 'top_posts'); 12 | this.templatize('#tmpl-product', '#product-of-the-month', this.data, 'product'); 13 | this.templatize('#tmpl-main-posts', '#todays-posts', this.data, 'todays_posts'); 14 | this.templatize('#tmpl-list-posts', '#list-posts', this.data, 'hot_list'); 15 | // Control flash of unstyled content in the page as templates load 16 | // When body has .unresolved it will be display: none; 17 | document.body.classList.remove('unresolved'); 18 | 19 | this.addEventListeners(); 20 | } 21 | 22 | templatize(tmplID, hostID, data, section) { 23 | var tmpl = Handlebars.compile(document.querySelector(tmplID).innerHTML); 24 | var context = data[section]; 25 | context.section = section; 26 | var html = tmpl(context); 27 | document.querySelector(hostID).innerHTML = html; 28 | } 29 | 30 | addEventListeners() { 31 | document.querySelector('.product-card__buy-btn') 32 | .addEventListener('click', openDialog); 33 | } 34 | 35 | } 36 | 37 | var appData = { 38 | "top_posts": { 39 | "posts": [{ 40 | "tag": "Food", 41 | "img": "images/food.jpg", 42 | "img_alt": "A pile of citrus fruits, mostly limes and lemons", 43 | "title": "Eat Fresh Every Day", 44 | "description": "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore." 45 | }, { 46 | "tag": "Nature", 47 | "img": "images/nature.jpg", 48 | "img_alt": "A man diving into a pool of water with his arms outstretched", 49 | "title": "Dive Into The Water", 50 | "description": "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore." 51 | }] 52 | }, 53 | "product": { 54 | "img": "images/coffee-cups.jpg", 55 | "img_alt": "A square wooden frame holding an antinque coffee pot. Three antique coffee cups hang off of the frame.", 56 | "title": "Vintage Coffee Cups", 57 | "price": "$123.15" 58 | }, 59 | "todays_posts": { 60 | "posts": [{ 61 | "tag": "Architecture", 62 | "img": "images/architecture.jpg", 63 | "img_alt": "Several intersecting glass and steel structures shot against a blue sky", 64 | "title": "Wonderful Buildings", 65 | "description": "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore." 66 | }, { 67 | "tag": "Decoration", 68 | "img": "images/decoration.jpg", 69 | "img_alt": "A wall with wood planks containing old framed images, a sports coat, and boat oar", 70 | "title": "Warm Moments", 71 | "description": "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore." 72 | }, { 73 | "tag": "Traveling", 74 | "img": "images/travelling.jpg", 75 | "img_alt": "A photograph of the back of a train as it rounds a mountain", 76 | "title": "Explore the World", 77 | "description": "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore." 78 | }, { 79 | "tag": "Photography", 80 | "img": "images/photography.jpg", 81 | "img_alt": "A hand holding a camera lens with a large lake and mountain scene viewed through the lens", 82 | "title": "Capture The Scene", 83 | "description": "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore." 84 | }, { 85 | "tag": "Music", 86 | "img": "images/music.jpg", 87 | "img_alt": "A band playing in a dark auditorium with bright yellow lights lighting the stage", 88 | "title": "Hear It All", 89 | "description": "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore." 90 | }, { 91 | "tag": "Art", 92 | "img": "images/art.jpg", 93 | "img_alt": "A hand holding a paint brush hovering over several multi-colored paint pots", 94 | "title": "Emerging Artists", 95 | "description": "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore." 96 | }, { 97 | "tag": "Sport", 98 | "img": "images/sport.jpg", 99 | "img_alt": "A man surfing on a large wave", 100 | "title": "Benefits Of Sports", 101 | "description": "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore." 102 | }, { 103 | "tag": "Fashion", 104 | "img": "images/fashion.jpg", 105 | "img_alt": "The back of a person's head. The person is wearing a colorful baseball cap.", 106 | "title": "Summer Wear", 107 | "description": "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore." 108 | }] 109 | }, 110 | "hot_list": { 111 | "posts": [{ 112 | "tag": "Nature", 113 | "title": "Lovely Landscapes Captured By Great Photographs", 114 | "img": "images/landscape.jpg", 115 | "img_alt": "A desert mountain landscape photographed from a great distance away", 116 | "description": "Lorem ipsum dolor sit amet, consectetur elit, sed do eiusmod tempor incididunt ut labore et dolore. Ut enim ad ipsum minim." 117 | }, { 118 | "tag": "Decoration", 119 | "title": "Inspiring Minimal Workspaces", 120 | "img": "images/workspace.jpg", 121 | "img_alt": "A dark wood desk with a laptop, camera, tablet, and phone spread around the center.", 122 | "description": "Lorem ipsum dolor sit amet, consectetur elit, sed do eiusmod tempor incididunt ut labore et dolore. Ut enim ad ipsum minim." 123 | }, { 124 | "tag": "Food", 125 | "title": "Outdoor Cuisine Made For The Open Flame", 126 | "img": "images/woodfire.jpg", 127 | "img_alt": "A pan sitting in a campfire cooking bright green vegetables and orange sausage", 128 | "description": "Lorem ipsum dolor sit amet, consectetur elit, sed do eiusmod tempor incididunt ut labore et dolore. Ut enim ad ipsum minim." 129 | }, { 130 | "tag": "Traveling", 131 | "title": "A Little House In The Country", 132 | "img": "images/vacation.jpg", 133 | "img_alt": "A cottage with a large reflecting pool in front of it. In the background is a canyon and mountainside.", 134 | "description": "Lorem ipsum dolor sit amet, consectetur elit, sed do eiusmod tempor incididunt ut labore et dolore. Ut enim ad ipsum minim." 135 | }] 136 | } 137 | }; 138 | 139 | window.App = new App(); 140 | -------------------------------------------------------------------------------- /examples/modals/scripts/modal.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license Copyright 2017 Google Inc. All Rights Reserved. 3 | * 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 4 | * 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. 5 | */ 6 | 7 | const KEYCODE = { 8 | ESC: 27 9 | }; 10 | 11 | const dialog = document.querySelector('.dialog'); 12 | const dialogMask = dialog.querySelector('.dialog__mask'); 13 | const dialogWindow = dialog.querySelector('.dialog__window'); 14 | let previousActiveElement; 15 | 16 | function openDialog() { 17 | // Grab a refernce to the previous activeElement. 18 | // We'll want to restore this when we close the dialog. 19 | previousActiveElement = document.activeElement; 20 | 21 | // Quick and dirty way to make all of the siblings of our 22 | // dialog inert. 23 | Array.from(document.body.children).forEach(child => { 24 | if (child !== dialog) 25 | child.inert = true; 26 | }); 27 | 28 | // Make the dialog visible. 29 | dialog.classList.add('opened'); 30 | 31 | // Listen for things that should close the dialog. 32 | dialogMask.addEventListener('click', closeDialog); 33 | dialogWindow.querySelectorAll('button').forEach(btn => { 34 | btn.addEventListener('click', closeDialog); 35 | }); 36 | document.addEventListener('keydown', checkCloseDialog); 37 | 38 | // Finally, move focus into the dialog. 39 | dialog.querySelector('button').focus(); 40 | } 41 | 42 | function checkCloseDialog(e) { 43 | if (e.keyCode === KEYCODE.ESC) 44 | closeDialog(); 45 | } 46 | 47 | function closeDialog() { 48 | // Clean up any event listeners. 49 | dialogMask.removeEventListener('click', closeDialog); 50 | dialogWindow.querySelectorAll('button').forEach(btn => { 51 | btn.removeEventListener('click', closeDialog); 52 | }); 53 | document.removeEventListener('keydown', checkCloseDialog); 54 | 55 | // Uninert our siblings. 56 | Array.from(document.body.children).forEach(child => { 57 | if (child !== dialog) 58 | child.inert = false; 59 | }); 60 | 61 | // Hide the dialog. 62 | dialog.classList.remove('opened'); 63 | 64 | // Restore focus to the previous active element. 65 | previousActiveElement.focus(); 66 | } 67 | -------------------------------------------------------------------------------- /examples/modals/scripts/number.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license Copyright 2017 Google Inc. All Rights Reserved. 3 | * 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 4 | * 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. 5 | */ 6 | 7 | var VK_UP = 38; 8 | var VK_DOWN = 40; 9 | 10 | var announcer = document.querySelector('#announcer'); 11 | 12 | function announce(text) { 13 | announcer.textContent = text; 14 | } 15 | 16 | function SpinButton(spinbutton) { 17 | this._el = spinbutton; 18 | this._minValue = parseInt(spinbutton.getAttribute('aria-valuemin'), 10); 19 | this._maxValue = parseInt(spinbutton.getAttribute('aria-valuemax'), 10); 20 | this._number = spinbutton.querySelector('.number'); 21 | this._number.value = 0; 22 | this._add = spinbutton.querySelector('.add'); 23 | this._subtract = spinbutton.querySelector('.subtract'); 24 | 25 | this._add.addEventListener('click', this.increment.bind(this)); 26 | this._subtract.addEventListener('click', this.decrement.bind(this)); 27 | this._el.addEventListener('keydown', this.onKeydown.bind(this)); 28 | } 29 | 30 | SpinButton.prototype = { 31 | updateValue: function(increment) { 32 | var oldValue = this._number.textContent; 33 | var newValue = parseInt(oldValue, 10) + increment; 34 | if (newValue < this._minValue || newValue > this._maxValue) 35 | return; 36 | this._number.textContent = newValue; 37 | this._el.setAttribute('aria-valuenow', newValue); 38 | announce(newValue); 39 | }, 40 | 41 | increment: function() { 42 | this.updateValue(1); 43 | }, 44 | 45 | decrement: function() { 46 | this.updateValue(-1); 47 | }, 48 | 49 | onKeydown: function(e) { 50 | switch (e.keyCode) { 51 | case VK_UP: 52 | this.increment(); 53 | e.stopPropagation(); 54 | e.preventDefault(); 55 | break; 56 | case VK_DOWN: 57 | this.decrement(); 58 | e.stopPropagation(); 59 | e.preventDefault(); 60 | break; 61 | } 62 | } 63 | } 64 | 65 | 66 | var spinbuttons = document.querySelectorAll('.spinbutton'); 67 | for (var i = 0; i < spinbuttons.length; i++) { 68 | new SpinButton(spinbuttons[i]); 69 | } 70 | -------------------------------------------------------------------------------- /examples/modals/scripts/shopping-bag.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license Copyright 2017 Google Inc. All Rights Reserved. 3 | * 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 4 | * 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. 5 | */ 6 | 7 | function ShoppingBag(bag, count) { 8 | this._el = bag; 9 | this._el.setAttribute('data-count', count); 10 | var itemPhrase = count === 1 ? `${count} item` : `${count} items`; 11 | this._el.setAttribute('aria-label', `${itemPhrase} in cart`); 12 | } 13 | 14 | var shoppingBags = document.querySelectorAll('.shopping-bag'); 15 | for (var i = 0; i < shoppingBags.length; i++) { 16 | // We'll fake the number of items that the user has by passing in 2 17 | // In a real app this would probably come from some user data in the backend 18 | new ShoppingBag(shoppingBags[i], 2); 19 | } 20 | -------------------------------------------------------------------------------- /examples/modals/scripts/stars.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license Copyright 2017 Google Inc. All Rights Reserved. 3 | * 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 4 | * 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. 5 | */ 6 | 7 | var VK_PGUP = 33; 8 | var VK_PGDN = 34; 9 | var VK_END = 35; 10 | var VK_HOME = 36; 11 | var VK_LEFT = 37; 12 | var VK_UP = 38; 13 | var VK_RIGHT = 39; 14 | var VK_DOWN = 40; 15 | 16 | function Stars(stars) { 17 | this._el = stars; 18 | this._stars = Array.prototype.slice.call(stars.querySelectorAll('.star')); 19 | for (var star of this._stars) { 20 | star.addEventListener('mouseenter', this.hoverStart.bind(this)); 21 | star.addEventListener('mouseleave', this.hoverEnd.bind(this)); 22 | star.addEventListener('click', this.select.bind(this)); 23 | } 24 | 25 | this._minValue = 0; 26 | this._el.setAttribute('aria-valuemin', this._minValue); 27 | this._maxValue = this._stars.length; 28 | this._el.setAttribute('aria-valuemax', this._maxValue); 29 | 30 | this.setValue(0); 31 | 32 | this._el.setAttribute('role', 'slider'); 33 | this._el.tabIndex = 0; 34 | 35 | this._el.addEventListener('keydown', this.onKeydown.bind(this)); 36 | } 37 | 38 | Stars.prototype = { 39 | showStars: function(numStarsOn) { 40 | this._stars.forEach(function(star, i) { 41 | if (i < numStarsOn) 42 | star.querySelector('img').src = 'images/star-on.png'; 43 | else 44 | star.querySelector('img').src = 'images/star-off.png'; 45 | }); 46 | }, 47 | 48 | set currentValue(value) { 49 | this._el.setAttribute('aria-valuenow', value); 50 | var englishValue = (value === 0 ? 'No' : value) + ' star' + (value !== 1 ? 's' : ''); 51 | this._el.setAttribute('aria-valuetext', englishValue); 52 | }, 53 | 54 | get currentValue() { 55 | return parseInt(this._el.getAttribute('aria-valuenow')); 56 | }, 57 | 58 | setValue: function(value) { 59 | this.currentValue = value; 60 | this.showStars(value); 61 | }, 62 | 63 | increment: function() { 64 | var currentValue = this.currentValue; 65 | if (currentValue === this._maxValue) 66 | return; 67 | this.currentValue = currentValue + 1; 68 | }, 69 | 70 | decrement: function() { 71 | var currentValue = this.currentValue; 72 | if (currentValue === this._minValue) 73 | return; 74 | this.currentValue = currentValue - 1; 75 | }, 76 | 77 | select: function(e) { 78 | var star = e.currentTarget; 79 | var index = this._stars.indexOf(star); 80 | this.setValue(index + 1); 81 | }, 82 | 83 | hoverStart: function(e) { 84 | var star = e.currentTarget; 85 | var index = this._stars.indexOf(star); 86 | this.showStars(index + 1); 87 | }, 88 | 89 | hoverEnd: function(e) { 90 | this.showStars(this.currentValue); 91 | }, 92 | 93 | onKeydown: function(e) { 94 | switch (e.keyCode) { 95 | case VK_UP: 96 | case VK_RIGHT: 97 | this.increment(); 98 | this.showStars(this.currentValue); 99 | e.stopPropagation(); 100 | e.preventDefault(); 101 | break; 102 | case VK_DOWN: 103 | case VK_LEFT: 104 | this.decrement(); 105 | this.showStars(this.currentValue); 106 | e.stopPropagation(); 107 | e.preventDefault(); 108 | break; 109 | case VK_HOME: 110 | case VK_PGUP: 111 | this.setValue(this._minValue); 112 | this.showStars(this.currentValue); 113 | e.stopPropagation(); 114 | e.preventDefault(); 115 | break; 116 | case VK_END: 117 | case VK_PGDN: 118 | this.setValue(this._maxValue); 119 | this.showStars(this.currentValue); 120 | e.stopPropagation(); 121 | e.preventDefault(); 122 | break; 123 | } 124 | } 125 | } 126 | 127 | 128 | var starWidgets = document.querySelectorAll('.stars'); 129 | for (var i = 0; i < starWidgets.length; i++) { 130 | new Stars(starWidgets[i]); 131 | } 132 | -------------------------------------------------------------------------------- /examples/modals/third_party/handlebars-grouped-each.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license Copyright 2017 Google Inc. All Rights Reserved. 3 | * 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 4 | * 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. 5 | */ 6 | 7 | // Credit to https://funkjedi.com/technology/412-every-nth-item-in-handlebars/ 8 | Handlebars.registerHelper('grouped_each', function(every, context, options) { 9 | var out = "", 10 | subcontext = [], 11 | i; 12 | if (context && context.length > 0) { 13 | for (i = 0; i < context.length; i++) { 14 | context[i].outer_index = i; 15 | if (i > 0 && i % every === 0) { 16 | out += options.fn(subcontext); 17 | subcontext = []; 18 | } 19 | subcontext.push(context[i]); 20 | } 21 | out += options.fn(subcontext); 22 | } 23 | return out; 24 | }); 25 | -------------------------------------------------------------------------------- /examples/reordering-content/index.html: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | Join our newsletter 18 | 21 |
22 |
23 |
24 |
25 | 26 | 27 | 28 |
29 |
30 | 31 | 32 | 33 |
34 |
35 | 36 | 37 | 38 |
39 |
40 |
41 | 42 | 43 | -------------------------------------------------------------------------------- /examples/reordering-content/styles.css: -------------------------------------------------------------------------------- 1 | /** 2 | * @license Copyright 2017 Google Inc. All Rights Reserved. 3 | * 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 4 | * 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. 5 | */ 6 | 7 | *, *::after, *::before { 8 | box-sizing: border-box; 9 | } 10 | 11 | html, body { 12 | height: 100%; 13 | } 14 | 15 | body { 16 | margin: 0; 17 | display: flex; 18 | align-items: center; 19 | justify-content: center; 20 | } 21 | 22 | .card { 23 | display: inline-block; 24 | background: white; 25 | border: 5px solid black; 26 | padding: 18px; 27 | } 28 | 29 | .line { 30 | display: flex; 31 | margin: 10px; 32 | } 33 | 34 | .line > * { 35 | margin: 4px; 36 | } 37 | 38 | .line-2 > button { 39 | order: 1; 40 | } 41 | 42 | .line-2 > input { 43 | order: 2; 44 | } 45 | 46 | .line-2 > label { 47 | order: 3; 48 | } 49 | 50 | .line-3 > label { 51 | order: 1; 52 | } 53 | 54 | .line-3 > button { 55 | order: 2; 56 | } 57 | 58 | .line-3 > input { 59 | order: 3; 60 | } 61 | 62 | .newsletter { 63 | position: absolute; 64 | bottom: 0; 65 | background: black; 66 | padding: 8px; 67 | color: white; 68 | width: 100%; 69 | } 70 | --------------------------------------------------------------------------------