├── lesson1-overview ├── README.md └── 06-experiencing-screen-reader │ ├── koala.jpg │ ├── basic.html │ ├── main.css │ └── modal.js ├── lesson5-semantics-aria ├── README.md ├── 21-dialog │ ├── uair-2000.jpg │ ├── solution │ │ ├── uair-2000.jpg │ │ ├── modal.js │ │ └── main.css │ ├── modal.js │ └── main.css ├── 06-radio-group │ ├── main.css │ ├── solution │ │ ├── main.css │ │ ├── radiogroup.html │ │ └── radiogroup.js │ ├── radiogroup.html │ └── radiogroup.js ├── 19-aria-live │ ├── main.css │ ├── number.js │ └── number.html ├── 03-first-steps │ ├── checkboxes.html │ ├── solution │ │ ├── checkboxes.html │ │ ├── main.css │ │ └── checkboxes.js │ ├── main.css │ └── checkboxes.js ├── 02-why-aria │ ├── checkboxes.html │ ├── main.css │ └── checkboxes.js └── 13-combobox │ ├── combobox.html │ ├── solution │ ├── combobox.html │ └── combobox.css │ └── combobox.css ├── lesson3-semantics-built-in ├── README.md ├── 05-writing-semantic-html │ ├── wombat.jpg │ ├── wombats.js │ ├── main.css │ └── wombats.html ├── 17-text-alternatives │ ├── images │ │ ├── bee.jpg │ │ ├── dog.jpg │ │ ├── goat.jpg │ │ ├── logo.png │ │ ├── coffee.jpg │ │ ├── football.jpg │ │ ├── telephone.jpg │ │ ├── 160204193356-01-cat-500.jpg │ │ └── search.svg │ ├── main.css │ └── funion.html ├── 02-chromevox-lite │ ├── embed.css │ ├── chromevox-no-background.svg │ └── chromevox-no-background-off.svg ├── 16-labelling-input-elements │ ├── label.js │ └── solution │ │ └── label.js └── 03-experience-screen-reader │ └── contact.html ├── lesson4-semantics-navigating ├── README.md ├── 08-link-text │ ├── logo.png │ ├── sandwich.jpg │ ├── cappuccino.jpg │ ├── solution │ │ ├── logo.png │ │ ├── sandwich.jpg │ │ ├── cappuccino.jpg │ │ ├── main.css │ │ └── index.html │ ├── main.css │ └── index.html └── 10-landmarks │ └── blog.css ├── CODEOWNERS ├── .gitignore ├── lesson2-focus ├── 01-basic-form │ ├── uair-2000.jpg │ ├── README.md │ └── main.css ├── 02-dom-order │ ├── images │ │ ├── bee.jpg │ │ ├── cat.jpg │ │ ├── dog.jpg │ │ ├── goat.jpg │ │ ├── coffee.jpg │ │ ├── football.jpg │ │ ├── telephone.jpg │ │ └── search.svg │ ├── solution │ │ ├── images │ │ │ ├── bee.jpg │ │ │ ├── cat.jpg │ │ │ ├── dog.jpg │ │ │ ├── goat.jpg │ │ │ ├── coffee.jpg │ │ │ ├── football.jpg │ │ │ ├── telephone.jpg │ │ │ └── search.svg │ │ ├── main.css │ │ └── index.html │ ├── README.md │ ├── main.css │ └── index.html ├── 06-skip-links │ ├── images │ │ ├── bee.jpg │ │ ├── cat.jpg │ │ ├── dog.jpg │ │ ├── goat.jpg │ │ ├── coffee.jpg │ │ ├── football.jpg │ │ ├── telephone.jpg │ │ └── search.svg │ ├── README.md │ ├── main.css │ └── index.html ├── 03-managing-focus │ ├── images │ │ └── vegemite.jpg │ ├── solution │ │ ├── images │ │ │ └── vegemite.jpg │ │ └── scripts │ │ │ └── main.js │ ├── README.md │ └── scripts │ │ └── main.js ├── 04-offscreen-content │ ├── README.md │ ├── solution │ │ └── blog.css │ └── blog.css ├── 07-modals-and-keyboard-traps │ ├── README.md │ ├── main.css │ ├── solution │ │ ├── main.css │ │ ├── index.html │ │ └── modal.js │ ├── index.html │ └── modal.js └── 05-radio-group │ ├── main.css │ ├── solution │ ├── main.css │ ├── index.html │ └── radiogroup.js │ ├── README.md │ ├── index.html │ └── radiogroup.js ├── final-project ├── ecommerce-site │ ├── images │ │ ├── art.jpg │ │ ├── bag.png │ │ ├── food.jpg │ │ ├── fashion.jpg │ │ ├── music.jpg │ │ ├── nature.jpg │ │ ├── sport.jpg │ │ ├── star-on.png │ │ ├── arrow-up.png │ │ ├── landscape.jpg │ │ ├── star-off.png │ │ ├── vacation.jpg │ │ ├── woodfire.jpg │ │ ├── workspace.jpg │ │ ├── architecture.jpg │ │ ├── card-guide.jpg │ │ ├── coffee-cups.jpg │ │ ├── decoration.jpg │ │ ├── down-arrow.png │ │ ├── photography.jpg │ │ └── travelling.jpg │ ├── solution │ │ ├── images │ │ │ ├── art.jpg │ │ │ ├── bag.png │ │ │ ├── food.jpg │ │ │ ├── music.jpg │ │ │ ├── sport.jpg │ │ │ ├── arrow-up.png │ │ │ ├── fashion.jpg │ │ │ ├── nature.jpg │ │ │ ├── star-off.png │ │ │ ├── star-on.png │ │ │ ├── vacation.jpg │ │ │ ├── woodfire.jpg │ │ │ ├── card-guide.jpg │ │ │ ├── decoration.jpg │ │ │ ├── down-arrow.png │ │ │ ├── landscape.jpg │ │ │ ├── travelling.jpg │ │ │ ├── wallabies.jpg │ │ │ ├── workspace.jpg │ │ │ ├── architecture.jpg │ │ │ ├── coffee-cups.jpg │ │ │ └── photography.jpg │ │ ├── scripts │ │ │ ├── shopping-bag.js │ │ │ ├── number.js │ │ │ └── stars.js │ │ ├── third_party │ │ │ └── handlebars-grouped-each.js │ │ └── placeholder.html │ ├── scripts │ │ ├── shopping-bag.js │ │ ├── number.js │ │ └── stars.js │ ├── third_party │ │ └── handlebars-grouped-each.js │ └── placeholder.html ├── README.md └── ANSWERS.md ├── lesson6-styling ├── 01-focus-styles │ ├── README.md │ ├── main.css │ ├── solution │ │ ├── main.css │ │ └── index.html │ └── index.html ├── 02-style-aria-states │ ├── README.md │ ├── main.css │ ├── disclosure.js │ ├── solution │ │ ├── disclosure.js │ │ ├── main.css │ │ └── index.html │ └── index.html ├── 03-contrast-audit │ ├── blog.css │ └── solution │ │ └── blog.css └── 04-mobile-screenreader │ └── blog.css ├── LICENSE ├── README.md └── styles └── components.css /lesson1-overview/README.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lesson5-semantics-aria/README.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lesson3-semantics-built-in/README.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lesson4-semantics-navigating/README.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @udacity/active-public-content -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | bower_components 4 | *.sketch 5 | stock-photos 6 | -------------------------------------------------------------------------------- /lesson2-focus/01-basic-form/uair-2000.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/ud891/HEAD/lesson2-focus/01-basic-form/uair-2000.jpg -------------------------------------------------------------------------------- /lesson2-focus/02-dom-order/images/bee.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/ud891/HEAD/lesson2-focus/02-dom-order/images/bee.jpg -------------------------------------------------------------------------------- /lesson2-focus/02-dom-order/images/cat.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/ud891/HEAD/lesson2-focus/02-dom-order/images/cat.jpg -------------------------------------------------------------------------------- /lesson2-focus/02-dom-order/images/dog.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/ud891/HEAD/lesson2-focus/02-dom-order/images/dog.jpg -------------------------------------------------------------------------------- /lesson2-focus/02-dom-order/images/goat.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/ud891/HEAD/lesson2-focus/02-dom-order/images/goat.jpg -------------------------------------------------------------------------------- /lesson2-focus/06-skip-links/images/bee.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/ud891/HEAD/lesson2-focus/06-skip-links/images/bee.jpg -------------------------------------------------------------------------------- /lesson2-focus/06-skip-links/images/cat.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/ud891/HEAD/lesson2-focus/06-skip-links/images/cat.jpg -------------------------------------------------------------------------------- /lesson2-focus/06-skip-links/images/dog.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/ud891/HEAD/lesson2-focus/06-skip-links/images/dog.jpg -------------------------------------------------------------------------------- /final-project/ecommerce-site/images/art.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/ud891/HEAD/final-project/ecommerce-site/images/art.jpg -------------------------------------------------------------------------------- /final-project/ecommerce-site/images/bag.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/ud891/HEAD/final-project/ecommerce-site/images/bag.png -------------------------------------------------------------------------------- /final-project/ecommerce-site/images/food.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/ud891/HEAD/final-project/ecommerce-site/images/food.jpg -------------------------------------------------------------------------------- /lesson2-focus/02-dom-order/images/coffee.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/ud891/HEAD/lesson2-focus/02-dom-order/images/coffee.jpg -------------------------------------------------------------------------------- /lesson2-focus/06-skip-links/images/goat.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/ud891/HEAD/lesson2-focus/06-skip-links/images/goat.jpg -------------------------------------------------------------------------------- /final-project/ecommerce-site/images/fashion.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/ud891/HEAD/final-project/ecommerce-site/images/fashion.jpg -------------------------------------------------------------------------------- /final-project/ecommerce-site/images/music.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/ud891/HEAD/final-project/ecommerce-site/images/music.jpg -------------------------------------------------------------------------------- /final-project/ecommerce-site/images/nature.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/ud891/HEAD/final-project/ecommerce-site/images/nature.jpg -------------------------------------------------------------------------------- /final-project/ecommerce-site/images/sport.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/ud891/HEAD/final-project/ecommerce-site/images/sport.jpg -------------------------------------------------------------------------------- /final-project/ecommerce-site/images/star-on.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/ud891/HEAD/final-project/ecommerce-site/images/star-on.png -------------------------------------------------------------------------------- /lesson2-focus/02-dom-order/images/football.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/ud891/HEAD/lesson2-focus/02-dom-order/images/football.jpg -------------------------------------------------------------------------------- /lesson2-focus/02-dom-order/images/telephone.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/ud891/HEAD/lesson2-focus/02-dom-order/images/telephone.jpg -------------------------------------------------------------------------------- /lesson2-focus/06-skip-links/images/coffee.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/ud891/HEAD/lesson2-focus/06-skip-links/images/coffee.jpg -------------------------------------------------------------------------------- /lesson2-focus/06-skip-links/images/football.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/ud891/HEAD/lesson2-focus/06-skip-links/images/football.jpg -------------------------------------------------------------------------------- /lesson5-semantics-aria/21-dialog/uair-2000.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/ud891/HEAD/lesson5-semantics-aria/21-dialog/uair-2000.jpg -------------------------------------------------------------------------------- /final-project/ecommerce-site/images/arrow-up.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/ud891/HEAD/final-project/ecommerce-site/images/arrow-up.png -------------------------------------------------------------------------------- /final-project/ecommerce-site/images/landscape.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/ud891/HEAD/final-project/ecommerce-site/images/landscape.jpg -------------------------------------------------------------------------------- /final-project/ecommerce-site/images/star-off.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/ud891/HEAD/final-project/ecommerce-site/images/star-off.png -------------------------------------------------------------------------------- /final-project/ecommerce-site/images/vacation.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/ud891/HEAD/final-project/ecommerce-site/images/vacation.jpg -------------------------------------------------------------------------------- /final-project/ecommerce-site/images/woodfire.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/ud891/HEAD/final-project/ecommerce-site/images/woodfire.jpg -------------------------------------------------------------------------------- /final-project/ecommerce-site/images/workspace.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/ud891/HEAD/final-project/ecommerce-site/images/workspace.jpg -------------------------------------------------------------------------------- /lesson2-focus/06-skip-links/images/telephone.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/ud891/HEAD/lesson2-focus/06-skip-links/images/telephone.jpg -------------------------------------------------------------------------------- /final-project/ecommerce-site/images/architecture.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/ud891/HEAD/final-project/ecommerce-site/images/architecture.jpg -------------------------------------------------------------------------------- /final-project/ecommerce-site/images/card-guide.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/ud891/HEAD/final-project/ecommerce-site/images/card-guide.jpg -------------------------------------------------------------------------------- /final-project/ecommerce-site/images/coffee-cups.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/ud891/HEAD/final-project/ecommerce-site/images/coffee-cups.jpg -------------------------------------------------------------------------------- /final-project/ecommerce-site/images/decoration.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/ud891/HEAD/final-project/ecommerce-site/images/decoration.jpg -------------------------------------------------------------------------------- /final-project/ecommerce-site/images/down-arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/ud891/HEAD/final-project/ecommerce-site/images/down-arrow.png -------------------------------------------------------------------------------- /final-project/ecommerce-site/images/photography.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/ud891/HEAD/final-project/ecommerce-site/images/photography.jpg -------------------------------------------------------------------------------- /final-project/ecommerce-site/images/travelling.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/ud891/HEAD/final-project/ecommerce-site/images/travelling.jpg -------------------------------------------------------------------------------- /final-project/ecommerce-site/solution/images/art.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/ud891/HEAD/final-project/ecommerce-site/solution/images/art.jpg -------------------------------------------------------------------------------- /final-project/ecommerce-site/solution/images/bag.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/ud891/HEAD/final-project/ecommerce-site/solution/images/bag.png -------------------------------------------------------------------------------- /lesson2-focus/02-dom-order/solution/images/bee.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/ud891/HEAD/lesson2-focus/02-dom-order/solution/images/bee.jpg -------------------------------------------------------------------------------- /lesson2-focus/02-dom-order/solution/images/cat.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/ud891/HEAD/lesson2-focus/02-dom-order/solution/images/cat.jpg -------------------------------------------------------------------------------- /lesson2-focus/02-dom-order/solution/images/dog.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/ud891/HEAD/lesson2-focus/02-dom-order/solution/images/dog.jpg -------------------------------------------------------------------------------- /lesson2-focus/02-dom-order/solution/images/goat.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/ud891/HEAD/lesson2-focus/02-dom-order/solution/images/goat.jpg -------------------------------------------------------------------------------- /lesson2-focus/03-managing-focus/images/vegemite.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/ud891/HEAD/lesson2-focus/03-managing-focus/images/vegemite.jpg -------------------------------------------------------------------------------- /lesson4-semantics-navigating/08-link-text/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/ud891/HEAD/lesson4-semantics-navigating/08-link-text/logo.png -------------------------------------------------------------------------------- /final-project/ecommerce-site/solution/images/food.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/ud891/HEAD/final-project/ecommerce-site/solution/images/food.jpg -------------------------------------------------------------------------------- /final-project/ecommerce-site/solution/images/music.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/ud891/HEAD/final-project/ecommerce-site/solution/images/music.jpg -------------------------------------------------------------------------------- /final-project/ecommerce-site/solution/images/sport.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/ud891/HEAD/final-project/ecommerce-site/solution/images/sport.jpg -------------------------------------------------------------------------------- /lesson2-focus/02-dom-order/solution/images/coffee.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/ud891/HEAD/lesson2-focus/02-dom-order/solution/images/coffee.jpg -------------------------------------------------------------------------------- /lesson4-semantics-navigating/08-link-text/sandwich.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/ud891/HEAD/lesson4-semantics-navigating/08-link-text/sandwich.jpg -------------------------------------------------------------------------------- /final-project/ecommerce-site/solution/images/arrow-up.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/ud891/HEAD/final-project/ecommerce-site/solution/images/arrow-up.png -------------------------------------------------------------------------------- /final-project/ecommerce-site/solution/images/fashion.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/ud891/HEAD/final-project/ecommerce-site/solution/images/fashion.jpg -------------------------------------------------------------------------------- /final-project/ecommerce-site/solution/images/nature.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/ud891/HEAD/final-project/ecommerce-site/solution/images/nature.jpg -------------------------------------------------------------------------------- /final-project/ecommerce-site/solution/images/star-off.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/ud891/HEAD/final-project/ecommerce-site/solution/images/star-off.png -------------------------------------------------------------------------------- /final-project/ecommerce-site/solution/images/star-on.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/ud891/HEAD/final-project/ecommerce-site/solution/images/star-on.png -------------------------------------------------------------------------------- /final-project/ecommerce-site/solution/images/vacation.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/ud891/HEAD/final-project/ecommerce-site/solution/images/vacation.jpg -------------------------------------------------------------------------------- /final-project/ecommerce-site/solution/images/woodfire.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/ud891/HEAD/final-project/ecommerce-site/solution/images/woodfire.jpg -------------------------------------------------------------------------------- /lesson1-overview/06-experiencing-screen-reader/koala.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/ud891/HEAD/lesson1-overview/06-experiencing-screen-reader/koala.jpg -------------------------------------------------------------------------------- /lesson2-focus/02-dom-order/solution/images/football.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/ud891/HEAD/lesson2-focus/02-dom-order/solution/images/football.jpg -------------------------------------------------------------------------------- /lesson2-focus/02-dom-order/solution/images/telephone.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/ud891/HEAD/lesson2-focus/02-dom-order/solution/images/telephone.jpg -------------------------------------------------------------------------------- /lesson4-semantics-navigating/08-link-text/cappuccino.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/ud891/HEAD/lesson4-semantics-navigating/08-link-text/cappuccino.jpg -------------------------------------------------------------------------------- /lesson5-semantics-aria/21-dialog/solution/uair-2000.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/ud891/HEAD/lesson5-semantics-aria/21-dialog/solution/uair-2000.jpg -------------------------------------------------------------------------------- /final-project/ecommerce-site/solution/images/card-guide.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/ud891/HEAD/final-project/ecommerce-site/solution/images/card-guide.jpg -------------------------------------------------------------------------------- /final-project/ecommerce-site/solution/images/decoration.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/ud891/HEAD/final-project/ecommerce-site/solution/images/decoration.jpg -------------------------------------------------------------------------------- /final-project/ecommerce-site/solution/images/down-arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/ud891/HEAD/final-project/ecommerce-site/solution/images/down-arrow.png -------------------------------------------------------------------------------- /final-project/ecommerce-site/solution/images/landscape.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/ud891/HEAD/final-project/ecommerce-site/solution/images/landscape.jpg -------------------------------------------------------------------------------- /final-project/ecommerce-site/solution/images/travelling.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/ud891/HEAD/final-project/ecommerce-site/solution/images/travelling.jpg -------------------------------------------------------------------------------- /final-project/ecommerce-site/solution/images/wallabies.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/ud891/HEAD/final-project/ecommerce-site/solution/images/wallabies.jpg -------------------------------------------------------------------------------- /final-project/ecommerce-site/solution/images/workspace.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/ud891/HEAD/final-project/ecommerce-site/solution/images/workspace.jpg -------------------------------------------------------------------------------- /lesson4-semantics-navigating/08-link-text/solution/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/ud891/HEAD/lesson4-semantics-navigating/08-link-text/solution/logo.png -------------------------------------------------------------------------------- /final-project/ecommerce-site/solution/images/architecture.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/ud891/HEAD/final-project/ecommerce-site/solution/images/architecture.jpg -------------------------------------------------------------------------------- /final-project/ecommerce-site/solution/images/coffee-cups.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/ud891/HEAD/final-project/ecommerce-site/solution/images/coffee-cups.jpg -------------------------------------------------------------------------------- /final-project/ecommerce-site/solution/images/photography.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/ud891/HEAD/final-project/ecommerce-site/solution/images/photography.jpg -------------------------------------------------------------------------------- /lesson2-focus/03-managing-focus/solution/images/vegemite.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/ud891/HEAD/lesson2-focus/03-managing-focus/solution/images/vegemite.jpg -------------------------------------------------------------------------------- /lesson3-semantics-built-in/05-writing-semantic-html/wombat.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/ud891/HEAD/lesson3-semantics-built-in/05-writing-semantic-html/wombat.jpg -------------------------------------------------------------------------------- /lesson3-semantics-built-in/17-text-alternatives/images/bee.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/ud891/HEAD/lesson3-semantics-built-in/17-text-alternatives/images/bee.jpg -------------------------------------------------------------------------------- /lesson3-semantics-built-in/17-text-alternatives/images/dog.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/ud891/HEAD/lesson3-semantics-built-in/17-text-alternatives/images/dog.jpg -------------------------------------------------------------------------------- /lesson3-semantics-built-in/17-text-alternatives/images/goat.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/ud891/HEAD/lesson3-semantics-built-in/17-text-alternatives/images/goat.jpg -------------------------------------------------------------------------------- /lesson3-semantics-built-in/17-text-alternatives/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/ud891/HEAD/lesson3-semantics-built-in/17-text-alternatives/images/logo.png -------------------------------------------------------------------------------- /lesson4-semantics-navigating/08-link-text/solution/sandwich.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/ud891/HEAD/lesson4-semantics-navigating/08-link-text/solution/sandwich.jpg -------------------------------------------------------------------------------- /lesson3-semantics-built-in/17-text-alternatives/images/coffee.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/ud891/HEAD/lesson3-semantics-built-in/17-text-alternatives/images/coffee.jpg -------------------------------------------------------------------------------- /lesson3-semantics-built-in/17-text-alternatives/images/football.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/ud891/HEAD/lesson3-semantics-built-in/17-text-alternatives/images/football.jpg -------------------------------------------------------------------------------- /lesson4-semantics-navigating/08-link-text/solution/cappuccino.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/ud891/HEAD/lesson4-semantics-navigating/08-link-text/solution/cappuccino.jpg -------------------------------------------------------------------------------- /lesson3-semantics-built-in/17-text-alternatives/images/telephone.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/ud891/HEAD/lesson3-semantics-built-in/17-text-alternatives/images/telephone.jpg -------------------------------------------------------------------------------- /lesson3-semantics-built-in/17-text-alternatives/images/160204193356-01-cat-500.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/ud891/HEAD/lesson3-semantics-built-in/17-text-alternatives/images/160204193356-01-cat-500.jpg -------------------------------------------------------------------------------- /lesson2-focus/06-skip-links/README.md: -------------------------------------------------------------------------------- 1 | Visual presentation doesn't match DOM order 2 | There are floated elements and absolutely positioned elements, completely screwing up the 3 | tab flow. 4 | Currently failing audit tool :\ 5 | -------------------------------------------------------------------------------- /lesson6-styling/01-focus-styles/README.md: -------------------------------------------------------------------------------- 1 | The buttons on this page don’t currently have focus styles, making them pretty 2 | much useless to a keyboard user. With your new CSS knowledge, try using the 3 | :focus pseudo-class to give these buttons interesting focus states. 4 | -------------------------------------------------------------------------------- /lesson6-styling/02-style-aria-states/README.md: -------------------------------------------------------------------------------- 1 | See if you can do a better job styling this button using ARIA states. One huge 2 | benefit to styling with ARIA is that it provides visual feedback that you've 3 | applied the state correctly, which can act as a safeguard when you're testing 4 | and debugging your code. 5 | -------------------------------------------------------------------------------- /lesson2-focus/04-offscreen-content/README.md: -------------------------------------------------------------------------------- 1 | Using either the [Accessibility Developer Tools extension](https://chrome.google.com/webstore/detail/accessibility-developer-t/fpkknkljclfencbdbgkenhalefipecmb?hl=en) or `document.activeElement` see if you can track down the element that's 2 | stealing focus and fix the page. 3 | -------------------------------------------------------------------------------- /lesson2-focus/02-dom-order/README.md: -------------------------------------------------------------------------------- 1 | Tabbing around the page should reveal a mixed up tab order. Read through the 2 | `index.html` and see if there are any places where elements may be in the wrong 3 | order. If something looks out of place see if you can fix it so the tab order 4 | works as expected. If you get stuck you can refer to the `solution` directory. 5 | -------------------------------------------------------------------------------- /lesson2-focus/07-modals-and-keyboard-traps/README.md: -------------------------------------------------------------------------------- 1 | See if you can figure out what needs to be added to the `modal.js` file to trap 2 | the keyboard. 3 | 4 | There's quite a bit of code involved in created a keyboard trap so don't worry 5 | if it seems overwhelming. Take your time and refer back to [this video](https://www.udacity.com/course/viewer#!/c-ud891/l-7962031279/m-7962141423) 6 | if you get stuck. 7 | -------------------------------------------------------------------------------- /lesson2-focus/02-dom-order/images/search.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /lesson2-focus/06-skip-links/images/search.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /lesson2-focus/02-dom-order/solution/images/search.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /lesson3-semantics-built-in/17-text-alternatives/images/search.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /lesson2-focus/01-basic-form/README.md: -------------------------------------------------------------------------------- 1 | Navigate the page using nothing but your keyboard. 2 | You'll need to search for a ticket that matches the following criteria: 3 | 4 | The ticket should... 5 | 6 | - Be a round trip 7 | - to Melbourne 8 | - leaving on 10/12/2017 9 | - returning on 10/23/2017 10 | - window seat 11 | - and you DO NOT want to receive promotional offers 😀 12 | 13 | When you have all the field filled out correctly you can return to the lesson 14 | -------------------------------------------------------------------------------- /final-project/ecommerce-site/scripts/shopping-bag.js: -------------------------------------------------------------------------------- 1 | function ShoppingBag(bag, count) { 2 | this._el = bag; 3 | this._el.setAttribute('data-count', count); 4 | var itemPhrase = count === 1 ? `${count} item` : `${count} items`; 5 | this._el.setAttribute('aria-label', `${itemPhrase} in cart`); 6 | } 7 | 8 | var shoppingBags = document.querySelectorAll('.shopping-bag'); 9 | for (var i = 0; i < shoppingBags.length; i++) { 10 | // We'll fake the number of items that the user has by passing in 2 11 | // In a real app this would probably come from some user data in the backend 12 | new ShoppingBag(shoppingBags[i], 2); 13 | } 14 | -------------------------------------------------------------------------------- /final-project/ecommerce-site/solution/scripts/shopping-bag.js: -------------------------------------------------------------------------------- 1 | function ShoppingBag(bag, count) { 2 | this._el = bag; 3 | this._el.setAttribute('data-count', count); 4 | var itemPhrase = count === 1 ? `${count} item` : `${count} items`; 5 | this._el.setAttribute('aria-label', `${itemPhrase} in cart`); 6 | } 7 | 8 | var shoppingBags = document.querySelectorAll('.shopping-bag'); 9 | for (var i = 0; i < shoppingBags.length; i++) { 10 | // We'll fake the number of items that the user has by passing in 2 11 | // In a real app this would probably come from some user data in the backend 12 | new ShoppingBag(shoppingBags[i], 2); 13 | } 14 | -------------------------------------------------------------------------------- /lesson2-focus/05-radio-group/main.css: -------------------------------------------------------------------------------- 1 | .demo { 2 | width: 200px; 3 | } 4 | 5 | .radiogroup { 6 | list-style: none; 7 | } 8 | 9 | .radio { 10 | position: relative; 11 | } 12 | 13 | .radio::before { 14 | content: ''; 15 | display: block; 16 | width: 10px; 17 | height: 10px; 18 | border: 1px solid black; 19 | position: absolute; 20 | left: -18px; 21 | top: 3px; 22 | border-radius: 50%; 23 | } 24 | 25 | .radio[checked]::after { 26 | content: ''; 27 | display: block; 28 | width: 8px; 29 | height: 8px; 30 | background: red; 31 | position: absolute; 32 | left: -16px; 33 | top: 5px; 34 | border-radius: 50%; 35 | } 36 | -------------------------------------------------------------------------------- /lesson2-focus/05-radio-group/solution/main.css: -------------------------------------------------------------------------------- 1 | .demo { 2 | width: 200px; 3 | } 4 | 5 | .radiogroup { 6 | list-style: none; 7 | } 8 | 9 | .radio { 10 | position: relative; 11 | } 12 | 13 | .radio::before { 14 | content: ''; 15 | display: block; 16 | width: 10px; 17 | height: 10px; 18 | border: 1px solid black; 19 | position: absolute; 20 | left: -18px; 21 | top: 3px; 22 | border-radius: 50%; 23 | } 24 | 25 | .radio[checked]::after { 26 | content: ''; 27 | display: block; 28 | width: 8px; 29 | height: 8px; 30 | background: red; 31 | position: absolute; 32 | left: -16px; 33 | top: 5px; 34 | border-radius: 50%; 35 | } 36 | -------------------------------------------------------------------------------- /lesson5-semantics-aria/06-radio-group/main.css: -------------------------------------------------------------------------------- 1 | .demo { 2 | margin: 50px; 3 | } 4 | 5 | .radiogroup { 6 | list-style: none; 7 | } 8 | 9 | .radio { 10 | position: relative; 11 | } 12 | 13 | .radio::before { 14 | content: ''; 15 | display: block; 16 | width: 10px; 17 | height: 10px; 18 | border: 1px solid black; 19 | position: absolute; 20 | left: -18px; 21 | top: 3px; 22 | border-radius: 50%; 23 | } 24 | 25 | .radio[checked]::after { 26 | content: ''; 27 | display: block; 28 | width: 8px; 29 | height: 8px; 30 | background: red; 31 | position: absolute; 32 | left: -16px; 33 | top: 5px; 34 | border-radius: 50%; 35 | } 36 | -------------------------------------------------------------------------------- /final-project/ecommerce-site/third_party/handlebars-grouped-each.js: -------------------------------------------------------------------------------- 1 | // Credit to https://funkjedi.com/technology/412-every-nth-item-in-handlebars/ 2 | 3 | Handlebars.registerHelper('grouped_each', function(every, context, options) { 4 | var out = "", 5 | subcontext = [], 6 | i; 7 | if (context && context.length > 0) { 8 | for (i = 0; i < context.length; i++) { 9 | context[i].outer_index = i; 10 | if (i > 0 && i % every === 0) { 11 | out += options.fn(subcontext); 12 | subcontext = []; 13 | } 14 | subcontext.push(context[i]); 15 | } 16 | out += options.fn(subcontext); 17 | } 18 | return out; 19 | }); 20 | -------------------------------------------------------------------------------- /lesson2-focus/05-radio-group/README.md: -------------------------------------------------------------------------------- 1 | You can find the files for this example in the `lesson2-focus/05-radio-group` 2 | directory. 3 | 4 | Using the ARIA Authoring Best Practices doc (either 5 | [version 1.0](https://www.w3.org/TR/wai-aria-practices/) or [version 1.1](https://www.w3.org/TR/wai-aria-practices-1.1/)) find the radio pattern 6 | and implement support for the `Down Arrow and Right Arrow` pattern. 7 | 8 | **Note**: The 1.0 version of the doc refers to this as a "Radio Button" whereas 9 | the 1.1 version of the doc refers to this as a "Radio Group". 10 | 11 | You'll want to work in the `radiogroup.js` file to implement your keyboarding 12 | support. 13 | -------------------------------------------------------------------------------- /lesson5-semantics-aria/06-radio-group/solution/main.css: -------------------------------------------------------------------------------- 1 | .demo { 2 | margin: 50px; 3 | } 4 | 5 | .radiogroup { 6 | list-style: none; 7 | } 8 | 9 | .radio { 10 | position: relative; 11 | } 12 | 13 | .radio::before { 14 | content: ''; 15 | display: block; 16 | width: 10px; 17 | height: 10px; 18 | border: 1px solid black; 19 | position: absolute; 20 | left: -18px; 21 | top: 3px; 22 | border-radius: 50%; 23 | } 24 | 25 | .radio[checked]::after { 26 | content: ''; 27 | display: block; 28 | width: 8px; 29 | height: 8px; 30 | background: red; 31 | position: absolute; 32 | left: -16px; 33 | top: 5px; 34 | border-radius: 50%; 35 | } 36 | -------------------------------------------------------------------------------- /final-project/ecommerce-site/solution/third_party/handlebars-grouped-each.js: -------------------------------------------------------------------------------- 1 | // Credit to https://funkjedi.com/technology/412-every-nth-item-in-handlebars/ 2 | 3 | Handlebars.registerHelper('grouped_each', function(every, context, options) { 4 | var out = "", 5 | subcontext = [], 6 | i; 7 | if (context && context.length > 0) { 8 | for (i = 0; i < context.length; i++) { 9 | context[i].outer_index = i; 10 | if (i > 0 && i % every === 0) { 11 | out += options.fn(subcontext); 12 | subcontext = []; 13 | } 14 | subcontext.push(context[i]); 15 | } 16 | out += options.fn(subcontext); 17 | } 18 | return out; 19 | }); 20 | -------------------------------------------------------------------------------- /lesson6-styling/01-focus-styles/main.css: -------------------------------------------------------------------------------- 1 | .masthead { 2 | margin: 32px 0; 3 | } 4 | 5 | button:hover, 6 | button:focus, 7 | a:hover, 8 | a:focus { 9 | outline: none; 10 | color: inherit; 11 | text-decoration: none; 12 | } 13 | 14 | .nav > li > a { 15 | padding: 0 16px; 16 | margin: 0 8px; 17 | } 18 | 19 | .nav > li > a:focus { 20 | text-decoration: none; 21 | background: transparent; 22 | } 23 | 24 | .jumbotron .mdl-button { 25 | background-color: #008b00; 26 | color: #FFFFFF; 27 | } 28 | 29 | .mdl-button.raised { 30 | box-shadow: 0 2px 2px 0 rgba(0, 0, 0, .14), 31 | 0 3px 1px -2px rgba(0, 0, 0, .2), 32 | 0 1px 5px 0 rgba(0, 0, 0, .12); 33 | } 34 | -------------------------------------------------------------------------------- /lesson2-focus/03-managing-focus/README.md: -------------------------------------------------------------------------------- 1 | To run this example you'll need to use a local server. The easiest way to do 2 | so is to use the [Web Server for Chrome extension](https://chrome.google.com/webstore/detail/web-server-for-chrome/ofhbbkphhbklhfoeikjpcbhemlocgigb?hl=en). 3 | 4 | Make sure your settings look like this: 5 | 6 | Chrome 200 OK settings 7 | 8 | Under the heading that says "Web Server URL(s)" click the first link to preview 9 | the app on your local server. 10 | 11 | Take a look at the `scripts/main.js` file to figure out how the route is updated 12 | and to determine when you would need to step in and manage focus. 13 | -------------------------------------------------------------------------------- /final-project/README.md: -------------------------------------------------------------------------------- 1 | # Final Project 2 | 3 | It's time to take the skills you've learned in this course and apply them to 4 | a real project. We've gone ahead and created a very fancy looking site but 5 | there are a number of accessibility issues that could be improved. Do your 6 | best to fix as many as you can and if you get stuck or need a hint you can 7 | check out the [answers file](ANSWERS.md) or the [full solution](https://github.com/udacity/ud891/tree/gh-pages/final-project/ecommerce-site/solution). 8 | 9 | Accessibility is one of those things that you can always improve upon so it's 10 | very possible you'll think up additions and solutions that even we didn't 11 | cover in our final version. Have fun with it and see if you can build an 12 | access that you think all of your users will love. 13 | -------------------------------------------------------------------------------- /lesson5-semantics-aria/19-aria-live/main.css: -------------------------------------------------------------------------------- 1 | .demo { 2 | margin: 50px; 3 | } 4 | 5 | html, body { 6 | margin: 0; 7 | padding: 0; 8 | height: 100%; 9 | font-family: 'Roboto', 'Helvetica', 'Arial', sans-serif; 10 | } 11 | 12 | 13 | .label { 14 | display: block; 15 | font-size: 14px; 16 | color: #3F51B5; 17 | margin-bottom: 8px; 18 | } 19 | 20 | [role=spinbutton] { 21 | border-radius: 4px; 22 | border: 1px solid #9E9E9E; 23 | font-size: 18px; 24 | padding: 4px; 25 | font-family: inherit; 26 | font-weight: 100; 27 | color: #616161; 28 | display: inline-block; 29 | } 30 | 31 | [role=spinbutton] #number { 32 | padding-top: 1px; 33 | width: 3em; 34 | } 35 | 36 | button { 37 | color: white; 38 | background: #3F51B5; 39 | border: none; 40 | border-radius: 4px; 41 | font-size: 20px; 42 | font-family: inherit; 43 | font-weight: 100; 44 | } 45 | -------------------------------------------------------------------------------- /lesson3-semantics-built-in/02-chromevox-lite/embed.css: -------------------------------------------------------------------------------- 1 | .hide-unless-focused { 2 | position: absolute; 3 | left: -10000px; 4 | top: -10000px; 5 | } 6 | 7 | .hide-unless-focused:focus { 8 | left: 10px; 9 | top: 10px; 10 | } 11 | 12 | button#enable-cvox { 13 | border: none; 14 | border-radius: 4px; 15 | font-weight: 100; 16 | font-family: 'Roboto', 'Helvetica', 'Arial', sans-serif; 17 | background-color: #2196F3; 18 | color: white; 19 | z-index: 1000; 20 | padding: 2px 7px; 21 | font-size: 18px; 22 | } 23 | 24 | body[cvox-enabled] button#enable-cvox { 25 | display: none; 26 | } 27 | 28 | div#self-destruct { 29 | position: absolute; 30 | left: -1000px; 31 | top: -1000px; 32 | } 33 | 34 | .blur { 35 | -webkit-filter: blur(10px); 36 | -moz-filter: blur(10px); 37 | -o-filter: blur(10px); 38 | -ms-filter: blur(10px); 39 | filter: blur(10px); 40 | } 41 | -------------------------------------------------------------------------------- /lesson3-semantics-built-in/16-labelling-input-elements/label.js: -------------------------------------------------------------------------------- 1 | var checkbox = document.querySelector('input#jLetter'); 2 | var parent = checkbox.parentElement; 3 | var labelText = parent.textContent.trim(); 4 | 5 | function observe(records, self) { 6 | if (!checkbox || !parent) { 7 | console.error('You deleted the element you were supposed to label! Maybe refresh the page and try again...'); 8 | return; 9 | } 10 | 11 | if (!checkbox.labels) 12 | return; 13 | 14 | for (var i = 0; i < checkbox.labels.length; i++) { 15 | var label = checkbox.labels[i]; 16 | if (label.textContent.trim() != labelText) 17 | continue; 18 | 19 | document.querySelector('.secret').textContent = 'The secret word is: "BILBY"' 20 | document.querySelector('.header').classList.add('success'); 21 | } 22 | 23 | } 24 | var observer = new MutationObserver(observe); 25 | observer.observe(parent, { childList: true, subtree: true }); 26 | -------------------------------------------------------------------------------- /lesson2-focus/07-modals-and-keyboard-traps/main.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: 'Georgia', sans-serif; 3 | } 4 | 5 | .modal { 6 | width: 50%; 7 | border-radius: 4px; 8 | margin-left: auto; 9 | margin-right: auto; 10 | padding: 24px; 11 | background-color: #fff; 12 | z-index: 3; /* places the modal on top of everything */ 13 | position: fixed; 14 | top: 25%; 15 | left: 25%; 16 | display: none; 17 | } 18 | 19 | .modal-overlay { 20 | width: 100%; 21 | height: 100%; 22 | z-index: 2; /* places the modalOverlay between the main page and the modal dialog */ 23 | background-color: #000; 24 | opacity: 0.5; 25 | position: fixed; 26 | top: 0; 27 | left: 0; 28 | display: none; 29 | margin: 0; 30 | padding: 0; 31 | } 32 | 33 | h1 { 34 | margin-top: 0; 35 | } 36 | 37 | .field { 38 | display: inline-block; 39 | vertical-align: bottom; 40 | } 41 | 42 | label { 43 | display: block; 44 | margin-bottom: 4px; 45 | } 46 | -------------------------------------------------------------------------------- /lesson3-semantics-built-in/16-labelling-input-elements/solution/label.js: -------------------------------------------------------------------------------- 1 | var checkbox = document.querySelector('input#jLetter'); 2 | var parent = checkbox.parentElement; 3 | var labelText = parent.textContent.trim(); 4 | 5 | function observe(records, self) { 6 | if (!checkbox || !parent) { 7 | console.error('You deleted the element you were supposed to label! Maybe refresh the page and try again...'); 8 | return; 9 | } 10 | 11 | if (!checkbox.labels) 12 | return; 13 | 14 | for (var i = 0; i < checkbox.labels.length; i++) { 15 | var label = checkbox.labels[i]; 16 | if (label.textContent.trim() != labelText) 17 | continue; 18 | 19 | document.querySelector('.secret').textContent = 'The secret word is: "BILBY"' 20 | document.querySelector('.header').classList.add('success'); 21 | } 22 | 23 | } 24 | var observer = new MutationObserver(observe); 25 | observer.observe(parent, { childList: true, subtree: true }); 26 | -------------------------------------------------------------------------------- /lesson2-focus/07-modals-and-keyboard-traps/solution/main.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: 'Georgia', sans-serif; 3 | } 4 | 5 | .modal { 6 | width: 50%; 7 | border-radius: 4px; 8 | margin-left: auto; 9 | margin-right: auto; 10 | padding: 24px; 11 | background-color: #fff; 12 | z-index: 3; /* places the modal on top of everything */ 13 | position: fixed; 14 | top: 25%; 15 | left: 25%; 16 | display: none; 17 | } 18 | 19 | .modal-overlay { 20 | width: 100%; 21 | height: 100%; 22 | z-index: 2; /* places the modalOverlay between the main page and the modal dialog */ 23 | background-color: #000; 24 | opacity: 0.5; 25 | position: fixed; 26 | top: 0; 27 | left: 0; 28 | display: none; 29 | margin: 0; 30 | padding: 0; 31 | } 32 | 33 | h1 { 34 | margin-top: 0; 35 | } 36 | 37 | .field { 38 | display: inline-block; 39 | vertical-align: bottom; 40 | } 41 | 42 | label { 43 | display: block; 44 | margin-bottom: 4px; 45 | } 46 | -------------------------------------------------------------------------------- /lesson5-semantics-aria/19-aria-live/number.js: -------------------------------------------------------------------------------- 1 | var number = document.querySelector('#number'); 2 | function increment() { 3 | var oldValue = number.textContent; 4 | number.textContent = parseInt(oldValue, 10) + 10; 5 | } 6 | 7 | function decrement() { 8 | var oldValue = number.textContent; 9 | number.textContent = parseInt(oldValue, 10) - 10; 10 | } 11 | 12 | document.querySelector('#add').addEventListener('click', increment); 13 | document.querySelector('#subtract').addEventListener('click', decrement); 14 | 15 | var VK_UP = 38; 16 | var VK_DOWN = 40; 17 | var spinbutton = document.querySelector('[role=spinbutton]'); 18 | spinbutton.addEventListener('keydown', function(e) { 19 | switch (e.keyCode) { 20 | case VK_UP: 21 | increment(); 22 | e.stopPropagation(); 23 | e.preventDefault(); 24 | break; 25 | case VK_DOWN: 26 | decrement(); 27 | e.stopPropagation(); 28 | e.preventDefault(); 29 | break; 30 | } 31 | }); 32 | -------------------------------------------------------------------------------- /lesson2-focus/05-radio-group/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 |

Drink Options

15 | 16 | 33 | 34 |
35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /lesson2-focus/05-radio-group/solution/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 |

Drink Options

15 | 16 | 33 | 34 |
35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /lesson2-focus/03-managing-focus/scripts/main.js: -------------------------------------------------------------------------------- 1 | page('/', function() { 2 | page.redirect('/what-is-vegemite'); 3 | }); 4 | 5 | page('/:slug', function(context) { 6 | // This will match any value after the first / in the url. For example, if 7 | // the url was /foo, the value of slug would be "foo". 8 | var slug = context.params.slug; 9 | 10 | // Remove is-active class from previous menu item and section 11 | var oldMenuItem = document.querySelector('#menu .is-active'); 12 | var oldPage = document.querySelector('main .is-active'); 13 | oldMenuItem.classList.remove('is-active'); 14 | oldPage.classList.remove('is-active'); 15 | 16 | // Add is-active class to new menu item and section using the URL slug 17 | var newMenuItem = document.querySelector('#menu [data-page='+slug+']'); 18 | var newPage = document.querySelector('main [data-page='+slug+']'); 19 | newMenuItem.classList.add('is-active'); 20 | newPage.classList.add('is-active'); 21 | 22 | }); 23 | 24 | page({ 25 | hashbang: true 26 | }); 27 | -------------------------------------------------------------------------------- /lesson6-styling/02-style-aria-states/main.css: -------------------------------------------------------------------------------- 1 | html, body { 2 | background: #FAFAFA; 3 | height: 100%; 4 | padding: 0; 5 | margin: 0; 6 | font-family: 'Roboto', 'Helvetica', sans-serif; 7 | } 8 | 9 | main { 10 | height: 100%; 11 | display: flex; 12 | flex-direction: column; 13 | align-items: center; 14 | justify-content: center; 15 | } 16 | 17 | .container { 18 | margin: 0 180px; 19 | } 20 | 21 | .disclosure-button:hover, .disclosure-button:focus { 22 | background-color: #D81B60; 23 | color: #FFF; 24 | } 25 | 26 | .disclosure-button .icon::after { 27 | display: inline-block; 28 | font-size: 14px; 29 | margin-right: 8px; 30 | width: 14px; 31 | height: 36px; 32 | } 33 | 34 | .disclosure-button .icon::after { 35 | content: '▶'; 36 | } 37 | 38 | .disclosure-button.expanded .icon::after { 39 | content: '▼'; 40 | } 41 | 42 | .disclosure-content.hidden { 43 | visibility: hidden; 44 | opacity: 0; 45 | } 46 | 47 | .disclosure-content { 48 | visibility: visible; 49 | opacity: 1; 50 | } 51 | -------------------------------------------------------------------------------- /lesson6-styling/01-focus-styles/solution/main.css: -------------------------------------------------------------------------------- 1 | .masthead { 2 | margin: 32px 0; 3 | } 4 | 5 | button:hover, 6 | button:focus, 7 | a:hover, 8 | a:focus { 9 | outline: none; 10 | color: inherit; 11 | text-decoration: none; 12 | } 13 | 14 | .nav > li > a { 15 | padding: 0 16px; 16 | margin: 0 8px; 17 | } 18 | 19 | .jumbotron .mdl-button { 20 | background-color: #008b00; 21 | color: #FFFFFF; 22 | } 23 | 24 | .mdl-button.raised { 25 | box-shadow: 0 2px 2px 0 rgba(0, 0, 0, .14), 26 | 0 3px 1px -2px rgba(0, 0, 0, .2), 27 | 0 1px 5px 0 rgba(0, 0, 0, .12); 28 | } 29 | 30 | .nav > li > a:hover, 31 | .nav > li > a:focus, 32 | .jumbotron .mdl-button:hover, 33 | .jumbotron .mdl-button:focus, 34 | .mdl-button.raised:hover, 35 | .mdl-button.raised:focus { 36 | background: #C2185B; 37 | color: #FFFFFF; 38 | text-decoration: underline; 39 | box-shadow: 0 2px 2px 0 rgba(0, 0, 0, .14), 40 | 0 3px 1px -2px rgba(0, 0, 0, .2), 41 | 0 1px 5px 0 rgba(0, 0, 0, .12); 42 | } 43 | -------------------------------------------------------------------------------- /lesson5-semantics-aria/03-first-steps/checkboxes.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 |

Custom checkboxes

15 | 16 |
17 | Tim-Tams 18 |
19 |
20 | Mint slices 21 |
22 | 23 |

Native checkboxes

24 | 25 |
26 | 30 |
31 |
32 | 36 |
37 | 38 |
39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /lesson5-semantics-aria/03-first-steps/solution/checkboxes.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 |

Custom checkboxes

15 | 16 |
17 | Tim-Tams 18 |
19 |
20 | Mint slices 21 |
22 | 23 |

Native checkboxes

24 | 25 |
26 | 30 |
31 |
32 | 36 |
37 | 38 |
39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /lesson6-styling/02-style-aria-states/disclosure.js: -------------------------------------------------------------------------------- 1 | var button = document.querySelector('button'); 2 | var content = document.getElementById('content'); 3 | 4 | button.addEventListener('click', toggleDisclosure); 5 | button.addEventListener('keydown', toggleDisclosure); 6 | 7 | function toggleDisclosure(e) { 8 | var type = e.type; 9 | 10 | // If the key pressed was not Space or Enter, return 11 | if (type === 'keydown' && (event.keyCode !== 13 && event.keyCode !== 32)) { 12 | return true; 13 | } 14 | 15 | e.preventDefault(); 16 | 17 | if (content.getAttribute('aria-hidden') === 'true') { 18 | 19 | content.setAttribute('aria-hidden', 'false'); 20 | content.classList.remove('hidden'); 21 | 22 | button.setAttribute('aria-expanded', 'true'); 23 | button.classList.add('expanded'); 24 | 25 | } else { 26 | 27 | content.setAttribute('aria-hidden', 'true'); 28 | content.classList.add('hidden'); 29 | 30 | button.setAttribute('aria-expanded', 'false'); 31 | button.classList.remove('expanded'); 32 | 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /lesson5-semantics-aria/02-why-aria/checkboxes.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Custom checkboxes 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 |

Custom checkboxes

15 | 16 |
17 | Tim-Tams 18 |
19 |
20 | Mint slices 21 |
22 | 23 |

Native checkboxes

24 | 25 |
26 | 30 |
31 |
32 | 36 |
37 | 38 |
39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /lesson6-styling/02-style-aria-states/solution/disclosure.js: -------------------------------------------------------------------------------- 1 | var button = document.querySelector('button'); 2 | var content = document.getElementById('content'); 3 | 4 | button.addEventListener('click', toggleDisclosure); 5 | button.addEventListener('keydown', toggleDisclosure); 6 | 7 | function toggleDisclosure(e) { 8 | var type = e.type; 9 | 10 | // If the key pressed was not Space or Enter, return 11 | if (type === 'keydown' && (event.keyCode !== 13 && event.keyCode !== 32)) { 12 | return true; 13 | } 14 | 15 | e.preventDefault(); 16 | 17 | if (content.getAttribute('aria-hidden') === 'true') { 18 | 19 | content.setAttribute('aria-hidden', 'false'); 20 | content.classList.remove('hidden'); 21 | 22 | button.setAttribute('aria-expanded', 'true'); 23 | button.classList.add('expanded'); 24 | 25 | } else { 26 | 27 | content.setAttribute('aria-hidden', 'true'); 28 | content.classList.add('hidden'); 29 | 30 | button.setAttribute('aria-expanded', 'false'); 31 | button.classList.remove('expanded'); 32 | 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /lesson5-semantics-aria/02-why-aria/main.css: -------------------------------------------------------------------------------- 1 | .demo { 2 | margin: 50px; 3 | } 4 | 5 | .checkboxes { 6 | list-style: none; 7 | } 8 | 9 | .checkbox { 10 | position: relative; 11 | padding-left: 22px; 12 | } 13 | 14 | .checkbox:focus { 15 | outline: none; 16 | } 17 | 18 | .checkbox::before { 19 | content: ''; 20 | display: block; 21 | width: 10px; 22 | height: 10px; 23 | border: 1px solid rgba(0, 0, 0, 0.25); 24 | border-radius: 2px; 25 | position: absolute; 26 | left: 3px; 27 | top: 3px; 28 | } 29 | 30 | .checkbox:focus::before { 31 | outline: #2196F3 auto 5px; 32 | } 33 | 34 | .checkbox[checked]::after { 35 | content: ''; 36 | background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAsAAAALCAQAAAADpb+tAAAAaElEQVR4Xl3PIQoCQQCF4Y8JW42D1bDZ4iVEjDbxFpstYhC7eIVBZHkXFGw734sv/TqDQQ8Xb1udja/I8igeIm7Aygj2IpoKTGZnVRNxAHYi4iPiDlA9xX+aNQDFySziqDN6uSp6y7ofEMwZ05uUZRkAAAAASUVORK5CYII=); 37 | background-size: 100% 100%; 38 | display: block; 39 | width: 8px; 40 | height: 8px; 41 | position: absolute; 42 | left: 5px; 43 | top: 5px; 44 | } 45 | -------------------------------------------------------------------------------- /lesson5-semantics-aria/03-first-steps/main.css: -------------------------------------------------------------------------------- 1 | .demo { 2 | margin: 50px; 3 | } 4 | 5 | .checkboxes { 6 | list-style: none; 7 | } 8 | 9 | .checkbox { 10 | position: relative; 11 | padding-left: 22px; 12 | } 13 | 14 | .checkbox:focus { 15 | outline: none; 16 | } 17 | 18 | .checkbox::before { 19 | content: ''; 20 | display: block; 21 | width: 10px; 22 | height: 10px; 23 | border: 1px solid rgba(0, 0, 0, 0.25); 24 | border-radius: 2px; 25 | position: absolute; 26 | left: 3px; 27 | top: 3px; 28 | } 29 | 30 | .checkbox:focus::before { 31 | outline: #2196F3 auto 5px; 32 | } 33 | 34 | .checkbox[checked]::after { 35 | content: ''; 36 | background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAsAAAALCAQAAAADpb+tAAAAaElEQVR4Xl3PIQoCQQCF4Y8JW42D1bDZ4iVEjDbxFpstYhC7eIVBZHkXFGw734sv/TqDQQ8Xb1udja/I8igeIm7Aygj2IpoKTGZnVRNxAHYi4iPiDlA9xX+aNQDFySziqDN6uSp6y7ofEMwZ05uUZRkAAAAASUVORK5CYII=); 37 | background-size: 100% 100%; 38 | display: block; 39 | width: 8px; 40 | height: 8px; 41 | position: absolute; 42 | left: 5px; 43 | top: 5px; 44 | } 45 | -------------------------------------------------------------------------------- /lesson5-semantics-aria/03-first-steps/solution/main.css: -------------------------------------------------------------------------------- 1 | .demo { 2 | margin: 50px; 3 | } 4 | 5 | .checkboxes { 6 | list-style: none; 7 | } 8 | 9 | .checkbox { 10 | position: relative; 11 | padding-left: 22px; 12 | } 13 | 14 | .checkbox:focus { 15 | outline: none; 16 | } 17 | 18 | .checkbox::before { 19 | content: ''; 20 | display: block; 21 | width: 10px; 22 | height: 10px; 23 | border: 1px solid rgba(0, 0, 0, 0.25); 24 | border-radius: 2px; 25 | position: absolute; 26 | left: 3px; 27 | top: 3px; 28 | } 29 | 30 | .checkbox:focus::before { 31 | outline: #2196F3 auto 5px; 32 | } 33 | 34 | .checkbox[checked]::after { 35 | content: ''; 36 | background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAsAAAALCAQAAAADpb+tAAAAaElEQVR4Xl3PIQoCQQCF4Y8JW42D1bDZ4iVEjDbxFpstYhC7eIVBZHkXFGw734sv/TqDQQ8Xb1udja/I8igeIm7Aygj2IpoKTGZnVRNxAHYi4iPiDlA9xX+aNQDFySziqDN6uSp6y7ofEMwZ05uUZRkAAAAASUVORK5CYII=); 37 | background-size: 100% 100%; 38 | display: block; 39 | width: 8px; 40 | height: 8px; 41 | position: absolute; 42 | left: 5px; 43 | top: 5px; 44 | } 45 | -------------------------------------------------------------------------------- /lesson6-styling/02-style-aria-states/solution/main.css: -------------------------------------------------------------------------------- 1 | html, body { 2 | background: #FAFAFA; 3 | height: 100%; 4 | padding: 0; 5 | margin: 0; 6 | font-family: 'Roboto', 'Helvetica', sans-serif; 7 | } 8 | 9 | main { 10 | height: 100%; 11 | display: flex; 12 | flex-direction: column; 13 | align-items: center; 14 | justify-content: center; 15 | } 16 | 17 | .container { 18 | margin: 0 180px; 19 | } 20 | 21 | .disclosure-button:hover, .disclosure-button:focus { 22 | background-color: #D81B60; 23 | color: #FFF; 24 | } 25 | 26 | .disclosure-button .icon::after { 27 | display: inline-block; 28 | font-size: 14px; 29 | margin-right: 8px; 30 | width: 14px; 31 | height: 36px; 32 | } 33 | 34 | .disclosure-button[aria-expanded="false"] .icon::after { 35 | content: '▶'; 36 | } 37 | 38 | .disclosure-button[aria-expanded="true"] .icon::after { 39 | content: '▼'; 40 | } 41 | 42 | .disclosure-content[aria-hidden="true"] { 43 | visibility: hidden; 44 | opacity: 0; 45 | } 46 | 47 | .disclosure-content[aria-hidden="false"] { 48 | visibility: visible; 49 | opacity: 1; 50 | } 51 | -------------------------------------------------------------------------------- /lesson4-semantics-navigating/08-link-text/main.css: -------------------------------------------------------------------------------- 1 | html, body { 2 | margin: 0; 3 | padding: 0; 4 | height: 100%; 5 | font-family: 'Roboto', 'Helvetica', 'Arial', sans-serif; 6 | font-size: 120%; 7 | } 8 | 9 | header { 10 | margin-bottom: 48px; 11 | border-bottom: 1px solid #AAA; 12 | } 13 | 14 | .container { 15 | max-width: 1000px; 16 | margin: 0 auto; 17 | } 18 | 19 | nav a.logo { 20 | flex: 1; 21 | } 22 | 23 | nav { 24 | display: flex; 25 | align-items: flex-end; 26 | } 27 | 28 | .tabs { 29 | float: right; 30 | } 31 | 32 | .tab { 33 | display: inline-block; 34 | background-color: #D2D2D2; 35 | padding: 10px 15px; 36 | margin: 0px 2px; 37 | } 38 | 39 | .tab-link { 40 | text-decoration: underline; 41 | cursor: pointer; 42 | } 43 | 44 | a { 45 | color: black; 46 | } 47 | 48 | ul.menu a, 49 | ul.menu button { 50 | float: right; 51 | } 52 | 53 | ul.menu button { 54 | display: inline; 55 | border: none; 56 | text-decoration: underline; 57 | font-family: inherit; 58 | font-size: inherit; 59 | background-color: inherit; 60 | padding: 0; 61 | } 62 | -------------------------------------------------------------------------------- /lesson4-semantics-navigating/08-link-text/solution/main.css: -------------------------------------------------------------------------------- 1 | html, body { 2 | margin: 0; 3 | padding: 0; 4 | height: 100%; 5 | font-family: 'Roboto', 'Helvetica', 'Arial', sans-serif; 6 | font-size: 120%; 7 | } 8 | 9 | header { 10 | margin-bottom: 48px; 11 | border-bottom: 1px solid #AAA; 12 | } 13 | 14 | .container { 15 | max-width: 1000px; 16 | margin: 0 auto; 17 | } 18 | 19 | nav a.logo { 20 | flex: 1; 21 | } 22 | 23 | nav { 24 | display: flex; 25 | align-items: flex-end; 26 | } 27 | 28 | .tabs { 29 | float: right; 30 | } 31 | 32 | .tab { 33 | display: inline-block; 34 | background-color: #D2D2D2; 35 | padding: 10px 15px; 36 | margin: 0px 2px; 37 | } 38 | 39 | .tab-link { 40 | text-decoration: underline; 41 | cursor: pointer; 42 | } 43 | 44 | a { 45 | color: black; 46 | } 47 | 48 | ul.menu a, 49 | ul.menu button { 50 | float: right; 51 | } 52 | 53 | ul.menu button { 54 | display: inline; 55 | border: none; 56 | text-decoration: underline; 57 | font-family: inherit; 58 | font-size: inherit; 59 | background-color: inherit; 60 | padding: 0; 61 | } 62 | -------------------------------------------------------------------------------- /lesson5-semantics-aria/06-radio-group/radiogroup.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 | 15 | 16 |
17 | 18 |

Drink Options

19 | 20 | 37 | 38 |
39 | 40 |
41 | 42 |
43 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /lesson5-semantics-aria/06-radio-group/solution/radiogroup.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 | 15 | 16 |
17 | 18 |

Drink Options

19 | 20 | 37 | 38 |
39 | 40 |
41 | 42 |
43 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /lesson5-semantics-aria/13-combobox/combobox.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Listbox ARIA example 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 |

Combo box example

14 | 31 |
32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /lesson5-semantics-aria/13-combobox/solution/combobox.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Listbox ARIA example 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 |

Combo box example

14 | 31 |
32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Udacity 4 | Portions copyright (c) 2015-2016 Google Inc. 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | -------------------------------------------------------------------------------- /final-project/ANSWERS.md: -------------------------------------------------------------------------------- 1 | 2 | - Missing responsive viewport tag 3 | - Students will need to add meta viewport with width=device-width and initial-scale=1 4 | - No lang set on 5 | - Student will need to add `lang=en`. This should get picked up by the a11y audit extension if they use it 6 | - `` has outline: none; 7 | - Students can either remove this, or replace it with their own outline styles 8 | - Body text is low contrast 9 | - Students need to change body color to at least #757575; The a11y audit tool will point this out 10 | - Subscribe form elements are labeled with divs 11 | - Student should replace these with `
13 |

Number input demo

14 |
15 | Current value 16 | 18 | 19 | 0 20 | 21 | 22 | 23 | 24 |
25 | Use "add" and "subtract" buttons, or up and down arrows, to add or subtract 10, respectively. 26 |
27 |
28 | 29 |

Demo inspired by Heydon Pickering's Practical ARIA examples

30 |
31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /lesson5-semantics-aria/03-first-steps/checkboxes.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 'use strict'; 3 | 4 | /** Define values for keycodes */ 5 | var VK_ENTER = 13; 6 | var VK_SPACE = 32; 7 | var VK_LEFT = 37; 8 | var VK_UP = 38; 9 | var VK_RIGHT = 39; 10 | var VK_DOWN = 40; 11 | 12 | /** Helper function to convert NodeLists to Arrays */ 13 | function slice(nodes) { 14 | return Array.prototype.slice.call(nodes); 15 | } 16 | 17 | function Checkbox(el) { 18 | this.el = el; 19 | 20 | this.el.addEventListener('keydown', this.handleKeyDown.bind(this)); 21 | this.el.addEventListener('click', this.toggle.bind(this)); 22 | 23 | // Any other set-up we want to do here? 24 | } 25 | 26 | Checkbox.prototype.handleKeyDown = function(e) { 27 | switch(e.keyCode) { 28 | case VK_ENTER: 29 | case VK_SPACE: { 30 | this.toggle(); 31 | break; 32 | } 33 | } 34 | }; 35 | 36 | Checkbox.prototype.toggle = function() { 37 | if (this.el.hasAttribute('checked')) { 38 | this.el.removeAttribute('checked'); 39 | 40 | // Hmm. 41 | 42 | } else { 43 | this.el.setAttribute('checked', ''); 44 | 45 | // Hmmmmm. 46 | 47 | } 48 | }; 49 | 50 | var checkboxes = slice(document.querySelectorAll('.checkbox')); 51 | for (var checkbox of checkboxes) 52 | checkbox.logic = new Checkbox(checkbox); 53 | 54 | }()); 55 | -------------------------------------------------------------------------------- /lesson3-semantics-built-in/02-chromevox-lite/chromevox-no-background.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 11 | 12 | 13 | 14 | 15 | 16 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /final-project/ecommerce-site/placeholder.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Udacity Lifestyle 7 | 8 | 9 | 10 | 21 | 22 | 23 | 24 | 35 |
36 |
37 |

Oh hi!

38 | A wallaby with a baby in its pouch 39 |

40 | This is a placeholder page. If you feel like building out the rest of 41 | the site that is extra credit ;) 42 |

43 | Take me back to the main page! 44 |
45 |
46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /final-project/ecommerce-site/solution/placeholder.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Udacity Lifestyle 7 | 8 | 9 | 10 | 21 | 22 | 23 | 24 | 35 |
36 |
37 |

Oh hi!

38 | A wallaby with a baby in its pouch 39 |

40 | This is a placeholder page. If you feel like building out the rest of 41 | the site that is extra credit ;) 42 |

43 | Take me back to the main page! 44 |
45 |
46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /lesson2-focus/07-modals-and-keyboard-traps/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Modals and Keyboard Traps 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /lesson2-focus/07-modals-and-keyboard-traps/solution/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Modals and Keyboard Traps 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /lesson1-overview/06-experiencing-screen-reader/basic.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Experiencing a screen reader 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 |
16 |
17 |

Click the button!

18 |
19 |
20 | 21 |
22 |
23 | 24 | 32 | 33 | 34 |
35 |
36 | 37 | 38 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /lesson5-semantics-aria/13-combobox/combobox.css: -------------------------------------------------------------------------------- 1 | .demo { 2 | margin: 50px; 3 | } 4 | 5 | .widget { 6 | display: inline-block; 7 | } 8 | 9 | button[aria-haspopup] { 10 | cursor: pointer; 11 | display: inline-block; 12 | user-select: none; 13 | background-color: #EEE; 14 | background-image: linear-gradient(#FCFCFC, #EEE); 15 | border: 1px solid #D5D5D5; 16 | border-radius: 3px; 17 | font-family: Roboto, Helvetica, Arial, sans serif; 18 | font-weight: bold; 19 | padding: 6px 10px; 20 | } 21 | 22 | [role=combobox] button::after { 23 | content: ""; 24 | display: inline-block; 25 | margin-left: 3px; 26 | position: relative; 27 | top: 3px; 28 | border-color: black transparent transparent; 29 | border-width: 5px 4px; 30 | border-style: solid; 31 | } 32 | 33 | [role=listbox] { 34 | border: 1px solid #D5D5D5; 35 | border-radius: 2px; 36 | position: absolute; 37 | font-family: Roboto, Helvetica, Arial, sans serif; 38 | font-size: 13px; 39 | padding: 5px; 40 | } 41 | 42 | [role=listbox] ul { 43 | list-style: none; 44 | margin: 0; 45 | padding: 0; 46 | } 47 | 48 | [role=listbox] li { 49 | padding: 2px 5px; 50 | margin: 2px; 51 | border-radius: 2px; 52 | } 53 | 54 | [role=option]:hover, 55 | [role=option]:focus, 56 | [role=option].active { 57 | outline: none; 58 | background-color: #EEE; 59 | } 60 | 61 | [role=option][aria-selected=true] { 62 | background-color: navy; 63 | color: white; 64 | } 65 | 66 | li.header { 67 | font-weight: bold; 68 | margin-left: -3px; 69 | margin-bottom: 3px; 70 | margin-top: 3px; 71 | } 72 | -------------------------------------------------------------------------------- /lesson5-semantics-aria/13-combobox/solution/combobox.css: -------------------------------------------------------------------------------- 1 | .demo { 2 | margin: 50px; 3 | } 4 | 5 | .widget { 6 | display: inline-block; 7 | } 8 | 9 | button[aria-haspopup] { 10 | cursor: pointer; 11 | display: inline-block; 12 | user-select: none; 13 | background-color: #EEE; 14 | background-image: linear-gradient(#FCFCFC, #EEE); 15 | border: 1px solid #D5D5D5; 16 | border-radius: 3px; 17 | font-family: Roboto, Helvetica, Arial, sans serif; 18 | font-weight: bold; 19 | padding: 6px 10px; 20 | } 21 | 22 | [role=combobox] button::after { 23 | content: ""; 24 | display: inline-block; 25 | margin-left: 3px; 26 | position: relative; 27 | top: 3px; 28 | border-color: black transparent transparent; 29 | border-width: 5px 4px; 30 | border-style: solid; 31 | } 32 | 33 | [role=listbox] { 34 | border: 1px solid #D5D5D5; 35 | border-radius: 2px; 36 | position: absolute; 37 | font-family: Roboto, Helvetica, Arial, sans serif; 38 | font-size: 13px; 39 | padding: 5px; 40 | } 41 | 42 | [role=listbox] ul { 43 | list-style: none; 44 | margin: 0; 45 | padding: 0; 46 | } 47 | 48 | [role=listbox] li { 49 | padding: 2px 5px; 50 | margin: 2px; 51 | border-radius: 2px; 52 | } 53 | 54 | [role=option]:hover, 55 | [role=option]:focus, 56 | [role=option].active { 57 | outline: none; 58 | background-color: #EEE; 59 | } 60 | 61 | [role=option][aria-selected=true] { 62 | background-color: navy; 63 | color: white; 64 | } 65 | 66 | li.header { 67 | font-weight: bold; 68 | margin-left: -3px; 69 | margin-bottom: 3px; 70 | margin-top: 3px; 71 | } 72 | -------------------------------------------------------------------------------- /lesson3-semantics-built-in/05-writing-semantic-html/wombats.js: -------------------------------------------------------------------------------- 1 | var wombatsButton = document.getElementById('wombats-button'); 2 | 3 | function handleClick(e) { 4 | if (e.target !== wombatsButton) 5 | return; 6 | document.body.classList.add('wombats'); 7 | document.getElementById('facts').style.visibility = 'visible'; 8 | } 9 | 10 | document.querySelector('main').addEventListener('click', handleClick); 11 | 12 | function addMutationObserver() { 13 | var main = document.querySelector('main'); 14 | var div = main.querySelector('div#wombats-button'); 15 | 16 | var divRemoved = false; 17 | var buttonAdded = false; 18 | 19 | function observe(records, self) { 20 | for (var record of records) { 21 | if (record.type != 'childList') 22 | continue; 23 | for (var i = 0; i < record.removedNodes.length; i++) { 24 | if (record.removedNodes[i] === div) { 25 | divRemoved = true; 26 | } 27 | } 28 | for (var i = 0; i < record.addedNodes.length; i++) { 29 | var addedNode = record.addedNodes[i]; 30 | if (addedNode.tagName === "BUTTON" && 31 | addedNode.parentElement === main && 32 | addedNode.className === "button" && 33 | addedNode.id === "wombats-button") { 34 | buttonAdded = true; 35 | } 36 | } 37 | if (divRemoved && buttonAdded) { 38 | document.querySelector('.secret').textContent = 'The secret word is: "MARSUPIAL"' 39 | document.querySelector('header').classList.add('success'); 40 | } 41 | } 42 | } 43 | var observer = new MutationObserver(observe); 44 | observer.observe(main, { childList: true, subtree: true }); 45 | } 46 | -------------------------------------------------------------------------------- /lesson6-styling/02-style-aria-states/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Styling with aria-states 7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 |
15 | 16 |

The Monty Hall problem

17 | 18 |

The Monty Hall problem is a brain teaser, in the form of a probability puzzle, loosely based on the American television game show Let's Make a Deal and named after its original host, Monty Hall. The problem was originally posed in a letter by Steve Selvin to the American Statistician in 1975.

19 | 20 |

Suppose you're on a game show, and you're given the choice of three doors: Behind one door is a car; behind the others, goats. You pick a door, say No. 1, and the host, who knows what's behind the doors, opens another door, say No. 3, which has a goat. He then says to you, "Do you want to pick door No. 2?" Is it to your advantage to switch your choice?

21 | 22 | 25 | 28 | 29 |
30 |
31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /lesson1-overview/06-experiencing-screen-reader/main.css: -------------------------------------------------------------------------------- 1 | html, body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: 'Roboto', 'Helvetica', 'Arial', sans-serif; 5 | } 6 | 7 | .wrapper { 8 | display: flex; 9 | flex-direction: column; 10 | min-height: 100vh; 11 | } 12 | 13 | main { 14 | display: flex; 15 | flex: 1; 16 | padding-left: 2em; 17 | align-items: center; 18 | justify-content: center; 19 | flex-direction: column; 20 | } 21 | 22 | * { 23 | box-sizing: border-box; 24 | } 25 | 26 | button { 27 | border: none; 28 | border-radius: 4px; 29 | font-family: inherit; 30 | font-weight: 100; 31 | font-size: 30px; 32 | background-color: #2196F3; 33 | color: white; 34 | padding: 0.25em 0.5em; 35 | display: inline-block; 36 | cursor: default; 37 | } 38 | 39 | .modal { 40 | width: 50%; 41 | border-radius: 4px; 42 | margin-left: auto; 43 | margin-right: auto; 44 | padding: 24px; 45 | background-color: #fff; 46 | z-index: 3; /* places the modal on top of everything */ 47 | position: fixed; 48 | top: 10%; 49 | left: 25%; 50 | display: none; 51 | align-items: center; 52 | justify-content: center; 53 | flex-direction: column; 54 | } 55 | 56 | .modal h1 { 57 | margin: 0; 58 | margin-bottom: 24px; 59 | } 60 | 61 | .modal-overlay { 62 | width: 100%; 63 | height: 100%; 64 | z-index: 2; /* places the modalOverlay between the main page and the modal dialog */ 65 | background-color: #000; 66 | opacity: 0.5; 67 | position: fixed; 68 | top: 0; 69 | left: 0; 70 | display: none; 71 | margin: 0; 72 | padding: 0; 73 | } 74 | 75 | .click-stealer { 76 | position: absolute; 77 | top: 0; 78 | right: 0; 79 | bottom: 0; 80 | left: 0; 81 | } 82 | -------------------------------------------------------------------------------- /lesson6-styling/02-style-aria-states/solution/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Styling with aria-states 7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 |
15 | 16 |

The Monty Hall problem

17 | 18 |

The Monty Hall problem is a brain teaser, in the form of a probability puzzle, loosely based on the American television game show Let's Make a Deal and named after its original host, Monty Hall. The problem was originally posed in a letter by Steve Selvin to the American Statistician in 1975.

19 | 20 |

Suppose you're on a game show, and you're given the choice of three doors: Behind one door is a car; behind the others, goats. You pick a door, say No. 1, and the host, who knows what's behind the doors, opens another door, say No. 3, which has a goat. He then says to you, "Do you want to pick door No. 2?" Is it to your advantage to switch your choice?

21 | 22 | 25 | 28 | 29 |
30 |
31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /lesson5-semantics-aria/03-first-steps/solution/checkboxes.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 'use strict'; 3 | 4 | /** Define values for keycodes */ 5 | var VK_ENTER = 13; 6 | var VK_SPACE = 32; 7 | var VK_LEFT = 37; 8 | var VK_UP = 38; 9 | var VK_RIGHT = 39; 10 | var VK_DOWN = 40; 11 | 12 | /** Helper function to convert NodeLists to Arrays */ 13 | function slice(nodes) { 14 | return Array.prototype.slice.call(nodes); 15 | } 16 | 17 | function Checkbox(el) { 18 | this.el = el; 19 | 20 | this.el.addEventListener('keydown', this.handleKeyDown.bind(this)); 21 | this.el.addEventListener('click', this.toggle.bind(this)); 22 | 23 | // Initialize role and aria-checked state. 24 | this.el.setAttribute('role', 'checkbox'); 25 | if (this.el.hasAttribute('checked')) { 26 | this.el.setAttribute('aria-checked', 'true'); 27 | } else { 28 | this.el.setAttribute('aria-checked', 'false'); 29 | } 30 | } 31 | 32 | Checkbox.prototype.handleKeyDown = function(e) { 33 | switch(e.keyCode) { 34 | case VK_ENTER: 35 | case VK_SPACE: { 36 | this.toggle(); 37 | break; 38 | } 39 | } 40 | }; 41 | 42 | Checkbox.prototype.toggle = function() { 43 | if (this.el.hasAttribute('checked')) { 44 | this.el.removeAttribute('checked'); 45 | 46 | // Keep checked attribute and aria-checked in sync. 47 | this.el.setAttribute('aria-checked', 'false'); 48 | } else { 49 | this.el.setAttribute('checked', ''); 50 | 51 | // Keep checked attribute and aria-checked in sync. 52 | this.el.setAttribute('aria-checked', 'true'); 53 | } 54 | }; 55 | 56 | var checkboxes = slice(document.querySelectorAll('.checkbox')); 57 | for (var checkbox of checkboxes) 58 | checkbox.logic = new Checkbox(checkbox); 59 | 60 | }()); 61 | -------------------------------------------------------------------------------- /lesson2-focus/05-radio-group/radiogroup.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 'use strict'; 3 | 4 | // Define values for keycodes 5 | var VK_ENTER = 13; 6 | var VK_SPACE = 32; 7 | var VK_LEFT = 37; 8 | var VK_UP = 38; 9 | var VK_RIGHT = 39; 10 | var VK_DOWN = 40; 11 | 12 | // Helper function to convert NodeLists to Arrays 13 | function slice(nodes) { 14 | return Array.prototype.slice.call(nodes); 15 | } 16 | 17 | function RadioGroup(id) { 18 | this.el = document.querySelector(id); 19 | this.buttons = slice(this.el.querySelectorAll('.radio')); 20 | this.focusedIdx = 0; 21 | this.focusedButton = this.buttons[this.focusedIdx]; 22 | 23 | this.el.addEventListener('keydown', this.handleKeyDown.bind(this)); 24 | } 25 | 26 | RadioGroup.prototype.handleKeyDown = function(e) { 27 | switch(e.keyCode) { 28 | 29 | case VK_UP: 30 | case VK_LEFT: { 31 | 32 | e.preventDefault(); 33 | 34 | // This seems like a good place to do some stuff :) 35 | 36 | break; 37 | 38 | } 39 | 40 | case VK_DOWN: 41 | case VK_RIGHT: { 42 | 43 | e.preventDefault(); 44 | 45 | // This seems like a good place to do some stuff :) 46 | 47 | break; 48 | } 49 | 50 | } 51 | 52 | this.changeFocus(this.focusedIdx); // <-- Hmm, interesting... 53 | }; 54 | 55 | RadioGroup.prototype.changeFocus = function(idx) { 56 | // Set the old button to tabindex -1 57 | this.focusedButton.tabIndex = -1; 58 | this.focusedButton.removeAttribute('checked'); 59 | 60 | // Set the new button to tabindex 0 and focus it 61 | this.focusedButton = this.buttons[idx]; 62 | this.focusedButton.tabIndex = 0; 63 | this.focusedButton.focus(); 64 | this.focusedButton.setAttribute('checked', 'checked'); 65 | }; 66 | 67 | var group1 = new RadioGroup('#group1'); 68 | 69 | }()); 70 | -------------------------------------------------------------------------------- /styles/components.css: -------------------------------------------------------------------------------- 1 | .mdl-button { 2 | background: transparent; 3 | border: none; 4 | border-radius: 2px; 5 | color: #000; 6 | position: relative; 7 | height: 36px; 8 | min-width: 64px; 9 | padding: 0 16px; 10 | display: inline-block; 11 | font-family: "Roboto", "Helvetica", "Arial", sans-serif; 12 | font-size: 14px; 13 | font-weight: 500; 14 | text-transform: uppercase; 15 | letter-spacing: 0; 16 | overflow: hidden; 17 | cursor: pointer; 18 | text-decoration: none; 19 | text-align: center; 20 | line-height: 36px; 21 | vertical-align: middle; 22 | } 23 | .mdl-button.raised { 24 | box-shadow: 0 2px 2px 0 rgba(0, 0, 0, .14), 0 3px 1px -2px rgba(0, 0, 0, .2), 0 1px 5px 0 rgba(0, 0, 0, .12); 25 | background: rgba(158, 158, 158, .2); 26 | } 27 | .card { 28 | font-size: 16px; 29 | font-weight: 400; 30 | z-index: 1; 31 | position: relative; 32 | background: #fff; 33 | border-radius: 2px; 34 | box-sizing: border-box; 35 | box-shadow: 0 2px 2px 0 rgba(0, 0, 0, .14), 0 3px 1px -2px rgba(0, 0, 0, .2), 0 1px 5px 0 rgba(0, 0, 0, .12); 36 | } 37 | .layout-horizontal { 38 | display: -webkit-flex; 39 | display: -ms-flexbox; 40 | display: flex; 41 | -webkit-box-orient: horizontal; 42 | -webkit-box-direction: normal; 43 | -webkit-flex-direction: row; 44 | -ms-flex-direction: row; 45 | flex-direction: row; 46 | } 47 | .layout-vertical { 48 | -webkit-box-orient: vertical; 49 | -webkit-box-direction: normal; 50 | -webkit-flex-direction: column; 51 | -ms-flex-direction: column; 52 | flex-direction: column; 53 | } 54 | .flex { 55 | -webkit-box-flex: 1; 56 | -webkit-flex: 1; 57 | -ms-flex: 1; 58 | flex: 1; 59 | } 60 | .center { 61 | -webkit-box-pack: center; 62 | -webkit-justify-content: center; 63 | -ms-flex-pack: center; 64 | justify-content: center; 65 | -webkit-box-align: center; 66 | -webkit-align-items: center; 67 | -ms-flex-align: center; 68 | align-items: center; 69 | } 70 | -------------------------------------------------------------------------------- /final-project/ecommerce-site/solution/scripts/number.js: -------------------------------------------------------------------------------- 1 | var VK_UP = 38; 2 | var VK_DOWN = 40; 3 | 4 | var announcer = document.querySelector('#announcer'); 5 | 6 | function announce(text) { 7 | announcer.textContent = text; 8 | } 9 | 10 | function SpinButton(spinbutton) { 11 | this._el = spinbutton; 12 | this._minValue = parseInt(spinbutton.getAttribute('aria-valuemin'), 10); 13 | this._maxValue = parseInt(spinbutton.getAttribute('aria-valuemax'), 10); 14 | this._number = spinbutton.querySelector('.number'); 15 | this._number.value = 0; 16 | this._add = spinbutton.querySelector('.add'); 17 | this._subtract = spinbutton.querySelector('.subtract'); 18 | 19 | this._add.addEventListener('click', this.increment.bind(this)); 20 | this._subtract.addEventListener('click', this.decrement.bind(this)); 21 | this._el.addEventListener('keydown', this.onKeydown.bind(this)); 22 | } 23 | 24 | SpinButton.prototype = { 25 | updateValue: function(increment) { 26 | var oldValue = this._number.textContent; 27 | var newValue = parseInt(oldValue, 10) + increment; 28 | if (newValue < this._minValue || newValue > this._maxValue) 29 | return; 30 | this._number.textContent = newValue; 31 | this._el.setAttribute('aria-valuenow', newValue); 32 | announce(newValue); 33 | }, 34 | 35 | increment: function() { 36 | this.updateValue(1); 37 | }, 38 | 39 | decrement: function() { 40 | this.updateValue(-1); 41 | }, 42 | 43 | onKeydown: function(e) { 44 | switch (e.keyCode) { 45 | case VK_UP: 46 | this.increment(); 47 | e.stopPropagation(); 48 | e.preventDefault(); 49 | break; 50 | case VK_DOWN: 51 | this.decrement(); 52 | e.stopPropagation(); 53 | e.preventDefault(); 54 | break; 55 | } 56 | } 57 | } 58 | 59 | 60 | var spinbuttons = document.querySelectorAll('.spinbutton'); 61 | for (var i = 0; i < spinbuttons.length; i++) { 62 | new SpinButton(spinbuttons[i]); 63 | } 64 | -------------------------------------------------------------------------------- /lesson3-semantics-built-in/02-chromevox-lite/chromevox-no-background-off.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 30 | 31 | 32 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /lesson2-focus/05-radio-group/solution/radiogroup.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 'use strict'; 3 | 4 | // Define values for keycodes 5 | var VK_ENTER = 13; 6 | var VK_SPACE = 32; 7 | var VK_LEFT = 37; 8 | var VK_UP = 38; 9 | var VK_RIGHT = 39; 10 | var VK_DOWN = 40; 11 | 12 | // Helper function to convert NodeLists to Arrays 13 | function slice(nodes) { 14 | return Array.prototype.slice.call(nodes); 15 | } 16 | 17 | function RadioGroup(id) { 18 | this.el = document.querySelector(id); 19 | this.buttons = slice(this.el.querySelectorAll('.radio')); 20 | this.focusedIdx = 0; 21 | this.focusedButton = this.buttons[this.focusedIdx]; 22 | 23 | this.el.addEventListener('keydown', this.handleKeyDown.bind(this)); 24 | } 25 | 26 | RadioGroup.prototype.handleKeyDown = function(e) { 27 | switch(e.keyCode) { 28 | 29 | case VK_UP: 30 | case VK_LEFT: { 31 | 32 | e.preventDefault(); 33 | 34 | if (this.focusedIdx === 0) { 35 | this.focusedIdx = this.buttons.length - 1; 36 | } else { 37 | this.focusedIdx--; 38 | } 39 | 40 | break; 41 | 42 | } 43 | 44 | case VK_DOWN: 45 | case VK_RIGHT: { 46 | 47 | e.preventDefault(); 48 | 49 | if (this.focusedIdx === this.buttons.length - 1) { 50 | this.focusedIdx = 0; 51 | } else { 52 | this.focusedIdx++; 53 | } 54 | 55 | break; 56 | } 57 | 58 | } 59 | 60 | this.changeFocus(this.focusedIdx); 61 | }; 62 | 63 | RadioGroup.prototype.changeFocus = function(idx) { 64 | // Set the old button to tabindex -1 65 | this.focusedButton.tabIndex = -1; 66 | this.focusedButton.removeAttribute('checked'); 67 | 68 | // Set the new button to tabindex 0 and focus it 69 | this.focusedButton = this.buttons[idx]; 70 | this.focusedButton.tabIndex = 0; 71 | this.focusedButton.focus(); 72 | this.focusedButton.setAttribute('checked', 'checked'); 73 | }; 74 | 75 | var group1 = new RadioGroup('#group1'); 76 | 77 | }()); 78 | -------------------------------------------------------------------------------- /lesson4-semantics-navigating/08-link-text/solution/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Bondi Brunch 5 | 6 | 7 | 8 |
9 | 17 | 18 |
19 |
20 |
21 | 24 |
25 |

Pre-order Brunch

26 |

Or, order dinner instead.

27 | 46 |
47 |
48 |
49 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /lesson3-semantics-built-in/05-writing-semantic-html/main.css: -------------------------------------------------------------------------------- 1 | html, body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: 'Roboto', 'Helvetica', 'Arial', sans-serif; 5 | } 6 | 7 | .wrapper { 8 | display: flex; 9 | flex-direction: column; 10 | min-height: 100vh; 11 | } 12 | 13 | body.wombats { 14 | background-image: url('./wombat.jpg'); /* CC-BY Kristian Thøgersen https://flic.kr/p/9258io */ 15 | background-size: cover; 16 | background-position: bottom right; 17 | } 18 | 19 | header { 20 | width: 100vw; 21 | display: flex; 22 | justify-content: center; 23 | padding: 10px; 24 | background-color: rgba(255, 255, 255, 0.8); 25 | visibility: hidden; 26 | } 27 | 28 | header.success { 29 | visibility: visible; 30 | } 31 | 32 | header .secret { 33 | display: block; 34 | } 35 | 36 | main { 37 | display: flex; 38 | flex: 1; 39 | padding-left: 2em; 40 | align-items: center; 41 | justify-content: center; 42 | } 43 | 44 | * { 45 | box-sizing: border-box; 46 | } 47 | 48 | .button, button { 49 | border: none; 50 | border-radius: 4px; 51 | font-family: inherit; 52 | font-weight: 100; 53 | font-size: 30px; 54 | background-color: #2196F3; 55 | color: white; 56 | padding: 0.25em 0.5em; 57 | display: inline-block; 58 | cursor: default; 59 | } 60 | 61 | footer { 62 | width: 100vw; 63 | display: flex; 64 | justify-content: center; 65 | padding: 10px; 66 | padding-bottom: 60px; 67 | visibility: hidden; 68 | background-color: rgba(255, 255, 255, 0.3); 69 | } 70 | 71 | .credit { 72 | text-shadow: 1px 1px 2px white; 73 | } 74 | 75 | body.wombats footer { 76 | visibility: visible; 77 | } 78 | 79 | .easter-egg { 80 | position: absolute; 81 | top: -10000px; 82 | left: -10000px; 83 | width: 1px; 84 | height: 1px; 85 | overflow: hidden; 86 | } 87 | 88 | body[cvox-enabled] .wrapper { 89 | -webkit-filter: blur(10px); 90 | -moz-filter: blur(10px); 91 | -o-filter: blur(10px); 92 | -ms-filter: blur(10px); 93 | filter: blur(10px); 94 | } 95 | -------------------------------------------------------------------------------- /final-project/ecommerce-site/scripts/number.js: -------------------------------------------------------------------------------- 1 | var VK_UP = 38; 2 | var VK_DOWN = 40; 3 | 4 | // Alert the user to the given new value for the number input 5 | function alertNewValue(newValue) { 6 | // FIXME: implement 7 | // hint: document.querySelector('#number-live'); 8 | } 9 | 10 | function SpinButton(spinbutton) { 11 | this._el = spinbutton; 12 | this._minValue = parseInt(spinbutton.getAttribute('aria-valuemin'), 10); 13 | this._maxValue = parseInt(spinbutton.getAttribute('aria-valuemax'), 10); 14 | this._number = spinbutton.querySelector('.number'); 15 | this._number.value = 0; 16 | this._add = spinbutton.querySelector('.add'); 17 | this._subtract = spinbutton.querySelector('.subtract'); 18 | 19 | this._add.addEventListener('click', this.increment.bind(this)); 20 | this._subtract.addEventListener('click', this.decrement.bind(this)); 21 | this._el.addEventListener('keydown', this.onKeydown.bind(this)); 22 | } 23 | 24 | SpinButton.prototype = { 25 | updateValue: function(increment) { 26 | var oldValue = this._number.textContent; 27 | var newValue = parseInt(oldValue, 10) + increment; 28 | if (newValue < this._minValue || newValue > this._maxValue) 29 | return; 30 | this._number.textContent = newValue; 31 | this._el.setAttribute('aria-valuenow', newValue); 32 | 33 | // Work around some screen readers not automatically speaking updated value 34 | alertNewValue(newValue); 35 | }, 36 | 37 | increment: function() { 38 | this.updateValue(1); 39 | }, 40 | 41 | decrement: function() { 42 | this.updateValue(-1); 43 | }, 44 | 45 | onKeydown: function(e) { 46 | switch (e.keyCode) { 47 | case VK_UP: 48 | this.increment(); 49 | e.stopPropagation(); 50 | e.preventDefault(); 51 | break; 52 | case VK_DOWN: 53 | this.decrement(); 54 | e.stopPropagation(); 55 | e.preventDefault(); 56 | break; 57 | } 58 | } 59 | } 60 | 61 | 62 | var spinbuttons = document.querySelectorAll('.spinbutton'); 63 | for (var i = 0; i < spinbuttons.length; i++) { 64 | new SpinButton(spinbuttons[i]); 65 | } 66 | -------------------------------------------------------------------------------- /lesson3-semantics-built-in/05-writing-semantic-html/wombats.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Writing semantic HTML 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 |
16 |
17 |
18 |
19 |
Give me wombats
20 |
21 | 24 |
25 | 38 |
39 |
40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /lesson4-semantics-navigating/08-link-text/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Bondi Brunch 5 | 6 | 7 | 8 |
9 | 18 |
19 |
20 |
21 | 24 |
25 |

Pre-order Brunch

26 |

To order dinner, click here.

27 | 46 |
47 |
48 |
49 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /lesson2-focus/07-modals-and-keyboard-traps/modal.js: -------------------------------------------------------------------------------- 1 | // Will hold previously focused element 2 | var focusedElementBeforeModal; 3 | 4 | // Find the modal and its overlay 5 | var modal = document.querySelector('.modal'); 6 | var modalOverlay = document.querySelector('.modal-overlay'); 7 | 8 | var modalToggle = document.querySelector('.modal-toggle'); 9 | modalToggle.addEventListener('click', openModal); 10 | 11 | function openModal() { 12 | // Save current focus 13 | focusedElementBeforeModal = document.activeElement; 14 | 15 | // Listen for and trap the keyboard 16 | modal.addEventListener('keydown', trapTabKey); 17 | 18 | // Listen for indicators to close the modal 19 | modalOverlay.addEventListener('click', closeModal); 20 | // Sign-Up button 21 | var signUpBtn = modal.querySelector('#signup'); 22 | signUpBtn.addEventListener('click', closeModal); 23 | 24 | // Find all focusable children 25 | var focusableElementsString = 'a[href], area[href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), button:not([disabled]), iframe, object, embed, [tabindex="0"], [contenteditable]'; 26 | var focusableElements = modal.querySelectorAll(focusableElementsString); 27 | // Convert NodeList to Array 28 | focusableElements = Array.prototype.slice.call(focusableElements); 29 | 30 | var firstTabStop = focusableElements[0]; 31 | var lastTabStop = focusableElements[focusableElements.length - 1]; 32 | 33 | // Show the modal and overlay 34 | modal.style.display = 'block'; 35 | modalOverlay.style.display = 'block'; 36 | 37 | // Focus first child 38 | firstTabStop.focus(); 39 | 40 | function trapTabKey(e) { 41 | // Check for TAB key press 42 | if (e.keyCode === 9) { 43 | 44 | // SHIFT + TAB 45 | if (e.shiftKey) { 46 | 47 | // TAB 48 | } else { 49 | 50 | } 51 | } 52 | 53 | // ESCAPE 54 | if (e.keyCode === 27) { 55 | 56 | } 57 | } 58 | } 59 | 60 | function closeModal() { 61 | // Hide the modal and overlay 62 | modal.style.display = 'none'; 63 | modalOverlay.style.display = 'none'; 64 | 65 | // Set focus back to element that had it before the modal was opened 66 | focusedElementBeforeModal.focus(); 67 | } 68 | -------------------------------------------------------------------------------- /lesson2-focus/07-modals-and-keyboard-traps/solution/modal.js: -------------------------------------------------------------------------------- 1 | // Will hold previously focused element 2 | var focusedElementBeforeModal; 3 | 4 | // Find the modal and its overlay 5 | var modal = document.querySelector('.modal'); 6 | var modalOverlay = document.querySelector('.modal-overlay'); 7 | 8 | var modalToggle = document.querySelector('.modal-toggle'); 9 | modalToggle.addEventListener('click', openModal); 10 | 11 | function openModal() { 12 | // Save current focus 13 | focusedElementBeforeModal = document.activeElement; 14 | 15 | // Listen for and trap the keyboard 16 | modal.addEventListener('keydown', trapTabKey); 17 | 18 | // Listen for indicators to close the modal 19 | modalOverlay.addEventListener('click', closeModal); 20 | // Sign-Up button 21 | var signUpBtn = modal.querySelector('#signup'); 22 | signUpBtn.addEventListener('click', closeModal); 23 | 24 | // Find all focusable children 25 | var focusableElementsString = 'a[href], area[href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), button:not([disabled]), iframe, object, embed, [tabindex="0"], [contenteditable]'; 26 | var focusableElements = modal.querySelectorAll(focusableElementsString); 27 | // Convert NodeList to Array 28 | focusableElements = Array.prototype.slice.call(focusableElements); 29 | 30 | var firstTabStop = focusableElements[0]; 31 | var lastTabStop = focusableElements[focusableElements.length - 1]; 32 | 33 | // Show the modal and overlay 34 | modal.style.display = 'block'; 35 | modalOverlay.style.display = 'block'; 36 | 37 | // Focus first child 38 | firstTabStop.focus(); 39 | 40 | function trapTabKey(e) { 41 | // Check for TAB key press 42 | if (e.keyCode === 9) { 43 | 44 | // SHIFT + TAB 45 | if (e.shiftKey) { 46 | if (document.activeElement === firstTabStop) { 47 | e.preventDefault(); 48 | lastTabStop.focus(); 49 | } 50 | 51 | // TAB 52 | } else { 53 | if (document.activeElement === lastTabStop) { 54 | e.preventDefault(); 55 | firstTabStop.focus(); 56 | } 57 | } 58 | } 59 | 60 | // ESCAPE 61 | if (e.keyCode === 27) { 62 | closeModal(); 63 | } 64 | } 65 | } 66 | 67 | function closeModal() { 68 | // Hide the modal and overlay 69 | modal.style.display = 'none'; 70 | modalOverlay.style.display = 'none'; 71 | 72 | // Set focus back to element that had it before the modal was opened 73 | focusedElementBeforeModal.focus(); 74 | } 75 | -------------------------------------------------------------------------------- /lesson1-overview/06-experiencing-screen-reader/modal.js: -------------------------------------------------------------------------------- 1 | // Will hold previously focused element 2 | var focusedElementBeforeModal; 3 | 4 | // Find the modal and its overlay 5 | var modal = document.querySelector('.modal'); 6 | var modalOverlay = document.querySelector('.modal-overlay'); 7 | 8 | function openModal() { 9 | // Save current focus 10 | focusedElementBeforeModal = document.activeElement; 11 | 12 | // Listen for and trap the keyboard 13 | modal.addEventListener('keydown', trapTabKey); 14 | 15 | // Listen for indicators to close the modal 16 | modalOverlay.addEventListener('click', closeModal); 17 | 18 | // close button 19 | var closeButton = modal.querySelector('#close'); 20 | closeButton.addEventListener('click', closeModal); 21 | 22 | // Find all focusable children 23 | var focusableElementsString = 'a[href], area[href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), button:not([disabled]), iframe, object, embed, [tabindex="0"], [contenteditable]'; 24 | var focusableElements = modal.querySelectorAll(focusableElementsString); 25 | // Convert NodeList to Array 26 | focusableElements = Array.prototype.slice.call(focusableElements); 27 | 28 | var firstTabStop = focusableElements[0]; 29 | var lastTabStop = focusableElements[focusableElements.length - 1]; 30 | 31 | // Show the modal and overlay 32 | modal.style.display = 'flex'; 33 | modalOverlay.style.display = 'block'; 34 | 35 | // Focus image 36 | window.setTimeout(function() { 37 | document.querySelector('#koala').focus(); 38 | console.log('focused', document.activeElement); 39 | }, 10); 40 | 41 | function trapTabKey(e) { 42 | // Check for TAB key press 43 | if (e.keyCode === 9) { 44 | 45 | // SHIFT + TAB 46 | if (e.shiftKey) { 47 | if (document.activeElement === firstTabStop) { 48 | e.preventDefault(); 49 | lastTabStop.focus(); 50 | } 51 | 52 | // TAB 53 | } else { 54 | if (document.activeElement === lastTabStop) { 55 | e.preventDefault(); 56 | firstTabStop.focus(); 57 | } 58 | } 59 | } 60 | 61 | // ESCAPE 62 | if (e.keyCode === 27) { 63 | closeModal(); 64 | } 65 | } 66 | } 67 | 68 | function closeModal() { 69 | // Hide the modal and overlay 70 | modal.style.display = 'none'; 71 | modalOverlay.style.display = 'none'; 72 | 73 | // Set focus back to element that had it before the modal was opened 74 | focusedElementBeforeModal.focus(); 75 | } 76 | -------------------------------------------------------------------------------- /lesson5-semantics-aria/21-dialog/modal.js: -------------------------------------------------------------------------------- 1 | // Will hold previously focused element 2 | var focusedElementBeforeModal; 3 | 4 | // Find the modal and its overlay 5 | var modal = document.querySelector('.modal'); 6 | var modalOverlay = document.querySelector('.modal-overlay'); 7 | 8 | var modalToggle = document.querySelector('.modal-toggle'); 9 | modalToggle.addEventListener('click', openModal); 10 | 11 | function openModal(e) { 12 | // Save current focus 13 | focusedElementBeforeModal = e.target; 14 | 15 | // Listen for and trap the keyboard 16 | modal.addEventListener('keydown', trapTabKey); 17 | 18 | // Listen for indicators to close the modal 19 | modalOverlay.addEventListener('click', closeModal); 20 | // Log In button 21 | var closeButton = modal.querySelector('#close'); 22 | closeButton.addEventListener('click', closeModal); 23 | 24 | // Find all focusable children 25 | var focusableElementsString = 'a[href], area[href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), button:not([disabled]), iframe, object, embed, [tabindex="0"], [contenteditable]'; 26 | var focusableElements = modal.querySelectorAll(focusableElementsString); 27 | // Convert NodeList to Array 28 | focusableElements = Array.prototype.slice.call(focusableElements); 29 | 30 | var firstTabStop = focusableElements[0]; 31 | var lastTabStop = focusableElements[focusableElements.length - 1]; 32 | 33 | // Show the modal and overlay 34 | modal.style.display = 'block'; 35 | modalOverlay.style.display = 'block'; 36 | 37 | // Focus first child 38 | firstTabStop.focus(); 39 | 40 | function trapTabKey(e) { 41 | // Check for TAB key press 42 | if (e.keyCode === 9) { 43 | 44 | // SHIFT + TAB 45 | if (e.shiftKey) { 46 | if (document.activeElement === firstTabStop) { 47 | e.preventDefault(); 48 | lastTabStop.focus(); 49 | } 50 | 51 | // TAB 52 | } else { 53 | if (document.activeElement === lastTabStop) { 54 | e.preventDefault(); 55 | firstTabStop.focus(); 56 | } 57 | } 58 | } 59 | 60 | // ESCAPE 61 | if (e.keyCode === 27) { 62 | closeModal(); 63 | } 64 | } 65 | 66 | // FIXME: hide non-modal content from screen readers 67 | } 68 | 69 | function closeModal() { 70 | // Hide the modal and overlay 71 | modal.style.display = 'none'; 72 | modalOverlay.style.display = 'none'; 73 | 74 | // Set focus back to element that had it before the modal was opened 75 | focusedElementBeforeModal.focus(); 76 | 77 | // FIXME: don't forget to make main content screen reader accessible again. 78 | } 79 | -------------------------------------------------------------------------------- /lesson5-semantics-aria/21-dialog/solution/modal.js: -------------------------------------------------------------------------------- 1 | // Will hold previously focused element 2 | var focusedElementBeforeModal; 3 | 4 | // Find the modal and its overlay 5 | var modal = document.querySelector('.modal'); 6 | var modalOverlay = document.querySelector('.modal-overlay'); 7 | 8 | var modalToggle = document.querySelector('.modal-toggle'); 9 | modalToggle.addEventListener('click', openModal); 10 | 11 | function openModal() { 12 | // Save current focus 13 | focusedElementBeforeModal = document.activeElement; 14 | 15 | // Listen for and trap the keyboard 16 | modal.addEventListener('keydown', trapTabKey); 17 | 18 | // Listen for indicators to close the modal 19 | modalOverlay.addEventListener('click', closeModal); 20 | // Sign-Up button 21 | var signUpBtn = modal.querySelector('#signup'); 22 | signUpBtn.addEventListener('click', closeModal); 23 | 24 | // Find all focusable children 25 | var focusableElementsString = 'a[href], area[href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), button:not([disabled]), iframe, object, embed, [tabindex="0"], [contenteditable]'; 26 | var focusableElements = modal.querySelectorAll(focusableElementsString); 27 | // Convert NodeList to Array 28 | focusableElements = Array.prototype.slice.call(focusableElements); 29 | 30 | var firstTabStop = focusableElements[0]; 31 | var lastTabStop = focusableElements[focusableElements.length - 1]; 32 | 33 | // Show the modal and overlay 34 | modal.style.display = 'block'; 35 | modalOverlay.style.display = 'block'; 36 | 37 | // Focus first child 38 | firstTabStop.focus(); 39 | 40 | function trapTabKey(e) { 41 | // Check for TAB key press 42 | if (e.keyCode === 9) { 43 | 44 | // SHIFT + TAB 45 | if (e.shiftKey) { 46 | if (document.activeElement === firstTabStop) { 47 | e.preventDefault(); 48 | lastTabStop.focus(); 49 | } 50 | 51 | // TAB 52 | } else { 53 | if (document.activeElement === lastTabStop) { 54 | e.preventDefault(); 55 | firstTabStop.focus(); 56 | } 57 | } 58 | } 59 | 60 | // ESCAPE 61 | if (e.keyCode === 27) { 62 | closeModal(); 63 | } 64 | } 65 | 66 | document.querySelector('.wrapper').setAttribute('aria-hidden', true); 67 | } 68 | 69 | function closeModal() { 70 | // Hide the modal and overlay 71 | modal.style.display = 'none'; 72 | modalOverlay.style.display = 'none'; 73 | 74 | // Set focus back to element that had it before the modal was opened 75 | focusedElementBeforeModal.focus(); 76 | 77 | document.querySelector('.wrapper').removeAttribute('aria-hidden'); 78 | } 79 | -------------------------------------------------------------------------------- /lesson6-styling/01-focus-styles/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Focus Styles 7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 | 15 |
16 | 26 |
27 | 28 |
29 |

Marketing stuff!

30 |

Cras justo odio, dapibus ac facilisis in, egestas eget quam. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet.

31 |

Get started today

32 |
33 | 34 |
35 |
36 |

Heading

37 |

Donec id elit non mi porta gravida at eget metus. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Etiam porta sem malesuada magna mollis euismod. Donec sed odio dui.

38 |

View details »

39 |
40 |
41 |

Heading

42 |

Donec id elit non mi porta gravida at eget metus. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Etiam porta sem malesuada magna mollis euismod. Donec sed odio dui.

43 |

View details »

44 |
45 |
46 |

Heading

47 |

Donec sed odio dui. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Vestibulum id ligula porta felis euismod semper. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa.

48 |

View details »

49 |
50 |
51 | 52 |
53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /lesson6-styling/01-focus-styles/solution/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Focus Styles 7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 | 15 |
16 | 26 |
27 | 28 |
29 |

Marketing stuff!

30 |

Cras justo odio, dapibus ac facilisis in, egestas eget quam. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet.

31 |

Get started today

32 |
33 | 34 |
35 |
36 |

Heading

37 |

Donec id elit non mi porta gravida at eget metus. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Etiam porta sem malesuada magna mollis euismod. Donec sed odio dui.

38 |

View details »

39 |
40 |
41 |

Heading

42 |

Donec id elit non mi porta gravida at eget metus. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Etiam porta sem malesuada magna mollis euismod. Donec sed odio dui.

43 |

View details »

44 |
45 |
46 |

Heading

47 |

Donec sed odio dui. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Vestibulum id ligula porta felis euismod semper. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa.

48 |

View details »

49 |
50 |
51 | 52 |
53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /lesson3-semantics-built-in/03-experience-screen-reader/contact.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Contact Udacity Airlines 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 |
20 |
21 |

Udacity 22 | Down Under 23 |

24 |
25 |
26 | 27 |
28 |
29 |
30 |
31 |

Contact us

32 |
33 |
34 | 35 | 36 |
37 |
38 | 39 | 40 |
41 |
42 | 43 |
44 | 45 | 46 |
47 | 48 |
49 |
50 | 51 |
52 | 53 |
54 |
55 |
56 |
57 |
58 |
59 | 60 | 76 |
77 |
78 | 79 | 87 | 88 | 89 | 90 | -------------------------------------------------------------------------------- /final-project/ecommerce-site/scripts/stars.js: -------------------------------------------------------------------------------- 1 | var VK_PGUP = 33; 2 | var VK_PGDN = 34; 3 | var VK_END = 35; 4 | var VK_HOME = 36; 5 | var VK_LEFT = 37; 6 | var VK_UP = 38; 7 | var VK_RIGHT = 39; 8 | var VK_DOWN = 40; 9 | 10 | function Stars(stars) { 11 | this._el = stars; 12 | this._stars = Array.prototype.slice.call(stars.querySelectorAll('.star')); 13 | for (var star of this._stars) { 14 | star.addEventListener('mouseenter', this.hoverStart.bind(this)); 15 | star.addEventListener('mouseleave', this.hoverEnd.bind(this)); 16 | star.addEventListener('click', this.select.bind(this)); 17 | } 18 | 19 | this._minValue = 0; 20 | this._maxValue = this._stars.length; 21 | 22 | this.setValue(0); 23 | 24 | // FIXME: Set role 25 | // hint: https://www.w3.org/TR/wai-aria/roles 26 | // hint: "A user input where the user selects a value from within a given range" 27 | 28 | // FIXME: Reflect minimum and maximum value for assistive technology 29 | // hint: https://www.w3.org/TR/wai-aria-practices-1.1/ 30 | 31 | // FIXME: Keyboard event handling 32 | // hint: https://www.w3.org/TR/wai-aria-practices-1.1/ 33 | } 34 | 35 | Stars.prototype = { 36 | showStars: function(numStarsOn) { 37 | this._stars.forEach(function(star, i) { 38 | if (i < numStarsOn) 39 | star.querySelector('img').src = 'images/star-on.png'; 40 | else 41 | star.querySelector('img').src = 'images/star-off.png'; 42 | }); 43 | }, 44 | 45 | set currentValue(value) { 46 | this._value = value; 47 | // FIXME: reflect current value for assistive technology 48 | // FIXME: provide value text for assistive technology: "No stars", "1 star" etc. 49 | }, 50 | 51 | get currentValue() { 52 | return this._value; 53 | }, 54 | 55 | setValue: function(value) { 56 | this.currentValue = value; 57 | this.showStars(value); 58 | }, 59 | 60 | increment: function() { 61 | var currentValue = this.currentValue; 62 | if (currentValue === this._maxValue) 63 | return; 64 | this.currentValue = currentValue + 1; 65 | }, 66 | 67 | decrement: function() { 68 | var currentValue = this.currentValue; 69 | if (currentValue === this._minValue) 70 | return; 71 | this.currentValue = currentValue - 1; 72 | }, 73 | 74 | select: function(e) { 75 | var star = e.currentTarget; 76 | var index = this._stars.indexOf(star); 77 | this.setValue(index + 1); 78 | }, 79 | 80 | hoverStart: function(e) { 81 | var star = e.currentTarget; 82 | var index = this._stars.indexOf(star); 83 | this.showStars(index + 1); 84 | }, 85 | 86 | hoverEnd: function(e) { 87 | this.showStars(this.currentValue); 88 | } 89 | } 90 | 91 | 92 | var starWidgets = document.querySelectorAll('.stars'); 93 | for (var i = 0; i < starWidgets.length; i++) { 94 | new Stars(starWidgets[i]); 95 | } 96 | -------------------------------------------------------------------------------- /lesson5-semantics-aria/06-radio-group/radiogroup.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 'use strict'; 3 | 4 | // Define values for keycodes 5 | var VK_ENTER = 13; 6 | var VK_SPACE = 32; 7 | var VK_LEFT = 37; 8 | var VK_UP = 38; 9 | var VK_RIGHT = 39; 10 | var VK_DOWN = 40; 11 | 12 | // Helper function to convert NodeLists to Arrays 13 | function slice(nodes) { 14 | return Array.prototype.slice.call(nodes); 15 | } 16 | 17 | function RadioGroup(id) { 18 | this.el = document.querySelector(id); 19 | this.buttons = slice(this.el.querySelectorAll('.radio')); 20 | this.focusedIdx = 0; 21 | this.focusedButton = this.buttons[this.focusedIdx]; 22 | 23 | this.el.addEventListener('keydown', this.handleKeyDown.bind(this)); 24 | this.el.addEventListener('click', this.handleClick.bind(this)); 25 | 26 | // Any more initialization to do here? 27 | 28 | var firstButton = true; 29 | for (var button of this.buttons) { 30 | if (firstButton) { 31 | button.tabIndex = "0"; 32 | firstButton = false; 33 | } else { 34 | button.tabIndex = "-1"; 35 | } 36 | 37 | // What about here? 38 | } 39 | 40 | } 41 | 42 | RadioGroup.prototype.handleKeyDown = function(e) { 43 | switch(e.keyCode) { 44 | 45 | case VK_UP: 46 | case VK_LEFT: { 47 | 48 | e.preventDefault(); 49 | 50 | this.focusedIdx--; 51 | if (this.focusedIdx < 0) 52 | this.focusedIdx = this.focusedIdx + this.buttons.length; 53 | 54 | break; 55 | } 56 | 57 | case VK_DOWN: 58 | case VK_RIGHT: { 59 | 60 | e.preventDefault(); 61 | 62 | this.focusedIdx = (this.focusedIdx + 1) % this.buttons.length; 63 | 64 | break; 65 | } 66 | 67 | case VK_SPACE: 68 | var focusedButton = e.target; 69 | var idx = this.buttons.indexOf(focusedButton); 70 | if (idx < 0) 71 | return; 72 | this.focusedIdx = idx; 73 | break; 74 | 75 | default: 76 | return; 77 | } 78 | 79 | this.changeFocus(); 80 | }; 81 | 82 | RadioGroup.prototype.handleClick = function(e) { 83 | var button = e.target; 84 | var idx = this.buttons.indexOf(button); 85 | if (idx < 0) 86 | return; 87 | this.focusedIdx = idx; 88 | this.changeFocus(); 89 | }; 90 | 91 | RadioGroup.prototype.changeFocus = function() { 92 | // Set the old button to tabindex -1 93 | this.focusedButton.tabIndex = -1; 94 | this.focusedButton.removeAttribute('checked'); 95 | 96 | // Set the new button to tabindex 0 and focus it 97 | this.focusedButton = this.buttons[this.focusedIdx]; 98 | this.focusedButton.tabIndex = 0; 99 | this.focusedButton.focus(); 100 | this.focusedButton.setAttribute('checked', ''); 101 | 102 | // ... we probably want to do some stuff here, too ... 103 | 104 | }; 105 | 106 | var group1 = new RadioGroup('#group1'); 107 | 108 | }()); 109 | -------------------------------------------------------------------------------- /lesson5-semantics-aria/06-radio-group/solution/radiogroup.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 'use strict'; 3 | 4 | // Define values for keycodes 5 | var VK_ENTER = 13; 6 | var VK_SPACE = 32; 7 | var VK_LEFT = 37; 8 | var VK_UP = 38; 9 | var VK_RIGHT = 39; 10 | var VK_DOWN = 40; 11 | 12 | // Helper function to convert NodeLists to Arrays 13 | function slice(nodes) { 14 | return Array.prototype.slice.call(nodes); 15 | } 16 | 17 | function RadioGroup(id) { 18 | this.el = document.querySelector(id); 19 | this.buttons = slice(this.el.querySelectorAll('.radio')); 20 | this.focusedIdx = 0; 21 | this.focusedButton = this.buttons[this.focusedIdx]; 22 | 23 | this.el.addEventListener('keydown', this.handleKeyDown.bind(this)); 24 | this.el.addEventListener('click', this.handleClick.bind(this)); 25 | 26 | // Set ARIA role for the radio group. 27 | this.el.setAttribute('role', 'radiogroup'); 28 | 29 | var firstButton = true; 30 | for (var button of this.buttons) { 31 | if (firstButton) { 32 | button.tabIndex = '0'; 33 | firstButton = false; 34 | } else { 35 | button.tabIndex = '-1'; 36 | } 37 | 38 | button.setAttribute('role', 'radio'); 39 | } 40 | } 41 | 42 | RadioGroup.prototype.handleKeyDown = function(e) { 43 | switch(e.keyCode) { 44 | 45 | case VK_UP: 46 | case VK_LEFT: { 47 | 48 | e.preventDefault(); 49 | 50 | this.focusedIdx--; 51 | if (this.focusedIdx < 0) 52 | this.focusedIdx = this.focusedIdx + this.buttons.length; 53 | 54 | break; 55 | } 56 | 57 | case VK_DOWN: 58 | case VK_RIGHT: { 59 | 60 | e.preventDefault(); 61 | 62 | this.focusedIdx = (this.focusedIdx + 1) % this.buttons.length; 63 | 64 | break; 65 | } 66 | 67 | case VK_SPACE: 68 | var focusedButton = e.target; 69 | var idx = this.buttons.indexOf(focusedButton); 70 | if (idx < 0) 71 | return; 72 | this.focusedIdx = idx; 73 | break; 74 | 75 | default: 76 | return; 77 | } 78 | 79 | this.changeFocus(); 80 | }; 81 | 82 | RadioGroup.prototype.handleClick = function(e) { 83 | var button = e.target; 84 | var idx = this.buttons.indexOf(button); 85 | if (idx < 0) 86 | return; 87 | this.focusedIdx = idx; 88 | this.changeFocus(); 89 | }; 90 | 91 | RadioGroup.prototype.changeFocus = function() { 92 | // Set the old button to tabindex -1 93 | this.focusedButton.tabIndex = -1; 94 | this.focusedButton.removeAttribute('checked'); 95 | this.focusedButton.setAttribute('aria-checked', 'false'); 96 | 97 | // Set the new button to tabindex 0 and focus it 98 | this.focusedButton = this.buttons[this.focusedIdx]; 99 | this.focusedButton.tabIndex = 0; 100 | this.focusedButton.focus(); 101 | this.focusedButton.setAttribute('checked', ''); 102 | this.focusedButton.setAttribute('aria-checked', 'true'); 103 | }; 104 | 105 | var group1 = new RadioGroup('#group1'); 106 | 107 | }()); 108 | -------------------------------------------------------------------------------- /lesson6-styling/03-contrast-audit/blog.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Globals 3 | */ 4 | 5 | body { 6 | font-family: Georgia, "Times New Roman", Times, serif; 7 | color: #555; 8 | } 9 | 10 | h1, .h1, 11 | h2, .h2, 12 | h3, .h3, 13 | h4, .h4, 14 | h5, .h5, 15 | h6, .h6 { 16 | margin-top: 0; 17 | font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; 18 | font-weight: normal; 19 | color: #333; 20 | } 21 | 22 | 23 | /* 24 | * Override Bootstrap's default container. 25 | */ 26 | 27 | @media (min-width: 1200px) { 28 | .container { 29 | width: 970px; 30 | } 31 | } 32 | 33 | 34 | /* 35 | * Masthead for nav 36 | */ 37 | 38 | .blog-masthead { 39 | background-color: #428bca; 40 | -webkit-box-shadow: inset 0 -2px 5px rgba(0,0,0,.1); 41 | box-shadow: inset 0 -2px 5px rgba(0,0,0,.1); 42 | } 43 | 44 | /* Nav links */ 45 | .blog-nav-item { 46 | position: relative; 47 | display: inline-block; 48 | padding: 10px; 49 | font-weight: 500; 50 | color: #cdddeb; 51 | } 52 | .blog-nav-item:hover, 53 | .blog-nav-item:focus { 54 | color: #fff; 55 | text-decoration: none; 56 | } 57 | 58 | /* Active state gets a caret at the bottom */ 59 | .blog-nav .active { 60 | color: #fff; 61 | } 62 | .blog-nav .active:after { 63 | position: absolute; 64 | bottom: 0; 65 | left: 50%; 66 | width: 0; 67 | height: 0; 68 | margin-left: -5px; 69 | vertical-align: middle; 70 | content: " "; 71 | border-right: 5px solid transparent; 72 | border-bottom: 5px solid; 73 | border-left: 5px solid transparent; 74 | } 75 | 76 | 77 | /* 78 | * Blog name and description 79 | */ 80 | 81 | .blog-header { 82 | padding-top: 20px; 83 | padding-bottom: 20px; 84 | } 85 | .blog-title { 86 | margin-top: 30px; 87 | margin-bottom: 0; 88 | font-size: 60px; 89 | font-weight: normal; 90 | } 91 | .blog-description { 92 | font-size: 20px; 93 | color: #999; 94 | } 95 | 96 | 97 | /* 98 | * Main column and sidebar layout 99 | */ 100 | 101 | .blog-main { 102 | font-size: 18px; 103 | line-height: 1.5; 104 | } 105 | 106 | /* Sidebar modules for boxing content */ 107 | .sidebar-module { 108 | padding: 15px; 109 | margin: 0 -15px 15px; 110 | } 111 | .sidebar-module-inset { 112 | padding: 15px; 113 | background-color: #f5f5f5; 114 | border-radius: 4px; 115 | } 116 | .sidebar-module-inset p:last-child, 117 | .sidebar-module-inset ul:last-child, 118 | .sidebar-module-inset ol:last-child { 119 | margin-bottom: 0; 120 | } 121 | 122 | 123 | /* Pagination */ 124 | .pager { 125 | margin-bottom: 60px; 126 | text-align: left; 127 | } 128 | .pager > li > a { 129 | width: 140px; 130 | padding: 10px 20px; 131 | text-align: center; 132 | border-radius: 30px; 133 | } 134 | 135 | 136 | /* 137 | * Blog posts 138 | */ 139 | 140 | .blog-post { 141 | margin-bottom: 60px; 142 | } 143 | .blog-post-title { 144 | margin-bottom: 5px; 145 | font-size: 40px; 146 | } 147 | .blog-post-meta { 148 | margin-bottom: 20px; 149 | color: #999; 150 | } 151 | 152 | 153 | /* 154 | * Footer 155 | */ 156 | 157 | .blog-footer { 158 | padding: 40px 0; 159 | color: #999; 160 | text-align: center; 161 | background-color: #f9f9f9; 162 | border-top: 1px solid #e5e5e5; 163 | } 164 | .blog-footer p { 165 | color: #737373; 166 | } 167 | .blog-footer a { 168 | color: #2a77b5; 169 | } 170 | .blog-footer p:last-child { 171 | margin-bottom: 0; 172 | } 173 | -------------------------------------------------------------------------------- /lesson2-focus/02-dom-order/main.css: -------------------------------------------------------------------------------- 1 | *, *::after, *::before { 2 | box-sizing: border-box; 3 | } 4 | 5 | html { 6 | position: relative; 7 | min-height: 100%; 8 | } 9 | 10 | body { 11 | margin: 0 0 60px; /* bottom = newsletter height */ 12 | font-family: 'Georgia', 'Times New Roman', serif; 13 | } 14 | 15 | h2 { 16 | margin-top: 0; 17 | } 18 | 19 | article h3 { 20 | margin-top: 0; 21 | font-size: 1.5em; 22 | -webkit-margin-after: 0.83em; 23 | } 24 | 25 | .container { 26 | max-width: 1000px; 27 | margin: 0 auto; 28 | } 29 | 30 | header { 31 | margin-bottom: 48px; 32 | box-shadow: 0 0 8px #BBB; 33 | border-bottom: 1px solid #AAA; 34 | } 35 | 36 | .logo { 37 | text-align: center; 38 | font-size: 32px; 39 | margin: 0; 40 | padding: 16px 0; 41 | border-bottom: 1px solid #DDD; 42 | } 43 | 44 | .offscreen { 45 | position: absolute; 46 | left: -10000px; 47 | top: -10000px; 48 | } 49 | 50 | .menu { 51 | padding: 16px 48px; 52 | } 53 | 54 | .menu a { 55 | color: #000; 56 | text-decoration: none; 57 | font-size: 18px; 58 | margin-right: 18px; 59 | } 60 | 61 | .menu a:last-child { 62 | margin-right: 0; 63 | } 64 | 65 | .menu .btn-search img { 66 | vertical-align: top; 67 | margin-right: 8px; 68 | width: 22px; 69 | height: 22px; 70 | } 71 | 72 | article p { 73 | line-height: 1.4; 74 | } 75 | 76 | .main-story { 77 | clear: both; 78 | overflow: hidden; 79 | margin-bottom: 16px; 80 | padding-bottom: 16px; 81 | } 82 | 83 | .main-story img { 84 | width: 50%; 85 | } 86 | 87 | .main-story .info { 88 | margin-right: 50%; 89 | padding-right: 16px; 90 | } 91 | 92 | .tier-header { 93 | border-top: 1px solid #AAA; 94 | margin-bottom: 15px; 95 | } 96 | 97 | .tier-header h2 { 98 | font-size: 17px; 99 | font-weight: 400; 100 | text-transform: uppercase; 101 | padding: 5px 15px; 102 | display: inline-block; 103 | margin: 0; 104 | color: #fff; 105 | background: #006b3a; 106 | } 107 | 108 | .side-by-side .column { 109 | display: inline-block; 110 | vertical-align: top; 111 | } 112 | 113 | .side-by-side article img { 114 | max-width: 100%; 115 | } 116 | 117 | .side-by-side .column:first-child { 118 | width: 70%; 119 | padding-right: 16px; 120 | border-right: 1px solid #AAA; 121 | } 122 | 123 | .side-by-side .column:last-child { 124 | padding-left: 16px; 125 | width: 29%; /* Account for whitespace gap from display: inline-block */ 126 | } 127 | 128 | .side-by-side .column:last-child .headline { 129 | font-size: 18px; 130 | } 131 | 132 | .three-col { 133 | margin-bottom: 100px; 134 | } 135 | 136 | .three-col h2, .three-col h3 { 137 | margin-top: 0; 138 | font-size: 16px; 139 | } 140 | 141 | .three-col .columns { 142 | display: flex; 143 | } 144 | 145 | .three-col .columns article { 146 | flex: 1; 147 | margin-right: 18px; 148 | } 149 | 150 | .three-col img { 151 | width: 100%; 152 | } 153 | 154 | .three-col .columns article:last-child { 155 | margin-right: 0; 156 | } 157 | 158 | .newsletter { 159 | position: absolute; 160 | left: 0; 161 | bottom: 0; 162 | height: 60px; 163 | width: 100%; 164 | background: #000; 165 | color: #FFF; 166 | border-top: 1px solid #AAA; 167 | } 168 | 169 | .newsletter h3 { 170 | display: inline-block; 171 | margin-right: 16px; 172 | } 173 | 174 | /* Helpers */ 175 | .pull-right { 176 | float: right; 177 | } 178 | -------------------------------------------------------------------------------- /lesson2-focus/02-dom-order/solution/main.css: -------------------------------------------------------------------------------- 1 | *, *::after, *::before { 2 | box-sizing: border-box; 3 | } 4 | 5 | html { 6 | position: relative; 7 | min-height: 100%; 8 | } 9 | 10 | body { 11 | margin: 0 0 60px; /* bottom = newsletter height */ 12 | font-family: 'Georgia', 'Times New Roman', serif; 13 | } 14 | 15 | h2 { 16 | margin-top: 0; 17 | } 18 | 19 | article h3 { 20 | margin-top: 0; 21 | font-size: 1.5em; 22 | -webkit-margin-after: 0.83em; 23 | } 24 | 25 | .container { 26 | max-width: 1000px; 27 | margin: 0 auto; 28 | } 29 | 30 | header { 31 | margin-bottom: 48px; 32 | box-shadow: 0 0 8px #BBB; 33 | border-bottom: 1px solid #AAA; 34 | } 35 | 36 | .logo { 37 | text-align: center; 38 | font-size: 32px; 39 | margin: 0; 40 | padding: 16px 0; 41 | border-bottom: 1px solid #DDD; 42 | } 43 | 44 | .offscreen { 45 | position: absolute; 46 | left: -10000px; 47 | top: -10000px; 48 | } 49 | 50 | .menu { 51 | padding: 16px 48px; 52 | } 53 | 54 | .menu a { 55 | color: #000; 56 | text-decoration: none; 57 | font-size: 18px; 58 | margin-right: 18px; 59 | } 60 | 61 | .menu a:last-child { 62 | margin-right: 0; 63 | } 64 | 65 | .menu .btn-search img { 66 | vertical-align: top; 67 | margin-right: 8px; 68 | width: 22px; 69 | height: 22px; 70 | } 71 | 72 | article p { 73 | line-height: 1.4; 74 | } 75 | 76 | .main-story { 77 | clear: both; 78 | overflow: hidden; 79 | margin-bottom: 16px; 80 | padding-bottom: 16px; 81 | } 82 | 83 | .main-story img { 84 | width: 50%; 85 | } 86 | 87 | .main-story .info { 88 | margin-right: 50%; 89 | padding-right: 16px; 90 | } 91 | 92 | .tier-header { 93 | border-top: 1px solid #AAA; 94 | margin-bottom: 15px; 95 | } 96 | 97 | .tier-header h2 { 98 | font-size: 17px; 99 | font-weight: 400; 100 | text-transform: uppercase; 101 | padding: 5px 15px; 102 | display: inline-block; 103 | margin: 0; 104 | color: #fff; 105 | background: #006b3a; 106 | } 107 | 108 | .side-by-side .column { 109 | display: inline-block; 110 | vertical-align: top; 111 | } 112 | 113 | .side-by-side article img { 114 | max-width: 100%; 115 | } 116 | 117 | .side-by-side .column:first-child { 118 | width: 70%; 119 | padding-right: 16px; 120 | border-right: 1px solid #AAA; 121 | } 122 | 123 | .side-by-side .column:last-child { 124 | padding-left: 16px; 125 | width: 29%; /* Account for whitespace gap from display: inline-block */ 126 | } 127 | 128 | .side-by-side .column:last-child .headline { 129 | font-size: 18px; 130 | } 131 | 132 | .three-col { 133 | margin-bottom: 100px; 134 | } 135 | 136 | .three-col h2, .three-col h3 { 137 | margin-top: 0; 138 | font-size: 16px; 139 | } 140 | 141 | .three-col .columns { 142 | display: flex; 143 | } 144 | 145 | .three-col .columns article { 146 | flex: 1; 147 | margin-right: 18px; 148 | } 149 | 150 | .three-col img { 151 | width: 100%; 152 | } 153 | 154 | .three-col .columns article:last-child { 155 | margin-right: 0; 156 | } 157 | 158 | .newsletter { 159 | position: absolute; 160 | left: 0; 161 | bottom: 0; 162 | height: 60px; 163 | width: 100%; 164 | background: #000; 165 | color: #FFF; 166 | border-top: 1px solid #AAA; 167 | } 168 | 169 | .newsletter h3 { 170 | display: inline-block; 171 | margin-right: 16px; 172 | } 173 | 174 | /* Helpers */ 175 | .pull-right { 176 | float: right; 177 | } 178 | -------------------------------------------------------------------------------- /lesson2-focus/04-offscreen-content/solution/blog.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Globals 3 | */ 4 | 5 | body { 6 | font-family: Georgia, "Times New Roman", Times, serif; 7 | color: #555; 8 | } 9 | 10 | h1, .h1, 11 | h2, .h2, 12 | h3, .h3, 13 | h4, .h4, 14 | h5, .h5, 15 | h6, .h6 { 16 | margin-top: 0; 17 | font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; 18 | font-weight: normal; 19 | color: #333; 20 | } 21 | 22 | 23 | /* 24 | * Override Bootstrap's default container. 25 | */ 26 | 27 | @media (min-width: 1200px) { 28 | .container { 29 | width: 970px; 30 | } 31 | } 32 | 33 | 34 | /* 35 | * Masthead for nav 36 | */ 37 | 38 | .blog-masthead { 39 | background-color: #a33000; 40 | -webkit-box-shadow: inset 0 -2px 5px rgba(0,0,0,.1); 41 | box-shadow: inset 0 -2px 5px rgba(0,0,0,.1); 42 | } 43 | 44 | /* Nav links */ 45 | .blog-nav-item { 46 | position: relative; 47 | display: inline-block; 48 | padding: 10px; 49 | font-weight: 500; 50 | color: #fff; 51 | } 52 | .blog-nav-item:hover, 53 | .blog-nav-item:focus { 54 | color: #fff; 55 | text-decoration: none; 56 | } 57 | 58 | /* Active state gets a caret at the bottom */ 59 | .blog-nav .active { 60 | color: #fff; 61 | } 62 | .blog-nav .active:after { 63 | position: absolute; 64 | bottom: 0; 65 | left: 50%; 66 | width: 0; 67 | height: 0; 68 | margin-left: -5px; 69 | vertical-align: middle; 70 | content: " "; 71 | border-right: 5px solid transparent; 72 | border-bottom: 5px solid; 73 | border-left: 5px solid transparent; 74 | } 75 | 76 | 77 | /* 78 | * Blog name and description 79 | */ 80 | 81 | .blog-header { 82 | padding-top: 20px; 83 | padding-bottom: 20px; 84 | } 85 | .blog-title { 86 | margin-top: 30px; 87 | margin-bottom: 0; 88 | font-size: 60px; 89 | font-weight: normal; 90 | } 91 | .blog-description { 92 | font-size: 20px; 93 | color: #595959; 94 | } 95 | 96 | 97 | /* 98 | * Main column and sidebar layout 99 | */ 100 | 101 | .blog-main { 102 | font-size: 18px; 103 | line-height: 1.5; 104 | } 105 | 106 | /* Sidebar modules for boxing content */ 107 | .sidebar-module { 108 | padding: 15px; 109 | margin: 0 -15px 15px; 110 | } 111 | .sidebar-module-inset { 112 | padding: 15px; 113 | background-color: #f5f5f5; 114 | border-radius: 4px; 115 | } 116 | .sidebar-module-inset p:last-child, 117 | .sidebar-module-inset ul:last-child, 118 | .sidebar-module-inset ol:last-child { 119 | margin-bottom: 0; 120 | } 121 | 122 | 123 | /* Pagination */ 124 | .pager { 125 | margin-bottom: 60px; 126 | text-align: left; 127 | } 128 | .pager > li > a { 129 | width: 140px; 130 | padding: 10px 20px; 131 | text-align: center; 132 | border-radius: 30px; 133 | } 134 | 135 | 136 | /* 137 | * Blog posts 138 | */ 139 | 140 | .blog-post { 141 | margin-bottom: 60px; 142 | } 143 | .blog-post-title { 144 | margin-bottom: 5px; 145 | font-size: 40px; 146 | } 147 | .blog-post-meta { 148 | margin-bottom: 20px; 149 | color: #595959; 150 | } 151 | 152 | 153 | /* 154 | * Footer 155 | */ 156 | 157 | .blog-footer { 158 | padding: 40px 0; 159 | color: #565656; 160 | text-align: center; 161 | background-color: #f9f9f9; 162 | border-top: 1px solid #e5e5e5; 163 | } 164 | .blog-footer p:last-child { 165 | margin-bottom: 0; 166 | } 167 | .blog-footer a { 168 | color: #00598f; 169 | } 170 | 171 | .modal { 172 | display: none; 173 | } 174 | -------------------------------------------------------------------------------- /lesson6-styling/03-contrast-audit/solution/blog.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Globals 3 | */ 4 | 5 | body { 6 | font-family: Georgia, "Times New Roman", Times, serif; 7 | color: #555; 8 | } 9 | 10 | h1, .h1, 11 | h2, .h2, 12 | h3, .h3, 13 | h4, .h4, 14 | h5, .h5, 15 | h6, .h6 { 16 | margin-top: 0; 17 | font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; 18 | font-weight: normal; 19 | color: #333; 20 | } 21 | 22 | 23 | /* 24 | * Override Bootstrap's default container. 25 | */ 26 | 27 | @media (min-width: 1200px) { 28 | .container { 29 | width: 970px; 30 | } 31 | } 32 | 33 | 34 | /* 35 | * Masthead for nav 36 | */ 37 | 38 | .blog-masthead { 39 | background-color: #00659e; 40 | -webkit-box-shadow: inset 0 -2px 5px rgba(0,0,0,.1); 41 | box-shadow: inset 0 -2px 5px rgba(0,0,0,.1); 42 | } 43 | 44 | /* Nav links */ 45 | .blog-nav-item { 46 | position: relative; 47 | display: inline-block; 48 | padding: 10px; 49 | font-weight: 500; 50 | color: #cdddeb; 51 | } 52 | .blog-nav-item:hover, 53 | .blog-nav-item:focus { 54 | color: #fff; 55 | text-decoration: none; 56 | } 57 | 58 | /* Active state gets a caret at the bottom */ 59 | .blog-nav .active { 60 | color: #fff; 61 | } 62 | .blog-nav .active:after { 63 | position: absolute; 64 | bottom: 0; 65 | left: 50%; 66 | width: 0; 67 | height: 0; 68 | margin-left: -5px; 69 | vertical-align: middle; 70 | content: " "; 71 | border-right: 5px solid transparent; 72 | border-bottom: 5px solid; 73 | border-left: 5px solid transparent; 74 | } 75 | 76 | 77 | /* 78 | * Blog name and description 79 | */ 80 | 81 | .blog-header { 82 | padding-top: 20px; 83 | padding-bottom: 20px; 84 | } 85 | .blog-title { 86 | margin-top: 30px; 87 | margin-bottom: 0; 88 | font-size: 60px; 89 | font-weight: normal; 90 | } 91 | .blog-description { 92 | font-size: 20px; 93 | color: #767676; 94 | } 95 | 96 | 97 | /* 98 | * Main column and sidebar layout 99 | */ 100 | 101 | .blog-main { 102 | font-size: 18px; 103 | line-height: 1.5; 104 | } 105 | 106 | /* Sidebar modules for boxing content */ 107 | .sidebar-module { 108 | padding: 15px; 109 | margin: 0 -15px 15px; 110 | } 111 | .sidebar-module-inset { 112 | padding: 15px; 113 | background-color: #f5f5f5; 114 | border-radius: 4px; 115 | } 116 | .sidebar-module-inset p:last-child, 117 | .sidebar-module-inset ul:last-child, 118 | .sidebar-module-inset ol:last-child { 119 | margin-bottom: 0; 120 | } 121 | 122 | 123 | /* Pagination */ 124 | .pager { 125 | margin-bottom: 60px; 126 | text-align: left; 127 | } 128 | .pager > li > a { 129 | width: 140px; 130 | padding: 10px 20px; 131 | text-align: center; 132 | border-radius: 30px; 133 | } 134 | 135 | 136 | /* 137 | * Blog posts 138 | */ 139 | 140 | .blog-post { 141 | margin-bottom: 60px; 142 | } 143 | .blog-post-title { 144 | margin-bottom: 5px; 145 | font-size: 40px; 146 | } 147 | .blog-post-meta { 148 | margin-bottom: 20px; 149 | color: #767676; 150 | } 151 | 152 | 153 | /* 154 | * Footer 155 | */ 156 | 157 | .blog-footer { 158 | padding: 40px 0; 159 | color: #999; 160 | text-align: center; 161 | background-color: #f9f9f9; 162 | border-top: 1px solid #e5e5e5; 163 | } 164 | .blog-footer p { 165 | color: #737373; 166 | } 167 | .blog-footer a { 168 | color: #2a77b5; 169 | } 170 | .blog-footer p:last-child { 171 | margin-bottom: 0; 172 | } 173 | -------------------------------------------------------------------------------- /lesson2-focus/06-skip-links/main.css: -------------------------------------------------------------------------------- 1 | *, *::after, *::before { 2 | box-sizing: border-box; 3 | } 4 | 5 | html { 6 | position: relative; 7 | min-height: 100%; 8 | } 9 | 10 | body { 11 | margin: 0 0 60px; /* bottom = newsletter height */ 12 | font-family: 'Georgia', 'Times New Roman', serif; 13 | } 14 | 15 | h2 { 16 | margin-top: 0; 17 | } 18 | 19 | .container { 20 | max-width: 1000px; 21 | margin: 0 auto; 22 | } 23 | 24 | nav { 25 | margin-bottom: 48px; 26 | box-shadow: 0 0 8px #BBB; 27 | border-bottom: 1px solid #AAA; 28 | } 29 | 30 | .logo { 31 | text-align: center; 32 | font-size: 32px; 33 | margin: 0; 34 | padding: 16px 0; 35 | border-bottom: 1px solid #DDD; 36 | } 37 | 38 | .menu { 39 | padding: 16px 48px; 40 | } 41 | 42 | .menu a { 43 | color: #000; 44 | text-decoration: none; 45 | font-size: 18px; 46 | margin-right: 18px; 47 | } 48 | 49 | .menu a:last-child { 50 | margin-right: 0; 51 | } 52 | 53 | .menu .btn-search img { 54 | vertical-align: top; 55 | margin-right: 8px; 56 | width: 22px; 57 | height: 22px; 58 | } 59 | 60 | article p { 61 | line-height: 1.4; 62 | } 63 | 64 | .main-story { 65 | clear: both; 66 | overflow: hidden; 67 | margin-bottom: 16px; 68 | padding-bottom: 16px; 69 | } 70 | 71 | .main-story img { 72 | width: 50%; 73 | } 74 | 75 | .main-story .info { 76 | margin-right: 50%; 77 | padding-right: 16px; 78 | } 79 | 80 | .tier-header { 81 | border-top: 1px solid #AAA; 82 | margin-bottom: 15px; 83 | } 84 | 85 | .tier-header h2 { 86 | font-size: 17px; 87 | font-weight: 400; 88 | text-transform: uppercase; 89 | padding: 5px 15px; 90 | display: inline-block; 91 | margin: 0; 92 | color: #fff; 93 | background: #006b3a; 94 | } 95 | 96 | .side-by-side article { 97 | display: inline-block; 98 | vertical-align: top; 99 | } 100 | 101 | .side-by-side article img { 102 | max-width: 100%; 103 | } 104 | 105 | .side-by-side article:first-child { 106 | width: 70%; 107 | padding-right: 16px; 108 | border-right: 1px solid #AAA; 109 | } 110 | 111 | .side-by-side article:last-child { 112 | padding-left: 16px; 113 | width: 29%; /* Account for whitespace gap from display: inline-block */ 114 | } 115 | 116 | .side-by-side article:last-child .headline { 117 | font-size: 18px; 118 | } 119 | 120 | .three-col { 121 | margin-bottom: 100px; 122 | } 123 | 124 | .three-col h2 { 125 | font-size: 16px; 126 | } 127 | 128 | .three-col .columns { 129 | display: flex; 130 | } 131 | 132 | .three-col .columns article { 133 | flex: 1; 134 | margin-right: 18px; 135 | } 136 | 137 | .three-col img { 138 | width: 100%; 139 | } 140 | 141 | .three-col .columns article:last-child { 142 | margin-right: 0; 143 | } 144 | 145 | .newsletter { 146 | position: absolute; 147 | left: 0; 148 | bottom: 0; 149 | height: 60px; 150 | width: 100%; 151 | background: #000; 152 | color: #FFF; 153 | border-top: 1px solid #AAA; 154 | } 155 | 156 | .newsletter h3 { 157 | display: inline-block; 158 | margin-right: 16px; 159 | } 160 | 161 | .skip-link { 162 | position: absolute; 163 | top: -40px; 164 | left: 0; 165 | background: #BF1722; 166 | color: white; 167 | padding: 8px; 168 | z-index: 100; 169 | border-bottom-right-radius: 8px; 170 | } 171 | 172 | .skip-link:focus { 173 | top: 0; 174 | } 175 | 176 | /* Helpers */ 177 | .pull-right { 178 | float: right; 179 | } 180 | -------------------------------------------------------------------------------- /lesson2-focus/04-offscreen-content/blog.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Globals 3 | */ 4 | 5 | body { 6 | font-family: Georgia, "Times New Roman", Times, serif; 7 | color: #555; 8 | } 9 | 10 | h1, .h1, 11 | h2, .h2, 12 | h3, .h3, 13 | h4, .h4, 14 | h5, .h5, 15 | h6, .h6 { 16 | margin-top: 0; 17 | font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; 18 | font-weight: normal; 19 | color: #333; 20 | } 21 | 22 | 23 | /* 24 | * Override Bootstrap's default container. 25 | */ 26 | 27 | @media (min-width: 1200px) { 28 | .container { 29 | width: 970px; 30 | } 31 | } 32 | 33 | 34 | /* 35 | * Masthead for nav 36 | */ 37 | 38 | .blog-masthead { 39 | background-color: #a33000; 40 | -webkit-box-shadow: inset 0 -2px 5px rgba(0,0,0,.1); 41 | box-shadow: inset 0 -2px 5px rgba(0,0,0,.1); 42 | } 43 | 44 | /* Nav links */ 45 | .blog-nav-item { 46 | position: relative; 47 | display: inline-block; 48 | padding: 10px; 49 | font-weight: 500; 50 | color: #fff; 51 | } 52 | .blog-nav-item:hover, 53 | .blog-nav-item:focus { 54 | color: #fff; 55 | text-decoration: none; 56 | } 57 | 58 | /* Active state gets a caret at the bottom */ 59 | .blog-nav .active { 60 | color: #fff; 61 | } 62 | .blog-nav .active:after { 63 | position: absolute; 64 | bottom: 0; 65 | left: 50%; 66 | width: 0; 67 | height: 0; 68 | margin-left: -5px; 69 | vertical-align: middle; 70 | content: " "; 71 | border-right: 5px solid transparent; 72 | border-bottom: 5px solid; 73 | border-left: 5px solid transparent; 74 | } 75 | 76 | 77 | /* 78 | * Blog name and description 79 | */ 80 | 81 | .blog-header { 82 | padding-top: 20px; 83 | padding-bottom: 20px; 84 | } 85 | .blog-title { 86 | margin-top: 30px; 87 | margin-bottom: 0; 88 | font-size: 60px; 89 | font-weight: normal; 90 | } 91 | .blog-description { 92 | font-size: 20px; 93 | color: #595959; 94 | } 95 | 96 | 97 | /* 98 | * Main column and sidebar layout 99 | */ 100 | 101 | .blog-main { 102 | font-size: 18px; 103 | line-height: 1.5; 104 | } 105 | 106 | /* Sidebar modules for boxing content */ 107 | .sidebar-module { 108 | padding: 15px; 109 | margin: 0 -15px 15px; 110 | } 111 | .sidebar-module-inset { 112 | padding: 15px; 113 | background-color: #f5f5f5; 114 | border-radius: 4px; 115 | } 116 | .sidebar-module-inset p:last-child, 117 | .sidebar-module-inset ul:last-child, 118 | .sidebar-module-inset ol:last-child { 119 | margin-bottom: 0; 120 | } 121 | 122 | 123 | /* Pagination */ 124 | .pager { 125 | margin-bottom: 60px; 126 | text-align: left; 127 | } 128 | .pager > li > a { 129 | width: 140px; 130 | padding: 10px 20px; 131 | text-align: center; 132 | border-radius: 30px; 133 | } 134 | 135 | 136 | /* 137 | * Blog posts 138 | */ 139 | 140 | .blog-post { 141 | margin-bottom: 60px; 142 | } 143 | .blog-post-title { 144 | margin-bottom: 5px; 145 | font-size: 40px; 146 | } 147 | .blog-post-meta { 148 | margin-bottom: 20px; 149 | color: #595959; 150 | } 151 | 152 | 153 | /* 154 | * Footer 155 | */ 156 | 157 | .blog-footer { 158 | padding: 40px 0; 159 | color: #565656; 160 | text-align: center; 161 | background-color: #f9f9f9; 162 | border-top: 1px solid #e5e5e5; 163 | } 164 | .blog-footer p:last-child { 165 | margin-bottom: 0; 166 | } 167 | .blog-footer a { 168 | color: #00598f; 169 | } 170 | 171 | .modal { 172 | display: block; 173 | left: 9999px; 174 | } 175 | -------------------------------------------------------------------------------- /lesson3-semantics-built-in/17-text-alternatives/main.css: -------------------------------------------------------------------------------- 1 | *, *::after, *::before { 2 | box-sizing: border-box; 3 | } 4 | 5 | html { 6 | position: relative; 7 | min-height: 100%; 8 | } 9 | 10 | body { 11 | margin: 0 0 60px; /* bottom = newsletter height */ 12 | font-family: 'Georgia', 'Times New Roman', serif; 13 | } 14 | 15 | h2 { 16 | margin-top: 0; 17 | } 18 | 19 | article h3 { 20 | margin-top: 0; 21 | font-size: 1.5em; 22 | -webkit-margin-after: 0.83em; 23 | } 24 | 25 | .container { 26 | max-width: 1000px; 27 | margin: 0 auto; 28 | } 29 | 30 | header { 31 | margin-bottom: 48px; 32 | box-shadow: 0 0 8px #BBB; 33 | border-bottom: 1px solid #AAA; 34 | } 35 | 36 | .logo { 37 | text-align: center; 38 | font-size: 32px; 39 | margin: 0; 40 | padding: 16px 0; 41 | border-bottom: 1px solid #DDD; 42 | } 43 | 44 | .offscreen { 45 | position: absolute; 46 | left: -10000px; 47 | top: -10000px; 48 | } 49 | 50 | .menu { 51 | padding: 16px 48px; 52 | } 53 | 54 | .menu a { 55 | color: #000; 56 | text-decoration: none; 57 | font-size: 18px; 58 | margin-right: 18px; 59 | } 60 | 61 | .menu a:last-child { 62 | margin-right: 0; 63 | } 64 | 65 | .menu .btn-search img { 66 | vertical-align: top; 67 | margin-right: 8px; 68 | width: 22px; 69 | height: 22px; 70 | } 71 | 72 | article p { 73 | line-height: 1.4; 74 | } 75 | 76 | .main-story { 77 | clear: both; 78 | overflow: hidden; 79 | margin-bottom: 16px; 80 | padding-bottom: 16px; 81 | } 82 | 83 | .main-story img { 84 | width: 50%; 85 | } 86 | 87 | .main-story .info { 88 | margin-right: 50%; 89 | padding-right: 16px; 90 | } 91 | 92 | .tier-header { 93 | border-top: 1px solid #AAA; 94 | margin-bottom: 15px; 95 | } 96 | 97 | .tier-header h2 { 98 | font-size: 17px; 99 | font-weight: 400; 100 | text-transform: uppercase; 101 | padding: 5px 15px; 102 | display: inline-block; 103 | margin: 0; 104 | color: #fff; 105 | background: #006b3a; 106 | } 107 | 108 | .side-by-side .column { 109 | display: inline-block; 110 | vertical-align: top; 111 | } 112 | 113 | .side-by-side article img { 114 | max-width: 100%; 115 | } 116 | 117 | .side-by-side .column:first-child { 118 | width: 70%; 119 | padding-right: 16px; 120 | border-right: 1px solid #AAA; 121 | } 122 | 123 | .side-by-side .column:last-child { 124 | padding-left: 16px; 125 | width: 29%; /* Account for whitespace gap from display: inline-block */ 126 | } 127 | 128 | .side-by-side .column:last-child .headline { 129 | font-size: 18px; 130 | } 131 | 132 | .three-col { 133 | margin-bottom: 100px; 134 | } 135 | 136 | .three-col h2, .three-col h3 { 137 | margin-top: 0; 138 | font-size: 16px; 139 | } 140 | 141 | .three-col .columns { 142 | display: flex; 143 | } 144 | 145 | .three-col .columns article { 146 | flex: 1; 147 | margin-right: 18px; 148 | } 149 | 150 | .three-col img { 151 | width: 100%; 152 | } 153 | 154 | .three-col .columns article:last-child { 155 | margin-right: 0; 156 | } 157 | 158 | .newsletter { 159 | position: absolute; 160 | left: 0; 161 | bottom: 0; 162 | height: 60px; 163 | width: 100%; 164 | background: #000; 165 | color: #FFF; 166 | border-top: 1px solid #AAA; 167 | } 168 | 169 | .newsletter h3 { 170 | display: inline-block; 171 | margin-right: 16px; 172 | } 173 | 174 | /* Helpers */ 175 | .pull-right { 176 | float: right; 177 | } 178 | -------------------------------------------------------------------------------- /lesson4-semantics-navigating/10-landmarks/blog.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Globals 3 | */ 4 | 5 | body { 6 | font-family: Georgia, "Times New Roman", Times, serif; 7 | color: #555; 8 | font-size: 18px; 9 | } 10 | 11 | h1, .h1, 12 | h2, .h2, 13 | h3, .h3, 14 | h4, .h4, 15 | h5, .h5, 16 | h6, .h6 { 17 | margin-top: 0; 18 | font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; 19 | font-weight: normal; 20 | color: #333; 21 | } 22 | 23 | 24 | /* 25 | * Override Bootstrap's default container. 26 | */ 27 | 28 | @media (min-width: 1200px) { 29 | .container { 30 | width: 970px; 31 | } 32 | } 33 | 34 | 35 | 36 | /* 37 | * Masthead for nav 38 | */ 39 | 40 | .blog-header { 41 | background-color: #a33000; 42 | -webkit-box-shadow: inset 0 -2px 5px rgba(0,0,0,.1); 43 | box-shadow: inset 0 -2px 5px rgba(0,0,0,.1); 44 | } 45 | 46 | /* Nav links */ 47 | .blog-nav-item { 48 | position: relative; 49 | display: inline-block; 50 | padding: 10px; 51 | font-weight: 500; 52 | color: #fff; 53 | } 54 | .blog-nav-item:hover, 55 | .blog-nav-item:focus { 56 | color: #fff; 57 | text-decoration: none; 58 | } 59 | 60 | /* Active state gets a caret at the bottom */ 61 | .blog-nav .active { 62 | color: #fff; 63 | } 64 | .blog-nav .active:after { 65 | position: absolute; 66 | bottom: 0; 67 | left: 50%; 68 | width: 0; 69 | height: 0; 70 | margin-left: -5px; 71 | vertical-align: middle; 72 | content: " "; 73 | border-right: 5px solid transparent; 74 | border-bottom: 5px solid; 75 | border-left: 5px solid transparent; 76 | } 77 | 78 | 79 | /* 80 | * Blog name and description 81 | */ 82 | 83 | .blog-hero { 84 | padding-top: 20px; 85 | padding-bottom: 20px; 86 | } 87 | .blog-title { 88 | margin-top: 30px; 89 | margin-bottom: 0; 90 | font-size: 60px; 91 | font-weight: normal; 92 | } 93 | .blog-description { 94 | font-size: 20px; 95 | color: #595959; 96 | } 97 | 98 | 99 | /* 100 | * Main column and sidebar layout 101 | */ 102 | 103 | .blog-main { 104 | line-height: 1.5; 105 | } 106 | 107 | /* Sidebar modules for boxing content */ 108 | .sidebar-module { 109 | padding: 15px; 110 | margin: 0 -15px 15px; 111 | } 112 | .sidebar-module-inset { 113 | padding: 15px; 114 | background-color: #f5f5f5; 115 | border-radius: 4px; 116 | } 117 | .sidebar-module-inset p:last-child, 118 | .sidebar-module-inset ul:last-child, 119 | .sidebar-module-inset ol:last-child { 120 | margin-bottom: 0; 121 | } 122 | 123 | 124 | /* Pagination */ 125 | .pager { 126 | margin-bottom: 60px; 127 | text-align: left; 128 | } 129 | .pager > li > a { 130 | width: 140px; 131 | padding: 10px 20px; 132 | text-align: center; 133 | border-radius: 30px; 134 | } 135 | 136 | 137 | /* 138 | * Blog posts 139 | */ 140 | 141 | .blog-post { 142 | margin-bottom: 60px; 143 | } 144 | .blog-post-title { 145 | margin-bottom: 5px; 146 | font-size: 40px; 147 | } 148 | .blog-post-meta { 149 | margin-bottom: 20px; 150 | color: #595959; 151 | } 152 | 153 | 154 | /* 155 | * Footer 156 | */ 157 | 158 | .blog-footer { 159 | padding: 40px 0; 160 | color: #565656; 161 | text-align: center; 162 | background-color: #f9f9f9; 163 | border-top: 1px solid #e5e5e5; 164 | } 165 | .blog-footer p:last-child { 166 | margin-bottom: 0; 167 | } 168 | .blog-footer a { 169 | color: #00598f; 170 | } 171 | 172 | .modal { 173 | display: block; 174 | left: 9999px; 175 | } 176 | -------------------------------------------------------------------------------- /lesson6-styling/04-mobile-screenreader/blog.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Globals 3 | */ 4 | 5 | body { 6 | font-family: Georgia, "Times New Roman", Times, serif; 7 | color: #555; 8 | } 9 | 10 | h1, .h1, 11 | h2, .h2, 12 | h3, .h3, 13 | h4, .h4, 14 | h5, .h5, 15 | h6, .h6 { 16 | margin-top: 0; 17 | font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; 18 | font-weight: normal; 19 | color: #333; 20 | } 21 | 22 | 23 | /* 24 | * Override Bootstrap's default container. 25 | */ 26 | 27 | @media (min-width: 1200px) { 28 | .container { 29 | width: 970px; 30 | } 31 | } 32 | 33 | 34 | /* 35 | * Masthead for nav 36 | */ 37 | 38 | .blog-masthead { 39 | background-color: #00659e; 40 | -webkit-box-shadow: inset 0 -2px 5px rgba(0,0,0,.1); 41 | box-shadow: inset 0 -2px 5px rgba(0,0,0,.1); 42 | } 43 | 44 | /* Nav links */ 45 | .blog-nav-item { 46 | position: relative; 47 | display: inline-block; 48 | padding: 10px; 49 | font-weight: 500; 50 | color: #cdddeb; 51 | } 52 | .blog-nav-item:hover, 53 | .blog-nav-item:focus { 54 | color: #fff; 55 | text-decoration: none; 56 | } 57 | 58 | /* Active state gets a caret at the bottom */ 59 | .blog-nav .active { 60 | color: #fff; 61 | } 62 | .blog-nav .active:after { 63 | position: absolute; 64 | bottom: 0; 65 | left: 50%; 66 | width: 0; 67 | height: 0; 68 | margin-left: -5px; 69 | vertical-align: middle; 70 | content: " "; 71 | border-right: 5px solid transparent; 72 | border-bottom: 5px solid; 73 | border-left: 5px solid transparent; 74 | } 75 | 76 | 77 | /* 78 | * Blog name and description 79 | */ 80 | 81 | .blog-header { 82 | padding-top: 20px; 83 | padding-bottom: 20px; 84 | } 85 | .blog-title { 86 | margin-top: 30px; 87 | margin-bottom: 0; 88 | font-size: 50px; 89 | font-weight: normal; 90 | } 91 | .blog-description { 92 | font-size: 20px; 93 | color: #767676; 94 | } 95 | 96 | 97 | /* 98 | * Main column and sidebar layout 99 | */ 100 | 101 | .blog-main { 102 | font-size: 18px; 103 | line-height: 1.5; 104 | } 105 | 106 | /* Sidebar modules for boxing content */ 107 | .sidebar-module { 108 | padding: 15px; 109 | margin: 0 -15px 15px; 110 | } 111 | .sidebar-module-inset { 112 | padding: 15px; 113 | background-color: #f5f5f5; 114 | border-radius: 4px; 115 | } 116 | .sidebar-module-inset p:last-child, 117 | .sidebar-module-inset ul:last-child, 118 | .sidebar-module-inset ol:last-child { 119 | margin-bottom: 0; 120 | } 121 | 122 | 123 | /* Pagination */ 124 | .pager { 125 | margin-bottom: 60px; 126 | text-align: left; 127 | } 128 | .pager > li > a { 129 | width: 140px; 130 | padding: 10px 20px; 131 | text-align: center; 132 | border-radius: 30px; 133 | } 134 | 135 | 136 | /* 137 | * Blog posts 138 | */ 139 | 140 | .blog-post { 141 | margin-bottom: 60px; 142 | } 143 | .blog-post-title { 144 | margin-bottom: 5px; 145 | font-size: 40px; 146 | } 147 | .blog-post-meta { 148 | margin-bottom: 20px; 149 | color: #767676; 150 | } 151 | 152 | 153 | /* 154 | * Footer 155 | */ 156 | 157 | .blog-footer { 158 | padding: 40px 0; 159 | color: #999; 160 | text-align: center; 161 | background-color: #f9f9f9; 162 | border-top: 1px solid #e5e5e5; 163 | } 164 | .blog-footer p { 165 | color: #737373; 166 | } 167 | .blog-footer a { 168 | color: #2a77b5; 169 | } 170 | .blog-footer p:last-child { 171 | margin-bottom: 0; 172 | } 173 | 174 | .magic-btn { 175 | display: inline-block; 176 | padding: 8px; 177 | background: #673AB7; 178 | border-radius: 4px; 179 | color: white; 180 | box-shadow: 1px 3px 6px rgba(0, 0, 0, 0.4); 181 | } 182 | -------------------------------------------------------------------------------- /final-project/ecommerce-site/solution/scripts/stars.js: -------------------------------------------------------------------------------- 1 | var VK_PGUP = 33; 2 | var VK_PGDN = 34; 3 | var VK_END = 35; 4 | var VK_HOME = 36; 5 | var VK_LEFT = 37; 6 | var VK_UP = 38; 7 | var VK_RIGHT = 39; 8 | var VK_DOWN = 40; 9 | 10 | function Stars(stars) { 11 | this._el = stars; 12 | this._stars = Array.prototype.slice.call(stars.querySelectorAll('.star')); 13 | for (var star of this._stars) { 14 | star.addEventListener('mouseenter', this.hoverStart.bind(this)); 15 | star.addEventListener('mouseleave', this.hoverEnd.bind(this)); 16 | star.addEventListener('click', this.select.bind(this)); 17 | } 18 | 19 | this._minValue = 0; 20 | this._el.setAttribute('aria-valuemin', this._minValue); 21 | this._maxValue = this._stars.length; 22 | this._el.setAttribute('aria-valuemax', this._maxValue); 23 | 24 | this.setValue(0); 25 | 26 | this._el.setAttribute('role', 'slider'); 27 | this._el.tabIndex = 0; 28 | 29 | this._el.addEventListener('keydown', this.onKeydown.bind(this)); 30 | } 31 | 32 | Stars.prototype = { 33 | showStars: function(numStarsOn) { 34 | this._stars.forEach(function(star, i) { 35 | if (i < numStarsOn) 36 | star.querySelector('img').src = 'images/star-on.png'; 37 | else 38 | star.querySelector('img').src = 'images/star-off.png'; 39 | }); 40 | }, 41 | 42 | set currentValue(value) { 43 | this._el.setAttribute('aria-valuenow', value); 44 | var englishValue = (value === 0 ? 'No' : value) + ' star' + (value !== 1 ? 's' : ''); 45 | this._el.setAttribute('aria-valuetext', englishValue); 46 | }, 47 | 48 | get currentValue() { 49 | return parseInt(this._el.getAttribute('aria-valuenow')); 50 | }, 51 | 52 | setValue: function(value) { 53 | this.currentValue = value; 54 | this.showStars(value); 55 | }, 56 | 57 | increment: function() { 58 | var currentValue = this.currentValue; 59 | if (currentValue === this._maxValue) 60 | return; 61 | this.currentValue = currentValue + 1; 62 | }, 63 | 64 | decrement: function() { 65 | var currentValue = this.currentValue; 66 | if (currentValue === this._minValue) 67 | return; 68 | this.currentValue = currentValue - 1; 69 | }, 70 | 71 | select: function(e) { 72 | var star = e.currentTarget; 73 | var index = this._stars.indexOf(star); 74 | this.setValue(index + 1); 75 | }, 76 | 77 | hoverStart: function(e) { 78 | var star = e.currentTarget; 79 | var index = this._stars.indexOf(star); 80 | this.showStars(index + 1); 81 | }, 82 | 83 | hoverEnd: function(e) { 84 | this.showStars(this.currentValue); 85 | }, 86 | 87 | onKeydown: function(e) { 88 | switch (e.keyCode) { 89 | case VK_UP: 90 | case VK_RIGHT: 91 | this.increment(); 92 | this.showStars(this.currentValue); 93 | e.stopPropagation(); 94 | e.preventDefault(); 95 | break; 96 | case VK_DOWN: 97 | case VK_LEFT: 98 | this.decrement(); 99 | this.showStars(this.currentValue); 100 | e.stopPropagation(); 101 | e.preventDefault(); 102 | break; 103 | case VK_HOME: 104 | case VK_PGUP: 105 | this.setValue(this._minValue); 106 | this.showStars(this.currentValue); 107 | e.stopPropagation(); 108 | e.preventDefault(); 109 | break; 110 | case VK_END: 111 | case VK_PGDN: 112 | this.setValue(this._maxValue); 113 | this.showStars(this.currentValue); 114 | e.stopPropagation(); 115 | e.preventDefault(); 116 | break; 117 | } 118 | } 119 | } 120 | 121 | 122 | var starWidgets = document.querySelectorAll('.stars'); 123 | for (var i = 0; i < starWidgets.length; i++) { 124 | new Stars(starWidgets[i]); 125 | } 126 | -------------------------------------------------------------------------------- /lesson2-focus/01-basic-form/main.css: -------------------------------------------------------------------------------- 1 | *, *::after, *::before { 2 | box-sizing: border-box; 3 | } 4 | 5 | html, body { 6 | margin: 0; 7 | padding: 0; 8 | height: 100%; 9 | font-family: 'Roboto', 'Helvetica', 'Arial', sans-serif; 10 | background-image: url('./uair-2000.jpg'); 11 | background-size: cover; 12 | } 13 | 14 | .click-stealer { 15 | position: absolute; 16 | top: 0; 17 | right: 0; 18 | bottom: 0; 19 | left: 0; 20 | } 21 | 22 | .wrapper { 23 | height: 100%; 24 | display: flex; 25 | flex-direction: column; 26 | pointer-events: none; 27 | cursor: default; 28 | } 29 | 30 | .container { 31 | max-width: 1000px; 32 | margin: 0 auto; 33 | } 34 | 35 | header, 36 | footer { 37 | background: #000000; 38 | color: #FAFAFA; 39 | padding: 16px 0; 40 | } 41 | 42 | header h1 { 43 | margin: 0 50px 0 0; 44 | display: inline-block; 45 | vertical-align: middle; 46 | font-weight: 100; 47 | } 48 | 49 | header h1 span { 50 | font-family: 'Courgette'; 51 | color: #FFC107; 52 | } 53 | 54 | nav { 55 | display: inline-block; 56 | vertical-align: middle; 57 | } 58 | 59 | nav a, 60 | footer a { 61 | color: inherit; 62 | text-decoration: none; 63 | margin-right: 16px; 64 | font-size: 20px; 65 | } 66 | 67 | main { 68 | margin: 64px 0; 69 | flex: 1; 70 | } 71 | 72 | .card { 73 | background: rgba(255, 255, 255, 0.9); 74 | max-width: 1000px; 75 | padding: 32px 48px; 76 | margin: 0 auto; 77 | border-radius: 4px; 78 | box-shadow: 0 4px 5px 0 rgba(0, 0, 0, 0.14), 0 1px 10px 0 rgba(0, 0, 0, 0.12), 0 2px 4px -1px rgba(0, 0, 0, 0.4); 79 | } 80 | 81 | h2 { 82 | font-size: 38px; 83 | font-weight: 100; 84 | text-align: center; 85 | } 86 | 87 | .row { 88 | margin-bottom: 16px; 89 | } 90 | 91 | /* Strip out display: inline-block whitespace */ 92 | .row > * { 93 | margin-left: -4px; 94 | } 95 | 96 | .row > *:first-child { 97 | margin-left: 0; 98 | } 99 | 100 | label { 101 | display: block; 102 | font-size: 14px; 103 | color: #3F51B5; 104 | margin-bottom: 8px; 105 | } 106 | 107 | input[type="text"], 108 | input[type="tel"], 109 | input[type="email"], 110 | input[type="date"], 111 | textarea { 112 | display: block; 113 | width: 100%; 114 | border-radius: 4px; 115 | border: 1px solid #9E9E9E; 116 | font-size: 18px; 117 | padding: 8px; 118 | font-family: inherit; 119 | font-weight: 100; 120 | color: #616161; 121 | } 122 | 123 | input[type="date"] { 124 | padding: 6px 8px; 125 | } 126 | 127 | select { 128 | font-size: 20px; 129 | width: 100%; 130 | } 131 | 132 | .inline-control { 133 | display: inline-block; 134 | vertical-align: top; 135 | margin-right: 16px; 136 | } 137 | 138 | .inline-control:last-child { 139 | margin-right: 0; 140 | } 141 | 142 | .trip-selector { 143 | max-width: 400px; 144 | margin: 0 auto; 145 | padding-bottom: 16px; 146 | text-align: center; 147 | border-bottom: 1px solid gray; 148 | } 149 | 150 | .trip-selector label { 151 | display: inline; 152 | margin-right: 16px; 153 | } 154 | 155 | .sign-up { 156 | vertical-align: bottom; 157 | } 158 | 159 | .submit-form { 160 | vertical-align: bottom; 161 | } 162 | 163 | button[type="submit"] { 164 | width: 100%; 165 | height: 42px; 166 | color: white; 167 | background: #3F51B5; 168 | border: none; 169 | border-radius: 4px; 170 | font-size: 20px; 171 | font-family: inherit; 172 | font-weight: 100; 173 | } 174 | 175 | footer [class*="col-"] { 176 | display: inline-block; 177 | text-align: center; 178 | } 179 | 180 | /* Columns */ 181 | 182 | .col-1 { 183 | width: calc(25% - 14px); 184 | } 185 | 186 | .col-2 { 187 | width: calc(50% - 14px); 188 | } 189 | 190 | .col-3 { 191 | width: calc(75% - 14px); 192 | } 193 | 194 | .col-4 { 195 | width: 100%; 196 | } 197 | 198 | 199 | /* Breakpoints */ 200 | 201 | @media (min-height: 1080px) { 202 | .row { 203 | margin-bottom: 32px; 204 | } 205 | 206 | main { 207 | padding-top: 150px; 208 | } 209 | } 210 | -------------------------------------------------------------------------------- /lesson2-focus/02-dom-order/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 |
13 |

The Funion

14 | 15 | 22 |
23 |
24 |
25 |
26 |

Like the Funion? Consider joining our newsletter!

27 | 28 | 29 |
30 |
31 |
32 |

Top story

33 |
34 | A cat staring menacingly off into space 35 |
36 |

Study shows 9 out of 10 cats quietly judging their owners as they sleep

37 |

38 | GRAND RAPIDS, MI—Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis 39 | aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Read More 40 |

41 |
42 |
43 | 44 |
45 |
46 |
47 |

In the news

48 |
49 |
50 |

Bee wonders if maybe everyone else is a drone and he's the only one who has it "figured out." Read More

51 | A bee on top of a flower 52 |
53 |
54 |
55 |
56 | A red, London telephone booth 57 |
58 |

Telephone booths. How is this still a thing?

59 |

60 | LONDON—Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute 61 | irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Read More 62 |

63 |
64 |
65 |
66 |
67 |
68 |
69 |

Recommended

70 |
71 |
72 |
73 | A dog at a desk looks up at a laptop and mouse 74 |
75 |

Turn man's best friend into man's best personal assistant. Read More

76 |
77 |
78 |
79 | A cup of coffee on a table 80 |
81 |

San Franciscans are losing their minds over this third wave, artisanal, fair trade, handcrafted, locavore coffee. Read More

82 |
83 |
84 |
85 | A football player hands the ball off to another player 86 |
87 |

Sportsball team scores a touchpoint to win the big race. Read More

88 |
89 |
90 |
91 |
92 |
93 | 94 | 95 | 96 | -------------------------------------------------------------------------------- /lesson2-focus/02-dom-order/solution/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 |
13 |

The Funion

14 | 15 | 22 |
23 |
24 |
25 |

Top story

26 |
27 | A cat staring menacingly off into space 28 |
29 |

Study shows 9 out of 10 cats quietly judging their owners as they sleep

30 |

31 | GRAND RAPIDS, MI—Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis 32 | aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Read More 33 |

34 |
35 |
36 |
37 |
38 |
39 |

In the news

40 |
41 |
42 |

Bee wonders if maybe everyone else is a drone and he's the only one who has it "figured out." Read More

43 | A bee on top of a flower 44 |
45 |
46 |
47 |
48 | A red, London telephone booth 49 |
50 |

Telephone booths. How is this still a thing?

51 |

52 | LONDON—Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute 53 | irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Read More 54 |

55 |
56 |
57 |
58 |
59 |
60 |
61 |

Recommended

62 |
63 |
64 |
65 | A dog at a desk looks up at a laptop and mouse 66 |
67 |

Turn man's best friend into man's best personal assistant. Read More

68 |
69 |
70 |
71 | A cup of coffee on a table 72 |
73 |

San Franciscans are losing their minds over this third wave, artisanal, fair trade, handcrafted, locavore coffee. Read More

74 |
75 |
76 |
77 | A football player hands the ball off to another player 78 |
79 |

Sportsball team scores a touchpoint to win the big race. Read More

80 |
81 |
82 |
83 |
84 |
85 |
86 |

Like the Funion? Consider joining our newsletter!

87 | 88 | 89 |
90 |
91 |
92 | 93 | 94 | 95 | -------------------------------------------------------------------------------- /lesson3-semantics-built-in/17-text-alternatives/funion.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 |
13 |

The Funion

14 | 15 | 22 |
23 |
24 |
25 |

Top story

26 |
27 | 28 |
29 |

Study shows 9 out of 10 cats quietly judging their owners as they sleep

30 |

31 | GRAND RAPIDS, MI—Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis 32 | aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Read More 33 |

34 |
35 |
36 |
37 |
38 |
39 |

In the news

40 |
41 |
42 |

Bee wonders if maybe everyone else is a drone and he's the only one who has it "figured out." Read More

43 | A bee on top of a flower 44 |
45 |
46 |
47 |
48 | A red, London telephone booth 49 |
50 |

Telephone booths. How is this still a thing?

51 |

52 | LONDON—Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute 53 | irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Read More 54 |

55 |
56 |
57 |
58 |
59 |
60 |
61 |

Recommended

62 |
63 |
64 |
65 | A dog at a desk looks up at a laptop and mouse 66 |
67 |

Turn man's best friend into man's best personal assistant. Read More

68 |
69 |
70 |
71 | A cup of coffee on a table 72 |
73 |

San Franciscans are losing their minds over this third wave, artisanal, fair trade, handcrafted, locavore coffee. Read More

74 |
75 |
76 |
77 | A football player hands the ball off to another player 78 |
79 |

Sportsball team scores a touchpoint to win the big race. Read More

80 |
81 |
82 |
83 |
84 |
85 |
86 |

Like the Funion? Consider joining our newsletter!

87 | 88 | 89 |
90 |
91 |
92 | 93 | 94 | 95 | -------------------------------------------------------------------------------- /lesson2-focus/06-skip-links/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 25 |
26 |
27 | A cat staring menacingly off into space 28 |
29 |
30 |

Study shows 9 out of 10 cats quietly judging their owners as they sleep

31 |
32 |

33 | GRAND RAPIDS, MI—Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis 34 | aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Read More 35 |

36 |
37 |
38 |
39 |
40 |
41 |

In the news

42 |
43 |
44 |

Bee wonders if maybe everyone else is a drone and he's the only one who has it "figured out." Read More

45 |
46 | A bee on top of a flower 47 |
48 |
49 | A red, London telephone booth 50 |
51 |
52 |

Telephone booths. How is this still a thing?

53 |
54 |

55 | LONDON—Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute 56 | irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Read More 57 |

58 |
59 |
60 |
61 |
62 |
63 |

Recommended

64 |
65 |
66 |
67 | A dog at a desk looks up at a laptop and mouse 68 |
69 |
70 |

Turn man's best friend into man's best personal assistant. Read More

71 |
72 |
73 |
74 |
75 | A cup of coffee on a table 76 |
77 |
78 |

San Franciscans are losing their minds over this third wave, artisanal, fair trade, handcrafted, locavore coffee. Read More

79 |
80 |
81 |
82 |
83 | A football player hands the ball off to another player 84 |
85 |
86 |

Sportsball team scores a touchpoint to win the big race. Read More

87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |

Like the Funion? Consider joining our newsletter!

95 | 96 | 97 |
98 |
99 |
100 | 101 | 102 | 103 | -------------------------------------------------------------------------------- /lesson5-semantics-aria/21-dialog/main.css: -------------------------------------------------------------------------------- 1 | .modal { 2 | border-radius: 4px; 3 | margin-left: auto; 4 | margin-right: auto; 5 | padding: 24px; 6 | background-color: #fff; 7 | z-index: 3; /* places the modal on top of everything */ 8 | position: fixed; 9 | top: 25%; 10 | left: 0; 11 | right: 0; 12 | width: 500px; 13 | display: none; 14 | } 15 | 16 | .modal-overlay { 17 | width: 100%; 18 | height: 100%; 19 | z-index: 2; /* places the modalOverlay between the main page and the modal dialog */ 20 | background-color: #000; 21 | opacity: 0.5; 22 | position: fixed; 23 | top: 0; 24 | left: 0; 25 | display: none; 26 | margin: 0; 27 | padding: 0; 28 | } 29 | 30 | .field { 31 | display: inline-block; 32 | vertical-align: bottom; 33 | } 34 | 35 | .modal .field { 36 | display: block; 37 | margin-bottom: 10px; 38 | } 39 | 40 | .modal input[type=text], 41 | .modal input[type=password] { 42 | width: auto; 43 | } 44 | 45 | .modal h1, 46 | [role=dialog] h1 { 47 | margin-top: 0; 48 | } 49 | 50 | 51 | label { 52 | display: block; 53 | margin-bottom: 4px; 54 | } 55 | 56 | *, *::after, *::before { 57 | box-sizing: border-box; 58 | } 59 | 60 | html, body { 61 | margin: 0; 62 | padding: 0; 63 | height: 100%; 64 | font-family: 'Roboto', 'Helvetica', 'Arial', sans-serif; 65 | background-image: url('./uair-2000.jpg'); 66 | background-size: cover; 67 | } 68 | 69 | .click-stealer { 70 | position: absolute; 71 | top: 0; 72 | right: 0; 73 | bottom: 0; 74 | left: 0; 75 | } 76 | 77 | .wrapper { 78 | height: 100%; 79 | display: flex; 80 | flex-direction: column; 81 | } 82 | 83 | .container { 84 | max-width: 1000px; 85 | margin: 0 auto; 86 | } 87 | 88 | header, 89 | footer { 90 | background: #000000; 91 | color: #FAFAFA; 92 | padding: 16px 0; 93 | } 94 | 95 | header h1 { 96 | margin: 0 50px 0 0; 97 | display: inline-block; 98 | vertical-align: middle; 99 | font-weight: 100; 100 | } 101 | 102 | header h1 span { 103 | font-family: 'Courgette'; 104 | color: #FFC107; 105 | } 106 | 107 | nav { 108 | display: inline-block; 109 | vertical-align: middle; 110 | } 111 | 112 | nav a, 113 | footer a { 114 | color: inherit; 115 | text-decoration: none; 116 | margin-right: 16px; 117 | font-size: 20px; 118 | } 119 | 120 | main { 121 | margin: 64px 0; 122 | flex: 1; 123 | } 124 | 125 | .card { 126 | background: rgba(255, 255, 255, 0.9); 127 | max-width: 1000px; 128 | padding: 32px 48px; 129 | margin: 0 auto; 130 | border-radius: 4px; 131 | box-shadow: 0 4px 5px 0 rgba(0, 0, 0, 0.14), 0 1px 10px 0 rgba(0, 0, 0, 0.12), 0 2px 4px -1px rgba(0, 0, 0, 0.4); 132 | } 133 | 134 | h2 { 135 | font-size: 38px; 136 | font-weight: 100; 137 | text-align: center; 138 | } 139 | 140 | .row { 141 | margin-bottom: 16px; 142 | } 143 | 144 | /* Strip out display: inline-block whitespace */ 145 | .row > * { 146 | margin-left: -4px; 147 | } 148 | 149 | .row > *:first-child { 150 | margin-left: 0; 151 | } 152 | 153 | label { 154 | display: block; 155 | font-size: 14px; 156 | color: #3F51B5; 157 | margin-bottom: 8px; 158 | } 159 | 160 | input[type="text"], 161 | input[type="tel"], 162 | input[type="email"], 163 | input[type="date"], 164 | input[type="password"], 165 | textarea { 166 | display: block; 167 | width: 100%; 168 | border-radius: 4px; 169 | border: 1px solid #9E9E9E; 170 | font-size: 18px; 171 | padding: 8px; 172 | font-family: inherit; 173 | font-weight: 100; 174 | color: #616161; 175 | } 176 | 177 | input[type="date"] { 178 | padding: 6px 8px; 179 | } 180 | 181 | select { 182 | font-size: 20px; 183 | width: 100%; 184 | } 185 | 186 | .inline-control { 187 | display: inline-block; 188 | vertical-align: top; 189 | margin-right: 16px; 190 | } 191 | 192 | .inline-control:last-child { 193 | margin-right: 0; 194 | } 195 | 196 | .trip-selector { 197 | max-width: 400px; 198 | margin: 0 auto; 199 | padding-bottom: 16px; 200 | text-align: center; 201 | border-bottom: 1px solid gray; 202 | } 203 | 204 | .trip-selector label { 205 | display: inline; 206 | margin-right: 16px; 207 | } 208 | 209 | .sign-up { 210 | vertical-align: bottom; 211 | } 212 | 213 | .submit-form { 214 | vertical-align: bottom; 215 | } 216 | 217 | button { 218 | height: 42px; 219 | color: white; 220 | background: #3F51B5; 221 | border: none; 222 | border-radius: 4px; 223 | font-size: 20px; 224 | font-family: inherit; 225 | font-weight: 100; 226 | padding: 0.5rem 1rem; 227 | } 228 | 229 | button[type=submit] { 230 | width: 100%; 231 | } 232 | 233 | footer [class*="col-"] { 234 | display: inline-block; 235 | text-align: center; 236 | } 237 | 238 | /* Columns */ 239 | 240 | .col-1 { 241 | width: calc(25% - 14px); 242 | } 243 | 244 | .col-2 { 245 | width: calc(50% - 14px); 246 | } 247 | 248 | .col-3 { 249 | width: calc(75% - 14px); 250 | } 251 | 252 | .col-4 { 253 | width: 100%; 254 | } 255 | 256 | 257 | /* Breakpoints */ 258 | 259 | @media (min-height: 1080px) { 260 | .row { 261 | margin-bottom: 32px; 262 | } 263 | 264 | main { 265 | padding-top: 150px; 266 | } 267 | } 268 | -------------------------------------------------------------------------------- /lesson5-semantics-aria/21-dialog/solution/main.css: -------------------------------------------------------------------------------- 1 | .modal { 2 | border-radius: 4px; 3 | margin-left: auto; 4 | margin-right: auto; 5 | padding: 24px; 6 | background-color: #fff; 7 | z-index: 3; /* places the modal on top of everything */ 8 | position: fixed; 9 | top: 25%; 10 | left: 0; 11 | right: 0; 12 | width: 500px; 13 | display: none; 14 | } 15 | 16 | .modal-overlay { 17 | width: 100%; 18 | height: 100%; 19 | z-index: 2; /* places the modalOverlay between the main page and the modal dialog */ 20 | background-color: #000; 21 | opacity: 0.5; 22 | position: fixed; 23 | top: 0; 24 | left: 0; 25 | display: none; 26 | margin: 0; 27 | padding: 0; 28 | } 29 | 30 | .field { 31 | display: inline-block; 32 | vertical-align: bottom; 33 | } 34 | 35 | .modal .field { 36 | display: block; 37 | margin-bottom: 10px; 38 | } 39 | 40 | .modal input[type=text], 41 | .modal input[type=password] { 42 | width: auto; 43 | } 44 | 45 | .modal h1, 46 | [role=dialog] h1 { 47 | margin-top: 0; 48 | } 49 | 50 | 51 | label { 52 | display: block; 53 | margin-bottom: 4px; 54 | } 55 | 56 | *, *::after, *::before { 57 | box-sizing: border-box; 58 | } 59 | 60 | html, body { 61 | margin: 0; 62 | padding: 0; 63 | height: 100%; 64 | font-family: 'Roboto', 'Helvetica', 'Arial', sans-serif; 65 | background-image: url('./uair-2000.jpg'); 66 | background-size: cover; 67 | } 68 | 69 | .click-stealer { 70 | position: absolute; 71 | top: 0; 72 | right: 0; 73 | bottom: 0; 74 | left: 0; 75 | } 76 | 77 | .wrapper { 78 | height: 100%; 79 | display: flex; 80 | flex-direction: column; 81 | } 82 | 83 | .container { 84 | max-width: 1000px; 85 | margin: 0 auto; 86 | } 87 | 88 | header, 89 | footer { 90 | background: #000000; 91 | color: #FAFAFA; 92 | padding: 16px 0; 93 | } 94 | 95 | header h1 { 96 | margin: 0 50px 0 0; 97 | display: inline-block; 98 | vertical-align: middle; 99 | font-weight: 100; 100 | } 101 | 102 | header h1 span { 103 | font-family: 'Courgette'; 104 | color: #FFC107; 105 | } 106 | 107 | nav { 108 | display: inline-block; 109 | vertical-align: middle; 110 | } 111 | 112 | nav a, 113 | footer a { 114 | color: inherit; 115 | text-decoration: none; 116 | margin-right: 16px; 117 | font-size: 20px; 118 | } 119 | 120 | main { 121 | margin: 64px 0; 122 | flex: 1; 123 | } 124 | 125 | .card { 126 | background: rgba(255, 255, 255, 0.9); 127 | max-width: 1000px; 128 | padding: 32px 48px; 129 | margin: 0 auto; 130 | border-radius: 4px; 131 | box-shadow: 0 4px 5px 0 rgba(0, 0, 0, 0.14), 0 1px 10px 0 rgba(0, 0, 0, 0.12), 0 2px 4px -1px rgba(0, 0, 0, 0.4); 132 | } 133 | 134 | h2 { 135 | font-size: 38px; 136 | font-weight: 100; 137 | text-align: center; 138 | } 139 | 140 | .row { 141 | margin-bottom: 16px; 142 | } 143 | 144 | /* Strip out display: inline-block whitespace */ 145 | .row > * { 146 | margin-left: -4px; 147 | } 148 | 149 | .row > *:first-child { 150 | margin-left: 0; 151 | } 152 | 153 | label { 154 | display: block; 155 | font-size: 14px; 156 | color: #3F51B5; 157 | margin-bottom: 8px; 158 | } 159 | 160 | input[type="text"], 161 | input[type="tel"], 162 | input[type="email"], 163 | input[type="date"], 164 | input[type="password"], 165 | textarea { 166 | display: block; 167 | width: 100%; 168 | border-radius: 4px; 169 | border: 1px solid #9E9E9E; 170 | font-size: 18px; 171 | padding: 8px; 172 | font-family: inherit; 173 | font-weight: 100; 174 | color: #616161; 175 | } 176 | 177 | input[type="date"] { 178 | padding: 6px 8px; 179 | } 180 | 181 | select { 182 | font-size: 20px; 183 | width: 100%; 184 | } 185 | 186 | .inline-control { 187 | display: inline-block; 188 | vertical-align: top; 189 | margin-right: 16px; 190 | } 191 | 192 | .inline-control:last-child { 193 | margin-right: 0; 194 | } 195 | 196 | .trip-selector { 197 | max-width: 400px; 198 | margin: 0 auto; 199 | padding-bottom: 16px; 200 | text-align: center; 201 | border-bottom: 1px solid gray; 202 | } 203 | 204 | .trip-selector label { 205 | display: inline; 206 | margin-right: 16px; 207 | } 208 | 209 | .sign-up { 210 | vertical-align: bottom; 211 | } 212 | 213 | .submit-form { 214 | vertical-align: bottom; 215 | } 216 | 217 | button { 218 | height: 42px; 219 | color: white; 220 | background: #3F51B5; 221 | border: none; 222 | border-radius: 4px; 223 | font-size: 20px; 224 | font-family: inherit; 225 | font-weight: 100; 226 | padding: 0.5rem 1rem; 227 | } 228 | 229 | button[type=submit] { 230 | width: 100%; 231 | } 232 | 233 | footer [class*="col-"] { 234 | display: inline-block; 235 | text-align: center; 236 | } 237 | 238 | /* Columns */ 239 | 240 | .col-1 { 241 | width: calc(25% - 14px); 242 | } 243 | 244 | .col-2 { 245 | width: calc(50% - 14px); 246 | } 247 | 248 | .col-3 { 249 | width: calc(75% - 14px); 250 | } 251 | 252 | .col-4 { 253 | width: 100%; 254 | } 255 | 256 | 257 | /* Breakpoints */ 258 | 259 | @media (min-height: 1080px) { 260 | .row { 261 | margin-bottom: 32px; 262 | } 263 | 264 | main { 265 | padding-top: 150px; 266 | } 267 | } 268 | --------------------------------------------------------------------------------