├── 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 |
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 |
32 |
33 |
34 |
Zombie Ipsum
35 |
36 |
37 |
38 |
Zombie ipsum reversus ab viral inferno, nam rick grimes malum cerebro. De carne lumbering animata corpora quaeritis. Summus brains sit, morbo vel maleficia? De apocalypsi gorger omero undead survivor dictum mauris. Hi mindless mortuis soulless creaturas, imo evil stalking monstra adventus resi dentevil vultus comedat cerebella viventium. Qui animated corpse, cricket bat max brucks terribilem incessu zomby. The voodoo sacerdos flesh eater, suscitat mortuos comedere carnem virus. Zonbi tattered for solum oculi eorum defunctis go lum cerebro. Nescio brains an Undead zombies. Sicut malus putrid voodoo horror. Nigh tofth eliv ingdead.
39 |
Cum horribilem walking dead resurgere de crazed sepulcris creaturis, zombie sicut de grave feeding iride et serpens. Pestilentia, shaun ofthe dead scythe animated corpses ipsa screams. Pestilentia est plague haec decaying ambulabat mortuos. Sicut zeder apathetic malus voodoo. Aenean a dolor plan et terror soulless vulnerum contagium accedunt, mortui iam vivam unlife. Qui tardius moveri, brid eof reanimator sed in magna copia sint terribiles undeath legionis. Alii missing oculis aliorum sicut serpere crabs nostram. Putridi braindead odores kill and infect, aere implent left four dead.
40 |
Lucio fulci tremor est dark vivos magna. Expansis creepy arm yof darkness ulnis witchcraft missing carnem armis Kirkman Moore and Adlard caeruleum in locis. Romero morbo Congress amarus in auras. Nihil horum sagittis tincidunt, zombie slack-jawed gelida survival portenta. The unleashed virus est, et iam zombie mortui ambulabunt super terram. Souless mortuum glassy-eyed oculos attonitos indifferent back zom bieapoc alypse. An hoc dead snow braaaiiiins sociopathic incipere Clairvius Narcisse, an ante? Is bello mundi z?
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
Hipster Ipsum
49 |
50 |
51 |
52 |
Butcher aesthetic photo booth coloring book lumbersexual squid banjo kogi shaman hella jean shorts. Photo booth prism polaroid, seitan PBR&B iceland irony you probably haven't heard of them. Mustache cold-pressed disrupt flexitarian lyft XOXO. Chillwave humblebrag authentic green juice XOXO la croix copper mug cardigan. Listicle salvia gentrify poutine, selfies messenger bag art party semiotics. Stumptown cloud bread sriracha banjo leggings hell of. Before they sold out cornhole intelligentsia pop-up narwhal mumblecore vice pabst tousled celiac franzen cold-pressed shabby chic ethical brooklyn. Sriracha humblebrag migas everyday carry.
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 |
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 |
--------------------------------------------------------------------------------