-
-
- {slide.__path__ ? // for debugging
-
- File path: {slide.__path__}
-
:
- null
- }
-
- {children}
-
-
+
+
+
+
+
+ {children}
+
+
+
);
}
MasterLayout.propTypes = {
/**
- * The index of the current slide
- */
- slideIndex: React.PropTypes.number,
-
- /**
- * All slides
+ * CSS class names to add to the page.
*/
- slides: React.PropTypes.array,
+ className: React.PropTypes.string,
/**
- * CSS class names to add to the page.
+ * The rendered slide is passed as child to the master layout.
*/
- className: React.PropTypes.string,
+ children: React.PropTypes.node,
};
diff --git a/js/SlideLayout.js b/js/SlideLayout.js
new file mode 100644
index 0000000..7632f7e
--- /dev/null
+++ b/js/SlideLayout.js
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2015-present, Facebook, Inc.
+ * All rights reserved.
+ *
+ * This source code is licensed under the license found in the LICENSE file in
+ * the root directory of this source tree.
+ */
+
+import ExtensionPoint from 'exerslide/components/ExtensionPoint';
+import React from 'react';
+
+/**
+ * The base layout for every slide. This allows you do add additional
+ * content to all slides before or after the content.
+ */
+export default function SlideLayout({children}) {
+ return (
+
+
+ {children}
+
+
+ );
+}
+
+SlideLayout.propTypes = {
+ /**
+ * The current slide content and header are passed in as children by exerslide
+ */
+ children: React.PropTypes.node,
+};
diff --git a/js/analytics.js b/js/analytics.js
new file mode 100644
index 0000000..78cdfe0
--- /dev/null
+++ b/js/analytics.js
@@ -0,0 +1,8 @@
+export default function(exerslide) {
+ /* global ga */
+ exerslide.subscribe('SLIDE.DID_MOUNT', ({slide}) => {
+ ga('send', 'pageview', {
+ 'page': location.pathname + slide.url,
+ });
+ });
+};
diff --git a/js/app.js b/js/app.js
deleted file mode 100755
index e76258b..0000000
--- a/js/app.js
+++ /dev/null
@@ -1,4 +0,0 @@
-import * as config from './config';
-import presentation from 'exerslide/js/presentation';
-
-presentation.init(config);
diff --git a/js/components/TOC.js b/js/components/TOC.js
index 17199dd..d990259 100644
--- a/js/components/TOC.js
+++ b/js/components/TOC.js
@@ -7,10 +7,8 @@
*/
import React from 'react';
-import {groupByChapter} from 'exerslide/js/chapterHelper';
-import {IS_MOBILE} from 'exerslide/js/deviceHelper';
-import {getOptions} from 'exerslide/js/optionHelper';
-import {getSlideURL} from 'exerslide/js/url';
+import * as exerslide from 'exerslide/browser';
+const {groupByChapter, IS_MOBILE} = exerslide;
import './css/toc.css';
@@ -32,27 +30,28 @@ class Entry extends React.Component {
render() {
const {slideIndex, slides, active} = this.props;
const slide = slides[slideIndex];
- const slideOptions = getOptions(slides, slideIndex);
- const classes = 'slide' + (active ? ' active' : '');
- const Layout = slide.layout;
- const layoutClasses =
- Layout && Layout.getClassNames && Layout.getClassNames(slideIndex);
+ const slideOptions = slide.options;
+ let classes = ['exerslide-toc-entry'];
+ const layout = slide.layout;
+ if (layout && layout.getClassNames) {
+ classes = classes.concat(layout.getClassNames(slideIndex, exerslide));
+ }
const title = slideOptions.toc || slideOptions.title ||
`Slide ${slideIndex + 1}`;
const props = {};
if (active) {
+ classes.push('active');
props['aria-current'] = 'page';
}
return (
-
+
+ href={slide.url}>
{title}
@@ -71,34 +70,34 @@ Entry.propTypes = {
*/
export default class TOC extends React.Component {
- constructor(props) {
- super(props);
- const slideOptions = getOptions(props.slides, props.slideIndex);
+ constructor(props, context) {
+ super(props, context);
+ const slideOptions = context.slide.options;
let collapsed = false;
if (props.togglable) {
// On mobile devices we collapse the TOC by default
if (IS_MOBILE) {
collapsed = true;
- } else if (slideOptions.hasOwnProperty('hidetoc')) {
- collapsed = slideOptions.hidetoc;
+ } else if (slideOptions.hasOwnProperty('hideTOC')) {
+ collapsed = slideOptions.hideTOC;
}
}
this.state = {
- groupedSlides: groupByChapter(props.slides),
+ groupedSlides: groupByChapter(context.slides),
collapsed,
explicitlyToggled: false,
};
this._onToggle = this._onToggle.bind(this);
}
- componentWillReceiveProps(nextProps) {
- if (nextProps.slides !== this.props.slides) {
- this.setState({groupedSlides: groupByChapter(nextProps.slides)});
+ componentWillReceiveProps(nextProps, nextContext) {
+ if (nextContext.slides !== this.context.slides) {
+ this.setState({groupedSlides: groupByChapter(nextContext.slides)});
}
- if (nextProps.slideIndex !== this.props.slideIndex) {
- const slideOptions = getOptions(nextProps.slides, nextProps.slideIndex);
+ if (nextContext.slide !== this.context.slide) {
+ const slideOptions = nextContext.slide.options;
if (!this.state.explicitlyToggled) {
- let collapsed = IS_MOBILE ? true : Boolean(slideOptions.hidetoc);
+ let collapsed = IS_MOBILE ? true : Boolean(slideOptions.hideTOC);
this.setState(
{
collapsed,
@@ -121,29 +120,30 @@ export default class TOC extends React.Component {
render() {
let slideIndex = 0;
- const {slides, togglable} = this.props;
+ const {togglable} = this.props;
+ const {slides} = this.context;
const {collapsed} = this.state;
const chapters = this.state.groupedSlides.map(chapter => {
let entry;
if (Array.isArray(chapter)) {
- const isActive = this.props.slideIndex >= slideIndex &&
- this.props.slideIndex < slideIndex + chapter.length;
+ const isActive = this.context.slideIndex >= slideIndex &&
+ this.context.slideIndex < slideIndex + chapter.length;
entry = chapter.map((slide, index) =>
);
entry =
-
+ className={'exerslide-toc-chapter' + (isActive ? ' active' : '')}>
+
{chapter[0].options.chapter}
- {entry}
+ {entry}
;
slideIndex += chapter.length;
} else {
@@ -152,39 +152,42 @@ export default class TOC extends React.Component {
key={slideIndex}
slideIndex={slideIndex}
slides={slides}
- active={this.props.slideIndex === slideIndex}
+ active={this.context.slideIndex === slideIndex}
/>;
slideIndex += 1;
}
return entry;
});
+ const icon =
+
;
+
return (
-
Table of Contents
+ className={'exerslide-toc-container' + (collapsed ? ' collapsed' : '')}>
+
Table of Contents
{togglable ?
+ /* This goes against the code formatting guidelines because VoiceOver
+ * is not able to announce this button properly if there is a line
+ * break in it.
+ */
-
- :
+ onClick={this._onToggle}>{icon} :
null
}
+ aria-labelledby="exerrslide-toc-title">
{chapters}
@@ -194,24 +197,31 @@ export default class TOC extends React.Component {
TOC.propTypes = {
/**
- * Index of the currently shown slide.
+ * Whether to show a toggle button or not.
*/
- slideIndex: React.PropTypes.number,
+ togglable: React.PropTypes.bool,
/**
- * All slides.
+ * Callback called when TOC is shown or hidden.
*/
- slides: React.PropTypes.array,
+ onToggle: React.PropTypes.func,
+};
+TOC.contextTypes = {
/**
- * Whether to show a toggle button or not.
+ * Current slide.
*/
- togglable: React.PropTypes.bool,
+ slide: React.PropTypes.object,
/**
- * Callback called when TOC is shown or hidden.
+ * Index of the currently shown slide.
*/
- onToggle: React.PropTypes.func,
+ slideIndex: React.PropTypes.number,
+
+ /**
+ * All slides.
+ */
+ slides: React.PropTypes.array,
};
TOC.defaultProps = {
diff --git a/js/components/Toolbar.js b/js/components/Toolbar.js
index 15cd3dc..69fb9e6 100644
--- a/js/components/Toolbar.js
+++ b/js/components/Toolbar.js
@@ -7,8 +7,8 @@
*/
import React from 'react';
-import {nextSlide, previousSlide} from 'exerslide/js/navigation';
-import {getContentWidthStyle} from 'exerslide/js/layoutHelper';
+import ExtensionPoint from 'exerslide/components/ExtensionPoint';
+import {forward, back} from 'exerslide/browser';
import './css/toolbar.css';
@@ -16,47 +16,55 @@ import './css/toolbar.css';
* This components generates a previous and next buttons (rendered as arrows,
* using Font Awesome) to navigate the presentation.
*/
-export default function Toolbar(props) {
- const {slideIndex} = props;
+export default function Toolbar({className}, {slideIndex, slides}) {
+ const numberOfSlides = slides.length;
+
return (
-
-
-
-
-
- {' ' + (slideIndex + 1) + '/' + props.numberOfSlides + ' '}
-
-
-
-
-
+
+
+
+
+
+
+ {' ' + (slideIndex + 1) + '/' + numberOfSlides + ' '}
+
+
+
+
+
+
);
}
Toolbar.propTypes = {
+ className: React.PropTypes.string,
+};
+
+Toolbar.contextTypes = {
/**
* This index of the current slide.
*/
slideIndex: React.PropTypes.number.isRequired,
+
/**
* Number of slides.
*/
- numberOfSlides: React.PropTypes.number.isRequired,
+ slides: React.PropTypes.arrayOf(React.PropTypes.object).isRequired,
};
-
diff --git a/js/components/css/toc.css b/js/components/css/toc.css
index 65837b7..29bf1b1 100644
--- a/js/components/css/toc.css
+++ b/js/components/css/toc.css
@@ -6,7 +6,7 @@
* the root directory of this source tree.
*/
-#toc {
+.exerslide-toc-container {
background-color: #F4F4F4;
border-right: 1px solid #CCC;
display: flex;
@@ -15,7 +15,7 @@
min-width: 20em;
}
-#toc.collapsed {
+.exerslide-toc-container.collapsed {
background-color: inherit;
border-right: none;
min-width: 0;
@@ -23,83 +23,93 @@
z-index: 100;
}
-#toc ol {
+ol.exerslide-toc-entries,
+ol#exerslide-toc-list {
+ list-style: none;
+ counter-reset: item;
+ overflow-y: auto;
margin: 0;
}
-#toc > h2,
-#toc.collapsed > #toc-list {
+
+ol#exerslide-toc-list {
+ padding-left: 1em;
+ padding-right: 1em;
+}
+
+ol.exerslide-toc-entries {
+ padding-left: 0;
+}
+
+ol#exerslide-toc-list > * {
+ margin-bottom: 1em;
+}
+
+#exerslide-toc-title,
+.exerslide-toc-container.collapsed > #exerslide-toc-list {
display: none;
}
-#toc > .toggleButton {
+.exerslide-toc-toggleButton {
align-self: flex-end;
background-color: transparent;
border: none;
color: #AAA;
+ cursor: pointer;
flex-shrink: 0;
+ font-size: 1em;
outline-width: thin;
- padding: 0.5rem;
+ padding: 0.5em;
}
-#toc > .toggleButton:hover {
+.exerslide-toc-toggleButton:hover {
color: inherit;
}
-#toc #toc-list,
-#toc #toc-list .slides {
- counter-reset: item;
- overflow-y: auto;
- margin-left: 0.6em;
-}
-
-#toc > #toc-list > * {
- margin-bottom: 1em;
-}
-
-#toc > #toc-list > :first-child {
+#toc-list > :first-child {
margin-top: 0;
}
-#toc .slide,
-#toc .chapter {
+.exerslide-toc-entry,
+.exerslide-toc-chapter {
display: block;
}
-#toc .chapter::before,
-#toc .slide::before {
+.exerslide-toc-entry::before,
+.exerslide-toc-chapter::before {
content: counters(item, '.') ". ";
counter-increment: item;
}
-#toc .chapter > .title {
+.exerslide-toc-title {
display: inline-block;
margin: 0;
+ margin-bottom: 0.5em;
}
-#toc #toc-list > .slide::before,
-#toc #toc-list > .slide,
-#toc .chapter::before,
-#toc .chapter > .title {
+#exerslide-toc-list > .exerslide-toc-entry::before,
+#exerslide-toc-list > .exerslide-toc-entry,
+.exerslide-toc-chapter::before,
+.exerslide-toc-title {
font-weight: bold;
font-size: 1em;
}
-#toc .slide {
+.exerslide-toc-entry {
+ padding: 0.1em 0;
}
-#toc .slide > a {
+.exerslide-toc-entry > a {
color: inherit;
}
-
-#toc .slide,
-#toc .slide > a {
+.exerslide-toc-entry,
+.exerslide-toc-entry > a {
text-decoration: none;
outline-width: thin;
}
-#toc .slide.active,
-#toc .slide.active > a,
-#toc .slide:hover {
+.exerslide-toc-entry.active,
+.exerslide-toc-entry.active > a,
+.exerslide-toc-entry:hover {
color: #428bca;
}
diff --git a/js/components/css/toolbar.css b/js/components/css/toolbar.css
index 813567c..e4f83a4 100644
--- a/js/components/css/toolbar.css
+++ b/js/components/css/toolbar.css
@@ -6,7 +6,7 @@
* the root directory of this source tree.
*/
-#toolbar {
+.exerslide-toolbar {
box-sizing: border-box;
color: #BBB;
padding: 0 1.5em;
@@ -14,33 +14,35 @@
width: 100%;
}
-#toolbar > * {
- font-size: 1.05em;
- max-width: 45em;
+.exerslide-toolbar-button,
+.exerslide-toolbar-text {
+ font-size: 1.05rem;
}
-
-#toolbar button {
+.exerslide-toolbar-button {
background-color: transparent;
+ cursor: pointer;
+ color: #BBB;
border: none;
padding: 10px;
}
-#toolbar button:focus,
-#toolbar button:hover {
+.exerslide-toolbar-button:focus,
+.exerslide-toolbar-button:hover {
color: #555;
}
-#toolbar button[disabled] {
+.exerslide-toolbar-button[disabled] {
visibility: hidden;
}
@media(max-width: 768px) {
- #toolbar {
+ .exerslide-toolbar {
text-align: center;
margin-bottom: 1em;
}
- #toolbar > * {
- font-size: 1.5em;
+ .exerslide-toolbar-button,
+ .exerslide-toolbar-text {
+ font-size: 1.5rem;
}
}
diff --git a/js/keyMap.js b/js/keyMap.js
deleted file mode 100755
index d2720cb..0000000
--- a/js/keyMap.js
+++ /dev/null
@@ -1,28 +0,0 @@
-/**
- * This sets up the default keybindings for a presentation. The module should
- * return an object with `keys -> function` mapping. exerslide will bind event
- * handlers for those keys (key combinations) and call the corresponding function
- * providing an API to control the presentation. Currently provided is
- *
- * - nextSlide(): Advance to the next slide
- * - previousSlide(): Go back to the previous slide
- * - gotToSlide(index): Go to slide
- *
- * We use http://dmauro.github.io/Keypress/ to bind the event handlers. Have
- * a look at the documentation to find out how to specify key combinations.
- */
-
-function next({nextSlide}) {
- nextSlide();
-}
-
-function prev({previousSlide}) {
- previousSlide();
-}
-
-export default {
- left: prev,
- right: next,
- 'alt pageup': prev,
- 'alt pagedown': next,
-};
diff --git a/js/presentation.js b/js/presentation.js
index 0565cf1..efcc192 100644
--- a/js/presentation.js
+++ b/js/presentation.js
@@ -14,7 +14,7 @@
* kind of custom initialization step.
*/
-import presentation from 'exerslide/js/presentation';
+import {present, use} from 'exerslide/browser';
/**
* The master layout for this presentation. To customize is, either edit it
@@ -23,32 +23,76 @@ import presentation from 'exerslide/js/presentation';
import MasterLayout from './MasterLayout';
/**
- * This sets up the default keybindings for a presentation. This should be an
- * object with `keys -> function` mapping. exerslide will bind event
- * handlers for those keys (key combinations) and call the corresponding function
- * providing an API to control the presentation. Currently provided is
+ * The base slide layout. To customize is, either edit it directly or copy it
+ * and point to the copy here.
+ */
+import SlideLayout from './SlideLayout';
+
+/**
+ * Many features of exerslide are actually made available via runtime plugins.
+ *
+ * The following plugin enables keyboard navigation. This sets up the default
+ * keybindings for a presentation. This should be an object with
+ * `keys -> function` mapping. exerslide will bind event handlers for those keys
+ * (key combinations) and call the corresponding function providing an API to
+ * control the presentation. Currently provided is
*
* - nextSlide(): Advance to the next slide
* - previousSlide(): Go back to the previous slide
- * - gotToSlide(index): Go to slide
*
* We use https://craig.is/killing/mice to bind the event handlers. Have
* a look at the documentation to find out how to specify key combinations.
*/
-function next({nextSlide}) {
- nextSlide();
+import keyboardNavigation from 'exerslide/browser-plugins/keyboardNavigation';
+function forward({forward}) {
+ forward();
}
-function prev({previousSlide}) {
- previousSlide();
+function back({back}) {
+ back();
}
+use(
+ keyboardNavigation,
+ {
+ left: back,
+ right: forward,
+ 'alt+pageup': back,
+ 'alt+pagedown': forward,
+ }
+);
+
+/**
+ * This plugin automatically scales the font size according to the provided
+ * settings. The default settings try to maintain a line length that is
+ * considered to be readable. To disable this plugin, just comment or remove the
+ * following two lines.
+ */
+import scaledContent from 'exerslide/browser-plugins/scaledContent';
+use(scaledContent);
+
+/**
+ * This plugin, when applied to content, injects alerts for screenreaders that
+ * let the author know if the content isn't fully visible. That allows authors
+ * with visual impairment to adjust the content or to scroll to make the content
+ * visible.
+ */
+import contentVisibility from 'exerslide/browser-plugins/contentVisibility';
+use(contentVisibility);
+
+/**
+ * This plugin is only enabled during development and shows the file path of the
+ * current slide and allows to view the source of the slide.
+ */
+import debugInformation from 'exerslide/browser-plugins/debugInformation';
+//use(debugInformation);
-const keyMap = {
- left: prev,
- right: next,
- 'alt+pageup': prev,
- 'alt+pagedown': next,
-};
+/**
+ * It is likely that you will be linking to the same external sources from
+ * multiple slides. By default exerslide provides the references.yml file as a
+ * central place to keep those references. The default markdown parser takes
+ * them into account.
+ */
+import references from '!!json!yaml!../references.yml';
/**
* __exerslide_slides__ is "magic" global variable that holds an array of slide
@@ -57,8 +101,12 @@ const keyMap = {
*/
/* global __exerslide_slides__*/
-presentation({
- MasterLayout,
- keyMap,
+import analytics from './analytics';
+use(analytics);
+
+present({
+ masterLayout: MasterLayout,
+ slideLayout: SlideLayout,
+ references,
slides: __exerslide_slides__,
});
diff --git a/js/scriptHelper.js b/js/scriptHelper.js
index 734f7b4..9d0f2a0 100755
--- a/js/scriptHelper.js
+++ b/js/scriptHelper.js
@@ -38,12 +38,12 @@ global.showHideDialog = function(ev) {
document.getElementById('inputfield').focus();
}
}
- if (el.style.display == "block") {
+ if (el.style.display == "flex") {
el.style.display = "none";
document.getElementById('sourceLink').focus();
document.body.style.overflow = 'initial';
} else {
- el.style.display = "block";
+ el.style.display = "flex";
document.getElementById('inputfield').focus();
document.body.style.overflow = 'hidden';
}
@@ -58,12 +58,12 @@ global.showHideDialog2 = function(ev) {
document.getElementById('inputfield2').focus();
}
}
- if (el.style.display == "block") {
+ if (el.style.display == "flex") {
el.style.display = "none";
document.getElementById('sourceLink2').focus();
document.body.style.overflow = 'initial';
} else {
- el.style.display = "block";
+ el.style.display = "flex";
document.getElementById('inputfield2').focus();
document.body.style.overflow = 'hidden';
}
@@ -89,9 +89,9 @@ global.closeDialog2 = function(ev) {
// Menu example code.
global.menuExample = {
onClick: function (event) {
- var isOpen = this.toggleMenu(event.currentTarget.parentNode);
+ var isOpen = this.toggleMenu(event.currentTarget);
if (isOpen) {
- this.setMenuItemFocus(event.currentTarget.parentNode);
+ this.setMenuItemFocus(event.currentTarget);
}
},
// Keep all clicks from escaping the widget.
@@ -231,14 +231,16 @@ global.menuExample = {
this.findButtonElement(menuWidgetElement).focus();
},
- getActiveMenuItem: function (menuWidgetElement) {
- var menuElement = this.findMenuElement(menuWidgetElement);
- var activeMenuItemElement = menuElement.querySelector('[data-active="true"]');
- },
findMenuElement: function (menuWidgetElement) {
- return menuWidgetElement.querySelector('[role="menu"]');
+ while (menuWidgetElement.className.indexOf('popupMenuWidget') === -1) {
+ menuWidgetElement = menuWidgetElement.parentNode;
+ }
+ return menuWidgetElement.querySelector('[role="menu"],ul');
},
findButtonElement: function (menuWidgetElement) {
- return menuWidgetElement.querySelector('[role="button"]');
+ while (menuWidgetElement.className.indexOf('popupMenuWidget') === -1) {
+ menuWidgetElement = menuWidgetElement.parentNode;
+ }
+ return menuWidgetElement.querySelector('[role="button"],a');
}
};
diff --git a/layouts/HTMLExercise.js b/layouts/HTMLExercise.js
index 3512e18..c4ec57a 100755
--- a/layouts/HTMLExercise.js
+++ b/layouts/HTMLExercise.js
@@ -1,5 +1,6 @@
import React from 'react';
import Editor from 'exerslide/components/Editor';
+import ContentRenderer from 'exerslide/components/ContentRenderer';
import '../js/scriptHelper';
import 'codemirror/mode/htmlmixed/htmlmixed';
@@ -77,11 +78,11 @@ class Example extends React.Component {
:
null
}
- {title}
+ {' '}{title}
:
null
}
- {description ? this.props.contentConverter(description) : null}
+ {description ? : null}
{this.props.code ?
@@ -129,7 +130,7 @@ class Example extends React.Component {
}
{this.props.note ?
- {this.props.contentConverter(description)}
+
:
null
}
@@ -140,20 +141,22 @@ class Example extends React.Component {
export default class HTMLExercise extends React.Component {
componentDidMount() {
- let {layoutData: {description, examples}, slideIndex} = this.props;
+ /*
if (slideIndex == 14) {
exerslide.platFormFn(document, 'script', 'facebook-jssdk');
}
+ */
}
render(element: ReactElement, container: DOMElement) {
- let {layoutData: {description, examples}, slideIndex} = this.props;
+ let {title, layoutData: {examples}, content} = this.props;
return (
- {description ? this.props.contentConverter(description) : null}
+ {title}
+ {content ? : null}
{examples && examples.map(
- (example, i) =>
+ (example, i) =>
)}
);
diff --git a/layouts/css/Center.css b/layouts/css/Center.css
deleted file mode 100755
index 010b305..0000000
--- a/layouts/css/Center.css
+++ /dev/null
@@ -1,8 +0,0 @@
-#slide > .Center-wrapper {
- align-items: center;
- display: flex;
- flex-direction: column;
- flex: 1;
- justify-content: center;
- text-align: center;
-}
diff --git a/layouts/css/TwoColumn.css b/layouts/css/TwoColumn.css
deleted file mode 100755
index 229fe44..0000000
--- a/layouts/css/TwoColumn.css
+++ /dev/null
@@ -1,19 +0,0 @@
-.TwoColumn-wrapper {
- display: flex;
-}
-
-.TwoColumn-column {
- flex: 1;
- padding: 10px;
-}
-
-.TwoColumn-column.TwoColumn-image {
- display: flex;
- flex-direction: column;
- align-items: center;
- justify-content: center;
-}
-
-.TwoColumn-column.TwoColumn-image > img {
- flex-shrink: 0;
-}
diff --git a/package.json b/package.json
index e56a794..87cf2b6 100644
--- a/package.json
+++ b/package.json
@@ -5,12 +5,12 @@
"private": true,
"dependencies": {
"codemirror": "^5.7.0",
- "exerslide": "~1.0.0",
- "exerslide-plugin-center-layout": "~1.0.0",
- "exerslide-plugin-column-layout": "~1.0.0",
- "exerslide-plugin-html-converter": "~1.0.0",
- "exerslide-plugin-markdown-converter": "~1.0.0",
- "exerslide-plugin-shared-urls": "~1.0.0",
+ "exerslide": "~1.0.2",
+ "exerslide-plugin-bulletlist-layout": "^1.0.0",
+ "exerslide-plugin-center-layout": "^1.0.0",
+ "exerslide-plugin-column-layout": "^1.0.0",
+ "exerslide-plugin-html-converter": "^1.0.0",
+ "exerslide-plugin-markdown-converter": "^1.0.1",
"font-awesome": "^4.4.0",
"foundation-sites": "^6.1.2",
"react": "^15.0.0",
@@ -29,7 +29,9 @@
"extract-text-webpack-plugin": "^0.9.1",
"file-loader": "^0.8.5",
"is-text-path": "^1.0.1",
+ "json-loader": "^0.5.4",
"style-loader": "^0.13.0",
- "webpack": "^1.12.9"
+ "webpack": "^1.12.9",
+ "yaml-loader": "^0.4.0"
}
}
diff --git a/pushSite.sh b/pushSite.sh
deleted file mode 100755
index f602153..0000000
--- a/pushSite.sh
+++ /dev/null
@@ -1,52 +0,0 @@
-#!/bin/sh
-set -e
-
-TARGETPATH="../$(basename $(pwd))_gh_pages"
-REMOTE=$(git remote -v | grep origin | grep "(push)" | cut -f 2 | cut -d ' ' -f 1)
-
-if ! git diff --quiet && git diff --cached --quiet; then
- echo >&2 "Cannot build, your index contains uncommitted changes."
- exit 1
-fi
-
-if [ -d "$TARGETPATH" ]; then
- rm -rf "$TARGETPATH"
-fi
-
-# Get latest changes from GitHub
-git checkout gh-pages
-git pull origin gh-pages
-git checkout master
-
-git clone ./ "$TARGETPATH"
-cd "$TARGETPATH"
-git checkout gh-pages
-git remote set-url --push origin $REMOTE
-cd - > /dev/null
-
-# Updating
-echo "Clear target..."
-cd "$TARGETPATH"
-git pull origin
-git rm -rf *
-cd - > /dev/null
-
-echo "Building..."
-rm -rf out/*
-exerslide build
-echo "Copying artifacts..."
-cp -R out/ "$TARGETPATH/"
-
-# Commit changes
-cd $TARGETPATH
-if git diff --quiet && git diff --cached --quite; then
- echo "No changes, nothing to commit..."
- exit 0
-fi
-echo "Committing..."
-git add -A
-git commit -m"Update site"
-echo "Pushing..."
-git push origin
-echo "done"
-
diff --git a/references.yml b/references.yml
new file mode 100644
index 0000000..85819e2
--- /dev/null
+++ b/references.yml
@@ -0,0 +1,7 @@
+# You can keep links to external sources here. This lets you avoid repeating the
+# same URL on different slides. The default markdown parser takes these into
+# account.The format is: "name: URL"
+#
+# Example:
+#
+# example: http://example.org
diff --git a/slides/00-Introduction/00-instructions.md b/slides/00-Introduction/00-instructions.md
index 09faf35..c27b7c2 100755
--- a/slides/00-Introduction/00-instructions.md
+++ b/slides/00-Introduction/00-instructions.md
@@ -10,13 +10,17 @@ Welcome to the Teach Access Tutorial! This resource is part of the Teach Access
use the verify button to check whether your solution is correct.
3. You can also verify the code sample output using VoiceOver - Apple's built-in screen reader (other screen readers work too). Here are some instructions for navigating with VoiceOver:
- *
cmd +
F5 turns VoiceOver on/off
- *
tab navigates to an interactive element
- *
ctrl +
opt +
right arrow (repeatedly) navigates to the next element
- *
ctrl +
opt +
cmd +
h (repeatedly) navigates by headings
-
+ *
cmd +
F5 turns VoiceOver on/off
+ *
tab navigates to an interactive element
+ *
ctrl +
opt +
right arrow (repeatedly)
+ navigates to the next element
+ *
ctrl +
opt +
cmd +
h
+ (repeatedly) navigates by headings
+
+
4. When verifying with VoiceOver, use Chrome on a Mac for the best experience. However, the "Verify" button provided after each exercise will work with any browser/OS combination.
-5. ARIA stands for Accessible Rich Internet Applications, a W3C standard for building accessible user interfaces on the web.
+5. ARIA stands for Accessible Rich Internet Applications, a W3C standard for
+ building accessible user interfaces on the web.
Happy learning!
Smiley Icon
diff --git a/slides/00-Introduction/01-faqs.md b/slides/00-Introduction/01-faqs.md
index e7ab9ba..e920bf6 100755
--- a/slides/00-Introduction/01-faqs.md
+++ b/slides/00-Introduction/01-faqs.md
@@ -19,15 +19,16 @@ The tutorial should work with most assistive technologies, but Apple's VoiceOver
**Where can I find more information on accessibility?**
Here are a few resources to get you started:
-- Web Content Accessibility Guidelines: https://www.w3.org/WAI/intro/wcag
-- WebAIM: http://webaim.org/
+- Web Content Accessibility Guidelines:
+- WebAIM:
**What are some good, free, developer tools for accessibility?**
-- WAVE by WebAIM for general website accessibility: http://wave.webaim.org/
-- Juicy Studio's Readability Test: http://juicystudio.com/services/readability.php
-- Vischeck's Color Blindness Checker: http://www.vischeck.com/
+- WAVE by WebAIM for general website accessibility:
+- Juicy Studio's Readability Test:
+
+- Vischeck's Color Blindness Checker:
**Are there jobs in the field of accessibility?**
-Definitely, and the industry is growing. Take a look here: https://twitter.com/a11yjobs
+Definitely, and the industry is growing. Take a look here:
diff --git a/slides/02-Developers/01-headings.html.md b/slides/02-Developers/01-headings.html.md
index 5f2fe21..1d15448 100755
--- a/slides/02-Developers/01-headings.html.md
+++ b/slides/02-Developers/01-headings.html.md
@@ -9,15 +9,11 @@ style: |
margin: 0;
}
- #slide h3 {
+ .exerslide-slide h3 {
margin: 5px;
}
-layoutData:
- description: |
- Headings provide structure to a page. A person using a screen reader can
- navigate a page quickly using headings on the page if the headings used are
- semantic. Semantic headings include real heading tags such as `h1`, `h2`.
+layout_data:
examples:
- title: Semantic Heading
description: |
@@ -43,3 +39,6 @@ layoutData:
);
---
+Headings provide structure to a page. A person using a screen reader can
+navigate a page quickly using headings on the page if the headings used are
+semantic. Semantic headings include real heading tags such as `h1`, `h2`.
diff --git a/slides/02-Developers/02-images.html.md b/slides/02-Developers/02-images.html.md
index 072672a..e316936 100755
--- a/slides/02-Developers/02-images.html.md
+++ b/slides/02-Developers/02-images.html.md
@@ -2,12 +2,8 @@
title: Images
chapter: Writing Code
-layoutData:
+layout_data:
description: |
- Screen readers interact with text on the screen. So, to convey the meaning of an image to screen reader users,
- we put an accessible text label in the HTML. If an image is decorative, we can hide it from screen
- reader users by giving it an empty label (`alt=""`). After the completing the exercise below, you can learn a lot more about writing good alt text for images by checking out WebAIM's resource on the topic .
-
examples:
- title: An Accessible Inline Image
description: |
@@ -38,3 +34,10 @@ layoutData:
"It doesn't look like you added an alt to your image."
);
---
+Screen readers interact with text on the screen. So, to convey the meaning of
+an image to screen reader users,
+we put an accessible text label in the HTML. If an image is decorative, we can hide it from screen
+reader users by giving it an empty label (`alt=""`). After the completing the
+exercise below, you can learn a lot more about writing good alt text for images
+by checking out [WebAIM's resource on the
+topic](http://webaim.org/techniques/alttext/).
diff --git a/slides/02-Developers/03-keyboard.html.md b/slides/02-Developers/03-keyboard.html.md
index 3c2f3c0..3237e37 100755
--- a/slides/02-Developers/03-keyboard.html.md
+++ b/slides/02-Developers/03-keyboard.html.md
@@ -13,13 +13,7 @@ style: |
text-align: center;
}
-layoutData:
- description: |
- Make all interactive elements work with a keyboard. For example, make sure a button that you activate
- with a click is also in the keyboard tab sequence and that pressing enter or space
- activates it. Set the tabindex attribute to 0 to include an element in the browser's keyboard tab sequence. If you want an element out of sequence, set its tabindex to -1 and use Javascript to control its focus and tab sequence, and related keyboard events. We do not recommend using tabindex values
- greater than 0 even though browsers support them. Note that HTML links and input elements have an implied tabindex of 0.
-
+layout_data:
examples:
- title: Semantic Button
description: |
@@ -37,16 +31,16 @@ layoutData:
description: |
The button below is constructed using an unsemantic div. The easiest way to
make this semantic is to use a real button or input tag. Here is another way. In the
- example below,
- 1. Add `role` = 'button'.
- 2. `tabindex` = '0'.
+ example below, add
+ 1. `role='button'`
+ 2. `tabindex='0'`
Verify with VoiceOver that you can tab to the button and hear the button name and the fact
that it is a button element. Note that you would need to add an onkeypress or onkeydown handler to the button so you
can tab to the button and press enter to activate it using just the keyboard.
code: |
-
+
Submit
@@ -63,3 +57,12 @@ layoutData:
);
---
+Make all interactive elements work with a keyboard. For example, make sure a
+button that you activate
+with a click is also in the keyboard tab sequence and that pressing enter or space
+activates it. Set the `tabindex` attribute to `0` to include an element in the
+browser's keyboard tab sequence. If you want an element out of sequence, set
+its `tabindex` to `-1` and use JavaScript to control its focus and tab
+sequence, and related keyboard events. We do not recommend using `tabindex`
+values greater than `0` even though browsers support them. Note that HTML links
+and input elements have an implied `tabindex` of `0`.
diff --git a/slides/02-Developers/04-labels.html.md b/slides/02-Developers/04-labels.html.md
index e6a56ac..70192c3 100755
--- a/slides/02-Developers/04-labels.html.md
+++ b/slides/02-Developers/04-labels.html.md
@@ -12,7 +12,6 @@ style: |
#composer {
border: 1px solid #1466F2;
- width: 200px;
}
#description {
@@ -23,16 +22,7 @@ style: |
font-size: 13px;
}
-layoutData:
- description: |
- Accessible labels are necessary to make several other types of elements understandable,
- such as inputs, widgets, and ARIA landmark regions.
- Accessible labels that create a delightful experience are:
- 1. Concise -- 1 to 3 simple words. Only occasionally as many as 5 words.
- 2. Meaningful -- accurately convey the purpose of the element.
-
- There are many ways to label an interactive element such as a button or an input field, which you will see below. You can check the results of adding the various label types by testing in your screen reader too!
-
+layout_data:
examples:
- title: Self-labeled
description: |
@@ -150,3 +140,13 @@ layoutData:
"It doesn't look like you added an aria-describedby to the input field"
);
---
+Accessible labels are necessary to make several other types of elements
+understandable,
+such as inputs, widgets, and ARIA landmark regions.
+Accessible labels that create a delightful experience are:
+
+1. Concise -- 1 to 3 simple words. Only occasionally as many as 5 words.
+2. Meaningful -- accurately convey the purpose of the element.
+
+There are many ways to label an interactive element such as a button or an input field, which you will see below. You can check the results of adding the various label types by testing in your screen reader too!
+
diff --git a/slides/02-Developers/05-list.html.md b/slides/02-Developers/05-list.html.md
index adb7aa7..5b42897 100755
--- a/slides/02-Developers/05-list.html.md
+++ b/slides/02-Developers/05-list.html.md
@@ -8,16 +8,12 @@ style: |
height: 100px;
}
-layoutData:
- description: |
- Semantic lists help screen readers understand the type of the element and the number of items in the element, and provide
- easier navigation via list commands specific to screen readers.
-
+layout_data:
examples:
- title: Semantic Lists Using HTML
description: |
HTML provides tags that express lists. Most likely you are already familiar with the
- ```ul``` (unordered list) and ```ol``` (ordered list) tags.
+ `ul` (unordered list) and `ol` (ordered list) tags.
In the example below, the list is correctly announced by screen readers as a list with three items.
@@ -78,3 +74,7 @@ layoutData:
"Wrap the items in an 'li' element or in a 'span' OR 'div' with the role 'listitem'."
);
---
+Semantic lists help screen readers understand the type of the element and the
+number of items in the element, and provide
+easier navigation via list commands specific to screen readers.
+
diff --git a/slides/02-Developers/06-dialog.html.md b/slides/02-Developers/06-dialog.html.md
index 0f76a06..96458dc 100755
--- a/slides/02-Developers/06-dialog.html.md
+++ b/slides/02-Developers/06-dialog.html.md
@@ -15,6 +15,8 @@ style: |
text-align:center;
z-index: 1000;
background-color: rgba(0, 0, 0, .4);
+ align-items: center;
+ justify-content: center;
}
#container, #container2 {
@@ -22,15 +24,6 @@ style: |
background-color: #fff;
border:1px solid #000;
padding:15px;
- text-align:center;
- }
-
- #container {
- margin: 400px auto;
- }
-
- #container2 {
- margin: 400px auto;
}
label {
@@ -63,10 +56,7 @@ style: |
margin: 10px 0;
}
-layoutData:
- description: |
- Dialogs are a little bit trickier to make accessible, but with a few key principles, they can be made keyboard navigable and usable with screen readers.
-
+layout_data:
examples:
- title: Semantic Dialog
description: |
@@ -183,3 +173,5 @@ layoutData:
);
---
+Dialogs are a little bit trickier to make accessible, but with a few key
+principles, they can be made keyboard navigable and usable with screen readers.
diff --git a/slides/02-Developers/07-tables.html.md b/slides/02-Developers/07-tables.html.md
index 3c033d4..33cfcb8 100755
--- a/slides/02-Developers/07-tables.html.md
+++ b/slides/02-Developers/07-tables.html.md
@@ -7,6 +7,7 @@ style: |
.editorWithPreview > .editor,
.editorWithPreview > .preview {
width: 50%;
+ max-width: 50%;
}
table, th, td {
@@ -48,12 +49,7 @@ style: |
display: table-row-group;
}
-layoutData:
- description: |
- Tables help screen readers process information presented in a tabular format.
- When information is presented using table markup, screen reader users can
- read down columns and across rows, and even hear column and row headings as they do so.
-
+layout_data:
examples:
- title: Semantic Table
description: |
@@ -132,3 +128,7 @@ layoutData:
"Are you using semantic td tags?"
);
---
+Tables help screen readers process information presented in a tabular format.
+When information is presented using table markup, screen reader users can
+read down columns and across rows, and even hear column and row headings as they do so.
+
diff --git a/slides/02-Developers/08-menu.html.md b/slides/02-Developers/08-menu.html.md
index 81f5bc4..41cd5a8 100755
--- a/slides/02-Developers/08-menu.html.md
+++ b/slides/02-Developers/08-menu.html.md
@@ -7,10 +7,46 @@ style: |
height: 100px;
}
-layoutData:
- description: |
- Menus, like dialogs, rely on a few key principles to render them usable with the keyboard or screen reader.
+ .popupMenuWidget {
+ padding: 1em;
+ position: relative;
+ }
+
+ .popupMenuWidget [role="button"] {
+ border: 1px solid black;
+ border-radius: 3px;
+ display: inline-block;
+ padding: 1px 3px;
+ position: relative;
+ z-index: 2;
+ }
+
+ .popupMenuWidget [role="button"]:hover,
+ .popupMenuWidget [role="button"]:focus {
+ text-decoration: none;
+ }
+
+ .popupMenuWidget [role="menu"] {
+ border: 1px solid black;
+ display: none;
+ position: relative;
+ top: -1px;
+ z-index: 1;
+ list-style: none;
+ margin: 0;
+ }
+
+ .popupMenuWidget [role="menu"] a {
+ display: block;
+ padding: 0px 5px;
+ }
+ .popupMenuWidget [role="menuitem"]:focus {
+ background-color: #428bca;
+ color: white;
+ }
+
+layout_data:
examples:
- title: Accessible Menu
description: |
@@ -175,3 +211,6 @@ layoutData:
);
---
+Menus, like dialogs, rely on a few key principles to render them usable with
+the keyboard or screen reader.
+
diff --git a/slides/02-Developers/09-aria.html.md b/slides/02-Developers/09-aria.html.md
index 1746172..660e31e 100755
--- a/slides/02-Developers/09-aria.html.md
+++ b/slides/02-Developers/09-aria.html.md
@@ -14,12 +14,8 @@ style: |
display: none;
}
-layoutData:
+layout_data:
description: |
- ARIA or
Accessible Rich Internet
- Applications provides a framework of roles, properties, and guidelines to help
- develop accessible user interactions.
-
examples:
- title: ARIA
description:
@@ -55,28 +51,33 @@ layoutData:
There are several types of landmark roles. Some of them are:
- `
= ` - Equivalent to “directing the gaze” of the screen
- reader user. Should contain only the primary content of the page.
+ - `
`,`` -- Equivalent to “directing the gaze” of
+ the screen reader user. Should contain only the primary content of the
+ page.
- `
= ` - contains a set of links or elements for
- navigating the site
+ - `
`,`` -- contains a set of links or
+ elements for navigating the site
- `